summaryrefslogtreecommitdiff
path: root/odb/odb
diff options
context:
space:
mode:
Diffstat (limited to 'odb/odb')
-rw-r--r--odb/odb/.gitignore2
-rw-r--r--odb/odb/buildfile182
-rw-r--r--odb/odb/common-query.cxx1440
-rw-r--r--odb/odb/common-query.hxx279
-rw-r--r--odb/odb/common.cxx584
-rw-r--r--odb/odb/common.hxx468
-rw-r--r--odb/odb/context.cxx3389
-rw-r--r--odb/odb/context.hxx1941
-rw-r--r--odb/odb/context.ixx26
-rw-r--r--odb/odb/cxx-lexer.cxx366
-rw-r--r--odb/odb/cxx-lexer.hxx131
-rw-r--r--odb/odb/cxx-token.hxx30
-rw-r--r--odb/odb/diagnostics.cxx136
-rw-r--r--odb/odb/diagnostics.hxx96
-rw-r--r--odb/odb/emitter.cxx50
-rw-r--r--odb/odb/emitter.hxx47
-rw-r--r--odb/odb/features.hxx25
-rw-r--r--odb/odb/gcc-fwd.hxx64
-rw-r--r--odb/odb/gcc.hxx215
-rw-r--r--odb/odb/generate.hxx31
-rw-r--r--odb/odb/generator.cxx1044
-rw-r--r--odb/odb/generator.hxx22
-rw-r--r--odb/odb/header.cxx902
-rw-r--r--odb/odb/include.cxx738
-rw-r--r--odb/odb/inline.cxx506
-rw-r--r--odb/odb/instance.cxx73
-rw-r--r--odb/odb/instance.hxx295
-rw-r--r--odb/odb/location.cxx13
-rw-r--r--odb/odb/location.hxx25
-rw-r--r--odb/odb/lookup.cxx374
-rw-r--r--odb/odb/lookup.hxx97
-rw-r--r--odb/odb/odb.cxx2191
-rw-r--r--odb/odb/option-functions.cxx132
-rw-r--r--odb/odb/option-functions.hxx12
-rw-r--r--odb/odb/option-parsers.hxx199
-rw-r--r--odb/odb/option-types.cxx352
-rw-r--r--odb/odb/option-types.hxx391
-rw-r--r--odb/odb/options.cli1086
-rw-r--r--odb/odb/parser.cxx2403
-rw-r--r--odb/odb/parser.hxx37
-rw-r--r--odb/odb/plugin.cxx458
-rw-r--r--odb/odb/pragma.cxx4442
-rw-r--r--odb/odb/pragma.hxx287
-rw-r--r--odb/odb/pregenerated/odb/options.cxx4034
-rw-r--r--odb/odb/pregenerated/odb/options.hxx2340
-rw-r--r--odb/odb/pregenerated/odb/options.ixx3471
-rw-r--r--odb/odb/processor.cxx3267
-rw-r--r--odb/odb/processor.hxx23
-rw-r--r--odb/odb/profile.cxx86
-rw-r--r--odb/odb/profile.hxx39
-rw-r--r--odb/odb/relational/changelog.cxx1239
-rw-r--r--odb/odb/relational/common-query.cxx169
-rw-r--r--odb/odb/relational/common-query.hxx63
-rw-r--r--odb/odb/relational/common.cxx27
-rw-r--r--odb/odb/relational/common.hxx273
-rw-r--r--odb/odb/relational/common.txx125
-rw-r--r--odb/odb/relational/context.cxx169
-rw-r--r--odb/odb/relational/context.hxx289
-rw-r--r--odb/odb/relational/context.ixx44
-rw-r--r--odb/odb/relational/generate.hxx81
-rw-r--r--odb/odb/relational/header.cxx1146
-rw-r--r--odb/odb/relational/header.hxx1466
-rw-r--r--odb/odb/relational/inline.cxx47
-rw-r--r--odb/odb/relational/inline.hxx693
-rw-r--r--odb/odb/relational/model.cxx121
-rw-r--r--odb/odb/relational/model.hxx868
-rw-r--r--odb/odb/relational/mssql/common.cxx603
-rw-r--r--odb/odb/relational/mssql/common.hxx293
-rw-r--r--odb/odb/relational/mssql/context.cxx766
-rw-r--r--odb/odb/relational/mssql/context.hxx194
-rw-r--r--odb/odb/relational/mssql/header.cxx312
-rw-r--r--odb/odb/relational/mssql/inline.cxx42
-rw-r--r--odb/odb/relational/mssql/model.cxx66
-rw-r--r--odb/odb/relational/mssql/schema.cxx651
-rw-r--r--odb/odb/relational/mssql/source.cxx1201
-rw-r--r--odb/odb/relational/mysql/common.cxx400
-rw-r--r--odb/odb/relational/mysql/common.hxx171
-rw-r--r--odb/odb/relational/mysql/context.cxx868
-rw-r--r--odb/odb/relational/mysql/context.hxx194
-rw-r--r--odb/odb/relational/mysql/header.cxx136
-rw-r--r--odb/odb/relational/mysql/inline.cxx42
-rw-r--r--odb/odb/relational/mysql/model.cxx161
-rw-r--r--odb/odb/relational/mysql/schema.cxx489
-rw-r--r--odb/odb/relational/mysql/source.cxx724
-rw-r--r--odb/odb/relational/oracle/common.cxx522
-rw-r--r--odb/odb/relational/oracle/common.hxx203
-rw-r--r--odb/odb/relational/oracle/context.cxx795
-rw-r--r--odb/odb/relational/oracle/context.hxx188
-rw-r--r--odb/odb/relational/oracle/header.cxx230
-rw-r--r--odb/odb/relational/oracle/inline.cxx42
-rw-r--r--odb/odb/relational/oracle/model.cxx64
-rw-r--r--odb/odb/relational/oracle/schema.cxx696
-rw-r--r--odb/odb/relational/oracle/source.cxx646
-rw-r--r--odb/odb/relational/pgsql/common.cxx351
-rw-r--r--odb/odb/relational/pgsql/common.hxx159
-rw-r--r--odb/odb/relational/pgsql/context.cxx786
-rw-r--r--odb/odb/relational/pgsql/context.hxx192
-rw-r--r--odb/odb/relational/pgsql/header.cxx285
-rw-r--r--odb/odb/relational/pgsql/inline.cxx42
-rw-r--r--odb/odb/relational/pgsql/model.cxx101
-rw-r--r--odb/odb/relational/pgsql/schema.cxx266
-rw-r--r--odb/odb/relational/pgsql/source.cxx1140
-rw-r--r--odb/odb/relational/processor.cxx1564
-rw-r--r--odb/odb/relational/processor.hxx15
-rw-r--r--odb/odb/relational/schema-source.cxx281
-rw-r--r--odb/odb/relational/schema-source.hxx126
-rw-r--r--odb/odb/relational/schema.cxx174
-rw-r--r--odb/odb/relational/schema.hxx1606
-rw-r--r--odb/odb/relational/source.cxx6345
-rw-r--r--odb/odb/relational/source.hxx7158
-rw-r--r--odb/odb/relational/sqlite/common.cxx217
-rw-r--r--odb/odb/relational/sqlite/common.hxx147
-rw-r--r--odb/odb/relational/sqlite/context.cxx490
-rw-r--r--odb/odb/relational/sqlite/context.hxx146
-rw-r--r--odb/odb/relational/sqlite/header.cxx63
-rw-r--r--odb/odb/relational/sqlite/inline.cxx42
-rw-r--r--odb/odb/relational/sqlite/model.cxx91
-rw-r--r--odb/odb/relational/sqlite/schema.cxx455
-rw-r--r--odb/odb/relational/sqlite/source.cxx471
-rw-r--r--odb/odb/relational/validator.cxx638
-rw-r--r--odb/odb/relational/validator.hxx24
-rw-r--r--odb/odb/semantics.hxx19
-rw-r--r--odb/odb/semantics/class-template.cxx54
-rw-r--r--odb/odb/semantics/class-template.hxx68
-rw-r--r--odb/odb/semantics/class.cxx175
-rw-r--r--odb/odb/semantics/class.hxx142
-rw-r--r--odb/odb/semantics/derived.cxx254
-rw-r--r--odb/odb/semantics/derived.hxx440
-rw-r--r--odb/odb/semantics/elements.cxx619
-rw-r--r--odb/odb/semantics/elements.hxx841
-rw-r--r--odb/odb/semantics/elements.ixx13
-rw-r--r--odb/odb/semantics/enum.cxx85
-rw-r--r--odb/odb/semantics/enum.hxx228
-rw-r--r--odb/odb/semantics/fundamental.cxx222
-rw-r--r--odb/odb/semantics/fundamental.hxx150
-rw-r--r--odb/odb/semantics/namespace.cxx107
-rw-r--r--odb/odb/semantics/namespace.hxx71
-rw-r--r--odb/odb/semantics/relational.hxx18
-rw-r--r--odb/odb/semantics/relational/changelog.cxx187
-rw-r--r--odb/odb/semantics/relational/changelog.hxx164
-rw-r--r--odb/odb/semantics/relational/changeset.cxx56
-rw-r--r--odb/odb/semantics/relational/changeset.hxx93
-rw-r--r--odb/odb/semantics/relational/column.cxx201
-rw-r--r--odb/odb/semantics/relational/column.hxx205
-rw-r--r--odb/odb/semantics/relational/deferrable.cxx55
-rw-r--r--odb/odb/semantics/relational/deferrable.hxx41
-rw-r--r--odb/odb/semantics/relational/elements.cxx179
-rw-r--r--odb/odb/semantics/relational/elements.hxx462
-rw-r--r--odb/odb/semantics/relational/elements.txx215
-rw-r--r--odb/odb/semantics/relational/foreign-key.cxx218
-rw-r--r--odb/odb/semantics/relational/foreign-key.hxx152
-rw-r--r--odb/odb/semantics/relational/index.cxx145
-rw-r--r--odb/odb/semantics/relational/index.hxx100
-rw-r--r--odb/odb/semantics/relational/key.cxx97
-rw-r--r--odb/odb/semantics/relational/key.hxx104
-rw-r--r--odb/odb/semantics/relational/model.cxx54
-rw-r--r--odb/odb/semantics/relational/model.hxx49
-rw-r--r--odb/odb/semantics/relational/name.cxx89
-rw-r--r--odb/odb/semantics/relational/name.hxx159
-rw-r--r--odb/odb/semantics/relational/primary-key.cxx80
-rw-r--r--odb/odb/semantics/relational/primary-key.hxx62
-rw-r--r--odb/odb/semantics/relational/table.cxx183
-rw-r--r--odb/odb/semantics/relational/table.hxx115
-rw-r--r--odb/odb/semantics/template.cxx88
-rw-r--r--odb/odb/semantics/template.hxx146
-rw-r--r--odb/odb/semantics/union-template.cxx54
-rw-r--r--odb/odb/semantics/union-template.hxx33
-rw-r--r--odb/odb/semantics/union.cxx36
-rw-r--r--odb/odb/semantics/union.hxx27
-rw-r--r--odb/odb/semantics/unit.cxx42
-rw-r--r--odb/odb/semantics/unit.hxx183
-rw-r--r--odb/odb/source.cxx151
-rw-r--r--odb/odb/sql-lexer.cxx250
-rw-r--r--odb/odb/sql-lexer.hxx129
-rw-r--r--odb/odb/sql-lexer.ixx84
-rw-r--r--odb/odb/sql-token.cxx44
-rw-r--r--odb/odb/sql-token.hxx89
-rw-r--r--odb/odb/sql-token.ixx44
-rw-r--r--odb/odb/traversal.hxx19
-rw-r--r--odb/odb/traversal/class-template.cxx62
-rw-r--r--odb/odb/traversal/class-template.hxx45
-rw-r--r--odb/odb/traversal/class.cxx34
-rw-r--r--odb/odb/traversal/class.hxx40
-rw-r--r--odb/odb/traversal/derived.cxx111
-rw-r--r--odb/odb/traversal/derived.hxx130
-rw-r--r--odb/odb/traversal/elements.cxx77
-rw-r--r--odb/odb/traversal/elements.hxx238
-rw-r--r--odb/odb/traversal/enum.cxx54
-rw-r--r--odb/odb/traversal/enum.hxx57
-rw-r--r--odb/odb/traversal/fundamental.hxx39
-rw-r--r--odb/odb/traversal/namespace.hxx15
-rw-r--r--odb/odb/traversal/relational.hxx18
-rw-r--r--odb/odb/traversal/relational/changelog.cxx63
-rw-r--r--odb/odb/traversal/relational/changelog.hxx53
-rw-r--r--odb/odb/traversal/relational/changeset.hxx18
-rw-r--r--odb/odb/traversal/relational/column.hxx21
-rw-r--r--odb/odb/traversal/relational/elements.hxx162
-rw-r--r--odb/odb/traversal/relational/foreign-key.hxx21
-rw-r--r--odb/odb/traversal/relational/index.hxx20
-rw-r--r--odb/odb/traversal/relational/key.cxx17
-rw-r--r--odb/odb/traversal/relational/key.hxx50
-rw-r--r--odb/odb/traversal/relational/model.hxx18
-rw-r--r--odb/odb/traversal/relational/primary-key.hxx18
-rw-r--r--odb/odb/traversal/relational/table.hxx21
-rw-r--r--odb/odb/traversal/template.cxx53
-rw-r--r--odb/odb/traversal/template.hxx56
-rw-r--r--odb/odb/traversal/union-template.cxx26
-rw-r--r--odb/odb/traversal/union-template.hxx29
-rw-r--r--odb/odb/traversal/union.hxx15
-rw-r--r--odb/odb/traversal/unit.hxx15
-rw-r--r--odb/odb/validator.cxx1917
-rw-r--r--odb/odb/validator.hxx23
-rw-r--r--odb/odb/version.hxx0
-rw-r--r--odb/odb/version.hxx.in54
214 files changed, 91715 insertions, 0 deletions
diff --git a/odb/odb/.gitignore b/odb/odb/.gitignore
new file mode 100644
index 0000000..4fd410e
--- /dev/null
+++ b/odb/odb/.gitignore
@@ -0,0 +1,2 @@
+/odb
+/options.?xx
diff --git a/odb/odb/buildfile b/odb/odb/buildfile
new file mode 100644
index 0000000..21b98ed
--- /dev/null
+++ b/odb/odb/buildfile
@@ -0,0 +1,182 @@
+# file : odb/buildfile
+# license : GNU GPL v3; see accompanying LICENSE file
+
+define plugin: libs
+
+plugin{*}:
+{
+ bin.lib.prefix = # No lib prefix.
+ backlink = true # Backlink in forwarded configs (next to exe).
+}
+
+# For now we use the .so extension everywhere except Windows (see
+# plugin_path() in odb.cxx for details).
+#
+if ($cxx.target.class != 'windows')
+ plugin{*}: extension = so
+
+# By default install the plugin next to the driver.
+#
+# On Windows this is the only sane option where we want the plugin (odb.dll)
+# to go into bin/ since failed that we won't be able to find libraries we
+# depend on.
+#
+# On other platforms another option is to install into the GCC's plugin
+# directory. This way the same driver can be used with multiple GCC versions
+# and is something that distributions packagers sometimes want to do.
+#
+# So at some point we should also make it configurable, including support for
+# installing into GCC's plugin directory.
+#
+# NOTE: see ODB_GCC_PLUGIN_DIR when adding this support.
+#
+# NOTE: also think about relocatable installation.
+#
+plugin{*}: install = bin/
+
+import libs = libcutl%lib{cutl}
+import libs += libstudxml%lib{studxml}
+
+./: exe{odb} plugin{odb}
+
+# We need to make driver depend on plugin but not link it so that when, for
+# example, driver is imported, plugin is updated as well.
+#
+# We, however, don't want to install via the driver since the same driver
+# build could be used with multiple plugin builds (e.g., for different GCC
+# versions, which is something distribution packagers sometimes want to do).
+#
+exe{odb}: cxx{odb}
+exe{odb}: libus{odb}: bin.whole = false
+exe{odb}: plugin{odb}:
+{
+ include = adhoc
+
+ # @@ This work but triggers "incompatible libs{cutl} build". Feels like to
+ # solve this we will also need to say update_for_install=false which
+ # we currently cant.
+ #
+ #install = false
+}
+
+# Target metadata, see also --build2-metadata in odb.cxx.
+#
+# While ODB itself doesn't use any environment variables, it uses GCC
+# underneath which does (see "Environment Variables Affecting GCC").
+#
+exe{odb}:
+{
+ export.metadata = 1 odb
+ odb.name = [string] odb
+ odb.version = [string] $version
+ odb.checksum = [string] $version
+ odb.environment = [strings] CPATH CPLUS_INCLUDE_PATH GCC_EXEC_PREFIX COMPILER_PATH
+}
+
+plugin{odb}: libus{odb}
+
+switch $cxx.target.system
+{
+ # On Windows we have to link the import stub.
+ #
+ case 'mingw32'
+ plugin{odb}: cxx.libs += $plugin_dir/cc1plus.exe.a
+
+ # On Mac OS we have to allow undefined symbols.
+ #
+ case 'darwin'
+ plugin{odb}: cxx.loptions += -undefined dynamic_lookup
+}
+
+libus{odb}: {hxx ixx txx cxx}{** -odb -version -options -pregenerated/**} \
+ hxx{version} $libs
+
+# Include the generated version header into the distribution (so that we don't
+# pick up an installed one) and don't remove it when cleaning in src (so that
+# clean results in a state identical to distributed).
+#
+hxx{version}: in{version} $src_root/manifest
+hxx{version}:
+{
+ dist = true
+ clean = ($src_root != $out_root)
+}
+
+# Build options.
+#
+# Note: escape backslashes in gxx_name.
+#
+cxx.poptions += "-I$plugin_dir/include"
+cxx.poptions += "-DODB_GXX_NAME=\"$regex.replace($gxx_name, '\\', '\\\\')\""
+
+## Consumption build ($develop == false).
+#
+
+# Use pregenerated versions in the consumption build.
+#
+libus{odb}: pregenerated/{hxx ixx cxx}{**}: include = (!$develop)
+
+if! $develop
+ cxx.poptions =+ "-I($src_base/pregenerated)" # Note: must come first.
+
+# Distribute pregenerated versions only in the consumption build.
+#
+pregenerated/{hxx ixx cxx}{*}: dist = (!$develop)
+
+#
+##
+
+## Development build ($develop == true).
+#
+
+libus{odb}: {hxx ixx cxx}{options}: include = $develop
+
+if $develop
+ import! [metadata] cli = cli%exe{cli}
+
+# In the development build distribute regenerated {hxx ixx cxx}{options},
+# 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.
+#
+<{hxx ixx cxx}{options}>: cli{options} $cli
+{
+ dist = ($develop ? pregenerated/odb/ : false)
+
+ # Symlink the generated code in src for convenience of development.
+ #
+ backlink = true
+}
+%
+if $develop
+{{
+ options = --include-with-brackets --include-prefix odb --guard-prefix ODB \
+ --generate-file-scanner --generate-specifier --generate-modifier \
+ --generate-description --suppress-undocumented \
+ --cxx-prologue '#include <odb/option-parsers.hxx>'
+
+ $cli $options -o $out_base $path($<[0])
+
+ # If the result differs from the pregenerated version, copy it over.
+ #
+ if diff $src_base/pregenerated/odb/options.hxx $path($>[0]) >- && \
+ diff $src_base/pregenerated/odb/options.ixx $path($>[1]) >- && \
+ diff $src_base/pregenerated/odb/options.cxx $path($>[2]) >-
+ exit
+ end
+
+ cp $path($>[0]) $src_base/pregenerated/odb/options.hxx
+ cp $path($>[1]) $src_base/pregenerated/odb/options.ixx
+ cp $path($>[2]) $src_base/pregenerated/odb/options.cxx
+}}
+
+#
+##
+
+# Pass the copyright notice extracted from the LICENSE file.
+#
+obj{odb}: cxx.poptions += -DODB_COPYRIGHT=\"$copyright\"
+
+# Don't install any of the plugin's headers.
+#
+{hxx ixx txx}{*}: install = false
diff --git a/odb/odb/common-query.cxx b/odb/odb/common-query.cxx
new file mode 100644
index 0000000..0b5d063
--- /dev/null
+++ b/odb/odb/common-query.cxx
@@ -0,0 +1,1440 @@
+// file : odb/common-query.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <odb/common-query.hxx>
+
+using namespace std;
+
+// query_utils
+//
+
+string query_utils::
+depth_suffix (size_t d)
+{
+ if (d != 0)
+ {
+ ostringstream os;
+ os << d;
+ return '_' + os.str ();
+ }
+
+ return string ();
+}
+
+// Collect nested (composite) types as generated by query_columns.
+//
+struct query_nested_types: object_columns_base, virtual context
+{
+ query_nested_types (bool ptr): ptr_ (ptr), in_ptr_ (false), depth_ (0) {}
+
+ virtual void
+ traverse_object (semantics::class_& c)
+ {
+ // We don't want to traverse bases.
+ //
+ names (c);
+ }
+
+ virtual void
+ traverse_composite (semantics::data_member* m, semantics::class_& c)
+ {
+ if (m != 0)
+ {
+ string name (prefix_ + public_name (*m));
+ name += in_ptr_ ? "_column_class" : "_class";
+ name += query_utils::depth_suffix (depth_);
+ name += '_';
+ types.push_back (name);
+
+ depth_++;
+ string p (prefix_);
+ prefix_ = name + "::";
+ object_columns_base::traverse_composite (m, c);
+ prefix_ = p;
+ depth_--;
+ }
+ else
+ object_columns_base::traverse_composite (m, c); // Base
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ // The same logic as in query_columns.
+ //
+ if (inverse (m, key_prefix_))
+ return;
+
+ bool poly_ref (m.count ("polymorphic-ref"));
+
+ if (composite_wrapper (utype (*id_member (c))))
+ {
+ if (ptr_ || poly_ref)
+ object_columns_base::traverse_pointer (m, c);
+ else
+ {
+ in_ptr_ = true;
+ object_columns_base::traverse_pointer (m, c);
+ in_ptr_ = false;
+ }
+ }
+ }
+
+public:
+ strings types;
+
+protected:
+ bool ptr_;
+ bool in_ptr_; // True while we are "inside" an object pointer.
+ string prefix_;
+ size_t depth_;
+};
+
+void query_utils::
+inst_query_columns (bool decl,
+ bool ptr,
+ string const& type,
+ string const& alias,
+ semantics::class_& c)
+{
+ inst_header (decl);
+ os << (ptr ? "pointer_" : "") << "query_columns<" << endl
+ << " " << type << "," << endl
+ << " id_" << db << "," << endl
+ << " " << alias << " >;"
+ << endl;
+
+ // If we are generating extern declarations, we also have to generate
+ // them for all the nested (composite) structs. That's what VC++ needs.
+ //
+ if (decl)
+ {
+ query_nested_types t (ptr);
+ t.traverse (c);
+
+ for (strings::iterator i (t.types.begin ()); i != t.types.end (); ++i)
+ {
+ inst_header (decl, true); // Omit export, GCC doesn't like it.
+ os << (ptr ? "pointer_" : "") << "query_columns<" << endl
+ << " " << type << "," << endl
+ << " id_" << db << "," << endl
+ << " " << alias << " >::" << *i << ";"
+ << endl;
+ }
+ }
+}
+
+// query_tags
+//
+
+void query_tags::
+traverse (semantics::class_& c)
+{
+ if (object (c) || composite (c))
+ {
+ object_columns_base::traverse (c);
+ }
+ else if (view (c))
+ {
+ if (c.get<size_t> ("object-count") != 0)
+ {
+ view_objects& objs (c.get<view_objects> ("objects"));
+
+ for (view_objects::const_iterator i (objs.begin ());
+ i < objs.end ();
+ ++i)
+ {
+ if (i->kind != view_object::object)
+ continue; // Skip tables.
+
+ if (i->alias.empty ())
+ continue;
+
+ generate (i->alias);
+ }
+ }
+ }
+ // Otherwise it is a transient base (of a composite value).
+
+ if (nl_)
+ os << endl;
+}
+
+void query_tags::
+traverse_object (semantics::class_& c)
+{
+ names (c); // We don't want to traverse bases.
+}
+
+void query_tags::
+traverse_composite (semantics::data_member* m, semantics::class_& c)
+{
+ // Base type.
+ //
+ if (m == 0)
+ {
+ object_columns_base::traverse_composite (m, c);
+ return;
+ }
+
+ // Don't generate an empty struct if we don't have any pointers.
+ //
+ if (!has_a (c, test_pointer))
+ return;
+
+ if (nl_)
+ os << endl;
+
+ os << "struct " << public_name (*m) << "_tag" <<
+ query_utils::depth_suffix (depth_)
+ << "{";
+
+ depth_++;
+ object_columns_base::traverse_composite (m, c);
+ depth_--;
+
+ os << "};";
+
+ nl_ = false;
+}
+
+void query_tags::
+traverse_pointer (semantics::data_member& m, semantics::class_&)
+{
+ // Ignore polymorphic id references.
+ //
+ if (m.count ("polymorphic-ref"))
+ return;
+
+ generate (public_name (m));
+}
+
+void query_tags::
+generate (string const& name)
+{
+ os << "struct " << name << "_tag;";
+ nl_ = true;
+}
+
+// query_alias_traits
+//
+
+query_alias_traits::
+query_alias_traits (semantics::class_& c, bool decl)
+ : decl_ (decl), depth_ (0)
+{
+ scope_ = "access::";
+ scope_ += (object (c) ? "object_traits_impl" : "view_traits_impl");
+ scope_ += "< " + class_fq_name (c) + ", id_" + db.string () + " >";
+}
+
+void query_alias_traits::
+traverse_object (semantics::class_& c)
+{
+ // We don't want to traverse bases.
+ //
+ names (c);
+}
+
+void query_alias_traits::
+traverse_composite (semantics::data_member* m, semantics::class_& c)
+{
+ // Base type.
+ //
+ if (m == 0)
+ {
+ object_columns_base::traverse_composite (m, c);
+ return;
+ }
+
+ string old_scope (scope_);
+ scope_ += "::" + public_name (*m) + "_tag" +
+ query_utils::depth_suffix (depth_);
+
+ depth_++;
+ object_columns_base::traverse_composite (m, c);
+ depth_--;
+
+ scope_ = old_scope;
+}
+
+void query_alias_traits::
+traverse_pointer (semantics::data_member& m, semantics::class_& c)
+{
+ // Ignore polymorphic id references.
+ //
+ if (m.count ("polymorphic-ref"))
+ return;
+
+ if (decl_)
+ generate_decl (public_name (m), c);
+ else
+ generate_def (m, c);
+}
+
+void query_alias_traits::
+generate_decl (string const& tag, semantics::class_& c)
+{
+ semantics::class_* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+ semantics::class_* poly_base (poly_derived ? &polymorphic_base (c) : 0);
+
+ if (poly_derived)
+ generate_decl (tag, *poly_base);
+
+ string const& fq_name (class_fq_name (c));
+
+ os << "template <>" << endl
+ << "struct " << exp << "alias_traits<" << endl
+ << " " << fq_name << "," << endl
+ << " id_" << db << "," << endl
+ << " " << scope_ << "::" << tag << "_tag>"
+ << "{";
+
+ if (poly_derived)
+ os << "typedef alias_traits<" << endl
+ << " " << class_fq_name (*poly_base) << "," << endl
+ << " id_" << db << "," << endl
+ << " " << scope_ << "::" << tag << "_tag>" << endl
+ << "base_traits;"
+ << endl;
+
+ // For dynamic multi-database support also generate common traits
+ // alias. Note that the tag type is the same since they all are
+ // derived from object_traits.
+ //
+ if (db != database::common && multi_dynamic)
+ os << "typedef alias_traits<" << endl
+ << " " << fq_name << "," << endl
+ << " id_common," << endl
+ << " " << scope_ << "::" << tag << "_tag>" << endl
+ << "common_traits;"
+ << endl;
+
+ generate_decl_body (); // Table name, etc.
+
+ os << "};";
+}
+
+void query_alias_traits::
+generate_decl_body ()
+{
+}
+
+void query_alias_traits::
+generate_def (semantics::data_member&, semantics::class_&)
+{
+}
+
+void query_alias_traits::
+generate_def (string const&, semantics::class_&, string const&)
+{
+}
+
+// query_columns_base
+//
+
+query_columns_base::
+query_columns_base (semantics::class_& c, bool decl, bool inst)
+ : decl_ (decl), inst_ (inst), depth_ (0)
+{
+ string const& n (class_fq_name (c));
+
+ if (decl)
+ scope_ = "access::object_traits_impl< " + n + ", id_" +
+ db.string () + " >";
+ else
+ scope_ = "query_columns_base< " + n + ", id_" + db.string () + " >";
+}
+
+void query_columns_base::
+traverse_object (semantics::class_& c)
+{
+ // We don't want to traverse bases.
+ //
+ names (c);
+}
+
+void query_columns_base::
+traverse_composite (semantics::data_member* m, semantics::class_& c)
+{
+ // Base type.
+ //
+ if (m == 0)
+ {
+ object_columns_base::traverse_composite (m, c);
+ return;
+ }
+
+ // Don't generate an empty struct if we don't have any pointers.
+ //
+ if (!has_a (c, test_pointer))
+ return;
+
+ string name (public_name (*m));
+ string dsuffix (query_utils::depth_suffix (depth_));
+
+ if (decl_)
+ {
+ os << "// " << name << endl
+ << "//" << endl
+ << "struct " << name << "_base" << dsuffix << '_'
+ << "{";
+
+ string old_scope (scope_);
+ scope_ += "::" + name + "_tag" + dsuffix;
+
+ depth_++;
+ object_columns_base::traverse_composite (m, c);
+ depth_--;
+
+ scope_ = old_scope;
+
+ os << "};";
+ }
+ else
+ {
+ string old_scope (scope_);
+ scope_ += "::" + name + "_base" + dsuffix + '_';
+
+ depth_++;
+ object_columns_base::traverse_composite (m, c);
+ depth_--;
+
+ scope_ = old_scope;
+ }
+}
+
+void query_columns_base::
+traverse_pointer (semantics::data_member& m, semantics::class_& c)
+{
+ // Ignore polymorphic id references.
+ //
+ if (m.count ("polymorphic-ref"))
+ return;
+
+ string name (public_name (m));
+ string const& fq_name (class_fq_name (c));
+ bool inv (inverse (m, key_prefix_));
+
+ if (decl_)
+ {
+ os << "// " << name << endl
+ << "//" << endl;
+
+ os << "typedef" << endl
+ << "odb::alias_traits<" << endl
+ << " " << fq_name << "," << endl
+ << " id_" << db << "," << endl
+ << " " << scope_ << "::" << name << "_tag>" << endl
+ << name << "_alias_;"
+ << endl;
+
+ if (inv)
+ {
+ os << "typedef" << endl
+ << "odb::query_pointer<" << endl
+ << " odb::pointer_query_columns<" << endl
+ << " " << fq_name << "," << endl
+ << " id_" << db << "," << endl
+ << " " << name << "_alias_ > >" << endl
+ << name << "_type_ ;"
+ << endl
+ << "static " << const_ << name << "_type_ " << name << ";"
+ << endl;
+ }
+ }
+ else if (inst_)
+ {
+ generate_inst (m, c);
+ }
+ else
+ {
+ // Generate explicit template instantiation directive for the
+ // pointed-to pointer_query_columns.
+ //
+ if (multi_dynamic)
+ generate_inst (m, c);
+
+ if (inv)
+ os << const_ << scope_ << "::" << name << "_type_" << endl
+ << scope_ << "::" << name << ";"
+ << endl;
+ }
+}
+
+void query_columns_base::
+generate_inst (semantics::data_member& m, semantics::class_& c)
+{
+ string name (public_name (m));
+ string const& fq_name (class_fq_name (c));
+
+ string alias (scope_ + "::" + name + "_alias_");
+
+ // Instantiate base [pointer_]query_columns.
+ //
+ {
+ instance<query_columns_base_insts> b (true, inst_, alias, true);
+ traversal::inherits i (*b);
+ inherits (c, i);
+ }
+
+ // If the pointed-to class has no pointers of its own then
+ // pointer_query_columns just derives from query_columns and
+ // that's what we need to instantiate.
+ //
+ inst_query_columns (inst_,
+ has_a (c, test_pointer | include_base),
+ fq_name,
+ alias,
+ c);
+}
+
+// query_columns
+//
+
+query_columns::
+query_columns (bool decl, bool ptr, semantics::class_& c)
+ : decl_ (decl), ptr_ (ptr), poly_ref_ (false), in_ptr_ (false),
+ fq_name_ (class_fq_name (c)),
+ resue_abstract_ (abstract (c) && !polymorphic (c)),
+ depth_ (0)
+{
+}
+
+void query_columns::
+traverse_object (semantics::class_& c)
+{
+ // We don't want to traverse bases.
+ //
+ names (c);
+}
+
+void query_columns::
+traverse_composite (semantics::data_member* m, semantics::class_& c)
+{
+ // Base type.
+ //
+ if (m == 0)
+ {
+ object_columns_base::traverse_composite (m, c);
+ return;
+ }
+
+ // Use _class_ instead of _type_ to avoid potential clashes between
+ // the class and member names.
+ //
+ string name (public_name (*m));
+ string suffix (in_ptr_ ? "_column_class" : "_class");
+
+ // Add depth to the nested composite to avoid potential name conflicts
+ // in situations like these:
+ //
+ // struct inner { ... };
+ // struct outer { inner value; };
+ // struct object { outer value; }
+ //
+ string dsuffix (query_utils::depth_suffix (depth_));
+ suffix += dsuffix;
+ suffix += '_';
+
+ depth_++;
+
+ if (decl_)
+ {
+ os << "// " << name << endl
+ << "//" << endl
+ << "struct ";
+
+ // For some bizarre reason VC++ needs the export directive for
+ // a type nested in an (exported) template. This appears not
+ // to cause any problems for GCC.
+ //
+ // We only generate the export directive if we are also
+ // explicitly instantiating the query_columns templates.
+ //
+ if (multi_dynamic && !resue_abstract_)
+ os << exp;
+
+ os << name << suffix;
+
+ // Derive from the base in query_columns_base. It contains columns
+ // data for the pointer members.
+ //
+ if (!ptr_ && !poly_ref_ && has_a (c, test_pointer))
+ os << ": " << name << "_base" << dsuffix << '_';
+
+ os << "{";
+
+ if (!const_.empty ())
+ os << name << suffix << " ()" // Need user-defined default c-tor for
+ << "{" // initialization of static const.
+ << "}";
+
+ object_columns_base::traverse_composite (m, c);
+
+ os << "};";
+
+ if (!in_ptr_)
+ os << "static " << const_ << name << suffix << " " << name << ";"
+ << endl;
+ }
+ else
+ {
+ // Handle nested members first.
+ //
+ string old_scope (scope_);
+ scope_ += "::" + name + suffix;
+
+ object_columns_base::traverse_composite (m, c);
+
+ scope_ = old_scope;
+
+ // Composite member. Note that here we don't use suffix for the in-
+ // pointer case because the actual pointer column type derives from
+ // the composite column type (dual interface; see traverse_pointer()
+ // below).
+ //
+ string tmpl (ptr_ ? "pointer_query_columns" : "query_columns");
+ tmpl += "< " + fq_name_ + ", id_" + db.string () + ", A >" + scope_;
+
+ os << "template <typename A>" << endl
+ << const_ << "typename " << tmpl << "::" << name <<
+ (in_ptr_ ? string ("_type_") : suffix) << endl
+ << tmpl << "::" << name << ";"
+ << endl;
+ }
+
+ depth_--;
+}
+
+void query_columns::
+column_ctor (string const&, string const&, string const&)
+{
+}
+
+void query_columns::
+column_common (semantics::data_member& m,
+ string const& type,
+ string const&,
+ string const& suffix)
+{
+ string name (public_name (m));
+
+ if (decl_)
+ {
+ os << "// " << name << endl
+ << "//" << endl;
+
+ os << "typedef odb::query_column< " << type << " > " << name <<
+ suffix << ";"
+ << endl;
+ }
+ else
+ {
+ // Note that here we don't use suffix.
+ //
+ string tmpl (ptr_ ? "pointer_query_columns" : "query_columns");
+ tmpl += "< " + fq_name_ + ", id_" + db.string () + ", A >" + scope_;
+
+ os << "template <typename A>" << endl
+ << const_ << "typename " << tmpl << "::" << name << "_type_" << endl
+ << tmpl << "::" << name << ";"
+ << endl;
+ }
+}
+
+bool query_columns::
+traverse_column (semantics::data_member& m, string const& column, bool)
+{
+ semantics::names* hint;
+ semantics::type* t (&utype (m, hint));
+
+ // Unwrap it if it is a wrapper.
+ //
+ if (semantics::type* wt = wrapper (*t, hint))
+ t = &utype (*wt, hint);
+
+ column_common (m, t->fq_name (hint), column);
+
+ if (decl_)
+ {
+ string name (public_name (m));
+
+ os << "static " << const_ << name << "_type_ " << name << ";"
+ << endl;
+ }
+
+ return true;
+}
+
+void query_columns::
+traverse_pointer (semantics::data_member& m, semantics::class_& c)
+{
+ // If this is for the pointer_query_columns and the member is not
+ // inverse, then create the normal member corresponding to the id
+ // column. This will allow the user to check it for NULL or to
+ // compare ids. In case this is for query_columns, then for the
+ // inverse member everything has been generated in query_columns_base.
+ //
+ if (inverse (m, key_prefix_))
+ return;
+
+ // If we ignore polymorphic references, then a view that uses a custom
+ // join condition based on id will use the id column from the base
+ // table. But the base table hasn't been joined yet. To resolve this
+ // we will generate the id member that points to our column.
+ //
+ poly_ref_ = m.count ("polymorphic-ref");
+
+ string name (public_name (m));
+
+ data_member_path& id (*id_member (c));
+ semantics::names* hint;
+ semantics::type& t (utype (id, hint));
+
+ if (composite_wrapper (t))
+ {
+ // Composite id.
+ //
+
+ // For pointer_query_columns and poly refs generate normal composite
+ // mapping.
+ //
+ if (ptr_ || poly_ref_)
+ object_columns_base::traverse_pointer (m, c);
+ else
+ {
+ // If this is a non-inverse relationship, then make the column have
+ // a dual interface: that of an object pointer and of an id column.
+ // The latter allows the user to, for example, use the is_null()
+ // test in a natural way. For inverse relationships there is no
+ // column and so the column interface is not available.
+ //
+ in_ptr_ = true;
+ object_columns_base::traverse_pointer (m, c);
+ in_ptr_ = false;
+
+ if (decl_)
+ {
+ os << "typedef" << endl
+ << "odb::query_pointer<" << endl
+ << " odb::pointer_query_columns<" << endl
+ << " " << class_fq_name (c) << "," << endl
+ << " id_" << db << "," << endl
+ << " " << name << "_alias_ > >" << endl
+ << name << "_pointer_type_;"
+ << endl;
+
+ os << "struct " << name << "_type_: " <<
+ name << "_pointer_type_, " <<
+ name << "_column_class" << query_utils::depth_suffix (depth_) << '_'
+ << "{";
+
+ if (!const_.empty ())
+ os << name << "_type_ ()" // Need user-defined default c-tor for
+ << "{" // initialization of static const.
+ << "}";
+
+ os << "};";
+
+ os << "static " << const_ << name << "_type_ " << name << ";"
+ << endl;
+ }
+ }
+ }
+ else
+ {
+ // Simple id.
+ //
+ string type (t.fq_name (hint));
+ string col (column_name (m, key_prefix_, default_name_, column_prefix_));
+
+ // For pointer_query_columns and poly refs generate normal column mapping.
+ //
+ if (ptr_ || poly_ref_)
+ column_common (m, type, col);
+ else
+ {
+ // If this is a non-inverse relationship, then make the column have
+ // a dual interface: that of an object pointer and of an id column.
+ // The latter allows the user to, for example, use the is_null()
+ // test in a natural way. For inverse relationships there is no
+ // column and so the column interface is not available.
+ //
+ column_common (m, type, col, "_column_type_");
+
+ if (decl_)
+ {
+ os << "typedef" << endl
+ << "odb::query_pointer<" << endl
+ << " odb::pointer_query_columns<" << endl
+ << " " << class_fq_name (c) << "," << endl
+ << " id_" << db << "," << endl
+ << " " << name << "_alias_ > >" << endl
+ << name << "_pointer_type_;"
+ << endl;
+
+ os << "struct " << name << "_type_: " <<
+ name << "_pointer_type_, " << name << "_column_type_"
+ << "{";
+
+ column_ctor (type, name + "_type_", name + "_column_type_");
+
+ os << "};";
+ }
+ }
+
+ if (decl_)
+ os << "static " << const_ << name << "_type_ " << name << ";"
+ << endl;
+ }
+
+ poly_ref_ = false;
+}
+
+// query_columns_bases
+//
+
+void query_columns_bases::
+traverse (type& c)
+{
+ // Ignore transient bases. Not used for views.
+ //
+ if (!object (c))
+ return;
+
+ if (first_)
+ {
+ os << ":" << endl
+ << " ";
+ first_ = false;
+ }
+ else
+ os << "," << endl
+ << " ";
+
+ os << (ptr_ ? "pointer_query_columns" : "query_columns") << "< " <<
+ class_fq_name (c) << ", id_" << db << ", ";
+
+ // If our base is polymorphic, then it has its own table/alias.
+ //
+ if (polymorphic (c))
+ os << "typename A::base_traits";
+ else
+ os << "A";
+
+ os << " >";
+}
+
+// query_columns_base_aliases
+//
+
+void query_columns_base_aliases::
+traverse (type& c)
+{
+ // Ignore transient bases. Not used for views.
+ //
+ if (!object (c))
+ return;
+
+ string const& name (class_name (c));
+
+ os << "// " << name << endl
+ << "//" << endl
+ << "typedef " << (ptr_ ? "pointer_query_columns" : "query_columns") <<
+ "< " << class_fq_name (c) << ", id_" << db << ", ";
+
+ if (polymorphic (c))
+ os << "typename A::base_traits";
+ else
+ os << "A";
+
+ os << " > " << name << ";"
+ << endl;
+}
+
+// query_columns_base_insts
+//
+
+query_columns_base_insts::
+query_columns_base_insts (bool test_ptr,
+ bool decl,
+ string const& alias,
+ bool poly)
+ : test_ptr_ (test_ptr), decl_ (decl), alias_ (alias), poly_ (poly)
+{
+ *this >> inherits_ >> *this;
+}
+
+query_columns_base_insts::
+query_columns_base_insts (query_columns_base_insts const& x)
+ : context (), // @@ -Wextra
+ test_ptr_ (x.test_ptr_),
+ decl_ (x.decl_),
+ alias_ (x.alias_),
+ poly_ (x.poly_)
+{
+ *this >> inherits_ >> *this;
+}
+
+void query_columns_base_insts::
+traverse (type& c)
+{
+ if (!object (c))
+ return;
+
+ bool poly (polymorphic (c));
+ if (poly && (poly != poly_))
+ return;
+
+ bool ptr (has_a (c, test_pointer | include_base));
+
+ string old_alias;
+ if (poly)
+ {
+ old_alias = alias_;
+ alias_ += "::base_traits";
+ }
+
+ // Instantiate bases recursively.
+ //
+ inherits (c, inherits_);
+
+ inst_query_columns (decl_,
+ test_ptr_ && ptr,
+ class_fq_name (c),
+ alias_,
+ c);
+
+ if (!test_ptr_ && ptr)
+ inst_query_columns (decl_, true, class_fq_name (c), alias_, c);
+
+ if (poly)
+ alias_ = old_alias;
+}
+
+// query_columns_type
+//
+
+void query_columns_type::
+traverse (type& c)
+{
+ string const& type (class_fq_name (c));
+
+ if (ptr_)
+ {
+ os << "template <typename A>" << endl
+ << "struct pointer_query_columns< " << type << ", id_" << db << ", A >";
+
+ // If we don't have pointers (in the whole hierarchy), then
+ // pointer_query_columns and query_columns are the same.
+ //
+ if (!has_a (c, test_pointer | include_base))
+ {
+ os << ":" << endl
+ << " query_columns< " << type << ", id_" << db << ", A >"
+ << "{"
+ << "};";
+ }
+ else
+ {
+ {
+ instance<query_columns_bases> b (ptr_);
+ traversal::inherits i (*b);
+ inherits (c, i);
+ }
+
+ os << "{";
+
+ {
+ instance<query_columns_base_aliases> b (ptr_);
+ traversal::inherits i (*b);
+ inherits (c, i);
+ }
+
+ {
+ bool true_ (true);
+ instance<query_columns> t (true_, ptr_, c); //@@ forwarding
+ t->traverse (c);
+ }
+
+ os << "};";
+
+ generate_impl (c);
+ }
+ }
+ else if (decl_)
+ {
+ bool has_ptr (has_a (c, test_pointer | exclude_base));
+
+ if (has_ptr)
+ {
+ // Generate aliases.
+ //
+ {
+ bool true_ (true); //@@ (im)perfect forwarding
+ instance<query_alias_traits> t (c, true_);
+ t->traverse (c);
+ }
+
+ // This class contains everything for inverse pointers and
+ // aliases for non-inverse ones. It doesn't depend on the
+ // table alias (A) template argument.
+ //
+ os << "template <>" << endl
+ << "struct " << exp << "query_columns_base< " << type << ", " <<
+ "id_" << db << " >"
+ << "{";
+
+ bool true_ (true); //@@ (im)perfect forwarding.
+ bool false_ (false);
+ instance<query_columns_base> t (c, true_, false_);
+ t->traverse (c);
+
+ os << "};";
+ }
+
+ os << "template <typename A>" << endl
+ << "struct query_columns< " << type << ", id_" << db << ", A >";
+
+ if (has_ptr)
+ os << ":" << endl
+ << " query_columns_base< " << type << ", id_" << db << " >";
+
+ {
+ instance<query_columns_bases> b (ptr_, !has_ptr);
+ traversal::inherits i (*b);
+ inherits (c, i);
+ }
+
+ os << "{";
+
+ {
+ instance<query_columns_base_aliases> b (ptr_);
+ traversal::inherits i (*b);
+ inherits (c, i);
+ }
+
+ {
+ instance<query_columns> t (decl_, ptr_, c);
+ t->traverse (c);
+ }
+
+ os << "};";
+
+ generate_impl (c);
+ }
+ else if (inst_)
+ {
+ // If we have the extern symbol, generate extern template declarations.
+ //
+ // Without a declaration of explicit template instantiation Clang issues
+ // -Wundefined-var-template. Note that extern template is only available
+ // since C++11 and this only appears to be an issue in dynamic multi-
+ // database support for id_common.
+ //
+ // Note also that this break our support for multi-file circular
+ // dependencies (see odb-tests/common/circule/multiple/).
+ //
+ if (!ext.empty () ||
+ (multi_dynamic &&
+ db == database::common &&
+ options.std () >= cxx_version::cxx11))
+ {
+ bool has_ptr (has_a (c, test_pointer | exclude_base));
+ bool reuse_abst (abstract (c) && !polymorphic (c));
+
+ if (has_ptr || !reuse_abst)
+ {
+ const string& guard (
+ !ext.empty ()
+ ? ext
+ : make_guard ("ODB_" + db.string () + "_QUERY_COLUMNS_DEF"));
+
+ os << (!ext.empty () ? "#ifdef " : "#ifndef ") << guard << endl
+ << endl;
+
+ if (has_ptr)
+ {
+ bool true_ (true); //@@ (im)perfect forwarding.
+ bool false_ (false);
+
+ instance<query_columns_base> t (c, false_, true_);
+ t->traverse (c);
+ }
+
+ // Don't generate it for reuse-abstract classes.
+ //
+ if (!reuse_abst)
+ generate_inst (c);
+
+ os << "#endif // " << guard << endl
+ << endl;
+ }
+ }
+ }
+ else
+ {
+ bool has_ptr (has_a (c, test_pointer | exclude_base));
+
+ // Generate alias_traits specializations. While the class
+ // is generated even if our base has a pointer, there is
+ // not source code if we don't have pointers ourselves.
+ //
+ if (has_ptr)
+ {
+ bool false_ (false); //@@ (im)perfect forwarding
+ instance<query_alias_traits> t (c, false_);
+ t->traverse (c);
+ }
+
+ // query_columns_base
+ //
+ if (has_ptr)
+ {
+ bool false_ (false); //@@ (im)perfect forwarding.
+ instance<query_columns_base> t (c, false_, false_);
+ t->traverse (c);
+ }
+
+ // Explicit template instantiations. Don't generate it for reuse-
+ // abstract classes.
+ //
+ if (multi_dynamic && (!abstract (c) || polymorphic (c)))
+ generate_inst (c);
+ }
+}
+
+void query_columns_type::
+generate_impl (type& c)
+{
+ string guard;
+
+ // Exclude definitions (they will be explicitly instantiated once in
+ // the source file) unless we have the extern symbol. In this case
+ // the extern template declaration will make sure we don't get
+ // instantiations in multiple places and we will avoid the VC++
+ // warning C4661 (no definition provided).
+ //
+ if (multi_dynamic && ext.empty ())
+ {
+ guard = make_guard ("ODB_" + db.string () + "_QUERY_COLUMNS_DEF");
+
+ os << "#ifdef " << guard << endl
+ << endl;
+ }
+
+ {
+ bool false_ (false);
+ instance<query_columns> t (false_, ptr_, c);
+ t->traverse (c);
+ }
+
+ if (!guard.empty ())
+ os << "#endif // " << guard << endl
+ << endl;
+}
+
+void query_columns_type::
+generate_inst (type& c)
+{
+ string const& type (class_fq_name (c));
+
+ // Explicit template instantiations. Here is what we need to
+ // instantiate:
+ //
+ // 1. Reuse inheritance bases all the way to the ultimate base.
+ // Unlike poly inheritance, reuse inheritance uses the table
+ // alias of the derived type. Note that bases can have object
+ // pointers of their own but their types have already been
+ // instantiated by step 3 below performed for the base object.
+ //
+ // 2. Object pointers. Note that while object pointers cannot have
+ // their own pointers, they can have reuse inheritance bases.
+ //
+ // 3. The query_columns class for the table itself.
+ //
+ // We also need to repeat these steps for pointer_query_columns
+ // since it is used by views.
+ //
+ string alias ("access::object_traits_impl< " + type + ", id_" +
+ db.string () + " >");
+
+ // 1
+ //
+ {
+ instance<query_columns_base_insts> b (false, inst_, alias, false);
+ traversal::inherits i (*b);
+ inherits (c, i);
+ }
+
+ // 2: Handled by query_columns_base (which is where we generate
+ // all the aliases for object pointers).
+ //
+
+ // 3
+ //
+ inst_query_columns (inst_, false, type, alias, c);
+
+ if (has_a (c, test_pointer | exclude_base))
+ inst_query_columns (inst_, true, type, alias, c);
+}
+
+// view_query_columns_type
+//
+
+void view_query_columns_type::
+traverse (type& c)
+{
+ if (decl_)
+ generate_decl (c);
+ else
+ generate_def (c);
+}
+
+void view_query_columns_type::
+generate_decl (type& c)
+{
+ string const& type (class_fq_name (c));
+ size_t obj_count (c.get<size_t> ("object-count"));
+ view_objects& objs (c.get<view_objects> ("objects"));
+
+ // Generate alias_traits specializations.
+ //
+ {
+ bool true_ (true); //@@ (im)perfect forwarding
+ instance<query_alias_traits> at (c, true_);
+
+ for (view_objects::const_iterator i (objs.begin ()); i < objs.end (); ++i)
+ {
+ if (i->kind != view_object::object)
+ continue; // Skip tables.
+
+ if (i->alias.empty ())
+ continue;
+
+ semantics::class_& o (*i->obj);
+ qname const& t (table_name (o));
+
+ // Check that the alias is not the same as the table name
+ // (if this is a polymorphic object, then the alias is just
+ // a prefix).
+ //
+ if (polymorphic (o) || t.qualified () || i->alias != t.uname ())
+ at->generate_decl (i->alias, o);
+ }
+ }
+
+ // If we have the extern symbol, generate extern template declarations.
+ // Do it before query_columns since the inheritance will trigger
+ // instantiation and we won't be able to change visibility (GCC).
+ //
+ // See query_columns_type::traverse() for background.
+ //
+ if (obj_count != 0 && multi_dynamic &&
+ (!ext.empty () ||
+ (multi_dynamic &&
+ db == database::common &&
+ options.std () >= cxx_version::cxx11)))
+ {
+ const string& guard (
+ !ext.empty ()
+ ? ext
+ : make_guard ("ODB_" + db.string () + "_QUERY_COLUMNS_DEF"));
+
+ os << (!ext.empty () ? "#ifdef " : "#ifndef ") << guard << endl
+ << endl;
+
+ generate_inst (c);
+
+ os << "#endif // " << guard << endl
+ << endl;
+ }
+
+ // query_columns
+ //
+ os << "struct " << exp << "access::view_traits_impl< " << type << ", " <<
+ "id_" << db << " >::query_columns";
+
+ if (obj_count > 1)
+ {
+ os << "{";
+
+ for (view_objects::const_iterator i (objs.begin ()); i < objs.end (); ++i)
+ {
+ if (i->kind != view_object::object)
+ continue; // Skip tables.
+
+ bool alias (!i->alias.empty ());
+ semantics::class_& o (*i->obj);
+ string const& oname (alias ? i->alias : class_name (o));
+ string const& otype (class_fq_name (o));
+ qname const& table (table_name (o));
+
+ os << "// " << oname << endl
+ << "//" << endl
+ << "typedef" << endl
+ << "odb::pointer_query_columns<" << endl
+ << " " << otype << "," << endl
+ << " id_" << db << "," << endl;
+
+ if (alias && (polymorphic (o) ||
+ table.qualified () ||
+ i->alias != table.uname ()))
+ {
+ os << " odb::alias_traits< " << otype << "," << endl
+ << " id_" << db << "," << endl
+ << " access::view_traits_impl< " << type << ", id_" << db <<
+ " >::" << i->alias << "_tag> >" << endl;
+ }
+ else
+ os << " odb::access::object_traits_impl< " << otype << ", id_" <<
+ db << " > >" << endl;
+
+ os << oname << ";"
+ << endl;
+ }
+
+ os << "};";
+ }
+ else
+ {
+ // For a single object view we generate a shortcut without
+ // an intermediate typedef.
+ //
+ view_object const* vo (0);
+ for (view_objects::const_iterator i (objs.begin ());
+ vo == 0 && i < objs.end ();
+ ++i)
+ {
+ if (i->kind == view_object::object)
+ vo = &*i;
+ }
+
+ bool alias (!vo->alias.empty ());
+ semantics::class_& o (*vo->obj);
+ string const& otype (class_fq_name (o));
+ qname const& table (table_name (o));
+
+ os << ":" << endl
+ << " odb::pointer_query_columns<" << endl
+ << " " << otype << "," << endl
+ << " id_" << db << "," << endl;
+
+ if (alias && (polymorphic (o) ||
+ table.qualified () ||
+ vo->alias != table.uname ()))
+ {
+ os << " odb::alias_traits<" << endl
+ << " " << otype << "," << endl
+ << " id_" << db << "," << endl
+ << " access::view_traits_impl< " << type << ", id_" <<
+ db << " >::" << vo->alias << "_tag> >";
+ }
+ else
+ os << " odb::access::object_traits_impl< " << otype <<
+ ", id_" << db << " > >";
+
+ os << "{"
+ << "};";
+ }
+}
+
+void view_query_columns_type::
+generate_def (type& c)
+{
+ view_objects& objs (c.get<view_objects> ("objects"));
+
+ // Generate alias_traits specializations.
+ //
+ {
+ bool false_ (false); //@@ (im)perfect forwarding
+ instance<query_alias_traits> at (c, false_);
+
+ for (view_objects::const_iterator i (objs.begin ());
+ i < objs.end ();
+ ++i)
+ {
+ if (i->kind != view_object::object)
+ continue; // Skip tables.
+
+ if (i->alias.empty ())
+ continue;
+
+ semantics::class_& o (*i->obj);
+ qname const& t (table_name (o));
+
+ // Check that the alias is not the same as the table name
+ // (if this is a polymorphic object, then the alias is just
+ // a prefix).
+ //
+ if (polymorphic (o) || t.qualified () || i->alias != t.uname ())
+ {
+ at->generate_def (i->alias, o, i->alias);
+ }
+ }
+ }
+
+ if (multi_dynamic)
+ generate_inst (c);
+}
+
+void view_query_columns_type::
+generate_inst (type& c)
+{
+ string const& type (class_fq_name (c));
+ view_objects& objs (c.get<view_objects> ("objects"));
+
+ string traits ("access::view_traits_impl< " + type + ", id_" +
+ db.string () + " >");
+
+ // Instantiate [pointer_]query_columns.
+ //
+ for (view_objects::const_iterator i (objs.begin ());
+ i < objs.end ();
+ ++i)
+ {
+ if (i->kind != view_object::object)
+ continue; // Skip tables.
+
+ if (i->alias.empty ())
+ continue; // Instantiated by object.
+
+ semantics::class_& o (*i->obj);
+ qname const& t (table_name (o));
+
+ // Check that the alias is not the same as the table name
+ // (if this is a polymorphic object, then the alias is just
+ // a prefix).
+ //
+ if (polymorphic (o) || t.qualified () || i->alias != t.uname ())
+ {
+ string const& otype (class_fq_name (o));
+ string alias ("odb::alias_traits<\n"
+ " " + otype + ",\n"
+ " id_" + db.string () + ",\n"
+ " " + traits + "::" + i->alias + "_tag>");
+
+ // Instantiate base [pointer_]query_columns.
+ //
+ {
+ instance<query_columns_base_insts> b (true, decl_, alias, true);
+ traversal::inherits i (*b);
+ inherits (o, i);
+ }
+
+ // If the pointed-to class has no pointers of its own then
+ // pointer_query_columns just derives from query_columns and
+ // that's what we need to instantiate.
+ //
+ inst_query_columns (decl_,
+ has_a (o, test_pointer | include_base),
+ otype,
+ alias,
+ o);
+ }
+ }
+}
diff --git a/odb/odb/common-query.hxx b/odb/odb/common-query.hxx
new file mode 100644
index 0000000..e90dd69
--- /dev/null
+++ b/odb/odb/common-query.hxx
@@ -0,0 +1,279 @@
+// file : odb/common-query.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_COMMON_QUERY_HXX
+#define ODB_COMMON_QUERY_HXX
+
+#include <odb/common.hxx>
+#include <odb/context.hxx>
+
+//
+// Query-related generators.
+//
+
+struct query_utils: virtual context
+{
+ void
+ inst_query_columns (bool decl, // Extern declaration or instanatiation.
+ bool ptr, // pointer_query_columns or query_columns
+ string const& type, // Object fq-type.
+ string const& alias, // Table alias.
+ semantics::class_&); // Traverse for nested structs.
+
+ static string
+ depth_suffix (size_t);
+};
+
+// Generate query tags for pointers in this object.
+//
+struct query_tags: object_columns_base, virtual context
+{
+ typedef query_tags base;
+
+ query_tags (): nl_ (false), depth_ (0) {}
+
+ virtual void
+ traverse (semantics::class_&);
+
+ virtual void
+ traverse_object (semantics::class_&);
+
+ virtual void
+ traverse_composite (semantics::data_member*, semantics::class_&);
+
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&);
+
+ virtual void
+ generate (string const& name);
+
+private:
+ bool nl_;
+ size_t depth_;
+};
+
+// Generate alias_traits specializations for pointers in this objects.
+//
+struct query_alias_traits: object_columns_base, virtual context
+{
+ typedef query_alias_traits base;
+
+ query_alias_traits (semantics::class_&, bool decl);
+
+ virtual void
+ traverse_object (semantics::class_&);
+
+ virtual void
+ traverse_composite (semantics::data_member*, semantics::class_&);
+
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&);
+
+ virtual void
+ generate_decl (string const& tag, semantics::class_&);
+
+ virtual void
+ generate_decl_body ();
+
+ virtual void
+ generate_def (semantics::data_member&, semantics::class_&);
+
+ virtual void
+ generate_def (string const& tag, semantics::class_&, string const& alias);
+
+protected:
+ bool decl_;
+ string scope_;
+ size_t depth_;
+};
+
+// Generate query columns in the query_columns_base class.
+//
+struct query_columns_base: object_columns_base, query_utils
+{
+ typedef query_columns_base base;
+
+ // If inst is true, then we generate extern template declarations
+ // in the header.
+ //
+ query_columns_base (semantics::class_&, bool decl, bool inst);
+
+ virtual void
+ traverse_object (semantics::class_&);
+
+ virtual void
+ traverse_composite (semantics::data_member*, semantics::class_&);
+
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&);
+
+ virtual void
+ generate_inst (semantics::data_member&, semantics::class_&);
+
+protected:
+ bool decl_;
+ bool inst_;
+ string const_; // Const prefix or empty.
+ string scope_;
+ size_t depth_;
+};
+
+// Generate query columns in the query_columns or pointer_query_columns
+// class.
+//
+struct query_columns: object_columns_base, virtual context
+{
+ typedef query_columns base;
+
+ query_columns (bool decl, bool ptr, semantics::class_&);
+
+ virtual void
+ column_ctor (string const& type, string const& name, string const& base);
+
+ virtual void
+ traverse_object (semantics::class_&);
+
+ virtual void
+ traverse_composite (semantics::data_member*, semantics::class_&);
+
+ virtual void
+ column_common (semantics::data_member&,
+ string const& type,
+ string const& column,
+ string const& suffix = "_type_");
+
+ virtual bool
+ traverse_column (semantics::data_member&, string const&, bool);
+
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&);
+
+protected:
+ bool decl_;
+ bool ptr_;
+ bool poly_ref_;
+ string const_; // Const prefix or empty.
+ bool in_ptr_; // True while we are "inside" an object pointer.
+ string fq_name_;
+ bool resue_abstract_; // Object is reuse-abstract.
+ string scope_;
+ size_t depth_;
+};
+
+// Generate the list of base classes for the query_columns or
+// pointer_query_columns class.
+//
+struct query_columns_bases: traversal::class_, virtual context
+{
+ typedef query_columns_bases base;
+
+ query_columns_bases (bool ptr, bool first = true)
+ : ptr_ (ptr), first_ (first) {}
+
+ virtual void
+ traverse (type&);
+
+private:
+ bool ptr_;
+ bool first_;
+};
+
+// Generate the base class aliases (typedefs). These can be used to
+// resolve member ambiguities.
+//
+struct query_columns_base_aliases: traversal::class_, virtual context
+{
+ typedef query_columns_base_aliases base;
+
+ query_columns_base_aliases (bool ptr): ptr_ (ptr) {}
+
+ virtual void
+ traverse (type&);
+
+private:
+ bool ptr_;
+};
+
+// Generate explicit instantiations of base classes.
+//
+struct query_columns_base_insts: traversal::class_, query_utils
+{
+ typedef query_columns_base_insts base;
+
+ query_columns_base_insts (bool test_ptr,
+ bool decl,
+ string const& alias,
+ bool poly); // Traverse polymorphic bases.
+ query_columns_base_insts (query_columns_base_insts const&);
+
+ virtual void
+ traverse (type&);
+
+private:
+ bool test_ptr_;
+ bool decl_;
+ string alias_;
+ bool poly_;
+ traversal::inherits inherits_;
+};
+
+// Generate the query_columns_base/query_columns or pointer_query_columns
+// classes for objects.
+//
+struct query_columns_type: traversal::class_, query_utils
+{
+ typedef query_columns_type base;
+
+ // Depending on the ptr argument, generate query_columns or
+ // pointer_query_columns specialization. The latter is used
+ // for object pointers where we don't support nested pointers.
+ //
+ // If decl is false then generate definitions (only needed for
+ // query_columns so ptr should be false).
+ //
+ // If inst if true, then generate extern template declarations
+ // in the header (ptr and decl should be false).
+ //
+ query_columns_type (bool ptr, bool decl, bool inst)
+ : ptr_ (ptr), decl_ (decl), inst_ (inst) {}
+
+ virtual void
+ traverse (type&);
+
+ virtual void
+ generate_impl (type&);
+
+ virtual void
+ generate_inst (type&);
+
+public:
+ bool ptr_;
+ bool decl_;
+ bool inst_;
+};
+
+// Generate the query_columns class for views.
+//
+struct view_query_columns_type: traversal::class_, query_utils
+{
+ typedef view_query_columns_type base;
+
+ view_query_columns_type (bool decl): decl_ (decl) {}
+
+ virtual void
+ traverse (type&);
+
+ void
+ generate_decl (type&);
+
+ void
+ generate_def (type&);
+
+ void
+ generate_inst (type&);
+
+public:
+ bool decl_;
+};
+
+#endif // ODB_COMMON_QUERY_HXX
diff --git a/odb/odb/common.cxx b/odb/odb/common.cxx
new file mode 100644
index 0000000..63e49ad
--- /dev/null
+++ b/odb/odb/common.cxx
@@ -0,0 +1,584 @@
+// file : odb/common.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/common.hxx>
+
+using namespace std;
+
+//
+// object_members_base
+//
+
+void object_members_base::
+traverse_simple (semantics::data_member&)
+{
+}
+
+void object_members_base::
+traverse_pointer (semantics::data_member& m, semantics::class_& c)
+{
+ if (!view_member (m)) // Not really "as if" pointed-to id member.
+ traverse_member (m, utype (*id_member (c)));
+}
+
+void object_members_base::
+traverse_composite (semantics::data_member*, semantics::class_& c)
+{
+ inherits (c);
+ names (c);
+}
+
+void object_members_base::
+traverse_composite_wrapper (semantics::data_member* m,
+ semantics::class_& c,
+ semantics::type*)
+{
+ traverse_composite (m, c);
+}
+
+void object_members_base::
+traverse_container (semantics::data_member&, semantics::type&)
+{
+}
+
+void object_members_base::
+traverse_object (semantics::class_& c)
+{
+ inherits (c);
+ names (c);
+}
+
+void object_members_base::
+traverse_view (semantics::class_& c)
+{
+ // A view has no bases.
+ //
+ names (c);
+}
+
+void object_members_base::
+traverse (semantics::class_& c)
+{
+ class_kind_type k (class_kind (c));
+
+ if (k == class_other)
+ {
+ // Ignore transient bases.
+ //
+ return;
+ }
+ else if (k == class_composite)
+ {
+ if (member_scope_.empty ())
+ member_scope_.push_back (class_inheritance_chain ());
+
+ member_scope_.back ().push_back (&c);
+
+ traverse_composite_wrapper (0, c, 0);
+
+ member_scope_.back ().pop_back ();
+
+ if (member_scope_.back ().empty ())
+ member_scope_.pop_back ();
+
+ return;
+ }
+
+ if (top_level_)
+ top_level_ = false;
+ else
+ {
+ // Unless requested otherwise, don't go into bases if we are a derived
+ // type in a polymorphic hierarchy.
+ //
+ if (!traverse_poly_base_ && polymorphic (c))
+ return;
+ }
+
+ if (context::top_object == 0)
+ context::top_object = &c;
+
+ semantics::class_* prev (context::cur_object);
+ context::cur_object = &c;
+
+ if (member_scope_.empty ())
+ member_scope_.push_back (class_inheritance_chain ());
+
+ member_scope_.back ().push_back (&c);
+
+ if (build_table_prefix_)
+ {
+ // Don't reset the table prefix if we are traversing a base.
+ //
+ bool tb (false);
+
+ if (table_prefix_.level == 0)
+ {
+ table_prefix_ = table_prefix (c);
+ tb = true;
+ }
+
+ if (k == class_object)
+ traverse_object (c);
+ else
+ traverse_view (c);
+
+ if (tb)
+ table_prefix_ = table_prefix ();
+ }
+ else
+ {
+ if (k == class_object)
+ traverse_object (c);
+ else
+ traverse_view (c);
+ }
+
+ member_scope_.back ().pop_back ();
+
+ if (member_scope_.back ().empty ())
+ member_scope_.pop_back ();
+
+ context::cur_object = prev;
+
+ if (prev == 0)
+ context::top_object = 0;
+}
+
+void object_members_base::
+traverse_member (semantics::data_member& m, semantics::type& t)
+{
+ if (semantics::class_* comp = context::composite_wrapper (t))
+ {
+ member_scope_.push_back (class_inheritance_chain ());
+ member_scope_.back ().push_back (comp);
+
+ table_prefix old_table_prefix;
+ string old_flat_prefix, old_member_prefix;
+
+ if (build_flat_prefix_)
+ {
+ old_flat_prefix = flat_prefix_;
+ flat_prefix_ += public_name (m);
+ flat_prefix_ += '_';
+ }
+
+ if (build_member_prefix_)
+ {
+ old_member_prefix = member_prefix_;
+ member_prefix_ += m.name ();
+ member_prefix_ += '.';
+ }
+
+ if (build_table_prefix_)
+ {
+ old_table_prefix = table_prefix_;
+ table_prefix_.append (m);
+ }
+
+ traverse_composite_wrapper (&m, *comp, (wrapper (t) ? &t : 0));
+
+ if (build_table_prefix_)
+ table_prefix_ = old_table_prefix;
+
+ if (build_flat_prefix_)
+ flat_prefix_ = old_flat_prefix;
+
+ if (build_member_prefix_)
+ member_prefix_ = old_member_prefix;
+
+ member_scope_.pop_back ();
+ }
+ else
+ traverse_simple (m);
+}
+
+bool object_members_base::
+section_test (data_member_path const& mp)
+{
+ // By default ignore members from the wrong section.
+ //
+ return section_ == 0 || *section_ == section (mp);
+}
+
+void object_members_base::member::
+traverse (semantics::data_member& m)
+{
+ if (transient (m))
+ return;
+
+ om_.member_path_.push_back (&m);
+
+ // Ignore members from the wrong section.
+ //
+ if (om_.section_test (om_.member_path_))
+ {
+ semantics::type& t (utype (m));
+
+ if (semantics::type* c = context::container (m))
+ om_.traverse_container (m, *c);
+ else if (semantics::class_* c = object_pointer (t))
+ om_.traverse_pointer (m, *c);
+ else
+ om_.traverse_member (m, t);
+ }
+
+ om_.member_path_.pop_back ();
+}
+
+//
+// object_columns_base
+//
+
+void object_columns_base::
+flush ()
+{
+}
+
+bool object_columns_base::
+traverse_column (semantics::data_member&, string const&, bool)
+{
+ return false;
+}
+
+void object_columns_base::
+traverse_pointer (semantics::data_member& m, semantics::class_& c)
+{
+ if (!view_member (m)) // Not really "as if" pointed-to id column.
+ traverse_member (m, utype (*id_member (c)));
+}
+
+void object_columns_base::
+traverse_points_to (semantics::data_member& m, semantics::class_&)
+{
+ traverse_member (m, utype (m));
+}
+
+void object_columns_base::
+traverse_composite (semantics::data_member*, semantics::class_& c)
+{
+ inherits (c);
+ names (c);
+}
+
+void object_columns_base::
+traverse_object (semantics::class_& c)
+{
+ inherits (c);
+ names (c);
+}
+
+void object_columns_base::
+traverse_view (semantics::class_& c)
+{
+ // A view has no bases.
+ //
+ names (c);
+}
+
+void object_columns_base::
+traverse_pre (semantics::nameable&)
+{
+}
+
+void object_columns_base::
+traverse_post (semantics::nameable&)
+{
+}
+
+void object_columns_base::
+traverse (semantics::data_member& m)
+{
+ traverse_pre (m);
+
+ semantics::type& t (utype (m));
+ semantics::class_* c (object_pointer (t));
+ semantics::type* rt (c == 0 ? &t : &utype (*id_member (*c)));
+
+ root_ = &m;
+
+ // It would seem natural to add m to member_path_ so that we don't
+ // have these two cases. However, for member path to work correctly
+ // with readonly() we also have to have corresponding member_scope,
+ // which is a whole different level of complexity.
+ //
+ root_id_ = member_path_.empty ()
+ ? context::id (m)
+ : context::id (member_path_) != 0;
+ root_op_ = (c != 0);
+ root_null_ = context::null (m);
+
+
+ if (root_op_)
+ traverse_pointer (m, *c);
+ else
+ traverse_member (m, *rt);
+
+ if (!first_ && composite_wrapper (*rt))
+ flush ();
+
+ root_ = 0;
+
+ traverse_post (m);
+}
+
+void object_columns_base::
+traverse (semantics::data_member& m,
+ semantics::type& t,
+ std::string const& kp,
+ std::string const& dn,
+ semantics::class_* to)
+{
+ traverse_pre (m);
+
+ semantics::class_* oto (context::top_object);
+
+ if (to != 0)
+ context::top_object = to;
+
+ semantics::class_* c (object_pointer (t));
+ semantics::type* rt (c == 0 ? &t : &utype (*id_member (*c)));
+
+ root_ = &m;
+ root_id_ = (kp == "id");
+ root_op_ = (c != 0);
+ root_null_ = context::null (m, kp);
+
+ key_prefix_ = kp;
+ default_name_ = dn;
+
+ if (root_op_)
+ traverse_pointer (m, *c);
+ else
+ traverse_member (m, *rt);
+
+ key_prefix_.clear ();
+ default_name_.clear ();
+
+ if (!first_ && composite_wrapper (*rt))
+ flush ();
+
+ root_ = 0;
+ context::top_object = oto;
+
+ traverse_post (m);
+}
+
+void object_columns_base::
+traverse (semantics::class_& c)
+{
+ class_kind_type k (class_kind (c));
+
+ // Ignore transient bases.
+ //
+ if (k == class_other)
+ return;
+
+ bool f (top_level_);
+
+ if (top_level_)
+ {
+ traverse_pre (c);
+ top_level_ = false;
+ }
+ else
+ {
+ // Unless requested otherwise, don't go into bases if we are a derived
+ // type in a polymorphic hierarchy.
+ //
+ if (!traverse_poly_base_ && polymorphic (c))
+ return;
+ }
+
+ semantics::class_* prev (0);
+ if (k == class_object || k == class_view)
+ {
+ if (context::top_object == 0)
+ context::top_object = &c;
+
+ prev = context::cur_object;
+ context::cur_object = &c;
+ }
+
+ if (member_scope_.empty ())
+ member_scope_.push_back (class_inheritance_chain ());
+
+ member_scope_.back ().push_back (&c);
+
+ if (k == class_object)
+ traverse_object (c);
+ else if (k == class_view)
+ traverse_view (c);
+ else if (k == class_composite)
+ traverse_composite (0, c);
+
+ member_scope_.back ().pop_back ();
+
+ if (member_scope_.back ().empty ())
+ member_scope_.pop_back ();
+
+ if (k == class_object || k == class_view)
+ {
+ context::cur_object = prev;
+
+ if (prev == 0)
+ context::top_object = 0;
+ }
+
+ if (f)
+ {
+ if (!first_)
+ flush ();
+
+ traverse_post (c);
+ }
+}
+
+void object_columns_base::
+traverse_member (semantics::data_member& m, semantics::type& t)
+{
+ if (semantics::class_* comp = composite_wrapper (t))
+ {
+ member_scope_.push_back (class_inheritance_chain ());
+ member_scope_.back ().push_back (comp);
+
+ column_prefix old_prefix (column_prefix_);
+ column_prefix_.append (m, key_prefix_, default_name_);
+
+ // Save and clear the key prefix and default name.
+ //
+ string old_kp, old_dn;
+ old_kp.swap (key_prefix_);
+ old_dn.swap (default_name_);
+
+ traverse_composite (&m, *comp);
+
+ old_kp.swap (key_prefix_);
+ old_dn.swap (default_name_);
+
+ column_prefix_ = old_prefix;
+ member_scope_.pop_back ();
+ }
+ else
+ {
+ string name (column_name (m, key_prefix_, default_name_, column_prefix_));
+
+ if (traverse_column (m, name, first_))
+ {
+ if (first_)
+ first_ = false;
+ }
+ }
+}
+
+bool object_columns_base::
+section_test (data_member_path const& mp)
+{
+ // By default ignore members from the wrong section.
+ //
+ return section_ == 0 || *section_ == section (mp);
+}
+
+void object_columns_base::member::
+traverse (semantics::data_member& m)
+{
+ if (transient (m))
+ return;
+
+ // Container gets its own table, so nothing to do here.
+ //
+ if (container (m))
+ return;
+
+ oc_.member_path_.push_back (&m);
+
+ if (oc_.section_test (oc_.member_path_))
+ {
+ using semantics::class_;
+ semantics::type& t (utype (m));
+
+ if (class_* c = object_pointer (t))
+ oc_.traverse_pointer (m, *c);
+ else if (class_* c = points_to (m))
+ oc_.traverse_points_to (m, *c);
+ else
+ oc_.traverse_member (m, t);
+ }
+
+ oc_.member_path_.pop_back ();
+}
+
+//
+// object_columns_list
+//
+
+void object_columns_list::
+traverse_pointer (semantics::data_member& m, semantics::class_& c)
+{
+ // Ignore inverse object pointers.
+ //
+ if (!ignore_inverse_ || !inverse (m, key_prefix_))
+ object_columns_base::traverse_pointer (m, c);
+}
+
+bool object_columns_list::
+traverse_column (semantics::data_member& m, std::string const& name, bool)
+{
+ columns_.push_back (column (name, column_type (), m));
+ return true;
+}
+
+//
+// typedefs
+//
+
+void typedefs::
+traverse (semantics::typedefs& t)
+{
+ if (check (t))
+ traversal::typedefs::traverse (t);
+}
+
+bool typedefs::
+check (semantics::typedefs& t)
+{
+ // This typedef must be for a class template instantiation.
+ //
+ using semantics::class_instantiation;
+ class_instantiation* ci (dynamic_cast<class_instantiation*> (&t.type ()));
+
+ if (ci == 0)
+ return false;
+
+ // It must be an object, view, or composite value.
+ //
+ if (class_kind (*ci) == class_other)
+ return false;
+
+ // This typedef name should be the one that was used in the pragma.
+ //
+ using semantics::names;
+ tree type (ci->get<tree> ("tree-node"));
+
+ names* hint;
+ if (ci->count ("tree-hint"))
+ hint = ci->get<names*> ("tree-hint");
+ else
+ {
+ hint = unit.find_hint (type);
+ ci->set ("tree-hint", hint); // Cache it.
+ }
+
+ if (hint != &t)
+ return false;
+
+ // And the definition may have to be in the file we are compiling.
+ //
+ if (!included_)
+ {
+ if (!options.at_once () && class_file (*ci) != unit.file ())
+ return false;
+ }
+
+ return true;
+}
diff --git a/odb/odb/common.hxx b/odb/odb/common.hxx
new file mode 100644
index 0000000..149def7
--- /dev/null
+++ b/odb/odb/common.hxx
@@ -0,0 +1,468 @@
+// file : odb/common.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_COMMON_HXX
+#define ODB_COMMON_HXX
+
+#include <string>
+#include <vector>
+#include <cstddef> // std::size_t
+#include <cassert>
+
+#include <odb/context.hxx>
+#include <odb/instance.hxx>
+
+// Traverse object members recursively by going into bases and
+// composite members.
+//
+struct object_members_base: traversal::class_, virtual context
+{
+ virtual void
+ traverse_simple (semantics::data_member&);
+
+ // Traverse object pointer. The second argument is the pointed-to
+ // class. When overriding this function, you will most likely want
+ // to call the base in order to traverse the pointer as a simple
+ // member (simple object id) or as composite (composite object id).
+ //
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&);
+
+ // If you override this function, you can call the base to traverse
+ // bases and members. The first argument is the data member and can
+ // be NULL if we are traversing the root type or a base. The second
+ // argument is the actual composite type, which is not necessarily
+ // the same as the member's type.
+ //
+ virtual void
+ traverse_composite (semantics::data_member*, semantics::class_&);
+
+ // More general version of the above function that allows detection
+ // of wrapped composite value. By default this function calls
+ // traverse_composite (m, comp) ignoring the wrapper type. Note that
+ // this function is called for all composite value (wrapped or not).
+ // If it is not wrapped, the wrapper argument will be NULL.
+ //
+ virtual void
+ traverse_composite_wrapper (semantics::data_member*,
+ semantics::class_& comp,
+ semantics::type* wrapper);
+
+ // The second argument is the actual container type in case the
+ // member type is a wrapper.
+ //
+ virtual void
+ traverse_container (semantics::data_member&, semantics::type&);
+
+ // If you override this function, you can call the base to traverse
+ // bases and members.
+ //
+ virtual void
+ traverse_object (semantics::class_&);
+
+ // If you override this function, you can call the base to traverse
+ // members.
+ //
+ virtual void
+ traverse_view (semantics::class_&);
+
+ virtual bool
+ section_test (data_member_path const&);
+
+public:
+ object_members_base (bool traverse_poly_base = false,
+ object_section* section = 0)
+ : section_ (section), top_level_ (true), member_ (*this)
+ {
+ init (false, false, false, traverse_poly_base);
+ }
+
+ object_members_base (bool build_flat_prefix,
+ bool build_table_prefix,
+ bool build_member_prefix,
+ bool traverse_poly_base = false,
+ object_section* section = 0)
+ : section_ (section), top_level_ (true), member_ (*this)
+ {
+ init (build_flat_prefix,
+ build_table_prefix,
+ build_member_prefix,
+ traverse_poly_base);
+ }
+
+ object_members_base (object_members_base const& x)
+ : context (), //@@ -Wextra
+ section_ (x.section_),
+ top_level_ (true),
+ member_ (*this)
+ {
+ init (x.build_flat_prefix_,
+ x.build_table_prefix_,
+ x.build_member_prefix_,
+ x.traverse_poly_base_);
+ }
+
+ virtual void
+ traverse (semantics::class_&);
+
+protected:
+ string flat_prefix_;
+ table_prefix table_prefix_;
+ string member_prefix_;
+
+ data_member_path member_path_;
+ data_member_scope member_scope_;
+
+ object_section* section_;
+
+protected:
+ semantics::data_member*
+ id () const
+ {
+ assert (!member_path_.empty ());
+ return context::id (member_path_);
+ }
+
+private:
+ void
+ init (bool build_flat_prefix,
+ bool build_table_prefix,
+ bool build_member_prefix,
+ bool traverse_poly_base)
+ {
+ build_flat_prefix_ = build_flat_prefix;
+ build_table_prefix_ = build_table_prefix;
+ build_member_prefix_ = build_member_prefix;
+ traverse_poly_base_ = traverse_poly_base;
+
+ *this >> names_ >> member_;
+ *this >> inherits_ >> *this;
+ }
+
+private:
+ virtual void
+ traverse_member (semantics::data_member&, semantics::type&);
+
+ struct member: traversal::data_member
+ {
+ member (object_members_base& om): om_ (om) {}
+
+ virtual void
+ traverse (semantics::data_member&);
+
+ public:
+ object_members_base& om_;
+ };
+
+ bool build_flat_prefix_;
+ bool build_table_prefix_;
+ bool build_member_prefix_;
+
+ bool traverse_poly_base_;
+
+ bool top_level_;
+
+ member member_;
+ traversal::names names_;
+ traversal::inherits inherits_;
+};
+
+// Traverse object columns recursively by going into composite members
+// and bases.
+//
+struct object_columns_base: traversal::class_, virtual context
+{
+ // Returning false means that the column has been ignored and the
+ // first flag should not be changed.
+ //
+ virtual bool
+ traverse_column (semantics::data_member&,
+ string const& name,
+ bool first);
+
+ // Traverse object pointer. The second argument is the pointed-to
+ // class. When overriding this function, you will most likely want
+ // to call the base in order to traverse the member as a column
+ // (simple object id) or columns (composite object id).
+ //
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&);
+
+ virtual void
+ traverse_points_to (semantics::data_member&, semantics::class_&);
+
+ // If you override this function, you can call the base to traverse
+ // bases and members. The first argument is the data member and can
+ // be NULL if we are traversing the root type or a base. The second
+ // argument is the actual composite type, which is not necessarily
+ // the same as the member type.
+ //
+ virtual void
+ traverse_composite (semantics::data_member*, semantics::class_&);
+
+ // If you override this function, you can call the base to traverse
+ // bases and members.
+ //
+ virtual void
+ traverse_object (semantics::class_&);
+
+ // If you override this function, you can call the base to traverse
+ // members.
+ //
+ virtual void
+ traverse_view (semantics::class_&);
+
+ // Called after the last column, provided at least one column hasn't
+ // been ignored.
+ //
+ virtual void
+ flush ();
+
+ virtual bool
+ section_test (data_member_path const&);
+
+ // Start/end traversal callbacks.
+ //
+ virtual void
+ traverse_pre (semantics::nameable&);
+
+ virtual void
+ traverse_post (semantics::nameable&);
+
+public:
+ object_columns_base (bool first = true,
+ column_prefix const& cp = column_prefix (),
+ object_section* section = 0)
+ : column_prefix_ (cp),
+ section_ (section),
+ root_ (0),
+ traverse_poly_base_ (false),
+ first_ (first),
+ top_level_ (true),
+ member_ (*this)
+ {
+ init ();
+ }
+
+ object_columns_base (bool first,
+ bool traverse_poly_base,
+ object_section* section = 0)
+ : section_ (section),
+ root_ (0),
+ traverse_poly_base_ (traverse_poly_base),
+ first_ (first),
+ top_level_ (true),
+ member_ (*this)
+ {
+ init ();
+ }
+
+ object_columns_base (object_columns_base const& x)
+ : context (), //@@ -Wextra
+ column_prefix_ (x.column_prefix_),
+ section_ (x.section_),
+ root_ (0),
+ traverse_poly_base_ (x.traverse_poly_base_),
+ first_ (x.first_),
+ top_level_ (true),
+ member_ (*this)
+ {
+ init ();
+ }
+
+ virtual void
+ traverse (semantics::class_&);
+
+ // Traverse a data member with type, which can be a simple or composite
+ // value type, or an object pointer (with a simple or composite id).
+ //
+ virtual void
+ traverse (semantics::data_member&);
+
+ virtual void
+ traverse (semantics::data_member& m, column_prefix const& cp)
+ {
+ column_prefix op (column_prefix_);
+ column_prefix_ = cp;
+ traverse (m);
+ column_prefix_ = op;
+ }
+
+ virtual void
+ traverse (data_member_path& mp)
+ {
+ data_member_path op (member_path_);
+ member_path_ = mp;
+ traverse (*mp.back (), column_prefix (mp));
+ member_path_ = op;
+ }
+
+ // Should only be used for containers.
+ //
+ virtual void
+ traverse (semantics::data_member&,
+ semantics::type&,
+ string const& key_prefix,
+ string const& default_name,
+ semantics::class_* top_object = 0); // If not 0, switch top object.
+
+protected:
+ string key_prefix_;
+ string default_name_;
+
+ column_prefix column_prefix_;
+
+ data_member_path member_path_;
+ data_member_scope member_scope_;
+
+ object_section* section_;
+
+protected:
+ semantics::data_member*
+ id () const
+ {
+ if (root_ != 0)
+ return root_id_ ? root_ : 0; // Cannot have ids below root.
+ else
+ {
+ assert (!member_path_.empty ());
+ return context::id (member_path_);
+ }
+ }
+
+ string
+ column_type ()
+ {
+ if (member_path_.empty ())
+ {
+ assert (root_ != 0);
+ return context::column_type (*root_, key_prefix_);
+ }
+ else
+ return context::column_type (
+ member_path_, key_prefix_, root_ != 0 && (root_id_ || root_op_));
+ }
+
+ bool
+ null () const
+ {
+ return (root_ != 0 && root_null_) || context::null (member_path_);
+ }
+
+private:
+ semantics::data_member* root_; // Root member if traversing from a member.
+ bool root_id_; // True if traversing root as object id.
+ bool root_op_; // True if traversing root as object pointer.
+ bool root_null_; // True if root is null-able.
+
+private:
+ void
+ init ()
+ {
+ *this >> names_ >> member_;
+ *this >> inherits_ >> *this;
+ }
+
+private:
+ virtual void
+ traverse_member (semantics::data_member&, semantics::type&);
+
+ struct member: traversal::data_member, context
+ {
+ member (object_columns_base& oc): oc_ (oc) {}
+
+ virtual void
+ traverse (semantics::data_member&);
+
+ public:
+ object_columns_base& oc_;
+ };
+
+ bool traverse_poly_base_;
+
+ bool first_;
+ bool top_level_;
+
+ member member_;
+ traversal::names names_;
+ traversal::inherits inherits_;
+};
+
+struct object_columns_list: object_columns_base
+{
+ typedef object_columns_list base;
+
+ object_columns_list (bool ignore_inverse = true)
+ : ignore_inverse_ (ignore_inverse)
+ {
+ }
+
+ object_columns_list (column_prefix const& cp, bool ignore_inverse = true)
+ : object_columns_base (true, cp), ignore_inverse_ (ignore_inverse)
+ {
+ }
+
+ struct column
+ {
+ column (std::string const& n,
+ std::string const& t,
+ semantics::data_member& m)
+ : name (n), type (t), member (&m)
+ {
+ }
+
+ std::string name;
+ std::string type;
+ semantics::data_member* member;
+ };
+
+ typedef std::vector<column> columns;
+ typedef columns::const_iterator iterator;
+
+ iterator
+ begin () const {return columns_.begin ();}
+
+ iterator
+ end () const {return columns_.end ();}
+
+ columns::size_type
+ size () const {return columns_.size ();}
+
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&);
+
+ virtual bool
+ traverse_column (semantics::data_member&, string const&, bool);
+
+private:
+ bool ignore_inverse_;
+ columns columns_;
+};
+
+// Traverse objects, views, and composite values that are class template
+// instantiations.
+//
+struct typedefs: traversal::typedefs, context
+{
+ typedefs (bool traverse_included)
+ : included_ (traverse_included)
+ {
+ }
+
+ virtual void
+ traverse (semantics::typedefs&);
+
+ // Returns true if we should traverse this typedef.
+ //
+ bool
+ check (semantics::typedefs&);
+
+private:
+ bool included_;
+};
+
+// Other common parts.
+//
+#include <odb/common-query.hxx>
+
+#endif // ODB_COMMON_HXX
diff --git a/odb/odb/context.cxx b/odb/odb/context.cxx
new file mode 100644
index 0000000..f678e64
--- /dev/null
+++ b/odb/odb/context.cxx
@@ -0,0 +1,3389 @@
+// file : odb/context.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <cctype> // std::toupper
+#include <cassert>
+#include <sstream>
+
+#include <odb/context.hxx>
+#include <odb/common.hxx>
+#include <odb/pragma.hxx>
+#include <odb/cxx-lexer.hxx>
+#include <odb/diagnostics.hxx>
+
+#include <odb/relational/mssql/context.hxx>
+#include <odb/relational/mysql/context.hxx>
+#include <odb/relational/oracle/context.hxx>
+#include <odb/relational/pgsql/context.hxx>
+#include <odb/relational/sqlite/context.hxx>
+
+using namespace std;
+
+static inline void
+add_space (string& s)
+{
+ string::size_type n (s.size ());
+ if (n != 0 && s[n - 1] != ' ')
+ s += ' ';
+}
+
+//
+// custom_cxx_type
+//
+string custom_cxx_type::
+translate (string const& val, const cxx_tokens& expr)
+{
+ // Similar to member_access::translate() and a few other places.
+ //
+ string r;
+
+ cxx_tokens_lexer l;
+ l.start (expr);
+
+ string tl;
+ for (cpp_ttype tt (l.next (tl)), ptt (CPP_EOF); tt != CPP_EOF;)
+ {
+ // Try to format the expression to resemble the style of the
+ // generated code.
+ //
+ switch (tt)
+ {
+ case CPP_NOT:
+ {
+ add_space (r);
+ r += '!';
+ break;
+ }
+ case CPP_COMMA:
+ {
+ r += ", ";
+ break;
+ }
+ case CPP_OPEN_PAREN:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD)
+ add_space (r);
+
+ r += '(';
+ break;
+ }
+ case CPP_CLOSE_PAREN:
+ {
+ r += ')';
+ break;
+ }
+ case CPP_OPEN_SQUARE:
+ {
+ r += '[';
+ break;
+ }
+ case CPP_CLOSE_SQUARE:
+ {
+ r += ']';
+ break;
+ }
+ case CPP_OPEN_BRACE:
+ {
+ add_space (r);
+ r += "{ ";
+ break;
+ }
+ case CPP_CLOSE_BRACE:
+ {
+ add_space (r);
+ r += '}';
+ break;
+ }
+ case CPP_SEMICOLON:
+ {
+ r += ';';
+ break;
+ }
+ case CPP_ELLIPSIS:
+ {
+ add_space (r);
+ r += "...";
+ break;
+ }
+ case CPP_PLUS:
+ case CPP_MINUS:
+ {
+ bool unary (ptt != CPP_NAME &&
+ ptt != CPP_SCOPE &&
+ ptt != CPP_NUMBER &&
+ ptt != CPP_STRING &&
+ ptt != CPP_CLOSE_PAREN &&
+ ptt != CPP_PLUS_PLUS &&
+ ptt != CPP_MINUS_MINUS);
+
+ if (!unary)
+ add_space (r);
+
+ r += cxx_lexer::token_spelling[tt];
+
+ if (!unary)
+ r += ' ';
+ break;
+ }
+ case CPP_PLUS_PLUS:
+ case CPP_MINUS_MINUS:
+ {
+ if (ptt != CPP_NAME &&
+ ptt != CPP_CLOSE_PAREN &&
+ ptt != CPP_CLOSE_SQUARE)
+ add_space (r);
+
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ case CPP_DEREF:
+ case CPP_DEREF_STAR:
+ case CPP_DOT:
+ case CPP_DOT_STAR:
+ {
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ case CPP_STRING:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += context::strlit (tl);
+ break;
+ }
+ case CPP_NUMBER:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += tl;
+ break;
+ }
+ case CPP_SCOPE:
+ {
+ // Add space except for a few common cases.
+ //
+ if (ptt != CPP_NAME &&
+ ptt != CPP_OPEN_PAREN &&
+ ptt != CPP_OPEN_SQUARE)
+ add_space (r);
+
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ case CPP_NAME:
+ {
+ // Start of a name.
+ //
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += tl;
+ break;
+ }
+ case CPP_QUERY:
+ {
+ if (ptt == CPP_OPEN_PAREN)
+ {
+ // Get the next token and see if it is ')'.
+ //
+ ptt = tt;
+ tt = l.next (tl);
+
+ if (tt == CPP_CLOSE_PAREN)
+ r += val;
+ else
+ {
+ add_space (r);
+ r += "? ";
+ }
+ continue; // We have already gotten the next token.
+ }
+ }
+ // Fall through.
+ default:
+ {
+ // Handle CPP_KEYWORD here to avoid a warning (it is not
+ // part of the cpp_ttype enumeration).
+ //
+ if (tt == CPP_KEYWORD)
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += tl;
+ }
+ else
+ {
+ // All the other operators.
+ //
+ add_space (r);
+ r += cxx_lexer::token_spelling[tt];
+ r += ' ';
+ }
+ break;
+ }
+ }
+
+ //
+ // Watch out for the continue statements above if you add any
+ // logic here.
+ //
+
+ ptt = tt;
+ tt = l.next (tl);
+ }
+
+ return r;
+}
+
+
+//
+// view_object
+//
+
+string view_object::
+name () const
+{
+ if (!alias.empty ())
+ return alias;
+
+ return kind == object ? context::class_name (*obj) : tbl_name.string ();
+}
+
+//
+// member_access
+//
+
+bool member_access::
+placeholder () const
+{
+ for (cxx_tokens::const_iterator i (expr.begin ()), e (expr.end ()); i != e;)
+ {
+ if (i->type == CPP_OPEN_PAREN)
+ {
+ if (++i != e && i->type == CPP_QUERY)
+ {
+ if (++i != e && i->type == CPP_CLOSE_PAREN)
+ return true;
+ }
+ }
+ else
+ ++i;
+ }
+
+ return false;
+}
+
+string member_access::
+translate (string const& obj, string const& val, string const& db) const
+{
+ if (empty ())
+ {
+ error (loc) << "non-empty " << kind << " expression required" << endl;
+ throw operation_failed ();
+ }
+
+ // This code is similar to translate_expression() from relations/source.cxx.
+ //
+ string r;
+
+ cxx_tokens_lexer l;
+ l.start (expr);
+
+ string tl;
+ for (cpp_ttype tt (l.next (tl)), ptt (CPP_EOF); tt != CPP_EOF;)
+ {
+ // Try to format the expression to resemble the style of the
+ // generated code.
+ //
+ switch (tt)
+ {
+ case CPP_COMMA:
+ {
+ r += ", ";
+ break;
+ }
+ case CPP_OPEN_PAREN:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD)
+ add_space (r);
+
+ r += '(';
+ break;
+ }
+ case CPP_CLOSE_PAREN:
+ {
+ r += ')';
+ break;
+ }
+ case CPP_OPEN_SQUARE:
+ {
+ r += '[';
+ break;
+ }
+ case CPP_CLOSE_SQUARE:
+ {
+ r += ']';
+ break;
+ }
+ case CPP_OPEN_BRACE:
+ {
+ add_space (r);
+ r += "{ ";
+ break;
+ }
+ case CPP_CLOSE_BRACE:
+ {
+ add_space (r);
+ r += '}';
+ break;
+ }
+ case CPP_SEMICOLON:
+ {
+ r += ';';
+ break;
+ }
+ case CPP_ELLIPSIS:
+ {
+ add_space (r);
+ r += "...";
+ break;
+ }
+ case CPP_PLUS:
+ case CPP_MINUS:
+ {
+ bool unary (ptt != CPP_NAME &&
+ ptt != CPP_SCOPE &&
+ ptt != CPP_NUMBER &&
+ ptt != CPP_STRING &&
+ ptt != CPP_CLOSE_PAREN &&
+ ptt != CPP_PLUS_PLUS &&
+ ptt != CPP_MINUS_MINUS);
+
+ if (!unary)
+ add_space (r);
+
+ r += cxx_lexer::token_spelling[tt];
+
+ if (!unary)
+ r += ' ';
+ break;
+ }
+ case CPP_PLUS_PLUS:
+ case CPP_MINUS_MINUS:
+ {
+ if (ptt != CPP_NAME &&
+ ptt != CPP_CLOSE_PAREN &&
+ ptt != CPP_CLOSE_SQUARE)
+ add_space (r);
+
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ case CPP_DEREF:
+ case CPP_DEREF_STAR:
+ case CPP_DOT:
+ case CPP_DOT_STAR:
+ {
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ case CPP_STRING:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += context::strlit (tl);
+ break;
+ }
+ case CPP_NUMBER:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += tl;
+ break;
+ }
+ case CPP_SCOPE:
+ {
+ // Add space except for a few common cases.
+ //
+ if (ptt != CPP_NAME &&
+ ptt != CPP_OPEN_PAREN &&
+ ptt != CPP_OPEN_SQUARE)
+ add_space (r);
+
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ case CPP_NAME:
+ {
+ // Start of a name.
+ //
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += tl;
+ break;
+ }
+ case CPP_NOT:
+ case CPP_QUERY:
+ {
+ if (ptt == CPP_OPEN_PAREN)
+ {
+ // Get the next token and see if it is ')'.
+ //
+ ptt = tt;
+ tt = l.next (tl);
+
+ if (tt == CPP_CLOSE_PAREN)
+ {
+ if (ptt == CPP_NOT)
+ {
+ if (db.empty ())
+ {
+ error (loc) << "database instance (!) not available in this "
+ << "context" << endl;
+ throw operation_failed ();
+ }
+
+ r += db;
+ }
+ else
+ r += val;
+ }
+ else
+ {
+ add_space (r);
+ r += (ptt == CPP_NOT ? "!" : "? ");
+ }
+ continue; // We have already gotten the next token.
+ }
+ }
+ // Fall through.
+ default:
+ {
+ // Handle CPP_KEYWORD here to avoid a warning (it is not
+ // part of the cpp_ttype enumeration).
+ //
+ if (tt == CPP_KEYWORD)
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ // Translate 'this'.
+ //
+ r += (tl == "this" ? obj : tl);
+ }
+ else
+ {
+ // All the other operators.
+ //
+ add_space (r);
+ r += cxx_lexer::token_spelling[tt];
+ r += ' ';
+ }
+ break;
+ }
+ }
+
+ //
+ // Watch out for the continue statements above if you add any
+ // logic here.
+ //
+
+ ptt = tt;
+ tt = l.next (tl);
+ }
+
+ return r;
+}
+
+// Sections.
+//
+main_section_type main_section;
+
+bool main_section_type::
+compare (object_section const& s) const
+{
+ main_section_type const* ms (dynamic_cast<main_section_type const*> (&s));
+ return ms != 0 && *this == *ms;
+}
+
+bool user_section::
+compare (object_section const& s) const
+{
+ user_section const* us (dynamic_cast<user_section const*> (&s));
+ return us != 0 && *this == *us;
+}
+
+user_section* user_section::
+total_base () const
+{
+ if (base != 0)
+ {
+ semantics::class_* poly_root (context::polymorphic (*object));
+ if (poly_root != 0 && poly_root != *object)
+ return base;
+ }
+
+ return 0;
+}
+
+size_t user_sections::
+count (unsigned short f) const
+{
+ size_t r (0);
+
+ semantics::class_* poly_root (context::polymorphic (*object));
+ bool poly_derived (poly_root != 0 && poly_root != object);
+
+ if (poly_derived && (f & count_total) != 0)
+ r = context::polymorphic_base (*object).get<user_sections> (
+ "user-sections").count (f);
+
+ for (const_iterator i (begin ()); i != end (); ++i)
+ {
+ // Skip special sections unless we were explicitly asked to count them.
+ //
+ if (i->special == user_section::special_version &&
+ (f & count_special_version) == 0)
+ continue;
+
+ // Skip non-versioned sections if we are only interested in the
+ // versioned ones.
+ //
+ if ((f & count_versioned_only) != 0 &&
+ !context::added (*i->member) && !context::deleted (*i->member))
+ continue;
+
+ bool ovd (i->base != 0 && poly_derived);
+
+ if (i->load != user_section::load_eager)
+ {
+ if (i->load_empty ())
+ {
+ if ((f & count_load_empty) != 0)
+ {
+ if (ovd)
+ {
+ if ((f & count_override) != 0)
+ {
+ r++;
+ continue; // Count each section only once.
+ }
+ }
+ else
+ {
+ if ((f & count_new) != 0 || (f & count_total) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ }
+ }
+ else
+ {
+ if ((f & count_load) != 0)
+ {
+ if (ovd)
+ {
+ if ((f & count_override) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ else
+ {
+ if ((f & count_new) != 0 || (f & count_total) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ }
+ }
+ }
+
+ if (i->update_empty ())
+ {
+ if ((f & count_update_empty) != 0)
+ {
+ if (ovd)
+ {
+ if ((f & count_override) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ else
+ {
+ if ((f & count_new) != 0 || (f & count_total) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ }
+ }
+ else
+ {
+ if ((f & count_update) != 0)
+ {
+ if (ovd)
+ {
+ if ((f & count_override) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ else
+ {
+ if ((f & count_new) != 0 || (f & count_total) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ }
+ }
+
+ if (i->optimistic ())
+ {
+ if ((f & count_optimistic) != 0)
+ {
+ if (ovd)
+ {
+ if ((f & count_override) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ else
+ {
+ if ((f & count_new) != 0 || (f & count_total) != 0)
+ {
+ r++;
+ continue;
+ }
+ }
+ }
+ }
+ }
+
+ return r;
+}
+
+//
+// context
+//
+
+namespace
+{
+ char const* keywords[] =
+ {
+ "NULL",
+ "and",
+ "asm",
+ "auto",
+ "bitand",
+ "bitor",
+ "bool",
+ "break",
+ "case",
+ "catch",
+ "char",
+ "class",
+ "compl",
+ "const",
+ "const_cast",
+ "continue",
+ "default",
+ "delete",
+ "do",
+ "double",
+ "dynamic_cast",
+ "else",
+ "end_eq",
+ "enum",
+ "explicit",
+ "export",
+ "extern",
+ "false",
+ "float",
+ "for",
+ "friend",
+ "goto",
+ "if",
+ "inline",
+ "int",
+ "long",
+ "mutable",
+ "namespace",
+ "new",
+ "not",
+ "not_eq",
+ "operator",
+ "or",
+ "or_eq",
+ "private",
+ "protected",
+ "public",
+ "register",
+ "reinterpret_cast",
+ "return",
+ "short",
+ "signed",
+ "sizeof",
+ "static",
+ "static_cast",
+ "struct",
+ "switch",
+ "template",
+ "this",
+ "throw",
+ "true",
+ "try",
+ "typedef",
+ "typeid",
+ "typename",
+ "union",
+ "unsigned",
+ "using",
+ "virtual",
+ "void",
+ "volatile",
+ "wchar_t",
+ "while",
+ "xor",
+ "xor_eq"
+ };
+}
+
+unique_ptr<context>
+create_context (ostream& os,
+ semantics::unit& unit,
+ options const& ops,
+ features& f,
+ semantics::relational::model* m)
+{
+ unique_ptr<context> r;
+
+ switch (ops.database ()[0])
+ {
+ case database::common:
+ {
+ r.reset (new context (os, unit, ops, f));
+ break;
+ }
+ case database::mssql:
+ {
+ r.reset (new relational::mssql::context (os, unit, ops, f, m));
+ break;
+ }
+ case database::mysql:
+ {
+ r.reset (new relational::mysql::context (os, unit, ops, f, m));
+ break;
+ }
+ case database::oracle:
+ {
+ r.reset (new relational::oracle::context (os, unit, ops, f, m));
+ break;
+ }
+ case database::pgsql:
+ {
+ r.reset (new relational::pgsql::context (os, unit, ops, f, m));
+ break;
+ }
+ case database::sqlite:
+ {
+ r.reset (new relational::sqlite::context (os, unit, ops, f, m));
+ break;
+ }
+ }
+
+ return r;
+}
+
+context::
+~context ()
+{
+ if (current_ == this)
+ current_ = 0;
+}
+
+context::
+context (ostream& os_,
+ semantics::unit& u,
+ options_type const& ops,
+ features_type& f,
+ data_ptr d)
+ : data_ (d ? d : data_ptr (new (shared) data (os_))),
+ extra (data_->extra_),
+ os (data_->os_),
+ unit (u),
+ options (ops),
+ features (f),
+ db (options.database ()[0]),
+ in_comment (data_->in_comment_),
+ exp (data_->exp_),
+ ext (data_->ext_),
+ keyword_set (data_->keyword_set_),
+ include_regex (data_->include_regex_),
+ accessor_regex (data_->accessor_regex_),
+ modifier_regex (data_->modifier_regex_),
+ embedded_schema (
+ ops.generate_schema () &&
+ ops.schema_format ()[db].count (schema_format::embedded)),
+ separate_schema (
+ ops.generate_schema () &&
+ ops.schema_format ()[db].count (schema_format::separate)),
+ multi_static (ops.multi_database () == multi_database::static_),
+ multi_dynamic (ops.multi_database () == multi_database::dynamic),
+ force_versioned (false),
+ top_object (data_->top_object_),
+ cur_object (data_->cur_object_)
+{
+ assert (current_ == 0);
+ current_ = this;
+
+ // Write boolean values as true/false.
+ //
+ os.setf (ios_base::boolalpha);
+
+ // Export control.
+ //
+ if (!ops.export_symbol ()[db].empty ())
+ exp = ops.export_symbol ()[db] + " ";
+
+ ext = ops.extern_symbol ()[db];
+
+ for (size_t i (0); i < sizeof (keywords) / sizeof (char*); ++i)
+ data_->keyword_set_.insert (keywords[i]);
+
+ // SQL name regex.
+ //
+ if (ops.table_regex ().count (db) != 0)
+ {
+ strings const& s (ops.table_regex ()[db]);
+ data_->sql_name_regex_[sql_name_table].assign (s.begin (), s.end ());
+ }
+
+ if (ops.column_regex ().count (db) != 0)
+ {
+ strings const& s (ops.column_regex ()[db]);
+ data_->sql_name_regex_[sql_name_column].assign (s.begin (), s.end ());
+ }
+
+ if (ops.index_regex ().count (db) != 0)
+ {
+ strings const& s (ops.index_regex ()[db]);
+ data_->sql_name_regex_[sql_name_index].assign (s.begin (), s.end ());
+ }
+
+ if (ops.fkey_regex ().count (db) != 0)
+ {
+ strings const& s (ops.fkey_regex ()[db]);
+ data_->sql_name_regex_[sql_name_fkey].assign (s.begin (), s.end ());
+ }
+
+ if (ops.sequence_regex ().count (db) != 0)
+ {
+ strings const& s (ops.sequence_regex ()[db]);
+ data_->sql_name_regex_[sql_name_sequence].assign (s.begin (), s.end ());
+ }
+
+ if (ops.statement_regex ().count (db) != 0)
+ {
+ strings const& s (ops.statement_regex ()[db]);
+ data_->sql_name_regex_[sql_name_statement].assign (s.begin (), s.end ());
+ }
+
+ if (ops.sql_name_regex ().count (db) != 0)
+ {
+ strings const& s (ops.sql_name_regex ()[db]);
+ data_->sql_name_regex_[sql_name_all].assign (s.begin (), s.end ());
+ }
+
+ // Include regex.
+ //
+ for (strings::const_iterator i (ops.include_regex ().begin ());
+ i != ops.include_regex ().end (); ++i)
+ data_->include_regex_.push_back (regexsub (*i));
+
+ // Common accessor/modifier naming variants. Try the user-supplied and
+ // more specific ones first.
+ //
+ for (strings::const_iterator i (ops.accessor_regex ().begin ());
+ i != ops.accessor_regex ().end (); ++i)
+ data_->accessor_regex_.push_back (regexsub (*i));
+
+ data_->accessor_regex_.push_back (regexsub ("/(.+)/get_$1/")); // get_foo
+ data_->accessor_regex_.push_back (regexsub ("/(.+)/get\\u$1/")); // getFoo
+ data_->accessor_regex_.push_back (regexsub ("/(.+)/get$1/")); // getfoo
+ data_->accessor_regex_.push_back (regexsub ("/(.+)/$1/")); // foo
+
+ for (strings::const_iterator i (ops.modifier_regex ().begin ());
+ i != ops.modifier_regex ().end (); ++i)
+ data_->modifier_regex_.push_back (regexsub (*i));
+
+ data_->modifier_regex_.push_back (regexsub ("/(.+)/set_$1/")); // set_foo
+ data_->modifier_regex_.push_back (regexsub ("/(.+)/set\\u$1/")); // setFoo
+ data_->modifier_regex_.push_back (regexsub ("/(.+)/set$1/")); // setfoo
+ data_->modifier_regex_.push_back (regexsub ("/(.+)/$1/")); // foo
+}
+
+context::
+context ()
+ : data_ (current ().data_),
+ extra (current ().extra),
+ os (current ().os),
+ unit (current ().unit),
+ options (current ().options),
+ features (current ().features),
+ db (current ().db),
+ in_comment (current ().in_comment),
+ exp (current ().exp),
+ ext (current ().ext),
+ keyword_set (current ().keyword_set),
+ include_regex (current ().include_regex),
+ accessor_regex (current ().accessor_regex),
+ modifier_regex (current ().modifier_regex),
+ embedded_schema (current ().embedded_schema),
+ separate_schema (current ().separate_schema),
+ multi_static (current ().multi_static),
+ multi_dynamic (current ().multi_dynamic),
+ force_versioned (current ().force_versioned),
+ top_object (current ().top_object),
+ cur_object (current ().cur_object)
+{
+}
+
+context* context::current_;
+
+semantics::data_member* context::
+id (data_member_path const& mp)
+{
+ semantics::data_member* idf (mp.front ());
+
+ if (!id (*idf))
+ return 0;
+
+ // This is for special ids, such as polymorphic-ref, which
+ // don't have "id-member" set (and we want to keep it that
+ // way since it is not really a full-fledged id).
+ //
+ if (idf->get<string> ("id").empty ()) // Not a nested id.
+ return idf;
+
+ const data_member_path& id (
+ *id_member (
+ dynamic_cast<semantics::class_&> (idf->scope ())));
+
+ // Now we need to make sure id is a prefix of mp;
+ //
+ return mp.sub (id) ? idf : 0;
+}
+
+semantics::data_member* context::
+object_pointer (data_member_path const& mp)
+{
+ for (data_member_path::const_reverse_iterator i (mp.rbegin ());
+ i != mp.rend (); ++i)
+ {
+ if (object_pointer (utype (**i)))
+ return *i;
+ }
+
+ return 0;
+}
+
+bool context::
+readonly (data_member_path const& mp, data_member_scope const& ms)
+{
+ assert (mp.size () == ms.size ());
+
+ data_member_scope::const_reverse_iterator si (ms.rbegin ());
+
+ for (data_member_path::const_reverse_iterator pi (mp.rbegin ());
+ pi != mp.rend ();
+ ++pi, ++si)
+ {
+ semantics::data_member& m (**pi);
+
+ if (m.count ("readonly"))
+ return true;
+
+ // Check if any of the classes in the inheritance chain for the
+ // class containing this member are readonly.
+ //
+ class_inheritance_chain const& ic (*si);
+
+ assert (ic.back () == &m.scope ());
+
+ for (class_inheritance_chain::const_reverse_iterator ci (ic.rbegin ());
+ ci != ic.rend ();
+ ++ci)
+ {
+ semantics::class_& c (**ci);
+
+ if (c.count ("readonly"))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool context::
+readonly (semantics::data_member& m)
+{
+ if (m.count ("readonly"))
+ return true;
+
+ // Check if the whole class (object or composite value) is marked
+ // as readonly.
+ //
+ if (m.scope ().count ("readonly"))
+ return true;
+
+ return false;
+}
+
+bool context::
+null (data_member_path const& mp) const
+{
+ // Outer members can override the null-ability of the inner ones. So
+ // start from the most outer member.
+ //
+ for (data_member_path::const_iterator i (mp.begin ()); i != mp.end (); ++i)
+ {
+ if (null (**i))
+ return true;
+ }
+
+ return false;
+}
+
+bool context::
+null (semantics::data_member& m) const
+{
+ semantics::names* hint;
+ semantics::type& t (utype (m, hint));
+
+ if (object_pointer (t))
+ {
+ // By default pointers can be null.
+ //
+ if (m.count ("null"))
+ return true;
+
+ if (!m.count ("not-null"))
+ {
+ if (t.count ("null"))
+ return true;
+
+ if (!t.count ("not-null"))
+ return true;
+ }
+
+ return false;
+ }
+ else
+ {
+ // Everything else by default is not null.
+ //
+ if (m.count ("null"))
+ return true;
+
+ if (!m.count ("not-null"))
+ {
+ if (t.count ("null"))
+ return true;
+
+ if (!t.count ("not-null"))
+ {
+ semantics::type* pt;
+
+ // Check if this type is a wrapper.
+ //
+ if (t.get<bool> ("wrapper"))
+ {
+ // First see if it is null by default.
+ //
+ if (t.get<bool> ("wrapper-null-handler") &&
+ t.get<bool> ("wrapper-null-default"))
+ return true;
+
+ // Otherwise, check the wrapped type.
+ //
+ pt = t.get<semantics::type*> ("wrapper-type");
+ hint = t.get<semantics::names*> ("wrapper-hint");
+ pt = &utype (*pt, hint);
+
+ if (pt->count ("null"))
+ return true;
+
+ if (pt->count ("not-null"))
+ return false;
+ }
+ else
+ pt = &t;
+ }
+ }
+
+ return false;
+ }
+}
+
+bool context::
+null (semantics::data_member& m, string const& kp) const
+{
+ if (kp.empty ())
+ return null (m);
+
+ semantics::type& c (utype (m));
+ semantics::type& t (utype (m, kp));
+
+ if (object_pointer (t))
+ {
+ if (m.count (kp + "-null"))
+ return true;
+
+ if (!m.count (kp + "-not-null"))
+ {
+ if (c.count (kp + "-null"))
+ return true;
+
+ if (!c.count (kp + "-not-null"))
+ {
+ if (t.count ("null"))
+ return true;
+
+ if (!t.count ("not-null"))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+ else
+ {
+ if (m.count (kp + "-null"))
+ return true;
+
+ if (!m.count (kp + "-not-null"))
+ {
+ if (c.count (kp + "-null"))
+ return true;
+
+ if (!c.count (kp + "-not-null"))
+ {
+ if (t.count ("null"))
+ return true;
+
+ if (!t.count ("not-null"))
+ {
+ semantics::type* pt;
+
+ // Check if this type is a wrapper.
+ //
+ if (t.get<bool> ("wrapper"))
+ {
+ // First see if it is null by default.
+ //
+ if (t.get<bool> ("wrapper-null-handler") &&
+ t.get<bool> ("wrapper-null-default"))
+ return true;
+
+ // Otherwise, check the wrapped type.
+ //
+ pt = t.get<semantics::type*> ("wrapper-type");
+ pt = &utype (*pt);
+
+ if (pt->count ("null"))
+ return true;
+
+ if (pt->count ("not-null"))
+ return false;
+ }
+ else
+ pt = &t;
+ }
+ }
+ }
+
+ return false;
+ }
+}
+
+size_t context::
+polymorphic_depth (semantics::class_& c)
+{
+ if (c.count ("polymorphic-depth"))
+ return c.get<size_t> ("polymorphic-depth");
+
+ // Calculate our hierarchy depth (number of classes).
+ //
+ using semantics::class_;
+
+ class_* root (polymorphic (c));
+ assert (root != 0);
+
+ size_t r (1); // One for the root.
+
+ for (class_* b (&c); b != root; b = &polymorphic_base (*b))
+ ++r;
+
+ c.set ("polymorphic-depth", r);
+ return r;
+}
+
+context::class_kind_type context::
+class_kind (semantics::class_& c)
+{
+ if (object (c))
+ return class_object;
+ else if (view (c))
+ return class_view;
+ else if (composite (c))
+ return class_composite;
+ else
+ return class_other;
+}
+
+string context::
+class_name (semantics::class_& c)
+{
+ return c.is_a<semantics::class_instantiation> ()
+ ? c.get<semantics::names*> ("tree-hint")->name ()
+ : c.name ();
+}
+
+string context::
+class_fq_name (semantics::class_& c)
+{
+ return c.is_a<semantics::class_instantiation> ()
+ ? c.fq_name (c.get<semantics::names*> ("tree-hint"))
+ : c.fq_name ();
+}
+
+semantics::scope& context::
+class_scope (semantics::class_& c)
+{
+ return c.is_a<semantics::class_instantiation> ()
+ ? c.get<semantics::names*> ("tree-hint")->scope ()
+ : c.scope ();
+}
+
+semantics::path context::
+class_file (semantics::class_& c)
+{
+ // If we have an explicit definition location, use that.
+ //
+ if (c.count ("definition"))
+ return semantics::path (LOCATION_FILE (c.get<location_t> ("definition")));
+ //
+ // Otherwise, if it is a template instantiation, use the location
+ // of the qualifier.
+ //
+ else if (c.is_a<semantics::class_instantiation> ())
+ return semantics::path (LOCATION_FILE (c.get<location_t> ("location")));
+ else
+ return c.file ();
+}
+
+location_t context::
+class_location (semantics::class_& c)
+{
+ return c.count ("definition")
+ ? c.get<location_t> ("definition")
+ : class_real_location (c);
+}
+
+location_t context::
+class_real_location (semantics::class_& c)
+{
+ return c.is_a<semantics::class_instantiation> ()
+ ? c.get<location_t> ("location")
+ : real_source_location (TYPE_NAME (c.tree_node ()));
+}
+
+string context::
+upcase (string const& s)
+{
+ string r;
+ string::size_type n (s.size ());
+
+ r.reserve (n);
+
+ for (string::size_type i (0); i < n; ++i)
+ r.push_back (toupper (s[i]));
+
+ return r;
+}
+
+void context::
+diverge (streambuf* sb)
+{
+ data_->os_stack_.push (data_->os_.rdbuf ());
+ data_->os_.rdbuf (sb);
+}
+
+void context::
+restore ()
+{
+ data_->os_.rdbuf (data_->os_stack_.top ());
+ data_->os_stack_.pop ();
+}
+
+semantics::type& context::
+utype (semantics::type& t)
+{
+ if (semantics::qualifier* q = dynamic_cast<semantics::qualifier*> (&t))
+ return q->base_type ();
+ else
+ return t;
+}
+
+semantics::type& context::
+utype (semantics::type& t, semantics::names*& hint)
+{
+ if (semantics::qualifier* q = dynamic_cast<semantics::qualifier*> (&t))
+ {
+ hint = q->qualifies ().hint ();
+ return q->base_type ();
+ }
+ else
+ return t;
+}
+
+semantics::type& context::
+utype (semantics::data_member& m,
+ semantics::names*& hint,
+ string const& kp,
+ const custom_cxx_type** translation)
+{
+ semantics::type* t (0);
+
+ if (kp.empty ())
+ {
+ t = &m.type ();
+
+ if (semantics::qualifier* q = dynamic_cast<semantics::qualifier*> (t))
+ {
+ hint = q->qualifies ().hint ();
+ t = &q->base_type ();
+ }
+ else
+ hint = m.belongs ().hint ();
+ }
+ else
+ {
+ if (m.count (kp + "-tree-type"))
+ t = indirect_type (m, kp, hint);
+ else
+ {
+ t = &utype (m);
+
+ // "See through" wrappers.
+ //
+ if (semantics::type* wt = wrapper (*t))
+ t = indirect_type (utype (*wt), kp, hint);
+ else
+ t = indirect_type (*t, kp, hint);
+ }
+ }
+
+ // Do we need to map this type?
+ //
+ // @@ Need to cache the result on the member.
+ //
+ if (translation != 0)
+ *translation = 0;
+
+ for (semantics::scope* s (&m.scope ());; s = &s->scope_ ())
+ {
+ using semantics::namespace_;
+
+ if (namespace_* ns = dynamic_cast<namespace_*> (s))
+ {
+ if (ns->extension ())
+ s = &ns->original ();
+ }
+
+ if (s->count ("custom-cxx-type-map"))
+ {
+ typedef custom_cxx_type_map map;
+
+ map& m (s->get<map> ("custom-cxx-type-map"));
+ map::const_iterator i (m.find (t));
+
+ if (i != m.end ())
+ {
+ hint = i->second->as_hint;
+ t = i->second->as;
+
+ if (translation != 0)
+ *translation = i->second;
+
+ // Currently we only support one level of mapping, but I am
+ // sure someone will want multiple levels.
+ //
+ break;
+ }
+ }
+
+ if (!s->named_p () || s->global_scope ())
+ break;
+ }
+
+ return *t;
+}
+
+bool context::
+const_type (semantics::type& t)
+{
+ if (semantics::qualifier* q = dynamic_cast<semantics::qualifier*> (&t))
+ return q->const_ ();
+
+ return false;
+}
+
+string context::
+type_ref_type (semantics::type& t,
+ semantics::names* hint,
+ bool mc,
+ string const& var,
+ bool decay)
+{
+ using semantics::array;
+ string r;
+
+ // Note that trailing const syntax is used for a reason (consider
+ // t == const foo*). We may also have to decay then top-level array.
+ //
+ array* a;
+ if (decay && (a = dynamic_cast<array*> (&utype (t))) != 0)
+ {
+ semantics::type& bt (a->base_type ());
+ hint = a->contains ().hint ();
+
+ if (bt.is_a<array> ())
+ {
+ // If we need to add/strip const or no name was used in the
+ // declaration, then create an array declaration (e.g., for
+ // char x[2][3] we will have char const (*x)[3]).
+ //
+ if (mc != const_type (t) || hint == 0)
+ return type_val_type (bt, 0, mc, "(*" + var + ")");
+ }
+
+ // Array base type is always cvr-unqualified.
+ //
+ if (mc)
+ r = bt.fq_name (hint) + " const";
+ else
+ r = bt.fq_name (hint);
+
+ r += '*';
+
+ if (!var.empty ())
+ r += ' ' + var;
+ }
+ else
+ {
+ if (mc == const_type (t))
+ r = t.fq_name (hint);
+ else if (mc)
+ r = t.fq_name (hint) + " const";
+ else
+ {
+ semantics::type& ut (utype (t, hint));
+ r = ut.fq_name (hint);
+ }
+
+ r += '&';
+
+ if (!var.empty ())
+ r += ' ' + var;
+ }
+
+ return r;
+}
+
+string context::
+type_val_type (semantics::type& t,
+ semantics::names* hint,
+ bool mc,
+ string const& var)
+{
+ using semantics::array;
+ string r;
+
+ // Arrays are a complicated case. Firstly, we may need to add/strip const
+ // to/from the base type. Secondly, the array dimensions are written after
+ // the variable name. All this is further complicated by multiple dimensions.
+ // Thanks, Dennis!
+ //
+ if (array* a = dynamic_cast<array*> (&utype (t)))
+ {
+ semantics::type& bt (a->base_type ());
+
+ // If we don't need to add/strip const and a name was used in the
+ // declaration, then use that name.
+ //
+ if (mc == const_type (t) && hint != 0)
+ {
+ r = t.fq_name (hint);
+
+ if (!var.empty ())
+ r += ' ' + var;
+ }
+ else
+ {
+ // Otherwise, construct the array declaration.
+ //
+ string v (var);
+ v += '[';
+ ostringstream ostr;
+ ostr << a->size ();
+ v += ostr.str ();
+
+ if (a->size () > 0xFFFFFFFF)
+ v += "ULL";
+ else if (a->size () > 2147483647)
+ v += "U";
+
+ v += ']';
+
+ r = type_val_type (bt, a->contains ().hint (), mc, v);
+ }
+ }
+ else
+ {
+ if (mc == const_type (t))
+ r = t.fq_name (hint);
+ else if (mc)
+ r = t.fq_name (hint) + " const";
+ else
+ {
+ semantics::type& ut (utype (t, hint));
+ r = ut.fq_name (hint);
+ }
+
+ if (!var.empty ())
+ r += ' ' + var;
+ }
+
+ return r;
+}
+
+void context::
+set_member (semantics::data_member& m,
+ const string& obj,
+ const string& val,
+ const string& db,
+ const string& type)
+{
+ member_access& ma (m.get<member_access> ("set"));
+
+ // If this is a custom expression, output the location of where
+ // it came from.
+ //
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ if (ma.placeholder ())
+ {
+ // Cast the database to the concrete type this code is generated
+ // for. This way the user is free to use either the concrete or
+ // the common.
+ //
+ string d;
+ if (!db.empty ())
+ d = "static_cast<" + context::db.string () + "::database&> (" + db + ")";
+
+ os << ma.translate (obj, val, d) << ";";
+ }
+ else
+ {
+ // If this member is const and we have a synthesized direct access,
+ // then cast away constness. Otherwise, we assume that the user-
+ // provided expression handles this.
+ //
+ bool cast (!type.empty () && ma.direct () && const_member (m));
+ if (cast)
+ os << "const_cast< " << type << "& > (" << endl;
+
+ os << ma.translate (obj);
+
+ if (cast)
+ os << ")";
+
+ os << " = " << val << ";";
+ }
+}
+
+void context::
+inc_member (semantics::data_member& m,
+ const string& obj,
+ const string& gobj,
+ const string& type)
+{
+ member_access& ma (m.get<member_access> ("set"));
+
+ // If this is a custom expression, output the location of where
+ // it came from.
+ //
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ if (ma.placeholder ())
+ {
+ member_access& gma (m.get<member_access> ("get"));
+
+ if (!gma.synthesized)
+ os << "// From " << location_string (gma.loc, true) << endl;
+
+ os << ma.translate (obj, gma.translate (gobj) + " + 1") << ";";
+ }
+ else
+ {
+ // If this member is const and we have a synthesized direct access,
+ // then cast away constness. Otherwise, we assume that the user-
+ // provided expression handles this.
+ //
+ os << "++";
+
+ bool cast (ma.direct () && const_member (m));
+ if (cast)
+ os << "const_cast< " << type << "& > (" << endl;
+
+ os << ma.translate (obj);
+
+ if (cast)
+ os << ")";
+
+ os << ";";
+ }
+}
+
+void context::
+resolve_data_members (data_member_path& r,
+ semantics::class_& c,
+ const string& name,
+ const location& l,
+ cxx_string_lexer& lex)
+{
+ using semantics::class_;
+ using semantics::data_member;
+
+ // The name was already verified to be syntactically correct so
+ // we don't need to do any extra error checking in this area.
+ //
+ lex.start (name);
+
+ try
+ {
+ string tl;
+ cpp_ttype tt (lex.next (tl));
+
+ data_member& m (c.lookup<data_member> (tl, class_::include_hidden));
+
+ r.push_back (&m);
+
+ if (container (m))
+ return;
+
+ // Resolve nested members if any.
+ //
+ for (tt = lex.next (tl); tt == CPP_DOT; tt = lex.next (tl))
+ {
+ lex.next (tl); // Get CPP_NAME.
+
+ data_member& om (*r.back ());
+
+ // Check that the outer member is composite and also unwrap it while
+ // at it.
+ //
+ class_* comp (composite_wrapper (utype (om)));
+ if (comp == 0)
+ {
+ error (l) << "data member '" << om.name () << "' is not composite"
+ << endl;
+ throw operation_failed ();
+ }
+
+ data_member& nm (
+ comp->lookup<data_member> (tl, class_::include_hidden));
+
+ r.push_back (&nm);
+
+ if (container (nm))
+ return;
+ }
+ }
+ catch (semantics::unresolved const& e)
+ {
+ if (e.type_mismatch)
+ error (l) << "name '" << e.name << "' does not refer to a data member"
+ << endl;
+ else
+ error (l) << "unable to resolve data member '" << e.name << endl;
+
+ throw operation_failed ();
+ }
+ catch (semantics::ambiguous const& e)
+ {
+ error (l) << "data member name '" << e.first.name () << "' is ambiguous"
+ << endl;
+
+ info (e.first.named ().location ()) << "could resolve to " <<
+ "this data member" << endl;
+
+ info (e.second.named ().location ()) << "or could resolve " <<
+ "to this data member" << endl;
+
+ throw operation_failed ();
+ }
+}
+
+bool context::
+composite_ (semantics::class_& c)
+{
+ bool r (c.count ("value") && !c.count ("simple") && !c.count ("container"));
+ c.set ("composite-value", r);
+ return r;
+}
+
+// context::table_prefix
+//
+context::table_prefix::
+table_prefix (semantics::class_& c)
+ : level (1)
+{
+ context& ctx (context::current ());
+
+ ns_schema = ctx.schema (class_scope (c));
+ ns_prefix = ctx.table_name_prefix (class_scope (c));
+ prefix = ctx.table_name (c, &derived);
+ prefix += "_";
+}
+
+void context::table_prefix::
+append (semantics::data_member& m)
+{
+ assert (level > 0);
+
+ context& ctx (context::current ());
+
+ // If a custom table prefix was specified, then ignore the top-level
+ // table prefix (this corresponds to a container directly inside an
+ // object) but keep the schema unless the alternative schema is fully
+ // qualified.
+ //
+ if (m.count ("table"))
+ {
+ qname p, n (m.get<qname> ("table"));
+
+ if (n.fully_qualified ())
+ p = n.qualifier ();
+ else
+ {
+ if (n.qualified ())
+ {
+ p = ns_schema;
+ p.append (n.qualifier ());
+ }
+ else
+ p = prefix.qualifier ();
+ }
+
+ if (level == 1)
+ {
+ p.append (ns_prefix);
+ derived = false;
+ }
+ else
+ p.append (prefix.uname ());
+
+ p += n.uname ();
+ prefix.swap (p);
+ }
+ // Otherwise use the member name and add an underscore unless it is
+ // already there.
+ //
+ else
+ {
+ string name (ctx.public_name_db (m));
+ size_t n (name.size ());
+
+ prefix += name;
+
+ if (n != 0 && name[n - 1] != '_')
+ prefix += "_";
+
+ derived = true;
+ }
+
+ level++;
+}
+
+qname context::
+schema (semantics::scope& s) const
+{
+ if (s.count ("qualified-schema"))
+ return s.get<qname> ("qualified-schema");
+
+ qname r;
+
+ for (semantics::scope* ps (&s);; ps = &ps->scope_ ())
+ {
+ using semantics::namespace_;
+
+ namespace_* ns (dynamic_cast<namespace_*> (ps));
+
+ if (ns == 0) // Some other scope.
+ {
+ if (!ps->named_p ())
+ break;
+
+ continue;
+ }
+
+ if (ns->extension ())
+ ns = &ns->original ();
+
+ bool sf (ns->count ("schema"));
+ bool tf (ns->count ("table"));
+
+ if (tf)
+ {
+ qname n (ns->get<qname> ("table"));
+ tf = n.qualified ();
+
+ // If we have both schema and qualified table prefix, see which
+ // takes precedence based on order.
+ //
+
+ if (tf && sf)
+ {
+ if (ns->get<location_t> ("table-location") >
+ ns->get<location_t> ("schema-location"))
+ sf = false;
+ else
+ tf = false;
+ }
+ }
+
+ if (sf || tf)
+ {
+ qname n (
+ sf
+ ? ns->get<qname> ("schema")
+ : ns->get<qname> ("table").qualifier ());
+ n.append (r);
+ n.swap (r);
+ }
+
+ if (r.fully_qualified () ||
+ ns->global_scope ()) // Note: namespaces always named.
+ break;
+ }
+
+ // If we are still not fully qualified, add the schema that was
+ // specified on the command line.
+ //
+ if (!r.fully_qualified () && options.schema ().count (db) != 0)
+ {
+ qname n (options.schema ()[db]);
+ n.append (r);
+ n.swap (r);
+ }
+
+ s.set ("qualified-schema", r);
+ return r;
+}
+
+string context::
+table_name_prefix (semantics::scope& s) const
+{
+ if (s.count ("table-prefix"))
+ return s.get<string> ("table-prefix");
+
+ string r;
+
+ for (semantics::scope* ps (&s);; ps = &ps->scope_ ())
+ {
+ using semantics::namespace_;
+
+ namespace_* ns (dynamic_cast<namespace_*> (ps));
+
+ if (ns == 0) // Some other scope.
+ {
+ if (!ps->named_p ())
+ break;
+
+ continue;
+ }
+
+ if (ns->extension ())
+ ns = &ns->original ();
+
+ if (ns->count ("table"))
+ {
+ qname n (ns->get<qname> ("table"));
+ r = n.uname () + r;
+ }
+
+ if (ns->global_scope ()) // Note: namespaces always named.
+ break;
+ }
+
+ // Add the prefix that was specified on the command line.
+ //
+ if (options.table_prefix ().count (db) != 0)
+ r = options.table_prefix ()[db] + r;
+
+ s.set ("table-prefix", r);
+ return r;
+}
+
+qname context::
+table_name (semantics::class_& c, bool* pd) const
+{
+ if (c.count ("qualified-table"))
+ return c.get<qname> ("qualified-table");
+
+ qname r;
+ bool sf (c.count ("schema"));
+ bool derived;
+
+ if (c.count ("table"))
+ {
+ r = c.get<qname> ("table");
+
+ if (sf)
+ {
+ // If we have both schema and qualified table, see which takes
+ // precedence based on order. If the table is unqualifed, then
+ // add the schema.
+ //
+ sf = !r.qualified () ||
+ c.get<location_t> ("table-location") <
+ c.get<location_t> ("schema-location");
+ }
+
+ derived = false;
+ }
+ else
+ {
+ r = class_name (c);
+ derived = true;
+ }
+
+ if (sf)
+ {
+ qname n (c.get<qname> ("schema"));
+ n.append (r.uname ());
+ n.swap (r);
+ }
+
+ // Unless we are fully qualified, add any schemas that were
+ // specified on the namespaces and/or with the command line
+ // option.
+ //
+ if (!r.fully_qualified ())
+ {
+ qname n (schema (class_scope (c)));
+ n.append (r);
+ n.swap (r);
+ }
+
+ // Add the table prefix if any.
+ //
+ r.uname () = table_name_prefix (class_scope (c)) + r.uname ();
+
+ if (derived)
+ r.uname () = transform_name (r.uname (), sql_name_table);
+
+ c.set ("qualified-table", r);
+
+ if (pd != 0)
+ *pd = derived;
+
+ return r;
+}
+
+qname context::
+table_name (semantics::class_& obj, data_member_path const& mp) const
+{
+ table_prefix tp (obj);
+
+ if (mp.size () == 1)
+ {
+ // Container directly in the object.
+ //
+ return table_name (*mp.back (), tp);
+ }
+ else
+ {
+ data_member_path::const_iterator i (mp.begin ());
+
+ // The last member is the container.
+ //
+ for (data_member_path::const_iterator e (mp.end () - 1); i != e; ++i)
+ tp.append (**i);
+
+ return table_name (**i, tp);
+ }
+}
+
+// The table prefix passed as the second argument must include the table
+// prefix specified on namespaces and with the --table-prefix option.
+//
+qname context::
+table_name (semantics::data_member& m, table_prefix const& p) const
+{
+ assert (p.level > 0);
+ qname r;
+ string rn;
+ bool derived; // Any of the components in the table name is derived.
+
+ // If a custom table name was specified, then ignore the top-level
+ // table prefix (this corresponds to a container directly inside an
+ // object). If the container table is unqualifed, then we use the
+ // object schema. If it is fully qualified, then we use that name.
+ // Finally, if it is qualified but not fully qualifed, then we
+ // append the object's namespace schema.
+ //
+ if (m.count ("table"))
+ {
+ qname n (m.get<qname> ("table"));
+
+ if (n.fully_qualified ())
+ r = n.qualifier ();
+ else
+ {
+ if (n.qualified ())
+ {
+ r = p.ns_schema;
+ r.append (n.qualifier ());
+ }
+ else
+ r = p.prefix.qualifier ();
+ }
+
+ if (p.level == 1)
+ {
+ rn = p.ns_prefix;
+ derived = false;
+ }
+ else
+ {
+ rn = p.prefix.uname ();
+ derived = p.derived;
+ }
+
+ rn += n.uname ();
+ }
+ else
+ {
+ r = p.prefix.qualifier ();
+ rn = p.prefix.uname () + public_name_db (m);
+ derived = true;
+ }
+
+ if (derived)
+ r.append (transform_name (rn, sql_name_table));
+ else
+ r.append (rn);
+
+ return r;
+}
+
+string context::
+table_options (semantics::class_& c)
+{
+ string r;
+
+ // Accumulate options from class.
+ //
+ // @@ Should we also get them from bases?
+ //
+ // @@ Note for some databases (like SQLite), options are seperated with
+ // comma, not space. Likely the same issue in the column_options().
+ //
+ if (c.count ("options"))
+ {
+ strings const& o (c.get<strings> ("options"));
+
+ for (strings::const_iterator i (o.begin ()); i != o.end (); ++i)
+ {
+ if (i->empty ())
+ r.clear ();
+ else
+ {
+ if (!r.empty ())
+ r += ' ';
+
+ r += *i;
+ }
+ }
+ }
+
+ return r;
+}
+
+string context::
+table_options (semantics::data_member& m, semantics::type& c)
+{
+ string r;
+
+ // Accumulate options from container and member.
+ //
+ // @@ Currently there is no way to assign options to the container type. If
+ // we use the value specifier, then it ends up being treated as a value
+ // type. To support this we will probably need to invent the container
+ // specifier.
+ //
+ if (c.count ("options"))
+ {
+ strings const& o (c.get<strings> ("options"));
+
+ for (strings::const_iterator i (o.begin ()); i != o.end (); ++i)
+ {
+ if (i->empty ())
+ r.clear ();
+ else
+ {
+ if (!r.empty ())
+ r += ' ';
+
+ r += *i;
+ }
+ }
+ }
+
+ if (m.count ("options"))
+ {
+ strings const& o (m.get<strings> ("options"));
+
+ for (strings::const_iterator i (o.begin ()); i != o.end (); ++i)
+ {
+ if (i->empty ())
+ r.clear ();
+ else
+ {
+ if (!r.empty ())
+ r += ' ';
+
+ r += *i;
+ }
+ }
+ }
+
+ return r;
+}
+
+// context::column_prefix
+//
+context::column_prefix::
+column_prefix (data_member_path const& mp, bool l)
+ : derived (false), underscore (false)
+{
+ if (mp.size () < (l ? 1 : 2))
+ return;
+
+ for (data_member_path::const_iterator i (mp.begin ()),
+ e (mp.end () - (l ? 0 : 1)); i != e; ++i)
+ append (**i);
+}
+
+void context::column_prefix::
+append (semantics::data_member& m, string const& kp, string const& dn)
+{
+ bool d;
+ context& ctx (context::current ());
+
+ if (kp.empty ())
+ prefix += ctx.column_name (m, d);
+ else
+ prefix += ctx.column_name (m, kp, dn, d);
+
+ // If the user provided the column prefix, then use it verbatime.
+ // Otherwise, append the underscore, unless it is already there.
+ //
+ underscore = false;
+ if (d)
+ {
+ size_t n (prefix.size ());
+
+ if (n != 0 && prefix[n - 1] != '_')
+ {
+ prefix += '_';
+ underscore = true;
+ }
+ }
+
+ derived = derived || d;
+}
+
+string context::
+column_name (semantics::data_member& m, bool& derived) const
+{
+ derived = !m.count ("column");
+ return derived
+ ? public_name_db (m)
+ : m.get<table_column> ("column").column;
+}
+
+string context::
+column_name (semantics::data_member& m, column_prefix const& cp) const
+{
+ bool d;
+ const string& cn (column_name (m, d));
+ string n (cp.prefix);
+
+ if (cn.empty () && cp.underscore)
+ n.resize (n.size () - 1); // Strip underscore that was auto added.
+
+ n += cn;
+
+ // If any component is derived, then run it through the SQL name regex.
+ //
+ if (d || cp.derived)
+ n = transform_name (n, sql_name_column);
+
+ return n;
+}
+
+string context::
+column_name (semantics::data_member& m,
+ string const& p,
+ string const& d,
+ bool& derived) const
+{
+ if (p.empty () && d.empty ())
+ return column_name (m, derived);
+
+ // A container column name can be specified for the member or for the
+ // container type.
+ //
+ string key (p + "-column");
+ derived = false;
+
+ if (m.count (key))
+ return m.get<string> (key);
+ else
+ {
+ semantics::type& t (utype (m));
+
+ if (t.count (key))
+ return t.get<string> (key);
+ }
+
+ derived = true;
+ return d;
+}
+
+string context::
+column_name (semantics::data_member& m,
+ string const& kp,
+ string const& dn,
+ column_prefix const& cp) const
+{
+ bool d;
+ const string& cn (column_name (m, kp, dn, d));
+ string n (cp.prefix);
+
+ if (cn.empty () && cp.underscore)
+ n.resize (n.size () - 1); // Strip underscore that was auto-added.
+
+ n += cn;
+
+ // If any component is derived, the run it through the SQL name regex.
+ //
+ if (d || cp.derived)
+ n = transform_name (n, sql_name_column);
+
+ return n;
+}
+
+string context::
+column_name (data_member_path const& mp) const
+{
+ return column_name (*mp.back (), column_prefix (mp));
+}
+
+string context::
+column_type (const data_member_path& mp, string const& kp, bool id)
+{
+ if (kp.empty ())
+ {
+ // Return the id type if this member is or is a part of an object id
+ // or pointer to object.
+ //
+ return mp.back ()->get<string> (
+ id || context::id (mp) || object_pointer (mp)
+ ? "column-id-type"
+ : "column-type");
+ }
+ else
+ return indirect_value<string> (*mp.back (), kp + "-column-type");
+}
+
+string context::
+column_type (semantics::data_member& m, string const& kp)
+{
+ return kp.empty ()
+ ? m.get<string> ("column-type")
+ : indirect_value<string> (m, kp + "-column-type");
+}
+
+string context::
+column_options (semantics::data_member& m)
+{
+ // Accumulate options from both type and member.
+ //
+ semantics::type* t (&utype (m));
+
+ if (semantics::class_* p = object_pointer (*t))
+ t = &utype (*id_member (*p));
+
+ if (semantics::type* w = wrapper (*t))
+ t = w;
+
+ string r;
+
+ if (t->count ("options"))
+ {
+ strings const& o (t->get<strings> ("options"));
+
+ for (strings::const_iterator i (o.begin ()); i != o.end (); ++i)
+ {
+ if (i->empty ())
+ r.clear ();
+ else
+ {
+ if (!r.empty ())
+ r += ' ';
+
+ r += *i;
+ }
+ }
+ }
+
+ if (m.count ("options"))
+ {
+ strings const& o (m.get<strings> ("options"));
+
+ for (strings::const_iterator i (o.begin ()); i != o.end (); ++i)
+ {
+ if (i->empty ())
+ r.clear ();
+ else
+ {
+ if (!r.empty ())
+ r += ' ';
+
+ r += *i;
+ }
+ }
+ }
+
+ return r;
+}
+
+string context::
+column_options (semantics::data_member& m, string const& kp)
+{
+ if (kp.empty ())
+ return column_options (m);
+
+ string k (kp + "-options");
+
+ // Accumulate options from type, container, and member.
+ //
+ semantics::type& c (utype (m));
+ semantics::type* t (&utype (m, kp));
+
+ if (semantics::class_* p = object_pointer (*t))
+ t = &utype (*id_member (*p));
+
+ if (semantics::type* w = wrapper (*t))
+ t = w;
+
+ string r;
+
+ if (t->count ("options"))
+ {
+ strings const& o (t->get<strings> ("options"));
+
+ for (strings::const_iterator i (o.begin ()); i != o.end (); ++i)
+ {
+ if (i->empty ())
+ r.clear ();
+ else
+ {
+ if (!r.empty ())
+ r += ' ';
+
+ r += *i;
+ }
+ }
+ }
+
+ if (c.count (k))
+ {
+ strings const& o (c.get<strings> (k));
+
+ for (strings::const_iterator i (o.begin ()); i != o.end (); ++i)
+ {
+ if (i->empty ())
+ r.clear ();
+ else
+ {
+ if (!r.empty ())
+ r += ' ';
+
+ r += *i;
+ }
+ }
+ }
+
+ if (m.count (k))
+ {
+ strings const& o (m.get<strings> (k));
+
+ for (strings::const_iterator i (o.begin ()); i != o.end (); ++i)
+ {
+ if (i->empty ())
+ r.clear ();
+ else
+ {
+ if (!r.empty ())
+ r += ' ';
+
+ r += *i;
+ }
+ }
+ }
+
+ return r;
+}
+
+context::type_map_type::const_iterator context::type_map_type::
+find (semantics::type& t, semantics::names* hint)
+{
+ const_iterator e (end ()), i (e);
+
+ // First check the hinted name. This allows us to handle things like
+ // size_t which is nice to map to the same type irrespective of the
+ // actual type. Since this type can be an alias for the one we are
+ // interested in, go into nested hints.
+ //
+ for (; hint != 0 && i == e; hint = hint->hint ())
+ i = base::find (t.fq_name (hint));
+
+ // If the hinted name didn't work, try the primary name (e.g.,
+ // ::std::string) instead of a user typedef (e.g., my_string).
+ //
+ if (i == e)
+ i = base::find (t.fq_name ());
+
+ return i;
+}
+
+string context::
+database_type_impl (semantics::type& t,
+ semantics::names* hint,
+ bool id,
+ bool* null)
+{
+ using semantics::enum_;
+
+ // By default map an enum as its underlying type.
+ //
+ if (enum_* e = dynamic_cast<enum_*> (&t))
+ return database_type_impl (
+ e->underlying_type (), e->underlying_type_hint (), id, null);
+
+ // Built-in type mapping.
+ //
+ type_map_type::const_iterator i (data_->type_map_.find (t, hint));
+ if (i != data_->type_map_.end ())
+ {
+ if (null != 0)
+ *null = i->second.null;
+ return id ? i->second.id_type : i->second.type;
+ }
+
+ return string ();
+}
+
+static string
+public_name_impl (semantics::data_member& m)
+{
+ string s (m.name ());
+ size_t n (s.size ());
+
+ // Do basic processing: remove trailing and leading underscores
+ // as well as the 'm_' prefix.
+ //
+ // @@ What if the resulting names conflict?
+ //
+ size_t b (0), e (n - 1);
+
+ if (n > 2 && s[0] == 'm' && s[1] == '_')
+ b += 2;
+
+ for (; b <= e && s[b] == '_'; b++) ;
+ for (; e >= b && s[e] == '_'; e--) ;
+
+ return b > e ? s : string (s, b, e - b + 1);
+}
+
+string context::
+public_name_db (semantics::data_member& m) const
+{
+ return public_name_impl (m);
+}
+
+string context::
+compose_name (string const& prefix, string const& name)
+{
+ string r (prefix);
+ size_t n (r.size ());
+
+ // Add an underscore unless one is already in the prefix or
+ // the name is empty. Similarly, remove it if it is there but
+ // the name is empty.
+ //
+ if (n != 0)
+ {
+ if (r[n - 1] != '_')
+ {
+ if (!name.empty ())
+ r += '_';
+ }
+ else
+ {
+ if (name.empty ())
+ r.resize (n - 1);
+ }
+ }
+
+ r += name;
+ return r;
+}
+
+string context::
+transform_name (string const& name, sql_name_type type) const
+{
+ string r;
+
+ if (!data_->sql_name_regex_[type].empty () ||
+ !data_->sql_name_regex_[sql_name_all].empty ())
+ {
+ bool t (options.sql_name_regex_trace ());
+
+ if (t)
+ cerr << "name: '" << name << "'" << endl;
+
+ bool found (false);
+
+ // First try the type-specific transformations, if that didn't work,
+ // try common transformations.
+ //
+ for (unsigned short j (0); !found && j < 2; ++j)
+ {
+ regex_mapping const& rm = data_->sql_name_regex_[
+ j == 0 ? type : sql_name_all];
+
+ for (regex_mapping::const_iterator i (rm.begin ()); i != rm.end (); ++i)
+ {
+ if (t)
+ cerr << "try: '" << i->regex () << "' : ";
+
+ if (i->match (name))
+ {
+ r = i->replace (name);
+ found = true;
+
+ if (t)
+ cerr << "'" << r << "' : ";
+ }
+
+ if (t)
+ cerr << (found ? '+' : '-') << endl;
+
+ if (found)
+ break;
+ }
+ }
+
+ if (!found)
+ r = name;
+ }
+ else
+ r = name;
+
+ if (options.sql_name_case ().count (db) != 0)
+ {
+ switch (options.sql_name_case ()[db])
+ {
+ case name_case::upper:
+ {
+ r = data_->sql_name_upper_.replace (r);
+ break;
+ }
+ case name_case::lower:
+ {
+ r = data_->sql_name_lower_.replace (r);
+ break;
+ }
+ }
+ }
+
+ return r;
+}
+
+string context::
+public_name (semantics::data_member& m, bool e) const
+{
+ return e ? escape (public_name_impl (m)) : public_name_impl (m);
+}
+
+string context::
+flat_name (string const& fq)
+{
+ string r;
+ r.reserve (fq.size ());
+
+ for (string::size_type i (0), n (fq.size ()); i < n; ++i)
+ {
+ char c (fq[i]);
+
+ if (c == ':')
+ {
+ if (!r.empty ())
+ r += '_';
+ ++i; // Skip the second ':'.
+ }
+ else
+ r += c;
+ }
+
+ return r;
+}
+
+string context::
+escape (string const& name) const
+{
+ typedef string::size_type size;
+
+ string r;
+ size n (name.size ());
+
+ // In most common cases we will have that many characters.
+ //
+ r.reserve (n);
+
+ for (size i (0); i < n; ++i)
+ {
+ char c (name[i]);
+
+ if (i == 0)
+ {
+ if (!((c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ c == '_'))
+ r = (c >= '0' && c <= '9') ? "cxx_" : "cxx";
+ }
+
+ if (!((c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9') ||
+ c == '_'))
+ r += '_';
+ else
+ r += c;
+ }
+
+ if (r.empty ())
+ r = "cxx";
+
+ // Custom reserved words.
+ //
+ /*
+ reserved_name_map_type::const_iterator i (reserved_name_map.find (r));
+
+ if (i != reserved_name_map.end ())
+ {
+ if (!i->second.empty ())
+ return i->second;
+ else
+ r += L'_';
+ }
+ */
+
+ // Keywords
+ //
+ if (keyword_set.find (r) != keyword_set.end ())
+ {
+ r += '_';
+
+ // Re-run custom words.
+ //
+ /*
+ i = reserved_name_map.find (r);
+
+ if (i != reserved_name_map.end ())
+ {
+ if (!i->second.empty ())
+ return i->second;
+ else
+ r += L'_';
+ }
+ */
+ }
+
+ return r;
+}
+
+string context::
+make_guard (string const& s) const
+{
+ // Split words, e.g., "FooBar" to "Foo_Bar" and convert everything
+ // to upper case.
+ //
+ string r;
+ for (string::size_type i (0), n (s.size ()); i < n - 1; ++i)
+ {
+ char c1 (s[i]);
+ char c2 (s[i + 1]);
+
+ r += toupper (c1);
+
+ if (isalpha (c1) && isalpha (c2) && islower (c1) && isupper (c2))
+ r += "_";
+ }
+ r += toupper (s[s.size () - 1]);
+
+ return escape (r);
+}
+
+static string
+charlit (unsigned int u)
+{
+ string r ("\\x");
+ bool lead (true);
+
+ for (short i (7); i >= 0; --i)
+ {
+ unsigned int x ((u >> (i * 4)) & 0x0F);
+
+ if (lead)
+ {
+ if (x == 0)
+ continue;
+
+ lead = false;
+ }
+
+ r += static_cast<char> (x < 10 ? ('0' + x) : ('A' + x - 10));
+ }
+
+ return r;
+}
+
+static string
+strlit_ascii (string const& str)
+{
+ string r;
+ string::size_type n (str.size ());
+
+ // In most common cases we will have that many chars.
+ //
+ r.reserve (n + 2);
+
+ r += '"';
+
+ bool escape (false);
+
+ for (string::size_type i (0); i < n; ++i)
+ {
+ unsigned int u (static_cast<unsigned int> (str[i]));
+
+ // [128 - ] - unrepresentable
+ // 127 - \x7F
+ // [32 - 126] - as is
+ // [0 - 31] - \X or \xXX
+ //
+
+ if (u < 32 || u == 127)
+ {
+ switch (u)
+ {
+ case '\n':
+ {
+ r += "\\n";
+ break;
+ }
+ case '\t':
+ {
+ r += "\\t";
+ break;
+ }
+ case '\v':
+ {
+ r += "\\v";
+ break;
+ }
+ case '\b':
+ {
+ r += "\\b";
+ break;
+ }
+ case '\r':
+ {
+ r += "\\r";
+ break;
+ }
+ case '\f':
+ {
+ r += "\\f";
+ break;
+ }
+ case '\a':
+ {
+ r += "\\a";
+ break;
+ }
+ default:
+ {
+ r += charlit (u);
+ escape = true;
+ break;
+ }
+ }
+ }
+ else if (u < 127)
+ {
+ if (escape)
+ {
+ // Close and open the string so there are no clashes.
+ //
+ r += '"';
+ r += '"';
+
+ escape = false;
+ }
+
+ switch (u)
+ {
+ case '"':
+ {
+ r += "\\\"";
+ break;
+ }
+ case '\\':
+ {
+ r += "\\\\";
+ break;
+ }
+ default:
+ {
+ r += static_cast<char> (u);
+ break;
+ }
+ }
+ }
+ else
+ {
+ // @@ Unrepresentable character.
+ //
+ r += '?';
+ }
+ }
+
+ r += '"';
+
+ return r;
+}
+
+string context::
+strlit (string const& str)
+{
+ return strlit_ascii (str);
+}
+
+void context::
+inst_header (bool decl, bool omit_exp)
+{
+ if (decl)
+ {
+ if (!ext.empty ())
+ os << ext << " ";
+ else
+ os << "extern ";
+ }
+
+ os << "template struct";
+
+ if (!omit_exp && !exp.empty ())
+ {
+ // If we are generating an explicit instantiation directive rather
+ // than the extern template declaration, then omit the export symbol
+ // if we already have it in the header (i.e., extern symbol specified
+ // and defined). If we don't do that, then we get GCC warnings saying
+ // that the second set of visibility attributes is ignored.
+ //
+ if (!decl && !ext.empty ())
+ os << endl
+ << "#ifndef " << ext << endl
+ << options.export_symbol ()[db] << endl
+ << "#endif" << endl;
+ else
+ os << " " << exp;
+ }
+ else
+ os << " ";
+}
+
+namespace
+{
+ struct column_count_impl: object_members_base
+ {
+ column_count_impl (object_section* section = 0)
+ : object_members_base (false, section)
+ {
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ // Object pointers in views require special treatment.
+ //
+ if (view_member (m))
+ {
+ using semantics::class_;
+
+ column_count_type cc;
+
+ if (class_* root = polymorphic (c))
+ {
+ // For a polymorphic class we are going to load all the members
+ // from all the bases (i.e., equivalent to the first statement
+ // in the list of SELECT statements generated for the object).
+ // So our count should be the same as the first value in the
+ // generated column_counts array.
+ //
+ for (class_* b (&c);; b = &polymorphic_base (*b))
+ {
+ column_count_type const& ccb (column_count (*b, section_));
+
+ cc.total += ccb.total - (b != root ? ccb.id : 0);
+ cc.separate_load += ccb.separate_load;
+ cc.soft += ccb.soft;
+
+ if (b == root)
+ break;
+ }
+ }
+ else
+ cc = column_count (c, section_);
+
+ c_.total += cc.total - cc.separate_load;
+
+ if (added (member_path_) != 0 || deleted (member_path_) != 0)
+ c_.soft += cc.total;
+ else
+ c_.soft += cc.soft;
+ }
+ else
+ {
+ size_t t (c_.total);
+
+ object_members_base::traverse_pointer (m, c);
+
+ if (context::inverse (m))
+ {
+ size_t n (c_.total - t);
+
+ c_.inverse += n;
+
+ if (separate_update (member_path_))
+ c_.separate_update -= n;
+ }
+ }
+ }
+
+ virtual void
+ traverse_simple (semantics::data_member& m)
+ {
+ c_.total++;
+
+ bool ro (context::readonly (member_path_, member_scope_));
+
+ if (id ())
+ c_.id++;
+ else if (ro)
+ c_.readonly++;
+ else if (context::version (m))
+ c_.optimistic_managed++;
+
+ // For now discriminator can only be a simple value.
+ //
+ if (discriminator (m))
+ c_.discriminator++;
+
+ {
+ unsigned long long av (added (member_path_));
+ unsigned long long dv (deleted (member_path_));
+
+ // If the addition/deletion version is the same as the section's,
+ // then don't count.
+ //
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if (av != 0)
+ c_.added++;
+
+ if (dv != 0)
+ c_.deleted++;
+
+ if (av != 0 || dv != 0)
+ c_.soft++;
+ }
+
+ if (separate_load (member_path_))
+ c_.separate_load++;
+
+ if (separate_update (member_path_) && !ro)
+ c_.separate_update++;
+ }
+
+ context::column_count_type c_;
+ };
+}
+
+context::column_count_type context::
+column_count (semantics::class_& c, object_section* s)
+{
+ if (s == 0)
+ {
+ // Whole class.
+ //
+ if (!c.count ("column-count"))
+ {
+ column_count_impl t;
+ t.traverse (c);
+ c.set ("column-count", t.c_);
+ }
+
+ return c.get<column_count_type> ("column-count");
+ }
+ else
+ {
+ column_count_impl t (s);
+ t.traverse (c);
+ return t.c_;
+ }
+}
+
+namespace
+{
+ struct has_a_impl: object_members_base
+ {
+ has_a_impl (unsigned short flags, object_section* s)
+ : object_members_base ((flags & context::include_base) != 0, s),
+ r_ (0),
+ flags_ (flags)
+ {
+ }
+
+ size_t
+ result () const
+ {
+ return r_;
+ }
+
+ virtual bool
+ section_test (data_member_path const& mp)
+ {
+ object_section& s (section (mp));
+
+ // Include eager loaded members into the main section if requested.
+ //
+ return section_ == 0 ||
+ *section_ == s ||
+ ((flags_ & include_eager_load) != 0 &&
+ *section_ == main_section &&
+ !s.separate_load ());
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_&)
+ {
+ // Ignore polymorphic id references; they are represented as
+ // pointers but are normally handled in a special way.
+ //
+ if (m.count ("polymorphic-ref"))
+ return;
+
+ // Ignore added/deleted members if so requested.
+ //
+ if (check_soft ())
+ return;
+
+ if (context::is_a (member_path_, member_scope_, flags_))
+ r_++;
+
+ // No need to go inside.
+ }
+
+ virtual void
+ traverse_simple (semantics::data_member&)
+ {
+ // Ignore added/deleted members if so requested.
+ //
+ if (check_soft ())
+ return;
+
+ if (context::is_a (member_path_, member_scope_, flags_))
+ r_++;
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type&)
+ {
+ // Ignore added/deleted members if so requested.
+ //
+ if (check_soft ())
+ return;
+
+ // Ignore versioned containers if so requested.
+ //
+ if ((flags_ & exclude_versioned) != 0 && versioned (m))
+ return;
+
+ // We don't cross the container boundaries (separate table).
+ //
+ unsigned short f (flags_ & (context::test_container |
+ context::test_straight_container |
+ context::test_inverse_container |
+ context::test_readonly_container |
+ context::test_readwrite_container |
+ context::test_smart_container));
+
+ if (context::is_a (member_path_,
+ member_scope_,
+ f,
+ context::container_vt (m),
+ "value"))
+ r_++;
+ }
+
+ virtual void
+ traverse_object (semantics::class_& c)
+ {
+ if ((flags_ & context::exclude_base) == 0)
+ inherits (c);
+
+ names (c);
+ }
+
+ private:
+ bool
+ check_soft ()
+ {
+ if ((flags_ & exclude_added) != 0 || (flags_ & exclude_deleted) != 0)
+ {
+ unsigned long long av (added (member_path_));
+ unsigned long long dv (deleted (member_path_));
+
+ // If the addition/deletion version is the same as the section's,
+ // then don't exclude.
+ //
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if ((av != 0 && (flags_ & exclude_added) != 0) ||
+ (dv != 0 && (flags_ & exclude_deleted) != 0))
+ return true;
+ }
+
+ return false;
+ }
+
+ private:
+ size_t r_;
+ unsigned short flags_;
+ };
+}
+
+bool context::
+is_a (data_member_path const& mp,
+ data_member_scope const& ms,
+ unsigned short f,
+ semantics::type& t,
+ string const& kp)
+{
+ bool r (false);
+
+ semantics::data_member& m (*mp.back ());
+
+ if (f & test_pointer)
+ r = r || object_pointer (t);
+
+ if (f & test_eager_pointer)
+ r = r || (object_pointer (t) && !lazy_pointer (t));
+
+ if (f & test_lazy_pointer)
+ r = r || (object_pointer (t) && lazy_pointer (t));
+
+ semantics::type* c;
+ if ((f & (test_container |
+ test_straight_container |
+ test_inverse_container |
+ test_readonly_container |
+ test_readwrite_container |
+ test_smart_container)) != 0 &&
+ (c = container (m)) != 0)
+ {
+ if (f & test_container)
+ r = r || true;
+
+ if (f & test_straight_container)
+ r = r || !inverse (m, kp);
+
+ if (f & test_inverse_container)
+ r = r || inverse (m, kp);
+
+ if (f & test_readonly_container)
+ r = r || readonly (mp, ms);
+
+ if (f & test_readwrite_container)
+ r = r || (!inverse (m, kp) && !readonly (mp, ms));
+
+ if (f & test_smart_container)
+ r = r || (!inverse (m, kp) && !unordered (m) && container_smart (*c));
+ }
+
+ return r;
+}
+
+size_t context::
+has_a (semantics::class_& c, unsigned short flags, object_section* s)
+{
+ has_a_impl impl (flags, s);
+ impl.dispatch (c);
+ return impl.result ();
+}
+
+string context::
+process_include_path (string const& ip, bool prefix, char open)
+{
+ bool t (options.include_regex_trace ());
+ string p (prefix ? options.include_prefix () : string ());
+
+ if (!p.empty () && p[p.size () - 1] != '/')
+ p.append ("/");
+
+ string path (p + ip), r;
+
+ if (t)
+ cerr << "include: '" << path << "'" << endl;
+
+ bool found (false);
+
+ for (regex_mapping::const_iterator i (include_regex.begin ());
+ i != include_regex.end (); ++i)
+ {
+ if (t)
+ cerr << "try: '" << i->regex () << "' : ";
+
+ if (i->match (path))
+ {
+ r = i->replace (path);
+ found = true;
+
+ if (t)
+ cerr << "'" << r << "' : ";
+ }
+
+ if (t)
+ cerr << (found ? '+' : '-') << endl;
+
+ if (found)
+ break;
+ }
+
+ if (!found)
+ r = path;
+
+ // Add brackets or quotes unless the path already has them.
+ //
+ if (!r.empty () && r[0] != '"' && r[0] != '<')
+ {
+ bool b (open == '<' || (open == '\0' && options.include_with_brackets ()));
+ char op (b ? '<' : '"'), cl (b ? '>' : '"');
+ r = op + r + cl;
+ }
+
+ return r;
+}
diff --git a/odb/odb/context.hxx b/odb/odb/context.hxx
new file mode 100644
index 0000000..ec4505b
--- /dev/null
+++ b/odb/odb/context.hxx
@@ -0,0 +1,1941 @@
+// file : odb/context.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_CONTEXT_HXX
+#define ODB_CONTEXT_HXX
+
+#include <odb/gcc-fwd.hxx>
+
+#include <map>
+#include <set>
+#include <list>
+#include <stack>
+#include <vector>
+#include <string>
+#include <memory> // std::unique_ptr
+#include <ostream>
+#include <cstddef> // std::size_t
+#include <iostream>
+
+#include <libcutl/re.hxx>
+#include <libcutl/shared-ptr.hxx>
+
+#include <odb/options.hxx>
+#include <odb/features.hxx>
+#include <odb/location.hxx>
+#include <odb/cxx-token.hxx>
+#include <odb/semantics.hxx>
+#include <odb/semantics/relational/name.hxx>
+#include <odb/semantics/relational/model.hxx>
+#include <odb/traversal.hxx>
+
+using std::endl;
+using std::cerr;
+
+// Regex.
+//
+using cutl::re::regex;
+using cutl::re::regexsub;
+typedef cutl::re::format regex_format;
+
+typedef std::vector<regexsub> regex_mapping;
+
+// Forward-declarations.
+//
+class cxx_string_lexer;
+
+// Generic exception thrown to indicate a failure when diagnostics
+// has already been issued (to stderr).
+//
+class operation_failed {};
+
+// Keep this enum synchronized with the one in libodb/odb/pointer-traits.hxx.
+//
+enum pointer_kind
+{
+ pk_raw,
+ pk_unique,
+ pk_shared,
+ pk_weak
+};
+
+// Keep this enum synchronized with the one in libodb/odb/container-traits.hxx.
+//
+enum container_kind
+{
+ ck_ordered,
+ ck_set,
+ ck_multiset,
+ ck_map,
+ ck_multimap
+};
+
+// The same as class_kind in libodb/odb/traits.hxx.
+//
+enum class_kind
+{
+ class_object,
+ class_view,
+ class_composite,
+ class_other
+};
+
+// Data member path.
+//
+// If it is a direct member of an object, then we will have just
+// one member. However, if this is a member inside a composite
+// value, then we will have a "path" constructed out of members
+// that lead all the way from the object member to the innermost
+// composite value member.
+//
+struct data_member_path: std::vector<semantics::data_member*>
+{
+ data_member_path () {}
+
+ explicit
+ data_member_path (semantics::data_member& m) {push_back (&m);}
+
+ // Return true if this is a sub-path of (or equal to) the
+ // specified path.
+ //
+ bool
+ sub (const data_member_path& p) const
+ {
+ size_t n (p.size ());
+
+ if (n > size ())
+ return false;
+
+ for (size_t i (0); i != n; ++i)
+ if ((*this)[i] != p[i])
+ return false;
+
+ return true;
+ }
+};
+
+// Class inheritance chain, from the most derived to base.
+//
+typedef std::vector<semantics::class_*> class_inheritance_chain;
+
+// A list of inheritance chains for a data member in an object.
+// The first entry in this list would correspond to the object.
+// All subsequent entries, if any, correspond to composite
+// values.
+//
+typedef std::vector<class_inheritance_chain> data_member_scope;
+
+//
+// Semantic graph context types.
+//
+
+// Custom C++ type mapping.
+//
+struct custom_cxx_type
+{
+ custom_cxx_type (): type_node (0), as_node (0) {}
+
+ std::string
+ translate_to (std::string const& v) const {return translate (v, to);}
+
+ std::string
+ translate_from (std::string const& v) const {return translate (v, from);}
+
+ tree type_node;
+ std::string type_name;
+ semantics::type* type;
+ semantics::names* type_hint;
+
+ tree as_node;
+ std::string as_name;
+ semantics::type* as;
+ semantics::names* as_hint;
+
+ cxx_tokens to;
+ bool to_move; // Single (?), so can move.
+
+ cxx_tokens from;
+ bool from_move; // Single (?), so can move.
+
+ location_t loc;
+ tree scope; // Scope for which this mapping is defined.
+
+private:
+ static std::string
+ translate (std::string const&, const cxx_tokens&);
+};
+
+typedef std::vector<custom_cxx_type> custom_cxx_types;
+typedef std::map<semantics::type*, custom_cxx_type*> custom_cxx_type_map;
+
+
+// Object or view pointer.
+//
+struct class_pointer
+{
+ std::string name;
+ tree scope;
+ location_t loc;
+};
+
+//
+//
+struct default_value
+{
+ enum kind_type
+ {
+ reset, // Default value reset.
+ null,
+ boolean, // Literal contains value (true or false).
+ integer, // Integer number. Literal contains sign.
+ floating, // Floating-point number.
+ string, // Literal contains value.
+ enumerator // Literal is the name, enum_value is the tree node.
+ };
+
+ kind_type kind;
+ std::string literal;
+
+ union
+ {
+ tree enum_value;
+ unsigned long long int_value;
+ double float_value;
+ };
+};
+
+// Database potentially-qualified and unqualifed names.
+//
+using semantics::relational::qname;
+using semantics::relational::uname;
+
+// Object or table associated with the view.
+//
+struct view_object
+{
+ // Return a diagnostic name for this association. It is either the
+ // alias, unqualified object name, or string representation of the
+ // table name.
+ //
+ std::string
+ name () const;
+
+ enum kind_type { object, table };
+ enum join_type { left, right, full, inner, cross };
+
+ kind_type kind;
+ join_type join;
+ tree obj_node; // Tree node if kind is object.
+ std::string obj_name; // Name as specified in the pragma if kind is object.
+ qname tbl_name; // Table name if kind is table.
+ std::string alias;
+ tree scope;
+ location_t loc;
+ semantics::class_* obj;
+ semantics::data_member* ptr; // Corresponding object pointer, if any.
+
+ cxx_tokens cond; // Join condition tokens.
+};
+
+typedef std::vector<view_object> view_objects;
+
+// The view_alias_map does not contain entries for tables.
+//
+typedef std::map<std::string, view_object*> view_alias_map;
+typedef std::map<semantics::class_*, view_object*> view_object_map;
+
+// Collection of relationships via which the objects are joined.
+// We need this information to figure out which alias/table
+// names to use for columns corresponding to inverse object
+// pointers inside objects that this view may be loading.
+//
+// The first object is the pointer (i.e., the one containing
+// this data member) while the other is the pointee. In other
+// words, first -> second. We always store the direct (i.e.,
+// non-inverse) side of the relationship. Note also that there
+// could be multiple objects joined using the same relationship.
+//
+typedef
+std::multimap<data_member_path, std::pair<view_object*, view_object*> >
+view_relationship_map;
+
+//
+//
+struct view_query
+{
+ view_query (): distinct (false), for_update (false) {}
+
+ enum kind_type
+ {
+ runtime,
+ complete_select, // SELECT query.
+ complete_execute, // Stored procedure call.
+ condition
+ };
+
+ kind_type kind;
+ std::string literal;
+ cxx_tokens expr;
+ tree scope;
+ location_t loc;
+
+ // Result modifiers (only for condition).
+ //
+ bool distinct; // SELECT DISTINCT
+ bool for_update; // SELECT FOR UPDATE
+};
+
+//
+//
+struct table_column
+{
+ qname table;
+ std::string column;
+ bool expr; // True if column is an expression, and therefore should not
+ // be quoted.
+
+ table_column () {}
+
+ explicit
+ table_column (const std::string& c): column (c), expr (false) {}
+};
+
+//
+//
+struct column_expr_part
+{
+ enum kind_type
+ {
+ literal,
+ reference
+ };
+
+ kind_type kind;
+ std::string value;
+ qname table; // Table name/alias for references.
+ data_member_path member_path; // Path to member for references.
+
+ // Scope and location of this pragma. Used to resolve the member name.
+ //
+ tree scope;
+ location_t loc;
+};
+
+struct column_expr: std::vector<column_expr_part>
+{
+ location_t loc;
+};
+
+//
+//
+struct member_access
+{
+ member_access (const location& l, const char* k, bool s)
+ : loc (l), kind (k), synthesized (s), by_value (false) {}
+
+ // Return true of we have the (?) placeholder.
+ //
+ bool
+ placeholder () const;
+
+ // Return true if this is a synthesized expression that goes
+ // directly for the member.
+ //
+ bool
+ direct () const
+ {
+ return synthesized && expr.size () == 3; // this.member
+ }
+
+ bool
+ empty () const
+ {
+ return expr.empty ();
+ }
+
+ // Issues diagnostics and throws operation_failed if expression is
+ // empty.
+ //
+ std::string
+ translate (std::string const& obj,
+ std::string const& val = std::string (),
+ std::string const& db = std::string ()) const;
+
+ location loc;
+ const char* kind; // accessor/modifier; used for diagnostics.
+ bool synthesized; // If true, then this is a synthesized expression.
+ cxx_tokens expr;
+ bool by_value; // True if accessor returns by value. False doesn't
+ // necessarily mean that it is by reference.
+};
+
+//
+//
+struct model_version
+{
+ unsigned long long base;
+ unsigned long long current;
+ bool open;
+};
+
+// Sections.
+//
+struct object_section
+{
+ virtual bool
+ compare (object_section const&) const = 0;
+
+ virtual bool
+ separate_load () const = 0;
+
+ virtual bool
+ separate_update () const = 0;
+};
+
+inline bool
+operator== (object_section const& x, object_section const& y)
+{
+ return x.compare (y);
+}
+
+inline bool
+operator!= (object_section const& x, object_section const& y)
+{
+ return !x.compare (y);
+}
+
+// Main section.
+//
+struct main_section_type: object_section
+{
+ virtual bool
+ compare (object_section const& s) const;
+
+ virtual bool
+ separate_load () const {return false;}
+
+ virtual bool
+ separate_update () const {return false;}
+};
+
+inline bool
+operator== (main_section_type const&, main_section_type const&)
+{
+ return true; // There is only one main section.
+}
+
+extern main_section_type main_section;
+
+// User-defined section.
+//
+struct user_section: object_section
+{
+ enum load_type
+ {
+ load_eager,
+ load_lazy
+ };
+
+ enum update_type
+ {
+ update_always,
+ update_change,
+ update_manual
+ };
+
+ enum special_type
+ {
+ special_ordinary,
+ special_version // Fake section for optimistic version update.
+ };
+
+ user_section (semantics::data_member& m,
+ semantics::class_& o,
+ std::size_t i,
+ load_type l,
+ update_type u,
+ special_type s = special_ordinary)
+ : member (&m), object (&o), base (0), index (i),
+ load (l), update (u), special (s),
+ total (0), inverse (0), readonly (0), versioned (false),
+ containers (false), readwrite_containers (false),
+ versioned_containers (false), readwrite_versioned_containers (false) {}
+
+ virtual bool
+ compare (object_section const& s) const;
+
+ virtual bool
+ separate_load () const {return load != load_eager;}
+
+ virtual bool
+ separate_update () const
+ {
+ // A separately-loaded section is always separately-updated since
+ // it might not be loaded when update is requested.
+ //
+ return separate_load () || update != update_always;
+ }
+
+ bool
+ load_empty () const;
+
+ bool
+ update_empty () const;
+
+ bool
+ empty () const
+ {
+ return load_empty () && update_empty ();
+ }
+
+ // A section is optimistic if the object that contains it is optimistic.
+ // For polymorphic hierarchies, only sections contained in the root are
+ // considered optimistic.
+ //
+ bool
+ optimistic () const;
+
+ semantics::data_member* member; // Data member of this section.
+ semantics::class_* object; // Object containing this section.
+ user_section* base; // Base of this section.
+ std::size_t index; // Index of this sections.
+
+ load_type load;
+ update_type update;
+ special_type special;
+
+ // Column counts.
+ //
+ std::size_t total;
+ std::size_t inverse;
+ std::size_t readonly;
+
+ bool versioned;
+
+ bool containers;
+ bool readwrite_containers;
+
+ bool versioned_containers;
+ bool readwrite_versioned_containers;
+
+ // Total counts across overrides.
+ //
+ std::size_t
+ total_total () const
+ {
+ user_section* b (total_base ());
+ return total + (b == 0 ? 0 : b->total_total ());
+ }
+
+ std::size_t
+ total_inverse () const
+ {
+ user_section* b (total_base ());
+ return inverse + (b == 0 ? 0 : b->total_inverse ());
+ }
+
+ std::size_t
+ total_readonly () const
+ {
+ user_section* b (total_base ());
+ return readonly + (b == 0 ? 0 : b->total_readonly ());
+ }
+
+ bool
+ total_containers ()
+ {
+ user_section* b (total_base ());
+ return containers || (b != 0 && b->total_containers ());
+ }
+
+ bool
+ total_readwrite_containers ()
+ {
+ user_section* b (total_base ());
+ return readwrite_containers ||
+ (b != 0 && b->total_readwrite_containers ());
+ }
+
+private:
+ user_section*
+ total_base () const;
+};
+
+inline bool
+operator== (user_section const& x, user_section const& y)
+{
+ return x.member == y.member;
+}
+
+// Using list for pointer for element stability (see user_section::base).
+//
+struct user_sections: std::list<user_section>
+{
+ // Count sections that have something to load.
+ //
+ static unsigned short const count_load = 0x01;
+
+ // Count sections that are non-eager but have nothing to load.
+ //
+ static unsigned short const count_load_empty = 0x02;
+
+ // Count sections that have something to update.
+ //
+ static unsigned short const count_update = 0x04;
+
+ // Count sections that have nothing to update.
+ //
+ static unsigned short const count_update_empty = 0x08;
+
+ // Count sections that are optimistic.
+ //
+ static unsigned short const count_optimistic = 0x10;
+
+ // Modifiers:
+ //
+
+ // Don't exclude fake optimistic version update section from the count.
+ //
+ static unsigned short const count_special_version = 0x20;
+
+ // Only count versioned sections.
+ //
+ static unsigned short const count_versioned_only = 0x40;
+
+
+ // Count all sections, but excluding special.
+ //
+ static unsigned short const count_all = count_update |
+ count_update_empty;
+
+ static unsigned short const count_new = 0x1000;
+ static unsigned short const count_override = 0x2000;
+ static unsigned short const count_total = 0x4000;
+
+ std::size_t
+ count (unsigned short flags) const;
+
+ user_sections (semantics::class_& o): object (&o) {};
+ semantics::class_* object;
+};
+
+// Context.
+//
+class context
+{
+public:
+ typedef std::size_t size_t;
+ typedef std::string string;
+ typedef std::vector<string> strings;
+ typedef std::ostream ostream;
+
+ typedef ::options options_type;
+
+ static string
+ upcase (string const&);
+
+public:
+ // Return cvr-unqualified base of the type, or type itself, if it is
+ // not qualified.
+ //
+ static semantics::type&
+ utype (semantics::type&);
+
+ // The same as above, but also returns the name hint for the unqualified
+ // type. If the original type is already unqualified, then the hint
+ // argument is not modified.
+ //
+ static semantics::type&
+ utype (semantics::type&, semantics::names*& hint);
+
+ // The same for a member's type but also do custom C++ type translation.
+ //
+ static semantics::type&
+ utype (semantics::data_member& m, const custom_cxx_type** translation = 0)
+ {
+ semantics::names* hint (0);
+ return utype (m, hint, string (), translation);
+ }
+
+ static semantics::type&
+ utype (semantics::data_member& m,
+ string const& key_prefix,
+ const custom_cxx_type** translation = 0)
+ {
+ semantics::names* hint (0);
+ return utype (m, hint, key_prefix, translation);
+ }
+
+ // In addition to the unqualified type, this version also returns the
+ // name hint for this type. If the member type is already unqualified,
+ // then the hint is from the belongs edge. Otherwise, it is from the
+ // qualifies edge.
+ //
+ static semantics::type&
+ utype (semantics::data_member&,
+ semantics::names*& hint,
+ string const& key_prefix = string (),
+ const custom_cxx_type** translation = 0);
+
+ static semantics::type&
+ utype (const data_member_path& mp, const custom_cxx_type** translation = 0)
+ {
+ return utype (*mp.back (), translation);
+ }
+
+ static semantics::type&
+ utype (const data_member_path& mp,
+ string const& key_prefix,
+ const custom_cxx_type** translation = 0)
+ {
+ return utype (*mp.back (), key_prefix, translation);
+ }
+
+ static semantics::type&
+ utype (const data_member_path& mp,
+ semantics::names*& hint,
+ string const& key_prefix = string (),
+ const custom_cxx_type** translation = 0)
+ {
+ return utype (*mp.back (), hint, key_prefix, translation);
+ }
+
+ // For arrays this function returns true if the (innermost) element
+ // type is const.
+ //
+ static bool
+ const_type (semantics::type&);
+
+ static bool
+ const_member (semantics::data_member& m) {return const_type (m.type ());}
+
+ // Form a reference type for a not mapped, actual member type. If
+ // make_const is true, then add top-level const qualifier, unless
+ // it is already there. If it is false, then strip it if it is
+ // already there. If var is not empty, then embed the variable
+ // name into the type (e.g., char (*v)[3]). If decay_array is
+ // false then don't decay the (top-level) array to a pointer.
+ //
+ static string
+ member_ref_type (semantics::data_member& m,
+ bool make_const,
+ string const& var = "",
+ bool decay_array = true)
+ {
+ return type_ref_type (
+ m.type (), m.belongs ().hint (), make_const, var, decay_array);
+ }
+
+ static string
+ type_ref_type (semantics::type&,
+ semantics::names* hint,
+ bool make_const,
+ string const& var = "",
+ bool decay_array = true);
+
+ // Form a value type for a not mapped, actual member type. If make_const
+ // is true, then add top-level const qualifier, unless it is already
+ // there. If it is false, then strip it if it is already there. If var is
+ // not empty, then embed the variable name into the type (e.g., char v[3]).
+ //
+ static string
+ member_val_type (semantics::data_member& m,
+ bool make_const,
+ string const& var = "")
+ {
+ return type_val_type (m.type (), m.belongs ().hint (), make_const, var);
+ }
+
+ static string
+ type_val_type (semantics::type&,
+ semantics::names* hint,
+ bool make_const,
+ string const& var = "");
+
+ // Member access helpers. Database can be empty. If type is not empty,
+ // then it should be the non-cvr type of the member (e.g., id_type).
+ //
+ void
+ set_member (semantics::data_member& m,
+ const std::string& obj,
+ const std::string& val,
+ const std::string& db,
+ const std::string& type = "");
+
+ void
+ inc_member (semantics::data_member& m,
+ const std::string& sobj, // Set expression object.
+ const std::string& gobj, // Get expression object.
+ const std::string& type = "");
+
+public:
+ // Resolve data member name in the form "a.b.c" to the data member path,
+ // issuing diagnostics and throwing operation_filed in case of an error.
+ // This function stops if it encounters a container leaving lex usable
+ // to continue parsing.
+ //
+ void
+ resolve_data_members (data_member_path& append,
+ semantics::class_& scope,
+ const std::string& name,
+ const location&,
+ cxx_string_lexer&);
+
+ data_member_path
+ resolve_data_members (semantics::class_& scope,
+ const std::string& name,
+ const location& l,
+ cxx_string_lexer& lex)
+ {
+ data_member_path r;
+ resolve_data_members (r, scope, name, l, lex);
+ return r;
+ }
+
+ // Predicates.
+ //
+public:
+ static bool
+ object (semantics::type& t)
+ {
+ return t.count ("object");
+ }
+
+ static bool
+ view (semantics::type& t)
+ {
+ return t.count ("view");
+ }
+
+ // Direct member of a view.
+ //
+ static bool
+ view_member (semantics::data_member& m)
+ {
+ return view (dynamic_cast<semantics::class_&> (m.scope ()));
+ }
+
+ // Check whether the type is a wrapper. Return the wrapped type if
+ // it is a wrapper and NULL otherwise. Note that the returned type
+ // may be cvr-qualified.
+ //
+ static semantics::type*
+ wrapper (semantics::type& t)
+ {
+ return t.count ("wrapper") && t.get<bool> ("wrapper")
+ ? t.get<semantics::type*> ("wrapper-type")
+ : 0;
+ }
+
+ static semantics::type*
+ wrapper (semantics::type& t, semantics::names*& hint)
+ {
+ if (t.count ("wrapper") && t.get<bool> ("wrapper"))
+ {
+ hint = t.get<semantics::names*> ("wrapper-hint");
+ return t.get<semantics::type*> ("wrapper-type");
+ }
+ else
+ return 0;
+ }
+
+ // Composite value type is a class type that was explicitly marked
+ // as value type and there was no database type mapping provided for
+ // it by the user (specifying the database type makes the value type
+ // simple).
+ //
+ static bool
+ composite (semantics::class_& c)
+ {
+ if (c.count ("composite-value"))
+ return c.get<bool> ("composite-value");
+ else
+ return composite_ (c);
+ }
+
+ // Return the class object if this type is a composite value type
+ // and NULL otherwise.
+ //
+ static semantics::class_*
+ composite (semantics::type& t)
+ {
+ semantics::class_* c (dynamic_cast<semantics::class_*> (&t));
+ return c != 0 && composite (*c) ? c : 0;
+ }
+
+ // As above but also "sees through" wrappers.
+ //
+ static semantics::class_*
+ composite_wrapper (semantics::type& t)
+ {
+ if (semantics::class_* c = composite (t))
+ return c;
+ else if (semantics::type* wt = wrapper (t))
+ return composite (utype (*wt));
+ else
+ return 0;
+ }
+
+ // Check if a data member is a container. "Sees through" wrappers and
+ // returns the actual container type or NULL if not a container.
+ //
+ // We require data member as input instead of the type because the
+ // same type (e.g., vector<char>) can be used for both container
+ // and simple value members.
+ //
+ static semantics::type*
+ container (semantics::data_member& m)
+ {
+ // The same type can be used as both a container and a simple value.
+ //
+ if (m.count ("simple"))
+ return 0;
+
+ semantics::type* t (&utype (m));
+
+ if (semantics::type* wt = wrapper (*t))
+ t = &utype (*wt);
+
+ return t->count ("container-kind") ? t : 0;
+ }
+
+ static semantics::class_*
+ object_pointer (semantics::type& t)
+ {
+ return t.get<semantics::class_*> ("element-type", 0);
+ }
+
+ // If this data member is or is part of an object pointer, then
+ // return the member that is the pointer. Otherwise, return 0.
+ //
+ static semantics::data_member*
+ object_pointer (data_member_path const&);
+
+ static semantics::class_*
+ points_to (semantics::data_member& m)
+ {
+ return m.get<semantics::class_*> ("points-to", 0);
+ }
+
+ static bool
+ abstract (semantics::class_& c)
+ {
+ // If a class is abstract in the C++ sense then it is also abstract in
+ // the database sense.
+ //
+ return c.abstract () || c.count ("abstract");
+ }
+
+ static bool
+ session (semantics::class_& c)
+ {
+ return c.get<bool> ("session");
+ }
+
+ static bool
+ transient (semantics::data_member& m)
+ {
+ return m.count ("transient");
+ }
+
+ // Return the deletion version or 0 if not soft-deleted.
+ //
+ static unsigned long long
+ deleted (semantics::class_& c, location_t* l = 0)
+ {
+ unsigned long long v (c.get<unsigned long long> ("deleted", 0));
+
+ if (v != 0 && l != 0)
+ *l = c.get<location_t> ("deleted-location");
+
+ return v;
+ }
+
+ static unsigned long long
+ deleted (semantics::data_member& m, location_t* l = 0)
+ {
+ unsigned long long v (m.get<unsigned long long> ("deleted", 0));
+
+ if (v != 0 && l != 0)
+ *l = m.get<location_t> ("deleted-location");
+
+ return v;
+ }
+
+ static unsigned long long
+ deleted (data_member_path const& mp, location_t* l = 0)
+ {
+ unsigned long long r (0);
+
+ // Find the earliest version since this member was deleted.
+ //
+ for (data_member_path::const_reverse_iterator i (mp.rbegin ());
+ i != mp.rend (); ++i)
+ {
+ unsigned long long v ((*i)->get<unsigned long long> ("deleted", 0));
+ if (v != 0 && (r == 0 || v < r))
+ {
+ r = v;
+
+ if (l != 0)
+ *l = (*i)->get<location_t> ("deleted-location");
+ }
+ }
+
+ return r;
+ }
+
+ static semantics::data_member*
+ deleted_member (data_member_path const& mp)
+ {
+ semantics::data_member* m (0);
+
+ // Find the earliest version since this member was deleted.
+ //
+ unsigned long long r (0);
+ for (data_member_path::const_reverse_iterator i (mp.rbegin ());
+ i != mp.rend (); ++i)
+ {
+ unsigned long long v ((*i)->get<unsigned long long> ("deleted", 0));
+ if (v != 0 && (r == 0 || v < r))
+ {
+ r = v;
+ m = *i;
+ }
+ }
+
+ return m;
+ }
+
+ // Return the addition version or 0 if not soft-added.
+ //
+ static unsigned long long
+ added (semantics::class_& c) // Used for composite only.
+ {
+ return c.get<unsigned long long> ("added", 0);
+ }
+
+ static unsigned long long
+ added (semantics::data_member& m)
+ {
+ return m.get<unsigned long long> ("added", 0);
+ }
+
+ static unsigned long long
+ added (data_member_path const& mp)
+ {
+ unsigned long long r (0);
+
+ // Find the latest version since this member was added.
+ //
+ for (data_member_path::const_reverse_iterator i (mp.rbegin ());
+ i != mp.rend (); ++i)
+ {
+ unsigned long long v ((*i)->get<unsigned long long> ("added", 0));
+ if (v != 0 && v > r)
+ r = v;
+ }
+
+ return r;
+ }
+
+ static semantics::data_member*
+ added_member (data_member_path const& mp)
+ {
+ semantics::data_member* m (0);
+
+ // Find the latest version since this member was added.
+ //
+ unsigned long long r (0);
+ for (data_member_path::const_reverse_iterator i (mp.rbegin ());
+ i != mp.rend (); ++i)
+ {
+ unsigned long long v ((*i)->get<unsigned long long> ("added", 0));
+ if (v != 0 && v > r)
+ {
+ r = v;
+ m = *i;
+ }
+ }
+
+ return m;
+ }
+
+ static bool
+ id (semantics::data_member& m)
+ {
+ return m.count ("id");
+ }
+
+ // If this data member is or is part of an id member, then return
+ // the member that is marked as the id. Otherwise, return 0.
+ //
+ static semantics::data_member*
+ id (data_member_path const&);
+
+ static bool
+ auto_ (semantics::data_member& m)
+ {
+ return id (m) && m.count ("auto");
+ }
+
+ // Must be a path returned by id(). In other words, it assumes
+ // the path is to the id member.
+ //
+ static bool
+ auto_ (data_member_path& mp)
+ {
+ return mp.front ()->count ("auto");
+ }
+
+ // The member scope is used to override readonly status when a readonly
+ // class (object or composite value) inherits from a readwrite base.
+ //
+ static bool
+ readonly (data_member_path const&, data_member_scope const&);
+
+ static bool
+ readonly (semantics::data_member&);
+
+ static bool
+ readonly (semantics::class_& c)
+ {
+ return c.count ("readonly");
+ }
+
+ // Null-able.
+ //
+ bool
+ null (data_member_path const&) const;
+
+ bool
+ null (semantics::data_member&) const;
+
+ bool
+ null (semantics::data_member&, string const& key_prefix) const;
+
+ // Optimistic concurrency.
+ //
+ static semantics::data_member*
+ optimistic (semantics::class_& c)
+ {
+ // Set by the validator.
+ //
+ return c.get<semantics::data_member*> ("optimistic-member", 0);
+ }
+
+ static bool
+ version (semantics::data_member& m)
+ {
+ return m.count ("version");
+ }
+
+ static bool
+ version (const data_member_path& mp)
+ {
+ return mp.size () == 1 && mp.back ()->count ("version");
+ }
+
+ // Polymorphic inheritance. Return root of the hierarchy or NULL if
+ // not polymorphic.
+ //
+ static semantics::class_*
+ polymorphic (semantics::class_& c)
+ {
+ // Set by the validator.
+ //
+ return c.get<semantics::class_*> ("polymorphic-root", 0);
+ }
+
+ static semantics::class_&
+ polymorphic_base (semantics::class_& c)
+ {
+ // Set by the validator.
+ //
+ return *c.get<semantics::class_*> ("polymorphic-base");
+ }
+
+ static size_t
+ polymorphic_depth (semantics::class_&);
+
+ static bool
+ discriminator (semantics::data_member& m)
+ {
+ return m.count ("discriminator");
+ }
+
+ static semantics::data_member*
+ discriminator (semantics::class_& c)
+ {
+ // Set by type processor.
+ //
+ return c.get<semantics::data_member*> ("discriminator", 0);
+ }
+
+ // Model version.
+ //
+ bool
+ versioned () const
+ {
+ return unit.count ("model-version") != 0;
+ }
+
+ model_version const&
+ version () const
+ {
+ return unit.get<model_version> ("model-version");
+ }
+
+ // Versioned object, view, or composite.
+ //
+ static bool
+ versioned (semantics::class_& c)
+ {
+ // Set by processor.
+ //
+ return c.count ("versioned") != 0;
+ }
+
+ // Versioned container.
+ //
+ static bool
+ versioned (semantics::data_member& m)
+ {
+ // Set by processor.
+ //
+ return container (m)->count ("versioned");
+ }
+
+ // Object sections.
+ //
+ static object_section&
+ section (semantics::data_member& m)
+ {
+ object_section* s (m.get<object_section*> ("section", 0));
+ return s == 0 ? main_section : *s;
+ }
+
+ static object_section&
+ section (data_member_path const& mp)
+ {
+ // The direct member of the object specifies the section. If the
+ // path is empty (which can happen, for example, for a container
+ // element), assume it is the main section.
+ //
+ //
+ return mp.empty () ? main_section : section (*mp.front ());
+ }
+
+ // Member belongs to a section that is loaded separately.
+ //
+ static bool
+ separate_load (semantics::data_member& m)
+ {
+ return section (m).separate_load ();
+ }
+
+ static bool
+ separate_load (data_member_path const& mp)
+ {
+ return section (mp).separate_load ();
+ }
+
+ // Member belongs to a section that is updated separately.
+ //
+ static bool
+ separate_update (semantics::data_member& m)
+ {
+ return section (m).separate_update ();
+ }
+
+ static bool
+ separate_update (data_member_path const& mp)
+ {
+ return section (mp).separate_update ();
+ }
+
+ //
+ //
+ typedef ::class_kind class_kind_type;
+
+ static class_kind_type
+ class_kind (semantics::class_&);
+
+ // Return class names. For ordinary classes, this will be the class
+ // name itself. For class template instantiations this will be the
+ // typedef name used in the pragma.
+ //
+ static string
+ class_name (semantics::class_&);
+
+ static string
+ class_fq_name (semantics::class_&);
+
+ // Return class scope. For ordinary classes, this will be the scope
+ // where the class is defined. For class template instantiations this
+ // will be the scope of the typedef name used in the pragma.
+ //
+ static semantics::scope&
+ class_scope (semantics::class_&);
+
+ // Return the class file. For ordinary classes, this will be the file
+ // where the class is defined. For class template instantiations this
+ // will be the file containing the pragma.
+ //
+ static semantics::path
+ class_file (semantics::class_&);
+
+ // Return the location (as location_t; useful for comparison) of
+ // an "ODB class" (i.e., an object, view, or composite value),
+ // taking into account things like definition point overrides,
+ // etc.
+ //
+ location_t
+ class_location (semantics::class_&);
+
+ // Same as above, but returns "real" location, that is, ignoring
+ // the definition point overrides.
+ //
+ location_t
+ class_real_location (semantics::class_&);
+
+ // Database names and types.
+ //
+public:
+ // Schema name for a namespace.
+ //
+ qname
+ schema (semantics::scope&) const;
+
+ // Table name prefix for a namespace.
+ //
+ string
+ table_name_prefix (semantics::scope&) const;
+
+ //
+ //
+ struct table_prefix
+ {
+ table_prefix (): level (0), derived (false) {}
+ table_prefix (semantics::class_&);
+
+ void
+ append (semantics::data_member&);
+
+ qname ns_schema; // Object's namespace schema.
+ string ns_prefix; // Object's namespace table prefix.
+ qname prefix;
+ size_t level;
+ bool derived; // One of the components in the prefix was derived.
+ };
+
+ qname
+ table_name (semantics::class_&, bool* derived = 0) const;
+
+ qname
+ table_name (semantics::class_&, data_member_path const&) const;
+
+ // Table name for the container member. The table prefix passed as the
+ // second argument must include the table prefix specified with the
+ // --table-prefix option.
+ //
+ qname
+ table_name (semantics::data_member&, table_prefix const&) const;
+
+ string
+ table_options (semantics::class_&);
+
+ // Table options for the container member.
+ //
+ string
+ table_options (semantics::data_member&, semantics::type& ct);
+
+ //
+ //
+ struct column_prefix
+ {
+ column_prefix (): derived (false), underscore (false) {}
+
+ column_prefix (semantics::data_member& m,
+ string const& key_prefix = string (),
+ string const& default_name = string ())
+ : derived (false), underscore (false)
+ {
+ append (m, key_prefix, default_name);
+ }
+
+ // If the last argument is true, the prefix will include the last member
+ // in the path.
+ //
+ column_prefix (data_member_path const&, bool last = false);
+
+ bool
+ empty () const {return prefix.empty ();}
+
+ void
+ append (semantics::data_member&,
+ string const& key_prefix = string (),
+ string const& default_name = string ());
+
+ string prefix;
+ bool derived; // One of the components in the prefix was derived.
+ bool underscore; // Trailing underscore was automatically added.
+ };
+
+ string
+ column_name (semantics::data_member&, bool& derived) const;
+
+ string
+ column_name (semantics::data_member&, column_prefix const&) const;
+
+ string
+ column_name (semantics::data_member&,
+ string const& key_prefix,
+ string const& default_name,
+ bool& derived) const;
+
+ string
+ column_name (semantics::data_member&,
+ string const& key_prefix,
+ string const& default_name,
+ column_prefix const&) const;
+
+ string
+ column_name (data_member_path const&) const;
+
+ //
+ //
+ string
+ column_type (const data_member_path&,
+ string const& key_prefix = string (),
+ bool id = false); // Pass true if this type is object id other
+ // than because of the members in the path.
+ string
+ column_type (semantics::data_member&, string const& key_prefix = string ());
+
+ string
+ column_options (semantics::data_member&);
+
+ string
+ column_options (semantics::data_member&, string const& key_prefix);
+
+ // Cleaned-up member name that can be used for database names.
+ //
+ string
+ public_name_db (semantics::data_member&) const;
+
+ // Compose the name by inserting/removing an underscore, as necessary.
+ //
+ static string
+ compose_name (string const& prefix, string const& name);
+
+ // SQL name transformations.
+ //
+ enum sql_name_type
+ {
+ sql_name_all,
+ sql_name_table,
+ sql_name_column,
+ sql_name_index,
+ sql_name_fkey,
+ sql_name_sequence,
+ sql_name_statement,
+ sql_name_count
+ };
+
+ string
+ transform_name (string const& name, sql_name_type) const;
+
+ // C++ names.
+ //
+public:
+ // Cleaned-up and potentially escaped member name that can be used
+ // in public C++ interfaces.
+ //
+ string
+ public_name (semantics::data_member&, bool escape = true) const;
+
+ // "Flatten" fully-qualified C++ name by replacing '::' with '_'
+ // and removing leading '::', if any.
+ //
+ static string
+ flat_name (string const& fqname);
+
+ // Escape C++ keywords, reserved names, and illegal characters.
+ //
+ string
+ escape (string const&) const;
+
+ // Make C++ include guard name by split words, e.g., "FooBar" to
+ // "Foo_Bar" and converting everything to upper case.
+ //
+ string
+ make_guard (string const&) const;
+
+ // Return a string literal that can be used in C++ source code. It
+ // includes "".
+ //
+ static string
+ strlit (string const&);
+
+public:
+ // Generate explicit instantiation headers with all the necessary
+ // extern and export symbols.
+ //
+ void
+ inst_header (bool decl, bool omit_exp = false);
+
+ // Counts and other information.
+ //
+public:
+ struct column_count_type
+ {
+ column_count_type ()
+ : total (0),
+ id (0),
+ inverse (0),
+ readonly (0),
+ optimistic_managed (0),
+ discriminator (0),
+ added (0),
+ deleted (0),
+ soft (0),
+ separate_load (0),
+ separate_update (0)
+ {
+ }
+
+ size_t total;
+ size_t id;
+ size_t inverse;
+ size_t readonly;
+ size_t optimistic_managed;
+ size_t discriminator;
+
+ size_t added; // Soft-added.
+ size_t deleted; // Soft-deleted.
+ size_t soft; // Soft-added/deleted (a column can be both).
+
+ size_t separate_load;
+ size_t separate_update; // Only readwrite.
+ };
+
+ static column_count_type
+ column_count (semantics::class_&, object_section* = 0);
+
+ static data_member_path*
+ id_member (semantics::class_& c)
+ {
+ // Set by the processor. May not be there for reuse-abstract
+ // classes or classes without object id.
+ //
+ return c.count ("id-member") ? &c.get<data_member_path> ("id-member") : 0;
+ }
+
+ // Object pointer information.
+ //
+public:
+ typedef ::pointer_kind pointer_kind_type;
+
+ pointer_kind_type
+ pointer_kind (semantics::type& p)
+ {
+ return p.get<pointer_kind_type> ("pointer-kind");
+ }
+
+ bool
+ lazy_pointer (semantics::type& p)
+ {
+ return p.get<bool> ("pointer-lazy");
+ }
+
+ bool
+ weak_pointer (semantics::type& p)
+ {
+ return pointer_kind (p) == pk_weak;
+ }
+
+ static data_member_path*
+ inverse (semantics::data_member& m)
+ {
+ return object_pointer (utype (m)) && m.count ("inverse")
+ ? &m.get<data_member_path> ("inverse")
+ : 0;
+ }
+
+ data_member_path*
+ inverse (semantics::data_member& m, string const& key_prefix)
+ {
+ if (key_prefix.empty ())
+ return inverse (m);
+
+ if (!object_pointer (utype (m, key_prefix)))
+ return 0;
+
+ string k (key_prefix + "-inverse");
+ return m.count (k) ? &m.get<data_member_path> (k) : 0;
+ }
+
+ // Container information.
+ //
+public:
+ typedef ::container_kind container_kind_type;
+
+ static container_kind_type
+ container_kind (semantics::type& c)
+ {
+ return c.get<container_kind_type> ("container-kind");
+ }
+
+ static bool
+ container_smart (semantics::type& c)
+ {
+ return c.get<bool> ("container-smart");
+ }
+
+ static semantics::type&
+ container_idt (semantics::data_member& m, const custom_cxx_type** trans = 0)
+ {
+ return utype (m, "id", trans);
+ }
+
+ static semantics::type&
+ container_vt (semantics::data_member& m, const custom_cxx_type** trans = 0)
+ {
+ return utype (m, "value", trans);
+ }
+
+ static semantics::type&
+ container_it (semantics::data_member& m, const custom_cxx_type** trans = 0)
+ {
+ return utype (m, "index", trans);
+ }
+
+ static semantics::type&
+ container_kt (semantics::data_member& m, const custom_cxx_type** trans = 0)
+ {
+ return utype (m, "key", trans);
+ }
+
+ static bool
+ unordered (semantics::data_member& m)
+ {
+ if (m.count ("unordered"))
+ return true;
+
+ if (semantics::type* c = container (m))
+ return c->count ("unordered");
+
+ return false;
+ }
+
+ // The 'is a' and 'has a' tests. The has_a() test currently does not
+ // cross the container boundaries.
+ //
+public:
+ static unsigned short const test_pointer = 0x01;
+ static unsigned short const test_eager_pointer = 0x02;
+ static unsigned short const test_lazy_pointer = 0x04;
+ static unsigned short const test_container = 0x08;
+ static unsigned short const test_straight_container = 0x10;
+ static unsigned short const test_inverse_container = 0x20;
+ static unsigned short const test_readonly_container = 0x40;
+ static unsigned short const test_readwrite_container = 0x80;
+ static unsigned short const test_smart_container = 0x100;
+
+ // Exclude versioned containers.
+ //
+ static unsigned short const exclude_versioned = 0x200;
+
+ // Treat eager loaded members as belonging to the main section.
+ // If this flag is specified, then section must be main_section.
+ //
+ static unsigned short const include_eager_load = 0x800;
+
+ // Exclude added/deleted members.
+ //
+ static unsigned short const exclude_added = 0x1000;
+ static unsigned short const exclude_deleted = 0x2000;
+
+ // By default the test goes into bases for non-polymorphic
+ // hierarchies and doesn't go for polymorphic. The following
+ // flags can be used to alter this behavior.
+ //
+ static unsigned short const exclude_base = 0x4000;
+ static unsigned short const include_base = 0x8000;
+
+ bool
+ is_a (data_member_path const& mp,
+ data_member_scope const& ms,
+ unsigned short flags)
+ {
+ return is_a (mp, ms, flags, utype (*mp.back ()), "");
+ }
+
+ bool
+ is_a (data_member_path const&,
+ data_member_scope const&,
+ unsigned short flags,
+ semantics::type&,
+ string const& key_prefix);
+
+ // Return the number of matching entities. Can be used as a just
+ // a bool value (0 means no match).
+ //
+ size_t
+ has_a (semantics::class_&, unsigned short flags, object_section* = 0);
+
+public:
+ // Process include path by adding the prefix, putting it through
+ // the include regex list, and adding opening and closing include
+ // characters ("" or <>) if necessary. The prefix argument indicates
+ // whether the include prefix specified with the --include-prefix
+ // option should be added. The open argument can be used to specify
+ // the opening character. It can have three values: ", <, or \0. In
+ // case of \0, the character is determined based on the value of the
+ // --include-with-bracket option.
+ //
+ string
+ process_include_path (string const&, bool prefix = true, char open = '\0');
+
+ // Diverge output.
+ //
+public:
+ void
+ diverge (std::ostream& os)
+ {
+ diverge (os.rdbuf ());
+ }
+
+ void
+ diverge (std::streambuf* sb);
+
+ void
+ restore ();
+
+ // Implementation details.
+ //
+private:
+ static bool
+ composite_ (semantics::class_&);
+
+ template <typename X>
+ static X
+ indirect_value (semantics::context const& c, string const& key)
+ {
+ typedef X (*func) ();
+ std::type_info const& ti (c.type_info (key));
+
+ if (ti == typeid (func))
+ return c.get<func> (key) ();
+ else
+ return c.get<X> (key);
+ }
+
+ static semantics::type*
+ indirect_type (semantics::context const& c,
+ string const& kp,
+ semantics::names*& hint)
+ {
+ typedef semantics::type* (*func) (semantics::names*&);
+
+ string const tk (kp + "-tree-type");
+ std::type_info const& ti (c.type_info (tk));
+
+ if (ti == typeid (func))
+ return c.get<func> (tk) (hint);
+ else
+ {
+ hint = c.get<semantics::names*> (kp + "-tree-hint");
+ return c.get<semantics::type*> (tk);
+ }
+ }
+
+public:
+ typedef std::set<string> keyword_set_type;
+
+ struct db_type_type
+ {
+ db_type_type () {}
+ db_type_type (string const& t, string const& it, bool n)
+ : type (t), id_type (it), null (n)
+ {
+ }
+
+ string type;
+ string id_type;
+ bool null;
+ };
+
+ struct type_map_type: std::map<string, db_type_type>
+ {
+ typedef std::map<string, db_type_type> base;
+
+ const_iterator
+ find (semantics::type&, semantics::names* hint);
+ };
+
+protected:
+ struct data
+ {
+ virtual
+ ~data () {}
+
+ data (std::ostream& os)
+ : extra_ (0),
+ os_ (os.rdbuf ()),
+ in_comment_ (false),
+ top_object_ (0),
+ cur_object_ (0),
+ sql_name_upper_ ("(.+)", "\\U$1"),
+ sql_name_lower_ ("(.+)", "\\L$1")
+ {
+ }
+
+ public:
+ void* extra_;
+
+ std::ostream os_;
+ std::stack<std::streambuf*> os_stack_;
+
+ bool in_comment_;
+
+ semantics::class_* top_object_;
+ semantics::class_* cur_object_;
+
+ string exp_;
+ string ext_;
+
+ keyword_set_type keyword_set_;
+ type_map_type type_map_;
+
+ regex_mapping sql_name_regex_[sql_name_count];
+ regexsub sql_name_upper_;
+ regexsub sql_name_lower_;
+
+ regex_mapping include_regex_;
+ regex_mapping accessor_regex_;
+ regex_mapping modifier_regex_;
+ };
+
+ typedef cutl::shared_ptr<data> data_ptr;
+ data_ptr data_;
+
+public:
+ typedef ::features features_type;
+
+ void*& extra; // Extra data that may need to be shared by a sub-system.
+
+ std::ostream& os;
+ semantics::unit& unit;
+ options_type const& options;
+ features_type& features;
+ database const db;
+
+ bool& in_comment;
+
+ string& exp; // Export symbol (with trailing space if specified).
+ string& ext; // Extern symbol.
+
+ keyword_set_type const& keyword_set;
+
+ regex_mapping const& include_regex;
+ regex_mapping const& accessor_regex;
+ regex_mapping const& modifier_regex;
+
+ bool embedded_schema;
+ bool separate_schema;
+
+ bool multi_static;
+ bool multi_dynamic;
+
+ bool force_versioned; // Force statement processing for debugging.
+
+ // Outermost object or view currently being traversed.
+ //
+ semantics::class_*& top_object;
+
+ // Object or view currently being traversed. It can be the same as
+ // top_object or it can a base of top_object.
+ //
+ semantics::class_*& cur_object;
+
+ // Per-database customizable functionality.
+ //
+protected:
+ // Return empty string if there is no mapping. The type passed is
+ // already cvr-unqualified. The null out argument indicates whether
+ // the column should allow NULL values by default.
+ //
+ string
+ database_type (semantics::type& t,
+ semantics::names* hint,
+ bool id,
+ bool* null = 0)
+ {
+ return current ().database_type_impl (t, hint, id, null);
+ }
+
+ // The default implementation uses the type map (populated by the database-
+ // specific context implementation) to come up with a mapping.
+ //
+ virtual string
+ database_type_impl (semantics::type&, semantics::names*, bool, bool*);
+
+public:
+ typedef context root_context;
+
+ virtual
+ ~context ();
+ context ();
+ context (std::ostream&,
+ semantics::unit&,
+ options_type const&,
+ features_type&,
+ data_ptr = data_ptr ());
+
+ static context&
+ current ()
+ {
+ return *current_;
+ }
+
+private:
+ static context* current_;
+
+#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
+ context&
+ operator= (context const&) = delete;
+#else
+private:
+ context&
+ operator= (context const&);
+#endif
+};
+
+// Create concrete database context.
+//
+std::unique_ptr<context>
+create_context (std::ostream&,
+ semantics::unit&,
+ options const&,
+ features&,
+ semantics::relational::model*);
+
+// Checks if scope Y names any of X.
+//
+template <typename X, typename Y>
+bool
+has (Y& y)
+{
+ for (semantics::scope::names_iterator i (y.names_begin ()),
+ e (y.names_end ()); i != e; ++i)
+ if (i->named (). template is_a<X> ())
+ return true;
+
+ return false;
+}
+
+#include <odb/context.ixx>
+
+#endif // ODB_CONTEXT_HXX
diff --git a/odb/odb/context.ixx b/odb/odb/context.ixx
new file mode 100644
index 0000000..5018743
--- /dev/null
+++ b/odb/odb/context.ixx
@@ -0,0 +1,26 @@
+// file : odb/context.ixx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+inline bool user_section::
+load_empty () const
+{
+ return !separate_load () || (total == 0 && !containers && !optimistic ());
+}
+
+inline bool user_section::
+update_empty () const
+{
+ return total == inverse + readonly && !readwrite_containers;
+}
+
+inline bool user_section::
+optimistic () const
+{
+ if (!context::optimistic (*object))
+ return false;
+ else
+ {
+ semantics::class_* poly_root (context::polymorphic (*object));
+ return poly_root == 0 || poly_root == object;
+ }
+}
diff --git a/odb/odb/cxx-lexer.cxx b/odb/odb/cxx-lexer.cxx
new file mode 100644
index 0000000..e4e0229
--- /dev/null
+++ b/odb/odb/cxx-lexer.cxx
@@ -0,0 +1,366 @@
+// file : odb/cxx-lexer.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#include <new> // std::bad_alloc
+#include <cassert>
+#include <iostream>
+
+#include <odb/cxx-lexer.hxx>
+
+using namespace std;
+
+//
+// cxx_lexer
+//
+
+// Token spelling. See cpplib.h for details.
+//
+#define OP(e, s) s ,
+#define TK(e, s) #e ,
+char const* cxx_lexer::token_spelling[N_TTYPES + 1] = { TTYPE_TABLE "KEYWORD"};
+#undef OP
+#undef TK
+
+cxx_lexer::
+~cxx_lexer ()
+{
+}
+
+//
+// cxx_tokens_lexer
+//
+
+void cxx_tokens_lexer::
+start (cxx_tokens const& ts, location_t start_loc)
+{
+ tokens_ = &ts;
+ cur_ = ts.begin ();
+ loc_ = start_loc;
+}
+
+cpp_ttype cxx_tokens_lexer::
+next (std::string& token, tree* node)
+{
+ if (cur_ != tokens_->end ())
+ {
+ loc_ = cur_->loc;
+ token = cur_->literal;
+ if (node != 0)
+ *node = cur_->node;
+ return static_cast<cpp_ttype> (cur_++->type);
+ }
+ else
+ return CPP_EOF;
+}
+
+location_t cxx_tokens_lexer::
+location () const
+{
+ return loc_;
+}
+
+//
+// cxx_pragma_lexer
+//
+
+void cxx_pragma_lexer::
+start ()
+{
+ token_ = &token_data_;
+ type_ = &type_data_;
+}
+
+string cxx_pragma_lexer::
+start (tree& token, cpp_ttype& type)
+{
+ token_ = &token;
+ type_ = &type;
+
+ return translate ();
+}
+
+cpp_ttype cxx_pragma_lexer::
+next (string& token, tree* node)
+{
+ *type_ = pragma_lex (token_);
+
+ // See if this is a keyword using the C++ parser machinery and
+ // the current C++ dialect.
+ //
+ if (*type_ == CPP_NAME &&
+#if BUILDING_GCC_MAJOR >= 8
+ IDENTIFIER_KEYWORD_P (*token_)
+#else
+ C_IS_RESERVED_WORD (*token_)
+#endif
+ )
+ *type_ = CPP_KEYWORD;
+
+ if (node != 0 && node != token_)
+ *node = *token_;
+
+ token = translate ();
+ return *type_;
+}
+
+location_t cxx_pragma_lexer::
+location () const
+{
+ // Starting from GCC 6 the input location seem to require the same
+ // translation as what we do in real_source_location().
+ //
+#if BUILDING_GCC_MAJOR >= 6
+ return linemap_resolve_location (
+ line_table, input_location, LRK_MACRO_EXPANSION_POINT, 0);
+#else
+ return input_location;
+#endif
+}
+
+string cxx_pragma_lexer::
+translate ()
+{
+ string r;
+
+ if (*type_ == CPP_NAME || *type_ == CPP_KEYWORD)
+ r = IDENTIFIER_POINTER (*token_);
+ else if (*type_ == CPP_STRING)
+ r = TREE_STRING_POINTER (*token_);
+
+ return r;
+}
+
+//
+// cxx_string_lexer
+//
+
+// Diagnostics callback.
+//
+extern "C" bool
+cpp_diagnostic_callback (
+ cpp_reader* reader,
+#if BUILDING_GCC_MAJOR >= 9
+ cpp_diagnostic_level level,
+#else
+ int level,
+#endif
+#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 5
+#if BUILDING_GCC_MAJOR >= 9
+ cpp_warning_reason,
+#else
+ int /*reason*/, // Added in GCC 4.6.0.
+#endif
+#endif
+#if BUILDING_GCC_MAJOR <= 5
+ location_t,
+ unsigned int,
+#else
+ rich_location*,
+#endif
+ char const* msg,
+ va_list *ap)
+{
+ char const* kind (0);
+ switch (level)
+ {
+ case CPP_DL_NOTE:
+ case CPP_DL_WARNING_SYSHDR:
+ case CPP_DL_WARNING:
+ case CPP_DL_PEDWARN:
+ // Ignore these.
+ break;
+ case CPP_DL_ERROR:
+ case CPP_DL_FATAL:
+ kind = "error";
+ break;
+ case CPP_DL_ICE:
+ kind = "ice";
+ break;
+ default:
+ kind = "unknown";
+ break;
+ }
+
+ if (kind != 0)
+ {
+ fprintf (stderr, "%s: ", kind);
+ vfprintf (stderr, msg, *ap);
+ fprintf (stderr, "\n");
+
+ // By resetting the callback we indicate to cxx_string_lexer that there
+ // was an error.
+ //
+#if BUILDING_GCC_MAJOR >= 9
+ cpp_get_callbacks (reader)->diagnostic = 0;
+#else
+ cpp_get_callbacks (reader)->error = 0;
+#endif
+ return true;
+ }
+
+ return false;
+}
+
+cxx_string_lexer::
+cxx_string_lexer ()
+ : reader_ (0)
+{
+#if BUILDING_GCC_MAJOR >= 5
+ linemap_init (&line_map_, UNKNOWN_LOCATION);
+#else
+ linemap_init (&line_map_);
+#endif
+
+#if BUILDING_GCC_MAJOR >= 14
+ line_map_.m_round_alloc_size = ggc_round_alloc_size;
+#elif BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 6
+ line_map_.round_alloc_size = ggc_round_alloc_size;
+#endif
+
+ linemap_add (&line_map_, LC_ENTER, 0, "<memory>", 0);
+
+ reader_ = cpp_create_reader (
+ cxx_dialect == cxx0x // Nothing new for C++14.
+#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 6
+ ? CLK_CXX11
+#else
+ ? CLK_CXX0X
+#endif
+ : CLK_CXX98,
+ 0,
+ &line_map_);
+
+ if (reader_ == 0)
+ throw bad_alloc ();
+
+ callbacks_ = cpp_get_callbacks (reader_);
+}
+
+cxx_string_lexer::
+~cxx_string_lexer ()
+{
+ if (reader_ != 0)
+ cpp_destroy (reader_);
+
+ // Was removed as "dead code" in GCC 4.7.0.
+ //
+#if BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR <= 6
+ linemap_free (&line_map_);
+#endif
+}
+
+void cxx_string_lexer::
+start (string const& data)
+{
+ // The previous lexing session should have popped the buffer.
+ //
+ assert (cpp_get_buffer (reader_) == 0);
+
+#if BUILDING_GCC_MAJOR >= 9
+ callbacks_->diagnostic = &cpp_diagnostic_callback;
+#else
+ callbacks_->error = &cpp_diagnostic_callback;
+#endif
+
+ data_ = data;
+ buf_ = data;
+ buf_ += '\n';
+ loc_ = 0;
+
+ cpp_push_buffer (
+ reader_,
+ reinterpret_cast<unsigned char const*> (buf_.c_str ()),
+ buf_.size (),
+ true);
+}
+
+cpp_ttype cxx_string_lexer::
+next (string& token, tree* node)
+{
+ token.clear ();
+ cpp_token const* t (cpp_get_token (reader_));
+
+ // If there was an error, the callback will be reset to 0. Diagnostics has
+ // already been issued.
+ //
+#if BUILDING_GCC_MAJOR >= 9
+ if (callbacks_->diagnostic == 0)
+#else
+ if (callbacks_->error == 0)
+#endif
+ throw invalid_input ();
+
+ cpp_ttype tt (t->type);
+
+ switch (tt)
+ {
+ case CPP_NAME:
+ {
+ char const* name (
+ reinterpret_cast<char const*> (NODE_NAME (t->val.node.node)));
+
+ // See if this is a keyword using the C++ parser machinery and
+ // the current C++ dialect.
+ //
+ tree id (get_identifier (name));
+
+ if (
+#if BUILDING_GCC_MAJOR >= 8
+ IDENTIFIER_KEYWORD_P (id)
+#else
+ C_IS_RESERVED_WORD (id)
+#endif
+ )
+ tt = CPP_KEYWORD;
+
+ if (node != 0)
+ *node = id;
+
+ token = name;
+ break;
+ }
+ case CPP_STRING:
+ case CPP_NUMBER:
+ {
+ if (node != 0)
+ *node = 0; // Doesn't seem to be available.
+
+ cpp_string const& s (t->val.str);
+ token.assign (reinterpret_cast<char const*> (s.text), s.len);
+ break;
+ }
+ default:
+ {
+ if (tt <= CPP_LAST_PUNCTUATOR)
+ {
+ if (node != 0)
+ *node = 0;
+ token = token_spelling[tt];
+ }
+ else
+ {
+ cerr << "unexpected token '" << token_spelling[tt] << "' in '" <<
+ data_ << "'" << endl;
+ throw invalid_input ();
+ }
+ break;
+ }
+ }
+
+ // Cache the location of this token.
+ //
+ loc_ = t->src_loc;
+
+ return tt;
+}
+
+location_t cxx_string_lexer::
+location () const
+{
+ return loc_;
+}
diff --git a/odb/odb/cxx-lexer.hxx b/odb/odb/cxx-lexer.hxx
new file mode 100644
index 0000000..ea16132
--- /dev/null
+++ b/odb/odb/cxx-lexer.hxx
@@ -0,0 +1,131 @@
+// file : odb/cxx-lexer.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_CXX_LEXER_HXX
+#define ODB_CXX_LEXER_HXX
+
+#include <odb/gcc.hxx>
+
+#include <string>
+
+#include <odb/cxx-token.hxx>
+
+// A C++ keyword. This is an extension to libcpp token types. GCC 4.7.0
+// adds this define.
+//
+#if BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR <= 6
+# define CPP_KEYWORD ((cpp_ttype) (N_TTYPES + 1))
+#endif
+
+class cxx_lexer
+{
+public:
+ virtual
+ ~cxx_lexer ();
+
+public:
+ struct invalid_input {};
+
+ virtual cpp_ttype
+ next (std::string& token, tree* node = 0) = 0;
+
+ // Location of the last returned token.
+ //
+ virtual location_t
+ location () const = 0;
+
+public:
+ static char const* token_spelling[N_TTYPES + 1];
+};
+
+
+// Adapter to scan a saved token sequence. It returns numbers in the same
+// form as they were saved in the token sequence.
+//
+class cxx_tokens_lexer: public cxx_lexer
+{
+public:
+ void
+ start (cxx_tokens const&, location_t start_loc = 0);
+
+ virtual cpp_ttype
+ next (std::string& token, tree* node = 0);
+
+ virtual location_t
+ location () const;
+
+private:
+ cxx_tokens const* tokens_;
+ cxx_tokens::const_iterator cur_;
+ location_t loc_;
+};
+
+
+// A thin wrapper around the pragma_lex() function that recognizes
+// CPP_KEYWORD. It returns numbers as tree nodes.
+//
+class cxx_pragma_lexer: public cxx_lexer
+{
+public:
+ void
+ start ();
+
+ // Start with an already extracted (using the pragma_lex() function)
+ // token. This function translates the CPP_NAME to CPP_KEYWORD if
+ // necessary and returns the string token. It also uses the passed
+ // token and type for subsequent calls to next() so after the last
+ // next() call they will contain the information about the last
+ // token parsed.
+ //
+ std::string
+ start (tree& token, cpp_ttype& type);
+
+ virtual cpp_ttype
+ next (std::string& token, tree* node = 0);
+
+ virtual location_t
+ location () const;
+
+private:
+ std::string
+ translate ();
+
+private:
+ tree* token_;
+ cpp_ttype* type_;
+
+ tree token_data_;
+ cpp_ttype type_data_;
+};
+
+// A thin wrapper around cpp_reader for lexing C++ code fragments. It
+// returns numbers as string literals.
+//
+class cxx_string_lexer: public cxx_lexer
+{
+public:
+ cxx_string_lexer ();
+
+ virtual
+ ~cxx_string_lexer ();
+
+public:
+ void
+ start (std::string const&);
+
+ virtual cpp_ttype
+ next (std::string& token, tree* node = 0);
+
+ virtual location_t
+ location () const;
+
+private:
+ std::string data_;
+ std::string buf_;
+ line_maps line_map_;
+ cpp_reader* reader_;
+ cpp_callbacks* callbacks_;
+ location_t loc_;
+};
+
+#endif // ODB_CXX_LEXER_HXX
diff --git a/odb/odb/cxx-token.hxx b/odb/odb/cxx-token.hxx
new file mode 100644
index 0000000..34b28d4
--- /dev/null
+++ b/odb/odb/cxx-token.hxx
@@ -0,0 +1,30 @@
+// file : odb/cxx-token.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_CXX_TOKEN_HXX
+#define ODB_CXX_TOKEN_HXX
+
+#include <string>
+#include <vector>
+
+#include <odb/gcc-fwd.hxx>
+
+struct cxx_token
+{
+ cxx_token (location_t l,
+ unsigned int t,
+ std::string const& lt = std::string (),
+ tree n = 0)
+ : loc (l), type (t), literal (lt), node (n) {}
+
+ location_t loc; // Location of this token.
+ unsigned int type; // Untyped cpp_ttype.
+ std::string literal; // Only used for name, keyword, string, amd number.
+ tree node; // Tree node for the number. The number can be
+ // represented as either literal, tree node, or
+ // both, depending on which lexer was used.
+};
+
+typedef std::vector<cxx_token> cxx_tokens;
+
+#endif // ODB_CXX_TOKEN_HXX
diff --git a/odb/odb/diagnostics.cxx b/odb/odb/diagnostics.cxx
new file mode 100644
index 0000000..57166bb
--- /dev/null
+++ b/odb/odb/diagnostics.cxx
@@ -0,0 +1,136 @@
+// file : odb/diagnostics.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <sstream>
+
+#include <odb/cxx-lexer.hxx>
+#include <odb/diagnostics.hxx>
+
+using namespace std;
+using cutl::fs::path;
+
+std::ostream&
+error (path const& p, size_t line, size_t clmn)
+{
+ //@@ We only need to do this if we are still parsing (i.e.,
+ // pragma parsing). Is there a way to detect this?
+ //
+ errorcount++;
+
+ cerr << p << ':' << line << ':' << clmn << ": error: ";
+ return cerr;
+}
+
+std::ostream&
+warn (path const& p, size_t line, size_t clmn)
+{
+ warningcount++;
+
+ cerr << p << ':' << line << ':' << clmn << ": warning: ";
+ return cerr;
+}
+
+std::ostream&
+info (path const& p, size_t line, size_t clmn)
+{
+ cerr << p << ':' << line << ':' << clmn << ": info: ";
+ return cerr;
+}
+
+std::ostream&
+error (location_t loc)
+{
+ errorcount++;
+ cerr << LOCATION_FILE (loc) << ':'
+ << LOCATION_LINE (loc) << ':'
+ << LOCATION_COLUMN (loc) << ':'
+ << " error: ";
+ return cerr;
+}
+
+std::ostream&
+warn (location_t loc)
+{
+ warningcount++;
+ cerr << LOCATION_FILE (loc) << ':'
+ << LOCATION_LINE (loc) << ':'
+ << LOCATION_COLUMN (loc) << ':'
+ << " warning: ";
+ return cerr;
+}
+
+std::ostream&
+info (location_t loc)
+{
+ cerr << LOCATION_FILE (loc) << ':'
+ << LOCATION_LINE (loc) << ':'
+ << LOCATION_COLUMN (loc) << ':'
+ << " info: ";
+ return cerr;
+}
+
+std::ostream&
+error (cxx_lexer& l)
+{
+ return error (l.location ());
+}
+
+std::ostream&
+warn (cxx_lexer& l)
+{
+ return warn (l.location ());
+}
+
+std::ostream&
+info (cxx_lexer& l)
+{
+ return info (l.location ());
+}
+
+std::string
+location_string (path const& p, size_t line, size_t clmn, bool leaf)
+{
+ ostringstream ostr;
+
+ if (leaf)
+ ostr << p.leaf ();
+ else
+ ostr << p;
+
+ ostr << ':' << line << ':' << clmn;
+ return ostr.str ();
+}
+
+std::string
+location_string (location_t loc, bool leaf)
+{
+ ostringstream ostr;
+
+ if (leaf)
+ ostr << path (LOCATION_FILE (loc)).leaf ();
+ else
+ ostr << LOCATION_FILE (loc);
+
+ ostr << ':' << LOCATION_LINE (loc) << ':' << LOCATION_COLUMN (loc);
+ return ostr.str ();
+}
+
+path
+location_file (location_t loc)
+{
+ return path (LOCATION_FILE (loc));
+}
+
+size_t
+location_line (location_t loc)
+{
+ return LOCATION_LINE (loc);
+}
+
+size_t
+location_column (location_t loc)
+{
+ return LOCATION_COLUMN (loc);
+}
diff --git a/odb/odb/diagnostics.hxx b/odb/odb/diagnostics.hxx
new file mode 100644
index 0000000..46f2272
--- /dev/null
+++ b/odb/odb/diagnostics.hxx
@@ -0,0 +1,96 @@
+// file : odb/diagnostics.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_DIAGNOSTICS_HXX
+#define ODB_DIAGNOSTICS_HXX
+
+#include <odb/gcc-fwd.hxx>
+
+#include <string>
+#include <cstddef>
+#include <iostream>
+
+#include <libcutl/fs/path.hxx>
+
+#include <odb/location.hxx>
+
+using std::endl;
+
+std::ostream&
+error (cutl::fs::path const&, std::size_t line, std::size_t clmn);
+
+std::ostream&
+warn (cutl::fs::path const&, std::size_t line, std::size_t clmn);
+
+std::ostream&
+info (cutl::fs::path const&, std::size_t line, std::size_t clmn);
+
+inline std::ostream&
+error (location const& l)
+{
+ return error (l.file, l.line, l.column);
+}
+
+inline std::ostream&
+warn (location const&l)
+{
+ return warn (l.file, l.line, l.column);
+}
+
+inline std::ostream&
+info (location const&l)
+{
+ return info (l.file, l.line, l.column);
+}
+
+std::ostream&
+error (location_t);
+
+std::ostream&
+warn (location_t);
+
+std::ostream&
+info (location_t);
+
+//
+//
+class cxx_lexer;
+
+std::ostream&
+error (cxx_lexer&);
+
+std::ostream&
+warn (cxx_lexer&);
+
+std::ostream&
+info (cxx_lexer&);
+
+// Location as a string in the "<file>:<line>:<column>" format.
+//
+std::string
+location_string (cutl::fs::path const&,
+ std::size_t line,
+ std::size_t clmn,
+ bool leaf = false);
+
+inline std::string
+location_string (location const& l, bool leaf = false)
+{
+ return location_string (l.file, l.line, l.column, leaf);
+}
+
+std::string
+location_string (location_t, bool leaf = false);
+
+// location_t macro wrappers.
+//
+cutl::fs::path
+location_file (location_t);
+
+std::size_t
+location_line (location_t);
+
+std::size_t
+location_column (location_t);
+
+#endif // ODB_DIAGNOSTICS_HXX
diff --git a/odb/odb/emitter.cxx b/odb/odb/emitter.cxx
new file mode 100644
index 0000000..d6d8eac
--- /dev/null
+++ b/odb/odb/emitter.cxx
@@ -0,0 +1,50 @@
+// file : odb/emitter.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/context.hxx>
+#include <odb/emitter.hxx>
+
+using namespace std;
+
+void emitter::
+pre ()
+{
+}
+
+void emitter::
+post ()
+{
+}
+
+int emitter_ostream::streambuf::
+sync ()
+{
+ string s (str ());
+
+ // Get rid of the trailing newline if any.
+ //
+ if (string::size_type n = s.size ())
+ {
+ if (s[n - 1] == '\n')
+ s.resize (n - 1);
+ }
+
+ // Temporary restore output diversion.
+ //
+ bool r (false);
+ context& ctx (context::current ());
+
+ if (ctx.os.rdbuf () == this)
+ {
+ ctx.restore ();
+ r = true;
+ }
+
+ e_.line (s);
+
+ if (r)
+ ctx.diverge (this);
+
+ str (string ());
+ return 0;
+}
diff --git a/odb/odb/emitter.hxx b/odb/odb/emitter.hxx
new file mode 100644
index 0000000..1071dab
--- /dev/null
+++ b/odb/odb/emitter.hxx
@@ -0,0 +1,47 @@
+// file : odb/emitter.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_EMITTER_HXX
+#define ODB_EMITTER_HXX
+
+#include <string>
+#include <sstream>
+
+// Emit a code construct one line at a time.
+//
+struct emitter
+{
+ virtual void
+ pre ();
+
+ virtual void
+ line (const std::string&) = 0;
+
+ virtual void
+ post ();
+};
+
+// Send output line-by-line (std::endl marker) to the emitter.
+//
+class emitter_ostream: public std::ostream
+{
+public:
+ emitter_ostream (emitter& e): std::ostream (&buf_), buf_ (e) {}
+
+private:
+ class streambuf: public std::stringbuf
+ {
+ public:
+ streambuf (emitter& e): e_ (e) {}
+
+ virtual int
+ sync ();
+
+ private:
+ emitter& e_;
+ };
+
+ streambuf buf_;
+};
+
+#endif // ODB_EMITTER_HXX
diff --git a/odb/odb/features.hxx b/odb/odb/features.hxx
new file mode 100644
index 0000000..1093684
--- /dev/null
+++ b/odb/odb/features.hxx
@@ -0,0 +1,25 @@
+// file : odb/features.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_FEATURES_HXX
+#define ODB_FEATURES_HXX
+
+#include <cstring> // std::memset
+
+// Optional features used by client code that affect generated code.
+//
+struct features
+{
+ features () {std::memset (this, 0, sizeof (features));}
+
+ bool tr1_pointer;
+ bool boost_pointer;
+ bool simple_object;
+ bool polymorphic_object;
+ bool no_id_object;
+ bool session_object;
+ bool section;
+ bool view;
+};
+
+#endif // ODB_FEATURES_HXX
diff --git a/odb/odb/gcc-fwd.hxx b/odb/odb/gcc-fwd.hxx
new file mode 100644
index 0000000..83d3746
--- /dev/null
+++ b/odb/odb/gcc-fwd.hxx
@@ -0,0 +1,64 @@
+// file : odb/gcc-fwd.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_GCC_FWD_HXX
+#define ODB_GCC_FWD_HXX
+
+#if __GNUC__ >= 5
+# if !__has_include(<bversion.h>)
+# error GCC plugin headers are not installed
+# endif
+#endif
+
+#include <bversion.h>
+
+#if BUILDING_GCC_MAJOR >= 6
+
+// If we include <system.h> here, it pulls in all kinds of GCC trouble that
+// "poisons" standard C/C++ declarations; see safe-ctype.h. So instead we
+// are going to "exclude" safe-ctype.h. To compensate, however, we will
+// include it first thing in gcc.hxx.
+//
+# include <config.h>
+# define SAFE_CTYPE_H
+# include <system.h>
+# undef SAFE_CTYPE_H
+# include <coretypes.h>
+
+typedef unsigned int source_location; // <line-map.h>
+typedef source_location location_t; // <input.h>
+
+#else // GCC < 6
+
+#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 8
+# include <limits.h> // CHAR_BIT
+# include <config.h>
+#endif
+
+#if BUILDING_GCC_MAJOR >= 5
+# include <stdint.h> // Needed by coretypes.h
+#endif
+
+extern "C"
+{
+// The hwint.h header uses gcc_checking_assert macro from system.h. But
+// if we include system.h here, it pulls in all kinds of GCC trouble that
+// "poisons" standard C/C++ declarations (see safe-ctype.h for an example).
+// Instead we are just going to provide the no-op definition of this macro.
+//
+#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 8
+# define gcc_checking_assert(expr) ((void)(0 & (expr)))
+# include <hwint.h>
+# undef gcc_checking_assert
+#endif
+
+#include <coretypes.h>
+
+typedef unsigned int source_location; // <line-map.h>
+typedef source_location location_t; // <input.h>
+
+} // extern "C"
+
+#endif
+
+#endif // ODB_GCC_FWD_HXX
diff --git a/odb/odb/gcc.hxx b/odb/odb/gcc.hxx
new file mode 100644
index 0000000..e5fecef
--- /dev/null
+++ b/odb/odb/gcc.hxx
@@ -0,0 +1,215 @@
+// file : odb/gcc.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_GCC_HXX
+#define ODB_GCC_HXX
+
+#include <odb/gcc-fwd.hxx>
+
+// Actually, let's keep it out. With it included we can compile in C++98
+// but not in C++14 (GCC 6 default).
+//
+// #if BUILDING_GCC_MAJOR >= 6
+// # include <safe-ctype.h> // See gcc-fwd.hxx.
+// #endif
+
+// GCC header includes to get the plugin and parse tree declarations.
+// The order is important and doesn't follow any kind of logic.
+//
+
+#include <stdlib.h>
+#include <gmp.h>
+
+#include <cstdlib> // Include before GCC poisons some declarations.
+
+// GCC 4.7 can be built using either C or C++ compiler. From 4.8 it
+// is always built as C++.
+//
+#if BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR <= 6
+# define ODB_GCC_PLUGIN_C
+#elif BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR <= 7
+# include <config.h>
+# ifndef ENABLE_BUILD_WITH_CXX
+# define ODB_GCC_PLUGIN_C
+# endif
+#endif
+
+#ifdef ODB_GCC_PLUGIN_C
+extern "C"
+{
+#endif
+
+// GCC's system.h below includes safe-ctype.h which "disables" versions
+// from ctype.h. Well, now it's gonna learn how it feels to be disabled.
+//
+#define SAFE_CTYPE_H
+
+#include <gcc-plugin.h>
+
+#include <config.h>
+#include <system.h>
+#include <coretypes.h>
+#include <tree.h>
+#include <real.h>
+
+#include <cpplib.h>
+#include <cp/cp-tree.h>
+
+#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 5
+# include <c-family/c-common.h>
+# include <c-family/c-pragma.h>
+#else
+# include <c-common.h>
+# include <c-pragma.h>
+#endif
+
+#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 8
+# include <stringpool.h> // get_identifier
+#endif
+
+#include <diagnostic.h>
+#include <output.h>
+
+#ifdef ODB_GCC_PLUGIN_C
+} // extern "C"
+#endif
+
+// Get the value of INTEGER_CST reinterpreted as unsigned.
+//
+inline unsigned long long
+integer_value (tree n)
+{
+ unsigned long long val;
+
+#if BUILDING_GCC_MAJOR >= 5
+ if (tree_fits_uhwi_p (n))
+ val = static_cast<unsigned long long> (tree_to_uhwi (n));
+ else
+ val = static_cast<unsigned long long> (tree_to_shwi (n));
+#else
+ HOST_WIDE_INT hwl (TREE_INT_CST_LOW (n));
+ HOST_WIDE_INT hwh (TREE_INT_CST_HIGH (n));
+ unsigned short width (HOST_BITS_PER_WIDE_INT);
+
+ if (hwh == 0)
+ val = static_cast<unsigned long long> (hwl);
+ else if (hwh == -1 && hwl != 0)
+ val = static_cast<unsigned long long> (hwl);
+ else
+ val = static_cast<unsigned long long> ((hwh << width) + hwl);
+#endif
+
+ return val;
+}
+
+// Since 4.7.0 the location may point inside a macro rather than at
+// the expansion point. We are only really interested in the expansion
+// points so we use the real_source_location() wrapper rather than
+// DECL_SOURCE_LOCATION() to do this at the source.
+//
+inline location_t
+real_source_location (tree n)
+{
+ location_t l (DECL_SOURCE_LOCATION (n));
+
+#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 6
+ l = linemap_resolve_location (line_table, l, LRK_MACRO_EXPANSION_POINT, 0);
+#endif
+
+ return l;
+}
+
+// In 4.9.0 the tree code type was changed from int to enum tree_code.
+// the tree_code_name array is also gone with the get_tree_code_name()
+// function in its place.
+//
+#if BUILDING_GCC_MAJOR > 4 || BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR > 8
+typedef enum tree_code gcc_tree_code_type;
+
+inline const char*
+gcc_tree_code_name (gcc_tree_code_type tc) {return get_tree_code_name (tc);}
+#else
+typedef int gcc_tree_code_type;
+
+inline const char*
+gcc_tree_code_name (gcc_tree_code_type tc) {return tree_code_name[tc];}
+#endif
+
+// Only since GCC 4.7.0.
+//
+#ifndef LOCATION_COLUMN
+#define LOCATION_COLUMN(LOC) (expand_location (LOC).column)
+#endif
+
+#ifndef DECL_SOURCE_COLUMN
+#define DECL_SOURCE_COLUMN(NODE) LOCATION_COLUMN (DECL_SOURCE_LOCATION (NODE))
+#endif
+
+// Only since GCC 4.6.0.
+//
+#ifndef DECL_CHAIN
+#define DECL_CHAIN(x) TREE_CHAIN(x)
+#endif
+
+// In GCC 6 ANON_AGGRNAME_P became anon_aggrname_p().
+// In GCC 10 anon_aggrname_p() became IDENTIFIER_ANON_P.
+//
+#if BUILDING_GCC_MAJOR < 6
+# define IDENTIFIER_ANON_P(X) ANON_AGGRNAME_P(X)
+#elif BUILDING_GCC_MAJOR < 10
+# define IDENTIFIER_ANON_P(X) anon_aggrname_p(X)
+#endif
+
+// In GCC 9:
+//
+// INCLUDED_FROM Became linemap_included_from_linemap().
+//
+// LAST_SOURCE_LINE Was removed apparently as no longer used. Studying
+// the line-map.h diff from 8.3 suggests that the old
+// implementation should still work.
+//
+#if BUILDING_GCC_MAJOR >= 9
+
+inline const line_map_ordinary*
+INCLUDED_FROM (line_maps* set, const line_map_ordinary* map)
+{
+ return linemap_included_from_linemap (set, map);
+}
+
+inline source_location
+LAST_SOURCE_LINE_LOCATION (const line_map_ordinary* map)
+{
+ return (((map[1].start_location - 1
+ - map->start_location)
+ & ~((1 << map->m_column_and_range_bits) - 1))
+ + map->start_location);
+}
+
+inline linenum_type
+LAST_SOURCE_LINE (const line_map_ordinary* map)
+{
+ return SOURCE_LINE (map, LAST_SOURCE_LINE_LOCATION (map));
+}
+
+#endif
+
+// In GCC 11:
+//
+// lookup_qualified_name() has a new interface.
+//
+// DECL_IS_BUILTIN became DECL_IS_UNDECLARED_BUILTIN.
+//
+#if BUILDING_GCC_MAJOR >= 11
+
+inline tree
+lookup_qualified_name (tree scope, tree name, bool type, bool complain)
+{
+ return lookup_qualified_name (
+ scope, name, (type ? LOOK_want::TYPE : LOOK_want::NORMAL), complain);
+}
+
+#define DECL_IS_BUILTIN(decl) DECL_IS_UNDECLARED_BUILTIN(decl)
+
+#endif
+
+#endif // ODB_GCC_HXX
diff --git a/odb/odb/generate.hxx b/odb/odb/generate.hxx
new file mode 100644
index 0000000..b3b9c43
--- /dev/null
+++ b/odb/odb/generate.hxx
@@ -0,0 +1,31 @@
+// file : odb/generate.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_GENERATE_HXX
+#define ODB_GENERATE_HXX
+
+namespace include
+{
+ bool
+ generate (bool header);
+}
+
+namespace header
+{
+ void
+ generate ();
+}
+
+namespace inline_
+{
+ void
+ generate ();
+}
+
+namespace source
+{
+ void
+ generate ();
+}
+
+#endif // ODB_GENERATE_HXX
diff --git a/odb/odb/generator.cxx b/odb/odb/generator.cxx
new file mode 100644
index 0000000..f0b92ab
--- /dev/null
+++ b/odb/odb/generator.cxx
@@ -0,0 +1,1044 @@
+// file : odb/generator.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cctype> // std::toupper, std::is{alpha,upper,lower}
+#include <string>
+#include <memory> // std::unique_ptr
+#include <iomanip>
+#include <fstream>
+#include <sstream>
+#include <iostream>
+
+#include <libcutl/fs/auto-remove.hxx>
+
+#include <libcutl/compiler/code-stream.hxx>
+#include <libcutl/compiler/cxx-indenter.hxx>
+#include <libcutl/compiler/sloc-counter.hxx>
+
+#include <libstudxml/parser.hxx>
+#include <libstudxml/serializer.hxx>
+
+#include <odb/version.hxx>
+#include <odb/context.hxx>
+#include <odb/generator.hxx>
+
+#include <odb/semantics/relational/model.hxx>
+#include <odb/semantics/relational/changeset.hxx>
+#include <odb/semantics/relational/changelog.hxx>
+
+#include <odb/generate.hxx>
+#include <odb/relational/generate.hxx>
+
+using namespace std;
+using namespace cutl;
+
+using semantics::path;
+typedef vector<string> strings;
+typedef vector<path> paths;
+typedef vector<cutl::shared_ptr<ofstream> > ofstreams;
+
+namespace
+{
+ static char const cxx_file_header[] =
+ "// -*- C++ -*-\n"
+ "//\n"
+ "// This file was generated by ODB, object-relational mapping (ORM)\n"
+ "// compiler for C++.\n"
+ "//\n\n";
+
+ static char const sql_file_header[] =
+ "/* This file was generated by ODB, object-relational mapping (ORM)\n"
+ " * compiler for C++.\n"
+ " */\n\n";
+
+ void
+ open (ifstream& ifs, path const& p)
+ {
+ ifs.open (p.string ().c_str (), ios_base::in | ios_base::binary);
+
+ if (!ifs.is_open ())
+ {
+ cerr << "error: unable to open '" << p << "' in read mode" << endl;
+ throw generator_failed ();
+ }
+ }
+
+ void
+ open (ofstream& ofs, path const& p, ios_base::openmode m = ios_base::out)
+ {
+ ofs.open (p.string ().c_str (), ios_base::out | m);
+
+ if (!ofs.is_open ())
+ {
+ cerr << "error: unable to open '" << p << "' in write mode" << endl;
+ throw generator_failed ();
+ }
+ }
+
+ void
+ append (ostream& os, strings const& text)
+ {
+ for (strings::const_iterator i (text.begin ());
+ i != text.end (); ++i)
+ {
+ os << *i << endl;
+ }
+ }
+
+ void
+ append (ostream& os, path const& file)
+ {
+ ifstream ifs;
+ open (ifs, file);
+
+ // getline() will set the failbit if it failed to extract anything,
+ // not even the delimiter and eofbit if it reached eof before seeing
+ // the delimiter.
+ //
+ // We used to just do:
+ //
+ // os << ifs.rdbuf ();
+ //
+ // But that has some drawbacks: it won't end with a newline if the file
+ // doesn't end with one. There were also some issues with Windows newlines
+ // (we ended up doubling them).
+ //
+ for (string s; getline (ifs, s); )
+ os << s << endl;
+ }
+
+ // Append prologue/interlude/epilogue.
+ //
+ void
+ append_logue (ostream& os,
+ database db,
+ database_map<vector<string> > const& text,
+ database_map<vector<string> > const& file,
+ char const* begin_comment,
+ char const* end_comment)
+ {
+ bool t (text.count (db) != 0);
+ bool f (file.count (db) != 0);
+
+ if (t || f)
+ {
+ os << begin_comment << endl;
+
+ if (t)
+ append (os, text[db]);
+
+ if (f)
+ {
+ strings const& fs (file[db]);
+
+ for (strings::const_iterator i (fs.begin ());
+ i != fs.end (); ++i)
+ append (os, path (*i));
+ }
+
+ os << end_comment << endl
+ << endl;
+ }
+ }
+}
+
+void
+generate (options const& ops,
+ features& fts,
+ semantics::unit& unit,
+ path const& p,
+ paths const& inputs)
+{
+ namespace sema_rel = semantics::relational;
+ using cutl::shared_ptr;
+
+ try
+ {
+ database db (ops.database ()[0]);
+ multi_database md (ops.multi_database ());
+
+ // First create the database model.
+ //
+ bool gen_schema (ops.generate_schema () && db != database::common);
+
+ shared_ptr<sema_rel::model> model;
+
+ if (gen_schema)
+ {
+ unique_ptr<context> ctx (create_context (cerr, unit, ops, fts, 0));
+
+ switch (db)
+ {
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ model = relational::model::generate ();
+ break;
+ }
+ case database::common:
+ break;
+ }
+ }
+
+ // Input files.
+ //
+ path file (ops.input_name ().empty ()
+ ? p.leaf ()
+ : path (ops.input_name ()).leaf ());
+ string base (file.base ().string ());
+
+ path in_log_path;
+ path log_dir (ops.changelog_dir ().count (db) != 0
+ ? ops.changelog_dir ()[db]
+ : "");
+ if (ops.changelog_in ().count (db) != 0)
+ {
+ in_log_path = path (ops.changelog_in ()[db]);
+
+ if (!log_dir.empty () && !in_log_path.absolute ())
+ in_log_path = log_dir / in_log_path;
+ }
+ else if (ops.changelog ().count (db) != 0)
+ {
+ in_log_path = path (ops.changelog ()[db]);
+
+ if (!in_log_path.absolute () && !log_dir.empty ())
+ in_log_path = log_dir / in_log_path;
+ }
+ else
+ {
+ string log_name (base + ops.changelog_file_suffix ()[db] +
+ ops.changelog_suffix ());
+ in_log_path = path (log_name);
+
+ if (!log_dir.empty ())
+ in_log_path = log_dir / in_log_path;
+ else
+ in_log_path = p.directory () / in_log_path; // Use input directory.
+ }
+
+ // Load the old changelog and generate a new one.
+ //
+ bool gen_changelog (gen_schema && unit.count ("model-version") != 0);
+ cutl::shared_ptr<sema_rel::changelog> changelog;
+ cutl::shared_ptr<sema_rel::changelog> old_changelog;
+ string old_changelog_xml;
+
+ path out_log_path;
+ if (ops.changelog_out ().count (db))
+ {
+ out_log_path = path (ops.changelog_out ()[db]);
+
+ if (!log_dir.empty () && !out_log_path.absolute ())
+ out_log_path = log_dir / out_log_path;
+ }
+ else
+ out_log_path = in_log_path;
+
+ if (gen_changelog)
+ {
+ ifstream log;
+
+ // Unless we are forced to re-initialize the changelog, load the
+ // old one.
+ //
+ if (!ops.init_changelog ())
+ log.open (in_log_path.string ().c_str (),
+ ios_base::in | ios_base::binary);
+
+ if (log.is_open ()) // The changelog might not exist.
+ {
+ try
+ {
+ // Get the XML into a buffer. We use it to avoid modifying the
+ // file when the changelog hasn't changed.
+ //
+ for (bool first (true); !log.eof (); )
+ {
+ string line;
+ getline (log, line);
+
+ if (log.fail ())
+ ios_base::failure ("getline");
+
+ if (first)
+ first = false;
+ else
+ old_changelog_xml += '\n';
+
+ old_changelog_xml += line;
+ }
+
+ istringstream is (old_changelog_xml);
+ is.exceptions (ios_base::badbit | ios_base::failbit);
+
+ xml::parser p (is, in_log_path.string ());
+ old_changelog.reset (new (shared) sema_rel::changelog (p));
+
+ if (old_changelog->database () != db.string ())
+ {
+ cerr << in_log_path << ": error: wrong database '" <<
+ old_changelog->database () << "', expected '" << db <<
+ "'" << endl;
+ throw generator_failed ();
+ }
+
+ string sn (ops.schema_name ()[db]);
+ if (old_changelog->schema_name () != sn)
+ {
+ cerr << in_log_path << ": error: wrong schema name '" <<
+ old_changelog->schema_name () << "', expected '" << sn <<
+ "'" << endl;
+ throw generator_failed ();
+ }
+ }
+ catch (const ios_base::failure& e)
+ {
+ cerr << in_log_path << ": read failure" << endl;
+ throw generator_failed ();
+ }
+ catch (const xml::parsing& e)
+ {
+ cerr << e.what () << endl;
+ throw generator_failed ();
+ }
+ }
+
+ changelog = relational::changelog::generate (
+ *model,
+ unit.get<model_version> ("model-version"),
+ old_changelog.get (),
+ in_log_path.string (),
+ out_log_path.string (),
+ ops);
+ }
+
+ // Output files.
+ //
+ fs::auto_removes auto_rm;
+
+ string hxx_name (base + ops.odb_file_suffix ()[db] + ops.hxx_suffix ());
+ string ixx_name (base + ops.odb_file_suffix ()[db] + ops.ixx_suffix ());
+ string cxx_name (base + ops.odb_file_suffix ()[db] + ops.cxx_suffix ());
+ string sch_name (base + ops.schema_file_suffix ()[db] + ops.cxx_suffix ());
+ string sql_name (base + ops.sql_file_suffix ()[db] + ops.sql_suffix ());
+
+ path hxx_path (hxx_name);
+ path ixx_path (ixx_name);
+ path cxx_path (cxx_name);
+ path sch_path (sch_name);
+ path sql_path (sql_name);
+ paths mig_pre_paths;
+ paths mig_post_paths;
+
+ bool gen_migration (gen_changelog && !ops.suppress_migration ());
+ bool gen_sql_migration (
+ gen_migration && ops.schema_format ()[db].count (schema_format::sql));
+
+ if (gen_sql_migration)
+ {
+ for (sema_rel::changelog::contains_changeset_iterator i (
+ changelog->contains_changeset_begin ());
+ i != changelog->contains_changeset_end (); ++i)
+ {
+ sema_rel::changeset& cs (i->changeset ());
+
+ // Default format: %N[-D%]-%3V-{pre|post}.sql
+ //
+ string n (base);
+
+ if (md != multi_database::disabled)
+ n += '-' + db.string ();
+
+ ostringstream os;
+ os << setfill ('0') << setw (3) << cs.version ();
+ n += '-' + os.str ();
+
+ mig_pre_paths.push_back (path (n + "-pre" + ops.sql_suffix ()));
+ mig_post_paths.push_back (path (n + "-post" + ops.sql_suffix ()));
+ }
+ }
+
+ if (!ops.output_dir ().empty ())
+ {
+ path dir (ops.output_dir ());
+ hxx_path = dir / hxx_path;
+ ixx_path = dir / ixx_path;
+ cxx_path = dir / cxx_path;
+ sch_path = dir / sch_path;
+ sql_path = dir / sql_path;
+
+ if (gen_sql_migration)
+ {
+ for (paths::size_type i (0); i < mig_pre_paths.size (); ++i)
+ {
+ mig_pre_paths[i] = dir / mig_pre_paths[i];
+ mig_post_paths[i] = dir / mig_post_paths[i];
+ }
+ }
+ }
+
+ //
+ //
+ bool gen_cxx (!ops.generate_schema_only ());
+
+ ofstream hxx;
+ if (gen_cxx)
+ {
+ open (hxx, hxx_path);
+ auto_rm.add (hxx_path);
+ }
+
+ //
+ //
+ ofstream ixx;
+ if (gen_cxx)
+ {
+ open (ixx, ixx_path);
+ auto_rm.add (ixx_path);
+ }
+
+ //
+ //
+ ofstream cxx;
+ if (gen_cxx && (db != database::common || md == multi_database::dynamic))
+ {
+ open (cxx, cxx_path);
+ auto_rm.add (cxx_path);
+ }
+
+ //
+ //
+ bool gen_sep_schema (
+ gen_schema &&
+ ops.schema_format ()[db].count (schema_format::separate));
+
+ ofstream sch;
+ if (gen_sep_schema)
+ {
+ open (sch, sch_path);
+ auto_rm.add (sch_path);
+ }
+
+ //
+ //
+ bool gen_sql_schema (gen_schema &&
+ ops.schema_format ()[db].count (schema_format::sql));
+ ofstream sql;
+ if (gen_sql_schema)
+ {
+ open (sql, sql_path);
+ auto_rm.add (sql_path);
+ }
+
+ //
+ //
+ ofstreams mig_pre, mig_post;
+ if (gen_sql_migration)
+ {
+ for (paths::size_type i (0); i < mig_pre_paths.size (); ++i)
+ {
+ shared_ptr<ofstream> pre (new (shared) ofstream);
+ shared_ptr<ofstream> post (new (shared) ofstream);
+
+ open (*pre, mig_pre_paths[i]);
+ auto_rm.add (mig_pre_paths[i]);
+ mig_pre.push_back (pre);
+
+ open (*post, mig_post_paths[i]);
+ auto_rm.add (mig_post_paths[i]);
+ mig_post.push_back (post);
+ }
+ }
+
+ // Print output file headers.
+ //
+ if (gen_cxx)
+ {
+ hxx << cxx_file_header;
+ ixx << cxx_file_header;
+
+ if (db != database::common)
+ cxx << cxx_file_header;
+ }
+
+ if (gen_sep_schema)
+ sch << cxx_file_header;
+
+ if (gen_sql_schema)
+ sql << sql_file_header;
+
+ if (gen_sql_migration)
+ {
+ for (ofstreams::size_type i (0); i < mig_pre.size (); ++i)
+ {
+ *mig_pre[i] << sql_file_header;
+ *mig_post[i] << sql_file_header;
+ }
+ }
+
+ typedef compiler::ostream_filter<compiler::cxx_indenter, char> ind_filter;
+ typedef compiler::ostream_filter<compiler::sloc_counter, char> sloc_filter;
+
+ size_t sloc_total (0);
+
+ // Include settings.
+ //
+ string gp (ops.guard_prefix ());
+ if (!gp.empty () && gp[gp.size () - 1] != '_')
+ gp.append ("_");
+
+ // HXX
+ //
+ if (gen_cxx)
+ {
+ unique_ptr<context> ctx (
+ create_context (hxx, unit, ops, fts, model.get ()));
+
+ sloc_filter sloc (ctx->os);
+
+ string guard (ctx->make_guard (gp + hxx_name));
+
+ hxx << "#ifndef " << guard << endl
+ << "#define " << guard << endl
+ << endl;
+
+ // Copy prologue.
+ //
+ append_logue (hxx,
+ db,
+ ops.hxx_prologue (),
+ ops.hxx_prologue_file (),
+ "// Begin prologue.\n//",
+ "//\n// End prologue.");
+
+ // Version check (see similar check in odb.cxx for background).
+ //
+ hxx << "#include <odb/version.hxx>" << endl
+ << endl
+#if 1
+ << "#if ODB_VERSION != " << ODB_VERSION << "UL" << endl
+#else
+ << "#if LIBODB_VERSION_FULL != " << ODB_COMPILER_VERSION << "ULL || \\" << endl
+ << " LIBODB_SNAPSHOT != " << ODB_COMPILER_SNAPSHOT << "ULL" << endl
+#endif
+ << "#error ODB runtime version mismatch" << endl
+ << "#endif" << endl
+ << endl;
+
+ hxx << "#include <odb/pre.hxx>" << endl
+ << endl;
+
+ // Include main file(s).
+ //
+ for (paths::const_iterator i (inputs.begin ()); i != inputs.end (); ++i)
+ hxx << "#include " <<
+ ctx->process_include_path (i->leaf ().string ()) << endl;
+
+ hxx << endl;
+
+ // There are no -odb.hxx includes if we are generating code for
+ // everything.
+ //
+ if (!ops.at_once ())
+ if (include::generate (true))
+ hxx << endl;
+
+ {
+ // We don't want to indent prologues/epilogues.
+ //
+ ind_filter ind (ctx->os);
+
+ switch (db)
+ {
+ case database::common:
+ {
+ header::generate ();
+ break;
+ }
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ if (md == multi_database::disabled)
+ header::generate ();
+ else
+ {
+ string n (base +
+ ops.odb_file_suffix ()[database::common] +
+ ops.hxx_suffix ());
+
+ ctx->os << "#include " << ctx->process_include_path (n) << endl
+ << endl;
+ }
+
+ relational::header::generate ();
+ break;
+ }
+ }
+ }
+
+ hxx << "#include " << ctx->process_include_path (ixx_name) << endl
+ << endl;
+
+ hxx << "#include <odb/post.hxx>" << endl
+ << endl;
+
+ // Copy epilogue.
+ //
+ append_logue (hxx,
+ db,
+ ops.hxx_epilogue (),
+ ops.hxx_epilogue_file (),
+ "// Begin epilogue.\n//",
+ "//\n// End epilogue.");
+
+ hxx << "#endif // " << guard << endl;
+
+ if (ops.show_sloc ())
+ cerr << hxx_name << ": " << sloc.stream ().count () << endl;
+
+ sloc_total += sloc.stream ().count ();
+ }
+
+ // IXX
+ //
+ if (gen_cxx)
+ {
+ unique_ptr<context> ctx (
+ create_context (ixx, unit, ops, fts, model.get ()));
+
+ sloc_filter sloc (ctx->os);
+
+ // Copy prologue.
+ //
+ append_logue (ixx,
+ db,
+ ops.ixx_prologue (),
+ ops.ixx_prologue_file (),
+ "// Begin prologue.\n//",
+ "//\n// End prologue.");
+
+ {
+ // We don't want to indent prologues/epilogues.
+ //
+ ind_filter ind (ctx->os);
+
+ switch (db)
+ {
+ case database::common:
+ {
+ inline_::generate ();
+ break;
+ }
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ if (md == multi_database::disabled)
+ inline_::generate ();
+
+ relational::inline_::generate ();
+ break;
+ }
+ }
+ }
+
+ // Copy epilogue.
+ //
+ append_logue (ixx,
+ db,
+ ops.ixx_epilogue (),
+ ops.ixx_epilogue_file (),
+ "// Begin epilogue.\n//",
+ "//\n// End epilogue.");
+
+ if (ops.show_sloc ())
+ cerr << ixx_name << ": " << sloc.stream ().count () << endl;
+
+ sloc_total += sloc.stream ().count ();
+ }
+
+ // CXX
+ //
+ if (gen_cxx && (db != database::common || md == multi_database::dynamic))
+ {
+ unique_ptr<context> ctx (
+ create_context (cxx, unit, ops, fts, model.get ()));
+
+ sloc_filter sloc (ctx->os);
+
+ // Copy prologue.
+ //
+ append_logue (cxx,
+ db,
+ ops.cxx_prologue (),
+ ops.cxx_prologue_file (),
+ "// Begin prologue.\n//",
+ "//\n// End prologue.");
+
+ cxx << "#include <odb/pre.hxx>" << endl
+ << endl;
+
+ // Include query columns implementations for explicit instantiations.
+ //
+ string impl_guard;
+ if (md == multi_database::dynamic && ctx->ext.empty ())
+ {
+ impl_guard = ctx->make_guard (
+ "ODB_" + db.string () + "_QUERY_COLUMNS_DEF");
+
+ cxx << "#define " << impl_guard << endl;
+ }
+
+ cxx << "#include " << ctx->process_include_path (hxx_name) << endl;
+
+ // There are no -odb.hxx includes if we are generating code for
+ // everything.
+ //
+ if (!ops.at_once ())
+ include::generate (false);
+
+ if (!impl_guard.empty ())
+ cxx << "#undef " << impl_guard << endl;
+
+ cxx << endl;
+
+ {
+ // We don't want to indent prologues/epilogues.
+ //
+ ind_filter ind (ctx->os);
+
+ switch (db)
+ {
+ case database::common:
+ {
+ // Dynamic multi-database support.
+ //
+ source::generate ();
+ break;
+ }
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ relational::source::generate ();
+
+ if (gen_schema &&
+ ops.schema_format ()[db].count (schema_format::embedded))
+ relational::schema::generate_source (changelog.get ());
+
+ break;
+ }
+ }
+ }
+
+ cxx << "#include <odb/post.hxx>" << endl;
+
+ // Copy epilogue.
+ //
+ append_logue (cxx,
+ db,
+ ops.cxx_epilogue (),
+ ops.cxx_epilogue_file (),
+ "// Begin epilogue.\n//",
+ "//\n// End epilogue.");
+
+ if (ops.show_sloc ())
+ cerr << cxx_name << ": " << sloc.stream ().count () << endl;
+
+ sloc_total += sloc.stream ().count ();
+ }
+
+ // SCH
+ //
+ if (gen_sep_schema)
+ {
+ unique_ptr<context> ctx (
+ create_context (sch, unit, ops, fts, model.get ()));
+
+ sloc_filter sloc (ctx->os);
+
+ // Copy prologue.
+ //
+ append_logue (sch,
+ db,
+ ops.schema_prologue (),
+ ops.schema_prologue_file (),
+ "// Begin prologue.\n//",
+ "//\n// End prologue.");
+
+ sch << "#include <odb/pre.hxx>" << endl
+ << endl;
+
+ sch << "#include <odb/database.hxx>" << endl
+ << "#include <odb/schema-catalog-impl.hxx>" << endl
+ << endl
+ << "#include <odb/details/unused.hxx>" << endl
+ << endl;
+
+ {
+ // We don't want to indent prologues/epilogues.
+ //
+ ind_filter ind (ctx->os);
+
+ switch (db)
+ {
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ relational::schema::generate_source (changelog.get ());
+ break;
+ }
+ case database::common:
+ assert (false);
+ }
+ }
+
+ sch << "#include <odb/post.hxx>" << endl;
+
+ // Copy epilogue.
+ //
+ append_logue (sch,
+ db,
+ ops.schema_epilogue (),
+ ops.schema_epilogue_file (),
+ "// Begin epilogue.\n//",
+ "//\n// End epilogue.");
+
+ if (ops.show_sloc ())
+ cerr << sch_name << ": " << sloc.stream ().count () << endl;
+
+ sloc_total += sloc.stream ().count ();
+ }
+
+ // SQL
+ //
+ if (gen_sql_schema)
+ {
+ unique_ptr<context> ctx (
+ create_context (sql, unit, ops, fts, model.get ()));
+
+ switch (db)
+ {
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ // Prologue.
+ //
+ relational::schema::generate_prologue ();
+ append_logue (sql,
+ db,
+ ops.sql_prologue (),
+ ops.sql_prologue_file (),
+ "/* Begin prologue.\n */",
+ "/*\n * End prologue. */");
+
+ if (!ops.omit_drop ())
+ relational::schema::generate_drop ();
+
+ // Interlude.
+ //
+ append_logue (sql,
+ db,
+ ops.sql_interlude (),
+ ops.sql_interlude_file (),
+ "/* Begin interlude.\n */",
+ "/*\n * End interlude. */");
+
+ if (!ops.omit_create ())
+ relational::schema::generate_create ();
+
+ // Epilogue.
+ //
+ append_logue (sql,
+ db,
+ ops.sql_epilogue (),
+ ops.sql_epilogue_file (),
+ "/* Begin epilogue.\n */",
+ "/*\n * End epilogue. */");
+ relational::schema::generate_epilogue ();
+
+ break;
+ }
+ case database::common:
+ assert (false);
+ }
+ }
+
+ // MIG
+ //
+ if (gen_sql_migration)
+ {
+ for (ofstreams::size_type i (0); i < mig_pre.size (); ++i)
+ {
+ sema_rel::changeset& cs (
+ changelog->contains_changeset_at (i).changeset ());
+
+ // pre
+ //
+ {
+ ofstream& mig (*mig_pre[i]);
+ unique_ptr<context> ctx (create_context (mig, unit, ops, fts, 0));
+
+ switch (db)
+ {
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ // Prologue.
+ //
+ relational::schema::generate_prologue ();
+ append_logue (mig,
+ db,
+ ops.migration_prologue (),
+ ops.migration_prologue_file (),
+ "/* Begin prologue.\n */",
+ "/*\n * End prologue. */");
+
+ relational::schema::generate_migrate_pre (cs);
+
+ // Epilogue.
+ //
+ append_logue (mig,
+ db,
+ ops.migration_epilogue (),
+ ops.migration_epilogue_file (),
+ "/* Begin epilogue.\n */",
+ "/*\n * End epilogue. */");
+ relational::schema::generate_epilogue ();
+
+ break;
+ }
+ case database::common:
+ assert (false);
+ }
+ }
+
+ // post
+ //
+ {
+ ofstream& mig (*mig_post[i]);
+ unique_ptr<context> ctx (create_context (mig, unit, ops, fts, 0));
+
+ switch (db)
+ {
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ // Prologue.
+ //
+ relational::schema::generate_prologue ();
+ append_logue (mig,
+ db,
+ ops.migration_prologue (),
+ ops.migration_prologue_file (),
+ "/* Begin prologue.\n */",
+ "/*\n * End prologue. */");
+
+ relational::schema::generate_migrate_post (cs);
+
+ // Epilogue.
+ //
+ append_logue (mig,
+ db,
+ ops.migration_epilogue (),
+ ops.migration_epilogue_file (),
+ "/* Begin epilogue.\n */",
+ "/*\n * End epilogue. */");
+ relational::schema::generate_epilogue ();
+
+ break;
+ }
+ case database::common:
+ assert (false);
+ }
+ }
+ }
+ }
+
+ // Save the changelog if it has changed.
+ //
+ if (gen_changelog)
+ {
+ try
+ {
+ ostringstream os;
+ os.exceptions (ifstream::badbit | ifstream::failbit);
+ xml::serializer s (os, out_log_path.string ());
+ changelog->serialize (s);
+ string const& changelog_xml (os.str ());
+
+ if (changelog_xml != old_changelog_xml)
+ {
+ ofstream log;
+ open (log, out_log_path, ios_base::binary);
+
+ if (old_changelog == 0)
+ auto_rm.add (out_log_path);
+
+ log.exceptions (ifstream::badbit | ifstream::failbit);
+ log << changelog_xml;
+ }
+ }
+ catch (const ios_base::failure& e)
+ {
+ cerr << out_log_path << ": write failure" << endl;
+ throw generator_failed ();
+ }
+ catch (const xml::serialization& e)
+ {
+ cerr << e.what () << endl;
+ throw generator_failed ();
+ }
+ }
+
+ // Communicate the sloc count to the driver. This is necessary to
+ // correctly handle the total if we are compiling multiple files in
+ // one invocation.
+ //
+ if (ops.show_sloc () || ops.sloc_limit_specified ())
+ cout << "odb:sloc:" << sloc_total << endl;
+
+ auto_rm.cancel ();
+ }
+ catch (operation_failed const&)
+ {
+ // Code generation failed. Diagnostics has already been issued.
+ //
+ throw generator_failed ();
+ }
+ catch (semantics::invalid_path const& e)
+ {
+ cerr << "error: '" << e.path () << "' is not a valid filesystem path"
+ << endl;
+ throw generator_failed ();
+ }
+ catch (fs::error const&)
+ {
+ // Auto-removal of generated files failed. Ignore it.
+ //
+ throw generator_failed ();
+ }
+}
diff --git a/odb/odb/generator.hxx b/odb/odb/generator.hxx
new file mode 100644
index 0000000..205043b
--- /dev/null
+++ b/odb/odb/generator.hxx
@@ -0,0 +1,22 @@
+// file : odb/generator.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_GENERATOR_HXX
+#define ODB_GENERATOR_HXX
+
+#include <vector>
+
+#include <odb/options.hxx>
+#include <odb/features.hxx>
+#include <odb/semantics/unit.hxx>
+
+class generator_failed {};
+
+void
+generate (options const&,
+ features&,
+ semantics::unit&,
+ semantics::path const& file,
+ std::vector<semantics::path> const& inputs);
+
+#endif // ODB_GENERATOR_HXX
diff --git a/odb/odb/header.cxx b/odb/odb/header.cxx
new file mode 100644
index 0000000..dacdd1d
--- /dev/null
+++ b/odb/odb/header.cxx
@@ -0,0 +1,902 @@
+// file : odb/header.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/common.hxx>
+#include <odb/context.hxx>
+#include <odb/generate.hxx>
+
+using namespace std;
+
+namespace header
+{
+ struct class1: traversal::class_, virtual context
+ {
+ class1 ()
+ : typedefs_ (false),
+ query_columns_type_ (false, true, false),
+ pointer_query_columns_type_ (true, true, false)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck == class_other ||
+ (!options.at_once () && class_file (c) != unit.file ()))
+ return;
+
+ names (c);
+
+ switch (ck)
+ {
+ case class_object: traverse_object (c); break;
+ case class_view: traverse_view (c); break;
+ default: break;
+ }
+ }
+
+ void
+ traverse_object (type&);
+
+ void
+ traverse_view (type&);
+
+ private:
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ instance<query_columns_type> query_columns_type_;
+ instance<query_columns_type> pointer_query_columns_type_;
+ };
+}
+
+void header::class1::
+traverse_object (type& c)
+{
+ using semantics::data_member;
+
+ data_member_path* id (id_member (c));
+ data_member* idf (id ? id->front () : 0);
+ bool auto_id (id && auto_ (*id));
+ bool base_id (id && &idf->scope () != &c); // Comes from base.
+
+ data_member* opt (context::optimistic (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+ type* poly_base (poly_derived ? &polymorphic_base (c) : 0);
+ data_member* discriminator (poly ? context::discriminator (*poly_root) : 0);
+
+ bool abst (abstract (c));
+ bool reuse_abst (abst && !poly);
+
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ string const& type (class_fq_name (c));
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl;
+
+ // class_traits
+ //
+ os << "template <>" << endl
+ << "struct class_traits< " << type << " >"
+ << "{"
+ << "static const class_kind kind = class_object;"
+ << "};";
+
+ // object_traits
+ //
+ os << "template <>" << endl
+ << "class " << exp << "access::object_traits< " << type << " >"
+ << "{"
+ << "public:" << endl;
+
+ // object_type & pointer_type
+ //
+ os << "typedef " << type << " object_type;"
+ << "typedef " << c.get<string> ("object-pointer") << " pointer_type;"
+ << "typedef odb::pointer_traits<pointer_type> pointer_traits;"
+ << endl;
+
+ // polymorphic, root_type, base_type, etc.
+ //
+ os << "static const bool polymorphic = " << poly << ";"
+ << endl;
+
+ if (poly)
+ {
+ os << "typedef " << class_fq_name (*poly_root) << " root_type;";
+
+ if (poly_derived)
+ {
+ os << "typedef " << class_fq_name (*poly_base) << " base_type;"
+ << "typedef object_traits<root_type>::discriminator_type " <<
+ "discriminator_type;"
+ << "typedef polymorphic_concrete_info<root_type> info_type;";
+
+ if (abst)
+ os << "typedef polymorphic_abstract_info<root_type> " <<
+ "abstract_info_type;";
+
+ // Calculate our hierarchy depth (number of classes).
+ //
+ size_t depth (polymorphic_depth (c));
+
+ os << endl
+ << "static const std::size_t depth = " << depth << "UL;";
+ }
+ else
+ {
+ semantics::names* hint;
+ semantics::type& t (utype (*discriminator, hint));
+
+ os << "typedef " << t.fq_name (hint) << " discriminator_type;"
+ << "typedef polymorphic_map<object_type> map_type;"
+ << "typedef polymorphic_concrete_info<object_type> info_type;";
+
+ if (abst)
+ os << "typedef polymorphic_abstract_info<object_type> " <<
+ "abstract_info_type;";
+
+ os << endl
+ << "static const std::size_t depth = 1UL;";
+ }
+
+ os << endl;
+ }
+
+ // id_type, version_type, etc.
+ //
+ if (id != 0)
+ {
+ if (base_id)
+ {
+ semantics::class_& b (dynamic_cast<semantics::class_&> (idf->scope ()));
+ string const& type (class_fq_name (b));
+
+ os << "typedef object_traits< " << type << " >::id_type id_type;";
+
+ if (opt != 0)
+ os << "typedef object_traits< " << type << " >::version_type " <<
+ "version_type;";
+
+ os << endl;
+
+ if (poly_derived)
+ os << "static const bool auto_id = false;";
+ else
+ os << "static const bool auto_id = object_traits< " << type <<
+ " >::auto_id;";
+ }
+ else
+ {
+ {
+ semantics::names* hint;
+ semantics::type& t (utype (*id, hint));
+ os << "typedef " << t.fq_name (hint) << " id_type;";
+ }
+
+ if (opt != 0)
+ {
+ semantics::names* hint;
+ semantics::type& t (utype (*opt, hint));
+ os << "typedef " << t.fq_name (hint) << " version_type;";
+ }
+
+ os << endl
+ << "static const bool auto_id = " << auto_id << ";";
+ }
+
+ os << endl;
+ }
+ else if (!reuse_abst)
+ {
+ // Object without id.
+ //
+ os << "typedef void id_type;"
+ << endl
+ << "static const bool auto_id = false;"
+ << endl;
+ }
+
+ // abstract
+ //
+ os << "static const bool abstract = " << abst << ";"
+ << endl;
+
+ // id()
+ //
+ if (id != 0 || !reuse_abst)
+ {
+ // We want to generate a dummy void id() accessor even if this
+ // object has no id to help us in the runtime. This way we can
+ // write generic code that will work for both void and non-void
+ // ids.
+ //
+ os << "static id_type" << endl
+ << "id (const object_type&);"
+ << endl;
+ }
+
+ // version()
+ //
+ if (opt != 0)
+ {
+ os << "static version_type" << endl
+ << "version (const object_type&);"
+ << endl;
+ }
+
+ // Query.
+ //
+ if (options.generate_query ())
+ {
+ // Generate object pointer tags here if we are generating dynamic
+ // multi-database support.
+ //
+ if (multi_dynamic && has_a (c, test_pointer | exclude_base))
+ {
+ query_tags t;
+ t.traverse (c);
+ }
+ }
+
+ // The rest does not apply to reuse-abstract objects.
+ //
+ if (!reuse_abst)
+ {
+ // Cache traits typedefs.
+ //
+ if (id == 0)
+ {
+ os << "typedef" << endl
+ << "no_id_pointer_cache_traits<pointer_type>" << endl
+ << "pointer_cache_traits;"
+ << endl
+ << "typedef" << endl
+ << "no_id_reference_cache_traits<object_type>" << endl
+ << "reference_cache_traits;"
+ << endl;
+ }
+ else
+ {
+ char const* obj (poly_derived ? "root_type" : "object_type");
+ char const* ptr (poly_derived
+ ? "object_traits<root_type>::pointer_type"
+ : "pointer_type");
+ if (session (c))
+ {
+ string const& s (options.session_type ());
+
+ os << "typedef" << endl
+ << "odb::pointer_cache_traits<" << endl
+ << " " << ptr << "," << endl
+ << " " << s << " >" << endl
+ << "pointer_cache_traits;"
+ << endl
+ << "typedef" << endl
+ << "odb::reference_cache_traits<" << endl
+ << " " << obj << "," << endl
+ << " " << s << " >" << endl
+ << "reference_cache_traits;"
+ << endl;
+ }
+ else
+ {
+ os << "typedef" << endl
+ << "no_op_pointer_cache_traits<" << ptr << ">" << endl
+ << "pointer_cache_traits;"
+ << endl
+ << "typedef" << endl
+ << "no_op_reference_cache_traits<" << obj << ">" << endl
+ << "reference_cache_traits;"
+ << endl;
+ }
+ }
+
+ // callback ()
+ //
+ os << "static void" << endl
+ << "callback (database&, object_type&, callback_event);"
+ << endl;
+
+ os << "static void" << endl
+ << "callback (database&, const object_type&, callback_event);"
+ << endl;
+ }
+
+ os << "};";
+
+ // The rest only applies to dynamic milti-database support.
+ //
+ if (!multi_dynamic)
+ return;
+
+ // pointer_query_columns & query_columns
+ //
+ if (options.generate_query ())
+ {
+ // If we don't have object pointers, then also generate
+ // query_columns (in this case pointer_query_columns and
+ // query_columns are the same and the former inherits from
+ // the latter). Otherwise we have to postpone query_columns
+ // generation until the second pass to deal with forward-
+ // declared objects.
+ //
+ if (!has_a (c, test_pointer | include_base))
+ query_columns_type_->traverse (c);
+
+ pointer_query_columns_type_->traverse (c);
+ }
+
+ // object_traits_impl
+ //
+ os << "template <>" << endl
+ << "class " << exp << "access::object_traits_impl< " << type << ", " <<
+ "id_common >:" << endl
+ << " public access::object_traits< " << type << " >"
+ << "{";
+
+ // We don't need to generate anything else for reuse-abstract objects.
+ //
+ if (reuse_abst)
+ {
+ os << "};";
+ return;
+ }
+
+ os << "public:" << endl;
+
+ if (options.generate_query ())
+ {
+ // base_traits is needed for query support.
+ //
+ if (poly_derived)
+ os << "typedef object_traits_impl<base_type, id_common> base_traits;"
+ << endl;
+
+ // query_base_type
+ //
+ os << "typedef odb::query_base query_base_type;"
+ << endl;
+ }
+
+ // function_table_type
+ //
+ os << "struct function_table_type"
+ << "{";
+
+ // persist ()
+ //
+ os << "void (*persist) (database&, " << (auto_id ? "" : "const ") <<
+ "object_type&" << (poly ? ", bool, bool" : "") << ");";
+
+ if (id != 0)
+ {
+ // find (id)
+ //
+ if (c.default_ctor ())
+ os << "pointer_type (*find1) (database&, const id_type&);";
+
+ // find (id, obj)
+ //
+ os << "bool (*find2) (database&, const id_type&, object_type&" <<
+ (poly ? ", bool" : "") << ");";
+
+ // reload ()
+ //
+ os << "bool (*reload) (database&, object_type&" <<
+ (poly ? ", bool" : "") << ");";
+
+ // update ()
+ //
+ if (!readonly (c) || poly)
+ {
+ os << "void (*update) (database&, const object_type&" <<
+ (poly ? ", bool, bool" : "") << ");";
+ }
+
+ // erase ()
+ //
+ os << "void (*erase1) (database&, const id_type&" <<
+ (poly ? ", bool, bool" : "") << ");";
+
+ os << "void (*erase2) (database&, const object_type&" <<
+ (poly ? ", bool, bool" : "") << ");";
+
+ // Sections.
+ //
+ if (uss.count (user_sections::count_total |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0)
+ os << "bool (*load_section) (connection&, object_type&, section&" <<
+ (poly ? ", const info_type*" : "") << ");";
+
+ if (uss.count (user_sections::count_total |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) != 0)
+ os << "bool (*update_section) (connection&, const object_type&, " <<
+ "const section&" << (poly ? ", const info_type*" : "") << ");";
+ }
+
+ if (options.generate_query ())
+ {
+ if (!options.omit_unprepared ())
+ os << "result<object_type> (*query) (database&, const query_base_type&);";
+
+ os << "unsigned long long (*erase_query) (database&, " <<
+ "const query_base_type&);";
+
+ if (options.generate_prepared ())
+ {
+ os << "odb::details::shared_ptr<prepared_query_impl> " <<
+ "(*prepare_query) (connection&, const char*, const query_base_type&);";
+
+ os << "odb::details::shared_ptr<result_impl> (*execute_query) ("
+ "prepared_query_impl&);";
+ }
+ }
+
+ os << "};" // function_table_type
+ << "static const function_table_type* function_table[database_count];"
+ << endl;
+
+ //
+ // Forwarding functions.
+ //
+
+ // persist ()
+ //
+ os << "static void" << endl
+ << "persist (database&, " << (auto_id ? "" : "const ") << "object_type&);"
+ << endl;
+
+ if (id != 0)
+ {
+ // find (id)
+ //
+ if (c.default_ctor ())
+ os << "static pointer_type" << endl
+ << "find (database&, const id_type&);"
+ << endl;
+
+ // find (id, obj)
+ //
+ os << "static bool" << endl
+ << "find (database&, const id_type&, object_type&);"
+ << endl;
+
+ // reload ()
+ //
+ os << "static bool" << endl
+ << "reload (database&, object_type&);"
+ << endl;
+
+ // update ()
+ //
+ if (!readonly (c) || poly)
+ {
+ os << "static void" << endl
+ << "update (database&, const object_type&);"
+ << endl;
+ }
+
+ // erase ()
+ //
+ os << "static void" << endl
+ << "erase (database&, const id_type&);"
+ << endl;
+
+ os << "static void" << endl
+ << "erase (database&, const object_type&);"
+ << endl;
+
+ // Sections.
+ //
+ if (uss.count (user_sections::count_total |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0)
+ os << "static bool" << endl
+ << "load (connection&, object_type&, section&);"
+ << endl;
+
+ if (uss.count (user_sections::count_total |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) != 0)
+ os << "static bool" << endl
+ << "update (connection&, const object_type&, const section&);"
+ << endl;
+ }
+
+ if (options.generate_query ())
+ {
+ if (!options.omit_unprepared ())
+ {
+ os << "static result<object_type>" << endl
+ << "query (database&, const query_base_type&);"
+ << endl;
+ }
+
+ os << "static unsigned long long" << endl
+ << "erase_query (database&, const query_base_type&);"
+ << endl;
+
+ if (options.generate_prepared ())
+ {
+ os << "static odb::details::shared_ptr<prepared_query_impl>" << endl
+ << "prepare_query (connection&, const char*, const query_base_type&);"
+ << endl;
+
+ os << "static odb::details::shared_ptr<result_impl>" << endl
+ << "execute_query (prepared_query_impl&);"
+ << endl;
+ }
+ }
+
+ os << "};"; // object_traits_impl
+}
+
+void header::class1::
+traverse_view (type& c)
+{
+ string const& type (class_fq_name (c));
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl;
+
+ // class_traits
+ //
+ os << "template <>" << endl
+ << "struct class_traits< " << type << " >"
+ << "{"
+ << "static const class_kind kind = class_view;"
+ << "};";
+
+ // view_traits
+ //
+ os << "template <>" << endl
+ << "class " << exp << "access::view_traits< " << type << " >"
+ << "{"
+ << "public:" << endl;
+
+ // view_type & pointer_type
+ //
+ os << "typedef " << type << " view_type;"
+ << "typedef " << c.get<string> ("object-pointer") << " pointer_type;"
+ << endl;
+
+ // Generate associated object tags here if we are generating dynamic
+ // multi-database support.
+ //
+ if (multi_dynamic)
+ {
+ query_tags t;
+ t.traverse (c);
+ }
+
+ // callback ()
+ //
+ os << "static void" << endl
+ << "callback (database&, view_type&, callback_event);"
+ << endl;
+
+ os << "};";
+
+ // The rest only applies to dynamic milti-database support.
+ //
+ if (!multi_dynamic)
+ return;
+
+ size_t obj_count (c.get<size_t> ("object-count"));
+
+ // view_traits_impl
+ //
+ os << "template <>" << endl
+ << "class " << exp << "access::view_traits_impl< " << type << ", " <<
+ "id_common >:" << endl
+ << " public access::view_traits< " << type << " >"
+ << "{"
+ << "public:" << endl;
+
+ // query_base_type and query_columns (definition generated by class2).
+ //
+ os << "typedef odb::query_base query_base_type;"
+ << "struct query_columns";
+
+ if (obj_count == 0)
+ os << "{"
+ << "};";
+ else
+ os << ";"
+ << endl;
+
+ // function_table_type
+ //
+ os << "struct function_table_type"
+ << "{";
+
+ if (!options.omit_unprepared ())
+ os << "result<view_type> (*query) (database&, const query_base_type&);"
+ << endl;
+
+ if (options.generate_prepared ())
+ {
+ os << "odb::details::shared_ptr<prepared_query_impl> " <<
+ "(*prepare_query) (connection&, const char*, const query_base_type&);"
+ << endl;
+
+ os << "odb::details::shared_ptr<result_impl> (*execute_query) ("
+ "prepared_query_impl&);"
+ << endl;
+ }
+
+ os << "};" // function_table_type
+ << "static const function_table_type* function_table[database_count];"
+ << endl;
+
+ //
+ // Forwarding functions.
+ //
+
+ if (!options.omit_unprepared ())
+ os << "static result<view_type>" << endl
+ << "query (database&, const query_base_type&);"
+ << endl;
+
+ if (options.generate_prepared ())
+ {
+ os << "static odb::details::shared_ptr<prepared_query_impl>" << endl
+ << "prepare_query (connection&, const char*, const query_base_type&);"
+ << endl;
+
+ os << "static odb::details::shared_ptr<result_impl>" << endl
+ << "execute_query (prepared_query_impl&);"
+ << endl;
+ }
+
+ os << "};";
+}
+
+namespace header
+{
+ struct class2: traversal::class_, virtual context
+ {
+ class2 ()
+ : typedefs_ (false),
+ query_columns_type_ (false, true, false),
+ query_columns_type_inst_ (false, false, true),
+ view_query_columns_type_ (true)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck == class_other ||
+ (!options.at_once () && class_file (c) != unit.file ()))
+ return;
+
+ names (c);
+
+ switch (ck)
+ {
+ case class_object: traverse_object (c); break;
+ case class_view: traverse_view (c); break;
+ default: break;
+ }
+ }
+
+ void
+ traverse_object (type&);
+
+ void
+ traverse_view (type&);
+
+ private:
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ instance<query_columns_type> query_columns_type_;
+ instance<query_columns_type> query_columns_type_inst_;
+ instance<view_query_columns_type> view_query_columns_type_;
+ };
+}
+
+void header::class2::
+traverse_object (type& c)
+{
+ if (options.generate_query ())
+ {
+ os << "// " << class_name (c) << endl
+ << "//" << endl;
+
+ // query_columns
+ //
+ // If we don't have any pointers, then query_columns is generated
+ // in pass 1 (see the comment in class1 for details).
+ //
+ if (has_a (c, test_pointer | include_base))
+ query_columns_type_->traverse (c);
+
+ // Generate extern template declarations.
+ //
+ query_columns_type_inst_->traverse (c);
+ }
+
+ // Move header comment out of if-block if adding any code here.
+}
+
+void header::class2::
+traverse_view (type& c)
+{
+ // query_columns
+ //
+ if (c.get<size_t> ("object-count") != 0)
+ {
+ os << "// " << class_name (c) << endl
+ << "//" << endl;
+
+ view_query_columns_type_->traverse (c);
+ }
+
+ // Move header comment out of if-block if adding any code here.
+}
+
+namespace header
+{
+ void
+ generate ()
+ {
+ context ctx;
+ ostream& os (ctx.os);
+
+ os << "#include <memory>" << endl
+ << "#include <cstddef>" << endl; // std::size_t
+
+ if (ctx.features.polymorphic_object)
+ os << "#include <string>" << endl; // For discriminator.
+
+ if (ctx.options.std () >= cxx_version::cxx11)
+ os << "#include <utility>" << endl; // move()
+
+ os << endl;
+
+ os << "#include <odb/core.hxx>" << endl
+ << "#include <odb/traits.hxx>" << endl
+ << "#include <odb/callback.hxx>" << endl
+ << "#include <odb/wrapper-traits.hxx>" << endl
+ << "#include <odb/pointer-traits.hxx>" << endl;
+
+ // @@ TMP: drop after 2.5.0.
+ //
+#if 0
+ if (ctx.options.std () == cxx_version::cxx98)
+ {
+ // In case of a boost TR1 implementation, we cannot distinguish
+ // between the boost::shared_ptr and std::tr1::shared_ptr usage since
+ // the latter is just a using-declaration for the former. To resolve
+ // this we will include TR1 traits if the Boost TR1 header is included.
+ //
+ if (ctx.features.tr1_pointer)
+ {
+ os << "#include <odb/tr1/wrapper-traits.hxx>" << endl
+ << "#include <odb/tr1/pointer-traits.hxx>" << endl;
+ }
+ else if (ctx.features.boost_pointer)
+ {
+ os << "#ifdef BOOST_TR1_MEMORY_HPP_INCLUDED" << endl
+ << "# include <odb/tr1/wrapper-traits.hxx>" << endl
+ << "# include <odb/tr1/pointer-traits.hxx>" << endl
+ << "#endif" << endl;
+ }
+ }
+#endif
+
+ os << "#include <odb/container-traits.hxx>" << endl;
+
+ if (ctx.features.session_object)
+ {
+ if (ctx.options.session_type () == "odb::session")
+ os << "#include <odb/session.hxx>" << endl;
+
+ os << "#include <odb/cache-traits.hxx>" << endl;
+ }
+ else
+ os << "#include <odb/no-op-cache-traits.hxx>" << endl;
+
+ if (ctx.features.polymorphic_object)
+ os << "#include <odb/polymorphic-info.hxx>" << endl;
+
+ if (ctx.options.generate_query ())
+ {
+ if (ctx.multi_dynamic)
+ os << "#include <odb/query-dynamic.hxx>" << endl;
+
+ if (ctx.options.generate_prepared ())
+ os << "#include <odb/prepared-query.hxx>" << endl;
+
+ os << "#include <odb/result.hxx>" << endl;
+
+ if (ctx.features.simple_object)
+ os << "#include <odb/simple-object-result.hxx>" << endl;
+
+ if (ctx.features.polymorphic_object)
+ os << "#include <odb/polymorphic-object-result.hxx>" << endl;
+
+ if (ctx.features.no_id_object)
+ os << "#include <odb/no-id-object-result.hxx>" << endl;
+
+ if (ctx.features.view)
+ os << "#include <odb/view-image.hxx>" << endl
+ << "#include <odb/view-result.hxx>" << endl;
+ }
+
+ os << endl
+ << "#include <odb/details/unused.hxx>" << endl;
+
+ if (ctx.options.generate_query ())
+ os << "#include <odb/details/shared-ptr.hxx>" << endl;
+
+ os << endl;
+
+ os << "namespace odb"
+ << "{";
+
+ // Generate common code.
+ //
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (false);
+ traversal::namespace_ ns;
+ class1 c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (false);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (ctx.unit);
+ }
+
+ if (ctx.multi_dynamic)
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (false);
+ traversal::namespace_ ns;
+ class2 c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (false);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (ctx.unit);
+ }
+
+ os << "}";
+ }
+}
diff --git a/odb/odb/include.cxx b/odb/odb/include.cxx
new file mode 100644
index 0000000..5fda7c0
--- /dev/null
+++ b/odb/odb/include.cxx
@@ -0,0 +1,738 @@
+// file : odb/include.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <set>
+#include <map>
+#include <locale>
+#include <cassert>
+#include <fstream>
+#include <sstream>
+
+#include <odb/common.hxx>
+#include <odb/context.hxx>
+#include <odb/generate.hxx>
+
+#include <iostream>
+
+using namespace std;
+using semantics::path;
+
+namespace
+{
+ struct include_directive
+ {
+ enum type { quote, bracket };
+
+ type type_;
+ path path_;
+ };
+
+#if BUILDING_GCC_MAJOR >= 6
+ typedef line_map_ordinary line_map_type;
+#else
+ typedef line_map line_map_type;
+# ifndef linemap_check_ordinary
+# define linemap_check_ordinary(X) (X)
+# endif
+#endif
+
+ struct includes
+ {
+ typedef std::map<line_map_type const*, include_directive> map_type;
+ bool trailing; // Included at the beginning or at the end of the main file.
+ map_type map;
+ };
+ typedef std::map<path, includes> include_map;
+
+ // Map of files to the lines which contain include directives
+ // that we are interested in.
+ //
+ typedef std::map<size_t, include_directive*> include_lines;
+ typedef std::map<string, include_lines> file_map;
+
+ // Set of include directives sorted in the preference order.
+ //
+ struct include_comparator
+ {
+ bool
+ operator() (include_directive const* x, include_directive const* y) const
+ {
+ // Prefer <> over "".
+ //
+ if (x->type_ != y->type_)
+ return x->type_ < y->type_;
+
+ // Otherwise, prefer longer (more qualified) paths over the
+ // shorter ones.
+ //
+ return x->path_.string ().size () < y->path_.string ().size ();
+ }
+ };
+
+ typedef
+ std::multiset<include_directive const*, include_comparator>
+ include_set;
+
+ struct class_: traversal::class_, context
+ {
+ class_ (include_map& map)
+ : typedefs_ (true), main_file_loc_ (0), map_ (map)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck == class_other)
+ return;
+
+ names (c); // Check nested classes.
+
+ // We only generate things for objects and composite value types. In
+ // particular, we don't care about views since they cannot be used in
+ // definitions of other views, objects, or composite values.
+ //
+ if (ck != class_object && ck != class_composite)
+ return;
+
+ // Not interested in classes that we are generating.
+ //
+ // If we have an explicit definition location, use that. Otherwise,
+ // if this is a class template instantiation, then get the file
+ // corresponding to the pragma, not the instantiation itself,
+ // since that's where we are generation the code for this class.
+ // While at it, also get the location.
+ //
+ using semantics::path;
+
+ path f;
+ location_t l;
+
+ // Pretty much the same code as in context::class_location().
+ //
+ if (c.count ("definition"))
+ {
+ l = c.get<location_t> ("definition");
+ f = path (LOCATION_FILE (l));
+ }
+ else if (c.is_a<semantics::class_instantiation> ())
+ {
+ l = c.get<location_t> ("location");
+ f = path (LOCATION_FILE (l));
+ }
+ else
+ {
+ f = c.file ();
+ tree decl (TYPE_NAME (c.tree_node ()));
+ l = real_source_location (decl);
+
+ // Any include directives that follow are trailing (specified at
+ // the end of the main file). Note that we ignore views in this
+ // test so if a file defines only views, then all includes will
+ // be treated as leading. This is ok since views cannot have
+ // circular dependencies. We also ignore overridden locations for
+ // the purpose of this test since they are not really in the file
+ // being compiled. We assume that any includes that come after
+ // such classes are still leading.
+ //
+ if (f == unit.file ())
+ {
+ if (main_file_loc_ == 0)
+ main_file_loc_ = l;
+ return;
+ }
+ }
+
+ // This is a persistent object or composite value type declared in
+ // another header file. Include its -odb header.
+ //
+ if (l > BUILTINS_LOCATION)
+ {
+ line_map_type const* lm (
+ linemap_check_ordinary (
+ linemap_lookup (line_table, l)));
+
+ if (lm != 0 && !MAIN_FILE_P (lm))
+ {
+ lm = INCLUDED_FROM (line_table, lm);
+
+ f.complete ();
+ f.normalize ();
+
+ if (map_.find (f) == map_.end ())
+ {
+ includes& i (map_[f]);
+ i.trailing = (main_file_loc_ != 0 && l > main_file_loc_);
+ i.map[lm] = include_directive ();
+ }
+ }
+ }
+ }
+
+ private:
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ location_t main_file_loc_;
+ include_map& map_;
+ };
+
+ class include_parser
+ {
+ public:
+ include_parser (options const& options)
+ : loc_ ("C"), options_ (options)
+ {
+ }
+
+ void
+ parse_file (string const& file, include_lines& lines)
+ {
+ string f (file);
+ size_t n (f.size ());
+ database db (options_.database ()[0]);
+
+ // Check if we have a synthesized prologue/epilogue fragment.
+ //
+ if (n != 0 && f[0] == '<' && f[n - 1] == '>')
+ {
+ size_t p (f.rfind ('-'));
+
+ if (p != string::npos)
+ {
+ string name (f, 1, p - 1);
+
+ if (name == "odb-prologue" || name == "odb-epilogue")
+ {
+ // Extract the fragment number.
+ //
+ {
+ istringstream istr (string (f, p + 1));
+ istr >> n;
+ }
+
+ n--; // Prologues/epilogues are counted from 1.
+
+ stringstream ss;
+ f.clear ();
+
+ // We don't need the #line part.
+ //
+ if (name == "odb-prologue")
+ {
+ size_t size (options_.odb_prologue ().size ());
+
+ if (n < size)
+ ss << options_.odb_prologue ()[db][n];
+ else
+ f = options_.odb_prologue_file ()[db][n - size];
+ }
+ else
+ {
+ size_t size (options_.odb_epilogue ().size ());
+
+ if (n < size)
+ ss << options_.odb_epilogue ()[db][n];
+ else
+ f = options_.odb_epilogue_file ()[db][n - size];
+ }
+
+ if (f.empty ())
+ {
+ parse_stream (ss, file, lines);
+ return;
+ }
+ // Otherwise use the code below to parse the file.
+ }
+ }
+ }
+
+ ifstream is (f.c_str ());
+
+ if (!is.is_open ())
+ {
+ cerr << "error: unable to open '" << f << "' in read mode" << endl;
+ throw operation_failed ();
+ }
+
+ parse_stream (is, f, lines);
+ }
+
+ void
+ parse_stream (istream& is, string const& name, include_lines& lines)
+ {
+ typedef char_traits<char>::int_type int_type;
+
+ size_t lmax (lines.rbegin ()->first);
+
+ string line;
+ bool bslash (false);
+ size_t lb (1), le (1);
+ bool eof (false);
+
+ for (int_type c (is.get ()); !eof; c = is.get ())
+ {
+ if (is.fail ())
+ {
+ if (is.eof ())
+ {
+ // If we are still in the range, treat this as the last newline.
+ //
+ c = '\n';
+ eof = true;
+ }
+ else
+ break; // Some other failure -- bail out.
+ }
+
+ if (c == '\n')
+ {
+ le++;
+
+ if (!bslash)
+ {
+ //cerr << "line: " << lb << "-" << (le - 1) << " " << line << endl;
+
+ // See if we are interested in this range of physical lines.
+ //
+ include_lines::iterator li (lines.lower_bound (lb));
+ include_lines::iterator ui (lines.upper_bound (le - 1));
+
+ // We should have at most one entry per logical line.
+ //
+ for (; li != ui; ++li)
+ {
+ if (li->first >= lb && li->first <= (le - 1))
+ {
+ if (!parse_line (line, *li->second))
+ {
+ cerr << name << ":" << lb << ":1: error: "
+ << "unable to parse #include directive" << endl;
+ throw operation_failed ();
+ }
+ }
+ }
+
+ if (le > lmax)
+ break;
+
+ lb = le;
+ line.clear ();
+ }
+
+ bslash = false;
+ continue;
+ }
+
+ if (bslash)
+ {
+ line += '\\';
+ bslash = false;
+ }
+
+ if (c == '\\')
+ bslash = true;
+ else
+ {
+ line += char (c);
+ }
+ }
+
+ if (is.bad () || (is.fail () && !is.eof ()))
+ {
+ cerr << "error: input error while reading '" << name << "'" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ private:
+ bool
+ parse_line (string const& l, include_directive& inc)
+ {
+ enum state
+ {
+ start_hash,
+ start_keyword,
+ parse_keyword,
+ start_path,
+ parse_path,
+ parse_done
+ };
+
+ bool com (false); // In C-style comment.
+ string lex;
+ char path_end ('\0');
+ state s (start_hash);
+
+ for (size_t i (0), n (l.size ()); i < n; ++i)
+ {
+ char c (l[i]);
+
+ if (com)
+ {
+ if (c == '*' && (i + 1) < n && l[i + 1] == '/')
+ {
+ ++i;
+ com = false;
+ c = ' '; // Replace a comment with a single space.
+ }
+ else
+ continue;
+ }
+
+ // We only ignore spaces in start states.
+ //
+ if (is_space (c))
+ {
+ switch (s)
+ {
+ case start_hash:
+ case start_keyword:
+ case start_path:
+ {
+ continue;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // C comment can be anywhere except in the path.
+ //
+ if (s != parse_path && c == '/' && (i + 1) < n && l[i + 1] == '*')
+ {
+ ++i;
+ com = true;
+ continue;
+ }
+
+ switch (s)
+ {
+ case start_hash:
+ {
+ if (c != '#')
+ return false;
+
+ s = start_keyword;
+ break;
+ }
+ case start_keyword:
+ {
+ lex.clear ();
+ s = parse_keyword;
+ }
+ // Fall through.
+ case parse_keyword:
+ {
+ if (is_alpha (c))
+ {
+ lex += c;
+ break;
+ }
+
+ if (lex != "include")
+ return false;
+
+ s = start_path;
+ --i; // Re-parse the same character again.
+ break;
+ }
+ case start_path:
+ {
+ if (c == '"')
+ {
+ path_end = '"';
+ inc.type_ = include_directive::quote;
+ }
+ else if (c == '<')
+ {
+ path_end = '>';
+ inc.type_ = include_directive::bracket;
+ }
+ else
+ return false;
+
+ lex.clear ();
+ s = parse_path;
+ break;
+ }
+ case parse_path:
+ {
+ if (c != path_end)
+ lex += c;
+ else
+ s = parse_done;
+
+ break;
+ }
+ default:
+ {
+ assert (false);
+ break;
+ }
+ }
+
+ if (s == parse_done)
+ break;
+ }
+
+ if (s != parse_done)
+ return false;
+
+ inc.path_ = path (lex);
+ return true;
+ }
+
+ private:
+ bool
+ is_alpha (char c) const
+ {
+ return isalpha (c, loc_);
+ }
+
+ bool
+ is_space (char c) const
+ {
+ return isspace (c, loc_);
+ }
+
+ private:
+ std::locale loc_;
+ options const& options_;
+ };
+
+ bool
+ generate_impl (bool header)
+ {
+ bool r (false);
+
+ // We do the same include directive collection and processing
+ // twice, once for the header file and once for the source file.
+ // If that proves to be too slow, we will need to do it only once
+ // and cache the result.
+ //
+ context ctx;
+ include_map imap;
+
+ // Collect all the files that we need to include.
+ //
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (true);
+ traversal::namespace_ ns;
+ class_ c (imap);
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (true);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (ctx.unit);
+ }
+
+ // Add all the known include locations for each file in the map.
+ //
+#if BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR <= 6
+ size_t used (line_table->used);
+ line_map_type const* maps (line_table->maps);
+#else
+ size_t used (line_table->info_ordinary.used);
+ line_map_type const* maps (line_table->info_ordinary.maps);
+#endif
+
+ for (size_t i (0); i < used; ++i)
+ {
+ line_map_type const* m (maps + i);
+
+ if (MAIN_FILE_P (m) || m->reason != LC_ENTER)
+ continue;
+
+ line_map_type const* ifm (INCLUDED_FROM (line_table, m));
+
+#if BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR <= 6
+ path f (m->to_file);
+#else
+ path f (ORDINARY_MAP_FILE_NAME (m));
+#endif
+
+ f.complete ();
+ f.normalize ();
+
+ include_map::iterator it (imap.find (f));
+
+ if (it != imap.end ())
+ it->second.map[ifm] = include_directive ();
+ }
+
+ //
+ //
+ file_map fmap;
+
+ for (include_map::iterator i (imap.begin ()), e (imap.end ()); i != e; ++i)
+ {
+ // Note that the LAST_SOURCE_LINE value of a map that includes another
+ // map is the line of that include.
+
+ /*
+ cerr << endl
+ << i->first << " included from" << endl;
+
+ for (includes::iterator j (i->second.begin ());
+ j != i->second.end (); ++j)
+ {
+ line_map_type const* lm (j->first);
+ cerr << '\t' << lm->to_file << ":" << LAST_SOURCE_LINE (lm) << endl;
+ }
+ */
+
+ // First see if there is an include from the main file. If so, then
+ // it is preferred over all others. Use the first one if there are
+ // several.
+ //
+ line_map_type const* main_lm (0);
+ include_directive* main_inc (0);
+
+ for (includes::map_type::iterator j (i->second.map.begin ());
+ j != i->second.map.end (); ++j)
+ {
+ line_map_type const* lm (j->first);
+
+ if (MAIN_FILE_P (lm))
+ {
+ if (main_lm == 0 ||
+ LAST_SOURCE_LINE (main_lm) > LAST_SOURCE_LINE (lm))
+ {
+ main_lm = lm;
+ main_inc = &j->second;
+ }
+ }
+ }
+
+ if (main_lm != 0)
+ {
+#if BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR <= 6
+ string f (main_lm->to_file);
+#else
+ string f (ORDINARY_MAP_FILE_NAME (main_lm));
+#endif
+ size_t n (f.size ());
+
+ // Check if this is a synthesized fragment.
+ //
+ if (!(n != 0 && f[0] == '<' && f[n - 1] == '>'))
+ {
+ path p (f);
+ p.complete ();
+ p.normalize ();
+ f = p.string ();
+ }
+
+ fmap[f][LAST_SOURCE_LINE (main_lm)] = main_inc;
+ continue;
+ }
+
+ // Otherwise, add all the entries.
+ //
+ for (includes::map_type::iterator j (i->second.map.begin ());
+ j != i->second.map.end (); ++j)
+ {
+ line_map_type const* lm (j->first);
+
+#if BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR <= 6
+ string f (lm->to_file);
+#else
+ string f (ORDINARY_MAP_FILE_NAME (lm));
+#endif
+ size_t n (f.size ());
+
+ // Check if this is a synthesized fragment.
+ //
+ if (!(n != 0 && f[0] == '<' && f[n - 1] == '>'))
+ {
+ path p (f);
+ p.complete ();
+ p.normalize ();
+ f = p.string ();
+ }
+
+ fmap[f][LAST_SOURCE_LINE (lm)] = &j->second;
+ }
+ }
+
+ // Parse the collected include directives.
+ //
+ include_parser ip (ctx.options);
+
+ for (file_map::iterator i (fmap.begin ()), e (fmap.end ()); i != e; ++i)
+ {
+ ip.parse_file (i->first, i->second);
+ }
+
+ // Finally, output the include directives.
+ //
+ for (include_map::const_iterator i (imap.begin ()), e (imap.end ());
+ i != e; ++i)
+ {
+ includes const& is (i->second);
+
+ // In header we generate only leading includes. In source -- only
+ // trailing.
+ //
+ if (header == is.trailing)
+ continue;
+
+ include_directive const* inc (0);
+
+ if (is.map.size () == 1)
+ {
+ inc = &is.map.begin ()->second;
+ }
+ else
+ {
+ include_set set;
+
+ for (includes::map_type::const_iterator j (i->second.map.begin ());
+ j != i->second.map.end (); ++j)
+ {
+ if (!j->second.path_.empty ())
+ set.insert (&j->second);
+ }
+
+ assert (set.size () > 0);
+ inc = *set.rbegin ();
+ }
+
+ path f (inc->path_.base ());
+ f += ctx.options.odb_file_suffix ()[ctx.options.database ()[0]];
+ f += ctx.options.hxx_suffix ();
+
+ char o (inc->type_ == include_directive::quote ? '"' : '<');
+ ctx.os << "#include " << ctx.process_include_path (
+ f.string (), false, o) << endl;
+ r = true;
+ }
+
+ return r;
+ }
+}
+
+namespace include
+{
+ bool
+ generate (bool header) {return generate_impl (header);}
+}
diff --git a/odb/odb/inline.cxx b/odb/odb/inline.cxx
new file mode 100644
index 0000000..15482aa
--- /dev/null
+++ b/odb/odb/inline.cxx
@@ -0,0 +1,506 @@
+// file : odb/inline.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/common.hxx>
+#include <odb/context.hxx>
+#include <odb/generate.hxx>
+#include <odb/diagnostics.hxx>
+
+using namespace std;
+
+namespace inline_
+{
+ //
+ //
+ struct callback_calls: traversal::class_, virtual context
+ {
+ callback_calls ()
+ {
+ *this >> inherits_ >> *this;
+ }
+
+ void
+ traverse (type& c, bool constant)
+ {
+ const_ = constant;
+ traverse (c);
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ bool obj (object (c));
+
+ // Ignore transient bases.
+ //
+ if (!(obj || view (c)))
+ return;
+
+ if (c.count ("callback"))
+ {
+ string name (c.get<string> ("callback"));
+
+ // In case of the const instance, we only generate the call if
+ // there is a const callback. Note also that we cannot use
+ // object_type/view_type alias because it can be a base type.
+ //
+ string const& type (class_fq_name (c));
+
+ if (const_)
+ {
+ if (c.count ("callback-const"))
+ os << "static_cast<const " << type << "&> (x)." << name <<
+ " (e, db);";
+ }
+ else
+ os << "static_cast< " << type << "&> (x)." << name << " (e, db);";
+ }
+ else if (obj)
+ inherits (c);
+ }
+
+ protected:
+ bool const_;
+ traversal::inherits inherits_;
+ };
+
+ struct class_: traversal::class_, virtual context
+ {
+ class_ ()
+ : typedefs_ (false)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck == class_other ||
+ (!options.at_once () && class_file (c) != unit.file ()))
+ return;
+
+ names (c);
+
+ switch (ck)
+ {
+ case class_object: traverse_object (c); break;
+ case class_view: traverse_view (c); break;
+ default: break;
+ }
+ }
+
+ void
+ traverse_object (type&);
+
+ void
+ traverse_view (type&);
+
+ private:
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ callback_calls callback_calls_;
+ };
+}
+
+void inline_::class_::
+traverse_object (type& c)
+{
+ using semantics::data_member;
+
+ data_member_path* id (id_member (c));
+ data_member* idf (id ? id->front () : 0);
+ bool auto_id (id && auto_ (*id));
+ bool base_id (id && &idf->scope () != &c); // Comes from base.
+
+ data_member* opt (context::optimistic (c));
+
+ // Base class that contains the object id.
+ //
+ type* base (base_id ? dynamic_cast<type*> (&idf->scope ()) : 0);
+
+ bool poly (polymorphic (c));
+ bool abst (abstract (c));
+ bool reuse_abst (abst && !poly);
+
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ string const& type (class_fq_name (c));
+ string traits ("access::object_traits< " + type + " >");
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl
+ << endl;
+
+ // id (object_type)
+ //
+ if (id != 0 || !reuse_abst)
+ {
+ os << "inline" << endl
+ << traits << "::id_type" << endl
+ << traits << "::" << endl
+ << "id (const object_type&" << (id != 0 ? " o" : "") << ")"
+ << "{";
+
+ if (id != 0)
+ {
+ if (base_id)
+ os << "return object_traits< " << class_fq_name (*base) <<
+ " >::id (o);";
+ else
+ {
+ // Get the id using the accessor expressions.
+ //
+ string r ("o");
+
+ for (data_member_path::const_iterator b (id->begin ()), i (b);
+ i != id->end ();
+ ++i)
+ {
+ member_access& ma ((*i)->get<member_access> ("get"));
+
+ // If this is not a synthesized expression, then output its
+ // location for easier error tracking.
+ //
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ r = ma.translate (r);
+ }
+
+ os << "return " << r << ";";
+ }
+ }
+
+ os << "}";
+ }
+
+ if (opt != 0)
+ {
+ os << "inline" << endl
+ << traits << "::version_type" << endl
+ << traits << "::" << endl
+ << "version (const object_type& o)"
+ << "{";
+
+ if (base_id)
+ os << "return object_traits< " << class_fq_name (*base) <<
+ " >::version (o);";
+ else
+ {
+ // Get the id using the accessor expression. If this is not
+ // a synthesized expression, then output its location for
+ // easier error tracking.
+ //
+ member_access& ma (opt->get<member_access> ("get"));
+
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << "return " << ma.translate ("o") << ";";
+ }
+
+ os << "}";
+ }
+
+ // The rest does not apply to reuse-abstract objects.
+ //
+ if (reuse_abst)
+ return;
+
+ // callback ()
+ //
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "callback (database& db, object_type& x, callback_event e)"
+ << endl
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << "ODB_POTENTIALLY_UNUSED (x);"
+ << "ODB_POTENTIALLY_UNUSED (e);"
+ << endl;
+ callback_calls_.traverse (c, false);
+ os << "}";
+
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "callback (database& db, const object_type& x, callback_event e)"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << "ODB_POTENTIALLY_UNUSED (x);"
+ << "ODB_POTENTIALLY_UNUSED (e);"
+ << endl;
+ callback_calls_.traverse (c, true);
+ os << "}";
+
+ // The rest only applies to dynamic milti-database support.
+ //
+ if (!multi_dynamic)
+ return;
+
+ traits = "access::object_traits_impl< " + type + ", id_common >";
+
+ //
+ // Forwarding functions.
+ //
+
+ // persist ()
+ //
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "persist (database& db, " << (auto_id ? "" : "const ") <<
+ "object_type& o)"
+ << "{"
+ << "function_table[db.id ()]->persist (db, o" <<
+ (poly ? ", true, true" : "") << ");"
+ << "}";
+
+ if (id != 0)
+ {
+ // find (id)
+ //
+ if (c.default_ctor ())
+ {
+ os << "inline" << endl
+ << traits << "::pointer_type" << endl
+ << traits << "::" << endl
+ << "find (database& db, const id_type& id)"
+ << "{"
+ << "return function_table[db.id ()]->find1 (db, id);"
+ << "}";
+ }
+
+ // find (id, obj)
+ //
+ os << "inline" << endl
+ << "bool " << traits << "::" << endl
+ << "find (database& db, const id_type& id, object_type& o)"
+ << "{"
+ << "return function_table[db.id ()]->find2 (db, id, o" <<
+ (poly ? ", true" : "") << ");"
+ << "}";
+
+ // reload ()
+ //
+ os << "inline" << endl
+ << "bool " << traits << "::" << endl
+ << "reload (database& db, object_type& o)"
+ << "{"
+ << "return function_table[db.id ()]->reload (db, o" <<
+ (poly ? ", true" : "") << ");"
+ << "}";
+
+ // update ()
+ //
+ // In case of a polymorphic object, we generate update() even if it is
+ // readonly since the potentially-readwrite base will rely on it to
+ // initialize the id image.
+ //
+ //
+ if (!readonly (c) || poly)
+ {
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "update (database& db, const object_type& o)"
+ << "{"
+ << "function_table[db.id ()]->update (db, o" <<
+ (poly ? ", true, true" : "") << ");"
+ << "}";
+ }
+
+ // erase ()
+ //
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "erase (database& db, const id_type& id)"
+ << "{"
+ << "function_table[db.id ()]->erase1 (db, id" <<
+ (poly ? ", true, true" : "") << ");"
+ << "}";
+
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "erase (database& db, const object_type& o)"
+ << "{"
+ << "function_table[db.id ()]->erase2 (db, o" <<
+ (poly ? ", true, true" : "") << ");"
+ << "}";
+
+ // Sections.
+ //
+ if (uss.count (user_sections::count_total |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0)
+ os << "inline" << endl
+ << "bool " << traits << "::" << endl
+ << "load (connection& c, object_type& o, section& s)"
+ << "{"
+ << "return function_table[c.database ().id ()]->load_section (" <<
+ "c, o, s" << (poly ? ", 0" : "") << ");"
+ << "}";
+
+ if (uss.count (user_sections::count_total |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) != 0)
+ os << "inline" << endl
+ << "bool " << traits << "::" << endl
+ << "update (connection& c, const object_type& o, const section& s)"
+ << "{"
+ << "return function_table[c.database ().id ()]->update_section (" <<
+ "c, o, s" << (poly ? ", 0" : "") << ");"
+ << "}";
+ }
+
+ if (options.generate_query ())
+ {
+ if (!options.omit_unprepared ())
+ {
+ os << "inline" << endl
+ << "result< " << traits << "::object_type >" << endl
+ << traits << "::" << endl
+ << "query (database& db, const query_base_type& q)"
+ << "{"
+ << "return function_table[db.id ()]->query (db, q);"
+ << "}";
+ }
+
+ os << "inline" << endl
+ << "unsigned long long " << traits << "::" << endl
+ << "erase_query (database& db, const query_base_type& q)"
+ << "{"
+ << "return function_table[db.id ()]->erase_query (db, q);"
+ << "}";
+
+ if (options.generate_prepared ())
+ {
+ os << "inline" << endl
+ << "odb::details::shared_ptr<prepared_query_impl>" << endl
+ << traits << "::" << endl
+ << "prepare_query (connection& c, const char* n, " <<
+ "const query_base_type& q)"
+ << "{"
+ << "return function_table[c.database ().id ()]->prepare_query (" <<
+ "c, n, q);"
+ << "}";
+
+ os << "inline" << endl
+ << "odb::details::shared_ptr<result_impl>" << endl
+ << traits << "::" << endl
+ << "execute_query (prepared_query_impl& pq)"
+ << "{"
+ << "return function_table[pq.conn.database ().id ()]->" <<
+ "execute_query (pq);"
+ << "}";
+ }
+ }
+}
+
+void inline_::class_::
+traverse_view (type& c)
+{
+ string const& type (class_fq_name (c));
+ string traits ("access::view_traits< " + type + " >");
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl
+ << endl;
+
+ // callback ()
+ //
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "callback (database& db, view_type& x, callback_event e)"
+ << endl
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << "ODB_POTENTIALLY_UNUSED (x);"
+ << "ODB_POTENTIALLY_UNUSED (e);"
+ << endl;
+ callback_calls_.traverse (c, false);
+ os << "}";
+
+ // The rest only applies to dynamic milti-database support.
+ //
+ if (!multi_dynamic)
+ return;
+
+ traits = "access::view_traits_impl< " + type + ", id_common >";
+
+ //
+ // Forwarding functions.
+ //
+
+ if (!options.omit_unprepared ())
+ {
+ os << "inline" << endl
+ << "result< " << traits << "::view_type >" << endl
+ << traits << "::" << endl
+ << "query (database& db, const query_base_type& q)"
+ << "{"
+ << "return function_table[db.id ()]->query (db, q);"
+ << "}";
+ }
+
+ if (options.generate_prepared ())
+ {
+ os << "inline" << endl
+ << "odb::details::shared_ptr<prepared_query_impl>" << endl
+ << traits << "::" << endl
+ << "prepare_query (connection& c, const char* n, " <<
+ "const query_base_type& q)"
+ << "{"
+ << "return function_table[c.database ().id ()]->prepare_query (" <<
+ "c, n, q);"
+ << "}";
+
+ os << "inline" << endl
+ << "odb::details::shared_ptr<result_impl>" << endl
+ << traits << "::" << endl
+ << "execute_query (prepared_query_impl& pq)"
+ << "{"
+ << "return function_table[pq.conn.database ().id ()]->" <<
+ "execute_query (pq);"
+ << "}";
+ }
+}
+
+namespace inline_
+{
+ void
+ generate ()
+ {
+ context ctx;
+ ostream& os (ctx.os);
+
+ if (ctx.multi_dynamic)
+ os << "#include <odb/database.hxx>" << endl
+ << endl;
+
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (false);
+ traversal::namespace_ ns;
+ class_ c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (false);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ os << "namespace odb"
+ << "{";
+
+ unit.dispatch (ctx.unit);
+
+ os << "}";
+ }
+}
diff --git a/odb/odb/instance.cxx b/odb/odb/instance.cxx
new file mode 100644
index 0000000..2d10239
--- /dev/null
+++ b/odb/odb/instance.cxx
@@ -0,0 +1,73 @@
+// file : odb/instance.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <sstream>
+#include <cstdlib> // abort
+#include <cxxabi.h> // abi::__cxa_demangle
+
+#include <odb/instance.hxx>
+
+using namespace std;
+
+struct demangled_name
+{
+ demangled_name (): s (0), n (0) {}
+ ~demangled_name () {free (s);}
+ char* s;
+ size_t n;
+};
+
+static demangled_name name_;
+
+string entry_base::
+name (type_info const& ti)
+{
+ char*& s (name_.s);
+
+ {
+ int r;
+ s = abi::__cxa_demangle (ti.name (), s, &name_.n, &r);
+
+ if (r != 0)
+ abort (); // We are in static initialization, so this is fatal.
+ }
+
+ string str (s), r;
+
+ // Get the first component. It can be a database kind or name.
+ //
+ string::size_type p (str.find ("::"));
+
+ if (p == string::npos)
+ abort (); // Derived type should be in a namespace.
+
+ string n (str, 0, p);
+
+ // See if it is one of the known kinds.
+ //
+ if (n == "relational")
+ {
+ r = n;
+ p = str.find ("::", 12); // 12 for "relational::"
+ n.assign (str, 12, p == string::npos ? p : p - 12);
+ }
+
+ // See if it is one of the known databases.
+ //
+ database db;
+ istringstream is (n);
+ if (!(is >> db))
+ {
+ if (r.empty ())
+ abort (); // Has to have either kind or database.
+ }
+ else
+ {
+ if (!r.empty ())
+ r += "::";
+
+ r += n;
+ }
+
+ return r;
+}
diff --git a/odb/odb/instance.hxx b/odb/odb/instance.hxx
new file mode 100644
index 0000000..2b47939
--- /dev/null
+++ b/odb/odb/instance.hxx
@@ -0,0 +1,295 @@
+// file : odb/instance.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_INSTANCE_HXX
+#define ODB_INSTANCE_HXX
+
+#include <map>
+#include <string>
+#include <cstddef> // std::size_t
+#include <typeinfo>
+
+#include <odb/option-types.hxx>
+#include <odb/context.hxx>
+
+#include <odb/traversal/elements.hxx>
+#include <odb/traversal/relational/elements.hxx>
+
+//
+// Dynamic traversal instantiation support.
+//
+
+template <typename B>
+struct factory
+{
+ static B*
+ create (B const& prototype)
+ {
+ std::string kind, name;
+ database db (context::current ().options.database ()[0]);
+
+ switch (db)
+ {
+ case database::common:
+ {
+ name = "common";
+ break;
+ }
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ kind = "relational";
+ name = kind + "::" + db.string ();
+ break;
+ }
+ }
+
+ if (map_ != 0)
+ {
+ typename map::const_iterator i;
+
+ if (!name.empty ())
+ i = map_->find (name);
+
+ if (i == map_->end ())
+ i = map_->find (kind);
+
+ if (i != map_->end ())
+ return i->second (prototype);
+ }
+
+ return new B (prototype);
+ }
+
+private:
+ template <typename>
+ friend struct entry;
+
+ static void
+ init ()
+ {
+ if (factory<B>::count_++ == 0)
+ factory<B>::map_ = new typename factory<B>::map;
+ }
+
+ static void
+ term ()
+ {
+ if (--factory<B>::count_ == 0)
+ delete factory<B>::map_;
+ }
+
+ typedef B* (*create_func) (B const&);
+ typedef std::map<std::string, create_func> map;
+ static map* map_;
+ static std::size_t count_;
+};
+
+template <typename B>
+typename factory<B>::map* factory<B>::map_;
+
+template <typename B>
+std::size_t factory<B>::count_;
+
+struct entry_base
+{
+ static std::string
+ name (std::type_info const&);
+};
+
+template <typename D>
+struct entry: entry_base
+{
+ typedef typename D::base base;
+
+ entry ()
+ {
+ factory<base>::init ();
+ (*factory<base>::map_)[name (typeid (D))] = &create;
+ }
+
+ ~entry ()
+ {
+ factory<base>::term ();
+ }
+
+ static base*
+ create (base const& prototype)
+ {
+ return new D (prototype);
+ }
+};
+
+template <typename B>
+struct instance
+{
+ typedef typename B::base base_type;
+ typedef ::factory<base_type> factory_type;
+
+ ~instance ()
+ {
+ delete x_;
+ }
+
+ instance ()
+ {
+ base_type prototype;
+ x_ = factory_type::create (prototype);
+ }
+
+ template <typename A1>
+ instance (A1& a1)
+ {
+ base_type prototype (a1);
+ x_ = factory_type::create (prototype);
+ }
+
+ template <typename A1>
+ instance (A1 const& a1)
+ {
+ base_type prototype (a1);
+ x_ = factory_type::create (prototype);
+ }
+
+ template <typename A1, typename A2>
+ instance (A1& a1, A2& a2)
+ {
+ base_type prototype (a1, a2);
+ x_ = factory_type::create (prototype);
+ }
+
+ template <typename A1, typename A2>
+ instance (A1 const& a1, A2 const& a2)
+ {
+ base_type prototype (a1, a2);
+ x_ = factory_type::create (prototype);
+ }
+
+ template <typename A1, typename A2, typename A3>
+ instance (A1& a1, A2& a2, A3& a3)
+ {
+ base_type prototype (a1, a2, a3);
+ x_ = factory_type::create (prototype);
+ }
+
+ template <typename A1, typename A2, typename A3>
+ instance (A1 const& a1, A2 const& a2, A3 const& a3)
+ {
+ base_type prototype (a1, a2, a3);
+ x_ = factory_type::create (prototype);
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4>
+ instance (A1& a1, A2& a2, A3& a3, A4& a4)
+ {
+ base_type prototype (a1, a2, a3, a4);
+ x_ = factory_type::create (prototype);
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4>
+ instance (A1 const& a1, A2 const& a2, A3 const& a3, A4 const& a4)
+ {
+ base_type prototype (a1, a2, a3, a4);
+ x_ = factory_type::create (prototype);
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4, typename A5>
+ instance (A1& a1, A2& a2, A3& a3, A4& a4, A5& a5)
+ {
+ base_type prototype (a1, a2, a3, a4, a5);
+ x_ = factory_type::create (prototype);
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4, typename A5>
+ instance (A1 const& a1, A2 const& a2, A3 const& a3, A4 const& a4,
+ A5 const& a5)
+ {
+ base_type prototype (a1, a2, a3, a4, a5);
+ x_ = factory_type::create (prototype);
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6>
+ instance (A1& a1, A2& a2, A3& a3, A4& a4, A5& a5, A6 a6)
+ {
+ base_type prototype (a1, a2, a3, a4, a5, a6);
+ x_ = factory_type::create (prototype);
+ }
+
+ template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6>
+ instance (A1 const& a1, A2 const& a2, A3 const& a3, A4 const& a4,
+ A5 const& a5, A6 const& a6)
+ {
+ base_type prototype (a1, a2, a3, a4, a5, a6);
+ x_ = factory_type::create (prototype);
+ }
+
+ instance (instance const& i)
+ {
+ // This is tricky: use the other instance as a prototype.
+ //
+ x_ = factory_type::create (*i.x_);
+ }
+
+ base_type*
+ operator-> () const
+ {
+ return x_;
+ }
+
+ base_type&
+ operator* () const
+ {
+ return *x_;
+ }
+
+ base_type*
+ get () const
+ {
+ return x_;
+ }
+
+private:
+ instance& operator= (instance const&);
+
+private:
+ base_type* x_;
+};
+
+template <typename T>
+inline traversal::edge_base&
+operator>> (instance<T>& n, traversal::edge_base& e)
+{
+ n->edge_traverser (e);
+ return e;
+}
+
+template <typename T>
+inline traversal::relational::edge_base&
+operator>> (instance<T>& n, traversal::relational::edge_base& e)
+{
+ n->edge_traverser (e);
+ return e;
+}
+
+template <typename T>
+inline traversal::node_base&
+operator>> (traversal::edge_base& e, instance<T>& n)
+{
+ e.node_traverser (*n);
+ return *n;
+}
+
+template <typename T>
+inline traversal::relational::node_base&
+operator>> (traversal::relational::edge_base& e, instance<T>& n)
+{
+ e.node_traverser (*n);
+ return *n;
+}
+
+#endif // ODB_INSTANCE_HXX
diff --git a/odb/odb/location.cxx b/odb/odb/location.cxx
new file mode 100644
index 0000000..23bd7ef
--- /dev/null
+++ b/odb/odb/location.cxx
@@ -0,0 +1,13 @@
+// file : odb/location.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/location.hxx>
+#include <odb/diagnostics.hxx>
+
+location::
+location (location_t l)
+ : file (location_file (l)),
+ line (location_line (l)),
+ column (location_column (l))
+{
+}
diff --git a/odb/odb/location.hxx b/odb/odb/location.hxx
new file mode 100644
index 0000000..cc59196
--- /dev/null
+++ b/odb/odb/location.hxx
@@ -0,0 +1,25 @@
+// file : odb/location.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_LOCATION_HXX
+#define ODB_LOCATION_HXX
+
+#include <odb/gcc-fwd.hxx>
+
+#include <cstddef>
+#include <libcutl/fs/path.hxx>
+
+struct location
+{
+ location (location_t);
+ location (cutl::fs::path const& f, std::size_t l, std::size_t c)
+ : file (f), line (l), column (c)
+ {
+ }
+
+ cutl::fs::path file;
+ std::size_t line;
+ std::size_t column;
+};
+
+#endif // ODB_LOCATION_HXX
diff --git a/odb/odb/lookup.cxx b/odb/odb/lookup.cxx
new file mode 100644
index 0000000..a54ef15
--- /dev/null
+++ b/odb/odb/lookup.cxx
@@ -0,0 +1,374 @@
+// file : odb/lookup.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/lookup.hxx>
+#include <odb/semantics.hxx>
+
+using namespace std;
+
+namespace lookup
+{
+ // Return canonical fundamental type name (<length> <sign> <type>, e.g.,
+ // short unsigned int) or empty string if it is not a fundamental type.
+ // Note that the type can still be invalid (e.g., unsigned float) so it
+ // needs to be resolved.
+ //
+ static string
+ parse_fundamental (cxx_lexer& l,
+ cpp_ttype& tt,
+ string& tl,
+ tree& tn,
+ cpp_ttype& ptt,
+ string& name)
+ {
+ bool
+ si (false), // signed
+ un (false), // unsigned
+ sh (false), // short
+ lo (false), // long
+ ll (false); // long long
+
+ string type;
+
+ for (; tt == CPP_KEYWORD; ptt = tt, tt = l.next (tl, &tn))
+ {
+ if (!name.empty ())
+ name += ' ';
+ name += tl;
+
+ if (tl == "signed")
+ {
+ if (si || un)
+ throw invalid_name ();
+
+ si = true;
+ }
+ else if (tl == "unsigned")
+ {
+ if (si || un)
+ throw invalid_name ();
+
+ un = true;
+ }
+ else if (tl == "short")
+ {
+ if (sh || lo || ll)
+ throw invalid_name ();
+
+ sh = true;
+ }
+ else if (tl == "long")
+ {
+ if (sh || ll)
+ throw invalid_name ();
+
+ if (lo)
+ {
+ lo = false;
+ ll = true;
+ }
+ else
+ lo = true;
+ }
+ else if (tl == "bool" ||
+ tl == "char" ||
+ tl == "wchar_t" ||
+ tl == "char16_t" || // C++11
+ tl == "char32_t" || // C++11
+ tl == "int" ||
+ tl == "float" ||
+ tl == "double")
+ {
+ if (!type.empty ())
+ throw invalid_name ();
+
+ type = tl;
+ }
+ else
+ break;
+ }
+
+ if (type.empty () && (si || un || sh || lo || ll))
+ type = "int"; // If type not specified, it defaults to int.
+
+ if (type.empty ())
+ return type;
+
+ string r;
+
+ if (sh)
+ r += "short ";
+
+ if (lo)
+ r += "long ";
+
+ if (ll)
+ r += "long long ";
+
+ if (si && type == "char")
+ r += "signed ";
+
+ if (un)
+ r += "unsigned ";
+
+ r += type;
+ return r;
+ }
+
+ string
+ parse_scoped_name (cxx_lexer& l, cpp_ttype& tt, string& tl, tree& tn)
+ {
+ string name;
+
+ if (tt == CPP_SCOPE)
+ {
+ name += "::";
+ tt = l.next (tl, &tn);
+ }
+ else if (tt == CPP_KEYWORD)
+ {
+ cpp_ttype ptt; // Not used.
+ string t (parse_fundamental (l, tt, tl, tn, ptt, name));
+
+ if (!t.empty ())
+ return name;
+ }
+
+ while (true)
+ {
+ if (tt != CPP_NAME)
+ throw invalid_name ();
+
+ name += tl;
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_SCOPE)
+ break;
+
+ name += "::";
+ tt = l.next (tl, &tn);
+ }
+
+ return name;
+ }
+
+ tree
+ resolve_scoped_name (cxx_lexer& l,
+ cpp_ttype& tt,
+ string& tl,
+ tree& tn,
+ cpp_ttype& ptt,
+ tree scope,
+ string& name,
+ bool is_type,
+ bool trailing_scope,
+ tree* end_scope)
+ {
+ tree id;
+ bool first (true);
+
+ if (tt == CPP_SCOPE)
+ {
+ name += "::";
+ scope = global_namespace;
+ first = false;
+
+ ptt = tt;
+ tt = l.next (tl, &tn);
+ }
+ else if (tt == CPP_KEYWORD)
+ {
+ string t (parse_fundamental (l, tt, tl, tn, ptt, name));
+
+ if (!t.empty ())
+ {
+ tree decl (
+ lookup_qualified_name (
+ global_namespace, get_identifier (t.c_str ()), true, false));
+
+ if (decl == error_mark_node)
+ throw unable_to_resolve (name, true);
+
+ if (end_scope != 0)
+ *end_scope = global_namespace;
+
+ return decl;
+ }
+ }
+
+ while (true)
+ {
+ if (end_scope != 0)
+ *end_scope = scope;
+
+ if (tt != CPP_NAME)
+ throw invalid_name ();
+
+ name += tl;
+ id = get_identifier (tl.c_str ());
+ ptt = tt;
+ tt = l.next (tl, &tn);
+
+ bool last (true);
+ if (tt == CPP_SCOPE)
+ {
+ // If trailing scope names are allowed, then we also need to
+ // check what's after the scope.
+ //
+ if (trailing_scope)
+ {
+ ptt = tt;
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_NAME)
+ last = false;
+ }
+ else
+ last = false;
+ }
+
+ tree decl = lookup_qualified_name (scope, id, last && is_type, false);
+
+ // If this is the first component in the name, then also search the
+ // outer scopes.
+ //
+ if (decl == error_mark_node && first && scope != global_namespace)
+ {
+ do
+ {
+ scope = TYPE_P (scope)
+ ? CP_TYPE_CONTEXT (scope)
+ : CP_DECL_CONTEXT (scope);
+ decl = lookup_qualified_name (scope, id, last && is_type, false);
+ } while (decl == error_mark_node && scope != global_namespace);
+ }
+
+ if (decl == error_mark_node)
+ throw unable_to_resolve (name, last);
+
+ scope = decl;
+
+ if (last)
+ break;
+
+ first = false;
+
+ if (TREE_CODE (scope) == TYPE_DECL)
+ scope = TREE_TYPE (scope);
+
+ name += "::";
+
+ if (!trailing_scope)
+ {
+ ptt = tt;
+ tt = l.next (tl, &tn);
+ }
+ }
+
+ return scope;
+ }
+
+ semantics::node&
+ resolve_scoped_name (cxx_lexer& l,
+ cpp_ttype& tt,
+ string& tl,
+ tree& tn,
+ cpp_ttype& ptt,
+ semantics::scope& start_scope,
+ string& name,
+ semantics::type_id const& tid,
+ bool trailing_scope,
+ semantics::scope** end_scope)
+ {
+ bool first (true);
+ semantics::scope* scope (&start_scope);
+
+ if (tt == CPP_SCOPE)
+ {
+ name += "::";
+ for (; !scope->global_scope (); scope = &scope->scope_ ()) ;
+ first = false;
+
+ ptt = tt;
+ tt = l.next (tl, &tn);
+ }
+ else if (tt == CPP_KEYWORD)
+ {
+ string t (parse_fundamental (l, tt, tl, tn, ptt, name));
+
+ if (!t.empty ())
+ {
+ for (; !scope->global_scope (); scope = &scope->scope_ ()) ;
+
+ if (end_scope != 0)
+ *end_scope = scope;
+
+ return scope->lookup<semantics::fund_type> (t);
+ }
+ }
+
+ semantics::names* r;
+
+ for (;;)
+ {
+ if (end_scope != 0)
+ *end_scope = scope;
+
+ if (tt != CPP_NAME)
+ throw invalid_name ();
+
+ name += tl;
+ string n (tl);
+ ptt = tt;
+ tt = l.next (tl, &tn);
+
+ bool last (true);
+ if (tt == CPP_SCOPE)
+ {
+ // If trailing scope names are allowed, then we also need to
+ // check what's after the scope.
+ //
+ if (trailing_scope)
+ {
+ ptt = tt;
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_NAME)
+ last = false;
+ }
+ else
+ last = false;
+ }
+
+ // If this is the first component in the name, then also search the
+ // outer scopes.
+ //
+ bool hidden (false);
+ r = scope->lookup (
+ n,
+ (last ? tid : typeid (semantics::scope)),
+ (first ? 0 : semantics::scope::exclude_outer) |
+ (last ? semantics::scope::include_hidden : 0),
+ (last ? &hidden : 0));
+
+ if (r == 0)
+ throw semantics::unresolved (name, hidden);
+
+ if (last)
+ break;
+
+ scope = &dynamic_cast<semantics::scope&> (r->named ());
+ first = false;
+
+ name += "::";
+
+ if (!trailing_scope)
+ {
+ ptt = tt;
+ tt = l.next (tl, &tn);
+ }
+ }
+
+ return r->named ();
+ }
+}
diff --git a/odb/odb/lookup.hxx b/odb/odb/lookup.hxx
new file mode 100644
index 0000000..86c65b2
--- /dev/null
+++ b/odb/odb/lookup.hxx
@@ -0,0 +1,97 @@
+// file : odb/lookup.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_LOOKUP_HXX
+#define ODB_LOOKUP_HXX
+
+#include <odb/gcc.hxx>
+
+#include <string>
+
+#include <odb/cxx-lexer.hxx>
+#include <odb/semantics/elements.hxx>
+
+namespace lookup
+{
+ struct invalid_name
+ {
+ };
+
+ struct unable_to_resolve
+ {
+ unable_to_resolve (std::string const& n, bool l): name_ (n), last_ (l) {}
+
+ std::string const&
+ name () const {return name_;}
+
+ // Last component in the name.
+ //
+ bool
+ last () const {return last_;}
+
+ private:
+ std::string name_;
+ bool last_;
+ };
+
+ std::string
+ parse_scoped_name (cxx_lexer&,
+ cpp_ttype&,
+ std::string& tl, // Token literal.
+ tree& tn); // Token node.
+
+ // If trailing_scope is true, then this function also handles
+ // names in the 'foo::bar::<something-other-than-name>' form.
+ // In this case token will be <something-other-than-name> and
+ // ptt will be CPP_SCOPE.
+ //
+ // The names are appended to the 'name' variable as they are
+ // being resolved.
+ //
+ tree
+ resolve_scoped_name (cxx_lexer&,
+ cpp_ttype&,
+ std::string& tl, // Token literal.
+ tree& tn, // Token node.
+ cpp_ttype& ptt, // Previous token type.
+ tree start_scope,
+ std::string& name,
+ bool is_type,
+ bool trailing_scope = false,
+ tree* end_scope = 0);
+
+ // The same but using semantic graph instead of GCC tree. Also
+ // throws semantics::unresolved instead of unable_to_resolve.
+ //
+ semantics::node&
+ resolve_scoped_name (cxx_lexer&,
+ cpp_ttype&,
+ std::string& tl, // Token literal.
+ tree& tn, // Token node.
+ cpp_ttype& ptt, // Previous token type.
+ semantics::scope& start_scope,
+ std::string& name,
+ semantics::type_id const&,
+ bool trailing_scope = false,
+ semantics::scope** end_scope = 0);
+
+ template <typename T>
+ T&
+ resolve_scoped_name (cxx_lexer& l,
+ cpp_ttype& tt,
+ std::string& tl, // Token literal.
+ tree& tn, // Token node.
+ cpp_ttype& ptt, // Previous token type.
+ semantics::scope& start_scope,
+ std::string& name,
+ bool trailing_scope = false,
+ semantics::scope** end_scope = 0)
+ {
+ return dynamic_cast<T&> (
+ resolve_scoped_name (
+ l, tt, tl, tn, ptt,
+ start_scope, name, typeid (T), trailing_scope, end_scope));
+ }
+}
+
+#endif // ODB_LOOKUP_HXX
diff --git a/odb/odb/odb.cxx b/odb/odb/odb.cxx
new file mode 100644
index 0000000..41063d5
--- /dev/null
+++ b/odb/odb/odb.cxx
@@ -0,0 +1,2191 @@
+// file : odb/odb.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <errno.h>
+#include <stdlib.h> // getenv, setenv
+#include <string.h> // strerror, memset
+#include <unistd.h> // stat, close
+#include <sys/types.h> // stat
+#include <sys/stat.h> // stat
+
+// Process.
+//
+#ifndef _WIN32
+# include <unistd.h> // execvp, fork, dup2, pipe, {STDIN,STDERR}_FILENO
+# include <sys/types.h> // waitpid
+# include <sys/wait.h> // waitpid
+#else
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# endif
+# include <windows.h> // CreatePipe, CreateProcess, GetTemp*, MAX_PATH
+# include <io.h> // _open_osfhandle
+# include <fcntl.h> // _O_TEXT
+#endif
+
+#include <string>
+#include <vector>
+#include <cstddef> // size_t
+#include <sstream>
+#include <fstream>
+#include <iostream>
+#include <ext/stdio_filebuf.h>
+
+#include <libcutl/fs/path.hxx>
+#include <libcutl/fs/auto-remove.hxx>
+
+#include <odb/version.hxx>
+#include <odb/options.hxx>
+#include <odb/profile.hxx>
+
+#ifdef HAVE_CONFIG_H
+# include <odb/config.h>
+#endif
+
+using namespace std;
+using cutl::fs::path;
+using cutl::fs::invalid_path;
+using cutl::fs::auto_remove;
+
+typedef vector<string> strings;
+typedef vector<path> paths;
+
+//
+// Path manipulation.
+//
+
+// Escape backslashes in the path.
+//
+static string
+escape_path (string const&);
+
+// Search the PATH environment variable for the file.
+//
+static path
+path_search (path const&);
+
+// Driver path with the directory part (search PATH).
+//
+static path
+driver_path (path const& driver);
+
+#ifndef ODB_STATIC_PLUGIN
+static path
+plugin_path (path const& driver, string const& gxx);
+#endif
+
+//
+// Process manipulation.
+//
+struct process_info
+{
+#ifndef _WIN32
+ pid_t id;
+#else
+ HANDLE id;
+#endif
+
+ int out_fd; // Write to this fd to send to the new process' stdin.
+ int in_efd; // Read from this fd to receive from the new process' stderr.
+ int in_ofd; // Read from this fd to receive from the new process' stdout.
+};
+
+struct process_failure {};
+
+#ifdef _WIN32
+// Deal with Windows command line length limit.
+//
+static auto_remove
+fixup_cmd_line (vector<const char*>& args,
+ size_t start,
+ const char* name,
+ string& arg);
+#endif
+
+// Start another process using the specified command line. Connect the
+// newly created process' stdin to out_fd. Also if connect_* are true,
+// connect the created process' stdout and stderr to in_*fd. Issue
+// diagnostics and throw process_failure if anything goes wrong. The
+// name argument is the name of the current process for diagnostics.
+//
+static process_info
+start_process (char const* args[],
+ char const* name,
+ bool connect_stderr = false,
+ bool connect_stdout = false);
+
+// Wait for the process to terminate. Return true if the process terminated
+// normally and with the zero exit status. Issue diagnostics and throw
+// process_failure if anything goes wrong. The name argument is the name
+// of the current process for diagnostics.
+//
+static bool
+wait_process (process_info, char const* name);
+
+//
+//
+static string
+encode_plugin_flag (string const& k);
+
+static string
+encode_plugin_option (string const& k, string const& v);
+
+// Extract header search paths from GCC's -v output. May throw the
+// profile_failure, process_failure and invalid_path exceptions. Name
+// is the program name (argv[0]) for diagnostics.
+//
+static paths
+profile_paths (strings const& args, char const* name);
+
+static char const* const db_macro[] =
+{
+ "-DODB_DATABASE_COMMON",
+ "-DODB_DATABASE_MSSQL",
+ "-DODB_DATABASE_MYSQL",
+ "-DODB_DATABASE_ORACLE",
+ "-DODB_DATABASE_PGSQL",
+ "-DODB_DATABASE_SQLITE"
+};
+
+int
+main (int argc, char* argv[])
+{
+ ostream& e (cerr);
+
+ try
+ {
+ strings args, plugin_args;
+ bool v (false);
+
+ // The first argument points to the program name, which is
+ // g++ by default.
+ //
+#ifdef ODB_GXX_NAME
+ path gxx (ODB_GXX_NAME);
+
+ if (gxx.empty ())
+ {
+ e << argv[0] << ": error: embedded g++ compile name is empty" << endl;
+ return 1;
+ }
+
+ // If the g++ name is a relative path (starts with '.'), then use
+ // our own path as base.
+ //
+ if (gxx.string ()[0] == '.')
+ {
+ path dp (driver_path (path (argv[0])));
+ path d (dp.directory ());
+
+ if (!d.empty ())
+ gxx = d / gxx;
+ }
+
+ args.push_back (gxx.string ());
+
+ // Also modify LD_LIBRARY_PATH to include the lib path.
+ //
+ // @@ Hm, I wonder why we are doing this? (One some platforms, like
+ // Fedora, the directory could be called something else, like lib64).
+ //
+#ifndef _WIN32
+ {
+#ifdef __APPLE__
+ char const name[] = "DYLD_LIBRARY_PATH";
+#else
+ char const name[] = "LD_LIBRARY_PATH";
+#endif
+
+ string ld_paths;
+
+ if (char const* s = getenv (name))
+ ld_paths = s;
+
+ path d (gxx.directory ());
+
+ if (!d.empty ())
+ {
+ d.complete ();
+ d /= path ("..") / path ("lib");
+
+ if (ld_paths.empty ())
+ ld_paths = d.string ();
+ else
+ ld_paths = d.string () + path::traits::path_separator + ld_paths;
+
+ if (setenv (name, ld_paths.c_str (), 1) != 0)
+ {
+ e << argv[0] << ": error: unable to update environment" << endl;
+ return 1;
+ }
+ }
+ }
+#endif // _WIN32
+
+#else
+ args.push_back ("g++");
+#endif // ODB_GXX_NAME
+
+ // Default options.
+ //
+ args.push_back ("-x");
+ args.push_back ("c++");
+ args.push_back (""); // Reserve space for -std=c++XX.
+ args.push_back ("-S");
+ args.push_back ("-Wunknown-pragmas");
+ args.push_back ("-Wno-deprecated");
+ args.push_back (""); // Reserve space for -fplugin=path.
+
+ // Parse the default options file if we have one.
+ //
+ strings def_inc_dirs;
+ strings def_defines;
+#ifdef ODB_DEFAULT_OPTIONS_FILE
+ {
+ path file (ODB_DEFAULT_OPTIONS_FILE);
+
+ // If the path is relative, then use the driver's path as a base. If
+ // the file is not found in that directory, then also try outer
+ // directory (so that we can find /etc if driver is in /usr/bin).
+ //
+ if (file.relative ())
+ {
+ bool found (false);
+ path dd (driver_path (path (argv[0])).directory ());
+
+ for (path d (dd);; d = d.directory ())
+ {
+ path f (d / file);
+ // Check that the file exist without checking for permissions, etc.
+ //
+ struct stat s;
+ if (stat (f.string ().c_str (), &s) == 0 && S_ISREG (s.st_mode))
+ {
+ file = f;
+ found = true;
+ break;
+ }
+
+ if (d.root ())
+ break;
+ }
+
+ if (!found)
+ file = dd / file; // For diagnostics.
+ }
+
+ cli::argv_file_scanner s (file.string ());
+
+ bool first_x (true);
+
+ while (s.more ())
+ {
+ string a (s.next ());
+ size_t n (a.size ());
+
+ // -x
+ //
+ if (a == "-x")
+ {
+ if (!s.more () || (a = s.next ()).empty ())
+ {
+ e << file << ": error: expected argument for the " << a
+ << " option" << endl;
+ return 1;
+ }
+
+ if (first_x)
+ {
+ first_x = false;
+
+ // If it doesn't start with '-', then it must be the g++
+ // executable name. Update the first argument with it.
+ //
+ if (a[0] != '-')
+ args[0] = a;
+ else
+ args.push_back (a);
+ }
+ else
+ args.push_back (a);
+ }
+ // -I
+ //
+ else if (n > 1 && a[0] == '-' && a[1] == 'I')
+ {
+ def_inc_dirs.push_back (a);
+
+ if (n == 2) // -I /path
+ {
+ if (!s.more () || (a = s.next ()).empty ())
+ {
+ e << file << ": error: expected argument for the -I option"
+ << endl;
+ return 1;
+ }
+
+ def_inc_dirs.push_back (a);
+ }
+ }
+ // -isystem, -iquote, -idirafter, and -framework (Mac OS X)
+ //
+ else if (a == "-isystem" ||
+ a == "-iquote" ||
+ a == "-idirafter" ||
+ a == "-framework")
+ {
+ def_inc_dirs.push_back (a);
+
+ if (!s.more () || (a = s.next ()).empty ())
+ {
+ e << file << ": error: expected argument for the " << a
+ << " option" << endl;
+ return 1;
+ }
+
+ def_inc_dirs.push_back (a);
+ }
+ // -D
+ //
+ else if (n > 1 && a[0] == '-' && a[1] == 'D')
+ {
+ def_defines.push_back (a);
+
+ if (n == 2) // -D macro
+ {
+ if (!s.more () || (a = s.next ()).empty ())
+ {
+ e << file << ": error: expected argument for the -D option"
+ << endl;
+ return 1;
+ }
+
+ def_defines.push_back (a);
+ }
+ }
+ // -U
+ //
+ else if (n > 1 && a[0] == '-' && a[1] == 'U')
+ {
+ def_defines.push_back (a);
+
+ if (n == 2) // -U macro
+ {
+ if (!s.more () || (a = s.next ()).empty ())
+ {
+ e << file << ": error: expected argument for the -U option"
+ << endl;
+ return 1;
+ }
+
+ def_defines.push_back (a);
+ }
+ }
+ else
+ plugin_args.push_back (a);
+ }
+ }
+#endif
+
+ // Add the default preprocessor defines (-D/-U) before the user-supplied
+ // ones.
+ //
+ args.insert (args.end (), def_defines.begin (), def_defines.end ());
+
+ // Parse driver options.
+ //
+ // We scan expanding --options-file in order to allow specifying ad hoc
+ // options (-I, etc) in options files.
+ //
+ bool first_x (true);
+
+ for (cli::argv_file_scanner scan (argc, argv, "--options-file");
+ scan.more (); )
+ {
+ string a (scan.next ());
+ size_t n (a.size ());
+
+ // -v
+ //
+ if (a == "-v")
+ {
+ v = true;
+ args.push_back (a);
+ }
+ // -x
+ //
+ else if (a == "-x")
+ {
+ const char* v;
+ if (!scan.more () || (v = scan.next ())[0] == '\0')
+ {
+ e << argv[0] << ": error: expected argument for the -x option"
+ << endl;
+ return 1;
+ }
+
+ if (first_x)
+ {
+ first_x = false;
+
+ // If it doesn't start with '-', then it must be the g++
+ // executable name. Update the first argument with it.
+ //
+ if (v[0] != '-')
+ args[0] = v;
+ else
+ args.push_back (v);
+ }
+ else
+ args.push_back (v);
+ }
+ // -I
+ //
+ else if (n > 1 && a[0] == '-' && a[1] == 'I')
+ {
+ args.push_back (a);
+
+ if (n == 2) // -I /path
+ {
+ const char* v;
+ if (!scan.more () || (v = scan.next ())[0] == '\0')
+ {
+ e << argv[0] << ": error: expected argument for the -I option"
+ << endl;
+ return 1;
+ }
+
+ args.push_back (v);
+ }
+ }
+ // -isystem, -iquote, -idirafter, and -framework (Mac OS X)
+ //
+ else if (a == "-isystem" ||
+ a == "-iquote" ||
+ a == "-idirafter" ||
+ a == "-framework")
+ {
+ args.push_back (a);
+
+ const char* v;
+ if (!scan.more () || (v = scan.next ())[0] == '\0')
+ {
+ e << argv[0] << ": error: expected argument for the " << a
+ << " option" << endl;
+ return 1;
+ }
+
+ args.push_back (v);
+ }
+ // -D
+ //
+ else if (n > 1 && a[0] == '-' && a[1] == 'D')
+ {
+ args.push_back (a);
+
+ if (n == 2) // -D macro
+ {
+ const char* v;
+ if (!scan.more () || (v = scan.next ())[0] == '\0')
+ {
+ e << argv[0] << ": error: expected argument for the -D option"
+ << endl;
+ return 1;
+ }
+
+ args.push_back (v);
+ }
+ }
+ // -U
+ //
+ else if (n > 1 && a[0] == '-' && a[1] == 'U')
+ {
+ args.push_back (a);
+
+ if (n == 2) // -U macro
+ {
+ const char* v;
+ if (!scan.more () || (v = scan.next ())[0] == '\0')
+ {
+ e << argv[0] << ": error: expected argument for the -U option"
+ << endl;
+ return 1;
+ }
+
+ args.push_back (v);
+ }
+ }
+ // Store everything else in a list so that we can parse it with the
+ // cli parser. This is the only reliable way to find out where the
+ // options end.
+ //
+ else
+ plugin_args.push_back (a);
+ }
+
+ // Add the default include directories (-I) after the user-supplied
+ // ones.
+ //
+ args.insert (args.end (), def_inc_dirs.begin (), def_inc_dirs.end ());
+
+ // Find the plugin.
+ //
+ {
+#ifndef ODB_STATIC_PLUGIN
+ path plugin (plugin_path (path (argv[0]), args[0]));
+#else
+ // Use a dummy name if the plugin is linked into the compiler.
+ //
+ path plugin ("odb");
+#endif
+
+ if (plugin.empty ())
+ return 1; // Diagnostics has already been issued.
+
+#ifdef _WIN32
+ // Here is the problem: since the plugin is loaded by GCC (cc1plus.exe
+ // to be precise), the DLL assembly magic we have for executables won't
+ // help here.
+ //
+ // To allow executing the ODB compiler in-place we add the odb.exe.dlls/
+ // directory to PATH. It is a bit of hack but then DLL assemblies for
+ // DLLs is a whole new level of insanity that we are unlikely to ever
+ // touch.
+ //
+ // And it turns out we have the same problem in the installed case: if
+ // the installation directory is not in PATH, then GCC won't find the
+ // DLLs the plugin needs. So we handle both cases here.
+ //
+ {
+ path d (plugin.directory ());
+ d.complete ();
+ d.normalize ();
+ d /= path ("odb.exe.dlls");
+
+ struct stat st;
+ if (stat (d.string ().c_str (), &st) != 0 || !S_ISDIR (st.st_mode))
+ d = d.directory ();
+
+ string v ("PATH=" + d.string ());
+
+ if (char const* p = getenv ("PATH"))
+ {
+ v += ';';
+ v += p;
+ }
+
+ _putenv (v.c_str ());
+ }
+#endif
+
+ args[7] = "-fplugin=" + plugin.string ();
+ }
+
+ // Parse plugin options. We have to do it twice to get the target
+ // database which is needed while loading profiles.
+ //
+ vector<char*> av;
+ av.push_back (argv[0]);
+
+ for (strings::iterator i (plugin_args.begin ()), end (plugin_args.end ());
+ i != end; ++i)
+ {
+ av.push_back (const_cast<char*> (i->c_str ()));
+ }
+
+ int ac (static_cast<int> (av.size ()));
+
+ cli::argv_file_scanner::option_info oi[3];
+ oi[0].option = "--options-file"; // Keep in case profile uses it.
+ oi[0].search_func = 0;
+ oi[1].option = "-p";
+ oi[2].option = "--profile";
+
+ vector<database> dbs;
+ bool show_sloc;
+ size_t sloc_limit;
+ {
+ oi[1].search_func = &profile_search_ignore;
+ oi[2].search_func = &profile_search_ignore;
+
+ cli::argv_file_scanner scan (ac, &av[0], oi, 3);
+ options ops (scan);
+
+ // Handle --build2-metadata (see also buildfile).
+ //
+ if (ops.build2_metadata_specified ())
+ {
+ ostream& o (cout);
+
+ // Note that the export.metadata variable should be the first non-
+ // blank/comment line.
+ //
+ o << "# build2 buildfile odb" << endl
+ << "export.metadata = 1 odb" << endl
+ << "odb.name = [string] odb" << endl
+ << "odb.version = [string] '" << ODB_COMPILER_VERSION_FULL << '\'' << endl
+ << "odb.checksum = [string] '" << ODB_COMPILER_VERSION_FULL << '\'' << endl
+ << "odb.environment = [strings] CPATH CPLUS_INCLUDE_PATH GCC_EXEC_PREFIX COMPILER_PATH" << endl;
+
+ return 0;
+ }
+
+ // Handle --version.
+ //
+ if (ops.version ())
+ {
+ ostream& o (cout);
+
+ o << "ODB object-relational mapping (ORM) compiler for C++ "
+ ODB_COMPILER_VERSION_STR << endl
+ << "Copyright (c) " << ODB_COPYRIGHT << "." << endl
+ << "This is free software; see the source for copying conditions. "
+ << "There is NO\nwarranty; not even for MERCHANTABILITY or FITNESS "
+ << "FOR A PARTICULAR PURPOSE." << endl;
+
+ return 0;
+ }
+
+ // Handle --help.
+ //
+ if (ops.help ())
+ {
+ ostream& o (cout);
+
+ o << "Usage: " << argv[0] << " [options] file [file ...]" << endl
+ << "Options:" << endl;
+
+ options::print_usage (cout);
+ return 0;
+ }
+
+ // Check that required options were specifed.
+ //
+ dbs = ops.database ();
+
+ if (dbs.empty ())
+ {
+ e << argv[0] << ": error: no database specified with the --database "
+ << "option" << endl;
+ return 1;
+ }
+
+ if (dbs.size () > 1 && !ops.multi_database_specified ())
+ {
+ e << argv[0] << ": error: --multi-database option required when " <<
+ "multiple databases are specified"<< endl;
+ return 1;
+ }
+
+ show_sloc = ops.show_sloc ();
+ sloc_limit = ops.sloc_limit_specified () ? ops.sloc_limit () : 0;
+
+ // Translate some ODB options to GCC options.
+ //
+ switch (ops.std ())
+ {
+ case cxx_version::cxx98:
+ {
+ args[3] = "-std=c++98";
+ break;
+ }
+ case cxx_version::cxx11:
+ {
+ args[3] = "-std=c++0x"; // c++11 was only added in GCC 4.7.0.
+ break;
+ }
+ case cxx_version::cxx14:
+ {
+ args[3] = "-std=c++1y";
+ break;
+ }
+ case cxx_version::cxx17:
+ {
+ args[3] = "-std=c++1z";
+ break;
+ }
+ case cxx_version::cxx20:
+ {
+ args[3] = "-std=c++2a";
+ break;
+ }
+ }
+ }
+
+ // Obtain profile (-I) search paths.
+ //
+ paths prof_paths (profile_paths (args, argv[0]));
+
+ if (v)
+ {
+ e << "Profile search paths:" << endl;
+
+ for (paths::const_iterator i (prof_paths.begin ());
+ i != prof_paths.end (); ++i)
+ e << " " << *i << endl;
+ }
+
+ // Pass profile search paths (svc-path option).
+ //
+ for (paths::const_iterator i (prof_paths.begin ());
+ i != prof_paths.end (); ++i)
+ {
+ args.push_back (encode_plugin_option ("svc-path", i->string ()));
+ }
+
+ // Add common ODB macros.
+ //
+ args.push_back ("-DODB_COMPILER");
+
+ {
+ ostringstream ostr;
+ ostr << ODB_COMPILER_VERSION_OLD;
+ args.push_back ("-DODB_COMPILER_VERSION=" + ostr.str ());
+ }
+
+ // Compile for each database.
+ //
+ size_t sloc_total (0);
+
+ for (vector<database>::iterator i (dbs.begin ()); i != dbs.end (); ++i)
+ {
+ database db (*i);
+ strings db_args (args);
+
+ // Add database-specific ODB macro.
+ //
+ db_args.push_back (db_macro[db]);
+
+ // Second parse.
+ //
+ profile_data pd (prof_paths, db, argv[0]);
+ oi[1].search_func = &profile_search;
+ oi[2].search_func = &profile_search;
+ oi[1].arg = &pd;
+ oi[2].arg = &pd;
+
+ cli::argv_file_scanner scan (ac, &av[0], oi, 3);
+ options ops (scan);
+
+ size_t end (scan.end () - 1); // We have one less in plugin_args.
+
+ if (end == plugin_args.size ())
+ {
+ e << argv[0] << ": error: input file expected" << endl;
+ return 1;
+ }
+
+ // Encode plugin options.
+ //
+ // Add the database we are compiling for first. More databases
+ // could be specified in options files but they will be ignored
+ // by the plugin (it only cares about the first).
+ //
+ db_args.push_back (encode_plugin_option ("database", db.string ()));
+
+ cli::options const& desc (options::description ());
+ for (size_t i (0); i < end; ++i)
+ {
+ string k, a (plugin_args[i]);
+
+ // Ignore certain options.
+ //
+ if (a == "--")
+ {
+ // Ignore the option seperator since GCC doesn't understand it.
+ //
+ continue;
+ }
+ else if (a == "-d" || a == "--database")
+ {
+ // Ignore all other databases.
+ //
+ i++; // Skip the value.
+ continue;
+ }
+
+ cli::options::const_iterator it (desc.find (a));
+
+ if (it == desc.end ())
+ {
+ e << argv[0] << ": ice: unexpected option '" << a << "'" << endl;
+ return 1;
+ }
+
+ if (a.size () > 2 && a[0] == '-' && a[1] == '-')
+ k = string (a, 2); // long format
+ else
+ k = string (a, 1); // short format
+
+ if (it->flag ())
+ db_args.push_back (encode_plugin_flag (k));
+ else
+ {
+ // If there are more arguments then we may have a value.
+ //
+ if (i + 1 == end)
+ {
+ e << argv[0] << ": ice: expected argument for '" << a << "'"
+ << endl;
+ return 1;
+ }
+
+ db_args.push_back (encode_plugin_option (k, plugin_args[++i]));
+ }
+ }
+
+ // Reserve space for and remember the position of the svc-file
+ // option.
+ //
+ size_t svc_file_pos (db_args.size ());
+ db_args.push_back ("");
+
+ // If compiling multiple input files at once, pass them also with
+ // the --svc-file option.
+ //
+ bool at_once (ops.at_once () && plugin_args.size () - end > 1);
+ if (at_once)
+ {
+ if (ops.input_name ().empty ())
+ {
+ e << "error: --input-name required when compiling multiple " <<
+ "input files at once (--at-once)" << endl;
+ return 1;
+ }
+
+ for (size_t i (end); i < plugin_args.size (); ++i)
+ db_args.push_back (
+ encode_plugin_option ("svc-file", plugin_args[i]));
+ }
+
+ // Create an execvp-compatible argument array.
+ //
+ typedef vector<char const*> cstrings;
+ cstrings exec_args;
+
+ for (strings::const_iterator i (db_args.begin ()), end (db_args.end ());
+ i != end; ++i)
+ {
+ exec_args.push_back (i->c_str ());
+ }
+
+ exec_args.push_back ("-"); // Compile stdin.
+ exec_args.push_back (0);
+
+ // Iterate over the input files and compile each of them.
+ //
+ for (; end < plugin_args.size (); ++end)
+ {
+ string name (at_once ? ops.input_name () : plugin_args[end]);
+
+ // Set the --svc-file option.
+ //
+ db_args[svc_file_pos] = encode_plugin_option ("svc-file", name);
+ exec_args[svc_file_pos] = db_args[svc_file_pos].c_str ();
+
+ //
+ //
+ ifstream ifs;
+
+ if (!at_once)
+ {
+ ifs.open (name.c_str (), ios_base::in | ios_base::binary);
+
+ if (!ifs.is_open ())
+ {
+ e << name << ": error: unable to open in read mode" << endl;
+ return 1;
+ }
+ }
+
+ if (v)
+ {
+ e << "Compiling " << name << endl;
+ for (cstrings::const_iterator i (exec_args.begin ());
+ i != exec_args.end (); ++i)
+ {
+ if (*i != 0)
+ e << *i << (*(i + 1) != 0 ? ' ' : '\n');
+ }
+ }
+
+ // Deal with Windows command line length limit.
+ //
+#ifdef _WIN32
+ string ops_file_arg;
+ auto_remove opt_file_rm (
+ fixup_cmd_line (exec_args, 1, argv[0], ops_file_arg));
+#endif
+
+ process_info pi (start_process (&exec_args[0], argv[0], false, true));
+
+ {
+ __gnu_cxx::stdio_filebuf<char> fb (
+ pi.out_fd, ios_base::out | ios_base::binary);
+ ostream os (&fb);
+
+ if (!at_once)
+ {
+ // See if we there is a UTF-8 BOM in the input file. If so,
+ // then we need to write it before prologues.
+ //
+ if (ifs.peek () == 0xEF)
+ {
+ ifs.get ();
+ if (ifs.get () != 0xBB || ifs.get () != 0xBF)
+ {
+ e << name << ": error: invalid UTF-8 BOM sequence" << endl;
+ fb.close ();
+ wait_process (pi, argv[0]);
+ return 1;
+ }
+
+ os.put (0xEF);
+ os.put (0xBB);
+ os.put (0xBF);
+ }
+ }
+
+ if (!ops.trace ())
+ {
+ // Add the standard prologue.
+ //
+ os << "#line 1 \"<standard-odb-prologue>\"" << endl;
+
+ // Make sure ODB compiler and libodb versions are compatible.
+ //
+ // Note that the snapshot comparision is only necessary of the
+ // version is a pre-release but having it always won't hurt (it
+ // will be 0 for final versions).
+ //
+ // After some experience with requiring the exact version match we
+ // found it just too tedious and went back to only comparing the
+ // interface version (we could support both with an option; see
+ // also similar code in generator.cxx).
+ //
+ os << "#include <odb/version.hxx>" << endl
+ << endl
+#if 1
+ << "#if ODB_VERSION != " << ODB_VERSION << "UL" << endl
+#else
+ << "#if LIBODB_VERSION_FULL != " << ODB_COMPILER_VERSION << "ULL"
+ " || LIBODB_SNAPSHOT != " << ODB_COMPILER_SNAPSHOT << "ULL" << endl
+#endif
+ << "# error incompatible ODB compiler and runtime " <<
+ "versions" << endl
+ << "#endif" << endl
+ << endl;
+
+ // Include std::string. It is used as a default type for
+ // the implicit discriminator member in polymorphism
+ // support.
+ //
+ os << "#include <string>" << endl
+ << endl;
+
+ // Add ODB compiler metaprogramming tests.
+ //
+ os << "namespace odb" << endl
+ << "{" << endl
+ << "namespace compiler" << endl
+ << "{" << endl;
+
+ // operator< test, used in validator.
+ //
+ // Note that typeof() cannot be used in the function signature
+ // directly so we have to go though lt_operator_type. This means
+ // we get diagnostics from the compiler (followed by ours) but
+ // it's doesn't look bad plus C++98 support is on its way out.
+ //
+ os << "template <typename T>" << endl
+ << "const T&" << endl
+ << "instance ();" << endl
+ << endl;
+
+ if (ops.std () == cxx_version::cxx98)
+ {
+ os << "template <typename T>" << endl
+ << "struct lt_operator_type" << endl
+ << "{" << endl
+ << "typedef __typeof__ (instance<T> () < instance<T> ()) R;" << endl
+ << "};" << endl
+ << endl
+ << "template <typename T>" << endl
+ << "typename lt_operator_type<T>::R" << endl
+ << "has_lt_operator ();" << endl;
+ }
+ else
+ {
+ os << "template <typename T>" << endl
+ << "decltype (instance<T> () < instance<T> ())" << endl
+ << "has_lt_operator ();" << endl;
+ }
+
+ os << "}" << endl
+ << "}" << endl;
+ }
+
+ // Add custom prologue if any.
+ //
+ // NOTE: if you change the format, you also need to update code
+ // in include.cxx
+ //
+ size_t pro_count (1);
+ if (ops.odb_prologue ().count (db) != 0)
+ {
+ strings const& pro (ops.odb_prologue ()[db]);
+ for (size_t i (0); i < pro.size (); ++i, ++pro_count)
+ {
+ os << "#line 1 \"<odb-prologue-" << pro_count << ">\"" << endl
+ << pro[i] << endl;
+ }
+ }
+
+ if (ops.odb_prologue_file ().count (db) != 0)
+ {
+ strings const& prof (ops.odb_prologue_file ()[db]);
+ for (size_t i (0); i < prof.size (); ++i, ++pro_count)
+ {
+ os << "#line 1 \"<odb-prologue-" << pro_count << ">\""
+ << endl;
+
+ ifstream ifs (prof[i].c_str (), ios_base::in | ios_base::binary);
+
+ if (!ifs.is_open ())
+ {
+ e << prof[i] << ": error: unable to open in read mode" << endl;
+ fb.close ();
+ wait_process (pi, argv[0]);
+ return 1;
+ }
+
+ if (!(os << ifs.rdbuf ()))
+ {
+ e << prof[i] << ": error: io failure" << endl;
+ fb.close ();
+ wait_process (pi, argv[0]);
+ return 1;
+ }
+
+ os << endl;
+ }
+ }
+
+ if (at_once)
+ {
+ // Include all the input files (no need to escape).
+ //
+ os << "#line 1 \"<command-line>\"" << endl;
+
+ bool b (ops.include_with_brackets ());
+ char op (b ? '<' : '"'), cl (b ? '>' : '"');
+
+ for (; end < plugin_args.size (); ++end)
+ os << "#include " << op << plugin_args[end] << cl << endl;
+ }
+ else
+ {
+ // Write the synthesized translation unit to stdout.
+ //
+ os << "#line 1 \"" << escape_path (name) << "\"" << endl;
+
+ if (!(os << ifs.rdbuf ()))
+ {
+ e << name << ": error: io failure" << endl;
+ fb.close ();
+ wait_process (pi, argv[0]);
+ return 1;
+ }
+
+ // Add a new line in case the input file doesn't end with one.
+ //
+ os << endl;
+ }
+
+ // Add custom epilogue if any.
+ //
+ // NOTE: if you change the format, you also need to update code
+ // in include.cxx
+ //
+ size_t epi_count (1);
+ if (ops.odb_epilogue ().count (db) != 0)
+ {
+ strings const& epi (ops.odb_epilogue ()[db]);
+ for (size_t i (0); i < epi.size (); ++i, ++epi_count)
+ {
+ os << "#line 1 \"<odb-epilogue-" << epi_count << ">\"" << endl
+ << epi[i] << endl;
+ }
+ }
+
+ if (ops.odb_epilogue_file ().count (db) != 0)
+ {
+ strings const& epif (ops.odb_epilogue_file ()[db]);
+ for (size_t i (0); i < epif.size (); ++i, ++epi_count)
+ {
+ os << "#line 1 \"<odb-epilogue-" << epi_count << ">\""
+ << endl;
+
+ ifstream ifs (epif[i].c_str (), ios_base::in | ios_base::binary);
+
+ if (!ifs.is_open ())
+ {
+ e << epif[i] << ": error: unable to open in read mode" << endl;
+ fb.close ();
+ wait_process (pi, argv[0]);
+ return 1;
+ }
+
+ if (!(os << ifs.rdbuf ()))
+ {
+ e << epif[i] << ": error: io failure" << endl;
+ fb.close ();
+ wait_process (pi, argv[0]);
+ return 1;
+ }
+
+ os << endl;
+ }
+ }
+
+ if (!ops.trace ())
+ {
+ // Add the standard epilogue at the end so that we see all
+ // the declarations.
+ //
+ os << "#line 1 \"<standard-odb-epilogue>\"" << endl;
+
+ // Includes for standard smart pointers. The Boost TR1 header
+ // may or may not delegate to the GCC implementation. In either
+ // case, the necessary declarations will be provided so we don't
+ // need to do anything.
+ //
+ os << "#include <memory>" << endl;
+
+ // Standard wrapper traits.
+ //
+ os << "#include <odb/wrapper-traits.hxx>" << endl;
+
+ // Standard pointer traits.
+ //
+ os << "#include <odb/pointer-traits.hxx>" << endl;
+
+ // Standard container traits.
+ //
+ os << "#include <odb/container-traits.hxx>" << endl;
+
+ // TR1 wrapper/pointer traits.
+ //
+ // @@ TMP: drop after 2.5.0.
+#if 0
+ if (ops.std () == cxx_version::cxx98)
+ os << endl
+ << "#ifndef BOOST_TR1_MEMORY_HPP_INCLUDED" << endl
+ << "# include <tr1/memory>" << endl
+ << "#endif" << endl
+ << "#include <odb/tr1/wrapper-traits.hxx>" << endl
+ << "#include <odb/tr1/pointer-traits.hxx>" << endl;
+#endif
+ }
+ }
+
+ // Filter the output stream looking for communication from the
+ // plugin.
+ //
+ {
+ __gnu_cxx::stdio_filebuf<char> fb (pi.in_ofd, ios_base::in);
+ istream is (&fb);
+
+ for (bool first (true); !is.eof (); )
+ {
+ string line;
+ getline (is, line);
+
+ if (is.fail () && !is.eof ())
+ {
+ e << argv[0] << ": error: io failure while parsing output"
+ << endl;
+ wait_process (pi, argv[0]);
+ return 1;
+ }
+
+ if (line.compare (0, 9, "odb:sloc:") == 0)
+ {
+ if (show_sloc || sloc_limit != 0)
+ {
+ size_t n;
+ istringstream is (string (line, 9, string::npos));
+
+ if (!(is >> n && is.eof ()))
+ {
+ e << argv[0] << ": error: invalid odb:sloc value" << endl;
+ wait_process (pi, argv[0]);
+ return 1;
+ }
+
+ sloc_total += n;
+ }
+
+ continue;
+ }
+
+ if (first)
+ first = false;
+ else
+ cout << endl;
+
+ cout << line;
+ }
+ }
+
+ if (!wait_process (pi, argv[0]))
+ return 1;
+ } // End input file loop.
+ } // End database loop.
+
+ // Handle SLOC.
+ //
+ if (show_sloc)
+ e << "total: " << sloc_total << endl;
+
+ if (sloc_limit != 0 && sloc_limit < sloc_total)
+ {
+ e << argv[0] << ": error: SLOC limit of " << sloc_limit << " lines " <<
+ "has been exceeded" << endl;
+
+ if (!show_sloc)
+ e << argv[0] << ": info: use the --show-sloc option to see the "
+ << "current total" << endl;
+
+ return 1;
+ }
+ }
+ catch (profile_failure const&)
+ {
+ // Diagnostics has already been issued.
+ //
+ return 1;
+ }
+ catch (process_failure const&)
+ {
+ // Diagnostics has already been issued.
+ //
+ return 1;
+ }
+ catch (invalid_path const& ex)
+ {
+ e << argv[0] << ": error: invalid path '" << ex.path () << "'" << endl;
+ return 1;
+ }
+ catch (cli::exception const& ex)
+ {
+ e << ex << endl;
+ return 1;
+ }
+}
+
+static inline string
+encode_plugin_flag (string const& k)
+{
+ return "-fplugin-arg-odb-" + k;
+}
+
+static string
+encode_plugin_option (string const& k, string const& cv)
+{
+ string o ("-fplugin-arg-odb-");
+ o += k;
+ o += '=';
+
+ if (!cv.empty ())
+ {
+ // A value cannot contain '='. Encode it as the backspace
+ // character.
+ //
+ string v (cv);
+ for (size_t i (0); i < v.size (); ++i)
+ if (v[i] == '=')
+ v[i] = '\b';
+
+ o += v;
+ }
+
+ return o;
+}
+
+static paths
+profile_paths (strings const& sargs, char const* name)
+{
+ // Copy some of the arguments from the passed list. We also need
+ // the g++ executable.
+ //
+ strings args;
+
+ args.push_back (sargs[0]);
+ args.push_back ("-v");
+ args.push_back ("-x");
+ args.push_back ("c++");
+ args.push_back ("-E");
+ args.push_back ("-P");
+
+ for (strings::const_iterator i (++sargs.begin ()), end (sargs.end ());
+ i != end; ++i)
+ {
+ string const& a (*i);
+
+ // -I
+ //
+ if (a.size () > 1 && a[0] == '-' && a[1] == 'I')
+ {
+ args.push_back (a);
+
+ if (a.size () == 2) // -I /path
+ {
+ args.push_back (*(++i));
+ }
+ }
+ // -framework
+ //
+ else if (a == "-isystem" ||
+ a == "-iquote" ||
+ a == "-idirafter" ||
+ a == "-isysroot" ||
+ a == "-framework")
+ {
+ args.push_back (a);
+
+ if (++i == end)
+ {
+ cerr << name << ": error: expected argument for the " << a
+ << " option" << endl;
+ throw profile_failure ();
+ }
+
+ args.push_back (*i);
+ }
+ // --sysroot
+ //
+ else if (a.compare (0, 10, "--sysroot=") == 0)
+ args.push_back (a);
+ // -std
+ //
+ else if (a.compare (0, 5, "-std=") == 0)
+ args.push_back (a);
+ }
+
+ // Create an execvp-compatible argument array.
+ //
+ vector<char const*> exec_args;
+
+ for (strings::const_iterator i (args.begin ()), end (args.end ());
+ i != end; ++i)
+ {
+ exec_args.push_back (i->c_str ());
+ }
+
+ exec_args.push_back ("-"); // Compile stdin.
+ exec_args.push_back (0);
+
+#ifdef _WIN32
+ string ops_file_arg;
+ auto_remove opt_file_rm (fixup_cmd_line (exec_args, 1, name, ops_file_arg));
+#endif
+
+ process_info pi (start_process (&exec_args[0], name, true));
+ close (pi.out_fd); // Preprocess empty file.
+
+ // Read the output into a temporary string stream. We don't parse
+ // it on the fly because we don't know whether it is the data or
+ // diagnostics until after the process is terminated and we get
+ // the exit code. We also cannot first wait for the exist code
+ // and then read the output because the process might get blocked.
+ //
+ stringstream ss;
+ {
+ __gnu_cxx::stdio_filebuf<char> fb (pi.in_efd, ios_base::in);
+ istream is (&fb);
+
+ for (bool first (true); !is.eof (); )
+ {
+ string line;
+ getline (is, line);
+
+ if (is.fail () && !is.eof ())
+ {
+ cerr << name << ": error: "
+ << "io failure while parsing profile paths" << endl;
+
+ wait_process (pi, name);
+ throw profile_failure ();
+ }
+
+ if (first)
+ first = false;
+ else
+ ss << endl;
+
+ ss << line;
+ }
+ }
+
+ if (!wait_process (pi, name))
+ {
+ // Things didn't go well and ss should contain the diagnostics.
+ // In case it is empty, issue our own.
+ //
+ if (!ss.str ().empty ())
+ cerr << ss.rdbuf ();
+ else
+ cerr << name << ": error: unable to extract profile paths" << endl;
+
+ throw profile_failure ();
+ }
+
+ // Parse the cached output.
+ //
+ paths r;
+ {
+ enum
+ {
+ read_prefix,
+ read_path,
+ read_suffix
+ } state = read_prefix;
+
+ while (!ss.eof () && state != read_suffix)
+ {
+ string line;
+ getline (ss, line);
+
+ if (ss.fail () && !ss.eof ())
+ {
+ cerr << name << ": error: "
+ << "io failure while parsing profile paths" << endl;
+ throw profile_failure ();
+ }
+
+ switch (state)
+ {
+ case read_prefix:
+ {
+ // The English string that we are looking for is "#include <...>
+ // search starts here:" but it can be translated. However, all
+ // the translations seems to have the "#include" and "<...>"
+ // parts, so we can search for those.
+ //
+ if (line.find ("#include") != string::npos &&
+ line.find ("<...>") != string::npos)
+ state = read_path;
+ break;
+ }
+ case read_path:
+ {
+ // The end of the list is terminated with the "End of search
+ // list." line, which, again, can be translated. Here we don't
+ // have any invariable parts that we can use. Instead, we will
+ // rely on the fact that all the paths are space-indented.
+ //
+ if (!line.empty () && line[0] != ' ')
+ state = read_suffix;
+ else
+ // Paths are indented with a space.
+ //
+ r.push_back (path (string (line, 1)));
+
+ break;
+ }
+ case read_suffix:
+ {
+ // We shouldn't get here.
+ break;
+ }
+ }
+ }
+
+ if (state != read_suffix)
+ {
+ cerr << name << ": error: unable to parse profile paths" << endl;
+ throw profile_failure ();
+ }
+ }
+
+ return r;
+}
+
+//
+// Path manipulation.
+//
+
+static string
+escape_path (string const& p)
+{
+ string r;
+
+ for (size_t i (0); i < p.size (); ++i)
+ {
+ if (p[i] == '\\')
+ r += "\\\\";
+ else
+ r += p[i];
+ }
+
+ return r;
+}
+
+static path
+path_search (path const& f)
+{
+ typedef path::traits traits;
+
+ // If there is a directory component in the file, then the PATH
+ // search does not apply.
+ //
+ if (!f.directory ().empty ())
+ return f;
+
+ string paths;
+
+ // If there is no PATH in environment then the default search
+ // path is the current directory.
+ //
+ if (char const* s = getenv ("PATH"))
+ paths = s;
+ else
+ paths = traits::path_separator;
+
+ // On Windows also check the current directory.
+ //
+#ifdef _WIN32
+ paths += traits::path_separator;
+#endif
+
+ struct stat info;
+
+ for (size_t b (0), e (paths.find (traits::path_separator));
+ b != string::npos;)
+ {
+ path p (string (paths, b, e != string::npos ? e - b : e));
+
+ // Empty path (i.e., a double colon or a colon at the beginning
+ // or end of PATH) means search in the current dirrectory.
+ //
+ if (p.empty ())
+ p = path (".");
+
+ path dp (p / f);
+
+ // Just check that the file exist without checking for permissions, etc.
+ //
+ if (stat (dp.string ().c_str (), &info) == 0 && S_ISREG (info.st_mode))
+ return dp;
+
+ // On Windows also try the path with the .exe extension.
+ //
+#ifdef _WIN32
+ dp += ".exe";
+
+ if (stat (dp.string ().c_str (), &info) == 0 && S_ISREG (info.st_mode))
+ return dp;
+#endif
+
+ if (e == string::npos)
+ b = e;
+ else
+ {
+ b = e + 1;
+ e = paths.find (traits::path_separator, b);
+ }
+ }
+
+ return path ();
+}
+
+static path
+driver_path (path const& drv)
+{
+ return drv.directory ().empty () ? path_search (drv) : drv;
+}
+
+#ifndef ODB_STATIC_PLUGIN
+static path
+plugin_path (path const& drv,
+#ifdef ODB_GCC_PLUGIN_DIR
+ string const& gxx)
+#else
+ string const&)
+#endif
+{
+#ifdef _WIN32
+ char const plugin_ext[] = ".dll";
+
+// While GCC 8 switched to using .dylib as the plugin extension, there is a
+// bug (86358) in the extension stripping code. So for now we use the .so
+// extension everywhere (see also buildfile if changing this).
+//
+//#elif defined(__APPLE__)
+// char const plugin_ext[] = ".dylib";
+#else
+ char const plugin_ext[] = ".so";
+#endif
+
+ // Figure out the plugin base name which is just the driver name (but
+ // without the .exe extension on Windows).
+ //
+#ifdef _WIN32
+ string b (drv.leaf ().base ().string ());
+#else
+ string b (drv.leaf ().string ());
+#endif
+
+ path dp (driver_path (drv));
+
+ if (dp.empty ())
+ {
+ cerr << drv << ": error: unable to resolve ODB compiler driver path" << endl;
+ return path ();
+ }
+
+ dp = dp.directory ();
+ struct stat info;
+
+ path pp;
+
+#ifdef ODB_GCC_PLUGIN_DIR
+ // Plugin should be installed into the GCC default plugin directory.
+ // Ideally, in this situation, we would simply pass the plugin name and
+ // let GCC append the correct directory. Unfortunately, this mechanism
+ // was only added in GCC 4.6 so in order to support 4.5 we will have to
+ // emulate it ourselves.
+ //
+ //@@ TMP: drop this after 2.5.0 since we no longer support GCC < 5.
+ //
+ {
+#if 1
+ // First get the default GCC plugin directory.
+ //
+ path d;
+ vector<char const*> exec_args;
+ exec_args.push_back (gxx.c_str ());
+ exec_args.push_back ("-print-file-name=plugin");
+ exec_args.push_back (0);
+
+ process_info pi (
+ start_process (
+ &exec_args[0], drv.string ().c_str (), false, true));
+ close (pi.out_fd);
+
+ // Read the path from stdout.
+ //
+ {
+ __gnu_cxx::stdio_filebuf<char> fb (pi.in_ofd, ios_base::in);
+ istream is (&fb);
+ string line;
+ getline (is, line);
+ d = path (line);
+ }
+
+ if (!wait_process (pi, drv.string ().c_str ()))
+ return path (); // Assume GCC issued some diagnostics.
+
+ if (d.string () == "plugin")
+ {
+ cerr << drv << ": error: unable to obtain GCC plugin directory" << endl;
+ return path ();
+ }
+
+ // See if the plugin is there.
+ //
+ pp = d / path (b + plugin_ext);
+ if (stat (pp.string ().c_str (), &info) != 0)
+ {
+ cerr << drv << ": error: no ODB plugin in GCC plugin directory '" <<
+ d << "'" << endl;
+ return path ();
+ }
+
+ return pp;
+#else
+ return path (b);
+#endif
+ }
+#elif defined (ODB_PLUGIN_PATH)
+ // If we were given a plugin path, use that.
+ //
+ {
+ string rp (ODB_PLUGIN_PATH);
+ if (!rp.empty ())
+ dp /= path (rp);
+
+ pp = dp / path (b + plugin_ext);
+
+ if (stat (pp.string ().c_str (), &info) != 0)
+ {
+ cerr << drv << ": error: no ODB plugin in '" << dp << "'" << endl;
+ return path ();
+ }
+
+ return pp;
+ }
+#endif
+
+ // Try in the current directory.
+ //
+ pp = dp / path (b + plugin_ext);
+ if (stat (pp.string ().c_str (), &info) != 0)
+ {
+ cerr << drv << ": error: unable to locate ODB plugin" << endl;
+ return path ();
+ }
+
+ return pp;
+}
+#endif
+
+//
+// Process manipulation.
+//
+
+#ifndef _WIN32
+
+static process_info
+start_process (char const* args[], char const* name, bool err, bool out)
+{
+ int out_fd[2];
+ int in_efd[2];
+ int in_ofd[2];
+
+ if (pipe (out_fd) == -1 ||
+ (err && pipe (in_efd) == -1) ||
+ (out && pipe (in_ofd) == -1))
+ {
+ char const* err (strerror (errno));
+ cerr << name << ": error: " << err << endl;
+ throw process_failure ();
+ }
+
+ pid_t pid (fork ());
+
+ if (pid == -1)
+ {
+ char const* err (strerror (errno));
+ cerr << name << ": error: " << err << endl;
+ throw process_failure ();
+ }
+
+ if (pid == 0)
+ {
+ // Child. Close the write end of the pipe and duplicate the read end
+ // to stdin. Then close the original read end descriptors.
+ //
+ if (close (out_fd[1]) == -1 ||
+ dup2 (out_fd[0], STDIN_FILENO) == -1 ||
+ close (out_fd[0]) == -1)
+ {
+ char const* err (strerror (errno));
+ cerr << name << ": error: " << err << endl;
+ throw process_failure ();
+ }
+
+ // Do the same for the stderr if requested.
+ //
+ if (err)
+ {
+ if (close (in_efd[0]) == -1 ||
+ dup2 (in_efd[1], STDERR_FILENO) == -1 ||
+ close (in_efd[1]) == -1)
+ {
+ char const* err (strerror (errno));
+ cerr << name << ": error: " << err << endl;
+ throw process_failure ();
+ }
+ }
+
+ // Do the same for the stdout if requested.
+ //
+ if (out)
+ {
+ if (close (in_ofd[0]) == -1 ||
+ dup2 (in_ofd[1], STDOUT_FILENO) == -1 ||
+ close (in_ofd[1]) == -1)
+ {
+ char const* err (strerror (errno));
+ cerr << name << ": error: " << err << endl;
+ throw process_failure ();
+ }
+ }
+
+ if (execvp (args[0], const_cast<char**> (&args[0])) == -1)
+ {
+ char const* err (strerror (errno));
+ cerr << args[0] << ": error: " << err << endl;
+ throw process_failure ();
+ }
+ }
+ else
+ {
+ // Parent. Close the other ends of the pipes.
+ //
+ if (close (out_fd[0]) == -1 ||
+ (err && close (in_efd[1]) == -1) ||
+ (out && close (in_ofd[1]) == -1))
+ {
+ char const* err (strerror (errno));
+ cerr << name << ": error: " << err << endl;
+ throw process_failure ();
+ }
+ }
+
+ process_info r;
+ r.id = pid;
+ r.out_fd = out_fd[1];
+ r.in_efd = err ? in_efd[0] : 0;
+ r.in_ofd = out ? in_ofd[0] : 0;
+ return r;
+}
+
+static bool
+wait_process (process_info pi, char const* name)
+{
+ int status;
+
+ if (waitpid (pi.id, &status, 0) == -1)
+ {
+ char const* err (strerror (errno));
+ cerr << name << ": error: " << err << endl;
+ throw process_failure ();
+ }
+
+ return WIFEXITED (status) && WEXITSTATUS (status) == 0;
+}
+
+#else // _WIN32
+
+static void
+print_error (char const* name)
+{
+ LPTSTR msg;
+ DWORD e (GetLastError());
+
+ if (!FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ 0,
+ e,
+ MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &msg,
+ 0,
+ 0))
+ {
+ cerr << name << ": error: unknown error code " << e << endl;
+ return;
+ }
+
+ cerr << name << ": error: " << msg << endl;
+ LocalFree (msg);
+}
+
+// On Windows we need to protect command line arguments with spaces using
+// quotes. Since there could be actual quotes in the value, we need to escape
+// them.
+//
+static void
+append_quoted (string& cmd_line, const char* ca)
+{
+ string a (ca);
+ bool quote (a.find (' ') != string::npos);
+
+ if (quote)
+ cmd_line += '"';
+
+ for (size_t i (0); i < a.size (); ++i)
+ {
+ if (a[i] == '"')
+ cmd_line += "\\\"";
+ else
+ cmd_line += a[i];
+ }
+
+ if (quote)
+ cmd_line += '"';
+}
+
+// Deal with Windows command line length limit.
+//
+// The best approach seems to be passing the command line in an "options file"
+// ("response file" in Microsoft's terminology).
+//
+static auto_remove
+fixup_cmd_line (vector<const char*>& args,
+ size_t start,
+ const char* name,
+ string& arg)
+{
+ // Calculate the would-be command line length similar to how start_process()
+ // implementation does it.
+ //
+ size_t n (0);
+ string s;
+ for (const char* a: args)
+ {
+ if (a != nullptr)
+ {
+ if (n != 0)
+ n++; // For the space separator.
+
+ s.clear ();
+ append_quoted (s, a);
+ n += s.size ();
+ }
+ }
+
+ if (n <= 32766) // 32768 - "Unicode terminating null character".
+ return auto_remove ();
+
+ // Create the temporary file.
+ //
+ char d[MAX_PATH + 1], p[MAX_PATH + 1];
+ if (GetTempPathA (sizeof (d), d) == 0 ||
+ GetTempFileNameA (d, "odb-options-", 0, p) == 0)
+ {
+ print_error (name);
+ throw process_failure ();
+ }
+
+ auto_remove rm = auto_remove (path (p));
+ try
+ {
+ ofstream ofs (p);
+ if (!ofs.is_open ())
+ {
+ cerr << name << ": error: unable to open '" << p << "' in write mode"
+ << endl;
+ throw process_failure ();
+ }
+
+ ofs.exceptions (ios_base::badbit | ios_base::failbit);
+
+ // Write the arguments to file.
+ //
+ // The format is a space-separated list of potentially-quoted arguments
+ // with support for backslash-escaping.
+ //
+ string b;
+ for (size_t i (start), n (args.size () - 1); i != n; ++i)
+ {
+ const char* a (args[i]);
+
+ // We will most likely have backslashes so just do it.
+ //
+ {
+ for (b.clear (); *a != '\0'; ++a)
+ {
+ if (*a != '\\')
+ b += *a;
+ else
+ b += "\\\\";
+ }
+
+ a = b.c_str ();
+ }
+
+ s.clear ();
+ append_quoted (s, a);
+ ofs << (i != start ? " " : "") << s;
+ }
+
+ ofs << '\n';
+ ofs.close ();
+ }
+ catch (const ios_base::failure&)
+ {
+ cerr << name << ": error: unable to write to '" << p << "'" << endl;
+ throw process_failure ();
+ }
+
+ // Rewrite the command line.
+ //
+ arg = string ("@") + p;
+ args.resize (start);
+ args.push_back (arg.c_str());
+ args.push_back (nullptr);
+
+ return rm;
+}
+
+static process_info
+start_process (char const* args[], char const* name, bool err, bool out)
+{
+ HANDLE out_h[2];
+ HANDLE in_eh[2];
+ HANDLE in_oh[2];
+ SECURITY_ATTRIBUTES sa;
+
+ sa.nLength = sizeof (SECURITY_ATTRIBUTES);
+ sa.bInheritHandle = true;
+ sa.lpSecurityDescriptor = 0;
+
+ if (!CreatePipe (&out_h[0], &out_h[1], &sa, 0) ||
+ !SetHandleInformation (out_h[1], HANDLE_FLAG_INHERIT, 0))
+ {
+ print_error (name);
+ throw process_failure ();
+ }
+
+ if (err)
+ {
+ if (!CreatePipe (&in_eh[0], &in_eh[1], &sa, 0) ||
+ !SetHandleInformation (in_eh[0], HANDLE_FLAG_INHERIT, 0))
+ {
+ print_error (name);
+ throw process_failure ();
+ }
+ }
+
+ if (out)
+ {
+ if (!CreatePipe (&in_oh[0], &in_oh[1], &sa, 0) ||
+ !SetHandleInformation (in_oh[0], HANDLE_FLAG_INHERIT, 0))
+ {
+ print_error (name);
+ throw process_failure ();
+ }
+ }
+
+ // Create the process.
+ //
+ path file (args[0]);
+
+ // Do PATH search.
+ //
+ if (file.directory ().empty ())
+ file = path_search (file);
+ else if (file.base () == file) // No extension
+ file += ".exe"; // Assume .exe.
+
+ if (file.empty ())
+ {
+ cerr << args[0] << ": error: file not found" << endl;
+ throw process_failure ();
+ }
+
+ // Serialize the arguments to string.
+ //
+ string cmd_line;
+
+ for (char const** p (args); *p != 0; ++p)
+ {
+ if (p != args)
+ cmd_line += ' ';
+
+ append_quoted (cmd_line, *p);
+ }
+
+ // Prepare other info.
+ //
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+
+ memset (&si, 0, sizeof (STARTUPINFO));
+ memset (&pi, 0, sizeof (PROCESS_INFORMATION));
+
+ si.cb = sizeof(STARTUPINFO);
+
+ if (err)
+ si.hStdError = in_eh[1];
+ else
+ si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
+
+ if (out)
+ si.hStdOutput = in_oh[1];
+ else
+ si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
+
+ si.hStdInput = out_h[0];
+ si.dwFlags |= STARTF_USESTDHANDLES;
+
+ if (!CreateProcess (
+ file.string ().c_str (),
+ const_cast<char*> (cmd_line.c_str ()),
+ 0, // Process security attributes.
+ 0, // Primary thread security attributes.
+ true, // Inherit handles.
+ 0, // Creation flags.
+ 0, // Use our environment.
+ 0, // Use our current directory.
+ &si,
+ &pi))
+ {
+ print_error (name);
+ throw process_failure ();
+ }
+
+ CloseHandle (pi.hThread);
+ CloseHandle (out_h[0]);
+
+ if (err)
+ CloseHandle (in_eh[1]);
+
+ if (out)
+ CloseHandle (in_oh[1]);
+
+ process_info r;
+ r.id = pi.hProcess;
+ r.out_fd = _open_osfhandle ((intptr_t) (out_h[1]), 0);
+
+ if (r.out_fd == -1)
+ {
+ cerr << name << ": error: unable to obtain C file handle" << endl;
+ throw process_failure ();
+ }
+
+ if (err)
+ {
+ // Pass _O_TEXT to get newline translation.
+ //
+ r.in_efd = _open_osfhandle ((intptr_t) (in_eh[0]), _O_TEXT);
+
+ if (r.in_efd == -1)
+ {
+ cerr << name << ": error: unable to obtain C file handle" << endl;
+ throw process_failure ();
+ }
+ }
+ else
+ r.in_efd = 0;
+
+ if (out)
+ {
+ // Pass _O_TEXT to get newline translation.
+ //
+ r.in_ofd = _open_osfhandle ((intptr_t) (in_oh[0]), _O_TEXT);
+
+ if (r.in_ofd == -1)
+ {
+ cerr << name << ": error: unable to obtain C file handle" << endl;
+ throw process_failure ();
+ }
+ }
+ else
+ r.in_ofd = 0;
+
+ return r;
+}
+
+static bool
+wait_process (process_info pi, char const* name)
+{
+ DWORD status;
+
+ if (WaitForSingleObject (pi.id, INFINITE) != WAIT_OBJECT_0 ||
+ !GetExitCodeProcess (pi.id, &status))
+ {
+ print_error (name);
+ throw process_failure ();
+ }
+
+ CloseHandle (pi.id);
+ return status == 0;
+}
+
+#endif // _WIN32
diff --git a/odb/odb/option-functions.cxx b/odb/odb/option-functions.cxx
new file mode 100644
index 0000000..7eda934
--- /dev/null
+++ b/odb/odb/option-functions.cxx
@@ -0,0 +1,132 @@
+// file : odb/option-functions.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <set>
+#include <utility> // std::make_pair()
+
+#include <odb/option-functions.hxx>
+
+using namespace std;
+
+void
+process_options (options& o)
+{
+ database db (o.database ()[0]);
+
+ // If --generate-schema-only was specified, then set --generate-schema
+ // as well.
+ //
+ if (o.generate_schema_only ())
+ o.generate_schema (true);
+
+ // If --warn-hard was specified, then set both --warn-hard-{add,delete}.
+ //
+ if (o.warn_hard ())
+ {
+ o.warn_hard_add (true);
+ o.warn_hard_delete (true);
+ }
+
+ // Set the default schema format depending on the database.
+ //
+ if (o.generate_schema () && o.schema_format ()[db].empty ())
+ {
+ set<schema_format>& f (o.schema_format ()[db]);
+
+ switch (db)
+ {
+ case database::common:
+ {
+ break; // No schema for common.
+ }
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ {
+ f.insert (schema_format::sql);
+ break;
+ }
+ case database::sqlite:
+ {
+ f.insert (schema_format::embedded);
+ break;
+ }
+ }
+ }
+
+ // Set default --schema-version-table value.
+ //
+ if (o.schema_version_table ().count (db) == 0)
+ o.schema_version_table ()[db] = "schema_version";
+
+ // Set default --schema-name value.
+ //
+ if (o.schema_name ().count (db) == 0)
+ o.schema_name ()[db] = "";
+
+ // Set default --fkeys-deferrable-mode value.
+ //
+ if (o.fkeys_deferrable_mode ().count (db) == 0)
+ o.fkeys_deferrable_mode ()[db] = deferrable::deferred;
+
+ // Set default --{export,extern}-symbol values.
+ //
+ if (o.export_symbol ().count (db) == 0)
+ o.export_symbol ()[db] = "";
+
+ if (o.extern_symbol ().count (db) == 0)
+ o.extern_symbol ()[db] = "";
+
+ // Set default --*-file-suffix values.
+ //
+ {
+ database cm (database::common);
+
+ o.odb_file_suffix ().insert (make_pair (cm, string ("-odb")));
+ o.sql_file_suffix ().insert (make_pair (cm, string ("")));
+ o.schema_file_suffix ().insert (make_pair (cm, string ("-schema")));
+ o.changelog_file_suffix ().insert (make_pair (cm, string ("")));
+ }
+
+ if (o.multi_database () == multi_database::disabled)
+ {
+ o.odb_file_suffix ().insert (make_pair (db, string ("-odb")));
+ o.sql_file_suffix ().insert (make_pair (db, string ("")));
+ o.schema_file_suffix ().insert (make_pair (db, string ("-schema")));
+ o.changelog_file_suffix ().insert (make_pair (db, string ("")));
+ }
+ else
+ {
+ o.odb_file_suffix ().insert (make_pair (db, "-odb-" + db.string ()));
+ o.sql_file_suffix ().insert (make_pair (db, "-" + db.string ()));
+ o.schema_file_suffix ().insert (make_pair (db, "-schema-" + db.string ()));
+ o.changelog_file_suffix ().insert (make_pair (db, '-' + db.string ()));
+ }
+
+ // Set default --default-database value.
+ //
+ if (!o.default_database_specified ())
+ {
+ switch (o.multi_database ())
+ {
+ case multi_database::disabled:
+ {
+ o.default_database (db);
+ o.default_database_specified (true);
+ break;
+ }
+ case multi_database::dynamic:
+ {
+ o.default_database (database::common);
+ o.default_database_specified (true);
+ break;
+ }
+ case multi_database::static_:
+ {
+ // No default database unless explicitly specified.
+ break;
+ }
+ }
+ }
+}
diff --git a/odb/odb/option-functions.hxx b/odb/odb/option-functions.hxx
new file mode 100644
index 0000000..a4b072c
--- /dev/null
+++ b/odb/odb/option-functions.hxx
@@ -0,0 +1,12 @@
+// file : odb/option-functions.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_OPTION_FUNCTIONS_HXX
+#define ODB_OPTION_FUNCTIONS_HXX
+
+#include <odb/options.hxx>
+
+void
+process_options (options&);
+
+#endif // ODB_OPTION_FUNCTIONS_HXX
diff --git a/odb/odb/option-parsers.hxx b/odb/odb/option-parsers.hxx
new file mode 100644
index 0000000..2daa6eb
--- /dev/null
+++ b/odb/odb/option-parsers.hxx
@@ -0,0 +1,199 @@
+// file : odb/option-parsers.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_OPTION_PARSERS_HXX
+#define ODB_OPTION_PARSERS_HXX
+
+#include <vector>
+#include <sstream>
+
+#include <odb/option-types.hxx>
+#include <odb/options.hxx>
+
+namespace cli
+{
+ // Return true if there is a database prefix.
+ //
+ template <typename V>
+ bool
+ parse_option_value (std::string const& o, std::string const& ov,
+ database& k, V& v)
+ {
+ bool r (false);
+ std::string::size_type p = ov.find (':');
+
+ std::string vstr;
+ if (p != std::string::npos)
+ {
+ std::string kstr (ov, 0, p);
+
+ // See if this prefix resolves to the database name. If not,
+ // assume there is no prefix.
+ //
+ std::istringstream ks (kstr);
+
+ if (ks >> k && ks.eof ())
+ {
+ r = true;
+ vstr.assign (ov, p + 1, std::string::npos);
+ }
+ }
+
+ if (!r)
+ vstr = ov; // Use the whole value.
+
+ if (!vstr.empty ())
+ {
+ std::istringstream vs (vstr);
+
+ if (!(vs >> v && vs.eof ()))
+ throw invalid_value (o, ov);
+ }
+ else
+ v = V ();
+
+ return r;
+ }
+
+ // Specialization for std::string.
+ //
+ bool
+ parse_option_value (std::string const&, std::string const& ov,
+ database& k, std::string& v)
+ {
+ bool r (false);
+ std::string::size_type p = ov.find (':');
+
+ if (p != std::string::npos)
+ {
+ std::string kstr (ov, 0, p);
+
+ // See if this prefix resolves to the database name. If not,
+ // assume there is no prefix.
+ //
+ std::istringstream ks (kstr);
+
+ if (ks >> k && ks.eof ())
+ {
+ r = true;
+ v.assign (ov, p + 1, std::string::npos);
+ }
+ }
+
+ if (!r)
+ v = ov; // Use the whole value.
+
+ return r;
+ }
+
+ template <typename V>
+ struct parser<database_map<V> >
+ {
+ typedef database_map<V> map;
+
+ static void
+ parse (map& m, bool& xs, scanner& s)
+ {
+ xs = true;
+ std::string o (s.next ());
+
+ if (s.more ())
+ {
+ database k;
+ V v;
+
+ if (parse_option_value (o, s.next (), k, v))
+ m[k] = v; // Override any old value.
+ else
+ {
+ // No database prefix is specified which means it applies to
+ // all the databases. We also don't want to override database-
+ // specific values, so use insert().
+ //
+ m.insert (typename map::value_type (database::common, v));
+ m.insert (typename map::value_type (database::mssql, v));
+ m.insert (typename map::value_type (database::mysql, v));
+ m.insert (typename map::value_type (database::oracle, v));
+ m.insert (typename map::value_type (database::pgsql, v));
+ m.insert (typename map::value_type (database::sqlite, v));
+ }
+ }
+ else
+ throw missing_value (o);
+ }
+ };
+
+ template <typename V>
+ struct parser<database_map<std::vector<V> > >
+ {
+ typedef database_map<std::vector<V> > map;
+
+ static void
+ parse (map& m, bool& xs, scanner& s)
+ {
+ xs = true;
+ std::string o (s.next ());
+
+ if (s.more ())
+ {
+ database k;
+ V v;
+
+ if (parse_option_value (o, s.next (), k, v))
+ m[k].push_back (v);
+ else
+ {
+ // No database prefix is specified which means it applies to
+ // all the databases.
+ //
+ m[database::common].push_back (v);
+ m[database::mssql].push_back (v);
+ m[database::mysql].push_back (v);
+ m[database::oracle].push_back (v);
+ m[database::pgsql].push_back (v);
+ m[database::sqlite].push_back (v);
+ }
+ }
+ else
+ throw missing_value (o);
+ }
+ };
+
+ template <typename V>
+ struct parser<database_map<std::set<V> > >
+ {
+ typedef database_map<std::set<V> > map;
+
+ static void
+ parse (map& m, bool& xs, scanner& s)
+ {
+ xs = true;
+ std::string o (s.next ());
+
+ if (s.more ())
+ {
+ database k;
+ V v;
+
+ if (parse_option_value (o, s.next (), k, v))
+ m[k].insert (v);
+ else
+ {
+ // No database prefix is specified which means it applies to
+ // all the databases.
+ //
+ m[database::common].insert (v);
+ m[database::mssql].insert (v);
+ m[database::mysql].insert (v);
+ m[database::oracle].insert (v);
+ m[database::pgsql].insert (v);
+ m[database::sqlite].insert (v);
+ }
+ }
+ else
+ throw missing_value (o);
+ }
+ };
+}
+
+#endif // ODB_OPTION_PARSERS_HXX
diff --git a/odb/odb/option-types.cxx b/odb/odb/option-types.cxx
new file mode 100644
index 0000000..c4a030b
--- /dev/null
+++ b/odb/odb/option-types.cxx
@@ -0,0 +1,352 @@
+// file : odb/option-types.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <istream>
+#include <ostream>
+#include <algorithm> // std::lower_bound
+
+#include <odb/option-types.hxx>
+
+using namespace std;
+
+//
+// cxx_version
+//
+
+static const char* cxx_version_[] =
+{
+ "c++98",
+ "c++11",
+ "c++14",
+ "c++17",
+ "c++20"
+};
+
+string cxx_version::
+string () const
+{
+ return cxx_version_[v_];
+}
+
+istream&
+operator>> (istream& is, cxx_version& v)
+{
+ string s;
+ is >> s;
+
+ if (!is.fail ())
+ {
+ if (s == "c++98")
+ v = cxx_version::cxx98;
+ else if (s == "c++11")
+ v = cxx_version::cxx11;
+ else if (s == "c++14")
+ v = cxx_version::cxx14;
+ else if (s == "c++17")
+ v = cxx_version::cxx17;
+ else if (s == "c++20")
+ v = cxx_version::cxx20;
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+}
+
+//
+// database
+//
+
+static const char* database_[] =
+{
+ "common",
+ "mssql",
+ "mysql",
+ "oracle",
+ "pgsql",
+ "sqlite"
+};
+
+static const char* database_name_[] =
+{
+ "Common Interface",
+ "SQL Server",
+ "MySQL",
+ "Oracle",
+ "PostgreSQL",
+ "SQLite"
+};
+
+string database::
+string () const
+{
+ return database_[v_];
+}
+
+string database::
+name () const
+{
+ return database_name_[v_];
+}
+
+istream&
+operator>> (istream& is, database& db)
+{
+ string s;
+ is >> s;
+
+ if (!is.fail ())
+ {
+ const char** e (database_ + sizeof (database_) / sizeof (char*));
+ const char** i (lower_bound (database_, e, s));
+
+ if (i != e && *i == s)
+ db = database::value (i - database_);
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+}
+
+ostream&
+operator<< (ostream& os, database db)
+{
+ return os << db.string ();
+}
+
+//
+// multi_database
+//
+
+static const char* multi_database_[] =
+{
+ "dynamic",
+ "static",
+ "disabled"
+};
+
+string multi_database::
+string () const
+{
+ return multi_database_[v_];
+}
+
+istream&
+operator>> (istream& is, multi_database& db)
+{
+ string s;
+ is >> s;
+
+ if (!is.fail ())
+ {
+ const char** e (
+ multi_database_ + sizeof (multi_database_) / sizeof (char*) - 1);
+ const char** i (lower_bound (multi_database_, e, s));
+
+ if (i != e && *i == s)
+ db = multi_database::value (i - multi_database_);
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+}
+
+ostream&
+operator<< (ostream& os, multi_database db)
+{
+ return os << db.string ();
+}
+
+//
+// schema_format
+//
+
+static const char* schema_format_[] =
+{
+ "embedded",
+ "separate",
+ "sql"
+};
+
+string schema_format::
+string () const
+{
+ return schema_format_[v_];
+}
+
+istream&
+operator>> (istream& is, schema_format& sf)
+{
+ string s;
+ is >> s;
+
+ if (!is.fail ())
+ {
+ const char** e (schema_format_ + sizeof (schema_format_) / sizeof (char*));
+ const char** i (lower_bound (schema_format_, e, s));
+
+ if (i != e && *i == s)
+ sf = schema_format::value (i - schema_format_);
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+}
+
+ostream&
+operator<< (ostream& os, schema_format sf)
+{
+ return os << sf.string ();
+}
+
+//
+// name_case
+//
+
+istream&
+operator>> (istream& is, name_case& v)
+{
+ string s;
+ is >> s;
+
+ if (!is.fail ())
+ {
+ if (s == "upper")
+ v = name_case::upper;
+ else if (s == "lower")
+ v = name_case::lower;
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+}
+
+//
+// pgsql_version
+//
+
+istream&
+operator>> (istream& is, pgsql_version& v)
+{
+ unsigned short major, minor;
+
+ // Extract the major version.
+ //
+ is >> major;
+
+ if (!is.fail ())
+ {
+ // Extract the decimal point.
+ //
+ char p;
+ is >> p;
+
+ if (!is.fail () && p == '.')
+ {
+ // Extract the minor version.
+ //
+ is >> minor;
+
+ if (!is.fail ())
+ v = pgsql_version (major, minor);
+ }
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+}
+
+ostream&
+operator<< (ostream& os, pgsql_version v)
+{
+ return os << v.ver_major () << '.' << v.ver_minor ();
+}
+
+//
+// oracle_version
+//
+
+istream&
+operator>> (istream& is, oracle_version& v)
+{
+ unsigned short major, minor;
+
+ // Extract the major version.
+ //
+ is >> major;
+
+ if (!is.fail ())
+ {
+ // Extract the decimal point.
+ //
+ char p;
+ is >> p;
+
+ if (!is.fail () && p == '.')
+ {
+ // Extract the minor version.
+ //
+ is >> minor;
+
+ if (!is.fail ())
+ v = oracle_version (major, minor);
+ }
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+}
+
+ostream&
+operator<< (ostream& os, oracle_version v)
+{
+ return os << v.ver_major () << '.' << v.ver_minor ();
+}
+
+//
+// mssql_version
+//
+
+istream&
+operator>> (istream& is, mssql_version& v)
+{
+ unsigned short major, minor;
+
+ // Extract the major version.
+ //
+ is >> major;
+
+ if (!is.fail ())
+ {
+ // Extract the decimal point.
+ //
+ char p;
+ is >> p;
+
+ if (!is.fail () && p == '.')
+ {
+ // Extract the minor version.
+ //
+ is >> minor;
+
+ if (!is.fail ())
+ v = mssql_version (major, minor);
+ }
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+}
+
+ostream&
+operator<< (ostream& os, mssql_version v)
+{
+ return os << v.ver_major () << '.' << v.ver_minor ();
+}
diff --git a/odb/odb/option-types.hxx b/odb/odb/option-types.hxx
new file mode 100644
index 0000000..869fc83
--- /dev/null
+++ b/odb/odb/option-types.hxx
@@ -0,0 +1,391 @@
+// file : odb/option-types.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_OPTION_TYPES_HXX
+#define ODB_OPTION_TYPES_HXX
+
+#include <map>
+#include <iosfwd>
+#include <string>
+#include <cassert>
+
+#include <odb/semantics/relational/name.hxx>
+#include <odb/semantics/relational/deferrable.hxx>
+
+using semantics::relational::qname;
+using semantics::relational::deferrable;
+
+struct cxx_version
+{
+ enum value
+ {
+ cxx98,
+ cxx11,
+ cxx14,
+ cxx17,
+ cxx20
+ };
+
+ cxx_version (value v = value (0)) : v_ (v) {}
+ operator value () const {return v_;}
+
+ std::string
+ string () const;
+
+private:
+ value v_;
+};
+
+std::istream&
+operator>> (std::istream&, cxx_version&);
+
+//
+//
+struct database
+{
+ enum value
+ {
+ // Keep in alphabetic order.
+ //
+ common,
+ mssql,
+ mysql,
+ oracle,
+ pgsql,
+ sqlite
+ };
+
+ database (value v = value (0)) : v_ (v) {}
+ operator value () const {return v_;}
+
+ std::string
+ string () const;
+
+ // Full name (e.g., PostgreSQL).
+ //
+ std::string
+ name () const;
+
+private:
+ value v_;
+};
+
+std::istream&
+operator>> (std::istream&, database&);
+
+std::ostream&
+operator<< (std::ostream&, database);
+
+//
+//
+template <typename V>
+struct database_map: std::map<database, V>
+{
+ typedef std::map<database, V> base_type;
+
+ using base_type::operator[];
+
+ V const&
+ operator[] (database const& k) const
+ {
+ typename base_type::const_iterator i (this->find (k));
+ assert (i != this->end ());
+ return i->second;
+ }
+};
+
+//
+//
+struct multi_database
+{
+ enum value
+ {
+ // Keep in alphabetic order.
+ //
+ dynamic,
+ static_,
+ disabled // Special value.
+ };
+
+ multi_database (value v = disabled) : v_ (v) {}
+ operator value () const {return v_;}
+
+ std::string
+ string () const;
+
+private:
+ value v_;
+};
+
+std::istream&
+operator>> (std::istream&, multi_database&);
+
+std::ostream&
+operator<< (std::ostream&, multi_database);
+
+//
+//
+struct schema_format
+{
+ enum value
+ {
+ // Keep in alphabetic order.
+ //
+ embedded,
+ separate,
+ sql
+ };
+
+ schema_format (value v = value (0)) : v_ (v) {}
+ operator value () const {return v_;}
+
+ std::string
+ string () const;
+
+private:
+ value v_;
+};
+
+std::istream&
+operator>> (std::istream&, schema_format&);
+
+std::ostream&
+operator<< (std::ostream&, schema_format);
+
+//
+//
+struct name_case
+{
+ enum value
+ {
+ upper,
+ lower
+ };
+
+ name_case (value v = value (0)) : v_ (v) {}
+ operator value () const {return v_;}
+
+private:
+ value v_;
+};
+
+std::istream&
+operator>> (std::istream&, name_case&);
+
+//
+//
+struct pgsql_version
+{
+ pgsql_version (unsigned short major, unsigned short minor)
+ : major_ (major), minor_ (minor)
+ {
+ }
+
+ unsigned short
+ ver_major () const
+ {
+ return major_;
+ }
+
+ unsigned short
+ ver_minor () const
+ {
+ return minor_;
+ }
+
+private:
+ unsigned short major_;
+ unsigned short minor_;
+};
+
+inline bool
+operator== (const pgsql_version& x, const pgsql_version& y)
+{
+ return x.ver_major () == y.ver_major ();
+}
+
+inline bool
+operator!= (const pgsql_version& x, const pgsql_version& y)
+{
+ return !(x == y);
+}
+
+inline bool
+operator< (const pgsql_version& x, const pgsql_version& y)
+{
+ return x.ver_major () < y.ver_major () ||
+ (x.ver_major () == y.ver_major () &&
+ x.ver_minor () < y.ver_minor ());
+}
+
+inline bool
+operator> (const pgsql_version& x, const pgsql_version& y)
+{
+ return x.ver_major () > y.ver_major () ||
+ (x.ver_major () == y.ver_major () &&
+ x.ver_minor () > y.ver_minor ());
+}
+
+inline bool
+operator<= (const pgsql_version& x, const pgsql_version& y)
+{
+ return !(x > y);
+}
+
+inline bool
+operator>= (const pgsql_version& x, const pgsql_version& y)
+{
+ return !(x < y);
+}
+
+std::istream&
+operator>> (std::istream&, pgsql_version&);
+
+std::ostream&
+operator<< (std::ostream&, pgsql_version);
+
+//
+//
+struct oracle_version
+{
+ oracle_version (unsigned short major, unsigned short minor)
+ : major_ (major), minor_ (minor)
+ {
+ }
+
+ unsigned short
+ ver_major () const
+ {
+ return major_;
+ }
+
+ unsigned short
+ ver_minor () const
+ {
+ return minor_;
+ }
+
+private:
+ unsigned short major_;
+ unsigned short minor_;
+};
+
+inline bool
+operator== (const oracle_version& x, const oracle_version& y)
+{
+ return x.ver_major () == y.ver_major ();
+}
+
+inline bool
+operator!= (const oracle_version& x, const oracle_version& y)
+{
+ return !(x == y);
+}
+
+inline bool
+operator< (const oracle_version& x, const oracle_version& y)
+{
+ return x.ver_major () < y.ver_major () ||
+ (x.ver_major () == y.ver_major () &&
+ x.ver_minor () < y.ver_minor ());
+}
+
+inline bool
+operator> (const oracle_version& x, const oracle_version& y)
+{
+ return x.ver_major () > y.ver_major () ||
+ (x.ver_major () == y.ver_major () &&
+ x.ver_minor () > y.ver_minor ());
+}
+
+inline bool
+operator<= (const oracle_version& x, const oracle_version& y)
+{
+ return !(x > y);
+}
+
+inline bool
+operator>= (const oracle_version& x, const oracle_version& y)
+{
+ return !(x < y);
+}
+
+std::istream&
+operator>> (std::istream&, oracle_version&);
+
+std::ostream&
+operator<< (std::ostream&, oracle_version);
+
+//
+//
+struct mssql_version
+{
+ mssql_version (unsigned short major, unsigned short minor)
+ : major_ (major), minor_ (minor)
+ {
+ }
+
+ unsigned short
+ ver_major () const
+ {
+ return major_;
+ }
+
+ unsigned short
+ ver_minor () const
+ {
+ return minor_;
+ }
+
+private:
+ unsigned short major_;
+ unsigned short minor_;
+};
+
+inline bool
+operator== (const mssql_version& x, const mssql_version& y)
+{
+ return x.ver_major () == y.ver_major ();
+}
+
+inline bool
+operator!= (const mssql_version& x, const mssql_version& y)
+{
+ return !(x == y);
+}
+
+inline bool
+operator< (const mssql_version& x, const mssql_version& y)
+{
+ return x.ver_major () < y.ver_major () ||
+ (x.ver_major () == y.ver_major () &&
+ x.ver_minor () < y.ver_minor ());
+}
+
+inline bool
+operator> (const mssql_version& x, const mssql_version& y)
+{
+ return x.ver_major () > y.ver_major () ||
+ (x.ver_major () == y.ver_major () &&
+ x.ver_minor () > y.ver_minor ());
+}
+
+inline bool
+operator<= (const mssql_version& x, const mssql_version& y)
+{
+ return !(x > y);
+}
+
+inline bool
+operator>= (const mssql_version& x, const mssql_version& y)
+{
+ return !(x < y);
+}
+
+std::istream&
+operator>> (std::istream&, mssql_version&);
+
+std::ostream&
+operator<< (std::ostream&, mssql_version);
+
+#endif // ODB_OPTION_TYPES_HXX
diff --git a/odb/odb/options.cli b/odb/odb/options.cli
new file mode 100644
index 0000000..17ee438
--- /dev/null
+++ b/odb/odb/options.cli
@@ -0,0 +1,1086 @@
+// file : odb/options.cli
+// license : GNU GPL v3; see accompanying LICENSE file
+
+include <set>;
+include <vector>;
+include <string>;
+include <cstddef>;
+include <cstdint>;
+
+include <odb/option-types.hxx>;
+
+class options
+{
+ //
+ // Wrapper options. These are not passed to the plugin.
+ //
+
+ std::uint64_t --build2-metadata; // Leave undocumented/hidden.
+
+ bool --help {"Print usage information and exit."};
+ bool --version {"Print version and exit."};
+
+ //
+ // C++ preprocessor options. Also not passed to the plugin.
+ //
+ std::vector<std::string> -I
+ {
+ "<dir>",
+ "Add <dir> to the beginning of the list of directories to be searched
+ for included header files."
+ };
+
+ std::vector<std::string> -D
+ {
+ "<name>[=<def>]",
+ "Define macro <name> with definition <def>. If definition is omitted,
+ define <name> to be 1."
+ };
+
+ std::vector<std::string> -U
+ {
+ "<name>",
+ "Cancel any previous definitions of macro <name>, either built-in or
+ provided with the \cb{-D} option."
+ };
+
+ //
+ // Plugin options.
+ //
+ std::vector< ::database> --database | -d
+ {
+ "<db>",
+ "Generate code for the <db> database. Valid values are \cb{mssql},
+ \cb{mysql}, \cb{oracle}, \cb{pgsql}, \cb{sqlite}, and \cb{common}
+ (multi-database mode only)."
+ };
+
+ ::multi_database --multi-database | -m = ::multi_database::disabled
+ {
+ "<type>",
+ "Enable multi-database support and specify its type. Valid values
+ for this option are \cb{static} and \cb{dynamic}.
+
+ In the multi-database mode, options that determine the kind (for
+ example, \cb{--schema-format}), names (for example,
+ \cb{--odb-file-suffix}), 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, \cb{mysql:value}.
+ This restricts the value of such an option to only apply to
+ generated files corresponding to this database."
+ };
+
+ ::database --default-database
+ {
+ "<db>",
+ "When static multi-database support is used, specify the database that
+ should be made the default. When dynamic multi-database support is used,
+ \cb{common} is always made the default database."
+ };
+
+ bool --generate-query | -q
+ {
+ "Generate query support code. Without this support you cannot use views
+ and can only load objects via their ids."
+ };
+
+ bool --generate-prepared
+ {
+ "Generate prepared query execution support code."
+ };
+
+ bool --omit-unprepared
+ {
+ "Omit un-prepared (once-off) query execution support code."
+ };
+
+ bool --generate-session | -e
+ {
+ "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 \cb{db session} pragma."
+ };
+
+ bool --generate-schema | -s
+ {
+ "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 (\cb{--database} 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 \cb{--schema-format} 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 \cb{--suppress-migration} option."
+ };
+
+ bool --generate-schema-only
+ {
+ "Generate only the database schema. Note that this option is only valid
+ when generating schema as a standalone SQL file (see \cb{--schema-format}
+ for details)."
+ };
+
+ bool --suppress-migration
+ {
+ "Suppress the generation of database schema migration statements."
+ };
+
+ bool --suppress-schema-version
+ {
+ "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
+ \cb{odb::database::schema_version()} function."
+ };
+
+ database_map<qname> --schema-version-table
+ {
+ "<name>",
+ "Specify the alternative schema version table name instead of the default
+ \cb{schema_version}. If you specify this option then you are also
+ expected to manually specify the schema version table name at runtime
+ using the \cb{odb::database::schema_version_table()} function. The table
+ name can be qualified."
+ };
+
+ database_map<std::set< ::schema_format> > --schema-format
+ {
+ "<format>",
+ "Generate the database schema in the specified format. Pass \cb{sql} as
+ <format> to generate the database schema as a standalone SQL file or
+ pass \cb{embedded} to embed the schema into the generated C++ code.
+ The \cb{separate} value is similar to \cb{embedded} except the schema
+ creation code is generated into a separate C++ file (\cb{name-schema.cxx}
+ 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."
+ };
+
+ bool --omit-drop
+ {
+ "Omit \cb{DROP} statements from the generated database schema."
+ };
+
+ bool --omit-create
+ {
+ "Omit \cb{CREATE} statements from the generated database schema."
+ };
+
+ database_map<std::string> --schema-name
+ {
+ "<name>",
+ "Use <name> 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 \cb{--schema} option. If
+ this option is not specified, the empty name, which is the default
+ schema name, is used."
+ };
+
+ database_map<deferrable> --fkeys-deferrable-mode
+ {
+ "<m>",
+ "Use constraint checking mode <m> in foreign keys generated for object
+ relationships. Valid values for this option are \cb{not_deferrable},
+ \cb{immediate}, and \cb{deferred} (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 \cb{not_deferrable} or \cb{immediate}
+ mode, then the order in which you persist, update, and erase objects
+ within a transaction becomes important."
+ };
+
+ std::string --default-pointer = "*"
+ {
+ "<ptr>",
+ "Use <ptr> as the default pointer for persistent objects and views.
+ Objects and views that do not have a pointer assigned with the
+ \cb{db pointer} pragma will use this pointer by default. The value
+ of this option can be '\cb{*}' which denotes the raw pointer and is
+ the default, or qualified name of a smart pointer class template,
+ for example, \cb{std::shared_ptr}. 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
+ \cb{std::shared_ptr<object>}. 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 \cb{<memory>} 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 \cb{--hxx-prologue} option to add the necessary \cb{#include}
+ directive to the generated code."
+ };
+
+ std::string --session-type = "odb::session"
+ {
+ "<type>",
+ "Use <type> as the alternative session type instead of the default
+ \cb{odb::session}. 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 \cb{--hxx-prologue*} options."
+ };
+
+ // The following option is "fake" in that it is actually handled by
+ // argv_file_scanner. We have it here to get the documentation.
+ //
+ std::string --profile | -p
+ {
+ "<name>",
+ "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 \cb{-}\ci{database}\cb{.options} suffix to <name>, where
+ \ci{database} is the database name as specified with the
+ \cb{--database} 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 \cb{.options} suffix.
+
+ The profile options files are searched for in the same set of
+ directories as C++ headers included with the \cb{#include <...>}
+ directive (built-in paths plus those specified with the \cb{-I}
+ options). The options file is first searched for in the directory
+ itself and then in its \cb{odb/} subdirectory.
+
+ For the format of the options file refer to the \cb{--options-file}
+ option below. You can repeat this option to specify more than one
+ profile."
+ };
+
+ bool --at-once
+ {
+ "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 \cb{--input-name} 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
+ \cb{#include} directive resolution."
+ };
+
+ database_map<qname> --schema
+ {
+ "<schema>",
+ "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
+ \cb{--schema-name} option."
+ };
+
+ // Export control.
+ //
+ database_map<std::string> --export-symbol
+ {
+ "<symbol>",
+ "Insert <symbol> in places where DLL export/import control statements
+ (\cb{__declspec(dllexport/dllimport)}) are necessary. See also the
+ \cb{--extern-symbol} option below."
+ };
+
+ database_map<std::string> --extern-symbol
+ {
+ "<symbol>",
+ "If <symbol> is defined, insert it in places where a template
+ instantiation must be declared \cb{extern}. This option is normally
+ used together with \cb{--export-symbol} when both multi-database
+ support and queries are enabled."
+ };
+
+ // Language.
+ //
+ // @@ TODO: perhaps we should switch to latest to match how we build
+ // runtime by default?
+ //
+ cxx_version --std = cxx_version::cxx98
+ {
+ "<version>",
+ "Specify the C++ standard that should be used during compilation.
+ Valid values are \cb{c++98} (default), \cb{c++11}, \cb{c++14},
+ \cb{c++17}, and \cb{c++20}."
+ };
+
+ // Diagnostics.
+ //
+ bool --warn-hard-add
+ {
+ "Warn about hard-added data members."
+ };
+
+ bool --warn-hard-delete
+ {
+ "Warn about hard-deleted data members and persistent classes."
+ };
+
+ bool --warn-hard
+ {
+ "Warn about both hard-added and hard-deleted data members and
+ persistent classes."
+ };
+
+ // Output.
+ //
+ std::string --output-dir | -o
+ {
+ "<dir>",
+ "Write the generated files to <dir> instead of the current directory."
+ };
+
+ std::string --input-name
+ {
+ "<name>",
+ "Use <name> instead of the input file to derive the names of the
+ generated files. If the \cb{--at-once} option is specified, then
+ the directory part of <name> is used as the location of the
+ combined file. Refer to the \cb{--at-once} option for details."
+ };
+
+ database_map<std::string> --changelog
+ {
+ "<file>",
+ "Read/write changelog from/to <file> 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 \cb{--output-dir} 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 \cb{--changelog-in} and \cb{--changelog-out}
+ options to specify different input and output chaneglog files."
+ };
+
+ database_map<std::string> --changelog-in
+ {
+ "<file>",
+ "Read changelog from <file> instead of the default changelog file. If
+ this option is specified, then you must also specify the output
+ chanegelog file with \cb{--changelog-out}."
+ };
+
+ database_map<std::string> --changelog-out
+ {
+ "<file>",
+ "Write changelog to <file> instead of the default changelog file. If
+ this option is specified, then you must also specify the input
+ chanegelog file with \cb{--changelog-in}."
+ };
+
+ database_map<std::string> --changelog-dir
+ {
+ "<dir>",
+ "Use <dir> instead of the input file directory as the changelog file
+ directory. This directory is also added to changelog files specified
+ with the \cb{--changelog}, \cb{--changelog-in}, and \cb{--changelog-in}
+ options unless they are absolute paths."
+ };
+
+ bool --init-changelog
+ {
+ "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."
+ };
+
+ database_map<std::string> --odb-file-suffix
+ {
+ "<suffix>",
+ "Use <suffix> to construct the names of the generated C++ files. In
+ the single-database mode the default value for this option is \cb{-odb}.
+ In the multi-database mode it is \cb{-odb} for the files corresponding
+ to the \cb{common} database and \c{\b{-odb-}\i{db}} (where \ci{db} is
+ the database name) for other databases."
+ };
+
+ database_map<std::string> --sql-file-suffix
+ {
+ "<suffix>",
+ "Use <suffix> 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
+ \c{\b{-}\i{db}} (where \ci{db} is the database name)."
+ };
+
+ database_map<std::string> --schema-file-suffix
+ {
+ "<suffix>",
+ "Use <suffix> to construct the name of the generated schema C++ source
+ file. In the single-database mode the default value for this option is
+ \cb{-schema}. In the multi-database mode it is \c{\b{-schema-}\i{db}}
+ (where \ci{db} is the database name). See the \cb{--schema-format}
+ option for details."
+ };
+
+ database_map<std::string> --changelog-file-suffix
+ {
+ "<sfx>",
+ "Use <sfx> 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
+ \c{\b{-}\i{db}} (where \ci{db} is the database name)."
+ };
+
+ std::string --hxx-suffix = ".hxx"
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{.hxx} to construct the name of
+ the generated C++ header file."
+ };
+
+ std::string --ixx-suffix = ".ixx"
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{.ixx} to construct the name of
+ the generated C++ inline file."
+ };
+
+ std::string --cxx-suffix = ".cxx"
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{.cxx} to construct the name of
+ the generated C++ source file."
+ };
+
+ std::string --sql-suffix = ".sql"
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{.sql} to construct the name of
+ the generated database schema file."
+ };
+
+ std::string --changelog-suffix = ".xml"
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{.xml} to construct the name of
+ the changelog file."
+ };
+
+ // Prologues.
+ //
+ database_map<std::vector<std::string> > --hxx-prologue
+ {
+ "<text>",
+ "Insert <text> at the beginning of the generated C++ header file."
+ };
+
+ database_map<std::vector<std::string> > --ixx-prologue
+ {
+ "<text>",
+ "Insert <text> at the beginning of the generated C++ inline file."
+ };
+
+ database_map<std::vector<std::string> > --cxx-prologue
+ {
+ "<text>",
+ "Insert <text> at the beginning of the generated C++ source file."
+ };
+
+ database_map<std::vector<std::string> > --schema-prologue
+ {
+ "<text>",
+ "Insert <text> at the beginning of the generated schema C++ source file."
+ };
+
+ database_map<std::vector<std::string> > --sql-prologue
+ {
+ "<text>",
+ "Insert <text> at the beginning of the generated database schema file."
+ };
+
+ database_map<std::vector<std::string> > --migration-prologue
+ {
+ "<text>",
+ "Insert <text> at the beginning of the generated database migration file."
+ };
+
+ // Interludes.
+ //
+ database_map<std::vector<std::string> > --sql-interlude
+ {
+ "<text>",
+ "Insert <text> after all the \cb{DROP} and before any \cb{CREATE}
+ statements in the generated database schema file."
+ };
+
+ // Epilogues.
+ //
+ database_map<std::vector<std::string> > --hxx-epilogue
+ {
+ "<text>",
+ "Insert <text> at the end of the generated C++ header file."
+ };
+
+ database_map<std::vector<std::string> > --ixx-epilogue
+ {
+ "<text>",
+ "Insert <text> at the end of the generated C++ inline file."
+ };
+
+ database_map<std::vector<std::string> > --cxx-epilogue
+ {
+ "<text>",
+ "Insert <text> at the end of the generated C++ source file."
+ };
+
+ database_map<std::vector<std::string> > --schema-epilogue
+ {
+ "<text>",
+ "Insert <text> at the end of the generated schema C++ source file."
+ };
+
+ database_map<std::vector<std::string> > --sql-epilogue
+ {
+ "<text>",
+ "Insert <text> at the end of the generated database schema file."
+ };
+
+ database_map<std::vector<std::string> > --migration-epilogue
+ {
+ "<text>",
+ "Insert <text> at the end of the generated database migration file."
+ };
+
+ // Prologue files.
+ //
+ database_map<std::vector<std::string> > --hxx-prologue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the beginning of the generated C++
+ header file."
+ };
+
+ database_map<std::vector<std::string> > --ixx-prologue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the beginning of the generated C++
+ inline file."
+ };
+
+ database_map<std::vector<std::string> > --cxx-prologue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the beginning of the generated C++
+ source file."
+ };
+
+ database_map<std::vector<std::string> > --schema-prologue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the beginning of the generated schema
+ C++ source file."
+ };
+
+ database_map<std::vector<std::string> > --sql-prologue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the beginning of the generated
+ database schema file."
+ };
+
+ database_map<std::vector<std::string> > --migration-prologue-file
+ {
+ "<f>",
+ "Insert the content of file <f> at the beginning of the generated database
+ migration file."
+ };
+
+ // Interlude files.
+ //
+ database_map<std::vector<std::string> > --sql-interlude-file
+ {
+ "<file>",
+ "Insert the content of <file> after all the \cb{DROP} and before any
+ \cb{CREATE} statements in the generated database schema file."
+ };
+
+ // Epilogue files.
+ //
+ database_map<std::vector<std::string> > --hxx-epilogue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the end of the generated C++ header
+ file."
+ };
+
+ database_map<std::vector<std::string> > --ixx-epilogue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the end of the generated C++ inline
+ file."
+ };
+
+ database_map<std::vector<std::string> > --cxx-epilogue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the end of the generated C++ source
+ file."
+ };
+
+ database_map<std::vector<std::string> > --schema-epilogue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the end of the generated schema C++
+ source file."
+ };
+
+ database_map<std::vector<std::string> > --sql-epilogue-file
+ {
+ "<file>",
+ "Insert the content of <file> at the end of the generated database
+ schema file."
+ };
+
+ database_map<std::vector<std::string> > --migration-epilogue-file
+ {
+ "<f>",
+ "Insert the content of file <f> at the end of the generated database
+ migration file."
+ };
+
+ // ODB compilation prologue/epilogue.
+ //
+ database_map<std::vector<std::string> > --odb-prologue
+ {
+ "<text>",
+ "Compile <text> before the input header file. This option allows you
+ to add additional declarations, such as custom traits specializations,
+ to the ODB compilation process."
+ };
+
+ database_map<std::vector<std::string> > --odb-prologue-file
+ {
+ "<file>",
+ "Compile <file> contents before the input header file. Prologue files
+ are compiled after all the prologue text fragments (\cb{--odb-prologue}
+ option)."
+ };
+
+ database_map<std::vector<std::string> > --odb-epilogue
+ {
+ "<text>",
+ "Compile <text> after the input header file. This option allows you
+ to add additional declarations, such as custom traits specializations,
+ to the ODB compilation process."
+ };
+
+ database_map<std::vector<std::string> > --odb-epilogue-file
+ {
+ "<file>",
+ "Compile <file> contents after the input header file. Epilogue files
+ are compiled after all the epilogue text fragments (\cb{--odb-epilogue}
+ option)."
+ };
+
+ // SQL names.
+ //
+ database_map<std::string> --table-prefix
+ {
+ "<prefix>",
+ "Add <prefix> 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 \cb{db table} and \cb{db index}
+ 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."
+ };
+
+ database_map<std::string> --index-suffix
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{_i} 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."
+ };
+
+ database_map<std::string> --fkey-suffix
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{_fk} 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."
+ };
+
+ database_map<std::string> --sequence-suffix
+ {
+ "<suffix>",
+ "Use <suffix> instead of the default \cb{_seq} 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."
+ };
+
+ database_map<name_case> --sql-name-case
+ {
+ "<case>",
+ "Convert all automatically-derived SQL names to upper or lower case.
+ Valid values for this option are \cb{upper} and \cb{lower}."
+ };
+
+ database_map<std::vector<std::string> > --table-regex
+ {
+ "<regex>",
+ "Add <regex> to the list of regular expressions that is used to
+ transform automatically-derived table names. See the SQL NAME
+ TRANSFORMATIONS section below for details."
+ };
+
+ database_map<std::vector<std::string> > --column-regex
+ {
+ "<regex>",
+ "Add <regex> to the list of regular expressions that is used to
+ transform automatically-derived column names. See the SQL NAME
+ TRANSFORMATIONS section below for details."
+ };
+
+ database_map<std::vector<std::string> > --index-regex
+ {
+ "<regex>",
+ "Add <regex> to the list of regular expressions that is used to
+ transform automatically-derived index names. See the SQL NAME
+ TRANSFORMATIONS section below for details."
+ };
+
+ database_map<std::vector<std::string> > --fkey-regex
+ {
+ "<regex>",
+ "Add <regex> 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."
+ };
+
+ database_map<std::vector<std::string> > --sequence-regex
+ {
+ "<regex>",
+ "Add <regex> to the list of regular expressions that is used to
+ transform automatically-derived sequence names. See the SQL NAME
+ TRANSFORMATIONS section below for details."
+ };
+
+ database_map<std::vector<std::string> > --statement-regex
+ {
+ "<regex>",
+ "Add <regex> 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."
+ };
+
+ database_map<std::vector<std::string> > --sql-name-regex
+ {
+ "<regex>",
+ "Add <regex> 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."
+ };
+
+ bool --sql-name-regex-trace
+ {
+ "Trace the process of applying regular expressions specified with the
+ SQL name \cb{--*-regex} options. Use this option to find out why your
+ regular expressions don't do what you expected them to do."
+ };
+
+ // Accessor/modifier options.
+ //
+ std::vector<std::string> --accessor-regex
+ {
+ "<regex>",
+ "Add <regex> 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 \c{\b{/}\i{pattern}\b{/}\i{replacement}\b{/}}.
+ Any character can be used as a delimiter instead of '\cb{/}' and the
+ delimiter can be escaped inside \ci{pattern} and \ci{replacement}
+ with a backslash (\cb{\\}). 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 \i{public name} which is obtained by
+ removing the common member name decorations, such as leading and
+ trailing underscores, the \cb{m_} prefix, etc. The ODB compiler also
+ includes a number of built-in expressions for commonly used accessor
+ names, such as \cb{get_foo}, \cb{getFoo}, \cb{getfoo}, and just
+ \cb{foo}. The built-in expressions are tried last.
+
+ As an example, the following expression transforms data members with
+ public names in the form \cb{foo} to accessor names in the form
+ \cb{GetFoo}:
+
+ \cb{/(.+)/Get\\u$1/}
+
+ See also the REGEX AND SHELL QUOTING section below."
+ };
+
+ bool --accessor-regex-trace
+ {
+ "Trace the process of applying regular expressions specified with the
+ \cb{--accessor-regex} option. Use this option to find out why your
+ regular expressions don't do what you expected them to do."
+ };
+
+ std::vector<std::string> --modifier-regex
+ {
+ "<regex>",
+ "Add <regex> 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 \c{\b{/}\i{pattern}\b{/}\i{replacement}\b{/}}.
+ Any character can be used as a delimiter instead of '\cb{/}' and the
+ delimiter can be escaped inside \ci{pattern} and \ci{replacement}
+ with a backslash (\cb{\\}). 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 \i{public name} which is obtained by
+ removing the common member name decorations, such as leading and
+ trailing underscores, the \cb{m_} prefix, etc. The ODB compiler also
+ includes a number of built-in expressions for commonly used modifier
+ names, such as \cb{set_foo}, \cb{setFoo}, \cb{setfoo}, and just
+ \cb{foo}. The built-in expressions are tried last.
+
+ As an example, the following expression transforms data members with
+ public names in the form \cb{foo} to modifier names in the form
+ \cb{SetFoo}:
+
+ \cb{/(.+)/Set\\u$1/}
+
+ See also the REGEX AND SHELL QUOTING section below."
+ };
+
+ bool --modifier-regex-trace
+ {
+ "Trace the process of applying regular expressions specified with the
+ \cb{--modifier-regex} option. Use this option to find out why your
+ regular expressions don't do what you expected them to do."
+ };
+
+ // Include options.
+ //
+ bool --include-with-brackets
+ {
+ "Use angle brackets (<>) instead of quotes (\"\") in the generated
+ \cb{#include} directives."
+ };
+
+ std::string --include-prefix
+ {
+ "<prefix>",
+ "Add <prefix> to the generated \cb{#include} directive paths."
+ };
+
+ std::vector<std::string> --include-regex
+ {
+ "<regex>",
+ "Add <regex> to the list of regular expressions used to transform
+ generated \cb{#include} directive paths. The argument to this option
+ is a Perl-like regular expression in the form
+ \c{\b{/}\i{pattern}\b{/}\i{replacement}\b{/}}. Any character can be
+ used as a delimiter instead of '\cb{/}' and the delimiter can be escaped
+ inside \ci{pattern} and \ci{replacement} with a backslash (\cb{\\}).
+ 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 \cb{foo/bar-odb.h} to paths in the form
+ \cb{foo/generated/bar-odb.h}:
+
+ \cb{%foo/(.+)-odb.h%foo/generated/$1-odb.h%}
+
+ See also the REGEX AND SHELL QUOTING section below."
+ };
+
+ bool --include-regex-trace
+ {
+ "Trace the process of applying regular expressions specified with the
+ \cb{--include-regex} option. Use this option to find out why your
+ regular expressions don't do what you expected them to do."
+ };
+
+ std::string --guard-prefix
+ {
+ "<prefix>",
+ "Add <prefix> 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."
+ };
+
+ bool --show-sloc
+ {
+ "Print the number of generated physical source lines of code (SLOC)."
+ };
+
+ std::size_t --sloc-limit
+ {
+ "<num>",
+ "Check that the number of generated physical source lines of code (SLOC)
+ does not exceed <num>."
+ };
+
+ // The following option is "fake" in that it is actually handled by
+ // argv_file_scanner. We have it here to get the documentation.
+ //
+ std::string --options-file
+ {
+ "<file>",
+ "Read additional options from <file>. Each option should appear on a
+ separate line optionally followed by space or equal sign (\cb{=}) and an
+ option value. Empty lines and lines starting with \cb{#} are ignored.
+ Option values can be enclosed in double (\cb{\"}) or single (\cb{'})
+ 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 \cb{'\"x\"'}.
+ 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 \cb{--options-file} option is specified except that
+ the shell escaping and quoting is not required. Repeat this option
+ to specify more than one options file."
+ };
+
+ std::vector<std::string> -x
+ {
+ "<option>",
+ "Pass <option> to the underlying C++ compiler (\cb{g++}). The <option>
+ value that doesn't start with '\cb{-}' is considered the \cb{g++}
+ executable name."
+ };
+
+ bool -v {"Print the commands executed to run the stages of compilation."};
+
+ bool --trace {"Trace the compilation process."};
+
+ //
+ // MySQL-specific options.
+ //
+
+ std::string --mysql-engine = "InnoDB"
+ {
+ "<engine>",
+ "Use <engine> instead of the default \cb{InnoDB} 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 \cb{default} as the value for this
+ option."
+ };
+
+ //
+ // SQLite-specific options.
+ //
+
+ bool --sqlite-override-null
+ {
+ "Make all columns in the generated database schema allow \cb{NULL}
+ values. This is primarily useful in schema migration since SQLite
+ does not support dropping of columns. By making all columns \cb{NULL}
+ we can later \"delete\" them by setting their values to \cb{NULL}.
+ Note that this option overrides even the \cb{not_null} pragma."
+ };
+
+ bool --sqlite-lax-auto-id
+ {
+ "Do not force monotonically increasing automatically-assigned
+ object ids. In this mode the generated database schema omits the
+ \cb{AUTOINCREMENT} 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."
+ };
+
+ //
+ // PostgreSQL-specific options.
+ //
+
+ ::pgsql_version --pgsql-server-version (7, 4)
+ {
+ "<ver>",
+ "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 \c{\i{major}\b{.}\i{minor}}
+ form, for example, \cb{9.1}. If this option is not specified, then
+ \cb{7.4} or later is assumed."
+ };
+
+ //
+ // Oracle-specific options.
+ //
+
+ ::oracle_version --oracle-client-version (10, 1)
+ {
+ "<ver>",
+ "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 \c{\i{major}\b{.}\i{minor}} form,
+ for example, \cb{11.2}. If this option is not specified, then
+ \cb{10.1} or later is assumed."
+ };
+
+ bool --oracle-warn-truncation
+ {
+ "Warn about SQL names that are longer than 30 characters and are
+ therefore truncated. Note that during database schema generation
+ (\cb{--generate-schema}) ODB detects when such truncations lead
+ to name conflicts and issues diagnostics even without this option
+ specified."
+ };
+
+ //
+ // SQL Server-specific options.
+ //
+
+ ::mssql_version --mssql-server-version (10, 0)
+ {
+ "<ver>",
+ "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 \c{\i{major}\b{.}\i{minor}}
+ form, for example, \cb{9.0} (SQL Server 2005), \cb{10.5} (2008R2), or
+ \cb{11.0} (2012). If this option is not specified, then \cb{10.0} (SQL
+ Server 2008) or later is assumed."
+ };
+
+ unsigned int --mssql-short-limit = 1024
+ {
+ "<size>",
+ "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}, otherwise it is \i{long
+ data}. 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 \cb{SQLGetData()}/\cb{SQLPutData()} 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."
+ };
+};
diff --git a/odb/odb/parser.cxx b/odb/odb/parser.cxx
new file mode 100644
index 0000000..c026808
--- /dev/null
+++ b/odb/odb/parser.cxx
@@ -0,0 +1,2403 @@
+// file : odb/parser.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx> // Keep it first.
+
+#include <set>
+#include <map>
+#include <vector>
+#include <string>
+#include <cassert>
+#include <sstream>
+#include <iostream>
+
+#include <odb/diagnostics.hxx>
+#include <odb/parser.hxx>
+#include <odb/semantics.hxx>
+
+using namespace std;
+using namespace semantics;
+
+class parser::impl
+{
+public:
+ typedef parser::failed failed;
+
+ impl (options const&, loc_pragmas&, ns_loc_pragmas&, decl_pragmas&);
+
+ unique_ptr<unit>
+ parse (tree global_scope, path const& main_file);
+
+private:
+ typedef semantics::access access;
+
+ // Extended GCC tree declaration that is either a GCC tree
+ // declaration, a virtual declaration, or a pragma. If it is
+ // a pragma, then the assoc flag indicated whether this pragma
+ // has been associated with a declaration. Otherwise, the assoc
+ // flag indicates whether pragmas have been associated with this
+ // declaration (we use this to ignore certain declarations for
+ // pragma association purposes, e.g., the anonymous type in
+ // struct {...} m_).
+ //
+ struct tree_decl
+ {
+ tree decl;
+ virt_declaration const* vdecl;
+ pragma const* prag;
+ mutable bool assoc; // Allow modification via std::set iterator.
+
+ tree_decl (tree d): decl (d), vdecl (0), prag (0), assoc (false) {}
+ tree_decl (virt_declaration const& d)
+ : decl (0), vdecl (&d), prag (0), assoc (false) {}
+ tree_decl (pragma const& p)
+ : decl (0), vdecl (0), prag (&p), assoc (false) {}
+
+ bool
+ operator< (tree_decl const& y) const;
+ };
+
+ typedef multiset<tree_decl> decl_set;
+
+private:
+ void
+ collect (tree ns);
+
+ void
+ emit ();
+
+ // Emit a type declaration. This is either a named class-type definition/
+ // declaration or a typedef. In the former case the function returns the
+ // newly created type node. In the latter case it returns 0.
+ //
+ type*
+ emit_type_decl (tree);
+
+ // Emit a template declaration.
+ //
+ void
+ emit_template_decl (tree);
+
+ class_template&
+ emit_class_template (tree, bool stub = false);
+
+ union_template&
+ emit_union_template (tree, bool stub = false);
+
+ template <typename T>
+ T&
+ emit_class (tree, path const& f, size_t l, size_t c, bool stub = false);
+
+ template <typename T>
+ T&
+ emit_union (tree, path const& f, size_t l, size_t c, bool stub = false);
+
+ // Access is not used when creating a stub.
+ //
+ enum_&
+ emit_enum (tree,
+ access,
+ path const& f,
+ size_t l,
+ size_t c,
+ bool stub = false);
+
+ // Create new or find existing semantic graph type.
+ //
+ type&
+ emit_type (tree, access, path const& f, size_t l, size_t c);
+
+ type&
+ create_type (tree, access, path const& f, size_t l, size_t c);
+
+ string
+ emit_type_name (tree, bool direct = true);
+
+
+ //
+ // Pragma handling.
+ //
+ void
+ add_pragma (node&, pragma const&);
+
+ // Process positioned and named pragmas.
+ //
+ void
+ process_pragmas (declaration const&,
+ node&,
+ string const& name,
+ decl_set::const_iterator begin,
+ decl_set::const_iterator cur,
+ decl_set::const_iterator end);
+
+ // Process named pragmas only.
+ //
+ void
+ process_named_pragmas (declaration const&, node&);
+
+ void
+ diagnose_unassoc_pragmas (decl_set const&);
+
+ // Return declaration's fully-qualified scope name (e.g., ::foo::bar).
+ //
+ string
+ fq_scope (tree);
+
+ // Return declaration's access.
+ //
+ access
+ decl_access (tree decl)
+ {
+ // Note that TREE_PUBLIC() returns something other than what we need.
+ //
+ if (TREE_PRIVATE (decl))
+ return access::private_;
+
+ if (TREE_PROTECTED (decl))
+ return access::protected_;
+
+ return access::public_;
+ }
+
+ //
+ //
+ template <typename T>
+ void
+ define_fund (tree);
+
+private:
+ options const& ops_;
+ loc_pragmas& loc_pragmas_;
+ ns_loc_pragmas& ns_loc_pragmas_;
+ decl_pragmas& decl_pragmas_;
+
+ bool trace;
+ ostream& ts;
+
+ unit* unit_;
+ scope* scope_;
+ vector<scope*> class_scopes_; // Current hierarchy of class-like scopes.
+ size_t error_;
+
+ decl_set decls_;
+
+ typedef map<location_t, tree> decl_map;
+ decl_map all_decls_;
+};
+
+bool parser::impl::tree_decl::
+operator< (tree_decl const& y) const
+{
+ location_t xl, yl;
+ int xb (0), yb (0);
+
+ if (decl != 0)
+ xl = real_source_location (decl);
+ else if (vdecl != 0)
+ {
+ xl = vdecl->ord;
+ xb = vdecl->ord_bias;
+ }
+ else
+ xl = prag->loc;
+
+ if (y.decl != 0)
+ yl = real_source_location (y.decl);
+ else if (y.vdecl != 0)
+ {
+ yl = y.vdecl->ord;
+ yb = y.vdecl->ord_bias;
+ }
+ else
+ yl = y.prag->loc;
+
+ // If both are virtual and at the same location, use the definition
+ // location to order them.
+ //
+ if (vdecl != 0 && y.vdecl != 0 && xl == yl && xb == yb)
+ {
+ xl = vdecl->loc;
+ yl = y.vdecl->loc;
+ }
+
+ return xl < yl || (xl == yl && xb < yb);
+}
+
+//
+// Function templates.
+//
+
+template <typename T>
+void parser::impl::
+define_fund (tree t)
+{
+ t = TYPE_MAIN_VARIANT (t);
+ char const* name (IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (t))));
+
+ T& node (unit_->new_fund_node<T> (t));
+ unit_->new_edge<defines> (*scope_, node, name);
+ unit_->insert (t, node);
+
+ process_named_pragmas (t, node);
+}
+
+template <typename T>
+T& parser::impl::
+emit_class (tree c, path const& file, size_t line, size_t clmn, bool stub)
+{
+ c = TYPE_MAIN_VARIANT (c);
+
+ // See if there is a stub already for this type.
+ //
+ T* c_node (0);
+
+ if (node* n = unit_->find (c))
+ {
+ c_node = &dynamic_cast<T&> (*n);
+ }
+ else
+ {
+ c_node = &unit_->new_node<T> (file, line, clmn, c);
+ unit_->insert (c, *c_node);
+ }
+
+ if (stub || !COMPLETE_TYPE_P (c))
+ return *c_node;
+
+ // Note: "include" the base classes into the class scope (see comment for
+ // self-typedefs in emit_type_decl()).
+ //
+ class_scopes_.push_back (c_node);
+
+ // Traverse base information.
+ //
+ tree bis (TYPE_BINFO (c));
+ size_t n (bis ? BINFO_N_BASE_BINFOS (bis) : 0);
+
+ for (size_t i (0); i < n; i++)
+ {
+ tree bi (BINFO_BASE_BINFO (bis, i));
+ access a (access::public_);
+
+ if (BINFO_BASE_ACCESSES (bis))
+ {
+ tree ac (BINFO_BASE_ACCESS (bis, i));
+
+ if (ac == NULL_TREE || ac == access_public_node)
+ {
+ a = access::public_;
+ }
+ else if (ac == access_protected_node)
+ {
+ a = access::protected_;
+ }
+ else
+ {
+ assert (ac == access_private_node);
+ a = access::private_;
+ }
+ }
+
+ bool virt (BINFO_VIRTUAL_P (bi));
+ tree base (TYPE_MAIN_VARIANT (BINFO_TYPE (bi)));
+
+ // Find the corresponding graph node. If we cannot find one then
+ // the base is a template instantiation since an ordinary class
+ // has to be defined (complete) in order to be a base.
+ //
+ class_* b_node (0);
+ string name;
+
+ if (node* n = unit_->find (base))
+ {
+ b_node = &dynamic_cast<class_&> (*n);
+
+ if (trace)
+ name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (base)));
+ }
+ else
+ {
+ // Use public access for a template instantiation in the inheritance
+ // declaration.
+ //
+ b_node = &dynamic_cast<class_&> (
+ emit_type (base, access::public_, file, line, clmn));
+
+ if (trace)
+ name = emit_type_name (base);
+ }
+
+ unit_->new_edge<inherits> (*c_node, *b_node, a, virt);
+
+ if (trace)
+ ts << "\t" << a.string () << (virt ? " virtual" : "") << " base "
+ << name << " (" << static_cast<type*> (b_node) << ")" << endl;
+ }
+
+ // Collect member declarations so that we can traverse them in
+ // the source code order.
+ //
+ decl_set decls;
+
+ for (tree d (TYPE_FIELDS (c)); d != NULL_TREE; d = TREE_CHAIN (d))
+ {
+ switch (TREE_CODE (d))
+ {
+ case TYPE_DECL:
+ {
+ if (!DECL_SELF_REFERENCE_P (d))
+ decls.insert (d);
+ break;
+ }
+ case TEMPLATE_DECL:
+ {
+ if (DECL_CLASS_TEMPLATE_P (d))
+ decls.insert (d);
+ break;
+ }
+ case FIELD_DECL:
+ {
+ if (!DECL_ARTIFICIAL (d))
+ decls.insert (d);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Add virtual declarations if any.
+ //
+ {
+ virt_declarations::const_iterator i (virt_declarations_.find (c));
+
+ if (i != virt_declarations_.end ())
+ decls.insert (i->second.begin (), i->second.end ());
+ }
+
+ // Add location pragmas if any.
+ //
+ {
+ loc_pragmas::const_iterator i (loc_pragmas_.find (c));
+
+ if (i != loc_pragmas_.end ())
+ decls.insert (i->second.begin (), i->second.end ());
+ }
+
+ scope* prev_scope (scope_);
+ scope_ = c_node;
+
+ for (decl_set::const_iterator b (decls.begin ()), i (b), e (decls.end ());
+ i != e; ++i)
+ {
+ // Skip pragmas.
+ //
+ if (i->prag != 0)
+ continue;
+
+ // Handle virtual declarations.
+ //
+ if (i->vdecl != 0)
+ {
+ virt_declaration const& vd (*i->vdecl);
+
+ switch (vd.tree_code)
+ {
+ case FIELD_DECL:
+ {
+ // First check that it doesn't conflict with any of the real
+ // data members defined in this class.
+ //
+ tree d (
+ lookup_qualified_name (
+ c, get_identifier (vd.name.c_str ()), false, false));
+
+ if (d != error_mark_node && TREE_CODE (d) == FIELD_DECL)
+ {
+ error (vd.loc) << "virtual data member declaration '" << vd.name
+ << "' conflicts with a previous declaration"
+ << endl;
+
+ location_t l (real_source_location (d));
+ info (l) << "'" << vd.name << "' was previously declared here"
+ << endl;
+
+ throw failed ();
+ }
+
+ path file (LOCATION_FILE (vd.loc));
+ size_t line (LOCATION_LINE (vd.loc));
+ size_t clmn (LOCATION_COLUMN (vd.loc));
+
+ access a (access::public_);
+
+ type& type_node (emit_type (vd.type, a, file, line, clmn));
+ data_member& member_node (
+ unit_->new_node<data_member> (file, line, clmn, tree (0)));
+
+ unit_->new_edge<names> (*c_node, member_node, vd.name, a);
+ belongs& edge (unit_->new_edge<belongs> (member_node, type_node));
+
+ // See if there is a name hint for this type.
+ //
+ if (names* hint = unit_->find_hint (vd.type))
+ edge.hint (*hint);
+
+ // Process pragmas that may be associated with this field.
+ //
+ process_pragmas (vd, member_node, vd.name, b, i, e);
+ break;
+ }
+ default:
+ {
+ assert (false);
+ break;
+ }
+ }
+ continue;
+ }
+
+ tree d (i->decl);
+
+ switch (TREE_CODE (d))
+ {
+ case TYPE_DECL:
+ {
+ type* n (emit_type_decl (d));
+
+ // If this is a named class-type definition, then handle
+ // the pragmas.
+ //
+ if (n != 0)
+ process_pragmas (n->tree_node (), *n, n->name (), b, i, e);
+
+ break;
+ }
+ case TEMPLATE_DECL:
+ {
+ emit_template_decl (d);
+ break;
+ }
+ case FIELD_DECL:
+ {
+ // If this is a bit-field then TREE_TYPE may be a modified type
+ // with lesser precision. In this case, DECL_BIT_FIELD_TYPE
+ // will be the type that was original specified. Use that type
+ // for now. Furthermore, bitfields can be anonymous, which we
+ // ignore.
+ //
+ //
+ bool bf (DECL_C_BIT_FIELD (d));
+
+ if (bf && DECL_NAME (d) == 0)
+ break;
+
+ // Another case where we can have NULL name is anonymous struct
+ // or union extension, for example:
+ //
+ // struct s
+ // {
+ // union
+ // {
+ // int a;
+ // int b;
+ // };
+ // };
+ //
+ // GCC appears to create a fake member for such a struct/union
+ // without any name. Ignore such members for now.
+ //
+ if (DECL_NAME (d) == 0)
+ break;
+
+ tree t (bf ? DECL_BIT_FIELD_TYPE (d) : TREE_TYPE (d));
+
+ char const* name (IDENTIFIER_POINTER (DECL_NAME (d)));
+
+ path file (DECL_SOURCE_FILE (d));
+ size_t line (DECL_SOURCE_LINE (d));
+ size_t clmn (DECL_SOURCE_COLUMN (d));
+
+ access a (decl_access (d));
+
+ type& type_node (emit_type (t, a, file, line, clmn));
+ data_member& member_node (
+ unit_->new_node<data_member> (file, line, clmn, d));
+ unit_->insert (d, member_node);
+
+ unit_->new_edge<names> (*c_node, member_node, name, a);
+ belongs& edge (unit_->new_edge<belongs> (member_node, type_node));
+
+ // See if there is a name hint for this type.
+ //
+ if (names* hint = unit_->find_hint (t))
+ edge.hint (*hint);
+
+ if (trace)
+ {
+ string type_name (emit_type_name (t));
+
+ ts << "\t" << a.string () << " data member " << type_name
+ << " (" << &type_node << ") " << name << " at "
+ << file << ":" << line << endl;
+ }
+
+ // Process pragmas that may be associated with this field.
+ //
+ process_pragmas (d, member_node, name, b, i, e);
+
+ break;
+ }
+ default:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+
+ // Diagnose any position pragmas that haven't been associated.
+ //
+ diagnose_unassoc_pragmas (decls);
+
+ scope_ = prev_scope;
+ class_scopes_.pop_back ();
+
+ return *c_node;
+}
+
+template <typename T>
+T& parser::impl::
+emit_union (tree u, path const& file, size_t line, size_t clmn, bool stub)
+{
+ u = TYPE_MAIN_VARIANT (u);
+
+ // See if there is a stub already for this type.
+ //
+ T* u_node (0);
+
+ if (node* n = unit_->find (u))
+ {
+ u_node = &dynamic_cast<T&> (*n);
+ }
+ else
+ {
+ u_node = &unit_->new_node<T> (file, line, clmn, u);
+ unit_->insert (u, *u_node);
+ }
+
+ if (stub || !COMPLETE_TYPE_P (u))
+ return *u_node;
+
+ class_scopes_.push_back (u_node);
+
+ // Collect member declarations so that we can traverse them in
+ // the source code order.
+ //
+ decl_set decls;
+
+ for (tree d (TYPE_FIELDS (u)); d != NULL_TREE ; d = TREE_CHAIN (d))
+ {
+ switch (TREE_CODE (d))
+ {
+ case TYPE_DECL:
+ {
+ if (!DECL_SELF_REFERENCE_P (d))
+ decls.insert (d);
+ break;
+ }
+ case TEMPLATE_DECL:
+ {
+ if (DECL_CLASS_TEMPLATE_P (d))
+ decls.insert (d);
+ break;
+ }
+ case FIELD_DECL:
+ {
+ if (!DECL_ARTIFICIAL (d))
+ decls.insert (d);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Add location pragmas if any.
+ //
+ {
+ loc_pragmas::const_iterator i (loc_pragmas_.find (u));
+
+ if (i != loc_pragmas_.end ())
+ decls.insert (i->second.begin (), i->second.end ());
+ }
+
+ scope* prev_scope (scope_);
+ scope_ = u_node;
+
+ for (decl_set::const_iterator b (decls.begin ()), i (b), e (decls.end ());
+ i != e; ++i)
+ {
+ // Skip pragmas.
+ //
+ if (i->prag)
+ continue;
+
+ tree d (i->decl);
+
+ switch (TREE_CODE (d))
+ {
+ case TYPE_DECL:
+ {
+ type* n (emit_type_decl (d));
+
+ // If this is a named class-type definition, then handle
+ // the pragmas.
+ //
+ if (n != 0)
+ process_pragmas (n->tree_node (), *n, n->name (), b, i, e);
+
+ break;
+ }
+ case TEMPLATE_DECL:
+ {
+ emit_template_decl (d);
+ break;
+ }
+ case FIELD_DECL:
+ {
+ // We can have NULL name in case of anonymous struct or union
+ // extension, for example:
+ //
+ // union s
+ // {
+ // struct
+ // {
+ // int a;
+ // int b;
+ // };
+ // int c;
+ // };
+ //
+ // GCC appears to create a fake member for such a struct/union
+ // without any name. Ignore such members for now.
+ //
+ if (DECL_NAME (d) == 0)
+ break;
+
+ tree t (TREE_TYPE (d));
+ char const* name (IDENTIFIER_POINTER (DECL_NAME (d)));
+
+ path file (DECL_SOURCE_FILE (d));
+ size_t line (DECL_SOURCE_LINE (d));
+ size_t clmn (DECL_SOURCE_COLUMN (d));
+
+ access a (decl_access (d));
+
+ type& type_node (emit_type (t, a, file, line, clmn));
+ data_member& member_node (
+ unit_->new_node<data_member> (file, line, clmn, d));
+ unit_->insert (d, member_node);
+
+ unit_->new_edge<names> (*u_node, member_node, name, a);
+ belongs& edge (unit_->new_edge<belongs> (member_node, type_node));
+
+ // See if there is a name hint for this type.
+ //
+ if (names* hint = unit_->find_hint (t))
+ edge.hint (*hint);
+
+ if (trace)
+ {
+ string type_name (emit_type_name (t));
+
+ ts << "\t" << a.string () << " union member " << type_name
+ << " (" << &type_node << ") " << name << " at "
+ << file << ":" << line << endl;
+ }
+
+ // Process pragmas that may be associated with this field.
+ //
+ process_pragmas (d, member_node, name, b, i, e);
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Diagnose any position pragmas that haven't been associated.
+ //
+ diagnose_unassoc_pragmas (decls);
+
+ scope_ = prev_scope;
+ class_scopes_.pop_back ();
+ return *u_node;
+}
+
+//
+// Functions.
+//
+
+parser::impl::
+impl (options const& ops,
+ loc_pragmas & lp,
+ ns_loc_pragmas& nslp,
+ decl_pragmas& dp)
+ : ops_ (ops),
+ loc_pragmas_ (lp),
+ ns_loc_pragmas_ (nslp),
+ decl_pragmas_ (dp),
+ trace (ops.trace ()),
+ ts (cerr)
+{
+}
+
+unique_ptr<unit> parser::impl::
+parse (tree global_scope, path const& main_file)
+{
+ unique_ptr<unit> u (new unit (main_file));
+ u->insert (global_namespace, *u);
+ process_named_pragmas (global_namespace, *u);
+
+ unit_ = u.get ();
+ scope_ = unit_;
+ error_ = 0;
+
+ // Define fundamental types.
+ //
+ define_fund<fund_void> (void_type_node);
+ define_fund<fund_bool> (boolean_type_node);
+ define_fund<fund_char> (char_type_node);
+ define_fund<fund_wchar> (wchar_type_node);
+
+ if (ops_.std () >= cxx_version::cxx11)
+ {
+ define_fund<fund_char16> (char16_type_node);
+ define_fund<fund_char32> (char32_type_node);
+ }
+
+ define_fund<fund_signed_char> (signed_char_type_node);
+ define_fund<fund_unsigned_char> (unsigned_char_type_node);
+ define_fund<fund_short> (short_integer_type_node);
+ define_fund<fund_unsigned_short> (short_unsigned_type_node);
+ define_fund<fund_int> (integer_type_node);
+ define_fund<fund_unsigned_int> (unsigned_type_node);
+ define_fund<fund_long> (long_integer_type_node);
+ define_fund<fund_unsigned_long> (long_unsigned_type_node);
+ define_fund<fund_long_long> (long_long_integer_type_node);
+ define_fund<fund_unsigned_long_long> (long_long_unsigned_type_node);
+ define_fund<fund_float> (float_type_node);
+ define_fund<fund_double> (double_type_node);
+ define_fund<fund_long_double> (long_double_type_node);
+
+ // First collect all the namespace-level declarations we are
+ // interested in in the line-decl map so that they appear in
+ // the source code order.
+ //
+ collect (global_scope);
+
+ // Add namespace-level position pragmas if any.
+ //
+ {
+ loc_pragmas::const_iterator i (loc_pragmas_.find (global_namespace));
+
+ if (i != loc_pragmas_.end ())
+ decls_.insert (i->second.begin (), i->second.end ());
+ }
+
+ // Convert position namespace pragmas to name pragmas.
+ //
+ for (ns_loc_pragmas::const_iterator i (ns_loc_pragmas_.begin ());
+ i != ns_loc_pragmas_.end (); ++i)
+ {
+ pragma const& p (i->pragma);
+
+ decl_map::const_iterator j (all_decls_.lower_bound (p.loc));
+
+ if (j == all_decls_.end ())
+ {
+ error (p.loc)
+ << "db pragma '" << p.pragma_name << "' is not associated with a "
+ << "namespace declaration" << endl;
+ error_++;
+ continue;
+ }
+
+ // Find the "namespace difference" between this declaration and
+ // the pragma's namespace. The outermost namespace in the result
+ // is what we are looking for.
+ //
+ tree ns (0);
+
+ for (tree prev (j->second), scope (CP_DECL_CONTEXT (prev));;
+ scope = CP_DECL_CONTEXT (scope))
+ {
+ if (scope == i->ns)
+ {
+ ns = prev;
+ break;
+ }
+
+ if (scope == global_namespace)
+ break;
+
+ prev = scope;
+ }
+
+ if (ns == 0 || TREE_CODE (ns) != NAMESPACE_DECL)
+ {
+ error (p.loc)
+ << "db pragma '" << p.pragma_name << "' is not associated with a "
+ << "namespace declaration" << endl;
+ error_++;
+ continue;
+ }
+
+ pragma_set& s (decl_pragmas_[ns]);
+ pragma_set::iterator it (s.find (p.context_name));
+
+ // Make sure we override only if this pragma came after the one
+ // already in the set.
+ //
+ if (it == s.end () || it->second.loc <= p.loc)
+ s.insert (p);
+ }
+
+ // Construct the semantic graph.
+ //
+ if (error_ == 0)
+ emit ();
+
+ if (error_ > 0)
+ throw failed ();
+
+ return u;
+}
+
+void parser::impl::
+collect (tree ns)
+{
+ cp_binding_level* level = NAMESPACE_LEVEL (ns);
+ tree decl = level->names;
+
+ // Collect declarations.
+ //
+ for (; decl != NULL_TREE; decl = TREE_CHAIN (decl))
+ {
+ all_decls_[real_source_location (decl)] = decl;
+
+ if (DECL_IS_BUILTIN (decl))
+ continue;
+
+ switch (TREE_CODE (decl))
+ {
+ case TYPE_DECL:
+ {
+ // Skip special type declarations.
+ //
+ if (DECL_NAME (decl) == NULL_TREE)
+ continue;
+
+ tree type (TREE_TYPE (decl));
+ if (LAMBDA_TYPE_P (type))
+ continue;
+
+ decls_.insert (decl);
+ break;
+ }
+ case TEMPLATE_DECL:
+ {
+ if (DECL_CLASS_TEMPLATE_P (decl))
+ decls_.insert (decl);
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Traverse namespaces.
+ //
+ for (
+#if BUILDING_GCC_MAJOR >= 8
+ decl = level->names;
+#else
+ decl = level->namespaces;
+#endif
+ decl != NULL_TREE;
+ decl = TREE_CHAIN (decl))
+ {
+#if BUILDING_GCC_MAJOR >= 8
+ // Now namespaces are interleaved with other declarations. In fact, we
+ // could probably collect everything in a single pass.
+ //
+ if (TREE_CODE (decl) != NAMESPACE_DECL)
+ continue;
+#endif
+
+ // Ignore namespace aliases.
+ //
+ if (DECL_NAMESPACE_ALIAS (decl))
+ continue;
+
+ if (!DECL_IS_BUILTIN (decl) || DECL_NAMESPACE_STD_P (decl))
+ {
+ tree dn (DECL_NAME (decl));
+
+ if (trace)
+ {
+ char const* name (dn ? IDENTIFIER_POINTER (dn) : "<anonymous>");
+
+ ts << "namespace " << name << " at "
+ << DECL_SOURCE_FILE (decl) << ":"
+ << DECL_SOURCE_LINE (decl) << endl;
+ }
+
+ // Skip anonymous namespaces (there could be nothing of interest to us
+ // inside but they wreck havoc with our attempts to sort declarations
+ // into namespaces).
+ //
+ if (dn != 0)
+ collect (decl);
+ }
+ }
+}
+
+void parser::impl::
+emit ()
+{
+ for (decl_set::const_iterator b (decls_.begin ()), i (b),
+ e (decls_.end ()); i != e; ++i)
+ {
+ // Skip pragmas.
+ //
+ if (i->prag)
+ continue;
+
+ tree decl (i->decl);
+
+ // Get this declaration's namespace and unwind our scope until
+ // we find a common prefix of namespaces.
+ //
+ string pfx;
+ string ns (fq_scope (decl));
+
+ for (pfx = scope_->fq_name (); !pfx.empty (); pfx = scope_->fq_name ())
+ {
+ size_t n (pfx.size ());
+
+ // Make sure we handle cases like ns="::foobar", pfx="::foo".
+ //
+ if (ns.compare (0, n, pfx) == 0 && (ns.size () == n || ns[n - 1] == ':'))
+ break;
+
+ if (trace)
+ ts << "closing namespace " << scope_->name () << endl;
+
+ scope_ = &scope_->scope_ ();
+ }
+
+ // Build the rest of the namespace hierarchy for this declaration.
+ //
+ if (ns != pfx)
+ {
+ path f (DECL_SOURCE_FILE (decl));
+ size_t l (DECL_SOURCE_LINE (decl));
+ size_t c (DECL_SOURCE_COLUMN (decl));
+
+ for (size_t b (pfx.size () + 2), e (ns.find ("::", b));
+ b != string::npos;)
+ {
+ string n (ns, b, e == string::npos ? e : e - b);
+
+ if (trace)
+ ts << "opening namespace " << n << " for "
+ << DECL_SOURCE_FILE (decl) << ":"
+ << DECL_SOURCE_LINE (decl) << endl;
+
+ // Use the declarations's file, line, and column as an
+ // approximation for this namespace origin. Also resolve
+ // the tree node for this namespace.
+ //
+#if BUILDING_GCC_MAJOR >= 8
+ tree tree_node (
+ get_namespace_binding (
+ scope_->tree_node (), get_identifier (n.c_str ())));
+#else
+ tree tree_node (
+ namespace_binding (
+ get_identifier (n.c_str ()), scope_->tree_node ()));
+#endif
+
+ namespace_& node (unit_->new_node<namespace_> (f, l, c, tree_node));
+ unit_->new_edge<defines> (*scope_, node, n);
+
+ if (namespace_* orig =
+ dynamic_cast<namespace_*> (unit_->find (tree_node)))
+ {
+ // This is an extension.
+ //
+ node.original (*orig);
+ }
+ else
+ {
+ // This is the original. Add it to the map and process any
+ // pragmas it might have (at this stage namespaces can only
+ // have name pragmas).
+ //
+ unit_->insert (tree_node, node);
+ process_named_pragmas (tree_node, node);
+ }
+
+ scope_ = &node;
+
+ if (e == string::npos)
+ b = e;
+ else
+ {
+ b = e + 2;
+ e = ns.find ("::", b);
+ }
+ }
+ }
+
+ switch (TREE_CODE (decl))
+ {
+ case TYPE_DECL:
+ {
+ type* n (emit_type_decl (decl));
+
+ // If this is a named class-type definition, then handle
+ // the pragmas.
+ //
+ if (n != 0)
+ process_pragmas (n->tree_node (), *n, n->name (), b, i, e);
+
+ break;
+ }
+ case TEMPLATE_DECL:
+ {
+ emit_template_decl (decl);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ assert (class_scopes_.empty ());
+ }
+
+ // Diagnose any position pragmas that haven't been associated.
+ //
+ diagnose_unassoc_pragmas (decls_);
+}
+
+type* parser::impl::
+emit_type_decl (tree decl)
+{
+ tree t (TREE_TYPE (decl));
+ gcc_tree_code_type tc (TREE_CODE (t));
+
+ tree decl_name (DECL_NAME (decl));
+ char const* name (IDENTIFIER_POINTER (decl_name));
+
+ if (DECL_ARTIFICIAL (decl) &&
+ (tc == RECORD_TYPE || tc == UNION_TYPE || tc == ENUMERAL_TYPE))
+ {
+ // If we have an anonymous class typedef, use the user-
+ // supplied name instead of the synthesized one. ARM
+ // says that in typedef struct {} S; S becomes struct's
+ // name.
+ //
+ if (IDENTIFIER_ANON_P (decl_name))
+ {
+ tree d (TYPE_NAME (t));
+
+ if (d != NULL_TREE &&
+ !DECL_ARTIFICIAL (d) &&
+ DECL_NAME (d) != NULL_TREE &&
+ !IDENTIFIER_ANON_P (DECL_NAME (d)))
+ {
+ decl = d;
+ decl_name = DECL_NAME (decl);
+ name = IDENTIFIER_POINTER (decl_name);
+ }
+ else
+ {
+ // This type has only the synthesized name which means that
+ // it is either typedef'ed as a derived type or it is used
+ // to declare a varibale or similar. The first case will be
+ // covered by the typedef handling code below. The second
+ // case will be covere by emit_type().
+ //
+ return 0;
+ }
+ }
+
+ path file (DECL_SOURCE_FILE (decl));
+ size_t line (DECL_SOURCE_LINE (decl));
+ size_t clmn (DECL_SOURCE_COLUMN (decl));
+
+ type* node (0);
+
+ // Pointers to member functions are represented as record
+ // types. Detect and handle this case.
+ //
+ if (TYPE_PTRMEMFUNC_P (t))
+ {
+ t = TYPE_MAIN_VARIANT (t);
+ node = &unit_->new_node<unsupported_type> (
+ file, line, clmn, t, "pointer_to_member_function_type");
+ unit_->insert (t, *node);
+ }
+ else
+ {
+
+ if (trace)
+ ts << "start " << gcc_tree_code_name(tc) << " " << name
+ << " at " << file << ":" << line << endl;
+
+ switch (tc)
+ {
+ case RECORD_TYPE:
+ {
+ node = &emit_class<class_> (t, file, line, clmn);
+ break;
+ }
+ case UNION_TYPE:
+ {
+ node = &emit_union<union_> (t, file, line, clmn);
+ break;
+ }
+ case ENUMERAL_TYPE:
+ {
+ node = &emit_enum (t, decl_access (decl), file, line, clmn);
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (trace)
+ ts << "end " << gcc_tree_code_name(tc) << " " << name
+ << " (" << node << ") at "
+ << DECL_SOURCE_FILE (decl) << ":"
+ << DECL_SOURCE_LINE (decl) << endl;
+ }
+
+ if (COMPLETE_TYPE_P (t))
+ unit_->new_edge<defines> (*scope_, *node, name);
+ else
+ unit_->new_edge<declares> (*scope_, *node, name);
+
+ return node;
+ }
+ else
+ {
+ // Normal typedef. We need to detect and ignore the anonymous
+ // class typedef case described above since we already used
+ // this name to define the class.
+ //
+ if ((tc == RECORD_TYPE || tc == UNION_TYPE || tc == ENUMERAL_TYPE) &&
+ TYPE_NAME (TYPE_MAIN_VARIANT (t)) == decl)
+ return 0;
+
+ path f (DECL_SOURCE_FILE (decl));
+ size_t l (DECL_SOURCE_LINE (decl));
+ size_t c (DECL_SOURCE_COLUMN (decl));
+
+ type& node (emit_type (t, decl_access (decl), f, l, c));
+
+ // Omit inner self-typedefs (e.g., a class typedefs itself in its own
+ // scope). Such aliases don't buy us anything (in particular, they cannot
+ // be used to form an fq-name) but they do cause scoping cycles if this
+ // name happens to be used to find outer scope (see scope::scope_()).
+ // Note that this means we can now have class template instantiations that
+ // are not named and therefore don't belong to any scope.
+ //
+ // Note that emit_type() might still enter this decl as a hint. It's fuzzy
+ // whether this is harmless or not.
+ //
+ // Note also that using the normal scope hierarchy does not work in more
+ // complex cases where templates cross-self-typedef. So instead we now use
+ // a special-purpose mechanism (class_scopes_). Note for this to work
+ // correctly (hopefully), the class should be "in scope" for its bases.
+ // Here is a representative examples (inspired by code in Eigen):
+ //
+ // template <typename M>
+ // struct PlainObjectBase
+ // {
+ // typedef M Self;
+ // };
+ //
+ // template <typename T, int X, int Y>
+ // struct Matrix: PlainObjectBase<Matrix<T, X, Y>>
+ // {
+ // typedef PlainObjectBase<Matrix> Base;
+ // typedef Matrix Self;
+ // };
+ //
+ // typedef Matrix<double, 3, 1> Vector3d;
+ //
+ // Here we want both Self's (but not Base) to be skipped.
+ //
+ if (scope* s = dynamic_cast<scope*> (&node))
+ {
+ for (auto i (class_scopes_.rbegin ()); i != class_scopes_.rend (); ++i)
+ {
+ if (s == *i)
+ {
+ if (trace)
+ {
+ string s (emit_type_name (t, false));
+
+ ts << "omitting inner self-typedef " << s << " (" << &node
+ << ") -> " << name << " at " << f << ":" << l << endl;
+ }
+ return 0;
+ }
+ }
+ }
+
+ typedefs& edge (unit_->new_edge<typedefs> (*scope_, node, name));
+
+ // Find our hint.
+ //
+ if (tree ot = DECL_ORIGINAL_TYPE (decl))
+ {
+ if (names* hint = unit_->find_hint (ot))
+ edge.hint (*hint);
+ }
+
+ // Add this edge to the hint map. It may already be there if we
+ // are handling something like this:
+ //
+ // typedef foo bar;
+ // typedef bar foo;
+ //
+ // GCC also appears to re-purpose a node for another name (not
+ // sure if its a bug or a feature), so use the latest seen name.
+ //
+ unit_->insert_hint (t, edge);
+
+ if (trace)
+ {
+ string s (emit_type_name (t, false));
+
+ ts << "typedef " << s << " (" << &node << ") -> " << name
+ << " at " << f << ":" << l << endl;
+ }
+
+ return 0;
+ }
+}
+
+void parser::impl::
+emit_template_decl (tree decl)
+{
+ // Currently we only handle class/union templates.
+ //
+ tree t (TREE_TYPE (DECL_TEMPLATE_RESULT (decl)));
+ gcc_tree_code_type tc (TREE_CODE (t));
+
+ if (trace)
+ {
+ ts << gcc_tree_code_name(tc) << " template (" << decl << ") "
+ << IDENTIFIER_POINTER (DECL_NAME (decl)) << " (" << t << ") at "
+ << DECL_SOURCE_FILE (decl) << ":"
+ << DECL_SOURCE_LINE (decl) << endl;
+
+ ts << "specializations:" << endl;
+
+ for (tree s (DECL_TEMPLATE_SPECIALIZATIONS (decl));
+ s != NULL_TREE; s = TREE_CHAIN (s))
+ {
+ tree t (TREE_TYPE (s));
+ tree d (TYPE_NAME (t));
+
+ ts << "\tspecialization " << t << " at "
+ << DECL_SOURCE_FILE (d) << ":"
+ << DECL_SOURCE_LINE (d) << endl;
+ }
+
+ ts << "instantiations:" << endl;
+
+ for (tree i (DECL_TEMPLATE_INSTANTIATIONS (decl));
+ i != NULL_TREE; i = TREE_CHAIN (i))
+ {
+ tree t (TREE_VALUE (i));
+ tree d (TYPE_NAME (t));
+
+ ts << "\tinstantiation " << t << " at "
+ << DECL_SOURCE_FILE (d) << ":"
+ << DECL_SOURCE_LINE (d) << endl;
+ }
+ }
+
+ char const* name (IDENTIFIER_POINTER (DECL_NAME (decl)));
+
+ if (trace)
+ ts << "start " << gcc_tree_code_name(tc) << " template " << name << " at "
+ << DECL_SOURCE_FILE (decl) << ":"
+ << DECL_SOURCE_LINE (decl) << endl;
+
+ type_template* t_node (0);
+
+ if (tc == RECORD_TYPE)
+ t_node = &emit_class_template (decl);
+ else
+ t_node = &emit_union_template (decl);
+
+ if (COMPLETE_TYPE_P (t))
+ unit_->new_edge<defines> (*scope_, *t_node, name);
+ else
+ unit_->new_edge<declares> (*scope_, *t_node, name);
+
+ if (trace)
+ ts << "end " << gcc_tree_code_name(tc) << " template " << name
+ << " (" << t_node << ") at "
+ << DECL_SOURCE_FILE (decl) << ":"
+ << DECL_SOURCE_LINE (decl) << endl;
+}
+
+class_template& parser::impl::
+emit_class_template (tree t, bool stub)
+{
+ // See if there is a stub already for this template.
+ //
+ class_template* ct_node (0);
+ tree c (TREE_TYPE (DECL_TEMPLATE_RESULT (t)));
+
+ if (node* n = unit_->find (t))
+ {
+ ct_node = &dynamic_cast<class_template&> (*n);
+ }
+ else
+ {
+ path f (DECL_SOURCE_FILE (t));
+ size_t ln (DECL_SOURCE_LINE (t));
+ size_t cl (DECL_SOURCE_COLUMN (t));
+
+ ct_node = &unit_->new_node<class_template> (f, ln, cl, c);
+ unit_->insert (t, *ct_node);
+ }
+
+ if (stub || !COMPLETE_TYPE_P (c))
+ return *ct_node;
+
+ class_scopes_.push_back (ct_node);
+
+ // Collect member declarations so that we can traverse them in
+ // the source code order. For now we are only interested in
+ // nested class template declarations.
+ //
+ decl_set decls;
+
+ for (tree d (TYPE_FIELDS (c)); d != NULL_TREE ; d = TREE_CHAIN (d))
+ {
+ switch (TREE_CODE (d))
+ {
+ case TEMPLATE_DECL:
+ {
+ if (DECL_CLASS_TEMPLATE_P (d))
+ decls.insert (d);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ scope* prev_scope (scope_);
+ scope_ = ct_node;
+
+ for (decl_set::const_iterator i (decls.begin ()), e (decls.end ());
+ i != e; ++i)
+ {
+ // Skip pragmas.
+ //
+ if (i->prag)
+ continue;
+
+ tree d (i->decl);
+
+ switch (TREE_CODE (d))
+ {
+ case TEMPLATE_DECL:
+ {
+ emit_template_decl (d);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Diagnose any position pragmas that haven't been associated.
+ //
+ diagnose_unassoc_pragmas (decls);
+
+ scope_ = prev_scope;
+ class_scopes_.pop_back ();
+ return *ct_node;
+}
+
+union_template& parser::impl::
+emit_union_template (tree t, bool stub)
+{
+ // See if there is a stub already for this template.
+ //
+ union_template* ut_node (0);
+ tree u (TREE_TYPE (DECL_TEMPLATE_RESULT (t)));
+
+ if (node* n = unit_->find (t))
+ {
+ ut_node = &dynamic_cast<union_template&> (*n);
+ }
+ else
+ {
+ path f (DECL_SOURCE_FILE (t));
+ size_t l (DECL_SOURCE_LINE (t));
+ size_t c (DECL_SOURCE_COLUMN (t));
+
+ ut_node = &unit_->new_node<union_template> (f, l, c, u);
+ unit_->insert (t, *ut_node);
+ }
+
+ if (stub || !COMPLETE_TYPE_P (u))
+ return *ut_node;
+
+ class_scopes_.push_back (ut_node);
+
+ // Collect member declarations so that we can traverse them in
+ // the source code order. For now we are only interested in
+ // nested class template declarations.
+ //
+ decl_set decls;
+
+ for (tree d (TYPE_FIELDS (u)); d != NULL_TREE ; d = TREE_CHAIN (d))
+ {
+ switch (TREE_CODE (d))
+ {
+ case TEMPLATE_DECL:
+ {
+ if (DECL_CLASS_TEMPLATE_P (d))
+ decls.insert (d);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ scope* prev_scope (scope_);
+ scope_ = ut_node;
+
+ for (decl_set::const_iterator i (decls.begin ()), e (decls.end ());
+ i != e; ++i)
+ {
+ // Skip pragmas.
+ //
+ if (i->prag)
+ continue;
+
+ tree d (i->decl);
+
+ switch (TREE_CODE (d))
+ {
+ case TEMPLATE_DECL:
+ {
+ emit_template_decl (d);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Diagnose any position pragmas that haven't been associated.
+ //
+ diagnose_unassoc_pragmas (decls);
+
+ scope_ = prev_scope;
+ class_scopes_.pop_back ();
+ return *ut_node;
+}
+
+enum_& parser::impl::
+emit_enum (tree e,
+ access access,
+ path const& file,
+ size_t line,
+ size_t clmn,
+ bool stub)
+{
+ e = TYPE_MAIN_VARIANT (e);
+
+ // See if there is a stub already for this type.
+ //
+ enum_* e_node (0);
+
+ if (node* n = unit_->find (e))
+ e_node = &dynamic_cast<enum_&> (*n);
+ else
+ {
+ e_node = &unit_->new_node<enum_> (file, line, clmn, e);
+
+ // Set the underlying type even for incomplete (forward-declared) enums.
+ //
+ tree ut (ENUM_UNDERLYING_TYPE (e));
+ names* hint (unit_->find_hint (ut));
+ integral_type* un = dynamic_cast<integral_type*> (
+ unit_->find (TYPE_MAIN_VARIANT (ut)));
+
+ // For "old" enums GCC creates a distinct type node and the only way to
+ // get to one of the known integrals is via its name.
+ //
+ if (un == 0)
+ {
+ ut = TREE_TYPE (TYPE_NAME (ut));
+ un = dynamic_cast<integral_type*> (unit_->find (TYPE_MAIN_VARIANT (ut)));
+ }
+
+ underlies& edge (unit_->new_edge<underlies> (*un, *e_node));
+
+ if (hint != 0)
+ edge.hint (*hint);
+
+ unit_->insert (e, *e_node);
+ }
+
+ if (stub || !COMPLETE_TYPE_P (e))
+ return *e_node;
+
+ // Traverse enumerators.
+ //
+ for (tree er (TYPE_VALUES (e)); er != NULL_TREE ; er = TREE_CHAIN (er))
+ {
+ char const* name (IDENTIFIER_POINTER (TREE_PURPOSE (er)));
+ tree decl (TREE_VALUE (er));
+ tree tval (DECL_INITIAL (decl));
+
+ unsigned long long val (integer_value (tval));
+
+ // There doesn't seem to be a way to get the proper position for
+ // each enumerator.
+ //
+ enumerator& er_node = unit_->new_node<enumerator> (
+ file, line, clmn, er, val);
+ unit_->new_edge<enumerates> (*e_node, er_node);
+ unit_->insert (decl, er_node);
+
+ // In C++11 the enumerators are always available in the enum's
+ // scope, even for old enums.
+ //
+ if (ops_.std () >= cxx_version::cxx11)
+ unit_->new_edge<names> (*e_node, er_node, name, access::public_);
+
+ // Inject enumerators into the outer scope unless this is an
+ // enum class.
+ //
+ if (UNSCOPED_ENUM_P (e))
+ unit_->new_edge<names> (*scope_, er_node, name, access);
+
+ if (trace)
+ ts << "\tenumerator " << name << " at " << file << ":" << line << endl;
+ }
+
+ return *e_node;
+}
+
+type& parser::impl::
+emit_type (tree t,
+ access access,
+ path const& file,
+ size_t line,
+ size_t clmn)
+{
+ tree mv (TYPE_MAIN_VARIANT (t));
+
+ if (trace)
+ {
+ ts << gcc_tree_code_name(TREE_CODE (t)) << " " << t
+ << " main " << mv << endl;
+
+ for (tree v (TYPE_MAIN_VARIANT (t)); v != 0; v = TYPE_NEXT_VARIANT (v))
+ ts << "\tvariant " << v << " " << CP_TYPE_CONST_P (v) << endl;
+ }
+
+ node* n (unit_->find (mv));
+
+ type& r (n != 0
+ ? dynamic_cast<type&> (*n)
+ : create_type (t, access, file, line, clmn));
+
+ if (trace && n != 0)
+ ts << "found node " << &r << " for type " << mv << endl;
+
+ if (cp_type_quals (t) == TYPE_UNQUALIFIED)
+ {
+ unit_->insert (t, r); // Add this variant to the map.
+ return r;
+ }
+
+ // See if this type already has this variant.
+ //
+ bool qc (CP_TYPE_CONST_P (t));
+ bool qv (CP_TYPE_VOLATILE_P (t));
+ bool qr (CP_TYPE_RESTRICT_P (t));
+
+ for (type::qualified_iterator i (r.qualified_begin ());
+ i != r.qualified_end (); ++i)
+ {
+ qualifier& q (i->qualifier ());
+
+ if (q.const_ () == qc && q.volatile_ () == qv && q.restrict_ () == qr)
+ {
+ if (trace)
+ ts << "found qualifier variant " << &q << endl;
+
+ unit_->insert (t, q); // Add this variant to the map.
+ return q;
+ }
+ }
+
+ // No such variant yet. Create a new one. Qualified types are not
+ // unique in the tree so don't add this node to the map.
+ //
+ qualifier& q (unit_->new_node<qualifier> (file, line, clmn, t, qc, qv, qr));
+ qualifies& e (unit_->new_edge<qualifies> (q, r));
+ unit_->insert (t, q);
+
+ // See if there is a name hint for this type.
+ //
+ // If TREE_TYPE (TYPE_NAME (t)) != t then we have an inline qualifier,
+ // as in:
+ //
+ // const foo x;
+ //
+ // If they are equal, then there are two possible cases. The first is
+ // when we have a qualifier typedef, as in:
+ //
+ // typedef const foo cfoo;
+ // cfoo x;
+ //
+ // The second is when we have a qualifier typedef but what is actually
+ // used in the declaration is an inline qualifier, as in:
+ //
+ // typedef const foo cfoo;
+ // const foo x;
+ //
+ // Unfortunately, in GCC, these two cases are indistinguishable. In
+ // certain cases this can lead to a wrong hint being used for the base
+ // type, for example:
+ //
+ // typedef foo my_foo;
+ // typedef foo foo_t;
+ // typedef const foo_t cfoo;
+ //
+ // const my_foo x;
+ //
+ // Above, the hint will be foo_t while it should be my_foo.
+ //
+ tree bt (0);
+
+ if (tree decl = TYPE_NAME (t))
+ {
+ bt = TREE_TYPE (decl);
+
+ if (t == bt)
+ {
+ // A const type can be named only with a typedef. Get the
+ // original type.
+ //
+ tree ot (DECL_ORIGINAL_TYPE (decl));
+
+ // And chase it one more time to get rid of the qualification.
+ //
+ decl = TYPE_NAME (ot);
+ bt = decl != 0 ? TREE_TYPE (decl) : 0;
+ }
+ }
+
+ if (bt != 0)
+ {
+ if (names* hint = unit_->find_hint (bt))
+ e.hint (*hint);
+ }
+
+ process_named_pragmas (t, q);
+
+ return q;
+}
+
+type& parser::impl::
+create_type (tree t,
+ access access,
+ path const& file,
+ size_t line,
+ size_t clmn)
+{
+ type* r (0);
+ gcc_tree_code_type tc (TREE_CODE (t));
+
+ switch (tc)
+ {
+ //
+ // User-defined types.
+ //
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ {
+ t = TYPE_MAIN_VARIANT (t);
+ tree ti (TYPE_TEMPLATE_INFO (t));
+
+ if (ti == NULL_TREE)
+ {
+ // Ordinary class. There are two situations which can lead
+ // here. First is when we have an anonymous class that is
+ // part of the declaration, for example:
+ //
+ // typedef const struct {...} s;
+ //
+ // The second situation is a named class definition which
+ // we haven't parsed yet. In this case we are going to
+ // create a "stub" class node which will be processed and
+ // filled in later.
+ //
+
+ // Pointers to member functions are represented as record
+ // types. Detect and handle this case.
+ //
+ if (TYPE_PTRMEMFUNC_P (t))
+ {
+ r = &unit_->new_node<unsupported_type> (
+ file, line, clmn, t, "pointer_to_member_function_type");
+ unit_->insert (t, *r);
+ }
+ else
+ {
+ tree d (TYPE_NAME (t));
+
+ if (trace)
+ ts << "start anon/stub " << gcc_tree_code_name(tc) << " at "
+ << file << ":" << line << endl;
+
+ if (d == NULL_TREE || IDENTIFIER_ANON_P (DECL_NAME (d)))
+ {
+ if (tc == RECORD_TYPE)
+ r = &emit_class<class_> (t, file, line, clmn);
+ else
+ r = &emit_union<union_> (t, file, line, clmn);
+ }
+ else
+ {
+ // Use the "defining" declaration's file, line, and column
+ // information to create the stub.
+ //
+ path f (DECL_SOURCE_FILE (d));
+ size_t l (DECL_SOURCE_LINE (d));
+ size_t c (DECL_SOURCE_COLUMN (d));
+
+ if (tc == RECORD_TYPE)
+ r = &emit_class<class_> (t, f, l, c, true);
+ else
+ r = &emit_union<union_> (t, f, l, c, true);
+ }
+
+ if (trace)
+ ts << "end anon/stub " << gcc_tree_code_name(tc) << " (" << r << ")"
+ << " at " << file << ":" << line << endl;
+ }
+ }
+ else
+ {
+ // Template instantiation.
+ //
+ tree decl (TI_TEMPLATE (ti)); // DECL_TEMPLATE
+
+ // Get to the most general template declaration.
+ //
+ while (DECL_TEMPLATE_INFO (decl))
+ decl = DECL_TI_TEMPLATE (decl);
+
+ type_template* t_node (0);
+
+ // Find the template node or create a stub if none exist.
+ //
+ if (node* n = unit_->find (decl))
+ t_node = &dynamic_cast<type_template&> (*n);
+ else
+ {
+ if (trace)
+ ts << "start stub " << gcc_tree_code_name(tc) << " template for ("
+ << decl << ") at " << file << ":" << line << endl;
+
+ if (tc == RECORD_TYPE)
+ t_node = &emit_class_template (decl, true);
+ else
+ t_node = &emit_union_template (decl, true);
+
+ if (trace)
+ ts << "end stub " << gcc_tree_code_name(tc) << " template ("
+ << t_node << ") at " << file << ":" << line << endl;
+ }
+
+ if (trace)
+ ts << "start " << gcc_tree_code_name(tc) << " instantiation ("
+ << t << ") for template (" << t_node << ")"
+ << " at " << file << ":" << line << endl;
+
+ type_instantiation* i_node (0);
+
+ if (tc == RECORD_TYPE)
+ i_node = &emit_class<class_instantiation> (t, file, line, clmn);
+ else
+ i_node = &emit_union<union_instantiation> (t, file, line, clmn);
+
+ if (trace)
+ ts << "end " << gcc_tree_code_name(tc) << " instantiation ("
+ << static_cast<type*> (i_node) << ")"
+ << " at " << file << ":" << line << endl;
+
+ unit_->new_edge<instantiates> (*i_node, *t_node);
+ process_named_pragmas (t, *i_node);
+ r = i_node;
+ }
+
+ break;
+ }
+ case ENUMERAL_TYPE:
+ {
+ // The same logic as in the "ordinary class" case above
+ // applies here.
+ //
+
+ t = TYPE_MAIN_VARIANT (t);
+ tree d (TYPE_NAME (t));
+
+ if (trace)
+ ts << "start anon/stub " << gcc_tree_code_name(tc) << " at "
+ << file << ":" << line << endl;
+
+ if (d == NULL_TREE || IDENTIFIER_ANON_P (DECL_NAME (d)))
+ {
+ r = &emit_enum (t, access, file, line, clmn);
+ }
+ else
+ {
+ // Use the "defining" declaration's file, line, and column
+ // information to create the stub.
+ //
+ path f (DECL_SOURCE_FILE (d));
+ size_t l (DECL_SOURCE_LINE (d));
+ size_t c (DECL_SOURCE_COLUMN (d));
+
+ r = &emit_enum (t, access, f, l, c, true);
+ }
+
+ if (trace)
+ ts << "end anon/stub " << gcc_tree_code_name(tc) << " (" << r << ")"
+ << " at " << file << ":" << line << endl;
+
+ break;
+ }
+
+ //
+ // Derived types.
+ //
+
+ case ARRAY_TYPE:
+ {
+ unsigned long long size (0);
+
+ if (tree index = TYPE_DOMAIN (t))
+ {
+ tree max (TYPE_MAX_VALUE (index));
+
+ if (TREE_CODE (max) == INTEGER_CST)
+ {
+ size = integer_value (max);
+
+ // The docs say that TYPE_DOMAIN will be NULL if the
+ // array doesn't specify bounds. In reality, it is
+ // set to ~0.
+ //
+ if (size == ~(unsigned long long) (0))
+ size = 0;
+
+ size++; // Convert max index to size.
+ }
+ else
+ {
+ error (file, line, clmn)
+ << "non-integer array index " <<
+ gcc_tree_code_name(TREE_CODE (max)) << endl;
+
+ throw failed ();
+ }
+ }
+
+ // In GCC tree a const array has both the array type itself and the
+ // element type marked as const. This doesn't bode well with our
+ // semantic graph model where we have a separate type node for
+ // qualifiers. To fix this, we are going to strip the const
+ // qualification from the element type and only preserve it in
+ // the array type. In other words, we view it as "constant array"
+ // rather than "array of constant elements".
+ //
+ using semantics::array; // vs std::array.
+
+ tree bt (TREE_TYPE (t));
+ tree bt_mv (TYPE_MAIN_VARIANT (bt));
+ type& bt_node (emit_type (bt_mv, access::public_, file, line, clmn));
+ t = TYPE_MAIN_VARIANT (t);
+ array& a (unit_->new_node<array> (file, line, clmn, t, size));
+ unit_->insert (t, a);
+ contains& edge (unit_->new_edge<contains> (a, bt_node));
+
+ // See if there is a name hint for the base type.
+ //
+ if (names* hint = unit_->find_hint (
+ cp_type_quals (bt) == TYPE_UNQUALIFIED ? bt : bt_mv))
+ edge.hint (*hint);
+
+ process_named_pragmas (t, a);
+ r = &a;
+ break;
+ }
+ case REFERENCE_TYPE:
+ {
+ tree bt (TREE_TYPE (t));
+ type& bt_node (emit_type (bt, access::public_, file, line, clmn));
+ t = TYPE_MAIN_VARIANT (t);
+ reference& ref (unit_->new_node<reference> (file, line, clmn, t));
+ unit_->insert (t, ref);
+ references& edge (unit_->new_edge<references> (ref, bt_node));
+
+ // See if there is a name hint for the base type.
+ //
+ if (names* hint = unit_->find_hint (bt))
+ edge.hint (*hint);
+
+ process_named_pragmas (t, ref);
+ r = &ref;
+ break;
+ }
+ case POINTER_TYPE:
+ {
+ if (!TYPE_PTRMEM_P (t))
+ {
+ tree bt (TREE_TYPE (t));
+ type& bt_node (emit_type (bt, access::public_, file, line, clmn));
+ t = TYPE_MAIN_VARIANT (t);
+ pointer& p (unit_->new_node<pointer> (file, line, clmn, t));
+ unit_->insert (t, p);
+ points& edge (unit_->new_edge<points> (p, bt_node));
+
+ // See if there is a name hint for the base type.
+ //
+ if (names* hint = unit_->find_hint (bt))
+ edge.hint (*hint);
+
+ process_named_pragmas (t, p);
+ r = &p;
+ }
+ else
+ {
+ t = TYPE_MAIN_VARIANT (t);
+ r = &unit_->new_node<unsupported_type> (
+ file, line, clmn, t, "pointer_to_data_member_type");
+ unit_->insert (t, *r);
+
+ if (trace)
+ ts << "unsupported pointer_to_data_member_type (" << r << ")"
+ << " at " << file << ":" << line << endl;
+ }
+ break;
+ }
+ default:
+ {
+ t = TYPE_MAIN_VARIANT (t);
+ r = &unit_->new_node<unsupported_type> (
+ file, line, clmn, t, gcc_tree_code_name(tc));
+ unit_->insert (t, *r);
+
+ if (trace)
+ ts << "unsupported " << gcc_tree_code_name(tc) << " (" << r << ")"
+ << " at " << file << ":" << line << endl;
+
+ break;
+ }
+ }
+
+ return *r;
+}
+
+string parser::impl::
+emit_type_name (tree type, bool direct)
+{
+ // First see if there is a "direct" name for this type.
+ //
+ if (direct)
+ {
+ if (tree decl = TYPE_NAME (type))
+ {
+ tree t (TREE_TYPE (decl));
+
+ if (t != 0 && same_type_p (type, t))
+ return IDENTIFIER_POINTER (DECL_NAME (decl));
+ }
+ }
+
+ string r;
+
+ if (CP_TYPE_CONST_P (type))
+ r += " const";
+
+ if (CP_TYPE_VOLATILE_P (type))
+ r += " volatile";
+
+ if (CP_TYPE_RESTRICT_P (type))
+ r += " __restrict";
+
+ gcc_tree_code_type tc (TREE_CODE (type));
+
+ switch (tc)
+ {
+ //
+ // User-defined types.
+ //
+
+ case RECORD_TYPE:
+ case UNION_TYPE:
+ {
+ tree ti (TYPE_TEMPLATE_INFO (type));
+
+ if (ti == NULL_TREE)
+ {
+ // Ordinary class.
+ //
+ type = TYPE_MAIN_VARIANT (type);
+
+ // Pointers to member functions are represented as record
+ // types and don't have names, not even the synthesized ones.
+ //
+ if (TYPE_PTRMEMFUNC_P (type))
+ r = "<pointer-to-member-function>" + r;
+ else
+ {
+ tree name (TYPE_NAME (type));
+ r = IDENTIFIER_POINTER (DECL_NAME (name)) + r;
+ }
+ }
+ else
+ {
+ // Template instantiation.
+ //
+ tree decl (TI_TEMPLATE (ti)); // DECL_TEMPLATE
+ string id (IDENTIFIER_POINTER (DECL_NAME (decl)));
+
+ id += '<';
+
+ tree args (INNERMOST_TEMPLATE_ARGS (TI_ARGS (ti)));
+
+ for (size_t i (0), n (TREE_VEC_LENGTH (args)); i < n ; ++i)
+ {
+ tree a (TREE_VEC_ELT (args, i));
+
+ if (i != 0)
+ id += ", ";
+
+ // Assume integer and type-only arguments.
+ //
+ if (TREE_CODE (a) == INTEGER_CST)
+ id += to_string (integer_value (a));
+ else
+ id += emit_type_name (a);
+ }
+
+ id += '>';
+
+ r = id + r;
+ }
+
+ break;
+ }
+
+ case ENUMERAL_TYPE:
+ {
+ type = TYPE_MAIN_VARIANT (type);
+ tree decl (TYPE_NAME (type));
+ r = IDENTIFIER_POINTER (DECL_NAME (decl)) + r;
+ break;
+ }
+
+ //
+ // Derived types.
+ //
+
+ case ARRAY_TYPE:
+ {
+ unsigned long long size (0);
+
+ if (tree index = TYPE_DOMAIN (type))
+ {
+ tree max (TYPE_MAX_VALUE (index));
+
+ if (TREE_CODE (max) == INTEGER_CST)
+ {
+ size = integer_value (max);
+
+ // Same as above.
+ //
+ if (size == ~(unsigned long long) (0))
+ size = 0;
+
+ size++;
+ }
+ else
+ {
+ // Non-integer array index which we do not support. The
+ // error has been/will be issued in emit_type.
+ //
+ }
+ }
+
+ tree t (TREE_TYPE (type));
+
+ if (size != 0)
+ {
+ ostringstream ostr;
+ ostr << size;
+ r = emit_type_name (t) + "[" + ostr.str () + "]" + r;
+ }
+ else
+ r = emit_type_name (t) + "[]" + r;
+
+ break;
+ }
+ case REFERENCE_TYPE:
+ {
+ tree t (TREE_TYPE (type));
+ r = emit_type_name (t) + "&" + r;
+ break;
+ }
+ case POINTER_TYPE:
+ {
+ if (!TYPE_PTRMEM_P (type))
+ {
+ tree t (TREE_TYPE (type));
+ r = emit_type_name (t) + "*" + r;
+ }
+ else
+ r = "<pointer_to_member_type>";
+
+ break;
+ }
+
+ //
+ // Fundamental types.
+ //
+
+ case VOID_TYPE:
+ case REAL_TYPE:
+ case BOOLEAN_TYPE:
+ case INTEGER_TYPE:
+ {
+ type = TYPE_MAIN_VARIANT (type);
+ tree decl (TYPE_NAME (type));
+ r = IDENTIFIER_POINTER (DECL_NAME (decl)) + r;
+ break;
+ }
+ default:
+ {
+ r = "<" + string (gcc_tree_code_name(tc)) + ">";
+ break;
+ }
+ }
+
+ return r;
+}
+
+void parser::impl::
+process_pragmas (declaration const& decl,
+ node& node,
+ string const& name,
+ decl_set::const_iterator begin,
+ decl_set::const_iterator cur,
+ decl_set::const_iterator /*end*/)
+{
+ // First process the position pragmas by iterating backwards until
+ // we get to the preceding non-pragma declaration that has been
+ // associated.
+ //
+ pragma_set prags;
+
+ if (cur != begin)
+ {
+ decl_set::const_iterator i (cur);
+ for (--i; i != begin && (i->prag != 0 || !i->assoc); --i) ;
+
+ for (; i != cur; ++i)
+ {
+ if (i->prag == 0) // Skip declarations.
+ continue;
+
+ assert (!i->assoc);
+
+ if (i->prag->check (decl, name, i->prag->pragma_name, i->prag->loc))
+ prags.insert (*i->prag);
+ else
+ error_++; // Diagnostic has already been issued.
+
+ i->assoc = true; // Mark this pragma as associated.
+ }
+
+ cur->assoc = true; // Mark the declaration as associated.
+ }
+
+ // Now see if there are any named pragmas for this declaration. By
+ // doing this after handling the position pragmas we ensure correct
+ // overriding.
+ //
+ {
+ decl_pragmas::const_iterator i (decl_pragmas_.find (decl));
+
+ if (i != decl_pragmas_.end ())
+ prags.insert (i->second.begin (), i->second.end ());
+ }
+
+ // Finally, copy the resulting pragma set to context.
+ //
+ for (pragma_set::iterator i (prags.begin ()); i != prags.end (); ++i)
+ add_pragma (node, i->second);
+}
+
+void parser::impl::
+process_named_pragmas (declaration const& decl, node& node)
+{
+ pragma_set prags;
+
+ decl_pragmas::const_iterator i (decl_pragmas_.find (decl));
+
+ if (i != decl_pragmas_.end ())
+ prags.insert (i->second.begin (), i->second.end ());
+
+ // Copy the resulting pragma set to context.
+ //
+ for (pragma_set::iterator i (prags.begin ()); i != prags.end (); ++i)
+ add_pragma (node, i->second);
+}
+
+void parser::impl::
+add_pragma (node& n, pragma const& p)
+{
+ if (trace)
+ ts << "\t\t pragma " << p.pragma_name << endl;
+
+ string const& k (p.context_name);
+
+ if (p.add == 0)
+ {
+ n.set (k, p.value);
+ n.set (k + "-location", p.loc);
+ }
+ else
+ p.add (n, k, p.value, p.loc);
+}
+
+void parser::impl::
+diagnose_unassoc_pragmas (decl_set const& decls)
+{
+ for (decl_set::const_iterator i (decls.begin ()), e (decls.end ());
+ i != e; ++i)
+ {
+ if (i->prag && !i->assoc)
+ {
+ pragma const& p (*i->prag);
+ error (p.loc)
+ << "db pragma '" << p.pragma_name << "' is not associated with a "
+ << "declaration" << endl;
+ error_++;
+ }
+ }
+}
+
+string parser::impl::
+fq_scope (tree decl)
+{
+ string s, tmp;
+
+ for (tree scope (CP_DECL_CONTEXT (decl));
+ scope != global_namespace;)
+ {
+ tree prev (CP_DECL_CONTEXT (scope));
+
+ // If this is an inline namespace, pretend it doesn't exist.
+ //
+#if BUILDING_GCC_MAJOR >= 8
+ if (!is_nested_namespace (prev, scope, true))
+#else
+ if (!is_associated_namespace (prev, scope))
+#endif
+ {
+ tree n = DECL_NAME (scope);
+
+ tmp = "::";
+ tmp += (n != NULL_TREE ? IDENTIFIER_POINTER (n) : "");
+ tmp += s;
+ s.swap (tmp);
+ }
+
+ scope = prev;
+ }
+
+ return s;
+}
+
+//
+// parser
+//
+
+parser::
+~parser ()
+{
+ // Needs parser::impl definition.
+}
+
+parser::
+parser (options const& ops,
+ loc_pragmas& lp,
+ ns_loc_pragmas& nslp,
+ decl_pragmas& dp)
+ : impl_ (new impl (ops, lp, nslp, dp))
+{
+}
+
+unique_ptr<unit> parser::
+parse (tree global_scope, path const& main_file)
+{
+ return impl_->parse (global_scope, main_file);
+}
diff --git a/odb/odb/parser.hxx b/odb/odb/parser.hxx
new file mode 100644
index 0000000..b26c8f1
--- /dev/null
+++ b/odb/odb/parser.hxx
@@ -0,0 +1,37 @@
+// file : odb/parser.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_PARSER_HXX
+#define ODB_PARSER_HXX
+
+#include <odb/gcc.hxx>
+
+#include <memory> // std::unique_ptr
+
+#include <odb/pragma.hxx>
+#include <odb/options.hxx>
+#include <odb/semantics/unit.hxx>
+
+class parser
+{
+public:
+ class failed {};
+
+ ~parser ();
+ parser (options const&, loc_pragmas&, ns_loc_pragmas&, decl_pragmas&);
+
+ std::unique_ptr<semantics::unit>
+ parse (tree global_scope, semantics::path const& main_file);
+
+private:
+ parser (parser const&);
+
+ parser&
+ operator= (parser const&);
+
+private:
+ class impl;
+ std::unique_ptr<impl> impl_;
+};
+
+#endif // ODB_PARSER_HXX
diff --git a/odb/odb/plugin.cxx b/odb/odb/plugin.cxx
new file mode 100644
index 0000000..c065a8a
--- /dev/null
+++ b/odb/odb/plugin.cxx
@@ -0,0 +1,458 @@
+// file : odb/plugin.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx> // Keep it first.
+
+#include <unistd.h> // stat()
+#include <sys/types.h> // stat
+#include <sys/stat.h> // stat
+
+#include <memory> // std::unique_ptr
+#include <string>
+#include <vector>
+#include <cstring> // std::strcpy, std::strstr
+#include <cassert>
+#include <iostream>
+
+#include <libcutl/re.hxx>
+#include <libcutl/fs/path.hxx>
+
+#include <odb/pragma.hxx>
+#include <odb/parser.hxx>
+#include <odb/options.hxx>
+#include <odb/option-functions.hxx>
+#include <odb/features.hxx>
+#include <odb/profile.hxx>
+#include <odb/version.hxx>
+#include <odb/validator.hxx>
+#include <odb/processor.hxx>
+#include <odb/generator.hxx>
+#include <odb/semantics/unit.hxx>
+
+using namespace std;
+using namespace semantics;
+
+using cutl::fs::path;
+using cutl::fs::invalid_path;
+
+typedef vector<path> paths;
+
+#if defined(_WIN32) && !defined(ODB_STATIC_PLUGIN)
+__declspec(dllexport)
+#endif
+int plugin_is_GPL_compatible;
+
+unique_ptr<options const> options_;
+paths profile_paths_;
+path file_; // File being compiled.
+paths inputs_; // List of input files in at-once mode or just file_.
+
+bool (*cpp_diagnostic_prev) (
+ cpp_reader*,
+#if BUILDING_GCC_MAJOR >= 9
+ cpp_diagnostic_level,
+ cpp_warning_reason,
+#else
+ int,
+ int,
+#endif
+#if BUILDING_GCC_MAJOR >= 6
+ rich_location*,
+#else
+ location_t,
+ unsigned int,
+#endif
+ const char*,
+ va_list*);
+
+static bool
+cpp_diagnostic_filter (cpp_reader* r,
+#if BUILDING_GCC_MAJOR >= 9
+ cpp_diagnostic_level level,
+ cpp_warning_reason reason,
+#else
+ int level,
+ int reason,
+#endif
+#if BUILDING_GCC_MAJOR >= 6
+ rich_location* l,
+#else
+ location_t l,
+ unsigned int column_override,
+#endif
+ const char* msg,
+ va_list* ap)
+{
+ // #pragma once in the main file. Note that the message that we get is
+ // potentially translated so we search for the substring (there is
+ // currently only one warning in libcpp that mentions #pragma once).
+ // Unfortunately, some translations translate the 'once' word (e.g,
+ // #pragma uno; I wonder if one can actually specify it like that in
+ // the source code). Oh, well, there is only so much we can do.
+ //
+ if (strstr (msg, "#pragma once") != 0)
+ return true;
+
+ return cpp_diagnostic_prev (
+ r,
+ level,
+ reason,
+#if BUILDING_GCC_MAJOR >= 6
+ l,
+#else
+ l,
+ column_override,
+#endif
+ msg,
+ ap);
+}
+
+// A prefix of the _cpp_file struct. This struct is not part of the
+// public interface so we have to resort to this technique (based on
+// libcpp/files.c).
+//
+struct cpp_file_prefix
+{
+ char const* name;
+ char const* path;
+ char const* pchname;
+ char const* dir_name;
+ _cpp_file* next_file;
+ const uchar* buffer;
+ const uchar* buffer_start;
+ const cpp_hashnode *cmacro;
+ cpp_dir *dir;
+ struct stat st;
+};
+
+extern "C" void
+start_unit_callback (void*, void*)
+{
+ // Set the preprocessor error callback to filter out useless diagnostics.
+ //
+ cpp_callbacks* cb (cpp_get_callbacks (parse_in));
+
+#if BUILDING_GCC_MAJOR >= 9
+ cpp_diagnostic_prev = cb->diagnostic;
+ cb->diagnostic = &cpp_diagnostic_filter;
+#else
+ cpp_diagnostic_prev = cb->error;
+ cb->error = &cpp_diagnostic_filter;
+#endif
+
+ if (cpp_diagnostic_prev == 0)
+ {
+ cerr << "ice: expected cpp diagnostic callback to be set" << endl;
+ exit (1);
+ }
+
+ // Set the directory of the main file (stdin) to that of the orginal
+ // file so that relative inclusion works. Also adjust the path and
+ // re-stat the file so that #pragma once works.
+ //
+ cpp_buffer* b (cpp_get_buffer (parse_in));
+ _cpp_file* f (cpp_get_file (b));
+ cpp_dir* d (cpp_get_dir (f));
+ char const* p (cpp_get_path (f));
+
+ cpp_file_prefix* fp (reinterpret_cast<cpp_file_prefix*> (f));
+
+ // Perform some sanity checks.
+ //
+ if (p != 0 && *p == '\0' // The path should be empty (stdin).
+ && cpp_get_prev (b) == 0 // This is the only buffer (main file).
+ && fp->path == p // Our prefix corresponds to the actual type.
+ && fp->dir == d // Our prefix corresponds to the actual type.
+ && fp->dir_name == 0) // The directory part hasn't been initialized.
+ {
+ // The dir_name is initialized by libcpp lazily so we can preemptively
+ // set it to what we need.
+ //
+ path d (file_.directory ());
+ char* s;
+
+ if (d.empty ())
+ {
+ s = XNEWVEC (char, 1);
+ *s = '\0';
+ }
+ else
+ {
+ size_t n (d.string ().size ());
+ s = XNEWVEC (char, n + 2);
+ strcpy (s, d.string ().c_str ());
+ s[n] = path::traits::directory_separator;
+ s[n + 1] = '\0';
+ }
+
+ fp->dir_name = s;
+
+ // Unless we are in the at-once mode (where input files are actually
+ // #include'ed into the synthesized stdin), pretend that we are the
+ // actual input file. This is necessary for the #pragma once to work.
+ //
+ // All this relies on the way things are implemented in libcpp. In
+ // particular, the #pragma once code first checks if the mtime and
+ // size of files match (that's why we need stat() below). If they
+ // do, then it goes ahead and compares their contents. To re-load
+ // the contents of the file libcpp uses the path (that's why we
+ // need to adjust that as well).
+ //
+ if (inputs_.size () == 1)
+ {
+ string const& f (file_.string ());
+
+ XDELETEVEC (fp->path);
+ size_t n (f.size ());
+ char* p (XNEWVEC (char, n + 1));
+ strcpy (p, f.c_str ());
+ p[n] = '\0';
+ fp->path = p;
+
+ // This call shouldn't fail since we've just opened it in the driver.
+ stat (fp->path, &fp->st);
+ }
+ }
+ else
+ {
+ cerr << "ice: unable to initialize main file directory" << endl;
+ exit (1);
+ }
+}
+
+extern "C" void
+gate_callback (void*, void*)
+{
+ // If there were errors during compilation, let GCC handle the
+ // exit.
+ //
+ if (errorcount || sorrycount)
+ return;
+
+ int r (0);
+
+ try
+ {
+ // Post process pragmas.
+ //
+ post_process_pragmas ();
+
+ // Parse the GCC tree to semantic graph.
+ //
+ parser p (*options_, loc_pragmas_, ns_loc_pragmas_, decl_pragmas_);
+ unique_ptr<unit> u (p.parse (global_namespace, file_));
+
+ features f;
+
+ // Process, pass 1.
+ //
+ process (*options_, f, *u, file_, 1);
+
+ // Validate, pass 1.
+ //
+ validate (*options_, f, *u, file_, 1);
+
+ // Process, pass 2.
+ //
+ process (*options_, f, *u, file_, 2);
+
+ // Validate, pass 2.
+ //
+ validate (*options_, f, *u, file_, 2);
+
+ // Generate.
+ //
+ generate (*options_, f, *u, file_, inputs_);
+ }
+ catch (cutl::re::format const& e)
+ {
+ cerr << "error: invalid regex: '" << e.regex () << "': " <<
+ e.description () << endl;
+ r = 1;
+ }
+ catch (pragmas_failed const&)
+ {
+ // Diagnostics has aready been issued.
+ //
+ r = 1;
+ }
+ catch (parser::failed const&)
+ {
+ // Diagnostics has aready been issued.
+ //
+ r = 1;
+ }
+ catch (validator_failed const&)
+ {
+ // Diagnostics has aready been issued.
+ //
+ r = 1;
+ }
+ catch (processor_failed const&)
+ {
+ // Diagnostics has aready been issued.
+ //
+ r = 1;
+ }
+ catch (generator_failed const&)
+ {
+ // Diagnostics has aready been issued.
+ //
+ r = 1;
+ }
+
+ exit (r);
+}
+
+static char const* const odb_version = ODB_COMPILER_VERSION_STR;
+
+typedef vector<string> strings;
+
+extern "C"
+#if defined(_WIN32) && !defined(ODB_STATIC_PLUGIN)
+__declspec(dllexport)
+#endif
+int
+plugin_init (plugin_name_args* plugin_info, plugin_gcc_version*)
+{
+ int r (0);
+ plugin_info->version = odb_version;
+
+ try
+ {
+ // Parse options.
+ //
+ {
+ strings argv_str;
+ argv_str.push_back (plugin_info->base_name);
+
+ for (int i (0); i < plugin_info->argc; ++i)
+ {
+ plugin_argument& a (plugin_info->argv[i]);
+
+ // A value cannot contain '=' so it is passed as the backspace
+ // character.
+ //
+ string v (a.value != 0 ? a.value : "");
+ for (size_t i (0); i < v.size (); ++i)
+ if (v[i] == '\b')
+ v[i] = '=';
+
+ // Handle service options.
+ //
+ if (strcmp (a.key, "svc-path") == 0)
+ {
+ profile_paths_.push_back (path (v));
+ continue;
+ }
+
+ if (strcmp (a.key, "svc-file") == 0)
+ {
+ // First is the main file. Subsequent are inputs in the at-once
+ // mode.
+ //
+ if (file_.empty ())
+ file_ = path (v);
+ else
+ inputs_.push_back (path (v));
+
+ continue;
+ }
+
+ string opt (strlen (a.key) > 1 ? "--" : "-");
+ opt += a.key;
+
+ argv_str.push_back (opt);
+
+ if (a.value != 0)
+ argv_str.push_back (v);
+ }
+
+ vector<char*> argv;
+ for (strings::iterator i (argv_str.begin ()); i != argv_str.end (); ++i)
+ argv.push_back (const_cast<char*> (i->c_str ()));
+
+ int argc (static_cast<int> (argv.size ()));
+
+ if (inputs_.empty ())
+ inputs_.push_back (file_);
+
+ // Two-phase options parsing, similar to the driver.
+ //
+ cli::argv_file_scanner::option_info oi[3];
+ oi[0].option = "--options-file";
+ oi[0].search_func = 0;
+ oi[1].option = "-p";
+ oi[2].option = "--profile";
+
+ database db;
+ {
+ oi[1].search_func = &profile_search_ignore;
+ oi[2].search_func = &profile_search_ignore;
+
+ cli::argv_file_scanner scan (argc, &argv[0], oi, 3);
+ options ops (scan);
+ assert (ops.database_specified ());
+ db = ops.database ()[0];
+ }
+
+ profile_data pd (profile_paths_, db, "odb plugin");
+ oi[1].search_func = &profile_search;
+ oi[2].search_func = &profile_search;
+ oi[1].arg = &pd;
+ oi[2].arg = &pd;
+
+ cli::argv_file_scanner scan (argc, &argv[0], oi, 3);
+ unique_ptr<options> ops (
+ new options (scan, cli::unknown_mode::fail, cli::unknown_mode::fail));
+
+ // Process options.
+ //
+ process_options (*ops);
+
+ options_ = move (ops);
+ pragma_db_ = db;
+ pragma_multi_ = options_->multi_database ();
+ }
+
+ if (options_->trace ())
+ cerr << "starting plugin " << plugin_info->base_name << endl;
+
+ // Disable assembly output. GCC doesn't define HOST_BIT_BUCKET
+ // correctly for MinGW (it still used /dev/null which fails to
+ // open).
+ //
+#ifdef _WIN32
+ asm_file_name = "nul";
+#else
+ asm_file_name = HOST_BIT_BUCKET;
+#endif
+
+ // Register callbacks.
+ //
+ register_callback (plugin_info->base_name,
+ PLUGIN_PRAGMAS,
+ register_odb_pragmas,
+ 0);
+
+ register_callback (plugin_info->base_name,
+ PLUGIN_START_UNIT,
+ start_unit_callback,
+ 0);
+
+ register_callback (plugin_info->base_name,
+ PLUGIN_OVERRIDE_GATE,
+ &gate_callback,
+ 0);
+ }
+ catch (cli::exception const& ex)
+ {
+ cerr << ex << endl;
+ r = 1;
+ }
+
+ if (r != 0)
+ exit (r);
+
+ return r;
+}
diff --git a/odb/odb/pragma.cxx b/odb/odb/pragma.cxx
new file mode 100644
index 0000000..6668733
--- /dev/null
+++ b/odb/odb/pragma.cxx
@@ -0,0 +1,4442 @@
+// file : odb/pragma.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <cctype> // std::isalnum
+#include <limits>
+#include <vector>
+#include <sstream>
+
+#include <odb/diagnostics.hxx>
+#include <odb/lookup.hxx>
+#include <odb/pragma.hxx>
+#include <odb/cxx-token.hxx>
+#include <odb/cxx-lexer.hxx>
+
+#include <odb/context.hxx>
+#include <odb/relational/context.hxx>
+
+using namespace std;
+using namespace cutl;
+
+using container::any;
+
+virt_declarations virt_declarations_;
+loc_pragmas loc_pragmas_;
+decl_pragmas decl_pragmas_;
+ns_loc_pragmas ns_loc_pragmas_;
+
+database pragma_db_;
+multi_database pragma_multi_;
+
+template <typename X>
+void
+accumulate (compiler::context& ctx, string const& k, any const& v, location_t)
+{
+ // Empty values are used to indicate that this pragma must be ignored.
+ //
+ if (v.empty ())
+ return;
+
+ typedef vector<X> container;
+
+ container& c (ctx.count (k)
+ ? ctx.get<container> (k)
+ : ctx.set (k, container ()));
+
+ c.push_back (v.value<X> ());
+}
+
+// Parse a qualified string. It can be in one of the following formats:
+//
+// "foo.bar.baz" (can have empty leading component, e.g., ".bar.baz")
+// "foo"."bar"."baz" (can have empty leading component, e.g., ""."bar"."baz")
+// ."foo.bar" (used to specify unqualifed names with periods)
+//
+// Empty leading components means fully qualified (similar to ::foo in C++).
+//
+static bool
+parse_qname (cxx_lexer& l,
+ cpp_ttype& tt,
+ string& tl,
+ tree& tn,
+ string const& p, // Pragma name for diagnostic.
+ qname& name,
+ bool* expr = 0, // If specified, detect an expression
+ string* expr_str = 0) // and store it here.
+{
+ assert (tt == CPP_STRING || tt == CPP_DOT);
+
+ // Handle the special case of unqualified name which can contain periods.
+ //
+ if (tt == CPP_DOT)
+ {
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_STRING)
+ {
+ error (l) << "name expected after '.' in db pragma " << p << endl;
+ return false;
+ }
+
+ name = tl;
+ tt = l.next (tl, &tn);
+ return true;
+ }
+
+ name.clear ();
+ string str (tl);
+
+ // See what comes after the string.
+ //
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_DOT)
+ {
+ name.append (str);
+
+ for (; tt == CPP_DOT; tt = l.next (tl, &tn))
+ {
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_STRING)
+ {
+ error (l) << "name expected after '.' in db pragma " << p << endl;
+ return false;
+ }
+
+ name.append (tl);
+ }
+
+ return true;
+ }
+
+ if (expr != 0 && tt == CPP_PLUS)
+ {
+ *expr = true;
+ *expr_str = str;
+ return true;
+ }
+
+ // Scan the string looking for '.' as well as for non-identifier
+ // characters if we need to detect expressions.
+ //
+ string::size_type prev (string::npos);
+
+ for (size_t i (0); i < str.size (); ++i)
+ {
+ char c (str[i]);
+
+ if (c == '.')
+ {
+ if (prev == string::npos)
+ name.append (string (str, 0, i));
+ else
+ name.append (string (str, prev + 1, i - prev - 1));
+
+ prev = i;
+ }
+ else if (expr != 0 && !(isalnum (c) || c == '_'))
+ {
+ *expr = true;
+ *expr_str = str;
+ return true;
+ }
+ }
+
+ if (prev == string::npos)
+ name.append (str);
+ else
+ name.append (string (str, prev + 1, string::npos));
+
+ return true;
+}
+
+static bool
+parse_expression (cxx_lexer& l,
+ cpp_ttype& tt,
+ string& tl,
+ tree& tn,
+ cxx_tokens& ts,
+ string const& prag)
+{
+ // Keep reading tokens until we see a mis-matching ')' or ',' while
+ // keeping track of the '()' and '{}' balance.
+ //
+ size_t p_balance (0), b_balance (0);
+
+ for (; tt != CPP_EOF; tt = l.next (tl, &tn))
+ {
+ bool done (false);
+ cxx_token ct (l.location (), tt);
+
+ switch (tt)
+ {
+ case CPP_OPEN_BRACE:
+ {
+ b_balance++;
+ break;
+ }
+ case CPP_CLOSE_BRACE:
+ {
+ b_balance--;
+ break;
+ }
+ case CPP_OPEN_PAREN:
+ {
+ p_balance++;
+ break;
+ }
+ case CPP_CLOSE_PAREN:
+ {
+ if (p_balance == 0 && b_balance == 0)
+ done = true;
+ else
+ p_balance--;
+ break;
+ }
+ case CPP_COMMA:
+ {
+ if (p_balance == 0 && b_balance == 0)
+ done = true;
+ else
+ break;
+ }
+ case CPP_STRING:
+ {
+ ct.literal = tl;
+ break;
+ }
+ case CPP_NAME:
+ //case CPP_KEYWORD: see default:
+ {
+ ct.literal = tl;
+ break;
+ }
+ case CPP_NUMBER:
+ {
+ switch (TREE_CODE (tn))
+ {
+ case INTEGER_CST:
+ {
+ tree type (TREE_TYPE (tn));
+ unsigned long long v (integer_value (tn));
+
+ ostringstream os;
+ os << v;
+
+ if (type == long_long_integer_type_node)
+ os << "LL";
+ else if (type == long_long_unsigned_type_node)
+ os << "ULL";
+ else if (type == long_integer_type_node)
+ os << "L";
+ else if (type == long_unsigned_type_node)
+ os << "UL";
+ else if (
+ TYPE_UNSIGNED (type) &&
+ TYPE_PRECISION (type) >= TYPE_PRECISION (integer_type_node))
+ os << "U";
+
+ ct.literal = os.str ();
+ break;
+ }
+ case REAL_CST:
+ {
+ tree type (TREE_TYPE (tn));
+ REAL_VALUE_TYPE val (TREE_REAL_CST (tn));
+
+ // This is the best we can do. val cannot be INF or NaN.
+ //
+ char tmp[256];
+ real_to_decimal (tmp, &val, sizeof (tmp), 0, true);
+ istringstream is (tmp);
+ ostringstream os;
+
+ if (type == float_type_node)
+ {
+ float f;
+ is >> f;
+ os << f << 'F';
+ }
+ else
+ {
+ double d;
+ is >> d;
+ os << d;
+ }
+
+ ct.literal = os.str ();
+ break;
+ }
+ default:
+ {
+ error (l) << "unexpected numeric constant in db pragma "
+ << prag << endl;
+ return false;
+ }
+ }
+
+ break;
+ }
+ default:
+ {
+ // CPP_KEYWORD is not in the cpp_ttype enumeration.
+ //
+ if (tt == CPP_KEYWORD)
+ ct.literal = tl;
+
+ break;
+ }
+ }
+
+ if (done)
+ break;
+
+ // We don't store the tree node in ct since we converted numbers to
+ // string literals.
+ //
+ ts.push_back (ct);
+ }
+
+ return true;
+}
+
+static string
+parse_scoped_name (cxx_lexer& l,
+ cpp_ttype& tt,
+ string& tl,
+ tree& tn,
+ string const& prag)
+{
+ try
+ {
+ return lookup::parse_scoped_name (l, tt, tl, tn);
+ }
+ catch (lookup::invalid_name const&)
+ {
+ error (l) << "invalid name in db pragma " << prag << endl;
+ return "";
+ }
+}
+
+static tree
+resolve_scoped_name (cxx_lexer& l,
+ cpp_ttype& tt,
+ string& tl,
+ tree& tn,
+ tree start_scope,
+ string& name,
+ bool is_type,
+ string const& prag,
+ bool trailing_scope = false,
+ cpp_ttype* prev_tt = 0)
+{
+ try
+ {
+ cpp_ttype ptt; // Not used.
+ tree r (
+ lookup::resolve_scoped_name (
+ l, tt, tl, tn, ptt, start_scope, name, is_type, trailing_scope));
+
+ if (prev_tt != 0)
+ *prev_tt = ptt;
+
+ return r;
+ }
+ catch (lookup::invalid_name const&)
+ {
+ error (l) << "invalid name in db pragma " << prag << endl;
+ return 0;
+ }
+ catch (lookup::unable_to_resolve const& e)
+ {
+ if (e.last ())
+ error (l) << "unable to resolve " << (is_type ? "type " : "") << "name "
+ << "'" << e.name () << "' in db pragma " << prag << endl;
+ else
+ error (l) << "unable to resolve name '" << e.name () << "' in db pragma "
+ << prag << endl;
+
+ return 0;
+ }
+}
+
+// Resolve a data member in the specified scope taking into account virtual
+// member declarations.
+//
+declaration
+resolve_data_member (tree scope,
+ const cxx_tokens& name,
+ string& decl_name, // Note: appended to.
+ string const& prag)
+{
+ declaration decl;
+
+ if (name.size () == 1 && name.back ().type == CPP_NAME)
+ {
+ virt_declarations::iterator i (virt_declarations_.find (scope));
+
+ if (i != virt_declarations_.end ())
+ {
+ string const& n (name.back ().literal);
+
+ virt_declaration_set::const_iterator j (i->second.find (n, FIELD_DECL));
+
+ if (j != i->second.end ())
+ {
+ decl = declaration (*j);
+ decl_name += n;
+ }
+ }
+ }
+
+ if (!decl)
+ {
+ cxx_tokens_lexer l;
+ l.start (name);
+
+ tree tn;
+ string tl;
+ cpp_ttype tt (l.next (tl));
+
+ tree d (resolve_scoped_name (
+ l, tt, tl, tn, scope, decl_name, false, prag));
+
+ if (d == 0)
+ return decl; // Diagnostics has already been issued.
+
+ decl = declaration (d);
+ }
+
+ return decl;
+}
+
+static bool
+check_spec_decl_type (declaration const& d,
+ string const& name,
+ string const& p,
+ location_t l)
+{
+ gcc_tree_code_type tc (d.tree_code ());
+ bool type (TREE_CODE_CLASS (tc) == tcc_type);
+
+ if (p == "no_id")
+ {
+ // No_id can be used on objects only.
+ //
+ if (tc != RECORD_TYPE)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a class" << endl;
+ return false;
+ }
+ }
+ else if (p == "id" ||
+ p == "auto" ||
+ p == "column" ||
+ p == "inverse" ||
+ p == "on_delete" ||
+ p == "points_to" ||
+ p == "section" ||
+ p == "load" ||
+ p == "update" ||
+ p == "version" ||
+ p == "index" ||
+ p == "unique" ||
+ p == "get" ||
+ p == "set" ||
+ p == "access")
+ {
+ if (tc != FIELD_DECL)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "transient")
+ {
+ // Transient can be used for both data members and classes (object,
+ // view, or composite value).
+ //
+ if (tc != FIELD_DECL && tc != RECORD_TYPE)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a data member or class" << endl;
+ return false;
+ }
+ }
+ else if (p == "added")
+ {
+ // Added can be used for data members only.
+ //
+ if (tc != FIELD_DECL)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "deleted")
+ {
+ // Deleted can be used for both data members and classes (object,
+ // view of composite value).
+ //
+ if (tc != FIELD_DECL && tc != RECORD_TYPE)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a data member or class" << endl;
+ return false;
+ }
+ }
+ else if (p == "readonly")
+ {
+ // Readonly can be used for both data members and classes (object or
+ // composite value).
+ //
+ if (tc != FIELD_DECL && tc != RECORD_TYPE)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a data member or class" << endl;
+ return false;
+ }
+ }
+ else if (p == "abstract" ||
+ p == "callback" ||
+ p == "query" ||
+ p == "object" ||
+ p == "optimistic" ||
+ p == "polymorphic" ||
+ p == "definition" ||
+ p == "sectionable" ||
+ p == "bulk")
+ {
+ if (tc != RECORD_TYPE)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a class" << endl;
+ return false;
+ }
+ }
+ else if (p == "pointer")
+ {
+ // Table can be used for namespaces and classes (object or view).
+ //
+ if (tc != NAMESPACE_DECL && tc != RECORD_TYPE)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a class" << endl;
+ return false;
+ }
+ }
+ else if (p == "table")
+ {
+ // Table can be used for namespaces, members (container), and types
+ // (container, object, or view).
+ //
+ if (tc != NAMESPACE_DECL && tc != FIELD_DECL && !type)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a namespace, type, or data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "session")
+ {
+ // Session can be used only for namespaces and objects.
+ //
+ if (tc != NAMESPACE_DECL && tc != RECORD_TYPE)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a namespace or class" << endl;
+ return false;
+ }
+ }
+ else if (p == "schema")
+ {
+ // For now schema can be used only for namespaces and objects.
+ //
+ if (tc != NAMESPACE_DECL && tc != RECORD_TYPE)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a namespace or class" << endl;
+ return false;
+ }
+ }
+ else if (p == "type" ||
+ p == "id_type" ||
+ p == "value_type" ||
+ p == "index_type" ||
+ p == "key_type")
+ {
+ // Type can be used for both members and types.
+ //
+ if (tc != FIELD_DECL && !type)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a type or data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "default")
+ {
+ // Default can be used for both members and types.
+ //
+ if (tc != FIELD_DECL && !type)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a type or data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "value_column" ||
+ p == "index_column" ||
+ p == "key_column" ||
+ p == "id_column")
+ {
+ // Container columns can be used for both members (container) and
+ // types (container).
+ //
+ if (tc != FIELD_DECL && !type)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a type or data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "options" ||
+ p == "value_options" ||
+ p == "index_options" ||
+ p == "key_options" ||
+ p == "id_options")
+ {
+ // Options can be used for both members and types.
+ //
+ if (tc != FIELD_DECL && !type)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a type or data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "null" ||
+ p == "not_null" ||
+ p == "key_null" ||
+ p == "key_not_null" ||
+ p == "value_null" ||
+ p == "value_not_null")
+ {
+ // Null pragmas can be used for both members and types (values,
+ // containers, and pointers).
+ //
+ if (tc != FIELD_DECL && !type)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a type or data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "unordered")
+ {
+ // Unordered can be used for both members (container) and
+ // types (container).
+ //
+ if (tc != FIELD_DECL && !type)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a type or data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "virtual")
+ {
+ // Virtual is specified for a member.
+ //
+ if (tc != FIELD_DECL)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a data member" << endl;
+ return false;
+ }
+ }
+ else if (p == "simple" ||
+ p == "container")
+ {
+ // Apply to both members and types.
+ //
+ if (tc != FIELD_DECL && !type)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a type or data member" << endl;
+ return false;
+ }
+ }
+ else
+ {
+ error (l) << "unknown db pragma " << p << endl;
+ return false;
+ }
+
+ return true;
+}
+
+static void
+add_pragma (pragma const& prag, declaration const& decl, bool ns)
+{
+ if (decl)
+ decl_pragmas_[decl].insert (prag);
+ else
+ {
+ tree scope (current_scope ());
+
+ if (!ns)
+ {
+ if (!CLASS_TYPE_P (scope))
+ scope = global_namespace;
+
+ loc_pragmas_[scope].push_back (prag);
+ }
+ else
+ ns_loc_pragmas_.push_back (ns_loc_pragma (scope, prag));
+ }
+}
+
+static void
+handle_pragma (cxx_lexer& l,
+ string db,
+ string p,
+ string const& qualifier,
+ any& qualifier_value,
+ declaration const& decl,
+ string const& decl_name,
+ bool ns) // True if this is a position namespace pragma.
+{
+ cpp_ttype tt;
+ string tl;
+ tree tn;
+
+ // See if there is a database prefix. The db argument may already
+ // contain it.
+ //
+ if (db.empty () &&
+ (p == "mysql" ||
+ p == "sqlite" ||
+ p == "pgsql" ||
+ p == "oracle" ||
+ p == "mssql"))
+ {
+ tt = l.next (tl);
+
+ if (tt != CPP_COLON)
+ {
+ error (l) << "':' expected after database prefix " << p << endl;
+ return;
+ }
+
+ // Specifier prefix.
+ //
+ db = p;
+ tt = l.next (p);
+
+ if (tt != CPP_NAME && tt != CPP_KEYWORD)
+ {
+ error (l) << "expected specifier after databas prefix " << db << endl;
+ return;
+ }
+ }
+
+ string name (p); // Pragma name.
+ any val; // Pragma value.
+ pragma::add_func adder (0); // Custom context adder.
+ location_t loc (l.location ()); // Pragma location.
+
+ if (qualifier == "model")
+ {
+ // version(unsigned long long base,
+ // unsigned long long current,
+ // open|closed)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ assert (decl == global_namespace);
+
+ if (p == "version")
+ {
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ model_version v;
+
+ // base
+ //
+ if (l.next (tl, &tn) != CPP_NUMBER || TREE_CODE (tn) != INTEGER_CST)
+ {
+ error (l) << "unsigned integer expected as base version" << endl;
+ return;
+ }
+
+ v.base = integer_value (tn);
+
+ if (v.base == 0)
+ {
+ error (l) << "base version cannot be zero" << endl;
+ return;
+ }
+
+ // current
+ //
+ if (l.next (tl, &tn) != CPP_COMMA)
+ {
+ error (l) << "current version expected after base version" << endl;
+ return;
+ }
+
+ if (l.next (tl, &tn) != CPP_NUMBER || TREE_CODE (tn) != INTEGER_CST)
+ {
+ error (l) << "unsigned integer expected as current version" << endl;
+ return;
+ }
+
+ v.current = integer_value (tn);
+
+ if (v.current == 0)
+ {
+ error (l) << "current version cannot be zero" << endl;
+ return;
+ }
+
+ if (v.base > v.current)
+ {
+ error (l) << "current version should be greater than or equal to " <<
+ "base version" << endl;
+ return;
+ }
+
+ // open|closed
+ //
+ tt = l.next (tl, &tn);
+ if (tt == CPP_COMMA)
+ {
+ if (l.next (tl, &tn) != CPP_NAME || (tl != "open" && tl != "closed"))
+ {
+ error (l) << "open or closed expected after current version" << endl;
+ return;
+ }
+
+ v.open = (tl == "open");
+ tt = l.next (tl, &tn);
+ }
+ else
+ v.open = true;
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ name = "model-version";
+ val = v;
+ tt = l.next (tl, &tn);
+ }
+ else
+ {
+ error (l) << "unknown db pragma " << p << endl;
+ return;
+ }
+ }
+ else if (qualifier == "map")
+ {
+ // type("<regex>") | type(<name>)
+ // as("<subst>") | as(<name>)
+ // to("<subst>") | to(<expr>)
+ // from("<subst>") | from(<expr>)
+ //
+
+ if (p != "type" &&
+ p != "as" &&
+ p != "to" &&
+ p != "from")
+ {
+ error (l) << "unknown db pragma " << p << endl;
+ return;
+ }
+
+ // Make sure we've got the correct declaration type.
+ //
+ assert (decl == global_namespace);
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (qualifier_value.type_info () == typeid (custom_cxx_type))
+ {
+ // C++ type mapping.
+ //
+ custom_cxx_type& ct (qualifier_value.value<custom_cxx_type> ());
+
+ if (p == "type" || p == "as")
+ {
+ // Can be built-in type (e.g., bool).
+ //
+ if (tt == CPP_NAME || tt == CPP_KEYWORD || tt == CPP_SCOPE)
+ {
+ string name;
+ tree decl (
+ resolve_scoped_name (
+ l, tt, tl, tn, current_scope (), name, true, p));
+
+ if (decl == 0)
+ return; // Diagnostics has already been issued.
+
+ if (TREE_CODE (decl) != TYPE_DECL)
+ {
+ error (loc) << "name '" << name << "' in db pragma "
+ << p << " does not refer to a type" << endl;
+ return;
+ }
+
+ (p == "type" ? ct.type_node : ct.as_node) = TREE_TYPE (decl);
+ (p == "type" ? ct.type_name : ct.as_name) = name;
+ }
+ else
+ {
+ error (l) << "type name expected in db pragma " << p << endl;
+ return;
+ }
+ }
+ else if (p == "to" || p == "from")
+ {
+ if (tt != CPP_CLOSE_PAREN) // Empty expression is ok.
+ {
+ if (!parse_expression (
+ l, tt, tl, tn, (p == "to" ? ct.to : ct.from), p))
+ return; // Diagnostics has already been issued.
+ }
+ }
+ }
+ else
+ {
+ using relational::custom_db_type;
+
+ // Database type mapping.
+ //
+ custom_db_type& ct (qualifier_value.value<custom_db_type> ());
+
+ if (p == "type")
+ {
+ if (tt != CPP_STRING)
+ {
+ error (l) << "type name regex expected in db pragma " << p << endl;
+ return;
+ }
+
+ try
+ {
+ // Make it case-insensitive.
+ //
+ ct.type.assign (tl, true);
+ }
+ catch (regex_format const& e)
+ {
+ error (l) << "invalid regex: '" << e.regex () << "' in db pragma "
+ << p << ": " << e.description () << endl;
+ return;
+ }
+ }
+ else if (p == "as")
+ {
+ if (tt != CPP_STRING)
+ {
+ error (l) << "type name expected in db pragma " << p << endl;
+ return;
+ }
+
+ ct.as = tl;
+ }
+ else if (p == "to")
+ {
+ if (tt != CPP_STRING)
+ {
+ error (l) << "expression expected in db pragma " << p << endl;
+ return;
+ }
+
+ ct.to = tl;
+ }
+ else if (p == "from")
+ {
+ if (tt != CPP_STRING)
+ {
+ error (l) << "expression expected in db pragma " << p << endl;
+ return;
+ }
+
+ ct.from = tl;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ name.clear (); // We don't need to add anything for this pragma.
+ tt = l.next (tl, &tn);
+ }
+ else if (qualifier == "index")
+ {
+ // unique
+ // type("<type>")
+ // method("<method>")
+ // options("<options>")
+ // member(<name>[, "<options>"])
+ // members(<name>[, <name>...])
+ //
+
+ if (p != "unique" &&
+ p != "type" &&
+ p != "method" &&
+ p != "options" &&
+ p != "member" &&
+ p != "members")
+ {
+ error (l) << "unknown db pragma " << p << endl;
+ return;
+ }
+
+ using relational::index;
+ index& in (qualifier_value.value<index> ());
+
+ if (p == "unique")
+ in.type = "UNIQUE";
+ else
+ {
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (p == "type")
+ {
+ if (tt != CPP_STRING)
+ {
+ error (l) << "index type expected in db pragma " << p << endl;
+ return;
+ }
+
+ in.type = tl;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "method")
+ {
+ if (tt != CPP_STRING)
+ {
+ error (l) << "index method expected in db pragma " << p << endl;
+ return;
+ }
+
+ in.method = tl;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "options")
+ {
+ if (tt != CPP_STRING)
+ {
+ error (l) << "index options expected in db pragma " << p << endl;
+ return;
+ }
+
+ in.options = tl;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "member")
+ {
+ if (tt != CPP_NAME)
+ {
+ error (l) << "data member name expected in db pragma " << p << endl;
+ return;
+ }
+
+ index::member m;
+ m.loc = loc;
+ m.name = tl;
+
+ tt = l.next (tl, &tn);
+
+ // Parse nested members if any.
+ //
+ for (; tt == CPP_DOT; tt = l.next (tl, &tn))
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "name expected after '.' in db pragma " << p << endl;
+ return;
+ }
+
+ m.name += '.';
+ m.name += tl;
+ }
+
+ // Parse member options, if any.
+ //
+ if (tt == CPP_COMMA)
+ {
+ if (l.next (tl, &tn) != CPP_STRING)
+ {
+ error (l) << "index member options expected in db pragma " << p
+ << endl;
+ return;
+ }
+
+ m.options = tl;
+ tt = l.next (tl, &tn);
+ }
+
+ in.members.push_back (m);
+ }
+ else if (p == "members")
+ {
+ for (;;)
+ {
+ if (tt != CPP_NAME)
+ {
+ error (l) << "data member name expected in db pragma " << p
+ << endl;
+ return;
+ }
+
+ index::member m;
+ m.loc = l.location ();
+ m.name = tl;
+
+ tt = l.next (tl, &tn);
+
+ // Parse nested members if any.
+ //
+ for (; tt == CPP_DOT; tt = l.next (tl, &tn))
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "name expected after '.' in db pragma " << p
+ << endl;
+ return;
+ }
+
+ m.name += '.';
+ m.name += tl;
+ }
+
+ in.members.push_back (m);
+
+ if (tt == CPP_COMMA)
+ tt = l.next (tl, &tn);
+ else
+ break;
+ }
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+ }
+
+ name.clear (); // We don't need to add anything for this pragma.
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "index" ||
+ p == "unique")
+ {
+ // index
+ // unique
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "get" ||
+ p == "set" ||
+ p == "access")
+ {
+ // get(name|expr)
+ // set(name|expr)
+ // access(name|expr)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ member_access ma (loc, p == "set" ? "modifier" : "accessor", false);
+
+ tt = l.next (tl, &tn);
+ if (tt != CPP_CLOSE_PAREN) // Empty expression are ok.
+ {
+ if (!parse_expression (l, tt, tl, tn, ma.expr, p))
+ return; // Diagnostics has already been issued.
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+ }
+
+ val = ma;
+
+ // Convert access to the get/set pair.
+ //
+ if (p == "access")
+ {
+ if (db.empty () || db == pragma_db_.string ())
+ add_pragma (
+ pragma (p, "get", val, loc, &check_spec_decl_type, 0), decl, ns);
+
+ ma.kind = "modifier";
+ val = ma;
+ name = "set";
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "table")
+ {
+ // table (<name>)
+ // table (<name> [= "<alias>"] [type] [: "<cond>"] (view only)
+ //
+ // <name> := "name" | "name.name" | "name"."name"
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_STRING && tt != CPP_DOT)
+ {
+ error (l) << "table name expected in db pragma " << p << endl;
+ return;
+ }
+
+ // The table specifier is used for both objects and views. In case
+ // of an object, the context values is just a string. In case of a
+ // view, the context value is a view_object entry. The problem is
+ // that at this stage we don't know whether we are dealing with an
+ // object or a view. To resolve this in a somewhat hackish way, we
+ // are going to create both a string and a view_object entry.
+ //
+ view_object vo;
+ vo.kind = view_object::table;
+
+ if (!parse_qname (l, tt, tl, tn, p, vo.tbl_name))
+ return; // Diagnostics has already been issued.
+
+ if (tt == CPP_EQ)
+ {
+ // We have an alias.
+ //
+ if (l.next (tl, &tn) != CPP_STRING)
+ {
+ error (l) << "table alias expected after '=' in db pragma " << p
+ << endl;
+ return;
+ }
+
+ vo.alias = tl;
+ tt = l.next (tl, &tn);
+ }
+
+ if (tt == CPP_NAME)
+ {
+ // We have a JOIN type.
+ //
+ if (tl == "left")
+ vo.join = view_object::left;
+ else if (tl == "right")
+ vo.join = view_object::right;
+ else if (tl == "full")
+ vo.join = view_object::full;
+ else if (tl == "inner")
+ vo.join = view_object::inner;
+ else if (tl == "cross")
+ vo.join = view_object::cross;
+ else
+ {
+ error (l) << "unknown join type '" << tl << "'" << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else
+ vo.join = view_object::left;
+
+ if (tt == CPP_COLON)
+ {
+ // We have a condition.
+ //
+ if (vo.join == view_object::cross)
+ {
+ error (l)
+ << "no join condition can be specified for a cross join" << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (!parse_expression (l, tt, tl, tn, vo.cond, p))
+ return; // Diagnostics has already been issued.
+
+ if (vo.cond.empty ())
+ {
+ error (l) << "join condition expected after ':' in db pragma " << p
+ << endl;
+ return;
+ }
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ // Add the "table" pragma.
+ //
+ if (vo.alias.empty () && vo.cond.empty ())
+ {
+ if (db.empty () || db == pragma_db_.string ())
+ {
+ add_pragma (
+ pragma (p, name, vo.tbl_name, loc, &check_spec_decl_type, 0),
+ decl,
+ ns);
+ }
+ }
+
+ vo.scope = current_scope ();
+ vo.loc = loc;
+ val = vo;
+ name = "objects";
+ adder = &accumulate<view_object>;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "session")
+ {
+ // session
+ // session (true|false)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_OPEN_PAREN)
+ {
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_KEYWORD || (tl != "true" && tl != "false"))
+ {
+ error (l) << "true or false expected in db pragma " << p << endl;
+ return;
+ }
+
+ val = (tl == "true");
+
+ tt = l.next (tl, &tn);
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ }
+ else if (p == "schema")
+ {
+ // schema (<name>)
+ //
+ // <name> := "name" | "name.name" | "name"."name"
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_STRING && tt != CPP_DOT)
+ {
+ error (l) << "table name expected in db pragma " << p << endl;
+ return;
+ }
+
+ qname s;
+ if (!parse_qname (l, tt, tl, tn, p, s))
+ return; // Diagnostics has already been issued.
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ val = s;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "pointer")
+ {
+ // pointer (qname)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ class_pointer cp;
+ size_t pb (0);
+ bool punc (false);
+
+ for (tt = l.next (tl, &tn);
+ tt != CPP_EOF && (tt != CPP_CLOSE_PAREN || pb != 0);
+ tt = l.next (tl, &tn))
+ {
+ if (punc && tt > CPP_LAST_PUNCTUATOR)
+ cp.name += ' ';
+
+ punc = false;
+
+ if (tt == CPP_OPEN_PAREN)
+ pb++;
+ else if (tt == CPP_CLOSE_PAREN)
+ pb--;
+
+ // @@ Need to handle literals, at least integer.
+ //
+ switch (tt)
+ {
+ case CPP_LESS:
+ {
+ cp.name += "< ";
+ break;
+ }
+ case CPP_GREATER:
+ {
+ cp.name += " >";
+ break;
+ }
+ case CPP_COMMA:
+ {
+ cp.name += ", ";
+ break;
+ }
+ case CPP_NAME:
+ // case CPP_KEYWORD: // see default:
+ {
+ cp.name += tl;
+ punc = true;
+ break;
+ }
+ default:
+ {
+ if (tt == CPP_KEYWORD)
+ {
+ cp.name += tl;
+ punc = true;
+ }
+ else if (tt <= CPP_LAST_PUNCTUATOR)
+ cp.name += cxx_lexer::token_spelling[tt];
+ else
+ {
+ error (l) << "unexpected token '" << cxx_lexer::token_spelling[tt]
+ << "' in db pragma " << p << endl;
+ return;
+ }
+ break;
+ }
+ }
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ if (cp.name.empty ())
+ {
+ error (l) << "expected pointer name in db pragma " << p << endl;
+ return;
+ }
+
+ cp.scope = current_scope ();
+ cp.loc = loc;
+ val = cp;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "abstract")
+ {
+ // abstract
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "optimistic")
+ {
+ // optimistic
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "polymorphic")
+ {
+ // polymorphic
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "definition")
+ {
+ // definition
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ val = l.location ();
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "sectionable")
+ {
+ // sectionable
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "callback")
+ {
+ // callback (name)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NAME)
+ {
+ error (l) << "member function name expected in db pragma " << p << endl;
+ return;
+ }
+
+ val = tl;
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "bulk")
+ {
+ // bulk (batch-size)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ if (l.next (tl, &tn) != CPP_NUMBER || TREE_CODE (tn) != INTEGER_CST)
+ {
+ error (l) << "unsigned integer expected as batch size" << endl;
+ return;
+ }
+
+ unsigned long long b (integer_value (tn));
+
+ if (b == 0 || b == 1)
+ {
+ error (l) << "batch size has to be greater than 1" << endl;
+ return;
+ }
+
+ val = b;
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "query")
+ {
+ // query ()
+ // query ("statement")
+ // query (expr)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ view_query vq;
+
+ bool s (false);
+ string str;
+
+ if (tt == CPP_STRING)
+ {
+ s = true;
+ str = tl;
+ tt = l.next (tl, &tn);
+ }
+
+ if (tt == CPP_CLOSE_PAREN)
+ {
+ if (s)
+ vq.literal = str;
+ else
+ {
+ // Empty query() pragma indicates that the statement will be
+ // provided at runtime. Encode this case as empty literal
+ // and expression.
+ //
+ }
+ }
+ else
+ {
+ // Expression.
+ //
+ if (s)
+ vq.expr.push_back (cxx_token (0, CPP_STRING, str));
+
+ if (!parse_expression (l, tt, tl, tn, vq.expr, p))
+ return; // Diagnostics has already been issued.
+ }
+
+ // Disallow query(, distinct).
+ //
+ if (tt == CPP_COMMA && vq.expr.empty ())
+ {
+ error (l) << "query expression expected in db pragma " << p << endl;
+ return;
+ }
+
+ // The query expression can be omitted with the modifier in its
+ // place. Handle this case.
+ //
+ if (vq.expr.size () == 1 && vq.expr.front ().type == CPP_NAME)
+ {
+ string const& n (vq.expr.front ().literal);
+
+ if (n == "distinct")
+ vq.distinct = true;
+ else if (n == "for_update")
+ vq.for_update = true;
+
+ if (vq.distinct || vq.for_update)
+ vq.expr.clear ();
+ }
+
+ if (tt == CPP_COMMA)
+ {
+ do
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "result modifier expected in db pragma " << p << endl;
+ return;
+ }
+
+ if (tl == "distinct")
+ vq.distinct = true;
+ else if (tl == "for_update")
+ vq.for_update = true;
+ else
+ {
+ error (l) << "unknown result modifier '" << tl << "'" << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ } while (tt == CPP_COMMA);
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ vq.scope = current_scope ();
+ vq.loc = loc;
+ val = vq;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "object")
+ {
+ // object (fq-name [ = name] [type] [: expr])
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NAME && tt != CPP_SCOPE)
+ {
+ error (l) << "type name expected in db pragma " << p << endl;
+ return;
+ }
+
+ view_object vo;
+ vo.kind = view_object::object;
+ vo.obj_node = resolve_scoped_name (
+ l, tt, tl, tn, current_scope (), vo.obj_name, true, p);
+
+ if (vo.obj_node == 0)
+ return; // Diagnostics has already been issued.
+
+ // Get the actual type if this is a TYPE_DECL. Also get the main variant.
+ //
+ if (TREE_CODE (vo.obj_node) == TYPE_DECL)
+ vo.obj_node = TREE_TYPE (vo.obj_node);
+
+ if (TYPE_P (vo.obj_node)) // Can be a template.
+ vo.obj_node = TYPE_MAIN_VARIANT (vo.obj_node);
+
+ if (tt == CPP_EQ)
+ {
+ // We have an alias.
+ //
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "alias name expected after '=' in db pragma " << p
+ << endl;
+ return;
+ }
+
+ vo.alias = tl;
+ tt = l.next (tl, &tn);
+ }
+
+ if (tt == CPP_NAME)
+ {
+ // We have a JOIN type.
+ //
+ if (tl == "left")
+ vo.join = view_object::left;
+ else if (tl == "right")
+ vo.join = view_object::right;
+ else if (tl == "full")
+ vo.join = view_object::full;
+ else if (tl == "inner")
+ vo.join = view_object::inner;
+ else if (tl == "cross")
+ vo.join = view_object::cross;
+ else
+ {
+ error (l) << "unknown join type '" << tl << "'" << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else
+ vo.join = view_object::left;
+
+ if (tt == CPP_COLON)
+ {
+ // We have a condition.
+ //
+ if (vo.join == view_object::cross)
+ {
+ error (l)
+ << "no join condition can be specified for a cross join" << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (!parse_expression (l, tt, tl, tn, vo.cond, p))
+ return; // Diagnostics has already been issued.
+
+ if (vo.cond.empty ())
+ {
+ error (l) << "join condition expected after ':' in db pragma " << p
+ << endl;
+ return;
+ }
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ vo.scope = current_scope ();
+ vo.loc = loc;
+ val = vo;
+ name = "objects"; // Change the context entry name.
+ adder = &accumulate<view_object>;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "id")
+ {
+ // id[(member-path)]
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ string name;
+
+ tt = l.next (tl, &tn);
+ if (tt == CPP_OPEN_PAREN)
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "data member name expected in db pragma " << p
+ << endl;
+ return;
+ }
+
+ name = tl;
+
+ for (tt = l.next (tl, &tn); tt == CPP_DOT; tt = l.next (tl, &tn))
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "name expected after '.' in db pragma " << p << endl;
+ return;
+ }
+
+ name += '.';
+ name += tl;
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+
+ val = name;
+ }
+ else if (p == "no_id")
+ {
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ name = "id";
+ val = false;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "auto")
+ {
+ // auto
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "column")
+ {
+ // column ("<name>")
+ // column ("<name>.<name>") (view only)
+ // column ("<name>"."<name>") (view only)
+ // column (fq-name) (view only)
+ // column (expr) (view only)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ bool expr (false);
+ string expr_str;
+ if (tt == CPP_STRING || tt == CPP_DOT)
+ {
+ qname qn;
+
+ if (!parse_qname (l, tt, tl, tn, p, qn, &expr, &expr_str))
+ return; // Diagnostics has already been issued.
+
+ if (tt == CPP_CLOSE_PAREN)
+ {
+ table_column tc;
+ tc.expr = expr;
+
+ if (expr)
+ tc.column = expr_str;
+ else
+ {
+ tc.table = qn.qualifier ();
+ tc.column = qn.uname ();
+ }
+
+ val = tc;
+ }
+ else if (!expr)
+ {
+ error (l) << "column name, expression, or data member name expected "
+ << "in db pragma " << p << endl;
+ return;
+ }
+ }
+
+ if (val.empty ())
+ {
+ // We have an expression.
+ //
+ column_expr e;
+
+ if (expr)
+ {
+ e.push_back (column_expr_part ());
+ e.back ().kind = column_expr_part::literal;
+ e.back ().value = expr_str;
+
+ if (tt != CPP_PLUS)
+ {
+ error (l) << "'+' or ')' expected in db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+
+ for (;;)
+ {
+ if (tt == CPP_STRING)
+ {
+ e.push_back (column_expr_part ());
+ e.back ().kind = column_expr_part::literal;
+ e.back ().value = tl;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (tt == CPP_NAME || tt == CPP_SCOPE)
+ {
+ string name (parse_scoped_name (l, tt, tl, tn, p));
+
+ if (name.empty ())
+ return; // Diagnostics has already been issued.
+
+ // Resolve nested members if any.
+ //
+ for (; tt == CPP_DOT; tt = l.next (tl, &tn))
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "name expected after '.' in db pragma " << p
+ << endl;
+ return;
+ }
+
+ name += '.';
+ name += tl;
+ }
+
+ e.push_back (column_expr_part ());
+ e.back ().kind = column_expr_part::reference;
+ e.back ().value = name;
+ e.back ().scope = current_scope ();
+ e.back ().loc = loc;
+ }
+ else
+ {
+ error (l) << "column name, expression, or data member name expected "
+ << "in db pragma " << p << endl;
+ return;
+ }
+
+ if (tt == CPP_PLUS)
+ tt = l.next (tl, &tn);
+ else if (tt == CPP_CLOSE_PAREN)
+ break;
+ else
+ {
+ error (l) << "'+' or ')' expected in db pragma " << p << endl;
+ return;
+ }
+ }
+
+ e.loc = loc;
+ val = e;
+ name = "column-expr";
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "value_column" ||
+ p == "index_column" ||
+ p == "key_column" ||
+ p == "id_column")
+ {
+ // value_column ("<name>")
+ // index_column ("<name>")
+ // key_column ("<name>")
+ // id_column ("<name>")
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_STRING)
+ {
+ error (l) << "column name expected in db pragma " << p << endl;
+ return;
+ }
+
+ val = tl;
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "options" ||
+ p == "value_options" ||
+ p == "index_options" ||
+ p == "key_options" ||
+ p == "id_options")
+ {
+ // options (["<name>"])
+ // value_options (["<name>"])
+ // index_options (["<name>"])
+ // key_options (["<name>"])
+ // id_options (["<name>"])
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_STRING)
+ {
+ // Ignore empty options strings. Internally, we use them to
+ // indicate options reset (see below).
+ //
+ if (!tl.empty ())
+ val = tl;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (tt == CPP_CLOSE_PAREN)
+ {
+ // Empty options specifier signals options reset. Encode it as an
+ // empty string.
+ //
+ val = string ();
+ }
+ else
+ {
+ error (l) << "options string expected in db pragma " << p << endl;
+ return;
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ adder = &accumulate<string>;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "type" ||
+ p == "id_type" ||
+ p == "value_type" ||
+ p == "index_type" ||
+ p == "key_type")
+ {
+ // type ("<type>")
+ // id_type ("<type>")
+ // value_type ("<type>")
+ // index_type ("<type>")
+ // key_type ("<type>")
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_STRING)
+ {
+ error (l) << "type name expected in db pragma " << p << endl;
+ return;
+ }
+
+ val = tl;
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "null" ||
+ p == "not_null" ||
+ p == "key_null" ||
+ p == "key_not_null" ||
+ p == "value_null" ||
+ p == "value_not_null")
+ {
+ // null
+ // not_null
+ // key_null
+ // key_not_null
+ // value_null
+ // value_not_null
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "default")
+ {
+ // default () (<empty>)
+ // default (null) (n)
+ // default (true|false) (t|f)
+ // default ([+|-]<number>) (-|+)
+ // default ("string") (s)
+ // default (<enumerator>) (e)
+ //
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ default_value dv;
+
+ switch (tt)
+ {
+ case CPP_CLOSE_PAREN:
+ {
+ // Default value reset.
+ //
+ dv.kind = default_value::reset;
+ break;
+ }
+ case CPP_STRING:
+ {
+ dv.kind = default_value::string;
+ dv.literal = tl;
+ tt = l.next (tl, &tn);
+ break;
+ }
+ case CPP_NAME:
+ {
+ // This can be null or an enumerator name.
+ //
+ if (tl == "null")
+ {
+ dv.kind = default_value::null;
+ tt = l.next (tl, &tn);
+ break;
+ }
+ }
+ // Fall through.
+ case CPP_SCOPE:
+ {
+ // We have a potentially scopped enumerator name.
+ //
+ dv.enum_value = resolve_scoped_name (
+ l, tt, tl, tn, current_scope (), dv.literal, false, p);
+
+ if (dv.enum_value == 0)
+ return; // Diagnostics has already been issued.
+
+ dv.kind = default_value::enumerator;
+ break;
+ }
+ case CPP_MINUS:
+ case CPP_PLUS:
+ {
+ if (tt == CPP_MINUS)
+ dv.literal = "-";
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NUMBER)
+ {
+ error (l) << "expected numeric constant after '"
+ << (tt == CPP_MINUS ? "-" : "+") << "' in db pragma "
+ << p << endl;
+ return;
+ }
+ }
+ // Fall through.
+ case CPP_NUMBER:
+ {
+ switch (TREE_CODE (tn))
+ {
+ case INTEGER_CST:
+ {
+ dv.int_value = integer_value (tn);
+ dv.kind = default_value::integer;
+ break;
+ }
+ case REAL_CST:
+ {
+ REAL_VALUE_TYPE d (TREE_REAL_CST (tn));
+
+ if (REAL_VALUE_ISINF (d))
+ dv.float_value = numeric_limits<double>::infinity ();
+ else if (REAL_VALUE_ISNAN (d))
+ dv.float_value = numeric_limits<double>::quiet_NaN ();
+ else
+ {
+ char tmp[256];
+ real_to_decimal (tmp, &d, sizeof (tmp), 0, true);
+ istringstream is (tmp);
+ is >> dv.float_value;
+ }
+
+ if (dv.literal == "-")
+ dv.float_value = -dv.float_value;
+
+ dv.kind = default_value::floating;
+ break;
+ }
+ default:
+ {
+ error (l) << "unexpected numeric constant in db pragma " << p
+ << endl;
+ return;
+ }
+ }
+
+ tt = l.next (tl, &tn);
+ break;
+ }
+ default:
+ {
+ // This can be the true or false keyword.
+ //
+ if (tt == CPP_KEYWORD && (tl == "true" || tl == "false"))
+ {
+ dv.kind = default_value::boolean;
+ dv.literal = tl;
+ tt = l.next (tl, &tn);
+ }
+ else
+ {
+ error (l) << "unexpected expression in db pragma " << p << endl;
+ return;
+ }
+
+ break;
+ }
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ val = dv;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "inverse")
+ {
+ // inverse (name)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NAME)
+ {
+ error (l) << "member name expected in db pragma " << p << endl;
+ return;
+ }
+
+ string name (tl);
+
+ tt = l.next (tl, &tn);
+
+ // Parse nested members if any.
+ //
+ for (; tt == CPP_DOT; tt = l.next (tl, &tn))
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "name expected after '.' in db pragma " << p << endl;
+ return;
+ }
+
+ name += '.';
+ name += tl;
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ val = name;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "on_delete")
+ {
+ // on_delete (cascade|set_null)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ if (l.next (tl, &tn) != CPP_NAME || (tl != "cascade" && tl != "set_null"))
+ {
+ error (l) << "cascade or set_null expected after '('" << endl;
+ return;
+ }
+
+ using semantics::relational::foreign_key;
+ val = (tl == "cascade" ? foreign_key::cascade : foreign_key::set_null);
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "points_to")
+ {
+ // points_to(<fq-name>)
+ //
+
+ tree type;
+ string type_name;
+
+ string p (tl);
+ location_t loc (l.location ());
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_NAME || tt == CPP_SCOPE)
+ {
+ type = resolve_scoped_name (
+ l, tt, tl, tn, current_scope (), type_name, true, p);
+
+ if (type == 0)
+ return; // Diagnostics has already been issued.
+
+ // Get the actual type if this is a TYPE_DECL. Also get the main
+ // variant.
+ //
+ if (TREE_CODE (type) == TYPE_DECL)
+ type = TREE_TYPE (type);
+
+ if (TYPE_P (type)) // Can be a template.
+ type = TYPE_MAIN_VARIANT (type);
+
+ if (TREE_CODE (type) != RECORD_TYPE)
+ {
+ error (loc) << "name '" << type_name << "' in db pragma " << p
+ << " does not refer to a class" << endl;
+ return;
+ }
+
+ val = type;
+ }
+ else
+ {
+ error (l) << "class name expected in db pragma " << p << endl;
+ return;
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "section")
+ {
+ // section (name)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NAME)
+ {
+ error (l) << "member name expected in db pragma " << p << endl;
+ return;
+ }
+
+ name = "section-member";
+ val = tl;
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "load")
+ {
+ // load (eager|lazy)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NAME || (tl != "eager" && tl != "lazy"))
+ {
+ error (l) << "eager or lazy expected in db pragma " << p << endl;
+ return;
+ }
+
+ name = "section-load";
+ val = (tl == "eager"
+ ? user_section::load_eager
+ : user_section::load_lazy);
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "update")
+ {
+ // update (always|change|manual)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NAME ||
+ (tl != "always" && tl != "change" && tl != "manual"))
+ {
+ error (l) << "always, change, or manual expected in db pragma " <<
+ p << endl;
+ return;
+ }
+
+ name = "section-update";
+
+ if (tl == "always")
+ val = user_section::update_always;
+ else if (tl == "change")
+ val = user_section::update_change;
+ else
+ val = user_section::update_manual;
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "unordered")
+ {
+ // unordered
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "readonly")
+ {
+ // readonly
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "transient")
+ {
+ // transient
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "added" || p == "deleted")
+ {
+ // added (unsigned long long version)
+ // deleted (unsigned long long version)
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ char const* n (p == "added" ? "addition" : "deletion");
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ if (l.next (tl, &tn) != CPP_NUMBER || TREE_CODE (tn) != INTEGER_CST)
+ {
+ error (l) << "unsigned integer expected as " << n << " version" << endl;
+ return;
+ }
+
+ unsigned long long v (integer_value (tn));
+
+ if (v == 0)
+ {
+ error (l) << n << " version cannot be zero" << endl;
+ return;
+ }
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ val = v;
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "version")
+ {
+ // version
+ //
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (decl && !check_spec_decl_type (decl, decl_name, p, loc))
+ return;
+
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "virtual")
+ {
+ // Stray virtual specifier (i.e., without explicit member name).
+ //
+ error (l) << "virtual member declaration requires name" << endl;
+ return;
+ }
+ else if (p == "before" || p == "after")
+ {
+ // Stray before/after specifier (i.e., without preceding virtual).
+ //
+ error (l) << p << " specifier must follow virtual" << endl;
+ return;
+ }
+ else
+ {
+ error (l) << "unknown db pragma " << p << endl;
+ return;
+ }
+
+ // Add the pragma unless was indicated otherwise.
+ //
+ if (!name.empty () && (db.empty () || db == pragma_db_.string ()))
+ {
+ // If the value is not specified and we don't use a custom adder,
+ // then make it bool (flag).
+ //
+ if (adder == 0 && val.empty ())
+ val = true;
+
+ // Convert '_' to '-' in the context name so we get foo-bar instead
+ // of foo_bar (that's the convention used).
+ //
+ for (size_t i (0); i < name.size (); ++i)
+ if (name[i] == '_')
+ name[i] = '-';
+
+ // Record this pragma.
+ //
+ add_pragma (
+ pragma (p, name, val, loc, &check_spec_decl_type, adder), decl, ns);
+ }
+
+ // Mark the type or member as simple value or container, depending
+ // on the pragma. For static multi-database support we only do it
+ // if the pragma applies to this database since in this case we can
+ // have different mappings for different databases (e.g., composite
+ // in one and simple in another). For dynamic multi-database support
+ // we do this regardless of the database since here the mapping
+ // should the consistent.
+ //
+ // @@ Did we add new simple value or container pragmas and forgot to
+ // account for them here?
+ //
+ if ((qualifier == "value" || qualifier == "member") &&
+ (pragma_multi_ == multi_database::dynamic || db.empty () ||
+ db == pragma_db_.string ()))
+ {
+ // We assume a data member is simple only if the database type was
+ // specified explicitly.
+ //
+ if (name == "type" ||
+ name == "id-type" ||
+ (qualifier == "value" &&
+ (name == "null" ||
+ name == "not-null" ||
+ name == "default" ||
+ name == "options")))
+ {
+ add_pragma (pragma (p, "simple", true, loc, &check_spec_decl_type, 0),
+ decl,
+ false);
+ }
+ else if (name == "table" ||
+ name == "value-type" ||
+ name == "index-type" ||
+ name == "key-type" ||
+
+ name == "key-null" ||
+ name == "key-not-null" ||
+ name == "value-null" ||
+ name == "value-not-null" ||
+
+ name == "value-column" ||
+ name == "index-column" ||
+ name == "key-column" ||
+ name == "index-column" ||
+
+ name == "value-options" ||
+ name == "index-options" ||
+ name == "key-options" ||
+ name == "index-options" ||
+
+ name == "unordered")
+ {
+ add_pragma (pragma (p, "container", true, loc, &check_spec_decl_type, 0),
+ decl,
+ false);
+ }
+ }
+
+ // See if there are any more pragmas.
+ //
+ if (tt == CPP_NAME || tt == CPP_KEYWORD)
+ {
+ handle_pragma (l,
+ "",
+ tl,
+ qualifier,
+ qualifier_value,
+ decl,
+ decl_name,
+ ns);
+ }
+ else if (tt != CPP_EOF)
+ error (l) << "unexpected text after " << p << " in db pragma" << endl;
+}
+
+//
+// Qualifiers.
+//
+
+static bool
+check_qual_decl_type (declaration const& d,
+ string const& name,
+ string const& p,
+ location_t l)
+{
+ gcc_tree_code_type tc (d.tree_code ());
+ bool type (TREE_CODE_CLASS (tc) == tcc_type);
+
+ if (p == "model" ||
+ p == "map")
+ {
+ assert (d == global_namespace);
+ }
+ else if (p == "index")
+ {
+ if (tc != RECORD_TYPE)
+ {
+ // For an index, name is not empty only if the class name was
+ // specified explicitly. Otherwise, the index definition scope
+ // is assumed.
+ //
+ if (name.empty ())
+ {
+ error (l) << "db pragma " << p << " outside of a class scope" << endl;
+ info (l) << "use the db pragma " << p << "(<class-name>) syntax "
+ << " instead" << endl;
+ }
+ else
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a class" << endl;
+ return false;
+ }
+ }
+ else if (p == "namespace")
+ {
+ if (tc != NAMESPACE_DECL)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a namespace" << endl;
+ return false;
+ }
+ }
+ else if (p == "object" ||
+ p == "view")
+ {
+ if (tc != RECORD_TYPE)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a class" << endl;
+ return false;
+ }
+ }
+ else if (p == "value")
+ {
+ if (!type)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a type" << endl;
+ return false;
+ }
+ }
+ else if (p == "member")
+ {
+ if (tc != FIELD_DECL)
+ {
+ error (l) << "name '" << name << "' in db pragma " << p << " does "
+ << "not refer to a data member" << endl;
+ return false;
+ }
+ }
+ else
+ {
+ error (l) << "unknown db pragma " << p << endl;
+ return false;
+ }
+
+ return true;
+}
+
+static void
+add_qual_entry (compiler::context& ctx,
+ string const& k,
+ any const& v,
+ location_t l)
+{
+ // Store the TYPE_DECL node that was referred to in the pragma. This
+ // can be used later as a name hint in case the type is a template
+ // instantiation. Also store the pragma location which is used as
+ // the "definition point" for this instantiation.
+ //
+ ctx.set ("tree-node", v);
+ ctx.set ("location", l);
+
+ ctx.set (k, true);
+}
+
+static void
+handle_pragma_qualifier (cxx_lexer& l, string p)
+{
+ cpp_ttype tt;
+ string tl;
+ tree tn;
+
+ declaration decl;
+ tree orig_decl (0); // Original declarations as used in the pragma.
+ string decl_name;
+
+ // Check for a database prefix.
+ //
+ string db;
+
+ if (p == "mysql" ||
+ p == "sqlite" ||
+ p == "pgsql" ||
+ p == "oracle" ||
+ p == "mssql")
+ {
+ tt = l.next (tl);
+
+ if (tt == CPP_COLON)
+ {
+ // Specifier prefix.
+ //
+ db = p;
+ tt = l.next (p);
+ }
+ else
+ {
+ // Qualifier prefix. Ignore the rest if the databases don't match.
+ //
+ if (p != pragma_db_.string ())
+ return;
+
+ p = tl;
+ }
+
+ if (tt != CPP_NAME && tt != CPP_KEYWORD)
+ {
+ error (l) << "expected specifier after db " << db << " pragma" << endl;
+ return;
+ }
+
+ // Make sure a qualifier prefix is not used before a specifier.
+ //
+ if (!db.empty () &&
+ (p == "model" ||
+ p == "map" ||
+ p == "namespace" ||
+ p == "object" ||
+ p == "view" ||
+ p == "value" ||
+ p == "member"))
+ {
+ error (l) << "specifier prefix '" << db << ":' used before qualifier " <<
+ p << endl;
+ return;
+ }
+ }
+
+ //
+ //
+ string name (p); // Pragma name.
+ any val; // Pragma value.
+ location_t loc (l.location ()); // Pragma location.
+ pragma::add_func adder (0); // Custom context adder.
+ bool ns (false); // Namespace location pragma.
+
+ cxx_tokens saved_tokens; // Saved token sequence to be replayed.
+
+ // Pragma qualifiers.
+ //
+ if (p == "model")
+ {
+ orig_decl = global_namespace;
+ decl = declaration (orig_decl);
+ tt = l.next (tl, &tn);
+ }
+ else if (p == "map")
+ {
+ // map type("<regex>") as("<subst>") [to("<subst>")] [from("<subst>")]
+ // map type(<c++-type>) as(<c++-type>) [to(<expr>)] [from(<expr>)]
+ //
+
+ // The thing that determines whether this is a database or C++ type
+ // mapping is what we have inside 'type'. So to handle this we are
+ // going to pre-scan the pragma looking for 'type' and saving the
+ // tokens. Once we determine what this is, we replay the saved
+ // tokens to actually parse them.
+ //
+
+ // Determine what this is by scanning the pragma until we see
+ // the 'type' qualifier or EOF.
+ //
+ bool db (true);
+
+ bool done (false);
+ size_t balance (0);
+ for (tt = l.next (tl, &tn);
+ !(done && balance == 0) && tt != CPP_EOF;
+ tt = l.next (tl, &tn))
+ {
+ saved_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
+
+ switch (tt)
+ {
+ case CPP_OPEN_PAREN:
+ {
+ balance++;
+ continue;
+ }
+ case CPP_CLOSE_PAREN:
+ {
+ if (balance > 0)
+ balance--;
+ else
+ {
+ error (l) << "unbalanced parenthesis in db pragma " << p << endl;
+ return;
+ }
+ continue;
+ }
+ case CPP_NAME:
+ {
+ if (balance == 0 && tl == "type")
+ break;
+
+ continue;
+ }
+ default:
+ continue;
+ }
+
+ tt = l.next (tl, &tn);
+ saved_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
+
+ if (tt == CPP_OPEN_PAREN)
+ {
+ balance++;
+
+ tt = l.next (tl, &tn);
+ saved_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
+
+ db = (tt == CPP_STRING);
+ }
+
+ done = true; // Scan until the closing ')'.
+ }
+
+ if (balance != 0)
+ {
+ error (l) << "unbalanced parenthesis in db pragma " << p << endl;
+ return;
+ }
+
+ orig_decl = global_namespace;
+ decl = declaration (orig_decl);
+
+ if (db)
+ {
+ using relational::custom_db_type;
+
+ custom_db_type ct;
+ ct.loc = loc;
+ val = ct;
+ name = "custom-db-types";
+ adder = &accumulate<custom_db_type>;
+ }
+ else
+ {
+ custom_cxx_type ct;
+ ct.loc = loc;
+ ct.scope = current_scope ();
+ val = ct;
+ name = "custom-cxx-types";
+ adder = &accumulate<custom_cxx_type>;
+ }
+ }
+ else if (p == "index")
+ {
+ // Index can be both a qualifier and a specifier. Things are complicated
+ // by the fact that when it is a specifier, it belongs to a member which
+ // means that the actual qualifier ('member') can be omitted. So we need
+ // to distinguish between cases like these:
+ //
+ // #pragma db index type("INTEGER") // specifier
+ // #pragma db index type("UNIQUE") member(foo_) // qualifier
+ //
+ // The thing that determines whether this is a qualifier or a specifier
+ // is the presence of the 'member' or 'members' specifier. So to handle
+ // this we are going to pre-scan the pragma looking for 'member' or
+ // 'members' and saving the tokens. Once we determine what this is,
+ // we replay the saved tokens to actually parse them.
+ //
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_OPEN_PAREN)
+ {
+ // Determine what this is by scanning the pragma until we see
+ // the 'member' qualifier or EOF.
+ //
+ bool qual (false);
+ size_t balance (0);
+
+ for (; tt != CPP_EOF; tt = l.next (tl, &tn))
+ {
+ switch (tt)
+ {
+ case CPP_OPEN_PAREN:
+ {
+ balance++;
+ break;
+ }
+ case CPP_CLOSE_PAREN:
+ {
+ if (balance > 0)
+ balance--;
+ else
+ {
+ error (l) << "unbalanced parenthesis in db pragma " << p << endl;
+ return;
+ }
+ break;
+ }
+ case CPP_NAME:
+ {
+ if (balance == 0 && (tl == "member" || tl == "members"))
+ qual = true;
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (qual)
+ break;
+
+ saved_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
+ }
+
+ if (balance != 0)
+ {
+ error (l) << "unbalanced parenthesis in db pragma " << p << endl;
+ return;
+ }
+
+ if (qual)
+ {
+ // This is a qualifer. The saved tokens sequence contains tokens
+ // until the first 'member' or 'members' specifier. So we will
+ // first need to re-play these tokens and then continue parsing
+ // as if we just saw the 'member' or 'members' specifier. The
+ // token type (tt) and token literal (tl) variables should contain
+ // the correct values.
+ //
+
+ // Also check that no specifier prefix was used for this qualifer.
+ //
+ if (!db.empty ())
+ {
+ error (loc) << "specifier prefix '" << db << ":' used before " <<
+ "qualifier index" << endl;
+ return;
+ }
+
+ orig_decl = current_scope ();
+ decl = declaration (orig_decl);
+ }
+ else
+ {
+ // This is a specifier. The saved tokens sequence contains all the
+ // tokens in this pragma until EOF.
+ //
+ cxx_tokens_lexer l;
+ l.start (saved_tokens, loc);
+ handle_pragma (
+ l, db, "index", "member", val, declaration (), "", false);
+ return;
+ }
+ }
+
+ relational::index in;
+ in.loc = loc;
+
+ if (tt == CPP_OPEN_PAREN)
+ {
+ // Specifier with the class fq-name, index name, or both.
+ //
+ // index(<fq-name>)
+ // index("<name>")
+ // index(<fq-name>::"<name>")
+ //
+ tt = l.next (tl, &tn);
+
+ // Resolve class name, if any.
+ //
+ if (tt == CPP_NAME || tt == CPP_SCOPE)
+ {
+ cpp_ttype ptt;
+ orig_decl = resolve_scoped_name (
+ l, tt, tl, tn, current_scope (), decl_name, true, p, true, &ptt);
+
+ if (orig_decl == 0)
+ return; // Diagnostics has already been issued.
+
+ // Get the actual type if this is a TYPE_DECL. Also get the main
+ // variant.
+ //
+ if (TREE_CODE (orig_decl) == TYPE_DECL)
+ orig_decl = TREE_TYPE (orig_decl);
+
+ if (TYPE_P (orig_decl)) // Can be a template.
+ decl = declaration (TYPE_MAIN_VARIANT (orig_decl));
+ else
+ decl = declaration (orig_decl);
+
+ if (tt == CPP_STRING && ptt != CPP_SCOPE)
+ {
+ error (l) << "'::' expected before index name in db pragma " << p
+ << endl;
+ return;
+ }
+
+ if (tt != CPP_STRING && ptt == CPP_SCOPE)
+ {
+ error (l) << "index name expected after '::' in db pragma " << p
+ << endl;
+ return;
+ }
+ }
+ else
+ {
+ orig_decl = current_scope ();
+ decl = declaration (orig_decl);
+ }
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (!check_qual_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (tt == CPP_STRING)
+ {
+ in.name = tl;
+ tt = l.next (tl, &tn);
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+
+ val = in;
+ adder = &accumulate<relational::index>;
+ }
+ else if (p == "namespace")
+ {
+ // namespace [(<identifier>)]
+ // namespace () (refers to global namespace)
+ //
+
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_OPEN_PAREN)
+ {
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_NAME || tt == CPP_SCOPE)
+ {
+ orig_decl = resolve_scoped_name (
+ l, tt, tl, tn, current_scope (), decl_name, false, p);
+
+ if (orig_decl == 0)
+ return; // Diagnostics has already been issued.
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (!check_qual_decl_type (orig_decl, decl_name, p, loc))
+ return;
+
+ // Resolve namespace aliases if any.
+ //
+ orig_decl = ORIGINAL_NAMESPACE (orig_decl);
+ decl = declaration (orig_decl);
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else if (tt == CPP_CLOSE_PAREN)
+ {
+ orig_decl = global_namespace;
+ decl = declaration (orig_decl);
+ tt = l.next (tl, &tn);
+ }
+ else
+ {
+ error (l) << "data member name expected in db pragma " << p << endl;
+ return;
+ }
+ }
+ else
+ {
+ // Make sure we are in a namespace scope.
+ //
+ if (TREE_CODE (current_scope ()) != NAMESPACE_DECL)
+ {
+ error (l) << "db pragma " << p << " is not in a namespace scope"
+ << endl;
+ return;
+ }
+
+ ns = true;
+ }
+ }
+ else if (p == "object" ||
+ p == "view" ||
+ p == "value")
+ {
+ // object [(<identifier>)]
+ // view [(<identifier>)]
+ // value [(<identifier>)]
+ //
+
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_OPEN_PAREN)
+ {
+ tt = l.next (tl, &tn);
+
+ // Can be built-in type (e.g., bool).
+ //
+ if (tt == CPP_NAME || tt == CPP_KEYWORD || tt == CPP_SCOPE)
+ {
+ orig_decl = resolve_scoped_name (
+ l, tt, tl, tn, current_scope (), decl_name, true, p);
+
+ if (orig_decl == 0)
+ return; // Diagnostics has already been issued.
+
+ // Get the actual type if this is a TYPE_DECL. Also get the main
+ // variant.
+ //
+ if (TREE_CODE (orig_decl) == TYPE_DECL)
+ orig_decl = TREE_TYPE (orig_decl);
+
+ if (TYPE_P (orig_decl)) // Can be a template.
+ decl = declaration (TYPE_MAIN_VARIANT (orig_decl));
+ else
+ decl = declaration (orig_decl);
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (!check_qual_decl_type (decl, decl_name, p, loc))
+ return;
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else
+ {
+ error (l) << "type name expected in db pragma " << p << endl;
+ return;
+ }
+ }
+ }
+ else if (p == "member")
+ {
+ // member [(<identifier>)]
+ //
+
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_OPEN_PAREN)
+ {
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NAME && tt != CPP_SCOPE)
+ {
+ error (l) << "data member name expected in db pragma " << p << endl;
+ return;
+ }
+
+ // We need to see if this is a virtual data member declaration. Also,
+ // if it is not, then the name can still refer to one so we need to
+ // take extra steps to handle that. But first, we save the name and
+ // look for the 'virtual' specifier.
+ //
+ cxx_tokens name_tokens;
+ for (; tt != CPP_CLOSE_PAREN && tt != CPP_EOF; tt = l.next (tl, &tn))
+ name_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ // Now scan the remainder of the pragma looking for the 'virtual'
+ // keyword and saving the tokens in between for later.
+ //
+ bool virt (false);
+ size_t balance (0);
+ for (tt = l.next (tl, &tn); tt != CPP_EOF; tt = l.next (tl, &tn))
+ {
+ switch (tt)
+ {
+ case CPP_OPEN_PAREN:
+ {
+ balance++;
+ break;
+ }
+ case CPP_CLOSE_PAREN:
+ {
+ if (balance > 0)
+ balance--;
+ else
+ {
+ error (l) << "unbalanced parenthesis in db pragma " << p << endl;
+ return;
+ }
+ break;
+ }
+ default:
+ {
+ if (balance == 0 && tt == CPP_KEYWORD && tl == "virtual")
+ virt = true;
+ break;
+ }
+ }
+
+ if (virt)
+ break;
+
+ saved_tokens.push_back (cxx_token (l.location (), tt, tl, tn));
+ }
+
+ if (balance != 0)
+ {
+ error (l) << "unbalanced parenthesis in db pragma " << p << endl;
+ return;
+ }
+
+ // Regardless of whether this is a virtual member declaration or a
+ // reference, resolve its scope name (if one is specified), which
+ // should be a class. We will need it in both cases.
+ //
+ tree scope;
+ if (name_tokens.size () > 2) // scope::name
+ {
+ size_t n (name_tokens.size ());
+
+ if (name_tokens[n - 2].type != CPP_SCOPE ||
+ name_tokens[n - 1].type != CPP_NAME)
+ {
+ error (l) << "invalid name in db pragma " << p << endl;
+ return;
+ }
+
+ cxx_tokens scope_tokens (1, name_tokens.back ());
+ name_tokens.pop_back (); // ::
+ name_tokens.pop_back (); // name
+ name_tokens.swap (scope_tokens);
+
+ cxx_tokens_lexer l;
+ l.start (scope_tokens);
+
+ tree tn;
+ string tl;
+ cpp_ttype tt (l.next (tl));
+
+ scope = resolve_scoped_name (
+ l, tt, tl, tn, current_scope (), decl_name, true, p);
+
+ if (scope == 0)
+ return; // Diagnostics has already been issued.
+
+ scope = TYPE_MAIN_VARIANT (TREE_TYPE (scope));
+
+ if (tt != CPP_EOF)
+ {
+ error (l) << "invalid name in db pragma " << p << endl;
+ return;
+ }
+
+ decl_name += "::";
+ }
+ else
+ scope = current_scope ();
+
+ if (virt)
+ {
+ // Should be a single name.
+ //
+ if (name_tokens.size () > 1 || name_tokens.back ().type != CPP_NAME)
+ {
+ location_t l (name_tokens.back ().loc);
+ error (l) << "invalid name in db pragma " << p << endl;
+ return;
+ }
+ string const& name (name_tokens.back ().literal);
+
+ // Parse the remainder of the virtual specifier.
+ //
+ // virtual(<fq-name>)
+ //
+ tree type;
+ string type_name;
+ location_t ord (loc);
+ int ord_bias (0);
+ {
+ string p (tl);
+ location_t loc (l.location ());
+
+ if (l.next (tl, &tn) != CPP_OPEN_PAREN)
+ {
+ error (l) << "'(' expected after db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ // Can be built-in type (e.g., bool).
+ //
+ if (tt == CPP_NAME || tt == CPP_KEYWORD || tt == CPP_SCOPE)
+ {
+ type = resolve_scoped_name (
+ l, tt, tl, tn, current_scope (), type_name, true, p);
+
+ if (type == 0)
+ return; // Diagnostics has already been issued.
+
+ if (TREE_CODE (type) != TYPE_DECL)
+ {
+ error (loc) << "name '" << type_name << "' in db pragma "
+ << p << " does not refer to a type" << endl;
+ return;
+ }
+
+ type = TREE_TYPE (type);
+ }
+ else
+ {
+ error (l) << "type name expected in db pragma " << p << endl;
+ return;
+ }
+
+ if (tt != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma " << p << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+
+ // See if we have before/after specifiers.
+ //
+ if (tt == CPP_NAME && tl == "before")
+ {
+ // before[(<member>)]
+ //
+ // Before without the member name means first.
+ //
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_OPEN_PAREN)
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "')' member name expected in db pragma before"
+ << endl;
+ }
+
+ string dn;
+ cxx_tokens ts (1, cxx_token (l.location (), CPP_NAME, tl));
+ declaration d (resolve_data_member (scope, ts, dn, "before"));
+
+ if (!d)
+ return; // Diagnostics has already been issued.
+
+ if (d.virt)
+ {
+ ord = d.decl.virt->ord;
+ ord_bias = d.decl.virt->ord_bias - 1;
+ }
+ else
+ {
+ ord = real_source_location (d.decl.real);
+ ord_bias = -1;
+ }
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma before"
+ << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else
+ ord = 0;
+ }
+
+ if (tt == CPP_NAME && tl == "after")
+ {
+ // after[(<member>)]
+ //
+ // Before without the member name means last.
+ //
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_OPEN_PAREN)
+ {
+ if (l.next (tl, &tn) != CPP_NAME)
+ {
+ error (l) << "')' member name expected in db pragma after"
+ << endl;
+ }
+
+ string dn;
+ cxx_tokens ts (1, cxx_token (l.location (), CPP_NAME, tl));
+ declaration d (resolve_data_member (scope, ts, dn, "after"));
+
+ if (!d)
+ return; // Diagnostics has already been issued.
+
+ if (d.virt)
+ {
+ ord = d.decl.virt->ord;
+ ord_bias = d.decl.virt->ord_bias + 1;
+ }
+ else
+ {
+ ord = real_source_location (d.decl.real);
+ ord_bias = 1;
+ }
+
+ if (l.next (tl, &tn) != CPP_CLOSE_PAREN)
+ {
+ error (l) << "')' expected at the end of db pragma after"
+ << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else
+ ord = ~location_t (0);
+ }
+ }
+
+ pair<virt_declaration_set::const_iterator, bool> r (
+ virt_declarations_[scope].insert (
+ virt_declaration (loc, ord, ord_bias, name, FIELD_DECL, type)));
+
+ if (!r.second)
+ {
+ error (loc) << "virtual data member declaration '" << name
+ << "' conflicts with a previous declaration" << endl;
+
+ info (r.first->loc) << "'" << name << "' was previously "
+ << "declared here" << endl;
+ return;
+ }
+
+ decl_name += name;
+ decl = declaration (*r.first);
+
+ // Mark it as virtual using the standard pragma machinery.
+ //
+ add_pragma (
+ pragma ("virtual", "virtual", true, loc, &check_spec_decl_type, 0),
+ decl,
+ false);
+ }
+ else
+ {
+ // Not a virtual member declaration.
+ //
+ decl = resolve_data_member (scope, name_tokens, decl_name, p);
+
+ if (!decl)
+ return; // Diagnostics has already been issued.
+
+ // Make sure we've got the correct declaration type.
+ //
+ if (!check_qual_decl_type (decl, decl_name, p, loc))
+ return;
+ }
+ }
+ }
+ //
+ // The member qualifier can be omitted so we need to also handle all
+ // the member pragmas here.
+ //
+ else if (p == "id" ||
+ p == "auto" ||
+ p == "unique" ||
+ p == "get" ||
+ p == "set" ||
+ p == "access" ||
+ p == "column" ||
+ p == "value_column" ||
+ p == "index_column" ||
+ p == "key_column" ||
+ p == "id_column" ||
+ p == "options" ||
+ p == "value_options" ||
+ p == "index_options" ||
+ p == "key_options" ||
+ p == "id_options" ||
+ p == "type" ||
+ p == "id_type" ||
+ p == "value_type" ||
+ p == "index_type" ||
+ p == "key_type" ||
+ p == "table" ||
+ p == "null" ||
+ p == "not_null" ||
+ p == "key_null" ||
+ p == "key_not_null" ||
+ p == "value_null" ||
+ p == "value_not_null" ||
+ p == "default" ||
+ p == "section" ||
+ p == "load" ||
+ p == "update" ||
+ p == "inverse" ||
+ p == "on_delete" ||
+ p == "points_to" ||
+ p == "unordered" ||
+ p == "readonly" ||
+ p == "transient" ||
+ p == "added" ||
+ p == "deleted" ||
+ p == "version" ||
+ p == "virtual")
+ {
+ handle_pragma (l, db, p, "member", val, declaration (), "", false);
+ return;
+ }
+ else
+ {
+ error (l) << "unknown db pragma " << p << endl;
+ return;
+ }
+
+ // Record this pragma. Delay this until after we process the
+ // specifiers for value (see comment below for the reason).
+ //
+ if (adder == 0)
+ val = orig_decl;
+
+ pragma prag (p,
+ name, // For now no need to translate '_' to '-'.
+ val,
+ loc,
+ &check_qual_decl_type,
+ adder != 0 ? adder : &add_qual_entry);
+
+ tree scope;
+ if (!decl)
+ {
+ scope = current_scope ();
+
+ if (!ns && !CLASS_TYPE_P (scope))
+ scope = global_namespace;
+ }
+
+ any* pval;
+ if (p != "value")
+ {
+ if (decl)
+ pval = &decl_pragmas_[decl].insert (prag).value;
+ else
+ {
+ if (!ns)
+ {
+ pragma_list& pl (loc_pragmas_[scope]);
+ pl.push_back (prag);
+ pval = &pl.back ().value;
+ }
+ else
+ {
+ ns_loc_pragmas_.push_back (ns_loc_pragma (scope, prag));
+ pval = &ns_loc_pragmas_.back ().pragma.value;
+ }
+ }
+ }
+ else
+ pval = &val;
+
+ // See if there are any saved tokens to replay.
+ //
+ if (!saved_tokens.empty ())
+ {
+ cxx_tokens_lexer l;
+ l.start (saved_tokens);
+
+ string tl;
+ cpp_ttype tt (l.next (tl));
+
+ if (tt == CPP_NAME || tt == CPP_KEYWORD)
+ {
+ handle_pragma (l, "", tl, p, *pval, decl, decl_name, ns);
+
+ if (errorcount != 0) // Avoid parsing the rest if there was an error.
+ return;
+ }
+ else if (tt != CPP_EOF)
+ {
+ error (l) << "unexpected text after " << p << " in db pragma" << endl;
+ return;
+ }
+ }
+
+ size_t count (0);
+ if (tt == CPP_NAME || tt == CPP_KEYWORD)
+ {
+ if (decl)
+ count = decl_pragmas_[decl].size ();
+ else
+ count = loc_pragmas_[scope].size ();
+
+ handle_pragma (l, "", tl, p, *pval, decl, decl_name, ns);
+ }
+ else if (tt != CPP_EOF)
+ {
+ error (l) << "unexpected text after " << p << " in db pragma" << endl;
+ return;
+ }
+
+ // Record the value pragma. Here things are complicated by the fact
+ // that we use the value pragma by itself to signify a composite value
+ // type declaration. Consider this example:
+ //
+ // #pragma db value pgsql:type("POINT")
+ // class point {...};
+ //
+ // Should this class be considered composite value type in other
+ // databases (because that's what would happen by default)? Probably
+ // not. So to overcome this we are going to detect and ignore cases
+ // where (a) some specifiers followed the value qualifier but (b)
+ // none of them are for the database that we are compiling for.
+ //
+ if (p == "value")
+ {
+ if (decl)
+ {
+ pragma_set& ps (decl_pragmas_[decl]);
+
+ if (tt == CPP_EOF || ps.size () > count)
+ ps.insert (prag);
+ }
+ else
+ {
+ pragma_list& pl (loc_pragmas_[scope]);
+
+ if (tt == CPP_EOF || pl.size () > count)
+ pl.push_back (prag);
+ }
+ }
+}
+
+/*
+extern "C" void
+handle_pragma_db_mysql (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "mysql");
+}
+
+extern "C" void
+handle_pragma_db_sqlite (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "sqlite");
+}
+
+extern "C" void
+handle_pragma_db_pgsql (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "pgsql");
+}
+
+extern "C" void
+handle_pragma_db_oracle (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "oracle");
+}
+
+extern "C" void
+handle_pragma_db_mssql (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "mssql");
+}
+
+extern "C" void
+handle_pragma_db_model (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "model");
+}
+
+extern "C" void
+handle_pragma_db_map (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "map");
+}
+
+extern "C" void
+handle_pragma_db_namespace (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "namespace");
+}
+
+extern "C" void
+handle_pragma_db_object (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "object");
+}
+
+extern "C" void
+handle_pragma_db_view (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "view");
+}
+
+extern "C" void
+handle_pragma_db_value (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "value");
+}
+
+extern "C" void
+handle_pragma_db_member (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "member");
+}
+
+extern "C" void
+handle_pragma_db_id (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "id");
+}
+
+extern "C" void
+handle_pragma_db_auto (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "auto");
+}
+
+extern "C" void
+handle_pragma_db_column (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "column");
+}
+
+extern "C" void
+handle_pragma_db_vcolumn (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "value_column");
+}
+
+extern "C" void
+handle_pragma_db_icolumn (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "index_column");
+}
+
+extern "C" void
+handle_pragma_db_kcolumn (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "key_column");
+}
+
+extern "C" void
+handle_pragma_db_idcolumn (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "id_column");
+}
+
+extern "C" void
+handle_pragma_db_options (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "options");
+}
+
+extern "C" void
+handle_pragma_db_voptions (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "value_options");
+}
+
+extern "C" void
+handle_pragma_db_ioptions (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "index_options");
+}
+
+extern "C" void
+handle_pragma_db_koptions (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "key_options");
+}
+
+extern "C" void
+handle_pragma_db_idoptions (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "id_options");
+}
+
+extern "C" void
+handle_pragma_db_type (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "type");
+}
+
+extern "C" void
+handle_pragma_db_id_type (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "id_type");
+}
+
+extern "C" void
+handle_pragma_db_vtype (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "value_type");
+}
+
+extern "C" void
+handle_pragma_db_itype (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "index_type");
+}
+
+extern "C" void
+handle_pragma_db_ktype (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "key_type");
+}
+
+extern "C" void
+handle_pragma_db_table (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "table");
+}
+
+extern "C" void
+handle_pragma_db_null (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "null");
+}
+
+extern "C" void
+handle_pragma_db_not_null (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "not_null");
+}
+
+extern "C" void
+handle_pragma_db_value_null (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "value_null");
+}
+
+extern "C" void
+handle_pragma_db_value_not_null (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "value_not_null");
+}
+
+extern "C" void
+handle_pragma_db_key_null (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "key_null");
+}
+
+extern "C" void
+handle_pragma_db_key_not_null (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "key_not_null");
+}
+
+extern "C" void
+handle_pragma_db_default (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "default");
+}
+
+extern "C" void
+handle_pragma_db_section (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "section");
+}
+
+extern "C" void
+handle_pragma_db_load (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "load");
+}
+
+extern "C" void
+handle_pragma_db_update (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "update");
+}
+
+extern "C" void
+handle_pragma_db_inverse (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "inverse");
+}
+
+extern "C" void
+handle_pragma_db_on_delete (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "on_delete");
+}
+
+extern "C" void
+handle_pragma_db_points_to (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "points_to");
+}
+
+extern "C" void
+handle_pragma_db_unordered (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "unordered");
+}
+
+extern "C" void
+handle_pragma_db_readonly (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "readonly");
+}
+
+extern "C" void
+handle_pragma_db_transient (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "transient");
+}
+
+extern "C" void
+handle_pragma_db_added (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "added");
+}
+
+extern "C" void
+handle_pragma_db_deleted (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "deleted");
+}
+
+extern "C" void
+handle_pragma_db_version (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "version");
+}
+
+extern "C" void
+handle_pragma_db_virtual (cpp_reader* r)
+{
+ handle_pragma_qualifier (r, "virtual");
+}
+*/
+
+extern "C" void
+handle_pragma_db (cpp_reader*)
+{
+ cxx_pragma_lexer l;
+ l.start ();
+
+ string tl;
+ cpp_ttype tt (l.next (tl));
+
+ if (tt != CPP_NAME && tt != CPP_KEYWORD)
+ {
+ error (l) << "expected specifier after db pragma" << endl;
+ return;
+ }
+
+ handle_pragma_qualifier (l, tl);
+}
+
+extern "C" void
+register_odb_pragmas (void*, void*)
+{
+ // GCC has a limited number of pragma slots and we have exhausted them.
+ // A workaround is to make 'db' a pragma rather than a namespace. This
+ // way we only have one pragma but the drawback of this approach is the
+ // fact that the specifier or qualifier name will now be macro-expanded
+ // (though this happens anyway if we have multiple specifiers in a single
+ // pragma). Once the GCC folks fix this, we can go back to the namespace
+ // approach.
+ //
+ c_register_pragma_with_expansion (0, "db", handle_pragma_db);
+
+ /*
+ c_register_pragma_with_expansion ("db", "mysql", handle_pragma_db_mysql);
+ c_register_pragma_with_expansion ("db", "sqlite", handle_pragma_db_sqlite);
+ c_register_pragma_with_expansion ("db", "pgsql", handle_pragma_db_pgsql);
+ c_register_pragma_with_expansion ("db", "oracle", handle_pragma_db_oracle);
+ c_register_pragma_with_expansion ("db", "mssql", handle_pragma_db_mssql);
+ c_register_pragma_with_expansion ("db", "model", handle_pragma_db_model);
+ c_register_pragma_with_expansion ("db", "map", handle_pragma_db_map);
+ c_register_pragma_with_expansion ("db", "namespace", handle_pragma_db_namespace);
+ c_register_pragma_with_expansion ("db", "object", handle_pragma_db_object);
+ c_register_pragma_with_expansion ("db", "view", handle_pragma_db_view);
+ c_register_pragma_with_expansion ("db", "value", handle_pragma_db_value);
+ c_register_pragma_with_expansion ("db", "member", handle_pragma_db_member);
+ c_register_pragma_with_expansion ("db", "id", handle_pragma_db_id);
+ c_register_pragma_with_expansion ("db", "auto", handle_pragma_db_auto);
+ c_register_pragma_with_expansion ("db", "column", handle_pragma_db_column);
+ c_register_pragma_with_expansion ("db", "value_column", handle_pragma_db_vcolumn);
+ c_register_pragma_with_expansion ("db", "index_column", handle_pragma_db_icolumn);
+ c_register_pragma_with_expansion ("db", "key_column", handle_pragma_db_kcolumn);
+ c_register_pragma_with_expansion ("db", "id_column", handle_pragma_db_idcolumn);
+ c_register_pragma_with_expansion ("db", "options", handle_pragma_db_options);
+ c_register_pragma_with_expansion ("db", "value_options", handle_pragma_db_voptions);
+ c_register_pragma_with_expansion ("db", "index_options", handle_pragma_db_ioptions);
+ c_register_pragma_with_expansion ("db", "key_options", handle_pragma_db_koptions);
+ c_register_pragma_with_expansion ("db", "id_options", handle_pragma_db_idoptions);
+ c_register_pragma_with_expansion ("db", "type", handle_pragma_db_type);
+ c_register_pragma_with_expansion ("db", "id_type", handle_pragma_db_id_type);
+ c_register_pragma_with_expansion ("db", "value_type", handle_pragma_db_vtype);
+ c_register_pragma_with_expansion ("db", "index_type", handle_pragma_db_itype);
+ c_register_pragma_with_expansion ("db", "key_type", handle_pragma_db_ktype);
+ c_register_pragma_with_expansion ("db", "table", handle_pragma_db_table);
+ c_register_pragma_with_expansion ("db", "null", handle_pragma_db_null);
+ c_register_pragma_with_expansion ("db", "not_null", handle_pragma_db_not_null);
+ c_register_pragma_with_expansion ("db", "key_null", handle_pragma_db_key_null);
+ c_register_pragma_with_expansion ("db", "key_not_null", handle_pragma_db_key_not_null);
+ c_register_pragma_with_expansion ("db", "value_null", handle_pragma_db_value_null);
+ c_register_pragma_with_expansion ("db", "value_not_null", handle_pragma_db_value_not_null);
+ c_register_pragma_with_expansion ("db", "default", handle_pragma_db_default);
+ c_register_pragma_with_expansion ("db", "section", handle_pragma_db_section);
+ c_register_pragma_with_expansion ("db", "load", handle_pragma_db_load);
+ c_register_pragma_with_expansion ("db", "update", handle_pragma_db_update);
+ c_register_pragma_with_expansion ("db", "inverse", handle_pragma_db_inverse);
+ c_register_pragma_with_expansion ("db", "on_delete", handle_pragma_db_on_delete);
+ c_register_pragma_with_expansion ("db", "points_to", handle_pragma_db_points_to);
+ c_register_pragma_with_expansion ("db", "unordered", handle_pragma_db_unordered);
+ c_register_pragma_with_expansion ("db", "readonly", handle_pragma_db_readonly);
+ c_register_pragma_with_expansion ("db", "transient", handle_pragma_db_transient);
+ c_register_pragma_with_expansion ("db", "added", handle_pragma_db_added);
+ c_register_pragma_with_expansion ("db", "deleted", handle_pragma_db_deleted);
+ c_register_pragma_with_expansion ("db", "version", handle_pragma_db_version);
+ c_register_pragma_with_expansion ("db", "virtual", handle_pragma_db_virtual);
+ */
+}
+
+void
+post_process_pragmas ()
+{
+ // Make sure object, view, and composite class template instantiations
+ // are fully instantiated.
+ //
+ for (decl_pragmas::iterator i (decl_pragmas_.begin ()),
+ e (decl_pragmas_.end ()); i != e; ++i)
+ {
+ if (i->first.virt)
+ continue;
+
+ tree type (i->first.decl.real);
+
+ if (!(CLASS_TYPE_P (type) && CLASSTYPE_TEMPLATE_INSTANTIATION (type)))
+ continue;
+
+ // Check whether this is an object, view, or composite value type.
+ //
+ pragma const* p (0);
+ string diag_name;
+
+ for (pragma_set::iterator j (i->second.begin ()), e (i->second.end ());
+ j != e; ++j)
+ {
+ string const& name (j->second.context_name);
+
+ if (name == "object")
+ {
+ p = &j->second;
+ diag_name = "persistent object";
+ break;
+ }
+ else if (name == "view")
+ {
+ p = &j->second;
+ diag_name = "view";
+ break;
+ }
+ else if (name == "value")
+ {
+ p = &j->second;
+ diag_name = "composite value";
+ break;
+ }
+ // We don't want to instantiate simple values since they may be
+ // incomplete.
+ //
+ else if (name == "simple" || name == "container")
+ {
+ p = 0;
+ break;
+ }
+ }
+
+ if (p == 0)
+ continue;
+
+ // Make sure it is instantiated.
+ //
+ tree decl (TYPE_NAME (p->value.value<tree> ()));
+ location_t loc (real_source_location (decl));
+
+ // Reset input location so that we get nice diagnostics in case
+ // of an error.
+ //
+ input_location = loc;
+
+ if (instantiate_class_template (type) == error_mark_node ||
+ errorcount != 0 ||
+ !COMPLETE_TYPE_P (type))
+ {
+ error (loc) << "unable to instantiate " << diag_name << " class template"
+ << endl;
+ throw pragmas_failed ();
+ }
+ }
+}
diff --git a/odb/odb/pragma.hxx b/odb/odb/pragma.hxx
new file mode 100644
index 0000000..0d4d3f1
--- /dev/null
+++ b/odb/odb/pragma.hxx
@@ -0,0 +1,287 @@
+// file : odb/pragma.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_PRAGMA_HXX
+#define ODB_PRAGMA_HXX
+
+#include <odb/gcc.hxx>
+
+#include <map>
+#include <set>
+#include <vector>
+#include <string>
+
+#include <odb/option-types.hxx> // database
+
+#include <libcutl/container/any.hxx>
+#include <libcutl/container/multi-index.hxx>
+#include <libcutl/compiler/context.hxx>
+
+struct virt_declaration
+{
+ virt_declaration (location_t l,
+ location_t o,
+ int ob,
+ std::string const& n,
+ gcc_tree_code_type tc,
+ tree t)
+ : loc (l), ord (o), ord_bias (ob), name (n), tree_code (tc), type (t) {}
+
+ location_t loc;
+ location_t ord; // Ordering location for before/after support.
+ int ord_bias; // Ordering bias for the same locations.
+ std::string name;
+ gcc_tree_code_type tree_code;
+ tree type; // Declaration's type.
+};
+
+// Note that we consider virtual declarations with the same name but
+// different tree codes unequal. If that is too loose, then the
+// inserting code must do additional checks.
+//
+struct virt_declaration_set
+{
+ typedef cutl::container::key<std::string, gcc_tree_code_type> key;
+ typedef std::map<key, virt_declaration> map;
+ typedef cutl::container::map_const_iterator<map> const_iterator;
+
+ std::pair<const_iterator, bool>
+ insert (const virt_declaration& v)
+ {
+ std::pair<map::iterator, bool> r (
+ map_.insert (map::value_type (key (v.name, v.tree_code), v)));
+
+ const_iterator i (r.first);
+
+ if (r.second)
+ r.first->first.assign (i->name, i->tree_code);
+
+ return std::make_pair (i, r.second);
+ }
+
+ const_iterator
+ find (std::string const& name, gcc_tree_code_type tree_code) const
+ {
+ return map_.find (key (name, tree_code));
+ }
+
+ const_iterator begin () const {return map_.begin ();}
+ const_iterator end () const {return map_.end ();}
+
+private:
+ map map_;
+};
+
+// Map of scopes (e.g., class, namespace) to sets of virtual declarations.
+//
+typedef std::map<tree, virt_declaration_set> virt_declarations;
+extern virt_declarations virt_declarations_;
+
+// Real or virtual declaration. If it is real, then it is a pointer to
+// the GCC tree node. Otherwise, it is a pointer to virt_declaration
+// from virt_declarations_ above.
+//
+struct declaration
+{
+ declaration (): virt (false) {decl.real = 0;}
+ declaration (tree d): virt (false) {decl.real = d;}
+ declaration (virt_declaration const& d): virt (true) {decl.virt = &d;}
+
+ bool virt;
+
+ union
+ {
+ tree real;
+ virt_declaration const* virt;
+ } decl;
+
+ gcc_tree_code_type
+ tree_code () const
+ {
+ return (virt ? decl.virt->tree_code : TREE_CODE (decl.real));
+ }
+
+ typedef bool declaration::*bool_convertible;
+ operator bool_convertible () const
+ {
+ return ptr () == 0 ? 0 : &declaration::virt;
+ }
+
+public:
+ bool
+ operator== (declaration const& x) const
+ {
+ return virt == x.virt && ptr () == x.ptr ();
+ }
+
+ bool
+ operator!= (declaration const& x) const
+ {
+ return !(*this == x);
+ }
+
+ bool
+ operator< (declaration const& x) const
+ {
+ return virt < x.virt || (virt == x.virt && ptr () < x.ptr ());
+ }
+
+public:
+ void const*
+ ptr () const
+ {
+ return virt
+ ? static_cast<void const*> (decl.virt)
+ : static_cast<void const*> (decl.real);
+ }
+};
+
+inline bool
+operator== (declaration const& x, tree y)
+{
+ return !x.virt && x.decl.real == y;
+}
+
+inline bool
+operator== (tree x, declaration const& y)
+{
+ return !y.virt && y.decl.real == x;
+}
+
+struct pragma
+{
+ // Check that the pragma is applicable to the declaration. Return true
+ // on success, complain and return false otherwise.
+ //
+ typedef bool (*check_func) (declaration const& decl,
+ std::string const& decl_name,
+ std::string const& prag_name,
+ location_t);
+
+ // Add the pragma value to the context.
+ //
+ typedef void (*add_func) (cutl::compiler::context&,
+ std::string const& key,
+ cutl::container::any const& value,
+ location_t);
+
+ pragma (std::string const& pn,
+ std::string const& cn,
+ cutl::container::any const& v,
+ location_t l,
+ check_func c,
+ add_func a)
+ : pragma_name (pn),
+ context_name (cn),
+ value (v),
+ loc (l),
+ check (c),
+ add (a)
+ {
+ }
+
+ std::string pragma_name; // Actual pragma name for diagnostics.
+ std::string context_name; // Context entry name.
+ cutl::container::any value;
+ location_t loc;
+ check_func check;
+ add_func add;
+};
+
+typedef std::vector<pragma> pragma_list;
+
+// A set of pragmas. Insertion of a pragma with the same name and no
+// custom add function overrides the old value.
+//
+struct pragma_set: std::multimap<std::string, pragma>
+{
+ typedef std::multimap<std::string, pragma> base;
+
+ pragma&
+ insert (pragma const& p)
+ {
+ std::string const& n (p.context_name);
+ std::pair<iterator, iterator> r (equal_range (n));
+
+ iterator i (end ());
+
+ if (p.add == 0)
+ {
+ if (r.first != r.second)
+ {
+ i = r.first;
+ assert (++r.first == r.second);
+
+ i->second = p;
+ }
+ }
+ else if (r.first != r.second)
+ assert ((--r.second)->second.loc <= p.loc);
+
+ if (i == end ())
+ i = base::insert (base::value_type (n, p));
+
+ return i->second;
+ }
+
+ void
+ insert (const_iterator begin, const_iterator end)
+ {
+ for (; begin != end; ++begin)
+ insert (begin->second);
+ }
+
+ // Return the last pragma in the equal range which (by construction) has the
+ // location greater or equal to all the other pragmas in this range.
+ //
+ iterator
+ find (std::string const& n)
+ {
+ return equal_range (n).second;
+ }
+};
+
+
+// Position pragmas inside a class or namespace. The key for the
+// namespace case is the global_namespace node.
+//
+typedef std::map<tree, pragma_list> loc_pragmas;
+extern loc_pragmas loc_pragmas_;
+
+// Position pragmas for namespaces. Because re-opened namespaces do
+// not have any representation in the GCC tree, these are handled in
+// a special way. They are stored as a list of pragmas and their outer
+// namespaces.
+//
+struct ns_loc_pragma
+{
+ typedef ::pragma pragma_type;
+ ns_loc_pragma (tree n, pragma_type const& p): ns (n), pragma (p) {}
+
+ tree ns;
+ pragma_type pragma;
+};
+
+typedef std::vector<ns_loc_pragma> ns_loc_pragmas;
+extern ns_loc_pragmas ns_loc_pragmas_;
+
+// Pragmas associated with specific declarations (real or virtual).
+//
+typedef std::map<declaration, pragma_set> decl_pragmas;
+extern decl_pragmas decl_pragmas_;
+
+// Database we are generating code for as well as multi-database support.
+// Used to ignore database-specific pragmas.
+//
+extern database pragma_db_;
+extern multi_database pragma_multi_;
+
+extern "C" void
+register_odb_pragmas (void*, void*);
+
+struct pragmas_failed {};
+
+void
+post_process_pragmas ();
+
+#endif // ODB_PRAGMA_HXX
diff --git a/odb/odb/pregenerated/odb/options.cxx b/odb/odb/pregenerated/odb/options.cxx
new file mode 100644
index 0000000..da22570
--- /dev/null
+++ b/odb/odb/pregenerated/odb/options.cxx
@@ -0,0 +1,4034 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+#include <odb/option-parsers.hxx>
+//
+// End prologue.
+
+#include <odb/options.hxx>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include <utility>
+#include <ostream>
+#include <sstream>
+#include <cstring>
+#include <fstream>
+
+namespace cli
+{
+ // unknown_option
+ //
+ unknown_option::
+ ~unknown_option () throw ()
+ {
+ }
+
+ void unknown_option::
+ print (::std::ostream& os) const
+ {
+ os << "unknown option '" << option ().c_str () << "'";
+ }
+
+ const char* unknown_option::
+ what () const throw ()
+ {
+ return "unknown option";
+ }
+
+ // unknown_argument
+ //
+ unknown_argument::
+ ~unknown_argument () throw ()
+ {
+ }
+
+ void unknown_argument::
+ print (::std::ostream& os) const
+ {
+ os << "unknown argument '" << argument ().c_str () << "'";
+ }
+
+ const char* unknown_argument::
+ what () const throw ()
+ {
+ return "unknown argument";
+ }
+
+ // missing_value
+ //
+ missing_value::
+ ~missing_value () throw ()
+ {
+ }
+
+ void missing_value::
+ print (::std::ostream& os) const
+ {
+ os << "missing value for option '" << option ().c_str () << "'";
+ }
+
+ const char* missing_value::
+ what () const throw ()
+ {
+ return "missing option value";
+ }
+
+ // invalid_value
+ //
+ invalid_value::
+ ~invalid_value () throw ()
+ {
+ }
+
+ void invalid_value::
+ print (::std::ostream& os) const
+ {
+ os << "invalid value '" << value ().c_str () << "' for option '"
+ << option ().c_str () << "'";
+
+ if (!message ().empty ())
+ os << ": " << message ().c_str ();
+ }
+
+ const char* invalid_value::
+ what () const throw ()
+ {
+ return "invalid option value";
+ }
+
+ // eos_reached
+ //
+ void eos_reached::
+ print (::std::ostream& os) const
+ {
+ os << what ();
+ }
+
+ const char* eos_reached::
+ what () const throw ()
+ {
+ return "end of argument stream reached";
+ }
+
+ // file_io_failure
+ //
+ file_io_failure::
+ ~file_io_failure () throw ()
+ {
+ }
+
+ void file_io_failure::
+ print (::std::ostream& os) const
+ {
+ os << "unable to open file '" << file ().c_str () << "' or read failure";
+ }
+
+ const char* file_io_failure::
+ what () const throw ()
+ {
+ return "unable to open file or read failure";
+ }
+
+ // unmatched_quote
+ //
+ unmatched_quote::
+ ~unmatched_quote () throw ()
+ {
+ }
+
+ void unmatched_quote::
+ print (::std::ostream& os) const
+ {
+ os << "unmatched quote in argument '" << argument ().c_str () << "'";
+ }
+
+ const char* unmatched_quote::
+ what () const throw ()
+ {
+ return "unmatched quote";
+ }
+
+ // scanner
+ //
+ scanner::
+ ~scanner ()
+ {
+ }
+
+ // argv_scanner
+ //
+ bool argv_scanner::
+ more ()
+ {
+ return i_ < argc_;
+ }
+
+ const char* argv_scanner::
+ peek ()
+ {
+ if (i_ < argc_)
+ return argv_[i_];
+ else
+ throw eos_reached ();
+ }
+
+ const char* argv_scanner::
+ next ()
+ {
+ if (i_ < argc_)
+ {
+ const char* r (argv_[i_]);
+
+ if (erase_)
+ {
+ for (int i (i_ + 1); i < argc_; ++i)
+ argv_[i - 1] = argv_[i];
+
+ --argc_;
+ argv_[argc_] = 0;
+ }
+ else
+ ++i_;
+
+ ++start_position_;
+ return r;
+ }
+ else
+ throw eos_reached ();
+ }
+
+ void argv_scanner::
+ skip ()
+ {
+ if (i_ < argc_)
+ {
+ ++i_;
+ ++start_position_;
+ }
+ else
+ throw eos_reached ();
+ }
+
+ std::size_t argv_scanner::
+ position ()
+ {
+ return start_position_;
+ }
+
+ // argv_file_scanner
+ //
+ int argv_file_scanner::zero_argc_ = 0;
+ std::string argv_file_scanner::empty_string_;
+
+ bool argv_file_scanner::
+ more ()
+ {
+ if (!args_.empty ())
+ return true;
+
+ while (base::more ())
+ {
+ // See if the next argument is the file option.
+ //
+ const char* a (base::peek ());
+ const option_info* oi = 0;
+ const char* ov = 0;
+
+ if (!skip_)
+ {
+ if ((oi = find (a)) != 0)
+ {
+ base::next ();
+
+ if (!base::more ())
+ throw missing_value (a);
+
+ ov = base::next ();
+ }
+ else if (std::strncmp (a, "-", 1) == 0)
+ {
+ if ((ov = std::strchr (a, '=')) != 0)
+ {
+ std::string o (a, 0, ov - a);
+ if ((oi = find (o.c_str ())) != 0)
+ {
+ base::next ();
+ ++ov;
+ }
+ }
+ }
+ }
+
+ if (oi != 0)
+ {
+ if (oi->search_func != 0)
+ {
+ std::string f (oi->search_func (ov, oi->arg));
+
+ if (!f.empty ())
+ load (f);
+ }
+ else
+ load (ov);
+
+ if (!args_.empty ())
+ return true;
+ }
+ else
+ {
+ if (!skip_)
+ skip_ = (std::strcmp (a, "--") == 0);
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ const char* argv_file_scanner::
+ peek ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? base::peek () : args_.front ().value.c_str ();
+ }
+
+ const std::string& argv_file_scanner::
+ peek_file ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? empty_string_ : *args_.front ().file;
+ }
+
+ std::size_t argv_file_scanner::
+ peek_line ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ return args_.empty () ? 0 : args_.front ().line;
+ }
+
+ const char* argv_file_scanner::
+ next ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ if (args_.empty ())
+ return base::next ();
+ else
+ {
+ hold_[i_ == 0 ? ++i_ : --i_].swap (args_.front ().value);
+ args_.pop_front ();
+ ++start_position_;
+ return hold_[i_].c_str ();
+ }
+ }
+
+ void argv_file_scanner::
+ skip ()
+ {
+ if (!more ())
+ throw eos_reached ();
+
+ if (args_.empty ())
+ return base::skip ();
+ else
+ {
+ args_.pop_front ();
+ ++start_position_;
+ }
+ }
+
+ const argv_file_scanner::option_info* argv_file_scanner::
+ find (const char* a) const
+ {
+ for (std::size_t i (0); i < options_count_; ++i)
+ if (std::strcmp (a, options_[i].option) == 0)
+ return &options_[i];
+
+ return 0;
+ }
+
+ std::size_t argv_file_scanner::
+ position ()
+ {
+ return start_position_;
+ }
+
+ void argv_file_scanner::
+ load (const std::string& file)
+ {
+ using namespace std;
+
+ ifstream is (file.c_str ());
+
+ if (!is.is_open ())
+ throw file_io_failure (file);
+
+ files_.push_back (file);
+
+ arg a;
+ a.file = &*files_.rbegin ();
+
+ for (a.line = 1; !is.eof (); ++a.line)
+ {
+ string line;
+ getline (is, line);
+
+ if (is.fail () && !is.eof ())
+ throw file_io_failure (file);
+
+ string::size_type n (line.size ());
+
+ // Trim the line from leading and trailing whitespaces.
+ //
+ if (n != 0)
+ {
+ const char* f (line.c_str ());
+ const char* l (f + n);
+
+ const char* of (f);
+ while (f < l && (*f == ' ' || *f == '\t' || *f == '\r'))
+ ++f;
+
+ --l;
+
+ const char* ol (l);
+ while (l > f && (*l == ' ' || *l == '\t' || *l == '\r'))
+ --l;
+
+ if (f != of || l != ol)
+ line = f <= l ? string (f, l - f + 1) : string ();
+ }
+
+ // Ignore empty lines, those that start with #.
+ //
+ if (line.empty () || line[0] == '#')
+ continue;
+
+ string::size_type p (string::npos);
+ if (line.compare (0, 1, "-") == 0)
+ {
+ p = line.find (' ');
+
+ string::size_type q (line.find ('='));
+ if (q != string::npos && q < p)
+ p = q;
+ }
+
+ string s1;
+ if (p != string::npos)
+ {
+ s1.assign (line, 0, p);
+
+ // Skip leading whitespaces in the argument.
+ //
+ if (line[p] == '=')
+ ++p;
+ else
+ {
+ n = line.size ();
+ for (++p; p < n; ++p)
+ {
+ char c (line[p]);
+ if (c != ' ' && c != '\t' && c != '\r')
+ break;
+ }
+ }
+ }
+ else if (!skip_)
+ skip_ = (line == "--");
+
+ string s2 (line, p != string::npos ? p : 0);
+
+ // If the string (which is an option value or argument) is
+ // wrapped in quotes, remove them.
+ //
+ n = s2.size ();
+ char cf (s2[0]), cl (s2[n - 1]);
+
+ if (cf == '"' || cf == '\'' || cl == '"' || cl == '\'')
+ {
+ if (n == 1 || cf != cl)
+ throw unmatched_quote (s2);
+
+ s2 = string (s2, 1, n - 2);
+ }
+
+ if (!s1.empty ())
+ {
+ // See if this is another file option.
+ //
+ const option_info* oi;
+ if (!skip_ && (oi = find (s1.c_str ())))
+ {
+ if (s2.empty ())
+ throw missing_value (oi->option);
+
+ if (oi->search_func != 0)
+ {
+ string f (oi->search_func (s2.c_str (), oi->arg));
+ if (!f.empty ())
+ load (f);
+ }
+ else
+ {
+ // If the path of the file being parsed is not simple and the
+ // path of the file that needs to be loaded is relative, then
+ // complete the latter using the former as a base.
+ //
+#ifndef _WIN32
+ string::size_type p (file.find_last_of ('/'));
+ bool c (p != string::npos && s2[0] != '/');
+#else
+ string::size_type p (file.find_last_of ("/\\"));
+ bool c (p != string::npos && s2[1] != ':');
+#endif
+ if (c)
+ s2.insert (0, file, 0, p + 1);
+
+ load (s2);
+ }
+
+ continue;
+ }
+
+ a.value = s1;
+ args_.push_back (a);
+ }
+
+ a.value = s2;
+ args_.push_back (a);
+ }
+ }
+
+ void options::
+ push_back (const option& o)
+ {
+ container_type::size_type n (size ());
+ container_type::push_back (o);
+ map_[o.name ()] = n;
+
+ for (option_names::const_iterator i (o.aliases ().begin ());
+ i != o.aliases ().end (); ++i)
+ map_[*i] = n;
+ }
+
+ template <typename X>
+ struct parser
+ {
+ static void
+ parse (X& x, bool& xs, scanner& s)
+ {
+ using namespace std;
+
+ const char* o (s.next ());
+ if (s.more ())
+ {
+ string v (s.next ());
+ istringstream is (v);
+ if (!(is >> x && is.peek () == istringstream::traits_type::eof ()))
+ throw invalid_value (o, v);
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <>
+ struct parser<bool>
+ {
+ static void
+ parse (bool& x, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ const char* v (s.next ());
+
+ if (std::strcmp (v, "1") == 0 ||
+ std::strcmp (v, "true") == 0 ||
+ std::strcmp (v, "TRUE") == 0 ||
+ std::strcmp (v, "True") == 0)
+ x = true;
+ else if (std::strcmp (v, "0") == 0 ||
+ std::strcmp (v, "false") == 0 ||
+ std::strcmp (v, "FALSE") == 0 ||
+ std::strcmp (v, "False") == 0)
+ x = false;
+ else
+ throw invalid_value (o, v);
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <>
+ struct parser<std::string>
+ {
+ static void
+ parse (std::string& x, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ x = s.next ();
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename X>
+ struct parser<std::pair<X, std::size_t> >
+ {
+ static void
+ parse (std::pair<X, std::size_t>& x, bool& xs, scanner& s)
+ {
+ x.second = s.position ();
+ parser<X>::parse (x.first, xs, s);
+ }
+ };
+
+ template <typename X>
+ struct parser<std::vector<X> >
+ {
+ static void
+ parse (std::vector<X>& c, bool& xs, scanner& s)
+ {
+ X x;
+ bool dummy;
+ parser<X>::parse (x, dummy, s);
+ c.push_back (x);
+ xs = true;
+ }
+ };
+
+ template <typename X, typename C>
+ struct parser<std::set<X, C> >
+ {
+ static void
+ parse (std::set<X, C>& c, bool& xs, scanner& s)
+ {
+ X x;
+ bool dummy;
+ parser<X>::parse (x, dummy, s);
+ c.insert (x);
+ xs = true;
+ }
+ };
+
+ template <typename K, typename V, typename C>
+ struct parser<std::map<K, V, C> >
+ {
+ static void
+ parse (std::map<K, V, C>& m, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ std::size_t pos (s.position ());
+ std::string ov (s.next ());
+ std::string::size_type p = ov.find ('=');
+
+ K k = K ();
+ V v = V ();
+ std::string kstr (ov, 0, p);
+ std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ()));
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (o),
+ 0
+ };
+
+ bool dummy;
+ if (!kstr.empty ())
+ {
+ av[1] = const_cast<char*> (kstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<K>::parse (k, dummy, s);
+ }
+
+ if (!vstr.empty ())
+ {
+ av[1] = const_cast<char*> (vstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<V>::parse (v, dummy, s);
+ }
+
+ m[k] = v;
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename K, typename V, typename C>
+ struct parser<std::multimap<K, V, C> >
+ {
+ static void
+ parse (std::multimap<K, V, C>& m, bool& xs, scanner& s)
+ {
+ const char* o (s.next ());
+
+ if (s.more ())
+ {
+ std::size_t pos (s.position ());
+ std::string ov (s.next ());
+ std::string::size_type p = ov.find ('=');
+
+ K k = K ();
+ V v = V ();
+ std::string kstr (ov, 0, p);
+ std::string vstr (ov, (p != std::string::npos ? p + 1 : ov.size ()));
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (o),
+ 0
+ };
+
+ bool dummy;
+ if (!kstr.empty ())
+ {
+ av[1] = const_cast<char*> (kstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<K>::parse (k, dummy, s);
+ }
+
+ if (!vstr.empty ())
+ {
+ av[1] = const_cast<char*> (vstr.c_str ());
+ argv_scanner s (0, ac, av, false, pos);
+ parser<V>::parse (v, dummy, s);
+ }
+
+ m.insert (typename std::multimap<K, V, C>::value_type (k, v));
+ }
+ else
+ throw missing_value (o);
+
+ xs = true;
+ }
+ };
+
+ template <typename X, typename T, T X::*M>
+ void
+ thunk (X& x, scanner& s)
+ {
+ parser<T>::parse (x.*M, s);
+ }
+
+ template <typename X, bool X::*M>
+ void
+ thunk (X& x, scanner& s)
+ {
+ s.next ();
+ x.*M = true;
+ }
+
+ template <typename X, typename T, T X::*M, bool X::*S>
+ void
+ thunk (X& x, scanner& s)
+ {
+ parser<T>::parse (x.*M, x.*S, s);
+ }
+}
+
+#include <map>
+
+// options
+//
+
+options::
+options ()
+: build2_metadata_ (),
+ build2_metadata_specified_ (false),
+ help_ (),
+ version_ (),
+ I_ (),
+ I_specified_ (false),
+ D_ (),
+ D_specified_ (false),
+ U_ (),
+ U_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ multi_database_ (::multi_database::disabled),
+ multi_database_specified_ (false),
+ default_database_ (),
+ default_database_specified_ (false),
+ generate_query_ (),
+ generate_prepared_ (),
+ omit_unprepared_ (),
+ generate_session_ (),
+ generate_schema_ (),
+ generate_schema_only_ (),
+ suppress_migration_ (),
+ suppress_schema_version_ (),
+ schema_version_table_ (),
+ schema_version_table_specified_ (false),
+ schema_format_ (),
+ schema_format_specified_ (false),
+ omit_drop_ (),
+ omit_create_ (),
+ schema_name_ (),
+ schema_name_specified_ (false),
+ fkeys_deferrable_mode_ (),
+ fkeys_deferrable_mode_specified_ (false),
+ default_pointer_ ("*"),
+ default_pointer_specified_ (false),
+ session_type_ ("odb::session"),
+ session_type_specified_ (false),
+ profile_ (),
+ profile_specified_ (false),
+ at_once_ (),
+ schema_ (),
+ schema_specified_ (false),
+ export_symbol_ (),
+ export_symbol_specified_ (false),
+ extern_symbol_ (),
+ extern_symbol_specified_ (false),
+ std_ (cxx_version::cxx98),
+ std_specified_ (false),
+ warn_hard_add_ (),
+ warn_hard_delete_ (),
+ warn_hard_ (),
+ output_dir_ (),
+ output_dir_specified_ (false),
+ input_name_ (),
+ input_name_specified_ (false),
+ changelog_ (),
+ changelog_specified_ (false),
+ changelog_in_ (),
+ changelog_in_specified_ (false),
+ changelog_out_ (),
+ changelog_out_specified_ (false),
+ changelog_dir_ (),
+ changelog_dir_specified_ (false),
+ init_changelog_ (),
+ odb_file_suffix_ (),
+ odb_file_suffix_specified_ (false),
+ sql_file_suffix_ (),
+ sql_file_suffix_specified_ (false),
+ schema_file_suffix_ (),
+ schema_file_suffix_specified_ (false),
+ changelog_file_suffix_ (),
+ changelog_file_suffix_specified_ (false),
+ hxx_suffix_ (".hxx"),
+ hxx_suffix_specified_ (false),
+ ixx_suffix_ (".ixx"),
+ ixx_suffix_specified_ (false),
+ cxx_suffix_ (".cxx"),
+ cxx_suffix_specified_ (false),
+ sql_suffix_ (".sql"),
+ sql_suffix_specified_ (false),
+ changelog_suffix_ (".xml"),
+ changelog_suffix_specified_ (false),
+ hxx_prologue_ (),
+ hxx_prologue_specified_ (false),
+ ixx_prologue_ (),
+ ixx_prologue_specified_ (false),
+ cxx_prologue_ (),
+ cxx_prologue_specified_ (false),
+ schema_prologue_ (),
+ schema_prologue_specified_ (false),
+ sql_prologue_ (),
+ sql_prologue_specified_ (false),
+ migration_prologue_ (),
+ migration_prologue_specified_ (false),
+ sql_interlude_ (),
+ sql_interlude_specified_ (false),
+ hxx_epilogue_ (),
+ hxx_epilogue_specified_ (false),
+ ixx_epilogue_ (),
+ ixx_epilogue_specified_ (false),
+ cxx_epilogue_ (),
+ cxx_epilogue_specified_ (false),
+ schema_epilogue_ (),
+ schema_epilogue_specified_ (false),
+ sql_epilogue_ (),
+ sql_epilogue_specified_ (false),
+ migration_epilogue_ (),
+ migration_epilogue_specified_ (false),
+ hxx_prologue_file_ (),
+ hxx_prologue_file_specified_ (false),
+ ixx_prologue_file_ (),
+ ixx_prologue_file_specified_ (false),
+ cxx_prologue_file_ (),
+ cxx_prologue_file_specified_ (false),
+ schema_prologue_file_ (),
+ schema_prologue_file_specified_ (false),
+ sql_prologue_file_ (),
+ sql_prologue_file_specified_ (false),
+ migration_prologue_file_ (),
+ migration_prologue_file_specified_ (false),
+ sql_interlude_file_ (),
+ sql_interlude_file_specified_ (false),
+ hxx_epilogue_file_ (),
+ hxx_epilogue_file_specified_ (false),
+ ixx_epilogue_file_ (),
+ ixx_epilogue_file_specified_ (false),
+ cxx_epilogue_file_ (),
+ cxx_epilogue_file_specified_ (false),
+ schema_epilogue_file_ (),
+ schema_epilogue_file_specified_ (false),
+ sql_epilogue_file_ (),
+ sql_epilogue_file_specified_ (false),
+ migration_epilogue_file_ (),
+ migration_epilogue_file_specified_ (false),
+ odb_prologue_ (),
+ odb_prologue_specified_ (false),
+ odb_prologue_file_ (),
+ odb_prologue_file_specified_ (false),
+ odb_epilogue_ (),
+ odb_epilogue_specified_ (false),
+ odb_epilogue_file_ (),
+ odb_epilogue_file_specified_ (false),
+ table_prefix_ (),
+ table_prefix_specified_ (false),
+ index_suffix_ (),
+ index_suffix_specified_ (false),
+ fkey_suffix_ (),
+ fkey_suffix_specified_ (false),
+ sequence_suffix_ (),
+ sequence_suffix_specified_ (false),
+ sql_name_case_ (),
+ sql_name_case_specified_ (false),
+ table_regex_ (),
+ table_regex_specified_ (false),
+ column_regex_ (),
+ column_regex_specified_ (false),
+ index_regex_ (),
+ index_regex_specified_ (false),
+ fkey_regex_ (),
+ fkey_regex_specified_ (false),
+ sequence_regex_ (),
+ sequence_regex_specified_ (false),
+ statement_regex_ (),
+ statement_regex_specified_ (false),
+ sql_name_regex_ (),
+ sql_name_regex_specified_ (false),
+ sql_name_regex_trace_ (),
+ accessor_regex_ (),
+ accessor_regex_specified_ (false),
+ accessor_regex_trace_ (),
+ modifier_regex_ (),
+ modifier_regex_specified_ (false),
+ modifier_regex_trace_ (),
+ include_with_brackets_ (),
+ include_prefix_ (),
+ include_prefix_specified_ (false),
+ include_regex_ (),
+ include_regex_specified_ (false),
+ include_regex_trace_ (),
+ guard_prefix_ (),
+ guard_prefix_specified_ (false),
+ show_sloc_ (),
+ sloc_limit_ (),
+ sloc_limit_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false),
+ x_ (),
+ x_specified_ (false),
+ v_ (),
+ trace_ (),
+ mysql_engine_ ("InnoDB"),
+ mysql_engine_specified_ (false),
+ sqlite_override_null_ (),
+ sqlite_lax_auto_id_ (),
+ pgsql_server_version_ (7, 4),
+ pgsql_server_version_specified_ (false),
+ oracle_client_version_ (10, 1),
+ oracle_client_version_specified_ (false),
+ oracle_warn_truncation_ (),
+ mssql_server_version_ (10, 0),
+ mssql_server_version_specified_ (false),
+ mssql_short_limit_ (1024),
+ mssql_short_limit_specified_ (false)
+{
+}
+
+options::
+options (int& argc,
+ char** argv,
+ bool erase,
+ ::cli::unknown_mode opt,
+ ::cli::unknown_mode arg)
+: build2_metadata_ (),
+ build2_metadata_specified_ (false),
+ help_ (),
+ version_ (),
+ I_ (),
+ I_specified_ (false),
+ D_ (),
+ D_specified_ (false),
+ U_ (),
+ U_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ multi_database_ (::multi_database::disabled),
+ multi_database_specified_ (false),
+ default_database_ (),
+ default_database_specified_ (false),
+ generate_query_ (),
+ generate_prepared_ (),
+ omit_unprepared_ (),
+ generate_session_ (),
+ generate_schema_ (),
+ generate_schema_only_ (),
+ suppress_migration_ (),
+ suppress_schema_version_ (),
+ schema_version_table_ (),
+ schema_version_table_specified_ (false),
+ schema_format_ (),
+ schema_format_specified_ (false),
+ omit_drop_ (),
+ omit_create_ (),
+ schema_name_ (),
+ schema_name_specified_ (false),
+ fkeys_deferrable_mode_ (),
+ fkeys_deferrable_mode_specified_ (false),
+ default_pointer_ ("*"),
+ default_pointer_specified_ (false),
+ session_type_ ("odb::session"),
+ session_type_specified_ (false),
+ profile_ (),
+ profile_specified_ (false),
+ at_once_ (),
+ schema_ (),
+ schema_specified_ (false),
+ export_symbol_ (),
+ export_symbol_specified_ (false),
+ extern_symbol_ (),
+ extern_symbol_specified_ (false),
+ std_ (cxx_version::cxx98),
+ std_specified_ (false),
+ warn_hard_add_ (),
+ warn_hard_delete_ (),
+ warn_hard_ (),
+ output_dir_ (),
+ output_dir_specified_ (false),
+ input_name_ (),
+ input_name_specified_ (false),
+ changelog_ (),
+ changelog_specified_ (false),
+ changelog_in_ (),
+ changelog_in_specified_ (false),
+ changelog_out_ (),
+ changelog_out_specified_ (false),
+ changelog_dir_ (),
+ changelog_dir_specified_ (false),
+ init_changelog_ (),
+ odb_file_suffix_ (),
+ odb_file_suffix_specified_ (false),
+ sql_file_suffix_ (),
+ sql_file_suffix_specified_ (false),
+ schema_file_suffix_ (),
+ schema_file_suffix_specified_ (false),
+ changelog_file_suffix_ (),
+ changelog_file_suffix_specified_ (false),
+ hxx_suffix_ (".hxx"),
+ hxx_suffix_specified_ (false),
+ ixx_suffix_ (".ixx"),
+ ixx_suffix_specified_ (false),
+ cxx_suffix_ (".cxx"),
+ cxx_suffix_specified_ (false),
+ sql_suffix_ (".sql"),
+ sql_suffix_specified_ (false),
+ changelog_suffix_ (".xml"),
+ changelog_suffix_specified_ (false),
+ hxx_prologue_ (),
+ hxx_prologue_specified_ (false),
+ ixx_prologue_ (),
+ ixx_prologue_specified_ (false),
+ cxx_prologue_ (),
+ cxx_prologue_specified_ (false),
+ schema_prologue_ (),
+ schema_prologue_specified_ (false),
+ sql_prologue_ (),
+ sql_prologue_specified_ (false),
+ migration_prologue_ (),
+ migration_prologue_specified_ (false),
+ sql_interlude_ (),
+ sql_interlude_specified_ (false),
+ hxx_epilogue_ (),
+ hxx_epilogue_specified_ (false),
+ ixx_epilogue_ (),
+ ixx_epilogue_specified_ (false),
+ cxx_epilogue_ (),
+ cxx_epilogue_specified_ (false),
+ schema_epilogue_ (),
+ schema_epilogue_specified_ (false),
+ sql_epilogue_ (),
+ sql_epilogue_specified_ (false),
+ migration_epilogue_ (),
+ migration_epilogue_specified_ (false),
+ hxx_prologue_file_ (),
+ hxx_prologue_file_specified_ (false),
+ ixx_prologue_file_ (),
+ ixx_prologue_file_specified_ (false),
+ cxx_prologue_file_ (),
+ cxx_prologue_file_specified_ (false),
+ schema_prologue_file_ (),
+ schema_prologue_file_specified_ (false),
+ sql_prologue_file_ (),
+ sql_prologue_file_specified_ (false),
+ migration_prologue_file_ (),
+ migration_prologue_file_specified_ (false),
+ sql_interlude_file_ (),
+ sql_interlude_file_specified_ (false),
+ hxx_epilogue_file_ (),
+ hxx_epilogue_file_specified_ (false),
+ ixx_epilogue_file_ (),
+ ixx_epilogue_file_specified_ (false),
+ cxx_epilogue_file_ (),
+ cxx_epilogue_file_specified_ (false),
+ schema_epilogue_file_ (),
+ schema_epilogue_file_specified_ (false),
+ sql_epilogue_file_ (),
+ sql_epilogue_file_specified_ (false),
+ migration_epilogue_file_ (),
+ migration_epilogue_file_specified_ (false),
+ odb_prologue_ (),
+ odb_prologue_specified_ (false),
+ odb_prologue_file_ (),
+ odb_prologue_file_specified_ (false),
+ odb_epilogue_ (),
+ odb_epilogue_specified_ (false),
+ odb_epilogue_file_ (),
+ odb_epilogue_file_specified_ (false),
+ table_prefix_ (),
+ table_prefix_specified_ (false),
+ index_suffix_ (),
+ index_suffix_specified_ (false),
+ fkey_suffix_ (),
+ fkey_suffix_specified_ (false),
+ sequence_suffix_ (),
+ sequence_suffix_specified_ (false),
+ sql_name_case_ (),
+ sql_name_case_specified_ (false),
+ table_regex_ (),
+ table_regex_specified_ (false),
+ column_regex_ (),
+ column_regex_specified_ (false),
+ index_regex_ (),
+ index_regex_specified_ (false),
+ fkey_regex_ (),
+ fkey_regex_specified_ (false),
+ sequence_regex_ (),
+ sequence_regex_specified_ (false),
+ statement_regex_ (),
+ statement_regex_specified_ (false),
+ sql_name_regex_ (),
+ sql_name_regex_specified_ (false),
+ sql_name_regex_trace_ (),
+ accessor_regex_ (),
+ accessor_regex_specified_ (false),
+ accessor_regex_trace_ (),
+ modifier_regex_ (),
+ modifier_regex_specified_ (false),
+ modifier_regex_trace_ (),
+ include_with_brackets_ (),
+ include_prefix_ (),
+ include_prefix_specified_ (false),
+ include_regex_ (),
+ include_regex_specified_ (false),
+ include_regex_trace_ (),
+ guard_prefix_ (),
+ guard_prefix_specified_ (false),
+ show_sloc_ (),
+ sloc_limit_ (),
+ sloc_limit_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false),
+ x_ (),
+ x_specified_ (false),
+ v_ (),
+ trace_ (),
+ mysql_engine_ ("InnoDB"),
+ mysql_engine_specified_ (false),
+ sqlite_override_null_ (),
+ sqlite_lax_auto_id_ (),
+ pgsql_server_version_ (7, 4),
+ pgsql_server_version_specified_ (false),
+ oracle_client_version_ (10, 1),
+ oracle_client_version_specified_ (false),
+ oracle_warn_truncation_ (),
+ mssql_server_version_ (10, 0),
+ mssql_server_version_specified_ (false),
+ mssql_short_limit_ (1024),
+ mssql_short_limit_specified_ (false)
+{
+ ::cli::argv_scanner s (argc, argv, erase);
+ _parse (s, opt, arg);
+}
+
+options::
+options (int start,
+ int& argc,
+ char** argv,
+ bool erase,
+ ::cli::unknown_mode opt,
+ ::cli::unknown_mode arg)
+: build2_metadata_ (),
+ build2_metadata_specified_ (false),
+ help_ (),
+ version_ (),
+ I_ (),
+ I_specified_ (false),
+ D_ (),
+ D_specified_ (false),
+ U_ (),
+ U_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ multi_database_ (::multi_database::disabled),
+ multi_database_specified_ (false),
+ default_database_ (),
+ default_database_specified_ (false),
+ generate_query_ (),
+ generate_prepared_ (),
+ omit_unprepared_ (),
+ generate_session_ (),
+ generate_schema_ (),
+ generate_schema_only_ (),
+ suppress_migration_ (),
+ suppress_schema_version_ (),
+ schema_version_table_ (),
+ schema_version_table_specified_ (false),
+ schema_format_ (),
+ schema_format_specified_ (false),
+ omit_drop_ (),
+ omit_create_ (),
+ schema_name_ (),
+ schema_name_specified_ (false),
+ fkeys_deferrable_mode_ (),
+ fkeys_deferrable_mode_specified_ (false),
+ default_pointer_ ("*"),
+ default_pointer_specified_ (false),
+ session_type_ ("odb::session"),
+ session_type_specified_ (false),
+ profile_ (),
+ profile_specified_ (false),
+ at_once_ (),
+ schema_ (),
+ schema_specified_ (false),
+ export_symbol_ (),
+ export_symbol_specified_ (false),
+ extern_symbol_ (),
+ extern_symbol_specified_ (false),
+ std_ (cxx_version::cxx98),
+ std_specified_ (false),
+ warn_hard_add_ (),
+ warn_hard_delete_ (),
+ warn_hard_ (),
+ output_dir_ (),
+ output_dir_specified_ (false),
+ input_name_ (),
+ input_name_specified_ (false),
+ changelog_ (),
+ changelog_specified_ (false),
+ changelog_in_ (),
+ changelog_in_specified_ (false),
+ changelog_out_ (),
+ changelog_out_specified_ (false),
+ changelog_dir_ (),
+ changelog_dir_specified_ (false),
+ init_changelog_ (),
+ odb_file_suffix_ (),
+ odb_file_suffix_specified_ (false),
+ sql_file_suffix_ (),
+ sql_file_suffix_specified_ (false),
+ schema_file_suffix_ (),
+ schema_file_suffix_specified_ (false),
+ changelog_file_suffix_ (),
+ changelog_file_suffix_specified_ (false),
+ hxx_suffix_ (".hxx"),
+ hxx_suffix_specified_ (false),
+ ixx_suffix_ (".ixx"),
+ ixx_suffix_specified_ (false),
+ cxx_suffix_ (".cxx"),
+ cxx_suffix_specified_ (false),
+ sql_suffix_ (".sql"),
+ sql_suffix_specified_ (false),
+ changelog_suffix_ (".xml"),
+ changelog_suffix_specified_ (false),
+ hxx_prologue_ (),
+ hxx_prologue_specified_ (false),
+ ixx_prologue_ (),
+ ixx_prologue_specified_ (false),
+ cxx_prologue_ (),
+ cxx_prologue_specified_ (false),
+ schema_prologue_ (),
+ schema_prologue_specified_ (false),
+ sql_prologue_ (),
+ sql_prologue_specified_ (false),
+ migration_prologue_ (),
+ migration_prologue_specified_ (false),
+ sql_interlude_ (),
+ sql_interlude_specified_ (false),
+ hxx_epilogue_ (),
+ hxx_epilogue_specified_ (false),
+ ixx_epilogue_ (),
+ ixx_epilogue_specified_ (false),
+ cxx_epilogue_ (),
+ cxx_epilogue_specified_ (false),
+ schema_epilogue_ (),
+ schema_epilogue_specified_ (false),
+ sql_epilogue_ (),
+ sql_epilogue_specified_ (false),
+ migration_epilogue_ (),
+ migration_epilogue_specified_ (false),
+ hxx_prologue_file_ (),
+ hxx_prologue_file_specified_ (false),
+ ixx_prologue_file_ (),
+ ixx_prologue_file_specified_ (false),
+ cxx_prologue_file_ (),
+ cxx_prologue_file_specified_ (false),
+ schema_prologue_file_ (),
+ schema_prologue_file_specified_ (false),
+ sql_prologue_file_ (),
+ sql_prologue_file_specified_ (false),
+ migration_prologue_file_ (),
+ migration_prologue_file_specified_ (false),
+ sql_interlude_file_ (),
+ sql_interlude_file_specified_ (false),
+ hxx_epilogue_file_ (),
+ hxx_epilogue_file_specified_ (false),
+ ixx_epilogue_file_ (),
+ ixx_epilogue_file_specified_ (false),
+ cxx_epilogue_file_ (),
+ cxx_epilogue_file_specified_ (false),
+ schema_epilogue_file_ (),
+ schema_epilogue_file_specified_ (false),
+ sql_epilogue_file_ (),
+ sql_epilogue_file_specified_ (false),
+ migration_epilogue_file_ (),
+ migration_epilogue_file_specified_ (false),
+ odb_prologue_ (),
+ odb_prologue_specified_ (false),
+ odb_prologue_file_ (),
+ odb_prologue_file_specified_ (false),
+ odb_epilogue_ (),
+ odb_epilogue_specified_ (false),
+ odb_epilogue_file_ (),
+ odb_epilogue_file_specified_ (false),
+ table_prefix_ (),
+ table_prefix_specified_ (false),
+ index_suffix_ (),
+ index_suffix_specified_ (false),
+ fkey_suffix_ (),
+ fkey_suffix_specified_ (false),
+ sequence_suffix_ (),
+ sequence_suffix_specified_ (false),
+ sql_name_case_ (),
+ sql_name_case_specified_ (false),
+ table_regex_ (),
+ table_regex_specified_ (false),
+ column_regex_ (),
+ column_regex_specified_ (false),
+ index_regex_ (),
+ index_regex_specified_ (false),
+ fkey_regex_ (),
+ fkey_regex_specified_ (false),
+ sequence_regex_ (),
+ sequence_regex_specified_ (false),
+ statement_regex_ (),
+ statement_regex_specified_ (false),
+ sql_name_regex_ (),
+ sql_name_regex_specified_ (false),
+ sql_name_regex_trace_ (),
+ accessor_regex_ (),
+ accessor_regex_specified_ (false),
+ accessor_regex_trace_ (),
+ modifier_regex_ (),
+ modifier_regex_specified_ (false),
+ modifier_regex_trace_ (),
+ include_with_brackets_ (),
+ include_prefix_ (),
+ include_prefix_specified_ (false),
+ include_regex_ (),
+ include_regex_specified_ (false),
+ include_regex_trace_ (),
+ guard_prefix_ (),
+ guard_prefix_specified_ (false),
+ show_sloc_ (),
+ sloc_limit_ (),
+ sloc_limit_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false),
+ x_ (),
+ x_specified_ (false),
+ v_ (),
+ trace_ (),
+ mysql_engine_ ("InnoDB"),
+ mysql_engine_specified_ (false),
+ sqlite_override_null_ (),
+ sqlite_lax_auto_id_ (),
+ pgsql_server_version_ (7, 4),
+ pgsql_server_version_specified_ (false),
+ oracle_client_version_ (10, 1),
+ oracle_client_version_specified_ (false),
+ oracle_warn_truncation_ (),
+ mssql_server_version_ (10, 0),
+ mssql_server_version_specified_ (false),
+ mssql_short_limit_ (1024),
+ mssql_short_limit_specified_ (false)
+{
+ ::cli::argv_scanner s (start, argc, argv, erase);
+ _parse (s, opt, arg);
+}
+
+options::
+options (int& argc,
+ char** argv,
+ int& end,
+ bool erase,
+ ::cli::unknown_mode opt,
+ ::cli::unknown_mode arg)
+: build2_metadata_ (),
+ build2_metadata_specified_ (false),
+ help_ (),
+ version_ (),
+ I_ (),
+ I_specified_ (false),
+ D_ (),
+ D_specified_ (false),
+ U_ (),
+ U_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ multi_database_ (::multi_database::disabled),
+ multi_database_specified_ (false),
+ default_database_ (),
+ default_database_specified_ (false),
+ generate_query_ (),
+ generate_prepared_ (),
+ omit_unprepared_ (),
+ generate_session_ (),
+ generate_schema_ (),
+ generate_schema_only_ (),
+ suppress_migration_ (),
+ suppress_schema_version_ (),
+ schema_version_table_ (),
+ schema_version_table_specified_ (false),
+ schema_format_ (),
+ schema_format_specified_ (false),
+ omit_drop_ (),
+ omit_create_ (),
+ schema_name_ (),
+ schema_name_specified_ (false),
+ fkeys_deferrable_mode_ (),
+ fkeys_deferrable_mode_specified_ (false),
+ default_pointer_ ("*"),
+ default_pointer_specified_ (false),
+ session_type_ ("odb::session"),
+ session_type_specified_ (false),
+ profile_ (),
+ profile_specified_ (false),
+ at_once_ (),
+ schema_ (),
+ schema_specified_ (false),
+ export_symbol_ (),
+ export_symbol_specified_ (false),
+ extern_symbol_ (),
+ extern_symbol_specified_ (false),
+ std_ (cxx_version::cxx98),
+ std_specified_ (false),
+ warn_hard_add_ (),
+ warn_hard_delete_ (),
+ warn_hard_ (),
+ output_dir_ (),
+ output_dir_specified_ (false),
+ input_name_ (),
+ input_name_specified_ (false),
+ changelog_ (),
+ changelog_specified_ (false),
+ changelog_in_ (),
+ changelog_in_specified_ (false),
+ changelog_out_ (),
+ changelog_out_specified_ (false),
+ changelog_dir_ (),
+ changelog_dir_specified_ (false),
+ init_changelog_ (),
+ odb_file_suffix_ (),
+ odb_file_suffix_specified_ (false),
+ sql_file_suffix_ (),
+ sql_file_suffix_specified_ (false),
+ schema_file_suffix_ (),
+ schema_file_suffix_specified_ (false),
+ changelog_file_suffix_ (),
+ changelog_file_suffix_specified_ (false),
+ hxx_suffix_ (".hxx"),
+ hxx_suffix_specified_ (false),
+ ixx_suffix_ (".ixx"),
+ ixx_suffix_specified_ (false),
+ cxx_suffix_ (".cxx"),
+ cxx_suffix_specified_ (false),
+ sql_suffix_ (".sql"),
+ sql_suffix_specified_ (false),
+ changelog_suffix_ (".xml"),
+ changelog_suffix_specified_ (false),
+ hxx_prologue_ (),
+ hxx_prologue_specified_ (false),
+ ixx_prologue_ (),
+ ixx_prologue_specified_ (false),
+ cxx_prologue_ (),
+ cxx_prologue_specified_ (false),
+ schema_prologue_ (),
+ schema_prologue_specified_ (false),
+ sql_prologue_ (),
+ sql_prologue_specified_ (false),
+ migration_prologue_ (),
+ migration_prologue_specified_ (false),
+ sql_interlude_ (),
+ sql_interlude_specified_ (false),
+ hxx_epilogue_ (),
+ hxx_epilogue_specified_ (false),
+ ixx_epilogue_ (),
+ ixx_epilogue_specified_ (false),
+ cxx_epilogue_ (),
+ cxx_epilogue_specified_ (false),
+ schema_epilogue_ (),
+ schema_epilogue_specified_ (false),
+ sql_epilogue_ (),
+ sql_epilogue_specified_ (false),
+ migration_epilogue_ (),
+ migration_epilogue_specified_ (false),
+ hxx_prologue_file_ (),
+ hxx_prologue_file_specified_ (false),
+ ixx_prologue_file_ (),
+ ixx_prologue_file_specified_ (false),
+ cxx_prologue_file_ (),
+ cxx_prologue_file_specified_ (false),
+ schema_prologue_file_ (),
+ schema_prologue_file_specified_ (false),
+ sql_prologue_file_ (),
+ sql_prologue_file_specified_ (false),
+ migration_prologue_file_ (),
+ migration_prologue_file_specified_ (false),
+ sql_interlude_file_ (),
+ sql_interlude_file_specified_ (false),
+ hxx_epilogue_file_ (),
+ hxx_epilogue_file_specified_ (false),
+ ixx_epilogue_file_ (),
+ ixx_epilogue_file_specified_ (false),
+ cxx_epilogue_file_ (),
+ cxx_epilogue_file_specified_ (false),
+ schema_epilogue_file_ (),
+ schema_epilogue_file_specified_ (false),
+ sql_epilogue_file_ (),
+ sql_epilogue_file_specified_ (false),
+ migration_epilogue_file_ (),
+ migration_epilogue_file_specified_ (false),
+ odb_prologue_ (),
+ odb_prologue_specified_ (false),
+ odb_prologue_file_ (),
+ odb_prologue_file_specified_ (false),
+ odb_epilogue_ (),
+ odb_epilogue_specified_ (false),
+ odb_epilogue_file_ (),
+ odb_epilogue_file_specified_ (false),
+ table_prefix_ (),
+ table_prefix_specified_ (false),
+ index_suffix_ (),
+ index_suffix_specified_ (false),
+ fkey_suffix_ (),
+ fkey_suffix_specified_ (false),
+ sequence_suffix_ (),
+ sequence_suffix_specified_ (false),
+ sql_name_case_ (),
+ sql_name_case_specified_ (false),
+ table_regex_ (),
+ table_regex_specified_ (false),
+ column_regex_ (),
+ column_regex_specified_ (false),
+ index_regex_ (),
+ index_regex_specified_ (false),
+ fkey_regex_ (),
+ fkey_regex_specified_ (false),
+ sequence_regex_ (),
+ sequence_regex_specified_ (false),
+ statement_regex_ (),
+ statement_regex_specified_ (false),
+ sql_name_regex_ (),
+ sql_name_regex_specified_ (false),
+ sql_name_regex_trace_ (),
+ accessor_regex_ (),
+ accessor_regex_specified_ (false),
+ accessor_regex_trace_ (),
+ modifier_regex_ (),
+ modifier_regex_specified_ (false),
+ modifier_regex_trace_ (),
+ include_with_brackets_ (),
+ include_prefix_ (),
+ include_prefix_specified_ (false),
+ include_regex_ (),
+ include_regex_specified_ (false),
+ include_regex_trace_ (),
+ guard_prefix_ (),
+ guard_prefix_specified_ (false),
+ show_sloc_ (),
+ sloc_limit_ (),
+ sloc_limit_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false),
+ x_ (),
+ x_specified_ (false),
+ v_ (),
+ trace_ (),
+ mysql_engine_ ("InnoDB"),
+ mysql_engine_specified_ (false),
+ sqlite_override_null_ (),
+ sqlite_lax_auto_id_ (),
+ pgsql_server_version_ (7, 4),
+ pgsql_server_version_specified_ (false),
+ oracle_client_version_ (10, 1),
+ oracle_client_version_specified_ (false),
+ oracle_warn_truncation_ (),
+ mssql_server_version_ (10, 0),
+ mssql_server_version_specified_ (false),
+ mssql_short_limit_ (1024),
+ mssql_short_limit_specified_ (false)
+{
+ ::cli::argv_scanner s (argc, argv, erase);
+ _parse (s, opt, arg);
+ end = s.end ();
+}
+
+options::
+options (int start,
+ int& argc,
+ char** argv,
+ int& end,
+ bool erase,
+ ::cli::unknown_mode opt,
+ ::cli::unknown_mode arg)
+: build2_metadata_ (),
+ build2_metadata_specified_ (false),
+ help_ (),
+ version_ (),
+ I_ (),
+ I_specified_ (false),
+ D_ (),
+ D_specified_ (false),
+ U_ (),
+ U_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ multi_database_ (::multi_database::disabled),
+ multi_database_specified_ (false),
+ default_database_ (),
+ default_database_specified_ (false),
+ generate_query_ (),
+ generate_prepared_ (),
+ omit_unprepared_ (),
+ generate_session_ (),
+ generate_schema_ (),
+ generate_schema_only_ (),
+ suppress_migration_ (),
+ suppress_schema_version_ (),
+ schema_version_table_ (),
+ schema_version_table_specified_ (false),
+ schema_format_ (),
+ schema_format_specified_ (false),
+ omit_drop_ (),
+ omit_create_ (),
+ schema_name_ (),
+ schema_name_specified_ (false),
+ fkeys_deferrable_mode_ (),
+ fkeys_deferrable_mode_specified_ (false),
+ default_pointer_ ("*"),
+ default_pointer_specified_ (false),
+ session_type_ ("odb::session"),
+ session_type_specified_ (false),
+ profile_ (),
+ profile_specified_ (false),
+ at_once_ (),
+ schema_ (),
+ schema_specified_ (false),
+ export_symbol_ (),
+ export_symbol_specified_ (false),
+ extern_symbol_ (),
+ extern_symbol_specified_ (false),
+ std_ (cxx_version::cxx98),
+ std_specified_ (false),
+ warn_hard_add_ (),
+ warn_hard_delete_ (),
+ warn_hard_ (),
+ output_dir_ (),
+ output_dir_specified_ (false),
+ input_name_ (),
+ input_name_specified_ (false),
+ changelog_ (),
+ changelog_specified_ (false),
+ changelog_in_ (),
+ changelog_in_specified_ (false),
+ changelog_out_ (),
+ changelog_out_specified_ (false),
+ changelog_dir_ (),
+ changelog_dir_specified_ (false),
+ init_changelog_ (),
+ odb_file_suffix_ (),
+ odb_file_suffix_specified_ (false),
+ sql_file_suffix_ (),
+ sql_file_suffix_specified_ (false),
+ schema_file_suffix_ (),
+ schema_file_suffix_specified_ (false),
+ changelog_file_suffix_ (),
+ changelog_file_suffix_specified_ (false),
+ hxx_suffix_ (".hxx"),
+ hxx_suffix_specified_ (false),
+ ixx_suffix_ (".ixx"),
+ ixx_suffix_specified_ (false),
+ cxx_suffix_ (".cxx"),
+ cxx_suffix_specified_ (false),
+ sql_suffix_ (".sql"),
+ sql_suffix_specified_ (false),
+ changelog_suffix_ (".xml"),
+ changelog_suffix_specified_ (false),
+ hxx_prologue_ (),
+ hxx_prologue_specified_ (false),
+ ixx_prologue_ (),
+ ixx_prologue_specified_ (false),
+ cxx_prologue_ (),
+ cxx_prologue_specified_ (false),
+ schema_prologue_ (),
+ schema_prologue_specified_ (false),
+ sql_prologue_ (),
+ sql_prologue_specified_ (false),
+ migration_prologue_ (),
+ migration_prologue_specified_ (false),
+ sql_interlude_ (),
+ sql_interlude_specified_ (false),
+ hxx_epilogue_ (),
+ hxx_epilogue_specified_ (false),
+ ixx_epilogue_ (),
+ ixx_epilogue_specified_ (false),
+ cxx_epilogue_ (),
+ cxx_epilogue_specified_ (false),
+ schema_epilogue_ (),
+ schema_epilogue_specified_ (false),
+ sql_epilogue_ (),
+ sql_epilogue_specified_ (false),
+ migration_epilogue_ (),
+ migration_epilogue_specified_ (false),
+ hxx_prologue_file_ (),
+ hxx_prologue_file_specified_ (false),
+ ixx_prologue_file_ (),
+ ixx_prologue_file_specified_ (false),
+ cxx_prologue_file_ (),
+ cxx_prologue_file_specified_ (false),
+ schema_prologue_file_ (),
+ schema_prologue_file_specified_ (false),
+ sql_prologue_file_ (),
+ sql_prologue_file_specified_ (false),
+ migration_prologue_file_ (),
+ migration_prologue_file_specified_ (false),
+ sql_interlude_file_ (),
+ sql_interlude_file_specified_ (false),
+ hxx_epilogue_file_ (),
+ hxx_epilogue_file_specified_ (false),
+ ixx_epilogue_file_ (),
+ ixx_epilogue_file_specified_ (false),
+ cxx_epilogue_file_ (),
+ cxx_epilogue_file_specified_ (false),
+ schema_epilogue_file_ (),
+ schema_epilogue_file_specified_ (false),
+ sql_epilogue_file_ (),
+ sql_epilogue_file_specified_ (false),
+ migration_epilogue_file_ (),
+ migration_epilogue_file_specified_ (false),
+ odb_prologue_ (),
+ odb_prologue_specified_ (false),
+ odb_prologue_file_ (),
+ odb_prologue_file_specified_ (false),
+ odb_epilogue_ (),
+ odb_epilogue_specified_ (false),
+ odb_epilogue_file_ (),
+ odb_epilogue_file_specified_ (false),
+ table_prefix_ (),
+ table_prefix_specified_ (false),
+ index_suffix_ (),
+ index_suffix_specified_ (false),
+ fkey_suffix_ (),
+ fkey_suffix_specified_ (false),
+ sequence_suffix_ (),
+ sequence_suffix_specified_ (false),
+ sql_name_case_ (),
+ sql_name_case_specified_ (false),
+ table_regex_ (),
+ table_regex_specified_ (false),
+ column_regex_ (),
+ column_regex_specified_ (false),
+ index_regex_ (),
+ index_regex_specified_ (false),
+ fkey_regex_ (),
+ fkey_regex_specified_ (false),
+ sequence_regex_ (),
+ sequence_regex_specified_ (false),
+ statement_regex_ (),
+ statement_regex_specified_ (false),
+ sql_name_regex_ (),
+ sql_name_regex_specified_ (false),
+ sql_name_regex_trace_ (),
+ accessor_regex_ (),
+ accessor_regex_specified_ (false),
+ accessor_regex_trace_ (),
+ modifier_regex_ (),
+ modifier_regex_specified_ (false),
+ modifier_regex_trace_ (),
+ include_with_brackets_ (),
+ include_prefix_ (),
+ include_prefix_specified_ (false),
+ include_regex_ (),
+ include_regex_specified_ (false),
+ include_regex_trace_ (),
+ guard_prefix_ (),
+ guard_prefix_specified_ (false),
+ show_sloc_ (),
+ sloc_limit_ (),
+ sloc_limit_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false),
+ x_ (),
+ x_specified_ (false),
+ v_ (),
+ trace_ (),
+ mysql_engine_ ("InnoDB"),
+ mysql_engine_specified_ (false),
+ sqlite_override_null_ (),
+ sqlite_lax_auto_id_ (),
+ pgsql_server_version_ (7, 4),
+ pgsql_server_version_specified_ (false),
+ oracle_client_version_ (10, 1),
+ oracle_client_version_specified_ (false),
+ oracle_warn_truncation_ (),
+ mssql_server_version_ (10, 0),
+ mssql_server_version_specified_ (false),
+ mssql_short_limit_ (1024),
+ mssql_short_limit_specified_ (false)
+{
+ ::cli::argv_scanner s (start, argc, argv, erase);
+ _parse (s, opt, arg);
+ end = s.end ();
+}
+
+options::
+options (::cli::scanner& s,
+ ::cli::unknown_mode opt,
+ ::cli::unknown_mode arg)
+: build2_metadata_ (),
+ build2_metadata_specified_ (false),
+ help_ (),
+ version_ (),
+ I_ (),
+ I_specified_ (false),
+ D_ (),
+ D_specified_ (false),
+ U_ (),
+ U_specified_ (false),
+ database_ (),
+ database_specified_ (false),
+ multi_database_ (::multi_database::disabled),
+ multi_database_specified_ (false),
+ default_database_ (),
+ default_database_specified_ (false),
+ generate_query_ (),
+ generate_prepared_ (),
+ omit_unprepared_ (),
+ generate_session_ (),
+ generate_schema_ (),
+ generate_schema_only_ (),
+ suppress_migration_ (),
+ suppress_schema_version_ (),
+ schema_version_table_ (),
+ schema_version_table_specified_ (false),
+ schema_format_ (),
+ schema_format_specified_ (false),
+ omit_drop_ (),
+ omit_create_ (),
+ schema_name_ (),
+ schema_name_specified_ (false),
+ fkeys_deferrable_mode_ (),
+ fkeys_deferrable_mode_specified_ (false),
+ default_pointer_ ("*"),
+ default_pointer_specified_ (false),
+ session_type_ ("odb::session"),
+ session_type_specified_ (false),
+ profile_ (),
+ profile_specified_ (false),
+ at_once_ (),
+ schema_ (),
+ schema_specified_ (false),
+ export_symbol_ (),
+ export_symbol_specified_ (false),
+ extern_symbol_ (),
+ extern_symbol_specified_ (false),
+ std_ (cxx_version::cxx98),
+ std_specified_ (false),
+ warn_hard_add_ (),
+ warn_hard_delete_ (),
+ warn_hard_ (),
+ output_dir_ (),
+ output_dir_specified_ (false),
+ input_name_ (),
+ input_name_specified_ (false),
+ changelog_ (),
+ changelog_specified_ (false),
+ changelog_in_ (),
+ changelog_in_specified_ (false),
+ changelog_out_ (),
+ changelog_out_specified_ (false),
+ changelog_dir_ (),
+ changelog_dir_specified_ (false),
+ init_changelog_ (),
+ odb_file_suffix_ (),
+ odb_file_suffix_specified_ (false),
+ sql_file_suffix_ (),
+ sql_file_suffix_specified_ (false),
+ schema_file_suffix_ (),
+ schema_file_suffix_specified_ (false),
+ changelog_file_suffix_ (),
+ changelog_file_suffix_specified_ (false),
+ hxx_suffix_ (".hxx"),
+ hxx_suffix_specified_ (false),
+ ixx_suffix_ (".ixx"),
+ ixx_suffix_specified_ (false),
+ cxx_suffix_ (".cxx"),
+ cxx_suffix_specified_ (false),
+ sql_suffix_ (".sql"),
+ sql_suffix_specified_ (false),
+ changelog_suffix_ (".xml"),
+ changelog_suffix_specified_ (false),
+ hxx_prologue_ (),
+ hxx_prologue_specified_ (false),
+ ixx_prologue_ (),
+ ixx_prologue_specified_ (false),
+ cxx_prologue_ (),
+ cxx_prologue_specified_ (false),
+ schema_prologue_ (),
+ schema_prologue_specified_ (false),
+ sql_prologue_ (),
+ sql_prologue_specified_ (false),
+ migration_prologue_ (),
+ migration_prologue_specified_ (false),
+ sql_interlude_ (),
+ sql_interlude_specified_ (false),
+ hxx_epilogue_ (),
+ hxx_epilogue_specified_ (false),
+ ixx_epilogue_ (),
+ ixx_epilogue_specified_ (false),
+ cxx_epilogue_ (),
+ cxx_epilogue_specified_ (false),
+ schema_epilogue_ (),
+ schema_epilogue_specified_ (false),
+ sql_epilogue_ (),
+ sql_epilogue_specified_ (false),
+ migration_epilogue_ (),
+ migration_epilogue_specified_ (false),
+ hxx_prologue_file_ (),
+ hxx_prologue_file_specified_ (false),
+ ixx_prologue_file_ (),
+ ixx_prologue_file_specified_ (false),
+ cxx_prologue_file_ (),
+ cxx_prologue_file_specified_ (false),
+ schema_prologue_file_ (),
+ schema_prologue_file_specified_ (false),
+ sql_prologue_file_ (),
+ sql_prologue_file_specified_ (false),
+ migration_prologue_file_ (),
+ migration_prologue_file_specified_ (false),
+ sql_interlude_file_ (),
+ sql_interlude_file_specified_ (false),
+ hxx_epilogue_file_ (),
+ hxx_epilogue_file_specified_ (false),
+ ixx_epilogue_file_ (),
+ ixx_epilogue_file_specified_ (false),
+ cxx_epilogue_file_ (),
+ cxx_epilogue_file_specified_ (false),
+ schema_epilogue_file_ (),
+ schema_epilogue_file_specified_ (false),
+ sql_epilogue_file_ (),
+ sql_epilogue_file_specified_ (false),
+ migration_epilogue_file_ (),
+ migration_epilogue_file_specified_ (false),
+ odb_prologue_ (),
+ odb_prologue_specified_ (false),
+ odb_prologue_file_ (),
+ odb_prologue_file_specified_ (false),
+ odb_epilogue_ (),
+ odb_epilogue_specified_ (false),
+ odb_epilogue_file_ (),
+ odb_epilogue_file_specified_ (false),
+ table_prefix_ (),
+ table_prefix_specified_ (false),
+ index_suffix_ (),
+ index_suffix_specified_ (false),
+ fkey_suffix_ (),
+ fkey_suffix_specified_ (false),
+ sequence_suffix_ (),
+ sequence_suffix_specified_ (false),
+ sql_name_case_ (),
+ sql_name_case_specified_ (false),
+ table_regex_ (),
+ table_regex_specified_ (false),
+ column_regex_ (),
+ column_regex_specified_ (false),
+ index_regex_ (),
+ index_regex_specified_ (false),
+ fkey_regex_ (),
+ fkey_regex_specified_ (false),
+ sequence_regex_ (),
+ sequence_regex_specified_ (false),
+ statement_regex_ (),
+ statement_regex_specified_ (false),
+ sql_name_regex_ (),
+ sql_name_regex_specified_ (false),
+ sql_name_regex_trace_ (),
+ accessor_regex_ (),
+ accessor_regex_specified_ (false),
+ accessor_regex_trace_ (),
+ modifier_regex_ (),
+ modifier_regex_specified_ (false),
+ modifier_regex_trace_ (),
+ include_with_brackets_ (),
+ include_prefix_ (),
+ include_prefix_specified_ (false),
+ include_regex_ (),
+ include_regex_specified_ (false),
+ include_regex_trace_ (),
+ guard_prefix_ (),
+ guard_prefix_specified_ (false),
+ show_sloc_ (),
+ sloc_limit_ (),
+ sloc_limit_specified_ (false),
+ options_file_ (),
+ options_file_specified_ (false),
+ x_ (),
+ x_specified_ (false),
+ v_ (),
+ trace_ (),
+ mysql_engine_ ("InnoDB"),
+ mysql_engine_specified_ (false),
+ sqlite_override_null_ (),
+ sqlite_lax_auto_id_ (),
+ pgsql_server_version_ (7, 4),
+ pgsql_server_version_specified_ (false),
+ oracle_client_version_ (10, 1),
+ oracle_client_version_specified_ (false),
+ oracle_warn_truncation_ (),
+ mssql_server_version_ (10, 0),
+ mssql_server_version_specified_ (false),
+ mssql_short_limit_ (1024),
+ mssql_short_limit_specified_ (false)
+{
+ _parse (s, opt, arg);
+}
+
+::cli::usage_para options::
+print_usage (::std::ostream& os, ::cli::usage_para p)
+{
+ CLI_POTENTIALLY_UNUSED (os);
+
+ if (p == ::cli::usage_para::text)
+ os << ::std::endl;
+
+ os << "--help Print usage information and exit." << ::std::endl;
+
+ os << "--version Print version and exit." << ::std::endl;
+
+ os << "-I <dir> Add <dir> to the beginning of the list of" << ::std::endl
+ << " directories to be searched for included header" << ::std::endl
+ << " files." << ::std::endl;
+
+ os << "-D <name>[=<def>] Define macro <name> with definition <def>." << ::std::endl;
+
+ os << "-U <name> Cancel any previous definitions of macro <name>," << ::std::endl
+ << " either built-in or provided with the -D option." << ::std::endl;
+
+ os << "--database|-d <db> Generate code for the <db> database." << ::std::endl;
+
+ os << "--multi-database|-m <type> Enable multi-database support and specify its" << ::std::endl
+ << " type." << ::std::endl;
+
+ os << "--default-database <db> When static multi-database support is used," << ::std::endl
+ << " specify the database that should be made the" << ::std::endl
+ << " default." << ::std::endl;
+
+ os << "--generate-query|-q Generate query support code." << ::std::endl;
+
+ os << "--generate-prepared Generate prepared query execution support code." << ::std::endl;
+
+ os << "--omit-unprepared Omit un-prepared (once-off) query execution" << ::std::endl
+ << " support code." << ::std::endl;
+
+ os << "--generate-session|-e Generate session support code." << ::std::endl;
+
+ os << "--generate-schema|-s Generate the database schema." << ::std::endl;
+
+ os << "--generate-schema-only Generate only the database schema." << ::std::endl;
+
+ os << "--suppress-migration Suppress the generation of database schema" << ::std::endl
+ << " migration statements." << ::std::endl;
+
+ os << "--suppress-schema-version Suppress the generation of schema version table." << ::std::endl;
+
+ os << "--schema-version-table <name> Specify the alternative schema version table name" << ::std::endl
+ << " instead of the default schema_version." << ::std::endl;
+
+ os << "--schema-format <format> Generate the database schema in the specified" << ::std::endl
+ << " format." << ::std::endl;
+
+ os << "--omit-drop Omit DROP statements from the generated database" << ::std::endl
+ << " schema." << ::std::endl;
+
+ os << "--omit-create Omit CREATE statements from the generated" << ::std::endl
+ << " database schema." << ::std::endl;
+
+ os << "--schema-name <name> Use <name> as the database schema name." << ::std::endl;
+
+ os << "--fkeys-deferrable-mode <m> Use constraint checking mode <m> in foreign keys" << ::std::endl
+ << " generated for object relationships." << ::std::endl;
+
+ os << "--default-pointer <ptr> Use <ptr> as the default pointer for persistent" << ::std::endl
+ << " objects and views." << ::std::endl;
+
+ os << "--session-type <type> Use <type> as the alternative session type" << ::std::endl
+ << " instead of the default odb::session." << ::std::endl;
+
+ os << "--profile|-p <name> Specify a profile that should be used during" << ::std::endl
+ << " compilation." << ::std::endl;
+
+ os << "--at-once Generate code for all the input files as well as" << ::std::endl
+ << " for all the files that they include at once." << ::std::endl;
+
+ os << "--schema <schema> Specify a database schema (database namespace)" << ::std::endl
+ << " that should be assigned to the persistent classes" << ::std::endl
+ << " in the file being compiled." << ::std::endl;
+
+ os << "--export-symbol <symbol> Insert <symbol> in places where DLL export/import" << ::std::endl
+ << " control statements" << ::std::endl
+ << " (__declspec(dllexport/dllimport)) are necessary." << ::std::endl;
+
+ os << "--extern-symbol <symbol> If <symbol> is defined, insert it in places where" << ::std::endl
+ << " a template instantiation must be declared extern." << ::std::endl;
+
+ os << "--std <version> Specify the C++ standard that should be used" << ::std::endl
+ << " during compilation." << ::std::endl;
+
+ os << "--warn-hard-add Warn about hard-added data members." << ::std::endl;
+
+ os << "--warn-hard-delete Warn about hard-deleted data members and" << ::std::endl
+ << " persistent classes." << ::std::endl;
+
+ os << "--warn-hard Warn about both hard-added and hard-deleted data" << ::std::endl
+ << " members and persistent classes." << ::std::endl;
+
+ os << "--output-dir|-o <dir> Write the generated files to <dir> instead of the" << ::std::endl
+ << " current directory." << ::std::endl;
+
+ os << "--input-name <name> Use <name> instead of the input file to derive" << ::std::endl
+ << " the names of the generated files." << ::std::endl;
+
+ os << "--changelog <file> Read/write changelog from/to <file> instead of" << ::std::endl
+ << " the default changelog file." << ::std::endl;
+
+ os << "--changelog-in <file> Read changelog from <file> instead of the default" << ::std::endl
+ << " changelog file." << ::std::endl;
+
+ os << "--changelog-out <file> Write changelog to <file> instead of the default" << ::std::endl
+ << " changelog file." << ::std::endl;
+
+ os << "--changelog-dir <dir> Use <dir> instead of the input file directory as" << ::std::endl
+ << " the changelog file directory." << ::std::endl;
+
+ os << "--init-changelog Force re-initialization of the changelog even if" << ::std::endl
+ << " one exists (all the existing change history will" << ::std::endl
+ << " be lost)." << ::std::endl;
+
+ os << "--odb-file-suffix <suffix> Use <suffix> to construct the names of the" << ::std::endl
+ << " generated C++ files." << ::std::endl;
+
+ os << "--sql-file-suffix <suffix> Use <suffix> to construct the name of the" << ::std::endl
+ << " generated schema SQL file." << ::std::endl;
+
+ os << "--schema-file-suffix <suffix> Use <suffix> to construct the name of the" << ::std::endl
+ << " generated schema C++ source file." << ::std::endl;
+
+ os << "--changelog-file-suffix <sfx> Use <sfx> to construct the name of the changelog" << ::std::endl
+ << " file." << ::std::endl;
+
+ os << "--hxx-suffix <suffix> Use <suffix> instead of the default .hxx to" << ::std::endl
+ << " construct the name of the generated C++ header" << ::std::endl
+ << " file." << ::std::endl;
+
+ os << "--ixx-suffix <suffix> Use <suffix> instead of the default .ixx to" << ::std::endl
+ << " construct the name of the generated C++ inline" << ::std::endl
+ << " file." << ::std::endl;
+
+ os << "--cxx-suffix <suffix> Use <suffix> instead of the default .cxx to" << ::std::endl
+ << " construct the name of the generated C++ source" << ::std::endl
+ << " file." << ::std::endl;
+
+ os << "--sql-suffix <suffix> Use <suffix> instead of the default .sql to" << ::std::endl
+ << " construct the name of the generated database" << ::std::endl
+ << " schema file." << ::std::endl;
+
+ os << "--changelog-suffix <suffix> Use <suffix> instead of the default .xml to" << ::std::endl
+ << " construct the name of the changelog file." << ::std::endl;
+
+ os << "--hxx-prologue <text> Insert <text> at the beginning of the generated" << ::std::endl
+ << " C++ header file." << ::std::endl;
+
+ os << "--ixx-prologue <text> Insert <text> at the beginning of the generated" << ::std::endl
+ << " C++ inline file." << ::std::endl;
+
+ os << "--cxx-prologue <text> Insert <text> at the beginning of the generated" << ::std::endl
+ << " C++ source file." << ::std::endl;
+
+ os << "--schema-prologue <text> Insert <text> at the beginning of the generated" << ::std::endl
+ << " schema C++ source file." << ::std::endl;
+
+ os << "--sql-prologue <text> Insert <text> at the beginning of the generated" << ::std::endl
+ << " database schema file." << ::std::endl;
+
+ os << "--migration-prologue <text> Insert <text> at the beginning of the generated" << ::std::endl
+ << " database migration file." << ::std::endl;
+
+ os << "--sql-interlude <text> Insert <text> after all the DROP and before any" << ::std::endl
+ << " CREATE statements in the generated database" << ::std::endl
+ << " schema file." << ::std::endl;
+
+ os << "--hxx-epilogue <text> Insert <text> at the end of the generated C++" << ::std::endl
+ << " header file." << ::std::endl;
+
+ os << "--ixx-epilogue <text> Insert <text> at the end of the generated C++" << ::std::endl
+ << " inline file." << ::std::endl;
+
+ os << "--cxx-epilogue <text> Insert <text> at the end of the generated C++" << ::std::endl
+ << " source file." << ::std::endl;
+
+ os << "--schema-epilogue <text> Insert <text> at the end of the generated schema" << ::std::endl
+ << " C++ source file." << ::std::endl;
+
+ os << "--sql-epilogue <text> Insert <text> at the end of the generated" << ::std::endl
+ << " database schema file." << ::std::endl;
+
+ os << "--migration-epilogue <text> Insert <text> at the end of the generated" << ::std::endl
+ << " database migration file." << ::std::endl;
+
+ os << "--hxx-prologue-file <file> Insert the content of <file> at the beginning of" << ::std::endl
+ << " the generated C++ header file." << ::std::endl;
+
+ os << "--ixx-prologue-file <file> Insert the content of <file> at the beginning of" << ::std::endl
+ << " the generated C++ inline file." << ::std::endl;
+
+ os << "--cxx-prologue-file <file> Insert the content of <file> at the beginning of" << ::std::endl
+ << " the generated C++ source file." << ::std::endl;
+
+ os << "--schema-prologue-file <file> Insert the content of <file> at the beginning of" << ::std::endl
+ << " the generated schema C++ source file." << ::std::endl;
+
+ os << "--sql-prologue-file <file> Insert the content of <file> at the beginning of" << ::std::endl
+ << " the generated database schema file." << ::std::endl;
+
+ os << "--migration-prologue-file <f> Insert the content of file <f> at the beginning" << ::std::endl
+ << " of the generated database migration file." << ::std::endl;
+
+ os << "--sql-interlude-file <file> Insert the content of <file> after all the DROP" << ::std::endl
+ << " and before any CREATE statements in the generated" << ::std::endl
+ << " database schema file." << ::std::endl;
+
+ os << "--hxx-epilogue-file <file> Insert the content of <file> at the end of the" << ::std::endl
+ << " generated C++ header file." << ::std::endl;
+
+ os << "--ixx-epilogue-file <file> Insert the content of <file> at the end of the" << ::std::endl
+ << " generated C++ inline file." << ::std::endl;
+
+ os << "--cxx-epilogue-file <file> Insert the content of <file> at the end of the" << ::std::endl
+ << " generated C++ source file." << ::std::endl;
+
+ os << "--schema-epilogue-file <file> Insert the content of <file> at the end of the" << ::std::endl
+ << " generated schema C++ source file." << ::std::endl;
+
+ os << "--sql-epilogue-file <file> Insert the content of <file> at the end of the" << ::std::endl
+ << " generated database schema file." << ::std::endl;
+
+ os << "--migration-epilogue-file <f> Insert the content of file <f> at the end of the" << ::std::endl
+ << " generated database migration file." << ::std::endl;
+
+ os << "--odb-prologue <text> Compile <text> before the input header file." << ::std::endl;
+
+ os << "--odb-prologue-file <file> Compile <file> contents before the input header" << ::std::endl
+ << " file." << ::std::endl;
+
+ os << "--odb-epilogue <text> Compile <text> after the input header file." << ::std::endl;
+
+ os << "--odb-epilogue-file <file> Compile <file> contents after the input header" << ::std::endl
+ << " file." << ::std::endl;
+
+ os << "--table-prefix <prefix> Add <prefix> to table names and, for databases" << ::std::endl
+ << " that have global index and/or foreign key names," << ::std::endl
+ << " to those names as well." << ::std::endl;
+
+ os << "--index-suffix <suffix> Use <suffix> instead of the default _i to" << ::std::endl
+ << " construct index names." << ::std::endl;
+
+ os << "--fkey-suffix <suffix> Use <suffix> instead of the default _fk to" << ::std::endl
+ << " construct foreign key names." << ::std::endl;
+
+ os << "--sequence-suffix <suffix> Use <suffix> instead of the default _seq to" << ::std::endl
+ << " construct sequence names." << ::std::endl;
+
+ os << "--sql-name-case <case> Convert all automatically-derived SQL names to" << ::std::endl
+ << " upper or lower case." << ::std::endl;
+
+ os << "--table-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " that is used to transform automatically-derived" << ::std::endl
+ << " table names." << ::std::endl;
+
+ os << "--column-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " that is used to transform automatically-derived" << ::std::endl
+ << " column names." << ::std::endl;
+
+ os << "--index-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " that is used to transform automatically-derived" << ::std::endl
+ << " index names." << ::std::endl;
+
+ os << "--fkey-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " that is used to transform automatically-derived" << ::std::endl
+ << " foreign key names." << ::std::endl;
+
+ os << "--sequence-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " that is used to transform automatically-derived" << ::std::endl
+ << " sequence names." << ::std::endl;
+
+ os << "--statement-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " that is used to transform automatically-derived" << ::std::endl
+ << " prepared statement names." << ::std::endl;
+
+ os << "--sql-name-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " that is used to transform all" << ::std::endl
+ << " automatically-derived SQL names." << ::std::endl;
+
+ os << "--sql-name-regex-trace Trace the process of applying regular expressions" << ::std::endl
+ << " specified with the SQL name --*-regex options." << ::std::endl;
+
+ os << "--accessor-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " used to transform data member names to function" << ::std::endl
+ << " names when searching for a suitable accessor" << ::std::endl
+ << " function." << ::std::endl;
+
+ os << "--accessor-regex-trace Trace the process of applying regular expressions" << ::std::endl
+ << " specified with the --accessor-regex option." << ::std::endl;
+
+ os << "--modifier-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " used to transform data member names to function" << ::std::endl
+ << " names when searching for a suitable modifier" << ::std::endl
+ << " function." << ::std::endl;
+
+ os << "--modifier-regex-trace Trace the process of applying regular expressions" << ::std::endl
+ << " specified with the --modifier-regex option." << ::std::endl;
+
+ os << "--include-with-brackets Use angle brackets (<>) instead of quotes (\"\") in" << ::std::endl
+ << " the generated #include directives." << ::std::endl;
+
+ os << "--include-prefix <prefix> Add <prefix> to the generated #include directive" << ::std::endl
+ << " paths." << ::std::endl;
+
+ os << "--include-regex <regex> Add <regex> to the list of regular expressions" << ::std::endl
+ << " used to transform generated #include directive" << ::std::endl
+ << " paths." << ::std::endl;
+
+ os << "--include-regex-trace Trace the process of applying regular expressions" << ::std::endl
+ << " specified with the --include-regex option." << ::std::endl;
+
+ os << "--guard-prefix <prefix> Add <prefix> to the generated header inclusion" << ::std::endl
+ << " guards." << ::std::endl;
+
+ os << "--show-sloc Print the number of generated physical source" << ::std::endl
+ << " lines of code (SLOC)." << ::std::endl;
+
+ os << "--sloc-limit <num> Check that the number of generated physical" << ::std::endl
+ << " source lines of code (SLOC) does not exceed" << ::std::endl
+ << " <num>." << ::std::endl;
+
+ os << "--options-file <file> Read additional options from <file>." << ::std::endl;
+
+ os << "-x <option> Pass <option> to the underlying C++ compiler" << ::std::endl
+ << " (g++)." << ::std::endl;
+
+ os << "-v Print the commands executed to run the stages of" << ::std::endl
+ << " compilation." << ::std::endl;
+
+ os << "--trace Trace the compilation process." << ::std::endl;
+
+ os << "--mysql-engine <engine> Use <engine> instead of the default InnoDB in the" << ::std::endl
+ << " generated database schema file." << ::std::endl;
+
+ os << "--sqlite-override-null Make all columns in the generated database schema" << ::std::endl
+ << " allow NULL values." << ::std::endl;
+
+ os << "--sqlite-lax-auto-id Do not force monotonically increasing" << ::std::endl
+ << " automatically-assigned object ids." << ::std::endl;
+
+ os << "--pgsql-server-version <ver> Specify the minimum PostgreSQL server version" << ::std::endl
+ << " with which the generated C++ code and schema will" << ::std::endl
+ << " be used." << ::std::endl;
+
+ os << "--oracle-client-version <ver> Specify the minimum Oracle client library (OCI)" << ::std::endl
+ << " version with which the generated C++ code will be" << ::std::endl
+ << " linked." << ::std::endl;
+
+ os << "--oracle-warn-truncation Warn about SQL names that are longer than 30" << ::std::endl
+ << " characters and are therefore truncated." << ::std::endl;
+
+ os << "--mssql-server-version <ver> Specify the minimum SQL Server server version" << ::std::endl
+ << " with which the generated C++ code and schema will" << ::std::endl
+ << " be used." << ::std::endl;
+
+ os << "--mssql-short-limit <size> Specify the short data size limit." << ::std::endl;
+
+ p = ::cli::usage_para::option;
+
+ return p;
+}
+
+struct _cli_options_desc_type: ::cli::options
+{
+ _cli_options_desc_type ()
+ {
+ ::options::fill (*this);
+ }
+};
+
+static _cli_options_desc_type _cli_options_desc_;
+
+void options::
+fill (::cli::options& os)
+{
+ // --build2-metadata
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--build2-metadata", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --help
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--help", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --version
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--version", a, true, dv);
+ os.push_back (o);
+ }
+
+ // -I
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("-I", a, false, dv);
+ os.push_back (o);
+ }
+
+ // -D
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("-D", a, false, dv);
+ os.push_back (o);
+ }
+
+ // -U
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("-U", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --database
+ //
+ {
+ ::cli::option_names a;
+ a.push_back ("-d");
+ std::string dv;
+ ::cli::option o ("--database", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --multi-database
+ //
+ {
+ ::cli::option_names a;
+ a.push_back ("-m");
+ std::string dv;
+ ::cli::option o ("--multi-database", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --default-database
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--default-database", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --generate-query
+ //
+ {
+ ::cli::option_names a;
+ a.push_back ("-q");
+ std::string dv;
+ ::cli::option o ("--generate-query", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --generate-prepared
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--generate-prepared", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --omit-unprepared
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--omit-unprepared", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --generate-session
+ //
+ {
+ ::cli::option_names a;
+ a.push_back ("-e");
+ std::string dv;
+ ::cli::option o ("--generate-session", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --generate-schema
+ //
+ {
+ ::cli::option_names a;
+ a.push_back ("-s");
+ std::string dv;
+ ::cli::option o ("--generate-schema", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --generate-schema-only
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--generate-schema-only", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --suppress-migration
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--suppress-migration", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --suppress-schema-version
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--suppress-schema-version", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --schema-version-table
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--schema-version-table", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --schema-format
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--schema-format", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --omit-drop
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--omit-drop", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --omit-create
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--omit-create", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --schema-name
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--schema-name", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --fkeys-deferrable-mode
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--fkeys-deferrable-mode", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --default-pointer
+ //
+ {
+ ::cli::option_names a;
+ std::string dv ("*");
+ ::cli::option o ("--default-pointer", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --session-type
+ //
+ {
+ ::cli::option_names a;
+ std::string dv ("odb::session");
+ ::cli::option o ("--session-type", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --profile
+ //
+ {
+ ::cli::option_names a;
+ a.push_back ("-p");
+ std::string dv;
+ ::cli::option o ("--profile", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --at-once
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--at-once", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --schema
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--schema", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --export-symbol
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--export-symbol", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --extern-symbol
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--extern-symbol", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --std
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--std", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --warn-hard-add
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--warn-hard-add", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --warn-hard-delete
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--warn-hard-delete", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --warn-hard
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--warn-hard", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --output-dir
+ //
+ {
+ ::cli::option_names a;
+ a.push_back ("-o");
+ std::string dv;
+ ::cli::option o ("--output-dir", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --input-name
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--input-name", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --changelog
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--changelog", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --changelog-in
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--changelog-in", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --changelog-out
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--changelog-out", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --changelog-dir
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--changelog-dir", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --init-changelog
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--init-changelog", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --odb-file-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--odb-file-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-file-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-file-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --schema-file-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--schema-file-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --changelog-file-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--changelog-file-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --hxx-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv (".hxx");
+ ::cli::option o ("--hxx-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --ixx-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv (".ixx");
+ ::cli::option o ("--ixx-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --cxx-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv (".cxx");
+ ::cli::option o ("--cxx-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv (".sql");
+ ::cli::option o ("--sql-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --changelog-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv (".xml");
+ ::cli::option o ("--changelog-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --hxx-prologue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--hxx-prologue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --ixx-prologue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--ixx-prologue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --cxx-prologue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--cxx-prologue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --schema-prologue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--schema-prologue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-prologue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-prologue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --migration-prologue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--migration-prologue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-interlude
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-interlude", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --hxx-epilogue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--hxx-epilogue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --ixx-epilogue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--ixx-epilogue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --cxx-epilogue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--cxx-epilogue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --schema-epilogue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--schema-epilogue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-epilogue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-epilogue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --migration-epilogue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--migration-epilogue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --hxx-prologue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--hxx-prologue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --ixx-prologue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--ixx-prologue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --cxx-prologue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--cxx-prologue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --schema-prologue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--schema-prologue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-prologue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-prologue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --migration-prologue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--migration-prologue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-interlude-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-interlude-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --hxx-epilogue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--hxx-epilogue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --ixx-epilogue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--ixx-epilogue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --cxx-epilogue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--cxx-epilogue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --schema-epilogue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--schema-epilogue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-epilogue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-epilogue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --migration-epilogue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--migration-epilogue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --odb-prologue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--odb-prologue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --odb-prologue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--odb-prologue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --odb-epilogue
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--odb-epilogue", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --odb-epilogue-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--odb-epilogue-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --table-prefix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--table-prefix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --index-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--index-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --fkey-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--fkey-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sequence-suffix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sequence-suffix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-name-case
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-name-case", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --table-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--table-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --column-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--column-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --index-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--index-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --fkey-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--fkey-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sequence-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sequence-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --statement-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--statement-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-name-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-name-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sql-name-regex-trace
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sql-name-regex-trace", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --accessor-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--accessor-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --accessor-regex-trace
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--accessor-regex-trace", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --modifier-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--modifier-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --modifier-regex-trace
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--modifier-regex-trace", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --include-with-brackets
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--include-with-brackets", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --include-prefix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--include-prefix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --include-regex
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--include-regex", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --include-regex-trace
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--include-regex-trace", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --guard-prefix
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--guard-prefix", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --show-sloc
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--show-sloc", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --sloc-limit
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sloc-limit", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --options-file
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--options-file", a, false, dv);
+ os.push_back (o);
+ }
+
+ // -x
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("-x", a, false, dv);
+ os.push_back (o);
+ }
+
+ // -v
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("-v", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --trace
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--trace", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --mysql-engine
+ //
+ {
+ ::cli::option_names a;
+ std::string dv ("InnoDB");
+ ::cli::option o ("--mysql-engine", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --sqlite-override-null
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sqlite-override-null", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --sqlite-lax-auto-id
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--sqlite-lax-auto-id", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --pgsql-server-version
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--pgsql-server-version", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --oracle-client-version
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--oracle-client-version", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --oracle-warn-truncation
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--oracle-warn-truncation", a, true, dv);
+ os.push_back (o);
+ }
+
+ // --mssql-server-version
+ //
+ {
+ ::cli::option_names a;
+ std::string dv;
+ ::cli::option o ("--mssql-server-version", a, false, dv);
+ os.push_back (o);
+ }
+
+ // --mssql-short-limit
+ //
+ {
+ ::cli::option_names a;
+ std::string dv ("1024");
+ ::cli::option o ("--mssql-short-limit", a, false, dv);
+ os.push_back (o);
+ }
+}
+
+const ::cli::options& options::
+description ()
+{
+ return _cli_options_desc_;
+}
+
+typedef
+std::map<std::string, void (*) (options&, ::cli::scanner&)>
+_cli_options_map;
+
+static _cli_options_map _cli_options_map_;
+
+struct _cli_options_map_init
+{
+ _cli_options_map_init ()
+ {
+ _cli_options_map_["--build2-metadata"] =
+ &::cli::thunk< options, std::uint64_t, &options::build2_metadata_,
+ &options::build2_metadata_specified_ >;
+ _cli_options_map_["--help"] =
+ &::cli::thunk< options, &options::help_ >;
+ _cli_options_map_["--version"] =
+ &::cli::thunk< options, &options::version_ >;
+ _cli_options_map_["-I"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::I_,
+ &options::I_specified_ >;
+ _cli_options_map_["-D"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::D_,
+ &options::D_specified_ >;
+ _cli_options_map_["-U"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::U_,
+ &options::U_specified_ >;
+ _cli_options_map_["--database"] =
+ &::cli::thunk< options, std::vector< ::database>, &options::database_,
+ &options::database_specified_ >;
+ _cli_options_map_["-d"] =
+ &::cli::thunk< options, std::vector< ::database>, &options::database_,
+ &options::database_specified_ >;
+ _cli_options_map_["--multi-database"] =
+ &::cli::thunk< options, ::multi_database, &options::multi_database_,
+ &options::multi_database_specified_ >;
+ _cli_options_map_["-m"] =
+ &::cli::thunk< options, ::multi_database, &options::multi_database_,
+ &options::multi_database_specified_ >;
+ _cli_options_map_["--default-database"] =
+ &::cli::thunk< options, ::database, &options::default_database_,
+ &options::default_database_specified_ >;
+ _cli_options_map_["--generate-query"] =
+ &::cli::thunk< options, &options::generate_query_ >;
+ _cli_options_map_["-q"] =
+ &::cli::thunk< options, &options::generate_query_ >;
+ _cli_options_map_["--generate-prepared"] =
+ &::cli::thunk< options, &options::generate_prepared_ >;
+ _cli_options_map_["--omit-unprepared"] =
+ &::cli::thunk< options, &options::omit_unprepared_ >;
+ _cli_options_map_["--generate-session"] =
+ &::cli::thunk< options, &options::generate_session_ >;
+ _cli_options_map_["-e"] =
+ &::cli::thunk< options, &options::generate_session_ >;
+ _cli_options_map_["--generate-schema"] =
+ &::cli::thunk< options, &options::generate_schema_ >;
+ _cli_options_map_["-s"] =
+ &::cli::thunk< options, &options::generate_schema_ >;
+ _cli_options_map_["--generate-schema-only"] =
+ &::cli::thunk< options, &options::generate_schema_only_ >;
+ _cli_options_map_["--suppress-migration"] =
+ &::cli::thunk< options, &options::suppress_migration_ >;
+ _cli_options_map_["--suppress-schema-version"] =
+ &::cli::thunk< options, &options::suppress_schema_version_ >;
+ _cli_options_map_["--schema-version-table"] =
+ &::cli::thunk< options, database_map<qname>, &options::schema_version_table_,
+ &options::schema_version_table_specified_ >;
+ _cli_options_map_["--schema-format"] =
+ &::cli::thunk< options, database_map<std::set< ::schema_format> >, &options::schema_format_,
+ &options::schema_format_specified_ >;
+ _cli_options_map_["--omit-drop"] =
+ &::cli::thunk< options, &options::omit_drop_ >;
+ _cli_options_map_["--omit-create"] =
+ &::cli::thunk< options, &options::omit_create_ >;
+ _cli_options_map_["--schema-name"] =
+ &::cli::thunk< options, database_map<std::string>, &options::schema_name_,
+ &options::schema_name_specified_ >;
+ _cli_options_map_["--fkeys-deferrable-mode"] =
+ &::cli::thunk< options, database_map<deferrable>, &options::fkeys_deferrable_mode_,
+ &options::fkeys_deferrable_mode_specified_ >;
+ _cli_options_map_["--default-pointer"] =
+ &::cli::thunk< options, std::string, &options::default_pointer_,
+ &options::default_pointer_specified_ >;
+ _cli_options_map_["--session-type"] =
+ &::cli::thunk< options, std::string, &options::session_type_,
+ &options::session_type_specified_ >;
+ _cli_options_map_["--profile"] =
+ &::cli::thunk< options, std::string, &options::profile_,
+ &options::profile_specified_ >;
+ _cli_options_map_["-p"] =
+ &::cli::thunk< options, std::string, &options::profile_,
+ &options::profile_specified_ >;
+ _cli_options_map_["--at-once"] =
+ &::cli::thunk< options, &options::at_once_ >;
+ _cli_options_map_["--schema"] =
+ &::cli::thunk< options, database_map<qname>, &options::schema_,
+ &options::schema_specified_ >;
+ _cli_options_map_["--export-symbol"] =
+ &::cli::thunk< options, database_map<std::string>, &options::export_symbol_,
+ &options::export_symbol_specified_ >;
+ _cli_options_map_["--extern-symbol"] =
+ &::cli::thunk< options, database_map<std::string>, &options::extern_symbol_,
+ &options::extern_symbol_specified_ >;
+ _cli_options_map_["--std"] =
+ &::cli::thunk< options, cxx_version, &options::std_,
+ &options::std_specified_ >;
+ _cli_options_map_["--warn-hard-add"] =
+ &::cli::thunk< options, &options::warn_hard_add_ >;
+ _cli_options_map_["--warn-hard-delete"] =
+ &::cli::thunk< options, &options::warn_hard_delete_ >;
+ _cli_options_map_["--warn-hard"] =
+ &::cli::thunk< options, &options::warn_hard_ >;
+ _cli_options_map_["--output-dir"] =
+ &::cli::thunk< options, std::string, &options::output_dir_,
+ &options::output_dir_specified_ >;
+ _cli_options_map_["-o"] =
+ &::cli::thunk< options, std::string, &options::output_dir_,
+ &options::output_dir_specified_ >;
+ _cli_options_map_["--input-name"] =
+ &::cli::thunk< options, std::string, &options::input_name_,
+ &options::input_name_specified_ >;
+ _cli_options_map_["--changelog"] =
+ &::cli::thunk< options, database_map<std::string>, &options::changelog_,
+ &options::changelog_specified_ >;
+ _cli_options_map_["--changelog-in"] =
+ &::cli::thunk< options, database_map<std::string>, &options::changelog_in_,
+ &options::changelog_in_specified_ >;
+ _cli_options_map_["--changelog-out"] =
+ &::cli::thunk< options, database_map<std::string>, &options::changelog_out_,
+ &options::changelog_out_specified_ >;
+ _cli_options_map_["--changelog-dir"] =
+ &::cli::thunk< options, database_map<std::string>, &options::changelog_dir_,
+ &options::changelog_dir_specified_ >;
+ _cli_options_map_["--init-changelog"] =
+ &::cli::thunk< options, &options::init_changelog_ >;
+ _cli_options_map_["--odb-file-suffix"] =
+ &::cli::thunk< options, database_map<std::string>, &options::odb_file_suffix_,
+ &options::odb_file_suffix_specified_ >;
+ _cli_options_map_["--sql-file-suffix"] =
+ &::cli::thunk< options, database_map<std::string>, &options::sql_file_suffix_,
+ &options::sql_file_suffix_specified_ >;
+ _cli_options_map_["--schema-file-suffix"] =
+ &::cli::thunk< options, database_map<std::string>, &options::schema_file_suffix_,
+ &options::schema_file_suffix_specified_ >;
+ _cli_options_map_["--changelog-file-suffix"] =
+ &::cli::thunk< options, database_map<std::string>, &options::changelog_file_suffix_,
+ &options::changelog_file_suffix_specified_ >;
+ _cli_options_map_["--hxx-suffix"] =
+ &::cli::thunk< options, std::string, &options::hxx_suffix_,
+ &options::hxx_suffix_specified_ >;
+ _cli_options_map_["--ixx-suffix"] =
+ &::cli::thunk< options, std::string, &options::ixx_suffix_,
+ &options::ixx_suffix_specified_ >;
+ _cli_options_map_["--cxx-suffix"] =
+ &::cli::thunk< options, std::string, &options::cxx_suffix_,
+ &options::cxx_suffix_specified_ >;
+ _cli_options_map_["--sql-suffix"] =
+ &::cli::thunk< options, std::string, &options::sql_suffix_,
+ &options::sql_suffix_specified_ >;
+ _cli_options_map_["--changelog-suffix"] =
+ &::cli::thunk< options, std::string, &options::changelog_suffix_,
+ &options::changelog_suffix_specified_ >;
+ _cli_options_map_["--hxx-prologue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::hxx_prologue_,
+ &options::hxx_prologue_specified_ >;
+ _cli_options_map_["--ixx-prologue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::ixx_prologue_,
+ &options::ixx_prologue_specified_ >;
+ _cli_options_map_["--cxx-prologue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::cxx_prologue_,
+ &options::cxx_prologue_specified_ >;
+ _cli_options_map_["--schema-prologue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::schema_prologue_,
+ &options::schema_prologue_specified_ >;
+ _cli_options_map_["--sql-prologue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sql_prologue_,
+ &options::sql_prologue_specified_ >;
+ _cli_options_map_["--migration-prologue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::migration_prologue_,
+ &options::migration_prologue_specified_ >;
+ _cli_options_map_["--sql-interlude"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sql_interlude_,
+ &options::sql_interlude_specified_ >;
+ _cli_options_map_["--hxx-epilogue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::hxx_epilogue_,
+ &options::hxx_epilogue_specified_ >;
+ _cli_options_map_["--ixx-epilogue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::ixx_epilogue_,
+ &options::ixx_epilogue_specified_ >;
+ _cli_options_map_["--cxx-epilogue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::cxx_epilogue_,
+ &options::cxx_epilogue_specified_ >;
+ _cli_options_map_["--schema-epilogue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::schema_epilogue_,
+ &options::schema_epilogue_specified_ >;
+ _cli_options_map_["--sql-epilogue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sql_epilogue_,
+ &options::sql_epilogue_specified_ >;
+ _cli_options_map_["--migration-epilogue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::migration_epilogue_,
+ &options::migration_epilogue_specified_ >;
+ _cli_options_map_["--hxx-prologue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::hxx_prologue_file_,
+ &options::hxx_prologue_file_specified_ >;
+ _cli_options_map_["--ixx-prologue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::ixx_prologue_file_,
+ &options::ixx_prologue_file_specified_ >;
+ _cli_options_map_["--cxx-prologue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::cxx_prologue_file_,
+ &options::cxx_prologue_file_specified_ >;
+ _cli_options_map_["--schema-prologue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::schema_prologue_file_,
+ &options::schema_prologue_file_specified_ >;
+ _cli_options_map_["--sql-prologue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sql_prologue_file_,
+ &options::sql_prologue_file_specified_ >;
+ _cli_options_map_["--migration-prologue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::migration_prologue_file_,
+ &options::migration_prologue_file_specified_ >;
+ _cli_options_map_["--sql-interlude-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sql_interlude_file_,
+ &options::sql_interlude_file_specified_ >;
+ _cli_options_map_["--hxx-epilogue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::hxx_epilogue_file_,
+ &options::hxx_epilogue_file_specified_ >;
+ _cli_options_map_["--ixx-epilogue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::ixx_epilogue_file_,
+ &options::ixx_epilogue_file_specified_ >;
+ _cli_options_map_["--cxx-epilogue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::cxx_epilogue_file_,
+ &options::cxx_epilogue_file_specified_ >;
+ _cli_options_map_["--schema-epilogue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::schema_epilogue_file_,
+ &options::schema_epilogue_file_specified_ >;
+ _cli_options_map_["--sql-epilogue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sql_epilogue_file_,
+ &options::sql_epilogue_file_specified_ >;
+ _cli_options_map_["--migration-epilogue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::migration_epilogue_file_,
+ &options::migration_epilogue_file_specified_ >;
+ _cli_options_map_["--odb-prologue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::odb_prologue_,
+ &options::odb_prologue_specified_ >;
+ _cli_options_map_["--odb-prologue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::odb_prologue_file_,
+ &options::odb_prologue_file_specified_ >;
+ _cli_options_map_["--odb-epilogue"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::odb_epilogue_,
+ &options::odb_epilogue_specified_ >;
+ _cli_options_map_["--odb-epilogue-file"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::odb_epilogue_file_,
+ &options::odb_epilogue_file_specified_ >;
+ _cli_options_map_["--table-prefix"] =
+ &::cli::thunk< options, database_map<std::string>, &options::table_prefix_,
+ &options::table_prefix_specified_ >;
+ _cli_options_map_["--index-suffix"] =
+ &::cli::thunk< options, database_map<std::string>, &options::index_suffix_,
+ &options::index_suffix_specified_ >;
+ _cli_options_map_["--fkey-suffix"] =
+ &::cli::thunk< options, database_map<std::string>, &options::fkey_suffix_,
+ &options::fkey_suffix_specified_ >;
+ _cli_options_map_["--sequence-suffix"] =
+ &::cli::thunk< options, database_map<std::string>, &options::sequence_suffix_,
+ &options::sequence_suffix_specified_ >;
+ _cli_options_map_["--sql-name-case"] =
+ &::cli::thunk< options, database_map<name_case>, &options::sql_name_case_,
+ &options::sql_name_case_specified_ >;
+ _cli_options_map_["--table-regex"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::table_regex_,
+ &options::table_regex_specified_ >;
+ _cli_options_map_["--column-regex"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::column_regex_,
+ &options::column_regex_specified_ >;
+ _cli_options_map_["--index-regex"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::index_regex_,
+ &options::index_regex_specified_ >;
+ _cli_options_map_["--fkey-regex"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::fkey_regex_,
+ &options::fkey_regex_specified_ >;
+ _cli_options_map_["--sequence-regex"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sequence_regex_,
+ &options::sequence_regex_specified_ >;
+ _cli_options_map_["--statement-regex"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::statement_regex_,
+ &options::statement_regex_specified_ >;
+ _cli_options_map_["--sql-name-regex"] =
+ &::cli::thunk< options, database_map<std::vector<std::string> >, &options::sql_name_regex_,
+ &options::sql_name_regex_specified_ >;
+ _cli_options_map_["--sql-name-regex-trace"] =
+ &::cli::thunk< options, &options::sql_name_regex_trace_ >;
+ _cli_options_map_["--accessor-regex"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::accessor_regex_,
+ &options::accessor_regex_specified_ >;
+ _cli_options_map_["--accessor-regex-trace"] =
+ &::cli::thunk< options, &options::accessor_regex_trace_ >;
+ _cli_options_map_["--modifier-regex"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::modifier_regex_,
+ &options::modifier_regex_specified_ >;
+ _cli_options_map_["--modifier-regex-trace"] =
+ &::cli::thunk< options, &options::modifier_regex_trace_ >;
+ _cli_options_map_["--include-with-brackets"] =
+ &::cli::thunk< options, &options::include_with_brackets_ >;
+ _cli_options_map_["--include-prefix"] =
+ &::cli::thunk< options, std::string, &options::include_prefix_,
+ &options::include_prefix_specified_ >;
+ _cli_options_map_["--include-regex"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::include_regex_,
+ &options::include_regex_specified_ >;
+ _cli_options_map_["--include-regex-trace"] =
+ &::cli::thunk< options, &options::include_regex_trace_ >;
+ _cli_options_map_["--guard-prefix"] =
+ &::cli::thunk< options, std::string, &options::guard_prefix_,
+ &options::guard_prefix_specified_ >;
+ _cli_options_map_["--show-sloc"] =
+ &::cli::thunk< options, &options::show_sloc_ >;
+ _cli_options_map_["--sloc-limit"] =
+ &::cli::thunk< options, std::size_t, &options::sloc_limit_,
+ &options::sloc_limit_specified_ >;
+ _cli_options_map_["--options-file"] =
+ &::cli::thunk< options, std::string, &options::options_file_,
+ &options::options_file_specified_ >;
+ _cli_options_map_["-x"] =
+ &::cli::thunk< options, std::vector<std::string>, &options::x_,
+ &options::x_specified_ >;
+ _cli_options_map_["-v"] =
+ &::cli::thunk< options, &options::v_ >;
+ _cli_options_map_["--trace"] =
+ &::cli::thunk< options, &options::trace_ >;
+ _cli_options_map_["--mysql-engine"] =
+ &::cli::thunk< options, std::string, &options::mysql_engine_,
+ &options::mysql_engine_specified_ >;
+ _cli_options_map_["--sqlite-override-null"] =
+ &::cli::thunk< options, &options::sqlite_override_null_ >;
+ _cli_options_map_["--sqlite-lax-auto-id"] =
+ &::cli::thunk< options, &options::sqlite_lax_auto_id_ >;
+ _cli_options_map_["--pgsql-server-version"] =
+ &::cli::thunk< options, ::pgsql_version, &options::pgsql_server_version_,
+ &options::pgsql_server_version_specified_ >;
+ _cli_options_map_["--oracle-client-version"] =
+ &::cli::thunk< options, ::oracle_version, &options::oracle_client_version_,
+ &options::oracle_client_version_specified_ >;
+ _cli_options_map_["--oracle-warn-truncation"] =
+ &::cli::thunk< options, &options::oracle_warn_truncation_ >;
+ _cli_options_map_["--mssql-server-version"] =
+ &::cli::thunk< options, ::mssql_version, &options::mssql_server_version_,
+ &options::mssql_server_version_specified_ >;
+ _cli_options_map_["--mssql-short-limit"] =
+ &::cli::thunk< options, unsigned int, &options::mssql_short_limit_,
+ &options::mssql_short_limit_specified_ >;
+ }
+};
+
+static _cli_options_map_init _cli_options_map_init_;
+
+bool options::
+_parse (const char* o, ::cli::scanner& s)
+{
+ _cli_options_map::const_iterator i (_cli_options_map_.find (o));
+
+ if (i != _cli_options_map_.end ())
+ {
+ (*(i->second)) (*this, s);
+ return true;
+ }
+
+ return false;
+}
+
+bool options::
+_parse (::cli::scanner& s,
+ ::cli::unknown_mode opt_mode,
+ ::cli::unknown_mode arg_mode)
+{
+ // Can't skip combined flags (--no-combined-flags).
+ //
+ assert (opt_mode != ::cli::unknown_mode::skip);
+
+ bool r = false;
+ bool opt = true;
+
+ while (s.more ())
+ {
+ const char* o = s.peek ();
+
+ if (std::strcmp (o, "--") == 0)
+ {
+ opt = false;
+ s.skip ();
+ r = true;
+ continue;
+ }
+
+ if (opt)
+ {
+ if (_parse (o, s))
+ {
+ r = true;
+ continue;
+ }
+
+ if (std::strncmp (o, "-", 1) == 0 && o[1] != '\0')
+ {
+ // Handle combined option values.
+ //
+ std::string co;
+ if (const char* v = std::strchr (o, '='))
+ {
+ co.assign (o, 0, v - o);
+ ++v;
+
+ int ac (2);
+ char* av[] =
+ {
+ const_cast<char*> (co.c_str ()),
+ const_cast<char*> (v)
+ };
+
+ ::cli::argv_scanner ns (0, ac, av);
+
+ if (_parse (co.c_str (), ns))
+ {
+ // Parsed the option but not its value?
+ //
+ if (ns.end () != 2)
+ throw ::cli::invalid_value (co, v);
+
+ s.next ();
+ r = true;
+ continue;
+ }
+ else
+ {
+ // Set the unknown option and fall through.
+ //
+ o = co.c_str ();
+ }
+ }
+
+ // Handle combined flags.
+ //
+ char cf[3];
+ {
+ const char* p = o + 1;
+ for (; *p != '\0'; ++p)
+ {
+ if (!((*p >= 'a' && *p <= 'z') ||
+ (*p >= 'A' && *p <= 'Z') ||
+ (*p >= '0' && *p <= '9')))
+ break;
+ }
+
+ if (*p == '\0')
+ {
+ for (p = o + 1; *p != '\0'; ++p)
+ {
+ std::strcpy (cf, "-");
+ cf[1] = *p;
+ cf[2] = '\0';
+
+ int ac (1);
+ char* av[] =
+ {
+ cf
+ };
+
+ ::cli::argv_scanner ns (0, ac, av);
+
+ if (!_parse (cf, ns))
+ break;
+ }
+
+ if (*p == '\0')
+ {
+ // All handled.
+ //
+ s.next ();
+ r = true;
+ continue;
+ }
+ else
+ {
+ // Set the unknown option and fall through.
+ //
+ o = cf;
+ }
+ }
+ }
+
+ switch (opt_mode)
+ {
+ case ::cli::unknown_mode::skip:
+ {
+ s.skip ();
+ r = true;
+ continue;
+ }
+ case ::cli::unknown_mode::stop:
+ {
+ break;
+ }
+ case ::cli::unknown_mode::fail:
+ {
+ throw ::cli::unknown_option (o);
+ }
+ }
+
+ break;
+ }
+ }
+
+ switch (arg_mode)
+ {
+ case ::cli::unknown_mode::skip:
+ {
+ s.skip ();
+ r = true;
+ continue;
+ }
+ case ::cli::unknown_mode::stop:
+ {
+ break;
+ }
+ case ::cli::unknown_mode::fail:
+ {
+ throw ::cli::unknown_argument (o);
+ }
+ }
+
+ break;
+ }
+
+ return r;
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
diff --git a/odb/odb/pregenerated/odb/options.hxx b/odb/odb/pregenerated/odb/options.hxx
new file mode 100644
index 0000000..74406a0
--- /dev/null
+++ b/odb/odb/pregenerated/odb/options.hxx
@@ -0,0 +1,2340 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+#ifndef ODB_OPTIONS_HXX
+#define ODB_OPTIONS_HXX
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <list>
+#include <deque>
+#include <map>
+#include <vector>
+#include <iosfwd>
+#include <string>
+#include <cstddef>
+#include <exception>
+
+#ifndef CLI_POTENTIALLY_UNUSED
+# if defined(_MSC_VER) || defined(__xlC__)
+# define CLI_POTENTIALLY_UNUSED(x) (void*)&x
+# else
+# define CLI_POTENTIALLY_UNUSED(x) (void)x
+# endif
+#endif
+
+namespace cli
+{
+ class usage_para
+ {
+ public:
+ enum value
+ {
+ none,
+ text,
+ option
+ };
+
+ usage_para (value);
+
+ operator value () const
+ {
+ return v_;
+ }
+
+ private:
+ value v_;
+ };
+
+ class unknown_mode
+ {
+ public:
+ enum value
+ {
+ skip,
+ stop,
+ fail
+ };
+
+ unknown_mode (value);
+
+ operator value () const
+ {
+ return v_;
+ }
+
+ private:
+ value v_;
+ };
+
+ // Exceptions.
+ //
+
+ class exception: public std::exception
+ {
+ public:
+ virtual void
+ print (::std::ostream&) const = 0;
+ };
+
+ ::std::ostream&
+ operator<< (::std::ostream&, const exception&);
+
+ class unknown_option: public exception
+ {
+ public:
+ virtual
+ ~unknown_option () throw ();
+
+ unknown_option (const std::string& option);
+
+ const std::string&
+ option () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ };
+
+ class unknown_argument: public exception
+ {
+ public:
+ virtual
+ ~unknown_argument () throw ();
+
+ unknown_argument (const std::string& argument);
+
+ const std::string&
+ argument () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string argument_;
+ };
+
+ class missing_value: public exception
+ {
+ public:
+ virtual
+ ~missing_value () throw ();
+
+ missing_value (const std::string& option);
+
+ const std::string&
+ option () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ };
+
+ class invalid_value: public exception
+ {
+ public:
+ virtual
+ ~invalid_value () throw ();
+
+ invalid_value (const std::string& option,
+ const std::string& value,
+ const std::string& message = std::string ());
+
+ const std::string&
+ option () const;
+
+ const std::string&
+ value () const;
+
+ const std::string&
+ message () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string option_;
+ std::string value_;
+ std::string message_;
+ };
+
+ class eos_reached: public exception
+ {
+ public:
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+ };
+
+ class file_io_failure: public exception
+ {
+ public:
+ virtual
+ ~file_io_failure () throw ();
+
+ file_io_failure (const std::string& file);
+
+ const std::string&
+ file () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string file_;
+ };
+
+ class unmatched_quote: public exception
+ {
+ public:
+ virtual
+ ~unmatched_quote () throw ();
+
+ unmatched_quote (const std::string& argument);
+
+ const std::string&
+ argument () const;
+
+ virtual void
+ print (::std::ostream&) const;
+
+ virtual const char*
+ what () const throw ();
+
+ private:
+ std::string argument_;
+ };
+
+ // Command line argument scanner interface.
+ //
+ // The values returned by next() are guaranteed to be valid
+ // for the two previous arguments up until a call to a third
+ // peek() or next().
+ //
+ // The position() function returns a monotonically-increasing
+ // number which, if stored, can later be used to determine the
+ // relative position of the argument returned by the following
+ // call to next(). Note that if multiple scanners are used to
+ // extract arguments from multiple sources, then the end
+ // position of the previous scanner should be used as the
+ // start position of the next.
+ //
+ class scanner
+ {
+ public:
+ virtual
+ ~scanner ();
+
+ virtual bool
+ more () = 0;
+
+ virtual const char*
+ peek () = 0;
+
+ virtual const char*
+ next () = 0;
+
+ virtual void
+ skip () = 0;
+
+ virtual std::size_t
+ position () = 0;
+ };
+
+ class argv_scanner: public scanner
+ {
+ public:
+ argv_scanner (int& argc,
+ char** argv,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_scanner (int start,
+ int& argc,
+ char** argv,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ int
+ end () const;
+
+ virtual bool
+ more ();
+
+ virtual const char*
+ peek ();
+
+ virtual const char*
+ next ();
+
+ virtual void
+ skip ();
+
+ virtual std::size_t
+ position ();
+
+ protected:
+ std::size_t start_position_;
+ int i_;
+ int& argc_;
+ char** argv_;
+ bool erase_;
+ };
+
+ class argv_file_scanner: public argv_scanner
+ {
+ public:
+ argv_file_scanner (int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (const std::string& file,
+ const std::string& option,
+ std::size_t start_position = 0);
+
+ struct option_info
+ {
+ // If search_func is not NULL, it is called, with the arg
+ // value as the second argument, to locate the options file.
+ // If it returns an empty string, then the file is ignored.
+ //
+ const char* option;
+ std::string (*search_func) (const char*, void* arg);
+ void* arg;
+ };
+
+ argv_file_scanner (int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase = false,
+ std::size_t start_position = 0);
+
+ argv_file_scanner (const std::string& file,
+ const option_info* options = 0,
+ std::size_t options_count = 0,
+ std::size_t start_position = 0);
+
+ virtual bool
+ more ();
+
+ virtual const char*
+ peek ();
+
+ virtual const char*
+ next ();
+
+ virtual void
+ skip ();
+
+ virtual std::size_t
+ position ();
+
+ // Return the file path if the peeked at argument came from a file and
+ // the empty string otherwise. The reference is guaranteed to be valid
+ // till the end of the scanner lifetime.
+ //
+ const std::string&
+ peek_file ();
+
+ // Return the 1-based line number if the peeked at argument came from
+ // a file and zero otherwise.
+ //
+ std::size_t
+ peek_line ();
+
+ private:
+ const option_info*
+ find (const char*) const;
+
+ void
+ load (const std::string& file);
+
+ typedef argv_scanner base;
+
+ const std::string option_;
+ option_info option_info_;
+ const option_info* options_;
+ std::size_t options_count_;
+
+ struct arg
+ {
+ std::string value;
+ const std::string* file;
+ std::size_t line;
+ };
+
+ std::deque<arg> args_;
+ std::list<std::string> files_;
+
+ // Circular buffer of two arguments.
+ //
+ std::string hold_[2];
+ std::size_t i_;
+
+ bool skip_;
+
+ static int zero_argc_;
+ static std::string empty_string_;
+ };
+
+ typedef std::vector<std::string> option_names;
+
+ class option
+ {
+ public:
+
+ const std::string&
+ name () const;
+
+ const option_names&
+ aliases () const;
+
+ bool
+ flag () const;
+
+ const std::string&
+ default_value () const;
+
+ public:option ();
+ option (const std::string& name,
+ const option_names& aliases,
+ bool flag,
+ const std::string& default_value);
+
+ private:
+ std::string name_;
+ option_names aliases_;
+ bool flag_;
+ std::string default_value_;
+ };
+
+ class options: public std::vector<option>
+ {
+ public:
+ typedef std::vector<option> container_type;
+
+ container_type::const_iterator
+ find (const std::string& name) const;
+
+ void
+ push_back (const option&);
+ private:
+ typedef std::map<std::string, container_type::size_type> map_type;
+ map_type map_;
+ };
+
+ template <typename X>
+ struct parser;
+}
+
+#include <set>
+
+#include <vector>
+
+#include <string>
+
+#include <cstddef>
+
+#include <cstdint>
+
+#include <odb/option-types.hxx>
+
+class options
+{
+ public:
+ options ();
+
+ options (int& argc,
+ char** argv,
+ bool erase = false,
+ ::cli::unknown_mode option = ::cli::unknown_mode::fail,
+ ::cli::unknown_mode argument = ::cli::unknown_mode::stop);
+
+ options (int start,
+ int& argc,
+ char** argv,
+ bool erase = false,
+ ::cli::unknown_mode option = ::cli::unknown_mode::fail,
+ ::cli::unknown_mode argument = ::cli::unknown_mode::stop);
+
+ options (int& argc,
+ char** argv,
+ int& end,
+ bool erase = false,
+ ::cli::unknown_mode option = ::cli::unknown_mode::fail,
+ ::cli::unknown_mode argument = ::cli::unknown_mode::stop);
+
+ options (int start,
+ int& argc,
+ char** argv,
+ int& end,
+ bool erase = false,
+ ::cli::unknown_mode option = ::cli::unknown_mode::fail,
+ ::cli::unknown_mode argument = ::cli::unknown_mode::stop);
+
+ options (::cli::scanner&,
+ ::cli::unknown_mode option = ::cli::unknown_mode::fail,
+ ::cli::unknown_mode argument = ::cli::unknown_mode::stop);
+
+ // Option accessors and modifiers.
+ //
+ const std::uint64_t&
+ build2_metadata () const;
+
+ std::uint64_t&
+ build2_metadata ();
+
+ void
+ build2_metadata (const std::uint64_t&);
+
+ bool
+ build2_metadata_specified () const;
+
+ void
+ build2_metadata_specified (bool);
+
+ const bool&
+ help () const;
+
+ bool&
+ help ();
+
+ void
+ help (const bool&);
+
+ const bool&
+ version () const;
+
+ bool&
+ version ();
+
+ void
+ version (const bool&);
+
+ const std::vector<std::string>&
+ I () const;
+
+ std::vector<std::string>&
+ I ();
+
+ void
+ I (const std::vector<std::string>&);
+
+ bool
+ I_specified () const;
+
+ void
+ I_specified (bool);
+
+ const std::vector<std::string>&
+ D () const;
+
+ std::vector<std::string>&
+ D ();
+
+ void
+ D (const std::vector<std::string>&);
+
+ bool
+ D_specified () const;
+
+ void
+ D_specified (bool);
+
+ const std::vector<std::string>&
+ U () const;
+
+ std::vector<std::string>&
+ U ();
+
+ void
+ U (const std::vector<std::string>&);
+
+ bool
+ U_specified () const;
+
+ void
+ U_specified (bool);
+
+ const std::vector< ::database>&
+ database () const;
+
+ std::vector< ::database>&
+ database ();
+
+ void
+ database (const std::vector< ::database>&);
+
+ bool
+ database_specified () const;
+
+ void
+ database_specified (bool);
+
+ const ::multi_database&
+ multi_database () const;
+
+ ::multi_database&
+ multi_database ();
+
+ void
+ multi_database (const ::multi_database&);
+
+ bool
+ multi_database_specified () const;
+
+ void
+ multi_database_specified (bool);
+
+ const ::database&
+ default_database () const;
+
+ ::database&
+ default_database ();
+
+ void
+ default_database (const ::database&);
+
+ bool
+ default_database_specified () const;
+
+ void
+ default_database_specified (bool);
+
+ const bool&
+ generate_query () const;
+
+ bool&
+ generate_query ();
+
+ void
+ generate_query (const bool&);
+
+ const bool&
+ generate_prepared () const;
+
+ bool&
+ generate_prepared ();
+
+ void
+ generate_prepared (const bool&);
+
+ const bool&
+ omit_unprepared () const;
+
+ bool&
+ omit_unprepared ();
+
+ void
+ omit_unprepared (const bool&);
+
+ const bool&
+ generate_session () const;
+
+ bool&
+ generate_session ();
+
+ void
+ generate_session (const bool&);
+
+ const bool&
+ generate_schema () const;
+
+ bool&
+ generate_schema ();
+
+ void
+ generate_schema (const bool&);
+
+ const bool&
+ generate_schema_only () const;
+
+ bool&
+ generate_schema_only ();
+
+ void
+ generate_schema_only (const bool&);
+
+ const bool&
+ suppress_migration () const;
+
+ bool&
+ suppress_migration ();
+
+ void
+ suppress_migration (const bool&);
+
+ const bool&
+ suppress_schema_version () const;
+
+ bool&
+ suppress_schema_version ();
+
+ void
+ suppress_schema_version (const bool&);
+
+ const database_map<qname>&
+ schema_version_table () const;
+
+ database_map<qname>&
+ schema_version_table ();
+
+ void
+ schema_version_table (const database_map<qname>&);
+
+ bool
+ schema_version_table_specified () const;
+
+ void
+ schema_version_table_specified (bool);
+
+ const database_map<std::set< ::schema_format> >&
+ schema_format () const;
+
+ database_map<std::set< ::schema_format> >&
+ schema_format ();
+
+ void
+ schema_format (const database_map<std::set< ::schema_format> >&);
+
+ bool
+ schema_format_specified () const;
+
+ void
+ schema_format_specified (bool);
+
+ const bool&
+ omit_drop () const;
+
+ bool&
+ omit_drop ();
+
+ void
+ omit_drop (const bool&);
+
+ const bool&
+ omit_create () const;
+
+ bool&
+ omit_create ();
+
+ void
+ omit_create (const bool&);
+
+ const database_map<std::string>&
+ schema_name () const;
+
+ database_map<std::string>&
+ schema_name ();
+
+ void
+ schema_name (const database_map<std::string>&);
+
+ bool
+ schema_name_specified () const;
+
+ void
+ schema_name_specified (bool);
+
+ const database_map<deferrable>&
+ fkeys_deferrable_mode () const;
+
+ database_map<deferrable>&
+ fkeys_deferrable_mode ();
+
+ void
+ fkeys_deferrable_mode (const database_map<deferrable>&);
+
+ bool
+ fkeys_deferrable_mode_specified () const;
+
+ void
+ fkeys_deferrable_mode_specified (bool);
+
+ const std::string&
+ default_pointer () const;
+
+ std::string&
+ default_pointer ();
+
+ void
+ default_pointer (const std::string&);
+
+ bool
+ default_pointer_specified () const;
+
+ void
+ default_pointer_specified (bool);
+
+ const std::string&
+ session_type () const;
+
+ std::string&
+ session_type ();
+
+ void
+ session_type (const std::string&);
+
+ bool
+ session_type_specified () const;
+
+ void
+ session_type_specified (bool);
+
+ const std::string&
+ profile () const;
+
+ std::string&
+ profile ();
+
+ void
+ profile (const std::string&);
+
+ bool
+ profile_specified () const;
+
+ void
+ profile_specified (bool);
+
+ const bool&
+ at_once () const;
+
+ bool&
+ at_once ();
+
+ void
+ at_once (const bool&);
+
+ const database_map<qname>&
+ schema () const;
+
+ database_map<qname>&
+ schema ();
+
+ void
+ schema (const database_map<qname>&);
+
+ bool
+ schema_specified () const;
+
+ void
+ schema_specified (bool);
+
+ const database_map<std::string>&
+ export_symbol () const;
+
+ database_map<std::string>&
+ export_symbol ();
+
+ void
+ export_symbol (const database_map<std::string>&);
+
+ bool
+ export_symbol_specified () const;
+
+ void
+ export_symbol_specified (bool);
+
+ const database_map<std::string>&
+ extern_symbol () const;
+
+ database_map<std::string>&
+ extern_symbol ();
+
+ void
+ extern_symbol (const database_map<std::string>&);
+
+ bool
+ extern_symbol_specified () const;
+
+ void
+ extern_symbol_specified (bool);
+
+ const cxx_version&
+ std () const;
+
+ cxx_version&
+ std ();
+
+ void
+ std (const cxx_version&);
+
+ bool
+ std_specified () const;
+
+ void
+ std_specified (bool);
+
+ const bool&
+ warn_hard_add () const;
+
+ bool&
+ warn_hard_add ();
+
+ void
+ warn_hard_add (const bool&);
+
+ const bool&
+ warn_hard_delete () const;
+
+ bool&
+ warn_hard_delete ();
+
+ void
+ warn_hard_delete (const bool&);
+
+ const bool&
+ warn_hard () const;
+
+ bool&
+ warn_hard ();
+
+ void
+ warn_hard (const bool&);
+
+ const std::string&
+ output_dir () const;
+
+ std::string&
+ output_dir ();
+
+ void
+ output_dir (const std::string&);
+
+ bool
+ output_dir_specified () const;
+
+ void
+ output_dir_specified (bool);
+
+ const std::string&
+ input_name () const;
+
+ std::string&
+ input_name ();
+
+ void
+ input_name (const std::string&);
+
+ bool
+ input_name_specified () const;
+
+ void
+ input_name_specified (bool);
+
+ const database_map<std::string>&
+ changelog () const;
+
+ database_map<std::string>&
+ changelog ();
+
+ void
+ changelog (const database_map<std::string>&);
+
+ bool
+ changelog_specified () const;
+
+ void
+ changelog_specified (bool);
+
+ const database_map<std::string>&
+ changelog_in () const;
+
+ database_map<std::string>&
+ changelog_in ();
+
+ void
+ changelog_in (const database_map<std::string>&);
+
+ bool
+ changelog_in_specified () const;
+
+ void
+ changelog_in_specified (bool);
+
+ const database_map<std::string>&
+ changelog_out () const;
+
+ database_map<std::string>&
+ changelog_out ();
+
+ void
+ changelog_out (const database_map<std::string>&);
+
+ bool
+ changelog_out_specified () const;
+
+ void
+ changelog_out_specified (bool);
+
+ const database_map<std::string>&
+ changelog_dir () const;
+
+ database_map<std::string>&
+ changelog_dir ();
+
+ void
+ changelog_dir (const database_map<std::string>&);
+
+ bool
+ changelog_dir_specified () const;
+
+ void
+ changelog_dir_specified (bool);
+
+ const bool&
+ init_changelog () const;
+
+ bool&
+ init_changelog ();
+
+ void
+ init_changelog (const bool&);
+
+ const database_map<std::string>&
+ odb_file_suffix () const;
+
+ database_map<std::string>&
+ odb_file_suffix ();
+
+ void
+ odb_file_suffix (const database_map<std::string>&);
+
+ bool
+ odb_file_suffix_specified () const;
+
+ void
+ odb_file_suffix_specified (bool);
+
+ const database_map<std::string>&
+ sql_file_suffix () const;
+
+ database_map<std::string>&
+ sql_file_suffix ();
+
+ void
+ sql_file_suffix (const database_map<std::string>&);
+
+ bool
+ sql_file_suffix_specified () const;
+
+ void
+ sql_file_suffix_specified (bool);
+
+ const database_map<std::string>&
+ schema_file_suffix () const;
+
+ database_map<std::string>&
+ schema_file_suffix ();
+
+ void
+ schema_file_suffix (const database_map<std::string>&);
+
+ bool
+ schema_file_suffix_specified () const;
+
+ void
+ schema_file_suffix_specified (bool);
+
+ const database_map<std::string>&
+ changelog_file_suffix () const;
+
+ database_map<std::string>&
+ changelog_file_suffix ();
+
+ void
+ changelog_file_suffix (const database_map<std::string>&);
+
+ bool
+ changelog_file_suffix_specified () const;
+
+ void
+ changelog_file_suffix_specified (bool);
+
+ const std::string&
+ hxx_suffix () const;
+
+ std::string&
+ hxx_suffix ();
+
+ void
+ hxx_suffix (const std::string&);
+
+ bool
+ hxx_suffix_specified () const;
+
+ void
+ hxx_suffix_specified (bool);
+
+ const std::string&
+ ixx_suffix () const;
+
+ std::string&
+ ixx_suffix ();
+
+ void
+ ixx_suffix (const std::string&);
+
+ bool
+ ixx_suffix_specified () const;
+
+ void
+ ixx_suffix_specified (bool);
+
+ const std::string&
+ cxx_suffix () const;
+
+ std::string&
+ cxx_suffix ();
+
+ void
+ cxx_suffix (const std::string&);
+
+ bool
+ cxx_suffix_specified () const;
+
+ void
+ cxx_suffix_specified (bool);
+
+ const std::string&
+ sql_suffix () const;
+
+ std::string&
+ sql_suffix ();
+
+ void
+ sql_suffix (const std::string&);
+
+ bool
+ sql_suffix_specified () const;
+
+ void
+ sql_suffix_specified (bool);
+
+ const std::string&
+ changelog_suffix () const;
+
+ std::string&
+ changelog_suffix ();
+
+ void
+ changelog_suffix (const std::string&);
+
+ bool
+ changelog_suffix_specified () const;
+
+ void
+ changelog_suffix_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ hxx_prologue () const;
+
+ database_map<std::vector<std::string> >&
+ hxx_prologue ();
+
+ void
+ hxx_prologue (const database_map<std::vector<std::string> >&);
+
+ bool
+ hxx_prologue_specified () const;
+
+ void
+ hxx_prologue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ ixx_prologue () const;
+
+ database_map<std::vector<std::string> >&
+ ixx_prologue ();
+
+ void
+ ixx_prologue (const database_map<std::vector<std::string> >&);
+
+ bool
+ ixx_prologue_specified () const;
+
+ void
+ ixx_prologue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ cxx_prologue () const;
+
+ database_map<std::vector<std::string> >&
+ cxx_prologue ();
+
+ void
+ cxx_prologue (const database_map<std::vector<std::string> >&);
+
+ bool
+ cxx_prologue_specified () const;
+
+ void
+ cxx_prologue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ schema_prologue () const;
+
+ database_map<std::vector<std::string> >&
+ schema_prologue ();
+
+ void
+ schema_prologue (const database_map<std::vector<std::string> >&);
+
+ bool
+ schema_prologue_specified () const;
+
+ void
+ schema_prologue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ sql_prologue () const;
+
+ database_map<std::vector<std::string> >&
+ sql_prologue ();
+
+ void
+ sql_prologue (const database_map<std::vector<std::string> >&);
+
+ bool
+ sql_prologue_specified () const;
+
+ void
+ sql_prologue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ migration_prologue () const;
+
+ database_map<std::vector<std::string> >&
+ migration_prologue ();
+
+ void
+ migration_prologue (const database_map<std::vector<std::string> >&);
+
+ bool
+ migration_prologue_specified () const;
+
+ void
+ migration_prologue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ sql_interlude () const;
+
+ database_map<std::vector<std::string> >&
+ sql_interlude ();
+
+ void
+ sql_interlude (const database_map<std::vector<std::string> >&);
+
+ bool
+ sql_interlude_specified () const;
+
+ void
+ sql_interlude_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ hxx_epilogue () const;
+
+ database_map<std::vector<std::string> >&
+ hxx_epilogue ();
+
+ void
+ hxx_epilogue (const database_map<std::vector<std::string> >&);
+
+ bool
+ hxx_epilogue_specified () const;
+
+ void
+ hxx_epilogue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ ixx_epilogue () const;
+
+ database_map<std::vector<std::string> >&
+ ixx_epilogue ();
+
+ void
+ ixx_epilogue (const database_map<std::vector<std::string> >&);
+
+ bool
+ ixx_epilogue_specified () const;
+
+ void
+ ixx_epilogue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ cxx_epilogue () const;
+
+ database_map<std::vector<std::string> >&
+ cxx_epilogue ();
+
+ void
+ cxx_epilogue (const database_map<std::vector<std::string> >&);
+
+ bool
+ cxx_epilogue_specified () const;
+
+ void
+ cxx_epilogue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ schema_epilogue () const;
+
+ database_map<std::vector<std::string> >&
+ schema_epilogue ();
+
+ void
+ schema_epilogue (const database_map<std::vector<std::string> >&);
+
+ bool
+ schema_epilogue_specified () const;
+
+ void
+ schema_epilogue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ sql_epilogue () const;
+
+ database_map<std::vector<std::string> >&
+ sql_epilogue ();
+
+ void
+ sql_epilogue (const database_map<std::vector<std::string> >&);
+
+ bool
+ sql_epilogue_specified () const;
+
+ void
+ sql_epilogue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ migration_epilogue () const;
+
+ database_map<std::vector<std::string> >&
+ migration_epilogue ();
+
+ void
+ migration_epilogue (const database_map<std::vector<std::string> >&);
+
+ bool
+ migration_epilogue_specified () const;
+
+ void
+ migration_epilogue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ hxx_prologue_file () const;
+
+ database_map<std::vector<std::string> >&
+ hxx_prologue_file ();
+
+ void
+ hxx_prologue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ hxx_prologue_file_specified () const;
+
+ void
+ hxx_prologue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ ixx_prologue_file () const;
+
+ database_map<std::vector<std::string> >&
+ ixx_prologue_file ();
+
+ void
+ ixx_prologue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ ixx_prologue_file_specified () const;
+
+ void
+ ixx_prologue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ cxx_prologue_file () const;
+
+ database_map<std::vector<std::string> >&
+ cxx_prologue_file ();
+
+ void
+ cxx_prologue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ cxx_prologue_file_specified () const;
+
+ void
+ cxx_prologue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ schema_prologue_file () const;
+
+ database_map<std::vector<std::string> >&
+ schema_prologue_file ();
+
+ void
+ schema_prologue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ schema_prologue_file_specified () const;
+
+ void
+ schema_prologue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ sql_prologue_file () const;
+
+ database_map<std::vector<std::string> >&
+ sql_prologue_file ();
+
+ void
+ sql_prologue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ sql_prologue_file_specified () const;
+
+ void
+ sql_prologue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ migration_prologue_file () const;
+
+ database_map<std::vector<std::string> >&
+ migration_prologue_file ();
+
+ void
+ migration_prologue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ migration_prologue_file_specified () const;
+
+ void
+ migration_prologue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ sql_interlude_file () const;
+
+ database_map<std::vector<std::string> >&
+ sql_interlude_file ();
+
+ void
+ sql_interlude_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ sql_interlude_file_specified () const;
+
+ void
+ sql_interlude_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ hxx_epilogue_file () const;
+
+ database_map<std::vector<std::string> >&
+ hxx_epilogue_file ();
+
+ void
+ hxx_epilogue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ hxx_epilogue_file_specified () const;
+
+ void
+ hxx_epilogue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ ixx_epilogue_file () const;
+
+ database_map<std::vector<std::string> >&
+ ixx_epilogue_file ();
+
+ void
+ ixx_epilogue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ ixx_epilogue_file_specified () const;
+
+ void
+ ixx_epilogue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ cxx_epilogue_file () const;
+
+ database_map<std::vector<std::string> >&
+ cxx_epilogue_file ();
+
+ void
+ cxx_epilogue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ cxx_epilogue_file_specified () const;
+
+ void
+ cxx_epilogue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ schema_epilogue_file () const;
+
+ database_map<std::vector<std::string> >&
+ schema_epilogue_file ();
+
+ void
+ schema_epilogue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ schema_epilogue_file_specified () const;
+
+ void
+ schema_epilogue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ sql_epilogue_file () const;
+
+ database_map<std::vector<std::string> >&
+ sql_epilogue_file ();
+
+ void
+ sql_epilogue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ sql_epilogue_file_specified () const;
+
+ void
+ sql_epilogue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ migration_epilogue_file () const;
+
+ database_map<std::vector<std::string> >&
+ migration_epilogue_file ();
+
+ void
+ migration_epilogue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ migration_epilogue_file_specified () const;
+
+ void
+ migration_epilogue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ odb_prologue () const;
+
+ database_map<std::vector<std::string> >&
+ odb_prologue ();
+
+ void
+ odb_prologue (const database_map<std::vector<std::string> >&);
+
+ bool
+ odb_prologue_specified () const;
+
+ void
+ odb_prologue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ odb_prologue_file () const;
+
+ database_map<std::vector<std::string> >&
+ odb_prologue_file ();
+
+ void
+ odb_prologue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ odb_prologue_file_specified () const;
+
+ void
+ odb_prologue_file_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ odb_epilogue () const;
+
+ database_map<std::vector<std::string> >&
+ odb_epilogue ();
+
+ void
+ odb_epilogue (const database_map<std::vector<std::string> >&);
+
+ bool
+ odb_epilogue_specified () const;
+
+ void
+ odb_epilogue_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ odb_epilogue_file () const;
+
+ database_map<std::vector<std::string> >&
+ odb_epilogue_file ();
+
+ void
+ odb_epilogue_file (const database_map<std::vector<std::string> >&);
+
+ bool
+ odb_epilogue_file_specified () const;
+
+ void
+ odb_epilogue_file_specified (bool);
+
+ const database_map<std::string>&
+ table_prefix () const;
+
+ database_map<std::string>&
+ table_prefix ();
+
+ void
+ table_prefix (const database_map<std::string>&);
+
+ bool
+ table_prefix_specified () const;
+
+ void
+ table_prefix_specified (bool);
+
+ const database_map<std::string>&
+ index_suffix () const;
+
+ database_map<std::string>&
+ index_suffix ();
+
+ void
+ index_suffix (const database_map<std::string>&);
+
+ bool
+ index_suffix_specified () const;
+
+ void
+ index_suffix_specified (bool);
+
+ const database_map<std::string>&
+ fkey_suffix () const;
+
+ database_map<std::string>&
+ fkey_suffix ();
+
+ void
+ fkey_suffix (const database_map<std::string>&);
+
+ bool
+ fkey_suffix_specified () const;
+
+ void
+ fkey_suffix_specified (bool);
+
+ const database_map<std::string>&
+ sequence_suffix () const;
+
+ database_map<std::string>&
+ sequence_suffix ();
+
+ void
+ sequence_suffix (const database_map<std::string>&);
+
+ bool
+ sequence_suffix_specified () const;
+
+ void
+ sequence_suffix_specified (bool);
+
+ const database_map<name_case>&
+ sql_name_case () const;
+
+ database_map<name_case>&
+ sql_name_case ();
+
+ void
+ sql_name_case (const database_map<name_case>&);
+
+ bool
+ sql_name_case_specified () const;
+
+ void
+ sql_name_case_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ table_regex () const;
+
+ database_map<std::vector<std::string> >&
+ table_regex ();
+
+ void
+ table_regex (const database_map<std::vector<std::string> >&);
+
+ bool
+ table_regex_specified () const;
+
+ void
+ table_regex_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ column_regex () const;
+
+ database_map<std::vector<std::string> >&
+ column_regex ();
+
+ void
+ column_regex (const database_map<std::vector<std::string> >&);
+
+ bool
+ column_regex_specified () const;
+
+ void
+ column_regex_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ index_regex () const;
+
+ database_map<std::vector<std::string> >&
+ index_regex ();
+
+ void
+ index_regex (const database_map<std::vector<std::string> >&);
+
+ bool
+ index_regex_specified () const;
+
+ void
+ index_regex_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ fkey_regex () const;
+
+ database_map<std::vector<std::string> >&
+ fkey_regex ();
+
+ void
+ fkey_regex (const database_map<std::vector<std::string> >&);
+
+ bool
+ fkey_regex_specified () const;
+
+ void
+ fkey_regex_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ sequence_regex () const;
+
+ database_map<std::vector<std::string> >&
+ sequence_regex ();
+
+ void
+ sequence_regex (const database_map<std::vector<std::string> >&);
+
+ bool
+ sequence_regex_specified () const;
+
+ void
+ sequence_regex_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ statement_regex () const;
+
+ database_map<std::vector<std::string> >&
+ statement_regex ();
+
+ void
+ statement_regex (const database_map<std::vector<std::string> >&);
+
+ bool
+ statement_regex_specified () const;
+
+ void
+ statement_regex_specified (bool);
+
+ const database_map<std::vector<std::string> >&
+ sql_name_regex () const;
+
+ database_map<std::vector<std::string> >&
+ sql_name_regex ();
+
+ void
+ sql_name_regex (const database_map<std::vector<std::string> >&);
+
+ bool
+ sql_name_regex_specified () const;
+
+ void
+ sql_name_regex_specified (bool);
+
+ const bool&
+ sql_name_regex_trace () const;
+
+ bool&
+ sql_name_regex_trace ();
+
+ void
+ sql_name_regex_trace (const bool&);
+
+ const std::vector<std::string>&
+ accessor_regex () const;
+
+ std::vector<std::string>&
+ accessor_regex ();
+
+ void
+ accessor_regex (const std::vector<std::string>&);
+
+ bool
+ accessor_regex_specified () const;
+
+ void
+ accessor_regex_specified (bool);
+
+ const bool&
+ accessor_regex_trace () const;
+
+ bool&
+ accessor_regex_trace ();
+
+ void
+ accessor_regex_trace (const bool&);
+
+ const std::vector<std::string>&
+ modifier_regex () const;
+
+ std::vector<std::string>&
+ modifier_regex ();
+
+ void
+ modifier_regex (const std::vector<std::string>&);
+
+ bool
+ modifier_regex_specified () const;
+
+ void
+ modifier_regex_specified (bool);
+
+ const bool&
+ modifier_regex_trace () const;
+
+ bool&
+ modifier_regex_trace ();
+
+ void
+ modifier_regex_trace (const bool&);
+
+ const bool&
+ include_with_brackets () const;
+
+ bool&
+ include_with_brackets ();
+
+ void
+ include_with_brackets (const bool&);
+
+ const std::string&
+ include_prefix () const;
+
+ std::string&
+ include_prefix ();
+
+ void
+ include_prefix (const std::string&);
+
+ bool
+ include_prefix_specified () const;
+
+ void
+ include_prefix_specified (bool);
+
+ const std::vector<std::string>&
+ include_regex () const;
+
+ std::vector<std::string>&
+ include_regex ();
+
+ void
+ include_regex (const std::vector<std::string>&);
+
+ bool
+ include_regex_specified () const;
+
+ void
+ include_regex_specified (bool);
+
+ const bool&
+ include_regex_trace () const;
+
+ bool&
+ include_regex_trace ();
+
+ void
+ include_regex_trace (const bool&);
+
+ const std::string&
+ guard_prefix () const;
+
+ std::string&
+ guard_prefix ();
+
+ void
+ guard_prefix (const std::string&);
+
+ bool
+ guard_prefix_specified () const;
+
+ void
+ guard_prefix_specified (bool);
+
+ const bool&
+ show_sloc () const;
+
+ bool&
+ show_sloc ();
+
+ void
+ show_sloc (const bool&);
+
+ const std::size_t&
+ sloc_limit () const;
+
+ std::size_t&
+ sloc_limit ();
+
+ void
+ sloc_limit (const std::size_t&);
+
+ bool
+ sloc_limit_specified () const;
+
+ void
+ sloc_limit_specified (bool);
+
+ const std::string&
+ options_file () const;
+
+ std::string&
+ options_file ();
+
+ void
+ options_file (const std::string&);
+
+ bool
+ options_file_specified () const;
+
+ void
+ options_file_specified (bool);
+
+ const std::vector<std::string>&
+ x () const;
+
+ std::vector<std::string>&
+ x ();
+
+ void
+ x (const std::vector<std::string>&);
+
+ bool
+ x_specified () const;
+
+ void
+ x_specified (bool);
+
+ const bool&
+ v () const;
+
+ bool&
+ v ();
+
+ void
+ v (const bool&);
+
+ const bool&
+ trace () const;
+
+ bool&
+ trace ();
+
+ void
+ trace (const bool&);
+
+ const std::string&
+ mysql_engine () const;
+
+ std::string&
+ mysql_engine ();
+
+ void
+ mysql_engine (const std::string&);
+
+ bool
+ mysql_engine_specified () const;
+
+ void
+ mysql_engine_specified (bool);
+
+ const bool&
+ sqlite_override_null () const;
+
+ bool&
+ sqlite_override_null ();
+
+ void
+ sqlite_override_null (const bool&);
+
+ const bool&
+ sqlite_lax_auto_id () const;
+
+ bool&
+ sqlite_lax_auto_id ();
+
+ void
+ sqlite_lax_auto_id (const bool&);
+
+ const ::pgsql_version&
+ pgsql_server_version () const;
+
+ ::pgsql_version&
+ pgsql_server_version ();
+
+ void
+ pgsql_server_version (const ::pgsql_version&);
+
+ bool
+ pgsql_server_version_specified () const;
+
+ void
+ pgsql_server_version_specified (bool);
+
+ const ::oracle_version&
+ oracle_client_version () const;
+
+ ::oracle_version&
+ oracle_client_version ();
+
+ void
+ oracle_client_version (const ::oracle_version&);
+
+ bool
+ oracle_client_version_specified () const;
+
+ void
+ oracle_client_version_specified (bool);
+
+ const bool&
+ oracle_warn_truncation () const;
+
+ bool&
+ oracle_warn_truncation ();
+
+ void
+ oracle_warn_truncation (const bool&);
+
+ const ::mssql_version&
+ mssql_server_version () const;
+
+ ::mssql_version&
+ mssql_server_version ();
+
+ void
+ mssql_server_version (const ::mssql_version&);
+
+ bool
+ mssql_server_version_specified () const;
+
+ void
+ mssql_server_version_specified (bool);
+
+ const unsigned int&
+ mssql_short_limit () const;
+
+ unsigned int&
+ mssql_short_limit ();
+
+ void
+ mssql_short_limit (const unsigned int&);
+
+ bool
+ mssql_short_limit_specified () const;
+
+ void
+ mssql_short_limit_specified (bool);
+
+ // Print usage information.
+ //
+ static ::cli::usage_para
+ print_usage (::std::ostream&,
+ ::cli::usage_para = ::cli::usage_para::none);
+
+ // Option description.
+ //
+ static const ::cli::options&
+ description ();
+
+ // Implementation details.
+ //
+ protected:
+ friend struct _cli_options_desc_type;
+
+ static void
+ fill (::cli::options&);
+
+ bool
+ _parse (const char*, ::cli::scanner&);
+
+ private:
+ bool
+ _parse (::cli::scanner&,
+ ::cli::unknown_mode option,
+ ::cli::unknown_mode argument);
+
+ public:
+ std::uint64_t build2_metadata_;
+ bool build2_metadata_specified_;
+ bool help_;
+ bool version_;
+ std::vector<std::string> I_;
+ bool I_specified_;
+ std::vector<std::string> D_;
+ bool D_specified_;
+ std::vector<std::string> U_;
+ bool U_specified_;
+ std::vector< ::database> database_;
+ bool database_specified_;
+ ::multi_database multi_database_;
+ bool multi_database_specified_;
+ ::database default_database_;
+ bool default_database_specified_;
+ bool generate_query_;
+ bool generate_prepared_;
+ bool omit_unprepared_;
+ bool generate_session_;
+ bool generate_schema_;
+ bool generate_schema_only_;
+ bool suppress_migration_;
+ bool suppress_schema_version_;
+ database_map<qname> schema_version_table_;
+ bool schema_version_table_specified_;
+ database_map<std::set< ::schema_format> > schema_format_;
+ bool schema_format_specified_;
+ bool omit_drop_;
+ bool omit_create_;
+ database_map<std::string> schema_name_;
+ bool schema_name_specified_;
+ database_map<deferrable> fkeys_deferrable_mode_;
+ bool fkeys_deferrable_mode_specified_;
+ std::string default_pointer_;
+ bool default_pointer_specified_;
+ std::string session_type_;
+ bool session_type_specified_;
+ std::string profile_;
+ bool profile_specified_;
+ bool at_once_;
+ database_map<qname> schema_;
+ bool schema_specified_;
+ database_map<std::string> export_symbol_;
+ bool export_symbol_specified_;
+ database_map<std::string> extern_symbol_;
+ bool extern_symbol_specified_;
+ cxx_version std_;
+ bool std_specified_;
+ bool warn_hard_add_;
+ bool warn_hard_delete_;
+ bool warn_hard_;
+ std::string output_dir_;
+ bool output_dir_specified_;
+ std::string input_name_;
+ bool input_name_specified_;
+ database_map<std::string> changelog_;
+ bool changelog_specified_;
+ database_map<std::string> changelog_in_;
+ bool changelog_in_specified_;
+ database_map<std::string> changelog_out_;
+ bool changelog_out_specified_;
+ database_map<std::string> changelog_dir_;
+ bool changelog_dir_specified_;
+ bool init_changelog_;
+ database_map<std::string> odb_file_suffix_;
+ bool odb_file_suffix_specified_;
+ database_map<std::string> sql_file_suffix_;
+ bool sql_file_suffix_specified_;
+ database_map<std::string> schema_file_suffix_;
+ bool schema_file_suffix_specified_;
+ database_map<std::string> changelog_file_suffix_;
+ bool changelog_file_suffix_specified_;
+ std::string hxx_suffix_;
+ bool hxx_suffix_specified_;
+ std::string ixx_suffix_;
+ bool ixx_suffix_specified_;
+ std::string cxx_suffix_;
+ bool cxx_suffix_specified_;
+ std::string sql_suffix_;
+ bool sql_suffix_specified_;
+ std::string changelog_suffix_;
+ bool changelog_suffix_specified_;
+ database_map<std::vector<std::string> > hxx_prologue_;
+ bool hxx_prologue_specified_;
+ database_map<std::vector<std::string> > ixx_prologue_;
+ bool ixx_prologue_specified_;
+ database_map<std::vector<std::string> > cxx_prologue_;
+ bool cxx_prologue_specified_;
+ database_map<std::vector<std::string> > schema_prologue_;
+ bool schema_prologue_specified_;
+ database_map<std::vector<std::string> > sql_prologue_;
+ bool sql_prologue_specified_;
+ database_map<std::vector<std::string> > migration_prologue_;
+ bool migration_prologue_specified_;
+ database_map<std::vector<std::string> > sql_interlude_;
+ bool sql_interlude_specified_;
+ database_map<std::vector<std::string> > hxx_epilogue_;
+ bool hxx_epilogue_specified_;
+ database_map<std::vector<std::string> > ixx_epilogue_;
+ bool ixx_epilogue_specified_;
+ database_map<std::vector<std::string> > cxx_epilogue_;
+ bool cxx_epilogue_specified_;
+ database_map<std::vector<std::string> > schema_epilogue_;
+ bool schema_epilogue_specified_;
+ database_map<std::vector<std::string> > sql_epilogue_;
+ bool sql_epilogue_specified_;
+ database_map<std::vector<std::string> > migration_epilogue_;
+ bool migration_epilogue_specified_;
+ database_map<std::vector<std::string> > hxx_prologue_file_;
+ bool hxx_prologue_file_specified_;
+ database_map<std::vector<std::string> > ixx_prologue_file_;
+ bool ixx_prologue_file_specified_;
+ database_map<std::vector<std::string> > cxx_prologue_file_;
+ bool cxx_prologue_file_specified_;
+ database_map<std::vector<std::string> > schema_prologue_file_;
+ bool schema_prologue_file_specified_;
+ database_map<std::vector<std::string> > sql_prologue_file_;
+ bool sql_prologue_file_specified_;
+ database_map<std::vector<std::string> > migration_prologue_file_;
+ bool migration_prologue_file_specified_;
+ database_map<std::vector<std::string> > sql_interlude_file_;
+ bool sql_interlude_file_specified_;
+ database_map<std::vector<std::string> > hxx_epilogue_file_;
+ bool hxx_epilogue_file_specified_;
+ database_map<std::vector<std::string> > ixx_epilogue_file_;
+ bool ixx_epilogue_file_specified_;
+ database_map<std::vector<std::string> > cxx_epilogue_file_;
+ bool cxx_epilogue_file_specified_;
+ database_map<std::vector<std::string> > schema_epilogue_file_;
+ bool schema_epilogue_file_specified_;
+ database_map<std::vector<std::string> > sql_epilogue_file_;
+ bool sql_epilogue_file_specified_;
+ database_map<std::vector<std::string> > migration_epilogue_file_;
+ bool migration_epilogue_file_specified_;
+ database_map<std::vector<std::string> > odb_prologue_;
+ bool odb_prologue_specified_;
+ database_map<std::vector<std::string> > odb_prologue_file_;
+ bool odb_prologue_file_specified_;
+ database_map<std::vector<std::string> > odb_epilogue_;
+ bool odb_epilogue_specified_;
+ database_map<std::vector<std::string> > odb_epilogue_file_;
+ bool odb_epilogue_file_specified_;
+ database_map<std::string> table_prefix_;
+ bool table_prefix_specified_;
+ database_map<std::string> index_suffix_;
+ bool index_suffix_specified_;
+ database_map<std::string> fkey_suffix_;
+ bool fkey_suffix_specified_;
+ database_map<std::string> sequence_suffix_;
+ bool sequence_suffix_specified_;
+ database_map<name_case> sql_name_case_;
+ bool sql_name_case_specified_;
+ database_map<std::vector<std::string> > table_regex_;
+ bool table_regex_specified_;
+ database_map<std::vector<std::string> > column_regex_;
+ bool column_regex_specified_;
+ database_map<std::vector<std::string> > index_regex_;
+ bool index_regex_specified_;
+ database_map<std::vector<std::string> > fkey_regex_;
+ bool fkey_regex_specified_;
+ database_map<std::vector<std::string> > sequence_regex_;
+ bool sequence_regex_specified_;
+ database_map<std::vector<std::string> > statement_regex_;
+ bool statement_regex_specified_;
+ database_map<std::vector<std::string> > sql_name_regex_;
+ bool sql_name_regex_specified_;
+ bool sql_name_regex_trace_;
+ std::vector<std::string> accessor_regex_;
+ bool accessor_regex_specified_;
+ bool accessor_regex_trace_;
+ std::vector<std::string> modifier_regex_;
+ bool modifier_regex_specified_;
+ bool modifier_regex_trace_;
+ bool include_with_brackets_;
+ std::string include_prefix_;
+ bool include_prefix_specified_;
+ std::vector<std::string> include_regex_;
+ bool include_regex_specified_;
+ bool include_regex_trace_;
+ std::string guard_prefix_;
+ bool guard_prefix_specified_;
+ bool show_sloc_;
+ std::size_t sloc_limit_;
+ bool sloc_limit_specified_;
+ std::string options_file_;
+ bool options_file_specified_;
+ std::vector<std::string> x_;
+ bool x_specified_;
+ bool v_;
+ bool trace_;
+ std::string mysql_engine_;
+ bool mysql_engine_specified_;
+ bool sqlite_override_null_;
+ bool sqlite_lax_auto_id_;
+ ::pgsql_version pgsql_server_version_;
+ bool pgsql_server_version_specified_;
+ ::oracle_version oracle_client_version_;
+ bool oracle_client_version_specified_;
+ bool oracle_warn_truncation_;
+ ::mssql_version mssql_server_version_;
+ bool mssql_server_version_specified_;
+ unsigned int mssql_short_limit_;
+ bool mssql_short_limit_specified_;
+};
+
+#include <odb/options.ixx>
+
+// Begin epilogue.
+//
+//
+// End epilogue.
+
+#endif // ODB_OPTIONS_HXX
diff --git a/odb/odb/pregenerated/odb/options.ixx b/odb/odb/pregenerated/odb/options.ixx
new file mode 100644
index 0000000..9a78a2e
--- /dev/null
+++ b/odb/odb/pregenerated/odb/options.ixx
@@ -0,0 +1,3471 @@
+// -*- C++ -*-
+//
+// This file was generated by CLI, a command line interface
+// compiler for C++.
+//
+
+// Begin prologue.
+//
+//
+// End prologue.
+
+#include <cassert>
+
+namespace cli
+{
+ // usage_para
+ //
+ inline usage_para::
+ usage_para (value v)
+ : v_ (v)
+ {
+ }
+
+ // unknown_mode
+ //
+ inline unknown_mode::
+ unknown_mode (value v)
+ : v_ (v)
+ {
+ }
+
+ // exception
+ //
+ inline ::std::ostream&
+ operator<< (::std::ostream& os, const exception& e)
+ {
+ e.print (os);
+ return os;
+ }
+
+ // unknown_option
+ //
+ inline unknown_option::
+ unknown_option (const std::string& option)
+ : option_ (option)
+ {
+ }
+
+ inline const std::string& unknown_option::
+ option () const
+ {
+ return option_;
+ }
+
+ // unknown_argument
+ //
+ inline unknown_argument::
+ unknown_argument (const std::string& argument)
+ : argument_ (argument)
+ {
+ }
+
+ inline const std::string& unknown_argument::
+ argument () const
+ {
+ return argument_;
+ }
+
+ // missing_value
+ //
+ inline missing_value::
+ missing_value (const std::string& option)
+ : option_ (option)
+ {
+ }
+
+ inline const std::string& missing_value::
+ option () const
+ {
+ return option_;
+ }
+
+ // invalid_value
+ //
+ inline invalid_value::
+ invalid_value (const std::string& option,
+ const std::string& value,
+ const std::string& message)
+ : option_ (option),
+ value_ (value),
+ message_ (message)
+ {
+ }
+
+ inline const std::string& invalid_value::
+ option () const
+ {
+ return option_;
+ }
+
+ inline const std::string& invalid_value::
+ value () const
+ {
+ return value_;
+ }
+
+ inline const std::string& invalid_value::
+ message () const
+ {
+ return message_;
+ }
+
+ // file_io_failure
+ //
+ inline file_io_failure::
+ file_io_failure (const std::string& file)
+ : file_ (file)
+ {
+ }
+
+ inline const std::string& file_io_failure::
+ file () const
+ {
+ return file_;
+ }
+
+ // unmatched_quote
+ //
+ inline unmatched_quote::
+ unmatched_quote (const std::string& argument)
+ : argument_ (argument)
+ {
+ }
+
+ inline const std::string& unmatched_quote::
+ argument () const
+ {
+ return argument_;
+ }
+
+ // argv_scanner
+ //
+ inline argv_scanner::
+ argv_scanner (int& argc,
+ char** argv,
+ bool erase,
+ std::size_t sp)
+ : start_position_ (sp + 1),
+ i_ (1),
+ argc_ (argc),
+ argv_ (argv),
+ erase_ (erase)
+ {
+ }
+
+ inline argv_scanner::
+ argv_scanner (int start,
+ int& argc,
+ char** argv,
+ bool erase,
+ std::size_t sp)
+ : start_position_ (sp + static_cast<std::size_t> (start)),
+ i_ (start),
+ argc_ (argc),
+ argv_ (argv),
+ erase_ (erase)
+ {
+ }
+
+ inline int argv_scanner::
+ end () const
+ {
+ return i_;
+ }
+
+ // argv_file_scanner
+ //
+ inline argv_file_scanner::
+ argv_file_scanner (int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (argc, argv, erase, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const std::string& option,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (start, argc, argv, erase, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (const std::string& file,
+ const std::string& option,
+ std::size_t sp)
+ : argv_scanner (0, zero_argc_, 0, sp),
+ option_ (option),
+ options_ (&option_info_),
+ options_count_ (1),
+ i_ (1),
+ skip_ (false)
+ {
+ option_info_.option = option_.c_str ();
+ option_info_.search_func = 0;
+
+ load (file);
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (argc, argv, erase, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (int start,
+ int& argc,
+ char** argv,
+ const option_info* options,
+ std::size_t options_count,
+ bool erase,
+ std::size_t sp)
+ : argv_scanner (start, argc, argv, erase, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ }
+
+ inline argv_file_scanner::
+ argv_file_scanner (const std::string& file,
+ const option_info* options,
+ std::size_t options_count,
+ std::size_t sp)
+ : argv_scanner (0, zero_argc_, 0, sp),
+ options_ (options),
+ options_count_ (options_count),
+ i_ (1),
+ skip_ (false)
+ {
+ load (file);
+ }
+
+ inline const std::string& option::
+ name () const
+ {
+ return name_;
+ }
+
+ inline const option_names& option::
+ aliases () const
+ {
+ return aliases_;
+ }
+
+ inline bool option::
+ flag () const
+ {
+ return flag_;
+ }
+
+ inline const std::string& option::
+ default_value () const
+ {
+ return default_value_;
+ }
+
+ inline option::
+ option ()
+ {
+ }
+
+ inline option::
+ option (const std::string& n,
+ const option_names& a,
+ bool f,
+ const std::string& dv)
+ : name_ (n), aliases_ (a), flag_ (f), default_value_ (dv)
+ {
+ }
+
+ inline options::container_type::const_iterator options::
+ find (const std::string& name) const
+ {
+ map_type::const_iterator i (map_.find (name));
+ return i != map_.end () ? begin () + i->second : end ();
+ }
+}
+
+// options
+//
+
+inline const std::uint64_t& options::
+build2_metadata () const
+{
+ return this->build2_metadata_;
+}
+
+inline std::uint64_t& options::
+build2_metadata ()
+{
+ return this->build2_metadata_;
+}
+
+inline void options::
+build2_metadata (const std::uint64_t& x)
+{
+ this->build2_metadata_ = x;
+}
+
+inline bool options::
+build2_metadata_specified () const
+{
+ return this->build2_metadata_specified_;
+}
+
+inline void options::
+build2_metadata_specified (bool x)
+{
+ this->build2_metadata_specified_ = x;
+}
+
+inline const bool& options::
+help () const
+{
+ return this->help_;
+}
+
+inline bool& options::
+help ()
+{
+ return this->help_;
+}
+
+inline void options::
+help (const bool& x)
+{
+ this->help_ = x;
+}
+
+inline const bool& options::
+version () const
+{
+ return this->version_;
+}
+
+inline bool& options::
+version ()
+{
+ return this->version_;
+}
+
+inline void options::
+version (const bool& x)
+{
+ this->version_ = x;
+}
+
+inline const std::vector<std::string>& options::
+I () const
+{
+ return this->I_;
+}
+
+inline std::vector<std::string>& options::
+I ()
+{
+ return this->I_;
+}
+
+inline void options::
+I (const std::vector<std::string>& x)
+{
+ this->I_ = x;
+}
+
+inline bool options::
+I_specified () const
+{
+ return this->I_specified_;
+}
+
+inline void options::
+I_specified (bool x)
+{
+ this->I_specified_ = x;
+}
+
+inline const std::vector<std::string>& options::
+D () const
+{
+ return this->D_;
+}
+
+inline std::vector<std::string>& options::
+D ()
+{
+ return this->D_;
+}
+
+inline void options::
+D (const std::vector<std::string>& x)
+{
+ this->D_ = x;
+}
+
+inline bool options::
+D_specified () const
+{
+ return this->D_specified_;
+}
+
+inline void options::
+D_specified (bool x)
+{
+ this->D_specified_ = x;
+}
+
+inline const std::vector<std::string>& options::
+U () const
+{
+ return this->U_;
+}
+
+inline std::vector<std::string>& options::
+U ()
+{
+ return this->U_;
+}
+
+inline void options::
+U (const std::vector<std::string>& x)
+{
+ this->U_ = x;
+}
+
+inline bool options::
+U_specified () const
+{
+ return this->U_specified_;
+}
+
+inline void options::
+U_specified (bool x)
+{
+ this->U_specified_ = x;
+}
+
+inline const std::vector< ::database>& options::
+database () const
+{
+ return this->database_;
+}
+
+inline std::vector< ::database>& options::
+database ()
+{
+ return this->database_;
+}
+
+inline void options::
+database (const std::vector< ::database>& x)
+{
+ this->database_ = x;
+}
+
+inline bool options::
+database_specified () const
+{
+ return this->database_specified_;
+}
+
+inline void options::
+database_specified (bool x)
+{
+ this->database_specified_ = x;
+}
+
+inline const ::multi_database& options::
+multi_database () const
+{
+ return this->multi_database_;
+}
+
+inline ::multi_database& options::
+multi_database ()
+{
+ return this->multi_database_;
+}
+
+inline void options::
+multi_database (const ::multi_database& x)
+{
+ this->multi_database_ = x;
+}
+
+inline bool options::
+multi_database_specified () const
+{
+ return this->multi_database_specified_;
+}
+
+inline void options::
+multi_database_specified (bool x)
+{
+ this->multi_database_specified_ = x;
+}
+
+inline const ::database& options::
+default_database () const
+{
+ return this->default_database_;
+}
+
+inline ::database& options::
+default_database ()
+{
+ return this->default_database_;
+}
+
+inline void options::
+default_database (const ::database& x)
+{
+ this->default_database_ = x;
+}
+
+inline bool options::
+default_database_specified () const
+{
+ return this->default_database_specified_;
+}
+
+inline void options::
+default_database_specified (bool x)
+{
+ this->default_database_specified_ = x;
+}
+
+inline const bool& options::
+generate_query () const
+{
+ return this->generate_query_;
+}
+
+inline bool& options::
+generate_query ()
+{
+ return this->generate_query_;
+}
+
+inline void options::
+generate_query (const bool& x)
+{
+ this->generate_query_ = x;
+}
+
+inline const bool& options::
+generate_prepared () const
+{
+ return this->generate_prepared_;
+}
+
+inline bool& options::
+generate_prepared ()
+{
+ return this->generate_prepared_;
+}
+
+inline void options::
+generate_prepared (const bool& x)
+{
+ this->generate_prepared_ = x;
+}
+
+inline const bool& options::
+omit_unprepared () const
+{
+ return this->omit_unprepared_;
+}
+
+inline bool& options::
+omit_unprepared ()
+{
+ return this->omit_unprepared_;
+}
+
+inline void options::
+omit_unprepared (const bool& x)
+{
+ this->omit_unprepared_ = x;
+}
+
+inline const bool& options::
+generate_session () const
+{
+ return this->generate_session_;
+}
+
+inline bool& options::
+generate_session ()
+{
+ return this->generate_session_;
+}
+
+inline void options::
+generate_session (const bool& x)
+{
+ this->generate_session_ = x;
+}
+
+inline const bool& options::
+generate_schema () const
+{
+ return this->generate_schema_;
+}
+
+inline bool& options::
+generate_schema ()
+{
+ return this->generate_schema_;
+}
+
+inline void options::
+generate_schema (const bool& x)
+{
+ this->generate_schema_ = x;
+}
+
+inline const bool& options::
+generate_schema_only () const
+{
+ return this->generate_schema_only_;
+}
+
+inline bool& options::
+generate_schema_only ()
+{
+ return this->generate_schema_only_;
+}
+
+inline void options::
+generate_schema_only (const bool& x)
+{
+ this->generate_schema_only_ = x;
+}
+
+inline const bool& options::
+suppress_migration () const
+{
+ return this->suppress_migration_;
+}
+
+inline bool& options::
+suppress_migration ()
+{
+ return this->suppress_migration_;
+}
+
+inline void options::
+suppress_migration (const bool& x)
+{
+ this->suppress_migration_ = x;
+}
+
+inline const bool& options::
+suppress_schema_version () const
+{
+ return this->suppress_schema_version_;
+}
+
+inline bool& options::
+suppress_schema_version ()
+{
+ return this->suppress_schema_version_;
+}
+
+inline void options::
+suppress_schema_version (const bool& x)
+{
+ this->suppress_schema_version_ = x;
+}
+
+inline const database_map<qname>& options::
+schema_version_table () const
+{
+ return this->schema_version_table_;
+}
+
+inline database_map<qname>& options::
+schema_version_table ()
+{
+ return this->schema_version_table_;
+}
+
+inline void options::
+schema_version_table (const database_map<qname>& x)
+{
+ this->schema_version_table_ = x;
+}
+
+inline bool options::
+schema_version_table_specified () const
+{
+ return this->schema_version_table_specified_;
+}
+
+inline void options::
+schema_version_table_specified (bool x)
+{
+ this->schema_version_table_specified_ = x;
+}
+
+inline const database_map<std::set< ::schema_format> >& options::
+schema_format () const
+{
+ return this->schema_format_;
+}
+
+inline database_map<std::set< ::schema_format> >& options::
+schema_format ()
+{
+ return this->schema_format_;
+}
+
+inline void options::
+schema_format (const database_map<std::set< ::schema_format> >& x)
+{
+ this->schema_format_ = x;
+}
+
+inline bool options::
+schema_format_specified () const
+{
+ return this->schema_format_specified_;
+}
+
+inline void options::
+schema_format_specified (bool x)
+{
+ this->schema_format_specified_ = x;
+}
+
+inline const bool& options::
+omit_drop () const
+{
+ return this->omit_drop_;
+}
+
+inline bool& options::
+omit_drop ()
+{
+ return this->omit_drop_;
+}
+
+inline void options::
+omit_drop (const bool& x)
+{
+ this->omit_drop_ = x;
+}
+
+inline const bool& options::
+omit_create () const
+{
+ return this->omit_create_;
+}
+
+inline bool& options::
+omit_create ()
+{
+ return this->omit_create_;
+}
+
+inline void options::
+omit_create (const bool& x)
+{
+ this->omit_create_ = x;
+}
+
+inline const database_map<std::string>& options::
+schema_name () const
+{
+ return this->schema_name_;
+}
+
+inline database_map<std::string>& options::
+schema_name ()
+{
+ return this->schema_name_;
+}
+
+inline void options::
+schema_name (const database_map<std::string>& x)
+{
+ this->schema_name_ = x;
+}
+
+inline bool options::
+schema_name_specified () const
+{
+ return this->schema_name_specified_;
+}
+
+inline void options::
+schema_name_specified (bool x)
+{
+ this->schema_name_specified_ = x;
+}
+
+inline const database_map<deferrable>& options::
+fkeys_deferrable_mode () const
+{
+ return this->fkeys_deferrable_mode_;
+}
+
+inline database_map<deferrable>& options::
+fkeys_deferrable_mode ()
+{
+ return this->fkeys_deferrable_mode_;
+}
+
+inline void options::
+fkeys_deferrable_mode (const database_map<deferrable>& x)
+{
+ this->fkeys_deferrable_mode_ = x;
+}
+
+inline bool options::
+fkeys_deferrable_mode_specified () const
+{
+ return this->fkeys_deferrable_mode_specified_;
+}
+
+inline void options::
+fkeys_deferrable_mode_specified (bool x)
+{
+ this->fkeys_deferrable_mode_specified_ = x;
+}
+
+inline const std::string& options::
+default_pointer () const
+{
+ return this->default_pointer_;
+}
+
+inline std::string& options::
+default_pointer ()
+{
+ return this->default_pointer_;
+}
+
+inline void options::
+default_pointer (const std::string& x)
+{
+ this->default_pointer_ = x;
+}
+
+inline bool options::
+default_pointer_specified () const
+{
+ return this->default_pointer_specified_;
+}
+
+inline void options::
+default_pointer_specified (bool x)
+{
+ this->default_pointer_specified_ = x;
+}
+
+inline const std::string& options::
+session_type () const
+{
+ return this->session_type_;
+}
+
+inline std::string& options::
+session_type ()
+{
+ return this->session_type_;
+}
+
+inline void options::
+session_type (const std::string& x)
+{
+ this->session_type_ = x;
+}
+
+inline bool options::
+session_type_specified () const
+{
+ return this->session_type_specified_;
+}
+
+inline void options::
+session_type_specified (bool x)
+{
+ this->session_type_specified_ = x;
+}
+
+inline const std::string& options::
+profile () const
+{
+ return this->profile_;
+}
+
+inline std::string& options::
+profile ()
+{
+ return this->profile_;
+}
+
+inline void options::
+profile (const std::string& x)
+{
+ this->profile_ = x;
+}
+
+inline bool options::
+profile_specified () const
+{
+ return this->profile_specified_;
+}
+
+inline void options::
+profile_specified (bool x)
+{
+ this->profile_specified_ = x;
+}
+
+inline const bool& options::
+at_once () const
+{
+ return this->at_once_;
+}
+
+inline bool& options::
+at_once ()
+{
+ return this->at_once_;
+}
+
+inline void options::
+at_once (const bool& x)
+{
+ this->at_once_ = x;
+}
+
+inline const database_map<qname>& options::
+schema () const
+{
+ return this->schema_;
+}
+
+inline database_map<qname>& options::
+schema ()
+{
+ return this->schema_;
+}
+
+inline void options::
+schema (const database_map<qname>& x)
+{
+ this->schema_ = x;
+}
+
+inline bool options::
+schema_specified () const
+{
+ return this->schema_specified_;
+}
+
+inline void options::
+schema_specified (bool x)
+{
+ this->schema_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+export_symbol () const
+{
+ return this->export_symbol_;
+}
+
+inline database_map<std::string>& options::
+export_symbol ()
+{
+ return this->export_symbol_;
+}
+
+inline void options::
+export_symbol (const database_map<std::string>& x)
+{
+ this->export_symbol_ = x;
+}
+
+inline bool options::
+export_symbol_specified () const
+{
+ return this->export_symbol_specified_;
+}
+
+inline void options::
+export_symbol_specified (bool x)
+{
+ this->export_symbol_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+extern_symbol () const
+{
+ return this->extern_symbol_;
+}
+
+inline database_map<std::string>& options::
+extern_symbol ()
+{
+ return this->extern_symbol_;
+}
+
+inline void options::
+extern_symbol (const database_map<std::string>& x)
+{
+ this->extern_symbol_ = x;
+}
+
+inline bool options::
+extern_symbol_specified () const
+{
+ return this->extern_symbol_specified_;
+}
+
+inline void options::
+extern_symbol_specified (bool x)
+{
+ this->extern_symbol_specified_ = x;
+}
+
+inline const cxx_version& options::
+std () const
+{
+ return this->std_;
+}
+
+inline cxx_version& options::
+std ()
+{
+ return this->std_;
+}
+
+inline void options::
+std (const cxx_version& x)
+{
+ this->std_ = x;
+}
+
+inline bool options::
+std_specified () const
+{
+ return this->std_specified_;
+}
+
+inline void options::
+std_specified (bool x)
+{
+ this->std_specified_ = x;
+}
+
+inline const bool& options::
+warn_hard_add () const
+{
+ return this->warn_hard_add_;
+}
+
+inline bool& options::
+warn_hard_add ()
+{
+ return this->warn_hard_add_;
+}
+
+inline void options::
+warn_hard_add (const bool& x)
+{
+ this->warn_hard_add_ = x;
+}
+
+inline const bool& options::
+warn_hard_delete () const
+{
+ return this->warn_hard_delete_;
+}
+
+inline bool& options::
+warn_hard_delete ()
+{
+ return this->warn_hard_delete_;
+}
+
+inline void options::
+warn_hard_delete (const bool& x)
+{
+ this->warn_hard_delete_ = x;
+}
+
+inline const bool& options::
+warn_hard () const
+{
+ return this->warn_hard_;
+}
+
+inline bool& options::
+warn_hard ()
+{
+ return this->warn_hard_;
+}
+
+inline void options::
+warn_hard (const bool& x)
+{
+ this->warn_hard_ = x;
+}
+
+inline const std::string& options::
+output_dir () const
+{
+ return this->output_dir_;
+}
+
+inline std::string& options::
+output_dir ()
+{
+ return this->output_dir_;
+}
+
+inline void options::
+output_dir (const std::string& x)
+{
+ this->output_dir_ = x;
+}
+
+inline bool options::
+output_dir_specified () const
+{
+ return this->output_dir_specified_;
+}
+
+inline void options::
+output_dir_specified (bool x)
+{
+ this->output_dir_specified_ = x;
+}
+
+inline const std::string& options::
+input_name () const
+{
+ return this->input_name_;
+}
+
+inline std::string& options::
+input_name ()
+{
+ return this->input_name_;
+}
+
+inline void options::
+input_name (const std::string& x)
+{
+ this->input_name_ = x;
+}
+
+inline bool options::
+input_name_specified () const
+{
+ return this->input_name_specified_;
+}
+
+inline void options::
+input_name_specified (bool x)
+{
+ this->input_name_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+changelog () const
+{
+ return this->changelog_;
+}
+
+inline database_map<std::string>& options::
+changelog ()
+{
+ return this->changelog_;
+}
+
+inline void options::
+changelog (const database_map<std::string>& x)
+{
+ this->changelog_ = x;
+}
+
+inline bool options::
+changelog_specified () const
+{
+ return this->changelog_specified_;
+}
+
+inline void options::
+changelog_specified (bool x)
+{
+ this->changelog_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+changelog_in () const
+{
+ return this->changelog_in_;
+}
+
+inline database_map<std::string>& options::
+changelog_in ()
+{
+ return this->changelog_in_;
+}
+
+inline void options::
+changelog_in (const database_map<std::string>& x)
+{
+ this->changelog_in_ = x;
+}
+
+inline bool options::
+changelog_in_specified () const
+{
+ return this->changelog_in_specified_;
+}
+
+inline void options::
+changelog_in_specified (bool x)
+{
+ this->changelog_in_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+changelog_out () const
+{
+ return this->changelog_out_;
+}
+
+inline database_map<std::string>& options::
+changelog_out ()
+{
+ return this->changelog_out_;
+}
+
+inline void options::
+changelog_out (const database_map<std::string>& x)
+{
+ this->changelog_out_ = x;
+}
+
+inline bool options::
+changelog_out_specified () const
+{
+ return this->changelog_out_specified_;
+}
+
+inline void options::
+changelog_out_specified (bool x)
+{
+ this->changelog_out_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+changelog_dir () const
+{
+ return this->changelog_dir_;
+}
+
+inline database_map<std::string>& options::
+changelog_dir ()
+{
+ return this->changelog_dir_;
+}
+
+inline void options::
+changelog_dir (const database_map<std::string>& x)
+{
+ this->changelog_dir_ = x;
+}
+
+inline bool options::
+changelog_dir_specified () const
+{
+ return this->changelog_dir_specified_;
+}
+
+inline void options::
+changelog_dir_specified (bool x)
+{
+ this->changelog_dir_specified_ = x;
+}
+
+inline const bool& options::
+init_changelog () const
+{
+ return this->init_changelog_;
+}
+
+inline bool& options::
+init_changelog ()
+{
+ return this->init_changelog_;
+}
+
+inline void options::
+init_changelog (const bool& x)
+{
+ this->init_changelog_ = x;
+}
+
+inline const database_map<std::string>& options::
+odb_file_suffix () const
+{
+ return this->odb_file_suffix_;
+}
+
+inline database_map<std::string>& options::
+odb_file_suffix ()
+{
+ return this->odb_file_suffix_;
+}
+
+inline void options::
+odb_file_suffix (const database_map<std::string>& x)
+{
+ this->odb_file_suffix_ = x;
+}
+
+inline bool options::
+odb_file_suffix_specified () const
+{
+ return this->odb_file_suffix_specified_;
+}
+
+inline void options::
+odb_file_suffix_specified (bool x)
+{
+ this->odb_file_suffix_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+sql_file_suffix () const
+{
+ return this->sql_file_suffix_;
+}
+
+inline database_map<std::string>& options::
+sql_file_suffix ()
+{
+ return this->sql_file_suffix_;
+}
+
+inline void options::
+sql_file_suffix (const database_map<std::string>& x)
+{
+ this->sql_file_suffix_ = x;
+}
+
+inline bool options::
+sql_file_suffix_specified () const
+{
+ return this->sql_file_suffix_specified_;
+}
+
+inline void options::
+sql_file_suffix_specified (bool x)
+{
+ this->sql_file_suffix_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+schema_file_suffix () const
+{
+ return this->schema_file_suffix_;
+}
+
+inline database_map<std::string>& options::
+schema_file_suffix ()
+{
+ return this->schema_file_suffix_;
+}
+
+inline void options::
+schema_file_suffix (const database_map<std::string>& x)
+{
+ this->schema_file_suffix_ = x;
+}
+
+inline bool options::
+schema_file_suffix_specified () const
+{
+ return this->schema_file_suffix_specified_;
+}
+
+inline void options::
+schema_file_suffix_specified (bool x)
+{
+ this->schema_file_suffix_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+changelog_file_suffix () const
+{
+ return this->changelog_file_suffix_;
+}
+
+inline database_map<std::string>& options::
+changelog_file_suffix ()
+{
+ return this->changelog_file_suffix_;
+}
+
+inline void options::
+changelog_file_suffix (const database_map<std::string>& x)
+{
+ this->changelog_file_suffix_ = x;
+}
+
+inline bool options::
+changelog_file_suffix_specified () const
+{
+ return this->changelog_file_suffix_specified_;
+}
+
+inline void options::
+changelog_file_suffix_specified (bool x)
+{
+ this->changelog_file_suffix_specified_ = x;
+}
+
+inline const std::string& options::
+hxx_suffix () const
+{
+ return this->hxx_suffix_;
+}
+
+inline std::string& options::
+hxx_suffix ()
+{
+ return this->hxx_suffix_;
+}
+
+inline void options::
+hxx_suffix (const std::string& x)
+{
+ this->hxx_suffix_ = x;
+}
+
+inline bool options::
+hxx_suffix_specified () const
+{
+ return this->hxx_suffix_specified_;
+}
+
+inline void options::
+hxx_suffix_specified (bool x)
+{
+ this->hxx_suffix_specified_ = x;
+}
+
+inline const std::string& options::
+ixx_suffix () const
+{
+ return this->ixx_suffix_;
+}
+
+inline std::string& options::
+ixx_suffix ()
+{
+ return this->ixx_suffix_;
+}
+
+inline void options::
+ixx_suffix (const std::string& x)
+{
+ this->ixx_suffix_ = x;
+}
+
+inline bool options::
+ixx_suffix_specified () const
+{
+ return this->ixx_suffix_specified_;
+}
+
+inline void options::
+ixx_suffix_specified (bool x)
+{
+ this->ixx_suffix_specified_ = x;
+}
+
+inline const std::string& options::
+cxx_suffix () const
+{
+ return this->cxx_suffix_;
+}
+
+inline std::string& options::
+cxx_suffix ()
+{
+ return this->cxx_suffix_;
+}
+
+inline void options::
+cxx_suffix (const std::string& x)
+{
+ this->cxx_suffix_ = x;
+}
+
+inline bool options::
+cxx_suffix_specified () const
+{
+ return this->cxx_suffix_specified_;
+}
+
+inline void options::
+cxx_suffix_specified (bool x)
+{
+ this->cxx_suffix_specified_ = x;
+}
+
+inline const std::string& options::
+sql_suffix () const
+{
+ return this->sql_suffix_;
+}
+
+inline std::string& options::
+sql_suffix ()
+{
+ return this->sql_suffix_;
+}
+
+inline void options::
+sql_suffix (const std::string& x)
+{
+ this->sql_suffix_ = x;
+}
+
+inline bool options::
+sql_suffix_specified () const
+{
+ return this->sql_suffix_specified_;
+}
+
+inline void options::
+sql_suffix_specified (bool x)
+{
+ this->sql_suffix_specified_ = x;
+}
+
+inline const std::string& options::
+changelog_suffix () const
+{
+ return this->changelog_suffix_;
+}
+
+inline std::string& options::
+changelog_suffix ()
+{
+ return this->changelog_suffix_;
+}
+
+inline void options::
+changelog_suffix (const std::string& x)
+{
+ this->changelog_suffix_ = x;
+}
+
+inline bool options::
+changelog_suffix_specified () const
+{
+ return this->changelog_suffix_specified_;
+}
+
+inline void options::
+changelog_suffix_specified (bool x)
+{
+ this->changelog_suffix_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+hxx_prologue () const
+{
+ return this->hxx_prologue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+hxx_prologue ()
+{
+ return this->hxx_prologue_;
+}
+
+inline void options::
+hxx_prologue (const database_map<std::vector<std::string> >& x)
+{
+ this->hxx_prologue_ = x;
+}
+
+inline bool options::
+hxx_prologue_specified () const
+{
+ return this->hxx_prologue_specified_;
+}
+
+inline void options::
+hxx_prologue_specified (bool x)
+{
+ this->hxx_prologue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+ixx_prologue () const
+{
+ return this->ixx_prologue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+ixx_prologue ()
+{
+ return this->ixx_prologue_;
+}
+
+inline void options::
+ixx_prologue (const database_map<std::vector<std::string> >& x)
+{
+ this->ixx_prologue_ = x;
+}
+
+inline bool options::
+ixx_prologue_specified () const
+{
+ return this->ixx_prologue_specified_;
+}
+
+inline void options::
+ixx_prologue_specified (bool x)
+{
+ this->ixx_prologue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+cxx_prologue () const
+{
+ return this->cxx_prologue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+cxx_prologue ()
+{
+ return this->cxx_prologue_;
+}
+
+inline void options::
+cxx_prologue (const database_map<std::vector<std::string> >& x)
+{
+ this->cxx_prologue_ = x;
+}
+
+inline bool options::
+cxx_prologue_specified () const
+{
+ return this->cxx_prologue_specified_;
+}
+
+inline void options::
+cxx_prologue_specified (bool x)
+{
+ this->cxx_prologue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+schema_prologue () const
+{
+ return this->schema_prologue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+schema_prologue ()
+{
+ return this->schema_prologue_;
+}
+
+inline void options::
+schema_prologue (const database_map<std::vector<std::string> >& x)
+{
+ this->schema_prologue_ = x;
+}
+
+inline bool options::
+schema_prologue_specified () const
+{
+ return this->schema_prologue_specified_;
+}
+
+inline void options::
+schema_prologue_specified (bool x)
+{
+ this->schema_prologue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+sql_prologue () const
+{
+ return this->sql_prologue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+sql_prologue ()
+{
+ return this->sql_prologue_;
+}
+
+inline void options::
+sql_prologue (const database_map<std::vector<std::string> >& x)
+{
+ this->sql_prologue_ = x;
+}
+
+inline bool options::
+sql_prologue_specified () const
+{
+ return this->sql_prologue_specified_;
+}
+
+inline void options::
+sql_prologue_specified (bool x)
+{
+ this->sql_prologue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+migration_prologue () const
+{
+ return this->migration_prologue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+migration_prologue ()
+{
+ return this->migration_prologue_;
+}
+
+inline void options::
+migration_prologue (const database_map<std::vector<std::string> >& x)
+{
+ this->migration_prologue_ = x;
+}
+
+inline bool options::
+migration_prologue_specified () const
+{
+ return this->migration_prologue_specified_;
+}
+
+inline void options::
+migration_prologue_specified (bool x)
+{
+ this->migration_prologue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+sql_interlude () const
+{
+ return this->sql_interlude_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+sql_interlude ()
+{
+ return this->sql_interlude_;
+}
+
+inline void options::
+sql_interlude (const database_map<std::vector<std::string> >& x)
+{
+ this->sql_interlude_ = x;
+}
+
+inline bool options::
+sql_interlude_specified () const
+{
+ return this->sql_interlude_specified_;
+}
+
+inline void options::
+sql_interlude_specified (bool x)
+{
+ this->sql_interlude_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+hxx_epilogue () const
+{
+ return this->hxx_epilogue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+hxx_epilogue ()
+{
+ return this->hxx_epilogue_;
+}
+
+inline void options::
+hxx_epilogue (const database_map<std::vector<std::string> >& x)
+{
+ this->hxx_epilogue_ = x;
+}
+
+inline bool options::
+hxx_epilogue_specified () const
+{
+ return this->hxx_epilogue_specified_;
+}
+
+inline void options::
+hxx_epilogue_specified (bool x)
+{
+ this->hxx_epilogue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+ixx_epilogue () const
+{
+ return this->ixx_epilogue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+ixx_epilogue ()
+{
+ return this->ixx_epilogue_;
+}
+
+inline void options::
+ixx_epilogue (const database_map<std::vector<std::string> >& x)
+{
+ this->ixx_epilogue_ = x;
+}
+
+inline bool options::
+ixx_epilogue_specified () const
+{
+ return this->ixx_epilogue_specified_;
+}
+
+inline void options::
+ixx_epilogue_specified (bool x)
+{
+ this->ixx_epilogue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+cxx_epilogue () const
+{
+ return this->cxx_epilogue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+cxx_epilogue ()
+{
+ return this->cxx_epilogue_;
+}
+
+inline void options::
+cxx_epilogue (const database_map<std::vector<std::string> >& x)
+{
+ this->cxx_epilogue_ = x;
+}
+
+inline bool options::
+cxx_epilogue_specified () const
+{
+ return this->cxx_epilogue_specified_;
+}
+
+inline void options::
+cxx_epilogue_specified (bool x)
+{
+ this->cxx_epilogue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+schema_epilogue () const
+{
+ return this->schema_epilogue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+schema_epilogue ()
+{
+ return this->schema_epilogue_;
+}
+
+inline void options::
+schema_epilogue (const database_map<std::vector<std::string> >& x)
+{
+ this->schema_epilogue_ = x;
+}
+
+inline bool options::
+schema_epilogue_specified () const
+{
+ return this->schema_epilogue_specified_;
+}
+
+inline void options::
+schema_epilogue_specified (bool x)
+{
+ this->schema_epilogue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+sql_epilogue () const
+{
+ return this->sql_epilogue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+sql_epilogue ()
+{
+ return this->sql_epilogue_;
+}
+
+inline void options::
+sql_epilogue (const database_map<std::vector<std::string> >& x)
+{
+ this->sql_epilogue_ = x;
+}
+
+inline bool options::
+sql_epilogue_specified () const
+{
+ return this->sql_epilogue_specified_;
+}
+
+inline void options::
+sql_epilogue_specified (bool x)
+{
+ this->sql_epilogue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+migration_epilogue () const
+{
+ return this->migration_epilogue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+migration_epilogue ()
+{
+ return this->migration_epilogue_;
+}
+
+inline void options::
+migration_epilogue (const database_map<std::vector<std::string> >& x)
+{
+ this->migration_epilogue_ = x;
+}
+
+inline bool options::
+migration_epilogue_specified () const
+{
+ return this->migration_epilogue_specified_;
+}
+
+inline void options::
+migration_epilogue_specified (bool x)
+{
+ this->migration_epilogue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+hxx_prologue_file () const
+{
+ return this->hxx_prologue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+hxx_prologue_file ()
+{
+ return this->hxx_prologue_file_;
+}
+
+inline void options::
+hxx_prologue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->hxx_prologue_file_ = x;
+}
+
+inline bool options::
+hxx_prologue_file_specified () const
+{
+ return this->hxx_prologue_file_specified_;
+}
+
+inline void options::
+hxx_prologue_file_specified (bool x)
+{
+ this->hxx_prologue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+ixx_prologue_file () const
+{
+ return this->ixx_prologue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+ixx_prologue_file ()
+{
+ return this->ixx_prologue_file_;
+}
+
+inline void options::
+ixx_prologue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->ixx_prologue_file_ = x;
+}
+
+inline bool options::
+ixx_prologue_file_specified () const
+{
+ return this->ixx_prologue_file_specified_;
+}
+
+inline void options::
+ixx_prologue_file_specified (bool x)
+{
+ this->ixx_prologue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+cxx_prologue_file () const
+{
+ return this->cxx_prologue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+cxx_prologue_file ()
+{
+ return this->cxx_prologue_file_;
+}
+
+inline void options::
+cxx_prologue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->cxx_prologue_file_ = x;
+}
+
+inline bool options::
+cxx_prologue_file_specified () const
+{
+ return this->cxx_prologue_file_specified_;
+}
+
+inline void options::
+cxx_prologue_file_specified (bool x)
+{
+ this->cxx_prologue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+schema_prologue_file () const
+{
+ return this->schema_prologue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+schema_prologue_file ()
+{
+ return this->schema_prologue_file_;
+}
+
+inline void options::
+schema_prologue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->schema_prologue_file_ = x;
+}
+
+inline bool options::
+schema_prologue_file_specified () const
+{
+ return this->schema_prologue_file_specified_;
+}
+
+inline void options::
+schema_prologue_file_specified (bool x)
+{
+ this->schema_prologue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+sql_prologue_file () const
+{
+ return this->sql_prologue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+sql_prologue_file ()
+{
+ return this->sql_prologue_file_;
+}
+
+inline void options::
+sql_prologue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->sql_prologue_file_ = x;
+}
+
+inline bool options::
+sql_prologue_file_specified () const
+{
+ return this->sql_prologue_file_specified_;
+}
+
+inline void options::
+sql_prologue_file_specified (bool x)
+{
+ this->sql_prologue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+migration_prologue_file () const
+{
+ return this->migration_prologue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+migration_prologue_file ()
+{
+ return this->migration_prologue_file_;
+}
+
+inline void options::
+migration_prologue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->migration_prologue_file_ = x;
+}
+
+inline bool options::
+migration_prologue_file_specified () const
+{
+ return this->migration_prologue_file_specified_;
+}
+
+inline void options::
+migration_prologue_file_specified (bool x)
+{
+ this->migration_prologue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+sql_interlude_file () const
+{
+ return this->sql_interlude_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+sql_interlude_file ()
+{
+ return this->sql_interlude_file_;
+}
+
+inline void options::
+sql_interlude_file (const database_map<std::vector<std::string> >& x)
+{
+ this->sql_interlude_file_ = x;
+}
+
+inline bool options::
+sql_interlude_file_specified () const
+{
+ return this->sql_interlude_file_specified_;
+}
+
+inline void options::
+sql_interlude_file_specified (bool x)
+{
+ this->sql_interlude_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+hxx_epilogue_file () const
+{
+ return this->hxx_epilogue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+hxx_epilogue_file ()
+{
+ return this->hxx_epilogue_file_;
+}
+
+inline void options::
+hxx_epilogue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->hxx_epilogue_file_ = x;
+}
+
+inline bool options::
+hxx_epilogue_file_specified () const
+{
+ return this->hxx_epilogue_file_specified_;
+}
+
+inline void options::
+hxx_epilogue_file_specified (bool x)
+{
+ this->hxx_epilogue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+ixx_epilogue_file () const
+{
+ return this->ixx_epilogue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+ixx_epilogue_file ()
+{
+ return this->ixx_epilogue_file_;
+}
+
+inline void options::
+ixx_epilogue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->ixx_epilogue_file_ = x;
+}
+
+inline bool options::
+ixx_epilogue_file_specified () const
+{
+ return this->ixx_epilogue_file_specified_;
+}
+
+inline void options::
+ixx_epilogue_file_specified (bool x)
+{
+ this->ixx_epilogue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+cxx_epilogue_file () const
+{
+ return this->cxx_epilogue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+cxx_epilogue_file ()
+{
+ return this->cxx_epilogue_file_;
+}
+
+inline void options::
+cxx_epilogue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->cxx_epilogue_file_ = x;
+}
+
+inline bool options::
+cxx_epilogue_file_specified () const
+{
+ return this->cxx_epilogue_file_specified_;
+}
+
+inline void options::
+cxx_epilogue_file_specified (bool x)
+{
+ this->cxx_epilogue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+schema_epilogue_file () const
+{
+ return this->schema_epilogue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+schema_epilogue_file ()
+{
+ return this->schema_epilogue_file_;
+}
+
+inline void options::
+schema_epilogue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->schema_epilogue_file_ = x;
+}
+
+inline bool options::
+schema_epilogue_file_specified () const
+{
+ return this->schema_epilogue_file_specified_;
+}
+
+inline void options::
+schema_epilogue_file_specified (bool x)
+{
+ this->schema_epilogue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+sql_epilogue_file () const
+{
+ return this->sql_epilogue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+sql_epilogue_file ()
+{
+ return this->sql_epilogue_file_;
+}
+
+inline void options::
+sql_epilogue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->sql_epilogue_file_ = x;
+}
+
+inline bool options::
+sql_epilogue_file_specified () const
+{
+ return this->sql_epilogue_file_specified_;
+}
+
+inline void options::
+sql_epilogue_file_specified (bool x)
+{
+ this->sql_epilogue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+migration_epilogue_file () const
+{
+ return this->migration_epilogue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+migration_epilogue_file ()
+{
+ return this->migration_epilogue_file_;
+}
+
+inline void options::
+migration_epilogue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->migration_epilogue_file_ = x;
+}
+
+inline bool options::
+migration_epilogue_file_specified () const
+{
+ return this->migration_epilogue_file_specified_;
+}
+
+inline void options::
+migration_epilogue_file_specified (bool x)
+{
+ this->migration_epilogue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+odb_prologue () const
+{
+ return this->odb_prologue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+odb_prologue ()
+{
+ return this->odb_prologue_;
+}
+
+inline void options::
+odb_prologue (const database_map<std::vector<std::string> >& x)
+{
+ this->odb_prologue_ = x;
+}
+
+inline bool options::
+odb_prologue_specified () const
+{
+ return this->odb_prologue_specified_;
+}
+
+inline void options::
+odb_prologue_specified (bool x)
+{
+ this->odb_prologue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+odb_prologue_file () const
+{
+ return this->odb_prologue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+odb_prologue_file ()
+{
+ return this->odb_prologue_file_;
+}
+
+inline void options::
+odb_prologue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->odb_prologue_file_ = x;
+}
+
+inline bool options::
+odb_prologue_file_specified () const
+{
+ return this->odb_prologue_file_specified_;
+}
+
+inline void options::
+odb_prologue_file_specified (bool x)
+{
+ this->odb_prologue_file_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+odb_epilogue () const
+{
+ return this->odb_epilogue_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+odb_epilogue ()
+{
+ return this->odb_epilogue_;
+}
+
+inline void options::
+odb_epilogue (const database_map<std::vector<std::string> >& x)
+{
+ this->odb_epilogue_ = x;
+}
+
+inline bool options::
+odb_epilogue_specified () const
+{
+ return this->odb_epilogue_specified_;
+}
+
+inline void options::
+odb_epilogue_specified (bool x)
+{
+ this->odb_epilogue_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+odb_epilogue_file () const
+{
+ return this->odb_epilogue_file_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+odb_epilogue_file ()
+{
+ return this->odb_epilogue_file_;
+}
+
+inline void options::
+odb_epilogue_file (const database_map<std::vector<std::string> >& x)
+{
+ this->odb_epilogue_file_ = x;
+}
+
+inline bool options::
+odb_epilogue_file_specified () const
+{
+ return this->odb_epilogue_file_specified_;
+}
+
+inline void options::
+odb_epilogue_file_specified (bool x)
+{
+ this->odb_epilogue_file_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+table_prefix () const
+{
+ return this->table_prefix_;
+}
+
+inline database_map<std::string>& options::
+table_prefix ()
+{
+ return this->table_prefix_;
+}
+
+inline void options::
+table_prefix (const database_map<std::string>& x)
+{
+ this->table_prefix_ = x;
+}
+
+inline bool options::
+table_prefix_specified () const
+{
+ return this->table_prefix_specified_;
+}
+
+inline void options::
+table_prefix_specified (bool x)
+{
+ this->table_prefix_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+index_suffix () const
+{
+ return this->index_suffix_;
+}
+
+inline database_map<std::string>& options::
+index_suffix ()
+{
+ return this->index_suffix_;
+}
+
+inline void options::
+index_suffix (const database_map<std::string>& x)
+{
+ this->index_suffix_ = x;
+}
+
+inline bool options::
+index_suffix_specified () const
+{
+ return this->index_suffix_specified_;
+}
+
+inline void options::
+index_suffix_specified (bool x)
+{
+ this->index_suffix_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+fkey_suffix () const
+{
+ return this->fkey_suffix_;
+}
+
+inline database_map<std::string>& options::
+fkey_suffix ()
+{
+ return this->fkey_suffix_;
+}
+
+inline void options::
+fkey_suffix (const database_map<std::string>& x)
+{
+ this->fkey_suffix_ = x;
+}
+
+inline bool options::
+fkey_suffix_specified () const
+{
+ return this->fkey_suffix_specified_;
+}
+
+inline void options::
+fkey_suffix_specified (bool x)
+{
+ this->fkey_suffix_specified_ = x;
+}
+
+inline const database_map<std::string>& options::
+sequence_suffix () const
+{
+ return this->sequence_suffix_;
+}
+
+inline database_map<std::string>& options::
+sequence_suffix ()
+{
+ return this->sequence_suffix_;
+}
+
+inline void options::
+sequence_suffix (const database_map<std::string>& x)
+{
+ this->sequence_suffix_ = x;
+}
+
+inline bool options::
+sequence_suffix_specified () const
+{
+ return this->sequence_suffix_specified_;
+}
+
+inline void options::
+sequence_suffix_specified (bool x)
+{
+ this->sequence_suffix_specified_ = x;
+}
+
+inline const database_map<name_case>& options::
+sql_name_case () const
+{
+ return this->sql_name_case_;
+}
+
+inline database_map<name_case>& options::
+sql_name_case ()
+{
+ return this->sql_name_case_;
+}
+
+inline void options::
+sql_name_case (const database_map<name_case>& x)
+{
+ this->sql_name_case_ = x;
+}
+
+inline bool options::
+sql_name_case_specified () const
+{
+ return this->sql_name_case_specified_;
+}
+
+inline void options::
+sql_name_case_specified (bool x)
+{
+ this->sql_name_case_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+table_regex () const
+{
+ return this->table_regex_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+table_regex ()
+{
+ return this->table_regex_;
+}
+
+inline void options::
+table_regex (const database_map<std::vector<std::string> >& x)
+{
+ this->table_regex_ = x;
+}
+
+inline bool options::
+table_regex_specified () const
+{
+ return this->table_regex_specified_;
+}
+
+inline void options::
+table_regex_specified (bool x)
+{
+ this->table_regex_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+column_regex () const
+{
+ return this->column_regex_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+column_regex ()
+{
+ return this->column_regex_;
+}
+
+inline void options::
+column_regex (const database_map<std::vector<std::string> >& x)
+{
+ this->column_regex_ = x;
+}
+
+inline bool options::
+column_regex_specified () const
+{
+ return this->column_regex_specified_;
+}
+
+inline void options::
+column_regex_specified (bool x)
+{
+ this->column_regex_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+index_regex () const
+{
+ return this->index_regex_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+index_regex ()
+{
+ return this->index_regex_;
+}
+
+inline void options::
+index_regex (const database_map<std::vector<std::string> >& x)
+{
+ this->index_regex_ = x;
+}
+
+inline bool options::
+index_regex_specified () const
+{
+ return this->index_regex_specified_;
+}
+
+inline void options::
+index_regex_specified (bool x)
+{
+ this->index_regex_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+fkey_regex () const
+{
+ return this->fkey_regex_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+fkey_regex ()
+{
+ return this->fkey_regex_;
+}
+
+inline void options::
+fkey_regex (const database_map<std::vector<std::string> >& x)
+{
+ this->fkey_regex_ = x;
+}
+
+inline bool options::
+fkey_regex_specified () const
+{
+ return this->fkey_regex_specified_;
+}
+
+inline void options::
+fkey_regex_specified (bool x)
+{
+ this->fkey_regex_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+sequence_regex () const
+{
+ return this->sequence_regex_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+sequence_regex ()
+{
+ return this->sequence_regex_;
+}
+
+inline void options::
+sequence_regex (const database_map<std::vector<std::string> >& x)
+{
+ this->sequence_regex_ = x;
+}
+
+inline bool options::
+sequence_regex_specified () const
+{
+ return this->sequence_regex_specified_;
+}
+
+inline void options::
+sequence_regex_specified (bool x)
+{
+ this->sequence_regex_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+statement_regex () const
+{
+ return this->statement_regex_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+statement_regex ()
+{
+ return this->statement_regex_;
+}
+
+inline void options::
+statement_regex (const database_map<std::vector<std::string> >& x)
+{
+ this->statement_regex_ = x;
+}
+
+inline bool options::
+statement_regex_specified () const
+{
+ return this->statement_regex_specified_;
+}
+
+inline void options::
+statement_regex_specified (bool x)
+{
+ this->statement_regex_specified_ = x;
+}
+
+inline const database_map<std::vector<std::string> >& options::
+sql_name_regex () const
+{
+ return this->sql_name_regex_;
+}
+
+inline database_map<std::vector<std::string> >& options::
+sql_name_regex ()
+{
+ return this->sql_name_regex_;
+}
+
+inline void options::
+sql_name_regex (const database_map<std::vector<std::string> >& x)
+{
+ this->sql_name_regex_ = x;
+}
+
+inline bool options::
+sql_name_regex_specified () const
+{
+ return this->sql_name_regex_specified_;
+}
+
+inline void options::
+sql_name_regex_specified (bool x)
+{
+ this->sql_name_regex_specified_ = x;
+}
+
+inline const bool& options::
+sql_name_regex_trace () const
+{
+ return this->sql_name_regex_trace_;
+}
+
+inline bool& options::
+sql_name_regex_trace ()
+{
+ return this->sql_name_regex_trace_;
+}
+
+inline void options::
+sql_name_regex_trace (const bool& x)
+{
+ this->sql_name_regex_trace_ = x;
+}
+
+inline const std::vector<std::string>& options::
+accessor_regex () const
+{
+ return this->accessor_regex_;
+}
+
+inline std::vector<std::string>& options::
+accessor_regex ()
+{
+ return this->accessor_regex_;
+}
+
+inline void options::
+accessor_regex (const std::vector<std::string>& x)
+{
+ this->accessor_regex_ = x;
+}
+
+inline bool options::
+accessor_regex_specified () const
+{
+ return this->accessor_regex_specified_;
+}
+
+inline void options::
+accessor_regex_specified (bool x)
+{
+ this->accessor_regex_specified_ = x;
+}
+
+inline const bool& options::
+accessor_regex_trace () const
+{
+ return this->accessor_regex_trace_;
+}
+
+inline bool& options::
+accessor_regex_trace ()
+{
+ return this->accessor_regex_trace_;
+}
+
+inline void options::
+accessor_regex_trace (const bool& x)
+{
+ this->accessor_regex_trace_ = x;
+}
+
+inline const std::vector<std::string>& options::
+modifier_regex () const
+{
+ return this->modifier_regex_;
+}
+
+inline std::vector<std::string>& options::
+modifier_regex ()
+{
+ return this->modifier_regex_;
+}
+
+inline void options::
+modifier_regex (const std::vector<std::string>& x)
+{
+ this->modifier_regex_ = x;
+}
+
+inline bool options::
+modifier_regex_specified () const
+{
+ return this->modifier_regex_specified_;
+}
+
+inline void options::
+modifier_regex_specified (bool x)
+{
+ this->modifier_regex_specified_ = x;
+}
+
+inline const bool& options::
+modifier_regex_trace () const
+{
+ return this->modifier_regex_trace_;
+}
+
+inline bool& options::
+modifier_regex_trace ()
+{
+ return this->modifier_regex_trace_;
+}
+
+inline void options::
+modifier_regex_trace (const bool& x)
+{
+ this->modifier_regex_trace_ = x;
+}
+
+inline const bool& options::
+include_with_brackets () const
+{
+ return this->include_with_brackets_;
+}
+
+inline bool& options::
+include_with_brackets ()
+{
+ return this->include_with_brackets_;
+}
+
+inline void options::
+include_with_brackets (const bool& x)
+{
+ this->include_with_brackets_ = x;
+}
+
+inline const std::string& options::
+include_prefix () const
+{
+ return this->include_prefix_;
+}
+
+inline std::string& options::
+include_prefix ()
+{
+ return this->include_prefix_;
+}
+
+inline void options::
+include_prefix (const std::string& x)
+{
+ this->include_prefix_ = x;
+}
+
+inline bool options::
+include_prefix_specified () const
+{
+ return this->include_prefix_specified_;
+}
+
+inline void options::
+include_prefix_specified (bool x)
+{
+ this->include_prefix_specified_ = x;
+}
+
+inline const std::vector<std::string>& options::
+include_regex () const
+{
+ return this->include_regex_;
+}
+
+inline std::vector<std::string>& options::
+include_regex ()
+{
+ return this->include_regex_;
+}
+
+inline void options::
+include_regex (const std::vector<std::string>& x)
+{
+ this->include_regex_ = x;
+}
+
+inline bool options::
+include_regex_specified () const
+{
+ return this->include_regex_specified_;
+}
+
+inline void options::
+include_regex_specified (bool x)
+{
+ this->include_regex_specified_ = x;
+}
+
+inline const bool& options::
+include_regex_trace () const
+{
+ return this->include_regex_trace_;
+}
+
+inline bool& options::
+include_regex_trace ()
+{
+ return this->include_regex_trace_;
+}
+
+inline void options::
+include_regex_trace (const bool& x)
+{
+ this->include_regex_trace_ = x;
+}
+
+inline const std::string& options::
+guard_prefix () const
+{
+ return this->guard_prefix_;
+}
+
+inline std::string& options::
+guard_prefix ()
+{
+ return this->guard_prefix_;
+}
+
+inline void options::
+guard_prefix (const std::string& x)
+{
+ this->guard_prefix_ = x;
+}
+
+inline bool options::
+guard_prefix_specified () const
+{
+ return this->guard_prefix_specified_;
+}
+
+inline void options::
+guard_prefix_specified (bool x)
+{
+ this->guard_prefix_specified_ = x;
+}
+
+inline const bool& options::
+show_sloc () const
+{
+ return this->show_sloc_;
+}
+
+inline bool& options::
+show_sloc ()
+{
+ return this->show_sloc_;
+}
+
+inline void options::
+show_sloc (const bool& x)
+{
+ this->show_sloc_ = x;
+}
+
+inline const std::size_t& options::
+sloc_limit () const
+{
+ return this->sloc_limit_;
+}
+
+inline std::size_t& options::
+sloc_limit ()
+{
+ return this->sloc_limit_;
+}
+
+inline void options::
+sloc_limit (const std::size_t& x)
+{
+ this->sloc_limit_ = x;
+}
+
+inline bool options::
+sloc_limit_specified () const
+{
+ return this->sloc_limit_specified_;
+}
+
+inline void options::
+sloc_limit_specified (bool x)
+{
+ this->sloc_limit_specified_ = x;
+}
+
+inline const std::string& options::
+options_file () const
+{
+ return this->options_file_;
+}
+
+inline std::string& options::
+options_file ()
+{
+ return this->options_file_;
+}
+
+inline void options::
+options_file (const std::string& x)
+{
+ this->options_file_ = x;
+}
+
+inline bool options::
+options_file_specified () const
+{
+ return this->options_file_specified_;
+}
+
+inline void options::
+options_file_specified (bool x)
+{
+ this->options_file_specified_ = x;
+}
+
+inline const std::vector<std::string>& options::
+x () const
+{
+ return this->x_;
+}
+
+inline std::vector<std::string>& options::
+x ()
+{
+ return this->x_;
+}
+
+inline void options::
+x (const std::vector<std::string>& x)
+{
+ this->x_ = x;
+}
+
+inline bool options::
+x_specified () const
+{
+ return this->x_specified_;
+}
+
+inline void options::
+x_specified (bool x)
+{
+ this->x_specified_ = x;
+}
+
+inline const bool& options::
+v () const
+{
+ return this->v_;
+}
+
+inline bool& options::
+v ()
+{
+ return this->v_;
+}
+
+inline void options::
+v (const bool& x)
+{
+ this->v_ = x;
+}
+
+inline const bool& options::
+trace () const
+{
+ return this->trace_;
+}
+
+inline bool& options::
+trace ()
+{
+ return this->trace_;
+}
+
+inline void options::
+trace (const bool& x)
+{
+ this->trace_ = x;
+}
+
+inline const std::string& options::
+mysql_engine () const
+{
+ return this->mysql_engine_;
+}
+
+inline std::string& options::
+mysql_engine ()
+{
+ return this->mysql_engine_;
+}
+
+inline void options::
+mysql_engine (const std::string& x)
+{
+ this->mysql_engine_ = x;
+}
+
+inline bool options::
+mysql_engine_specified () const
+{
+ return this->mysql_engine_specified_;
+}
+
+inline void options::
+mysql_engine_specified (bool x)
+{
+ this->mysql_engine_specified_ = x;
+}
+
+inline const bool& options::
+sqlite_override_null () const
+{
+ return this->sqlite_override_null_;
+}
+
+inline bool& options::
+sqlite_override_null ()
+{
+ return this->sqlite_override_null_;
+}
+
+inline void options::
+sqlite_override_null (const bool& x)
+{
+ this->sqlite_override_null_ = x;
+}
+
+inline const bool& options::
+sqlite_lax_auto_id () const
+{
+ return this->sqlite_lax_auto_id_;
+}
+
+inline bool& options::
+sqlite_lax_auto_id ()
+{
+ return this->sqlite_lax_auto_id_;
+}
+
+inline void options::
+sqlite_lax_auto_id (const bool& x)
+{
+ this->sqlite_lax_auto_id_ = x;
+}
+
+inline const ::pgsql_version& options::
+pgsql_server_version () const
+{
+ return this->pgsql_server_version_;
+}
+
+inline ::pgsql_version& options::
+pgsql_server_version ()
+{
+ return this->pgsql_server_version_;
+}
+
+inline void options::
+pgsql_server_version (const ::pgsql_version& x)
+{
+ this->pgsql_server_version_ = x;
+}
+
+inline bool options::
+pgsql_server_version_specified () const
+{
+ return this->pgsql_server_version_specified_;
+}
+
+inline void options::
+pgsql_server_version_specified (bool x)
+{
+ this->pgsql_server_version_specified_ = x;
+}
+
+inline const ::oracle_version& options::
+oracle_client_version () const
+{
+ return this->oracle_client_version_;
+}
+
+inline ::oracle_version& options::
+oracle_client_version ()
+{
+ return this->oracle_client_version_;
+}
+
+inline void options::
+oracle_client_version (const ::oracle_version& x)
+{
+ this->oracle_client_version_ = x;
+}
+
+inline bool options::
+oracle_client_version_specified () const
+{
+ return this->oracle_client_version_specified_;
+}
+
+inline void options::
+oracle_client_version_specified (bool x)
+{
+ this->oracle_client_version_specified_ = x;
+}
+
+inline const bool& options::
+oracle_warn_truncation () const
+{
+ return this->oracle_warn_truncation_;
+}
+
+inline bool& options::
+oracle_warn_truncation ()
+{
+ return this->oracle_warn_truncation_;
+}
+
+inline void options::
+oracle_warn_truncation (const bool& x)
+{
+ this->oracle_warn_truncation_ = x;
+}
+
+inline const ::mssql_version& options::
+mssql_server_version () const
+{
+ return this->mssql_server_version_;
+}
+
+inline ::mssql_version& options::
+mssql_server_version ()
+{
+ return this->mssql_server_version_;
+}
+
+inline void options::
+mssql_server_version (const ::mssql_version& x)
+{
+ this->mssql_server_version_ = x;
+}
+
+inline bool options::
+mssql_server_version_specified () const
+{
+ return this->mssql_server_version_specified_;
+}
+
+inline void options::
+mssql_server_version_specified (bool x)
+{
+ this->mssql_server_version_specified_ = x;
+}
+
+inline const unsigned int& options::
+mssql_short_limit () const
+{
+ return this->mssql_short_limit_;
+}
+
+inline unsigned int& options::
+mssql_short_limit ()
+{
+ return this->mssql_short_limit_;
+}
+
+inline void options::
+mssql_short_limit (const unsigned int& x)
+{
+ this->mssql_short_limit_ = x;
+}
+
+inline bool options::
+mssql_short_limit_specified () const
+{
+ return this->mssql_short_limit_specified_;
+}
+
+inline void options::
+mssql_short_limit_specified (bool x)
+{
+ this->mssql_short_limit_specified_ = x;
+}
+
+// Begin epilogue.
+//
+//
+// End epilogue.
diff --git a/odb/odb/processor.cxx b/odb/odb/processor.cxx
new file mode 100644
index 0000000..fb129fa
--- /dev/null
+++ b/odb/odb/processor.cxx
@@ -0,0 +1,3267 @@
+// file : odb/processor.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <iostream>
+#include <algorithm> // std::find
+
+#include <odb/common.hxx>
+#include <odb/lookup.hxx>
+#include <odb/context.hxx>
+#include <odb/cxx-lexer.hxx>
+#include <odb/processor.hxx>
+#include <odb/diagnostics.hxx>
+
+#include <odb/relational/processor.hxx>
+
+using namespace std;
+
+namespace
+{
+ // Find name hint for this type decl.
+ //
+ static semantics::names*
+ find_hint (semantics::unit& u, tree decl)
+ {
+ semantics::names* r (0);
+
+ for (tree ot (DECL_ORIGINAL_TYPE (decl));
+ ot != 0;
+ ot = decl ? DECL_ORIGINAL_TYPE (decl) : 0)
+ {
+ if ((r = u.find_hint (ot)))
+ break;
+
+ decl = TYPE_NAME (ot);
+ }
+
+ return r;
+ }
+
+ // Indirect (dynamic) context values.
+ //
+ static semantics::type*
+ id_tree_type (semantics::names*& hint)
+ {
+ context& c (context::current ());
+ data_member_path& id (*context::id_member (*c.top_object));
+ return &c.utype (id, hint);
+ }
+
+ struct data_member: traversal::data_member, context
+ {
+ virtual void
+ traverse (semantics::data_member& m)
+ {
+ if (transient (m))
+ return;
+
+ semantics::names* hint;
+ semantics::type& t (utype (m, hint));
+
+ // See if this member is a section.
+ //
+ if (t.fq_name () == "::odb::section")
+ {
+ using semantics::class_;
+
+ class_& c (dynamic_cast<class_&> (m.scope ()));
+ class_* poly_root (polymorphic (c));
+ semantics::data_member* opt (optimistic (c));
+
+ // If we have sections in a polymorphic optimistic hierarchy,
+ // then the version member should be in the root.
+ //
+ if (poly_root == &c && opt != 0 && &opt->scope () != &c)
+ {
+ error (m.location ()) << "version must be a direct data member " <<
+ "of a class that contains sections" << endl;
+ info (opt->location ()) << "version member is declared here" << endl;
+ throw operation_failed ();
+ }
+
+ process_user_section (m, c);
+
+ // We don't need a modifier but the accessor should be by-reference.
+ //
+ process_access (m, "get");
+
+ member_access& ma (m.get<member_access> ("get"));
+ if (ma.by_value)
+ {
+ error (ma.loc) << "accessor returning a value cannot be used "
+ << "for a section" << endl;
+ info (ma.loc) << "accessor returning a const reference is required"
+ << endl;
+ info (m.location ()) << "data member is defined here" << endl;
+ throw operation_failed ();
+ }
+
+ // Mark this member as transient since we don't store it in the
+ // database.
+ //
+ m.set ("transient", true);
+
+ features.section = true;
+ return;
+ }
+ else
+ {
+ process_access (m, "get");
+ process_access (m, "set");
+ }
+
+ // See if this member belongs to a section.
+ //
+ if (m.count ("section-member") != 0)
+ process_section_member (m);
+
+ // We don't need to do any further processing for common if we
+ // are generating static multi-database code.
+ //
+ if (multi_static && options.database ()[0] == database::common)
+ return;
+
+ // Handle wrappers.
+ //
+ semantics::type* wt (0), *qwt (0);
+ semantics::names* whint (0);
+ if (process_wrapper (t))
+ {
+ qwt = t.get<semantics::type*> ("wrapper-type");
+ whint = t.get<semantics::names*> ("wrapper-hint");
+ wt = &utype (*qwt, whint);
+ }
+
+ // If the type is const and the member is not id, version, or
+ // inverse, then mark it as readonly. In case of a wrapper,
+ // both the wrapper type and the wrapped type must be const.
+ // To see why, consider these possibilities:
+ //
+ // unique_ptr<const T> - can modify by setting a new pointer
+ // const unique_ptr<T> - can modify by changing the pointed-to value
+ //
+ if (const_member (m) && !(id (m) || version (m) || m.count ("inverse")))
+ {
+ if (qwt == 0 || const_type (*qwt))
+ m.set ("readonly", true);
+ }
+
+ process_points_to (m);
+
+ if (composite_wrapper (t))
+ return;
+
+ // Process object pointer. The resulting column will be a simple
+ // or composite value.
+ //
+ if (process_object_pointer (m, t))
+ return;
+
+ // Before checking if this is a container, check if this member
+ // or its type were deduced to be a simple value based on the
+ // pragmas. This is necessary because a container member (e.g.,
+ // vector<char>) can be "overridden" into a simple value (e.g.,
+ // BLOB) with a pragma.
+ //
+ if (m.count ("simple") ||
+ t.count ("simple") ||
+ (wt != 0 && wt->count ("simple")))
+ return;
+
+ process_container (m, (wt != 0 ? *wt : t));
+ }
+
+ //
+ // Process member access expressions.
+ //
+
+ enum found_type
+ {
+ found_none,
+ found_some, // Found something but keep looking for a better one.
+ found_best
+ };
+
+ // Check if a function is a suitable accessor for this member.
+ //
+ found_type
+ check_accessor (semantics::data_member& m,
+ tree f,
+ string const& n,
+ member_access& ma,
+ bool strict)
+ {
+ // Must be const.
+ //
+ if (!DECL_CONST_MEMFUNC_P (f))
+ return found_none;
+
+ // Accessor is a function with no arguments (other than 'this').
+ //
+ if (FUNCTION_FIRST_USER_PARMTYPE (f) != void_list_node)
+ return found_none;
+
+ // Note that to get the return type we have to use
+ // TREE_TYPE(TREE_TYPE()) and not DECL_RESULT, as
+ // suggested in the documentation.
+ //
+ tree r (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (f))));
+ gcc_tree_code_type tc (TREE_CODE (r));
+
+ // In the strict mode make sure the function returns for non-array
+ // types a value or a (const) reference to the member type and for
+ // array types a (const) pointer to element type. In the lax mode
+ // we just check that the return value is not void.
+ //
+ if (strict)
+ {
+ semantics::type& t (utype (m));
+ semantics::array* ar (dynamic_cast<semantics::array*> (&t));
+
+ if (ar != 0 && tc != POINTER_TYPE)
+ return found_none;
+
+ tree bt (ar != 0 || tc == REFERENCE_TYPE ? TREE_TYPE (r) : r);
+ tree bt_mv (TYPE_MAIN_VARIANT (bt));
+
+ if ((ar != 0 ? ar->base_type () : t).tree_node () != bt_mv)
+ return found_none;
+ }
+ else if (r == void_type_node)
+ return found_none;
+
+ cxx_tokens& e (ma.expr);
+ e.push_back (cxx_token (0, CPP_KEYWORD, "this"));
+ e.push_back (cxx_token (0, CPP_DOT));
+ e.push_back (cxx_token (0, CPP_NAME, n));
+ e.push_back (cxx_token (0, CPP_OPEN_PAREN));
+ e.push_back (cxx_token (0, CPP_CLOSE_PAREN));
+
+ // See if it returns by value.
+ //
+ ma.by_value = (tc != REFERENCE_TYPE && tc != POINTER_TYPE);
+
+ return found_best;
+ }
+
+ // Check if a function is a suitable modifier for this member.
+ //
+ found_type
+ check_modifier (semantics::data_member& m,
+ tree f,
+ string const& n,
+ member_access& ma,
+ bool strict)
+ {
+ tree a (FUNCTION_FIRST_USER_PARMTYPE (f));
+
+ // For a modifier, it can either be a function that returns a non-
+ // const reference (or non-const pointer, in case the member is an
+ // array) or a by-value modifier that sets a new value. If both are
+ // available, we prefer the former for efficiency.
+ //
+ cxx_tokens& e (ma.expr);
+ semantics::type& t (utype (m));
+ semantics::array* ar (dynamic_cast<semantics::array*> (&t));
+
+ if (a == void_list_node)
+ {
+ // Note that to get the return type we have to use
+ // TREE_TYPE(TREE_TYPE()) and not DECL_RESULT, as
+ // suggested in the documentation.
+ //
+ tree r (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (f))));
+ gcc_tree_code_type tc (TREE_CODE (r));
+
+ // By-reference modifier. Should return a reference or a pointer.
+ //
+ if (tc != (ar != 0 ? POINTER_TYPE : REFERENCE_TYPE))
+ return found_none;
+
+ // The base type should not be const and, in strict mode, should
+ // match the member type.
+ //
+ tree bt (TREE_TYPE (r));
+
+ if (CP_TYPE_CONST_P (bt))
+ return found_none;
+
+ tree bt_mv (TYPE_MAIN_VARIANT (bt));
+
+ if (strict && (ar != 0 ? ar->base_type () : t).tree_node () != bt_mv)
+ return found_none;
+
+ e.clear (); // Could contain by value modifier.
+ e.push_back (cxx_token (0, CPP_KEYWORD, "this"));
+ e.push_back (cxx_token (0, CPP_DOT));
+ e.push_back (cxx_token (0, CPP_NAME, n));
+ e.push_back (cxx_token (0, CPP_OPEN_PAREN));
+ e.push_back (cxx_token (0, CPP_CLOSE_PAREN));
+
+ return found_best;
+ }
+ // Otherwise look for a by value modifier, which is a function
+ // with a single argument.
+ //
+ else if (TREE_CHAIN (a) == void_list_node)
+ {
+ // In the lax mode any function with a single argument works
+ // for us. And we don't care what it returns.
+ //
+ if (strict)
+ {
+ // In the strict mode make sure the argument matches the
+ // member. This is exactly the same logic as in accessor
+ // with regards to arrays, references, etc.
+ //
+ tree at (TREE_VALUE (a));
+ gcc_tree_code_type tc (TREE_CODE (at));
+
+ if (ar != 0 && tc != POINTER_TYPE)
+ return found_none;
+
+ tree bt (ar != 0 || tc == REFERENCE_TYPE ? TREE_TYPE (at) : at);
+ tree bt_mv (TYPE_MAIN_VARIANT (bt));
+
+ if ((ar != 0 ? ar->base_type () : t).tree_node () != bt_mv)
+ return found_none;
+ }
+
+ if (e.empty ())
+ {
+ e.push_back (cxx_token (0, CPP_KEYWORD, "this"));
+ e.push_back (cxx_token (0, CPP_DOT));
+ e.push_back (cxx_token (0, CPP_NAME, n));
+ e.push_back (cxx_token (0, CPP_OPEN_PAREN));
+ e.push_back (cxx_token (0, CPP_QUERY));
+ e.push_back (cxx_token (0, CPP_CLOSE_PAREN));
+
+ // Continue searching in case there is version that returns a
+ // non-const reference which we prefer for efficiency.
+ //
+ return found_some;
+ }
+ else
+ return found_none; // We didn't find anything better.
+ }
+
+ return found_none;
+ }
+
+ void
+ process_access (semantics::data_member& m, std::string const& k)
+ {
+ bool virt (m.count ("virtual"));
+
+ // Ignore certain special virtual members.
+ //
+ if (virt && (m.count ("polymorphic-ref") || m.count ("discriminator")))
+ return;
+
+ char const* kind (k == "get" ? "accessor" : "modifier");
+ semantics::class_& c (dynamic_cast<semantics::class_&> (m.scope ()));
+
+ // If we don't have an access expression, try to come up with
+ // one.
+ //
+ if (!m.count (k))
+ {
+ found_type found (found_none);
+ semantics::access const& a (m.named ().access ());
+ member_access& ma (
+ m.set (
+ k, member_access (m.location (), kind, true)));
+
+ // If this member is not virtual and is either public or if we
+ // are a friend of this class, then go for the member directly.
+ //
+ if (!virt && (a == semantics::access::public_ ||
+ c.get<bool> ("friend")))
+ {
+ ma.expr.push_back (cxx_token (0, CPP_KEYWORD, "this"));
+ ma.expr.push_back (cxx_token (0, CPP_DOT));
+ ma.expr.push_back (cxx_token (0, CPP_NAME, m.name ()));
+ found = found_best;
+ }
+
+ // Otherwise, try to find a suitable accessor/modifier.
+ //
+
+ // First try the original name. If that doesn't produce anything,
+ // then try the public name.
+ //
+ bool t (k == "get"
+ ? options.accessor_regex_trace ()
+ : options.modifier_regex_trace ());
+ regex_mapping const& re (
+ k == "get" ? accessor_regex : modifier_regex);
+
+ for (unsigned short j (0); found != found_best && j != 2; ++j)
+ {
+ string b (j == 0 ? m.name () : public_name (m, false));
+
+ // Skip the second pass if original and public names are the same.
+ //
+ if (j == 1 && b == m.name ())
+ continue;
+
+ if (t)
+ cerr << kind << (j == 0 ? " original" : " public")
+ << " name '" << b << "'" << endl;
+
+ for (regex_mapping::const_iterator i (re.begin ());
+ found != found_best && i != re.end ();
+ ++i)
+ {
+ if (t)
+ cerr << "try: '" << i->regex () << "' : ";
+
+ if (!i->match (b))
+ {
+ if (t)
+ cerr << '-' << endl;
+ continue;
+ }
+
+ string n (i->replace (b));
+
+ if (t)
+ cerr << "'" << n << "' : ";
+
+ tree decl (
+ lookup_qualified_name (
+ c.tree_node (), get_identifier (n.c_str ()), false, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != BASELINK)
+ {
+ if (t)
+ cerr << '-' << endl;
+ continue;
+ }
+
+ // OVL_* macros work for both FUNCTION_DECL and OVERLOAD.
+ //
+#if BUILDING_GCC_MAJOR >= 8
+ for (ovl_iterator i (BASELINK_FUNCTIONS (decl)); i; ++i)
+#else
+ for (tree o (BASELINK_FUNCTIONS (decl)); o != 0; o = OVL_NEXT (o))
+#endif
+ {
+#if BUILDING_GCC_MAJOR >= 8
+ tree f (*i);
+#else
+ tree f (OVL_CURRENT (o));
+#endif
+ // We are only interested in public non-static member
+ // functions. Note that TREE_PUBLIC() returns something
+ // other than what we need.
+ //
+ if (
+#if BUILDING_GCC_MAJOR >= 14
+ !DECL_OBJECT_MEMBER_FUNCTION_P (f)
+#else
+ !DECL_NONSTATIC_MEMBER_FUNCTION_P (f)
+#endif
+ || TREE_PRIVATE (f)
+ || TREE_PROTECTED (f))
+ continue;
+
+ found_type r (k == "get"
+ ? check_accessor (m, f, n, ma, true)
+ : check_modifier (m, f, n, ma, true));
+
+ if (r != found_none)
+ {
+ // Update the location of the access expression to point
+ // to this function.
+ //
+ ma.loc = location (real_source_location (f));
+ found = r;
+ }
+ }
+
+ if (t)
+ cerr << (found != found_none ? '+' : '-') << endl;
+ }
+ }
+
+ // If that didn't work then the generated code won't be able
+ // to access this member.
+ //
+ if (found == found_none)
+ {
+ location const& l (m.location ());
+
+ if (virt)
+ {
+ error (l) << "no suitable " << kind << " function could be "
+ << "automatically found for virtual data member '"
+ << m.name () << "'" << endl;
+
+ info (l) << "use '#pragma db " << k << "' to explicitly "
+ << "specify the " << kind << " function or "
+ << "expression" << endl;
+ }
+ else
+ {
+ error (l) << "data member '" << m.name () << "' is "
+ << a.string () << " and no suitable " << kind
+ << " function could be automatically found" << endl;
+
+ info (l) << "consider making class 'odb::access' a friend of "
+ << "class '" << class_name (c) << "'" << endl;
+
+ info (l) << "or use '#pragma db " << k << "' to explicitly "
+ << "specify the " << kind << " function or "
+ << "expression" << endl;
+ }
+
+ throw operation_failed ();
+ }
+ }
+
+ member_access& ma (m.get<member_access> (k));
+
+ if (ma.empty ())
+ return;
+
+ cxx_tokens& e (ma.expr);
+
+ // If it is just a name, resolve it and convert to an appropriate
+ // expression.
+ //
+ if (e.size () == 1 && e.back ().type == CPP_NAME)
+ {
+ string n (e.back ().literal);
+ e.clear ();
+
+ tree decl (
+ lookup_qualified_name (
+ c.tree_node (), get_identifier (n.c_str ()), false, false));
+
+ if (decl == error_mark_node)
+ {
+ error (ma.loc) << "unable to resolve data member or function "
+ << "name '" << n << "'" << endl;
+ throw operation_failed ();
+ }
+
+ switch (TREE_CODE (decl))
+ {
+ case FIELD_DECL:
+ {
+ e.push_back (cxx_token (0, CPP_KEYWORD, "this"));
+ e.push_back (cxx_token (0, CPP_DOT));
+ e.push_back (cxx_token (0, CPP_NAME, n));
+ break;
+ }
+ case BASELINK:
+ {
+ // OVL_* macros work for both FUNCTION_DECL and OVERLOAD.
+ //
+#if BUILDING_GCC_MAJOR >= 8
+ for (ovl_iterator i (BASELINK_FUNCTIONS (decl)); i; ++i)
+#else
+ for (tree o (BASELINK_FUNCTIONS (decl)); o != 0; o = OVL_NEXT (o))
+#endif
+ {
+#if BUILDING_GCC_MAJOR >= 8
+ tree f (*i);
+#else
+ tree f (OVL_CURRENT (o));
+#endif
+ // We are only interested in non-static member functions.
+ //
+ if (
+#if BUILDING_GCC_MAJOR >= 14
+ !DECL_OBJECT_MEMBER_FUNCTION_P (f)
+#else
+ !DECL_NONSTATIC_MEMBER_FUNCTION_P (f)
+#endif
+ )
+ continue;
+
+ if ((k == "get"
+ ? check_accessor (m, f, n, ma, false)
+ : check_modifier (m, f, n, ma, false)) == found_best)
+ break;
+ }
+
+ if (e.empty ())
+ {
+ error (ma.loc) << "unable to find suitable " << kind
+ << " function '" << n << "'" << endl;
+ throw operation_failed ();
+ }
+ break;
+ }
+ default:
+ {
+ error (ma.loc) << "name '" << n << "' does not refer to a data "
+ << "member or function" << endl;
+ throw operation_failed ();
+ }
+ }
+ }
+
+ // If there is no 'this' keyword, then add it as a prefix.
+ //
+ {
+ bool t (false);
+ for (cxx_tokens::iterator i (e.begin ()); i != e.end (); ++i)
+ {
+ if (i->type == CPP_KEYWORD && i->literal == "this")
+ {
+ t = true;
+ break;
+ }
+ }
+
+ if (!t)
+ {
+ e.insert (e.begin (), cxx_token (0, CPP_DOT));
+ e.insert (e.begin (), cxx_token (0, CPP_KEYWORD, "this"));
+ }
+ }
+
+ // Check that there is no placeholder in the accessor expression.
+ //
+ if (k == "get" && ma.placeholder ())
+ {
+ error (ma.loc) << "(?) placeholder in the accessor expression"
+ << endl;
+ throw operation_failed ();
+ }
+
+ // Check that the member type is default-constructible if we
+ // have a by value modifier.
+ //
+ if (k == "set" && ma.placeholder ())
+ {
+ semantics::class_* c (dynamic_cast<semantics::class_*> (&utype (m)));
+
+ // Assume all other types are default-constructible.
+ //
+ if (c != 0)
+ {
+ // If this type is a class template instantiation, then make
+ // sure it is instantiated. While types used in real members
+ // will be instantiated, this is not necessarily the case for
+ // virtual members. Without the instantiation we won't be able
+ // to detect whether the type has the default ctor.
+ //
+ // It would have been cleaner to do it in post_process_pragmas()
+ // but there we don't yet know whether we need the default ctor.
+ // And it is a good idea not to require instantiability unless
+ // we really need it.
+ //
+ tree type (c->tree_node ());
+
+ if (!COMPLETE_TYPE_P (type) &&
+ CLASSTYPE_TEMPLATE_INSTANTIATION (type))
+ {
+ // Reset input location so that we get nice diagnostics in
+ // case of an error. Use the location of the virtual pragma.
+ //
+ location_t loc (m.get<location_t> ("virtual-location"));
+ input_location = loc;
+
+ if (instantiate_class_template (type) == error_mark_node ||
+ errorcount != 0 ||
+ !COMPLETE_TYPE_P (type))
+ {
+ error (loc) << "unable to instantiate virtual data member " <<
+ "type" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ if (!c->default_ctor ())
+ {
+ error (ma.loc) << "modifier expression requires member type " <<
+ "to be default-constructible" << endl;
+ throw operation_failed ();
+ }
+ }
+ }
+ }
+
+ //
+ // Process section.
+ //
+
+ user_section&
+ process_user_section (semantics::data_member& m, semantics::class_& c)
+ {
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ user_section::load_type l (
+ m.get ("section-load", user_section::load_eager));
+
+ user_section::update_type u (
+ m.get ("section-update", user_section::update_always));
+
+ if (l == user_section::load_eager && u == user_section::update_always)
+ {
+ location const& l (m.location ());
+
+ error (l) << "eager-loaded, always-updated section is pointless"
+ << endl;
+
+ info (l) << "use '#pragma db load' and/or '#pragma db update' to "
+ "specify an alternative loading and/or updating strategy" << endl;
+
+ info (l) << "or remove the section altogether" << endl;
+
+ throw operation_failed ();
+ }
+
+ size_t n (uss.count (user_sections::count_total |
+ user_sections::count_all |
+ user_sections::count_special_version));
+ user_section us (m, c, n, l, u);
+
+ // We may already have seen this section (e.g., forward reference
+ // from a member of this section).
+ //
+ user_sections::iterator i (find (uss.begin (), uss.end (), us));
+
+ if (i != uss.end ())
+ return *i;
+
+ // If we are adding a new section to an optimistic class with
+ // version in a base, make sure the base is sectionable.
+ //
+ semantics::data_member* opt (optimistic (c));
+ if (opt != 0 && &opt->scope () != &c)
+ {
+ semantics::class_* poly_root (polymorphic (c));
+ semantics::node* base (poly_root ? poly_root : &opt->scope ());
+
+ if (!base->count ("sectionable"))
+ {
+ error (m.location ()) << "adding new section to a derived class " <<
+ "in an optimistic hierarchy requires sectionable base class" <<
+ endl;
+
+ info (base->location ()) << "use '#pragma db object sectionable' " <<
+ "to make the base class of this hierarchy sectionable" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ uss.push_back (us);
+ return uss.back ();
+ }
+
+ void
+ process_section_member (semantics::data_member& m)
+ {
+ using semantics::class_;
+ using semantics::data_member;
+
+ string name (m.get<string> ("section-member"));
+ location_t loc (m.get<location_t> ("section-member-location"));
+ class_& c (dynamic_cast<class_&> (m.scope ()));
+
+ class_* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ try
+ {
+ data_member& us (c.lookup<data_member> (name, class_::include_hidden));
+
+ // Make sure we are referencing a section.
+ //
+ if (utype (us).fq_name () != "::odb::section")
+ {
+ error (loc) << "data member '" << name << "' in '#pragma db " <<
+ "section' is not of the odb::section type" << endl;
+ throw operation_failed ();
+ }
+
+ // If the section is in the base, handle polymorphic inheritance.
+ //
+ class_& b (dynamic_cast<class_&> (us.scope ()));
+ user_section* s (0);
+
+ if (&c != &b && poly_derived)
+ {
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ // This is a section override. See if we have already handled
+ // this section.
+ //
+ for (user_sections::iterator i (uss.begin ());
+ s == 0 && i != uss.end ();
+ ++i)
+ {
+ if (i->member == &us)
+ s = &*i;
+ }
+
+ // Otherwise, find and copy the nearest override in the base.
+ // The result should be a chain of overrides leading all the
+ // way to the original section.
+ //
+ if (s == 0)
+ {
+ for (class_* b (&polymorphic_base (c));;
+ b = &polymorphic_base (*b))
+ {
+ user_sections& buss (b->get<user_sections> ("user-sections"));
+
+ for (user_sections::iterator i (buss.begin ());
+ s == 0 && i != buss.end ();
+ ++i)
+ {
+ if (i->member == &us)
+ {
+ uss.push_back (*i);
+ uss.back ().object = &c;
+ uss.back ().base = &*i;
+ s = &uss.back ();
+ }
+ }
+
+ if (s != 0)
+ break;
+
+ assert (b != poly_root); // We should have found it by now.
+ }
+ }
+ }
+ else
+ s = &process_user_section (us, c);
+
+ // Mark the member as added/deleted if the section is added/deleted.
+ // Also check that the version ordering is correct.
+ //
+ if (unsigned long long sav = added (*s->member))
+ {
+ location_t sl (s->member->get<location_t> ("added-location"));
+
+ if (unsigned long long mav = added (m))
+ {
+ location_t ml (m.get<location_t> ("added-location"));
+
+ if (mav < sav)
+ {
+ error (ml) << "member addition version is less than the " <<
+ "section addition version" << endl;
+ info (sl) << "section addition version is specified here" <<
+ endl;
+ throw operation_failed ();
+ }
+
+ if (mav == sav)
+ {
+ error (ml) << "member addition version is the same as " <<
+ "section addition version" << endl;
+ info (sl) << "section addition version is specified here" <<
+ endl;
+ info (ml) << "delete this pragma" << endl;
+ throw operation_failed ();
+ }
+ }
+ else
+ {
+ m.set ("added", sav);
+ m.set ("added-location", sl);
+ }
+ }
+
+ if (unsigned long long sdv = deleted (*s->member))
+ {
+ location_t sl (s->member->get<location_t> ("deleted-location"));
+
+ if (unsigned long long mdv = deleted (m))
+ {
+ location_t ml (m.get<location_t> ("deleted-location"));
+
+ if (mdv > sdv)
+ {
+ error (ml) << "member deletion version is greater than the " <<
+ "section deletion version" << endl;
+ info (sl) << "section deletion version is specified here" <<
+ endl;
+ throw operation_failed ();
+ }
+
+ if (mdv == sdv)
+ {
+ error (ml) << "member deletion version is the same as " <<
+ "section deletion version" << endl;
+ info (sl) << "section deletion version is specified here" <<
+ endl;
+ info (ml) << "delete this pragma" << endl;
+ throw operation_failed ();
+ }
+ }
+ else
+ {
+ m.set ("deleted", sdv);
+ m.set ("deleted-location", sl);
+ }
+ }
+
+ // Insert as object_section.
+ //
+ m.set ("section", static_cast<object_section*> (s));
+ }
+ catch (semantics::unresolved const& e)
+ {
+ if (e.type_mismatch)
+ error (loc) << "name '" << name << "' in '#pragma db section' " <<
+ "does not refer to a data member" << endl;
+ else
+ error (loc) << "unable to resolve data member '" << name << "' " <<
+ "specified with '#pragma db section'" << endl;
+
+ throw operation_failed ();
+ }
+ catch (semantics::ambiguous const& e)
+ {
+ error (loc) << "data member name '" << name << "' specified " <<
+ "with '#pragma db section' is ambiguous" << endl;
+
+ info (e.first.named ().location ()) << "could resolve to this " <<
+ "data member" << endl;
+
+ info (e.second.named ().location ()) << "or could resolve to " <<
+ "this data member" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ //
+ // Process wrapper.
+ //
+
+ bool
+ process_wrapper (semantics::type& t)
+ {
+ if (t.count ("wrapper"))
+ return t.get<bool> ("wrapper");
+
+ // Check this type with wrapper_traits.
+ //
+ tree inst (instantiate_template (wrapper_traits_, t.tree_node ()));
+
+ if (inst == 0)
+ {
+ t.set ("wrapper", false);
+ return false;
+ }
+
+ // @@ This points to the primary template, not the specialization.
+ //
+ tree decl (TYPE_NAME (inst));
+
+ string f (DECL_SOURCE_FILE (decl));
+ size_t l (DECL_SOURCE_LINE (decl));
+ size_t c (DECL_SOURCE_COLUMN (decl));
+
+ // Get the wrapped type.
+ //
+ try
+ {
+ tree decl (
+ lookup_qualified_name (
+ inst, get_identifier ("wrapped_type"), true, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL)
+ throw operation_failed ();
+
+ // The wrapped_type alias is a typedef in an instantiation
+ // that we just instantiated dynamically. As a result there
+ // is no semantic graph edges corresponding to this typedef
+ // since we haven't parsed it yet (unless it was instantiated
+ // explicitly by the user; see below). So to get the tree node
+ // that can actually be resolved to the graph node, we use
+ // the source type of this typedef.
+ //
+ tree type (DECL_ORIGINAL_TYPE (decl));
+
+ bool qc (CP_TYPE_CONST_P (type));
+ bool qv (CP_TYPE_VOLATILE_P (type));
+ bool qr (CP_TYPE_RESTRICT_P (type));
+
+ type = TYPE_MAIN_VARIANT (type);
+ semantics::type* wt (
+ dynamic_cast<semantics::type*> (unit.find (type)));
+
+ // Object pointers and wrappers often use the same smart
+ // pointers so check if the wrapped type is an object.
+ //
+ if (object (*wt))
+ {
+ t.set ("wrapper", false);
+ return false;
+ }
+
+ if (qc || qv || qr)
+ {
+ for (semantics::type::qualified_iterator i (wt->qualified_begin ());
+ i != wt->qualified_end (); ++i)
+ {
+ semantics::qualifier& q (i->qualifier ());
+
+ if (q.const_ () == qc &&
+ q.volatile_ () == qv &&
+ q.restrict_ () == qr)
+ {
+ wt = &q;
+ break;
+ }
+ }
+ }
+
+ // Find the hint.
+ //
+ // If we can't find any, then try to fallback to the wrapped_type
+ // alias inside wrapper_traits. This requires an explicit
+ // wrapper_traits instantiation (see above).
+ //
+ semantics::names* wh (find_hint (unit, decl));
+
+ if (wh == nullptr)
+ wh = unit.find_hint (TREE_TYPE (decl));
+
+ t.set ("wrapper-type", wt);
+ t.set ("wrapper-hint", wh);
+ }
+ catch (operation_failed const&)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "wrapper_traits specialization does not define the "
+ << "wrapped_type type" << endl;
+ throw;
+ }
+
+ // Get the null_handler flag.
+ //
+ bool null_handler (false);
+
+ try
+ {
+ tree nh (
+ lookup_qualified_name (
+ inst, get_identifier ("null_handler"), false, false));
+
+ if (nh == error_mark_node || TREE_CODE (nh) != VAR_DECL)
+ throw operation_failed ();
+
+ // Instantiate this decalaration so that we can get its value.
+ //
+ if (DECL_TEMPLATE_INSTANTIATION (nh) &&
+ !DECL_TEMPLATE_INSTANTIATED (nh) &&
+ !DECL_EXPLICIT_INSTANTIATION (nh))
+ instantiate_decl (nh, false, false);
+
+ tree init (DECL_INITIAL (nh));
+
+ if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
+ throw operation_failed ();
+
+ null_handler = static_cast<bool> (integer_value (init));
+ t.set ("wrapper-null-handler", null_handler);
+ }
+ catch (operation_failed const&)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "wrapper_traits specialization does not define the "
+ << "null_handler constant" << endl;
+ throw;
+ }
+
+ // Get the null_default flag.
+ //
+ if (null_handler)
+ {
+ try
+ {
+ tree nh (
+ lookup_qualified_name (
+ inst, get_identifier ("null_default"), false, false));
+
+ if (nh == error_mark_node || TREE_CODE (nh) != VAR_DECL)
+ throw operation_failed ();
+
+ // Instantiate this decalaration so that we can get its value.
+ //
+ if (DECL_TEMPLATE_INSTANTIATION (nh) &&
+ !DECL_TEMPLATE_INSTANTIATED (nh) &&
+ !DECL_EXPLICIT_INSTANTIATION (nh))
+ instantiate_decl (nh, false, false);
+
+ tree init (DECL_INITIAL (nh));
+
+ if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
+ throw operation_failed ();
+
+ t.set ("wrapper-null-default",
+ static_cast<bool> (integer_value (init)));
+ }
+ catch (operation_failed const&)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "wrapper_traits specialization does not define the "
+ << "null_default constant" << endl;
+ throw;
+ }
+ }
+
+ // Check if the wrapper is a TR1 template instantiation.
+ //
+ if (tree ti = TYPE_TEMPLATE_INFO (t.tree_node ()))
+ {
+ tree decl (TI_TEMPLATE (ti)); // DECL_TEMPLATE
+
+ // Get to the most general template declaration.
+ //
+ while (DECL_TEMPLATE_INFO (decl))
+ decl = DECL_TI_TEMPLATE (decl);
+
+ bool& tr1 (features.tr1_pointer);
+ bool& boost (features.boost_pointer);
+
+ string n (decl_as_string (decl, TFF_PLAIN_IDENTIFIER));
+
+ // In case of a boost TR1 implementation, we cannot distinguish
+ // between the boost:: and std::tr1:: usage since the latter is
+ // just a using-declaration for the former.
+ //
+ tr1 = tr1
+ || n.compare (0, 8, "std::tr1") == 0
+ || n.compare (0, 10, "::std::tr1") == 0;
+
+ boost = boost
+ || n.compare (0, 17, "boost::shared_ptr") == 0
+ || n.compare (0, 19, "::boost::shared_ptr") == 0;
+ }
+
+ t.set ("wrapper", true);
+ return true;
+ }
+
+ //
+ // Process object pointer.
+ //
+
+ semantics::class_*
+ process_object_pointer (semantics::data_member& m,
+ semantics::type& t,
+ string const& kp = string ())
+ {
+ using semantics::class_;
+ using semantics::data_member;
+
+ class_* c (0);
+
+ // The overall idea is as follows: try to instantiate the pointer
+ // traits class template. If we are successeful, then get the
+ // element type and see if it is an object.
+ //
+ if (t.count ("element-type"))
+ c = t.get<class_*> ("element-type");
+ else
+ {
+ tree inst (instantiate_template (pointer_traits_, t.tree_node ()));
+
+ if (inst == 0)
+ return 0;
+
+ // @@ This points to the primary template, not the specialization.
+ //
+ tree decl (TYPE_NAME (inst));
+
+ string fl (DECL_SOURCE_FILE (decl));
+ size_t ln (DECL_SOURCE_LINE (decl));
+ size_t cl (DECL_SOURCE_COLUMN (decl));
+
+ // Get the element type.
+ //
+ tree tn (0);
+ try
+ {
+ tree decl (
+ lookup_qualified_name (
+ inst, get_identifier ("element_type"), true, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL)
+ throw operation_failed ();
+
+ tn = TYPE_MAIN_VARIANT (TREE_TYPE (decl));
+
+ // Check if the pointer is a TR1 template instantiation.
+ //
+ if (tree ti = TYPE_TEMPLATE_INFO (t.tree_node ()))
+ {
+ decl = TI_TEMPLATE (ti); // DECL_TEMPLATE
+
+ // Get to the most general template declaration.
+ //
+ while (DECL_TEMPLATE_INFO (decl))
+ decl = DECL_TI_TEMPLATE (decl);
+
+ bool& tr1 (features.tr1_pointer);
+ bool& boost (features.boost_pointer);
+
+ string n (decl_as_string (decl, TFF_PLAIN_IDENTIFIER));
+
+ // In case of a boost TR1 implementation, we cannot distinguish
+ // between the boost:: and std::tr1:: usage since the latter is
+ // just a using-declaration for the former.
+ //
+ tr1 = tr1
+ || n.compare (0, 8, "std::tr1") == 0
+ || n.compare (0, 10, "::std::tr1") == 0;
+
+ boost = boost
+ || n.compare (0, 17, "boost::shared_ptr") == 0
+ || n.compare (0, 19, "::boost::shared_ptr") == 0;
+ }
+ }
+ catch (operation_failed const&)
+ {
+ os << fl << ":" << ln << ":" << cl << ": error: pointer_traits "
+ << "specialization does not define the 'element_type' type"
+ << endl;
+ throw;
+ }
+
+ c = dynamic_cast<class_*> (unit.find (tn));
+
+ if (c == 0 || !object (*c))
+ return 0;
+
+ t.set ("element-type", c);
+
+ // Determine the pointer kind.
+ //
+ try
+ {
+ tree kind (
+ lookup_qualified_name (
+ inst, get_identifier ("kind"), false, false));
+
+ if (kind == error_mark_node || TREE_CODE (kind) != VAR_DECL)
+ throw operation_failed ();
+
+ // Instantiate this decalaration so that we can get its value.
+ //
+ if (DECL_TEMPLATE_INSTANTIATION (kind) &&
+ !DECL_TEMPLATE_INSTANTIATED (kind) &&
+ !DECL_EXPLICIT_INSTANTIATION (kind))
+ instantiate_decl (kind, false, false);
+
+ tree init (DECL_INITIAL (kind));
+
+ if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
+ throw operation_failed ();
+
+ pointer_kind_type pk = static_cast<pointer_kind_type> (
+ integer_value (init));
+ t.set ("pointer-kind", pk);
+ }
+ catch (operation_failed const&)
+ {
+ os << fl << ":" << ln << ":" << cl << ": error: pointer_traits "
+ << "specialization does not define the 'kind' constant" << endl;
+ throw;
+ }
+
+ // Get the lazy flag.
+ //
+ try
+ {
+ tree lazy (
+ lookup_qualified_name (
+ inst, get_identifier ("lazy"), false, false));
+
+ if (lazy == error_mark_node || TREE_CODE (lazy) != VAR_DECL)
+ throw operation_failed ();
+
+ // Instantiate this decalaration so that we can get its value.
+ //
+ if (DECL_TEMPLATE_INSTANTIATION (lazy) &&
+ !DECL_TEMPLATE_INSTANTIATED (lazy) &&
+ !DECL_EXPLICIT_INSTANTIATION (lazy))
+ instantiate_decl (lazy, false, false);
+
+ tree init (DECL_INITIAL (lazy));
+
+ if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
+ throw operation_failed ();
+
+ t.set ("pointer-lazy", static_cast<bool> (integer_value (init)));
+ }
+ catch (operation_failed const&)
+ {
+ os << fl << ":" << ln << ":" << cl << ": error: pointer_traits "
+ << "specialization does not define the 'kind' constant" << endl;
+ throw;
+ }
+ }
+
+ // See if this is the inverse side of a bidirectional relationship.
+ // If so, then resolve the member path and cache it in the context.
+ //
+ if (m.count ("inverse"))
+ {
+ string name (m.get<string> ("inverse"));
+ location_t l (m.get<location_t> ("inverse-location"));
+ data_member_path mp (resolve_data_members (*c, name, l, lex_));
+
+ {
+ string tl;
+ data_member& m (*mp.back ());
+
+ if (container (m) && lex_.next (tl) != CPP_EOF)
+ {
+ error (l) << "unexpect name after container member " <<
+ m.name () << " in '#pragma db inverse'" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ // Validate each member.
+ //
+ for (data_member_path::iterator i (mp.begin ()); i != mp.end (); ++i)
+ {
+ data_member& im (**i);
+ const string& n (im.name ());
+
+ if (im.count ("transient"))
+ {
+ error (l) << "data member '" << n << "' specified with " <<
+ "'#pragma db inverse' is transient" << endl;
+ info (im.location ()) << "data member '" << n << "' is " <<
+ "defined here" << endl;
+ throw operation_failed ();
+ }
+
+ if (im.count ("inverse") || im.count ("value-inverse"))
+ {
+ error (l) << "data member '" << n << "' specified with " <<
+ "'#pragma db inverse' is itself inverse" << endl;
+ info (im.location ()) << "data member '" << n << "' is " <<
+ "defined here" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ // @@ Would be good to check that the other end is actually
+ // an object pointer/points_to and points to the correct
+ // object. But the other class may not have been processed
+ // yet. Need to do in validator, pass 2.
+ //
+ m.remove ("inverse");
+ m.set (kp + (kp.empty () ? "": "-") + "inverse", mp);
+ }
+
+ return c;
+ }
+
+ //
+ // Process points-to pragma.
+ //
+
+ void
+ process_points_to (semantics::data_member& m,
+ string const& /*kp*/ = string ())
+ {
+ if (!m.count ("points-to"))
+ return;
+
+ using semantics::class_;
+
+ tree t (m.get<tree> ("points-to"));
+ location_t l (m.get<location_t> ("points-to-location"));
+
+ class_* c (dynamic_cast<class_*> (unit.find (t)));
+
+ if (c == 0 || !object (*c))
+ {
+ error (l) << "name specified with '#pragma db points_to' does "
+ << "not refer to an object" << endl;
+ throw operation_failed ();
+ }
+
+ m.remove ("points-to");
+ m.set (/*kp + (kp.empty () ? "": "-") + */"points-to", c);
+ }
+
+ //
+ // Process container.
+ //
+
+ void
+ process_container_value (semantics::type& t,
+ semantics::data_member& m,
+ string const& prefix,
+ bool obj_ptr)
+ {
+ if (composite_wrapper (t))
+ return;
+
+ if (obj_ptr)
+ process_object_pointer (m, t, prefix);
+ }
+
+ bool
+ process_container (semantics::data_member& m, semantics::type& t)
+ {
+ // The overall idea is as follows: try to instantiate the container
+ // traits class template. If we are successeful, then this is a
+ // container type and we can extract the various information from
+ // the instantiation. Otherwise, this is not a container.
+ //
+ location ml (m.location ());
+
+ container_kind_type ck;
+ bool smart;
+ semantics::type* vt (0);
+ semantics::type* it (0);
+ semantics::type* kt (0);
+
+ semantics::names* vh (0);
+ semantics::names* ih (0);
+ semantics::names* kh (0);
+
+ if (t.count ("container-kind"))
+ {
+ ck = t.get<container_kind_type> ("container-kind");
+ smart = t.get<bool> ("container-smart");
+
+ vt = &utype (m, vh, "value");
+
+ if (ck == ck_ordered)
+ it = &utype (m, ih, "index");
+
+ if (ck == ck_map || ck == ck_multimap)
+ kt = &utype (m, kh, "key");
+ }
+ else
+ {
+ tree inst (instantiate_template (container_traits_, t.tree_node ()));
+
+ if (inst == 0)
+ return false;
+
+ // @@ This points to the primary template, not the specialization.
+ //
+ tree decl (TYPE_NAME (inst));
+
+ string f (DECL_SOURCE_FILE (decl));
+ size_t l (DECL_SOURCE_LINE (decl));
+ size_t c (DECL_SOURCE_COLUMN (decl));
+
+ // Determine the container kind.
+ //
+ try
+ {
+ tree decl (
+ lookup_qualified_name (
+ inst, get_identifier ("kind"), false, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != VAR_DECL)
+ throw operation_failed ();
+
+ // Instantiate this decalaration so that we can get its value.
+ //
+ if (DECL_TEMPLATE_INSTANTIATION (decl) &&
+ !DECL_TEMPLATE_INSTANTIATED (decl) &&
+ !DECL_EXPLICIT_INSTANTIATION (decl))
+ instantiate_decl (decl, false, false);
+
+ tree init (DECL_INITIAL (decl));
+
+ if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
+ throw operation_failed ();
+
+ ck = static_cast<container_kind_type> (integer_value (init));
+ }
+ catch (operation_failed const&)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "container_traits specialization does not define the "
+ << "container kind constant" << endl;
+
+ throw;
+ }
+
+ t.set ("container-kind", ck);
+
+ // See if it is a smart container.
+ //
+ try
+ {
+ tree decl (
+ lookup_qualified_name (
+ inst, get_identifier ("smart"), false, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != VAR_DECL)
+ throw operation_failed ();
+
+ // Instantiate this decalaration so that we can get its value.
+ //
+ if (DECL_TEMPLATE_INSTANTIATION (decl) &&
+ !DECL_TEMPLATE_INSTANTIATED (decl) &&
+ !DECL_EXPLICIT_INSTANTIATION (decl))
+ instantiate_decl (decl, false, false);
+
+ tree init (DECL_INITIAL (decl));
+
+ if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST)
+ throw operation_failed ();
+
+ smart = static_cast<bool> (integer_value (init));
+ }
+ catch (operation_failed const&)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "container_traits specialization does not define the "
+ << "'smart' constant" << endl;
+ throw;
+ }
+
+ // For now we only support ordered smart containers.
+ //
+ if (smart && ck != ck_ordered)
+ {
+ os << f << ":" << l << ":" << c << ": error: only ordered smart " <<
+ "containers are currently supported" << endl;
+ throw operation_failed ();
+ }
+
+ t.set ("container-smart", smart);
+
+ // Mark id column as not null.
+ //
+ t.set ("id-not-null", true);
+
+ // Get the value type.
+ //
+ {
+ tree decl (lookup_qualified_name (
+ inst, get_identifier ("value_type"), true, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "container_traits specialization does not define the "
+ << "value_type type" << endl;
+
+ throw operation_failed ();
+ }
+
+ tree type (TYPE_MAIN_VARIANT (TREE_TYPE (decl)));
+
+ if (semantics::node* n = unit.find (type))
+ vt = &dynamic_cast<semantics::type&> (*n);
+ else
+ {
+ error (ml) << "container value type is not instantiated" << endl;
+ info (ml) << "use typedef/using to instantiate" << endl;
+ throw operation_failed ();
+ }
+
+ // Find the hint.
+ //
+ vh = find_hint (unit, decl);
+ }
+
+
+ t.set ("value-tree-type", vt);
+ t.set ("value-tree-hint", vh);
+ vt = &utype (m, vh, "value"); // Map.
+
+ // Issue a warning if we are relaxing null-ness in the container
+ // type.
+ //
+ if (t.count ("value-null") && vt->count ("not-null"))
+ {
+ os << t.file () << ":" << t.line () << ":" << t.column () << ":"
+ << " warning: container value declared null while its type "
+ << "is declared not null" << endl;
+ }
+
+ // Get the index type for ordered containers.
+ //
+ if (ck == ck_ordered)
+ {
+ tree decl (
+ lookup_qualified_name (
+ inst, get_identifier ("index_type"), true, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "container_traits specialization does not define the "
+ << "index_type type" << endl;
+ throw operation_failed ();
+ }
+
+ tree type (TYPE_MAIN_VARIANT (TREE_TYPE (decl)));
+
+ if (semantics::node* n = unit.find (type))
+ it = &dynamic_cast<semantics::type&> (*n);
+ else
+ {
+ error (ml) << "container index type is not instantiated" << endl;
+ info (ml) << "use typedef/using to instantiate" << endl;
+ throw operation_failed ();
+ }
+
+ // Find the hint.
+ //
+ ih = find_hint (unit, decl);
+
+ t.set ("index-not-null", true);
+ t.set ("index-tree-type", it);
+ t.set ("index-tree-hint", ih);
+ it = &utype (m, ih, "index"); // Map.
+ }
+
+ // Get the key type for maps.
+ //
+ if (ck == ck_map || ck == ck_multimap)
+ {
+ tree decl (
+ lookup_qualified_name (
+ inst, get_identifier ("key_type"), true, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL)
+ {
+ os << f << ":" << l << ":" << c << ": error: "
+ << "container_traits specialization does not define the "
+ << "key_type type" << endl;
+ throw operation_failed ();
+ }
+
+ tree type (TYPE_MAIN_VARIANT (TREE_TYPE (decl)));
+
+ if (semantics::node* n = unit.find (type))
+ kt = &dynamic_cast<semantics::type&> (*n);
+ else
+ {
+ error (ml) << "container key type is not instantiated" << endl;
+ info (ml) << "use typedef/using to instantiate" << endl;
+ throw operation_failed ();
+ }
+
+ // Find the hint.
+ //
+ kh = find_hint (unit, decl);
+
+ t.set ("key-tree-type", kt);
+ t.set ("key-tree-hint", kh);
+ kt = &utype (m, kh, "key"); // Map.
+
+ // Issue a warning if we are relaxing null-ness in the container
+ // type.
+ //
+ if (t.count ("key-null") && kt->count ("not-null"))
+ {
+ os << t.file () << ":" << t.line () << ":" << t.column () << ":"
+ << " warning: container key declared null while its type "
+ << "is declared not null" << endl;
+ }
+ }
+
+ // Determine if container value/index/key types are wrappers.
+ //
+ process_wrapper (*vt);
+
+ if (it != 0)
+ process_wrapper (*it);
+
+ if (kt != 0)
+ process_wrapper (*kt);
+
+ // Check if we are versioned. For now we are not allowing for
+ // soft-add/delete in container keys (might be used in WHERE,
+ // primary key).
+ //
+ {
+ semantics::class_* comp (0);
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ comp = composite_wrapper (*vt);
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ comp = composite_wrapper (*kt);
+ if (comp == 0 || column_count (*comp).soft == 0)
+ {
+ comp = composite_wrapper (*vt);
+ break;
+ }
+
+ error (ml) << "map key type cannot have soft-added/deleted " <<
+ "data members" << endl;
+ info (kt->location ()) << "key type is defined here" << endl;
+ throw operation_failed ();
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ comp = composite_wrapper (*vt);
+ if (comp == 0 || column_count (*comp).soft == 0)
+ {
+ comp = 0;
+ break;
+ }
+
+ error (ml) << "set value type cannot have soft-added/deleted " <<
+ "data members" << endl;
+ info (vt->location ()) << "value type is defined here" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ if (force_versioned || (comp != 0 && column_count (*comp).soft != 0))
+ t.set ("versioned", true);
+ }
+ }
+
+ // Process member data.
+ //
+ m.set ("id-tree-type", &id_tree_type);
+
+ // Has to be first to handle inverse.
+ //
+ process_container_value (*vt, m, "value", true);
+
+ if (it != 0)
+ process_container_value (*it, m, "index", false);
+
+ if (kt != 0)
+ process_container_value (*kt, m, "key", true);
+
+ // A map cannot be an inverse container.
+ //
+ if (m.count ("value-inverse") && (ck == ck_map || ck == ck_multimap))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: inverse container cannot be a map" << endl;
+ throw operation_failed ();
+ }
+
+ // If this is an inverse side of a bidirectional object relationship
+ // and it is an ordered container, mark it as unordred since there is
+ // no concept of order in this construct.
+ //
+ if (ck == ck_ordered && m.count ("value-inverse"))
+ m.set ("unordered", true);
+
+ // Issue an error if we have a non-inverse smart unordered container.
+ //
+ if (smart && ck == ck_ordered && unordered (m) &&
+ !m.count ("value-inverse"))
+ {
+ error (ml) << "smart ordered container cannot be unordered" << endl;
+ throw operation_failed ();
+ }
+
+ // Issue a warning if we are relaxing null-ness in the member.
+ //
+ if (m.count ("value-null") &&
+ (t.count ("value-not-null") || vt->count ("not-null")))
+ {
+ warn (ml) << "container value declared null while the container "
+ << "type or value type declares it as not null" << endl;
+ }
+
+ if (ck == ck_map || ck == ck_multimap)
+ {
+ if (m.count ("key-null") &&
+ (t.count ("key-not-null") || kt->count ("not-null")))
+ {
+ warn (ml) << "container key declared null while the container "
+ << "type or key type declares it as not null" << endl;
+ }
+ }
+
+ return true;
+ }
+
+ //
+ // Implementation details (c-tor, helpers).
+ //
+
+ data_member ()
+ {
+ // Find the odb namespace.
+ //
+ tree odb = lookup_qualified_name (
+ global_namespace, get_identifier ("odb"), false, false);
+
+ if (odb == error_mark_node)
+ {
+ os << unit.file () << ": error: unable to resolve odb namespace"
+ << endl;
+
+ throw operation_failed ();
+ }
+
+ // Find wrapper traits.
+ //
+ wrapper_traits_ = lookup_qualified_name (
+ odb, get_identifier ("wrapper_traits"), true, false);
+
+ if (wrapper_traits_ == error_mark_node ||
+ !DECL_CLASS_TEMPLATE_P (wrapper_traits_))
+ {
+ os << unit.file () << ": error: unable to resolve wrapper_traits "
+ << "in the odb namespace" << endl;
+
+ throw operation_failed ();
+ }
+
+ // Find pointer traits.
+ //
+ pointer_traits_ = lookup_qualified_name (
+ odb, get_identifier ("pointer_traits"), true, false);
+
+ if (pointer_traits_ == error_mark_node ||
+ !DECL_CLASS_TEMPLATE_P (pointer_traits_))
+ {
+ os << unit.file () << ": error: unable to resolve pointer_traits "
+ << "in the odb namespace" << endl;
+
+ throw operation_failed ();
+ }
+
+ // Find the access class.
+ //
+ tree access = lookup_qualified_name (
+ odb, get_identifier ("access"), true, false);
+
+ if (access == error_mark_node)
+ {
+ os << unit.file () << ": error: unable to resolve access class"
+ << "in the odb namespace" << endl;
+
+ throw operation_failed ();
+ }
+
+ access = TREE_TYPE (access);
+
+ // Find container_traits.
+ //
+ container_traits_ = lookup_qualified_name (
+ access, get_identifier ("container_traits"), true, false);
+
+ if (container_traits_ == error_mark_node ||
+ !DECL_CLASS_TEMPLATE_P (container_traits_))
+ {
+ os << unit.file () << ": error: unable to resolve container_traits "
+ << "in the odb namespace" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ static tree
+ instantiate_template (tree t, tree arg)
+ {
+ tree args (make_tree_vec (1));
+ TREE_VEC_ELT (args, 0) = arg;
+
+ // This step should succeed regardles of whether there is a
+ // specialization for this type.
+ //
+ tree inst (
+ lookup_template_class (t, args, 0, 0, 0, tf_warning_or_error));
+
+ if (inst == error_mark_node)
+ {
+ // Diagnostics has already been issued by lookup_template_class.
+ //
+ throw operation_failed ();
+ }
+
+ inst = TYPE_MAIN_VARIANT (inst);
+
+ // The instantiation may already be complete if it matches a
+ // (complete) specialization or was used before.
+ //
+ if (!COMPLETE_TYPE_P (inst))
+ inst = instantiate_class_template (inst);
+
+ // If we cannot instantiate this type, assume there is no suitable
+ // specialization for it.
+ //
+ if (inst == error_mark_node || !COMPLETE_TYPE_P (inst))
+ return 0;
+
+ return inst;
+ }
+
+ private:
+ tree wrapper_traits_;
+ tree pointer_traits_;
+ tree container_traits_;
+
+ cxx_string_lexer lex_;
+ };
+
+ struct view_data_member: traversal::data_member, context
+ {
+ view_data_member (semantics::class_& c)
+ : view_ (c),
+ amap_ (c.get<view_alias_map> ("alias-map")),
+ omap_ (c.get<view_object_map> ("object-map")) {}
+
+ virtual void
+ traverse (semantics::data_member& m)
+ {
+ using semantics::data_member;
+
+ if (transient (m))
+ return;
+
+ semantics::type& t (utype (m));
+
+ if (semantics::class_* c = object_pointer (t))
+ {
+ location const& l (m.location ());
+
+ if (lazy_pointer (t))
+ {
+ error (l) << "lazy object pointer in view" << endl;
+ throw operation_failed ();
+ }
+
+ // Find the corresponding associated object. First see if this
+ // data member name matches any aliases.
+ //
+ view_alias_map::iterator i (amap_.find (m.name ()));
+
+ if (i == amap_.end ())
+ i = amap_.find (public_name (m, false));
+
+ view_object* vo (0);
+
+ if (i != amap_.end ())
+ {
+ vo = i->second;
+
+ if (vo->obj != c) // @@ Poly base/derived difference?
+ {
+ error (l) << "different pointed-to and associated objects" << endl;
+ info (vo->loc) << "associated object is defined here" << endl;
+ throw operation_failed ();
+ }
+ }
+ else
+ {
+ // If there is no alias match, try the object type.
+ //
+ view_object_map::iterator i (omap_.find (c));
+
+ if (i == omap_.end ())
+ {
+ error (l) << "unable to find associated object for object "
+ << "pointer" << endl;
+ info (l) << "use associated object alias as this data member "
+ << "name" << endl;
+ throw operation_failed ();
+ }
+
+ vo = i->second;
+ }
+
+ if (vo->ptr != 0)
+ {
+ location const& l2 (vo->ptr->location ());
+
+ error (l) << "associated object is already loaded via another "
+ << "object pointer" << endl;
+ info (l2) << "the other data member is defined here" << endl;
+ info (l2) << "use associated object alias as this data member "
+ << "name to load a different object" << endl;
+
+ throw operation_failed ();
+ }
+
+ vo->ptr = &m;
+ m.set ("view-object", vo);
+ }
+ }
+
+ private:
+ semantics::class_& view_;
+ view_alias_map& amap_;
+ view_object_map& omap_;
+ };
+
+ // Figure out the "summary" added/deleted version for a composite
+ // value type.
+ //
+ struct summary_version: object_members_base
+ {
+ summary_version (): av (0), dv (0), a_ (true), d_ (true) {}
+
+ virtual void
+ traverse_simple (semantics::data_member&)
+ {
+ if (a_)
+ {
+ if (unsigned long long v = added (member_path_))
+ {
+ if (av == 0 || av < v)
+ av = v;
+ }
+ else
+ {
+ av = 0;
+ a_ = false;
+ }
+ }
+
+ if (d_)
+ {
+ if (unsigned long long v = deleted (member_path_))
+ {
+ if (dv == 0 || dv > v)
+ dv = v;
+ }
+ else
+ {
+ dv = 0;
+ d_ = false;
+ }
+ }
+ }
+
+ public:
+ unsigned long long av;
+ unsigned long long dv;
+
+ bool a_;
+ bool d_;
+ };
+
+ struct class_: traversal::class_, context
+ {
+ class_ ()
+ : typedefs_ (true),
+ std_string_ (0),
+ std_string_hint_ (0),
+ access_ (0)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+
+ member_names_ >> member_;
+
+ // Resolve the std::string type node.
+ //
+ using semantics::scope;
+
+ for (scope::names_iterator_pair ip (unit.find ("std"));
+ ip.first != ip.second; ++ip.first)
+ {
+ if (scope* ns = dynamic_cast<scope*> (&ip.first->named ()))
+ {
+ scope::names_iterator_pair jp (ns->find ("string"));
+
+ if (jp.first != jp.second)
+ {
+ std_string_ = dynamic_cast<semantics::type*> (
+ &jp.first->named ());
+ std_string_hint_ = &*jp.first;
+ break;
+ }
+ }
+ }
+
+ assert (std_string_ != 0); // No std::string?
+
+ // Resolve odb::access, if any.
+ //
+ tree odb = lookup_qualified_name (
+ global_namespace, get_identifier ("odb"), false, false);
+
+ if (odb != error_mark_node)
+ {
+ access_ = lookup_qualified_name (
+ odb, get_identifier ("access"), true, false);
+
+ access_ = (access_ != error_mark_node ? TREE_TYPE (access_) : 0);
+ }
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type k (class_kind (c));
+
+ if (k == class_other)
+ return;
+
+ names (c); // Process nested classes.
+
+ // Check if odb::access is a friend of this class.
+ //
+ c.set ("friend", access_ != 0 && is_friend (c.tree_node (), access_));
+
+ // Assign pointer.
+ //
+ if (k == class_object || k == class_view)
+ assign_pointer (c);
+
+ if (k == class_object)
+ traverse_object_pre (c);
+ else if (k == class_view)
+ traverse_view_pre (c);
+
+ names (c, member_names_);
+
+ if (k == class_object)
+ traverse_object_post (c);
+ else if (k == class_view)
+ traverse_view_post (c);
+ else if (k == class_composite)
+ traverse_composite_post (c);
+ }
+
+ //
+ // Object.
+ //
+
+ virtual void
+ traverse_object_pre (type& c)
+ {
+ using semantics::class_;
+ using semantics::data_member;
+
+ class_* poly_root (polymorphic (c));
+
+ // Sections.
+ //
+ user_sections& uss (c.set ("user-sections", user_sections (c)));
+
+ // Copy sections from reuse bases. For polymorphic classes, sections
+ // are overridden.
+ //
+ if (poly_root == 0 || poly_root == &c)
+ {
+ for (type::inherits_iterator i (c.inherits_begin ());
+ i != c.inherits_end (); ++i)
+ {
+ type& b (i->base ());
+
+ if (object (b))
+ {
+ user_sections& buss (b.get<user_sections> ("user-sections"));
+
+ for (user_sections::iterator j (buss.begin ());
+ j != buss.end ();
+ ++j)
+ {
+ // Don't copy the special version update section.
+ //
+ if (j->special == user_section::special_version)
+ continue;
+
+ uss.push_back (*j);
+ uss.back ().object = &c;
+ uss.back ().base = &*j;
+ }
+ }
+ }
+ }
+
+ // Determine whether it is a session object.
+ //
+ if (!c.count ("session"))
+ {
+ // If this is a derived class in a polymorphic hierarchy,
+ // then it should have the same session value as the root.
+ //
+ if (poly_root != 0 && poly_root != &c)
+ c.set ("session", session (*poly_root));
+ else
+ {
+ // See if any of the namespaces containing this class specify
+ // the session value.
+ //
+ bool found (false);
+ for (semantics::scope* s (&class_scope (c));; s = &s->scope_ ())
+ {
+ using semantics::namespace_;
+
+ namespace_* ns (dynamic_cast<namespace_*> (s));
+
+ if (ns == 0) // Some other scope.
+ {
+ if (!s->named_p ())
+ break;
+
+ continue;
+ }
+
+ if (ns->extension ())
+ ns = &ns->original ();
+
+ if (ns->count ("session"))
+ {
+ c.set ("session", ns->get<bool> ("session"));
+ found = true;
+ break;
+ }
+
+ if (ns->global_scope ()) // Note: namespaces always named.
+ break;
+ }
+
+ // If still not found, then use the default value.
+ //
+ if (!found)
+ c.set ("session", options.generate_session ());
+ }
+ }
+
+ if (session (c))
+ features.session_object = true;
+
+ if (poly_root != 0)
+ {
+ using namespace semantics;
+ using semantics::data_member;
+
+ data_member_path& id (*id_member (*poly_root));
+ data_member* idm (id.front ());
+
+ if (poly_root != &c)
+ {
+ // If we are a derived class in the polymorphic persistent
+ // class hierarchy, then add a synthesized virtual pointer
+ // member that points back to the root.
+ //
+ semantics::class_& base (polymorphic_base (c));
+
+ if (&base != poly_root)
+ idm = &dynamic_cast<data_member&> (base.names_begin ()->named ());
+
+ path const& f (idm->file ());
+ size_t l (idm->line ()), col (idm->column ());
+
+ semantics::data_member& m (
+ unit.new_node<semantics::data_member> (f, l, col, tree (0)));
+ m.set ("virtual", true);
+
+ // Make it the first member in the class. This is important:
+ // we rely on the corrensponding foreign key to be first.
+ //
+ node_position<type, scope::names_iterator> np (c, c.names_end ());
+ unit.new_edge<semantics::names> (
+ np, m, idm->name (), access::public_);
+
+ // Use the raw pointer as this member's type.
+ //
+ if (!base.pointed_p ())
+ {
+ // Create the pointer type in the graph. The pointer node
+ // in GCC seems to always be present, even if not explicitly
+ // used in the translation unit.
+ //
+ tree t (base.tree_node ());
+ tree ptr (TYPE_POINTER_TO (t));
+ assert (ptr != 0);
+ ptr = TYPE_MAIN_VARIANT (ptr);
+ pointer& p (unit.new_node<pointer> (f, l, col, ptr));
+ unit.insert (ptr, p);
+ unit.new_edge<points> (p, base);
+ assert (base.pointed_p ());
+ }
+
+ unit.new_edge<belongs> (m, base.pointed ().pointer ());
+
+ // Mark it as a special kind of id.
+ //
+ m.set ("id", string ());
+ m.set ("polymorphic-ref", true);
+
+ // Make sure we also use the same column name as the root.
+ //
+ if (composite_wrapper (utype (id)))
+ m.set ("column", table_column (column_prefix (id, true).prefix));
+ else
+ m.set ("column", table_column (column_name (id)));
+ }
+ else
+ {
+ // If we are a root of the polymorphic persistent class hierarchy,
+ // then add a synthesized virtual member for the discriminator.
+ // Use the location of the polymorphic pragma as the location of
+ // this member.
+ //
+ location_t loc (c.get<location_t> ("polymorphic-location"));
+ semantics::data_member& m (
+ unit.new_node<semantics::data_member> (
+ path (LOCATION_FILE (loc)),
+ LOCATION_LINE (loc),
+ LOCATION_COLUMN (loc),
+ tree (0)));
+ m.set ("virtual", true);
+
+ // Insert it after the id member (or first if this id comes
+ // from reuse-base).
+ //
+ node_position<type, scope::names_iterator> np (
+ c, c.find (idm->named ()));
+ unit.new_edge<semantics::names> (
+ np, m, "typeid_", access::public_);
+
+ belongs& edge (unit.new_edge<belongs> (m, *std_string_));
+ edge.hint (*std_string_hint_);
+
+ m.set ("readonly", true);
+ m.set ("discriminator", true);
+
+ c.set ("discriminator", &m);
+ }
+ }
+ }
+
+ virtual void
+ traverse_object_post (type& c)
+ {
+ semantics::class_* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ semantics::data_member* opt (optimistic (c));
+
+ // Figure out if we are versioned. We are versioned if we have
+ // soft-added/deleted columns ourselves or our poly-base is
+ // versioned.
+ //
+ if (force_versioned ||
+ column_count (c).soft != 0 ||
+ (poly_derived && polymorphic_base (c).count ("versioned")))
+ c.set ("versioned", true);
+
+ // Sections.
+ //
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ // See if we need to add a special fake section for version update.
+ //
+ if (c.count ("sectionable"))
+ {
+ uss.push_back (
+ user_section (*opt,
+ c,
+ uss.count (user_sections::count_total |
+ user_sections::count_all |
+ user_sections::count_special_version),
+ user_section::load_lazy,
+ user_section::update_manual,
+ user_section::special_version));
+
+ // If we are a root of a polymorphic hierarchy and the version is in
+ // a reuse-base, then we need to make sure that base is sectionable
+ // and derive from its special version update section.
+ //
+ semantics::node& opt_base (opt->scope ());
+ if (poly_root == &c && &opt_base != &c)
+ {
+ if (!opt_base.count ("sectionable"))
+ {
+ location_t l (c.get<location_t> ("sectionable-location"));
+
+ error (l) << "reuse base class of a sectionable polymorphic " <<
+ "root class must be sectionable" << endl;
+
+ info (opt_base.location ()) << "use '#pragma db object " <<
+ "sectionable' to make the base class of this hierarchy " <<
+ "sectionable" << endl;
+
+ throw operation_failed ();
+ }
+
+ uss.back ().base =
+ &opt_base.get<user_sections> ("user-sections").back ();
+ }
+ }
+
+ // Calculate column counts for sections.
+ //
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ column_count_type cc (column_count (c, &*i));
+ i->total = cc.total;
+ i->inverse = cc.inverse;
+ i->readonly = cc.readonly;
+
+ // Figure out if we are versioned. We are versioned if we have
+ // soft-added/deleted columns ourselves or our poly-base is
+ // versioned.
+ //
+ if (force_versioned || cc.soft != 0 ||
+ (poly_derived && i->base != 0 && i->base->versioned))
+ i->versioned = true;
+
+ if (size_t n = has_a (c, test_container, &*i))
+ {
+ i->containers = true;
+ i->versioned_containers =
+ n != has_a (c,
+ test_container |
+ exclude_deleted | exclude_added | exclude_versioned,
+ &*i);
+
+ if ((n = has_a (c, test_readwrite_container, &*i)))
+ {
+ i->readwrite_containers = true;
+ i->readwrite_versioned_containers =
+ n != has_a (c,
+ test_readwrite_container |
+ exclude_deleted | exclude_added | exclude_versioned,
+ &*i);
+ }
+ }
+ }
+ }
+
+ //
+ // View.
+ //
+
+ virtual void
+ traverse_view_pre (type& c)
+ {
+ // Resolve referenced objects from tree nodes to semantic graph
+ // nodes. Also populate maps and compute counts.
+ //
+ view_alias_map& amap (c.set ("alias-map", view_alias_map ()));
+ view_object_map& omap (c.set ("object-map", view_object_map ()));
+
+ size_t& obj_count (c.set ("object-count", size_t (0)));
+ size_t& tbl_count (c.set ("table-count", size_t (0)));
+
+ if (c.count ("objects"))
+ {
+ using semantics::class_;
+
+ view_objects& objs (c.get<view_objects> ("objects"));
+
+ for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
+ {
+ if (i->kind != view_object::object)
+ {
+ tbl_count++;
+ continue;
+ }
+ else
+ obj_count++;
+
+ tree n (TYPE_MAIN_VARIANT (i->obj_node));
+
+ if (TREE_CODE (n) != RECORD_TYPE)
+ {
+ error (i->loc) << "name '" << i->obj_name << "' in db pragma " <<
+ "object does not name a class" << endl;
+
+ throw operation_failed ();
+ }
+
+ class_& o (dynamic_cast<class_&> (*unit.find (n)));
+
+ if (!object (o))
+ {
+ error (i->loc) << "name '" << i->obj_name << "' in db pragma " <<
+ "object does not name a persistent class" << endl;
+
+ info (o.location ()) << "class '" << i->obj_name << "' is " <<
+ "defined here" << endl;
+
+ throw operation_failed ();
+ }
+
+ i->obj = &o;
+ i->ptr = 0; // Nothing yet.
+
+ if (i->alias.empty ())
+ {
+ if (!omap.insert (view_object_map::value_type (&o, &*i)).second)
+ {
+ error (i->loc) << "persistent class '" << i->obj_name <<
+ "' is used in the view more than once" << endl;
+
+ error (omap[&o]->loc) << "previously used here" << endl;
+
+ info (i->loc) << "use the alias clause to assign it a " <<
+ "different name" << endl;
+
+ throw operation_failed ();
+ }
+
+ // Also add the bases of a polymorphic object.
+ //
+ class_* poly_root (polymorphic (o));
+
+ if (poly_root != 0 && poly_root != &o)
+ {
+ for (class_* b (&polymorphic_base (o));;
+ b = &polymorphic_base (*b))
+ {
+ if (!omap.insert (view_object_map::value_type (b, &*i)).second)
+ {
+ error (i->loc) << "base class '" << class_name (*b) <<
+ "' is used in the view more than once" << endl;
+
+ error (omap[b]->loc) << "previously used here" << endl;
+
+ info (i->loc) << "use the alias clause to assign it a " <<
+ "different name" << endl;
+
+ throw operation_failed ();
+ }
+
+ if (b == poly_root)
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (!amap.insert (
+ view_alias_map::value_type (i->alias, &*i)).second)
+ {
+ error (i->loc) << "alias '" << i->alias << "' is used in " <<
+ "the view more than once" << endl;
+
+ throw operation_failed ();
+ }
+ }
+ }
+ }
+ }
+
+ virtual void
+ traverse_view_post (type& c)
+ {
+ // Handle data members.
+ //
+ {
+ view_data_member t (c);
+ traversal::names n (t);
+ names (c, n);
+ }
+
+ // Figure out if we are versioned. Forced versioning is handled
+ // in relational/processing.
+ //
+ if (column_count (c).soft != 0)
+ c.set ("versioned", true);
+ }
+
+ //
+ // Composite.
+ //
+
+ virtual void
+ traverse_composite_post (type& c)
+ {
+ // Figure out if we are versioned.
+ //
+ if (force_versioned || column_count (c).soft != 0)
+ {
+ c.set ("versioned", true);
+
+ // See if we are "summarily" added/deleted, that is, all the
+ // columns are added/deleted. Note: this does not include
+ // containers.
+ //
+ summary_version sv;
+ sv.traverse (c);
+
+ // Note: there are no locations.
+ //
+ if (sv.av != 0)
+ c.set ("added", sv.av);
+
+ if (sv.dv != 0)
+ c.set ("deleted", sv.dv);
+ }
+ }
+
+ //
+ // Assign object/view pointer.
+ //
+
+ void
+ assign_pointer (type& c)
+ {
+ location_t loc (0); // Pragma location, or 0 if not used.
+
+ try
+ {
+ bool raw;
+ string ptr;
+ string const& type (class_fq_name (c));
+
+ tree decl (0); // Resolved template node.
+ string decl_name; // User-provided template name.
+ tree resolve_scope (0); // Scope in which we resolve names.
+
+ class_pointer const* cp (0);
+ bool cp_template (false);
+
+ if (c.count ("pointer"))
+ {
+ cp = &c.get<class_pointer> ("pointer");
+ }
+ // If we are a derived type in polymorphic hierarchy, then use
+ // our root's pointer type by default.
+ //
+ else if (semantics::class_* r = polymorphic (c))
+ {
+ if (&c != r && r->count ("pointer-template"))
+ cp = r->get<class_pointer const*> ("pointer-template");
+ }
+
+ if (cp != 0)
+ {
+ string const& p (cp->name);
+
+ if (p == "*")
+ {
+ raw = true;
+ ptr = type + "*";
+ cp_template = true;
+ }
+ else if (p[p.size () - 1] == '*')
+ {
+ raw = true;
+ ptr = p;
+ }
+ else if (p.find ('<') != string::npos)
+ {
+ // Template-id.
+ //
+ raw = false; // Fair to assume not raw, though technically can be.
+ ptr = p;
+ decl_name.assign (p, 0, p.find ('<'));
+ }
+ else
+ {
+ // This is not a template-id. Resolve it and see if it is a
+ // template or a type.
+ //
+ decl = resolve_name (p, cp->scope, true);
+ gcc_tree_code_type tc (TREE_CODE (decl));
+
+ if (tc == TYPE_DECL)
+ {
+ raw = (TREE_CODE (TREE_TYPE (decl)) == POINTER_TYPE);
+ ptr = p;
+
+ // This can be a typedef'ed alias for a TR1 template-id.
+ //
+ if (tree ti = TYPE_TEMPLATE_INFO (TREE_TYPE (decl)))
+ {
+ decl = TI_TEMPLATE (ti); // DECL_TEMPLATE
+
+ // Get to the most general template declaration.
+ //
+ while (DECL_TEMPLATE_INFO (decl))
+ decl = DECL_TI_TEMPLATE (decl);
+ }
+ else
+ decl = 0; // Not a template.
+ }
+ else if (tc == TEMPLATE_DECL && DECL_CLASS_TEMPLATE_P (decl))
+ {
+ raw = false;
+ ptr = p + "< " + type + " >";
+ decl_name = p;
+ cp_template = true;
+ }
+ else
+ {
+ error (cp->loc)
+ << "name '" << p << "' specified with db pragma pointer "
+ << "does not name a type or a template" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ // Resolve scope is the scope of the pragma.
+ //
+ resolve_scope = cp->scope;
+ loc = cp->loc;
+ }
+ else
+ {
+ // See if any of the namespaces containing this class specify
+ // a pointer.
+ //
+ for (semantics::scope* s (&class_scope (c));; s = &s->scope_ ())
+ {
+ using semantics::namespace_;
+
+ namespace_* ns (dynamic_cast<namespace_*> (s));
+
+ if (ns == 0) // Some other scope.
+ {
+ if (!s->named_p ())
+ break;
+
+ continue;
+ }
+
+ if (ns->extension ())
+ ns = &ns->original ();
+
+ if (!ns->count ("pointer"))
+ {
+ if (ns->global_scope ()) // Note: namespace always named.
+ break;
+ else
+ continue;
+ }
+
+ cp = &ns->get<class_pointer> ("pointer");
+ string const& p (cp->name);
+
+ // Namespace-specified pointer can only be '*' or are template.
+ //
+ if (p == "*")
+ {
+ raw = true;
+ ptr = type + "*";
+ }
+ else if (p[p.size () - 1] == '*')
+ {
+ error (cp->loc)
+ << "name '" << p << "' specified with db pragma pointer "
+ << "at namespace level cannot be a raw pointer" << endl;
+ }
+ else if (p.find ('<') != string::npos)
+ {
+ error (cp->loc)
+ << "name '" << p << "' specified with db pragma pointer "
+ << "at namespace level cannot be a template-id" << endl;
+ }
+ else
+ {
+ // Resolve this name and make sure it is a template.
+ //
+ decl = resolve_name (p, cp->scope, true);
+ gcc_tree_code_type tc (TREE_CODE (decl));
+
+ if (tc == TEMPLATE_DECL && DECL_CLASS_TEMPLATE_P (decl))
+ {
+ raw = false;
+ ptr = p + "< " + type + " >";
+ decl_name = p;
+ }
+ else
+ {
+ error (cp->loc)
+ << "name '" << p << "' specified with db pragma pointer "
+ << "does not name a template" << endl;
+ }
+ }
+
+ if (ptr.empty ())
+ throw operation_failed ();
+
+ cp_template = true;
+
+ // Resolve scope is the scope of the pragma.
+ //
+ resolve_scope = cp->scope;
+ loc = cp->loc;
+ break;
+ }
+
+ // Use the default pointer.
+ //
+ if (ptr.empty ())
+ {
+ string const& p (options.default_pointer ());
+
+ if (p == "*")
+ {
+ raw = true;
+ ptr = type + "*";
+ }
+ else
+ {
+ raw = false;
+ ptr = p + "< " + type + " >";
+ decl_name = p;
+ }
+
+ // Resolve scope is the scope of the class.
+ //
+ resolve_scope = class_scope (c).tree_node ();
+ }
+ }
+
+ // If this class is a root of a polymorphic hierarchy, then cache
+ // the pointer template so that we can use it for derived classes.
+ //
+ if (cp != 0 && cp_template && polymorphic (c) == &c)
+ c.set ("pointer-template", cp);
+
+ // Check if we are using TR1.
+ //
+ if (decl != 0 || !decl_name.empty ())
+ {
+ bool& tr1 (features.tr1_pointer);
+ bool& boost (features.boost_pointer);
+
+ // First check the user-supplied name.
+ //
+ tr1 = tr1
+ || decl_name.compare (0, 8, "std::tr1") == 0
+ || decl_name.compare (0, 10, "::std::tr1") == 0;
+
+ // If there was no match, also resolve the name since it can be
+ // a using-declaration for a TR1 template.
+ //
+ if (!tr1)
+ {
+ if (decl == 0)
+ decl = resolve_name (decl_name, resolve_scope, false);
+
+ if (TREE_CODE (decl) != TEMPLATE_DECL || !
+ DECL_CLASS_TEMPLATE_P (decl))
+ {
+ // This is only checked for the --default-pointer option.
+ //
+ error (c.file (), c.line (), c.column ())
+ << "name '" << decl_name << "' specified with the "
+ << "--default-pointer option does not name a class "
+ << "template" << endl;
+
+ throw operation_failed ();
+ }
+
+ string n (decl_as_string (decl, TFF_PLAIN_IDENTIFIER));
+
+ // In case of a boost TR1 implementation, we cannot distinguish
+ // between the boost:: and std::tr1:: usage since the latter is
+ // just a using-declaration for the former.
+ //
+ tr1 = tr1
+ || n.compare (0, 8, "std::tr1") == 0
+ || n.compare (0, 10, "::std::tr1") == 0;
+
+ boost = boost
+ || n.compare (0, 17, "boost::shared_ptr") == 0
+ || n.compare (0, 19, "::boost::shared_ptr") == 0;
+ }
+ }
+
+ // Fully-qualify all the unqualified components of the name.
+ //
+ try
+ {
+ lex_.start (ptr);
+ ptr.clear ();
+
+ string t;
+ bool punc (false);
+ bool scoped (false);
+
+ for (cpp_ttype tt (lex_.next (t));
+ tt != CPP_EOF;
+ tt = lex_.next (t))
+ {
+ if (punc && tt > CPP_LAST_PUNCTUATOR)
+ ptr += ' ';
+
+ punc = false;
+
+ switch (static_cast<unsigned> (tt))
+ {
+ case CPP_LESS:
+ {
+ ptr += "< ";
+ break;
+ }
+ case CPP_GREATER:
+ {
+ ptr += " >";
+ break;
+ }
+ case CPP_COMMA:
+ {
+ ptr += ", ";
+ break;
+ }
+ case CPP_NAME:
+ {
+ // If the name was not preceeded with '::', look it
+ // up in the pragmas's scope and add the qualifer.
+ //
+ if (!scoped)
+ {
+ tree decl (resolve_name (t, resolve_scope, false));
+ tree scope (CP_DECL_CONTEXT (decl));
+
+ // If this is an inline namespace, skip it until we get
+ // to the non-inline one.
+ //
+ while (scope != global_namespace)
+ {
+ tree prev (CP_DECL_CONTEXT (scope));
+
+#if BUILDING_GCC_MAJOR >= 8
+ if (!is_nested_namespace (prev, scope, true))
+#else
+ if (!is_associated_namespace (prev, scope))
+#endif
+ break;
+
+ scope = prev;
+ }
+
+ if (scope != global_namespace)
+ {
+ ptr += "::";
+ ptr += decl_as_string (scope, TFF_PLAIN_IDENTIFIER);
+ }
+
+ ptr += "::";
+ }
+
+ ptr += t;
+ punc = true;
+ break;
+ }
+ case CPP_KEYWORD:
+ case CPP_NUMBER:
+ {
+ ptr += t;
+ punc = true;
+ break;
+ }
+ default:
+ {
+ ptr += t;
+ break;
+ }
+ }
+
+ scoped = (tt == CPP_SCOPE);
+ }
+ }
+ catch (cxx_lexer::invalid_input const&)
+ {
+ throw operation_failed ();
+ }
+
+ c.set ("object-pointer", ptr);
+ c.set ("object-pointer-raw", raw);
+ }
+ catch (invalid_name const& ex)
+ {
+ if (loc != 0)
+ error (loc)
+ << "name '" << ex.name () << "' specified with db pragma "
+ << "pointer is invalid" << endl;
+ else
+ error (c.file (), c.line (), c.column ())
+ << "name '" << ex.name () << "' specified with the "
+ << "--default-pointer option is invalid" << endl;
+
+
+ throw operation_failed ();
+ }
+ catch (unable_to_resolve const& ex)
+ {
+ if (loc != 0)
+ error (loc)
+ << "unable to resolve name '" << ex.name () << "' specified "
+ << "with db pragma pointer" << endl;
+ else
+ error (c.file (), c.line (), c.column ())
+ << "unable to resolve name '" << ex.name () << "' specified "
+ << "with the --default-pointer option" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ private:
+ struct invalid_name
+ {
+ invalid_name (string const& n): name_ (n) {}
+
+ string const&
+ name () const {return name_;}
+
+ private:
+ string name_;
+ };
+
+ typedef lookup::unable_to_resolve unable_to_resolve;
+
+ tree
+ resolve_name (string const& qn, tree scope, bool is_type)
+ {
+ try
+ {
+ string tl;
+ tree tn;
+ cpp_ttype tt, ptt;
+
+ nlex_.start (qn);
+ tt = nlex_.next (tl, &tn);
+
+ string name;
+ return lookup::resolve_scoped_name (
+ nlex_, tt, tl, tn, ptt, scope, name, is_type);
+ }
+ catch (cxx_lexer::invalid_input const&)
+ {
+ throw invalid_name (qn);
+ }
+ catch (lookup::invalid_name const&)
+ {
+ throw invalid_name (qn);
+ }
+ }
+
+ private:
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ data_member member_;
+ traversal::names member_names_;
+
+ cxx_string_lexer lex_;
+ cxx_string_lexer nlex_; // Nested lexer.
+
+ semantics::type* std_string_;
+ semantics::names* std_string_hint_;
+
+ tree access_; // odb::access node.
+ };
+
+ static bool
+ check_to_from (const cxx_tokens& ex, const char* c, location_t l)
+ {
+ // Make sure we have one and only one placeholder (?).
+ //
+ bool r (false), m (true);
+
+ for (cxx_tokens::const_iterator i (ex.begin ()), e (ex.end ()); i != e;)
+ {
+ if (i->type == CPP_OPEN_PAREN)
+ {
+ if (++i != e && i->type == CPP_QUERY)
+ {
+ if (++i != e && i->type == CPP_CLOSE_PAREN)
+ {
+ if (r)
+ m = false; // Multiple (?), can't move.
+ else
+ r = true;
+ }
+ }
+ }
+ else
+ ++i;
+ }
+
+ if (!r)
+ {
+ error (l) << "no '(?)' expression in the '" << c << "' clause "
+ << "of db pragma map" << endl;
+
+ throw operation_failed ();
+ }
+
+ return m;
+ }
+}
+
+static void
+process1 (semantics::unit& u)
+{
+ // Process custom C++ type mapping.
+ //
+
+ // Create an empty list if we don't have one. This makes the
+ // rest of the code simpler.
+ //
+ if (!u.count ("custom-cxx-types"))
+ u.set ("custom-cxx-types", custom_cxx_types ());
+
+ custom_cxx_types & cts (u.get<custom_cxx_types> ("custom-cxx-types"));
+
+ for (custom_cxx_types::iterator i (cts.begin ()); i != cts.end (); ++i)
+ {
+ custom_cxx_type& ct (*i);
+
+ // type
+ //
+ if (ct.type_node == 0)
+ {
+ error (ct.loc) << "'type' clause expected in db pragma map" << endl;
+ throw operation_failed ();
+ }
+
+ ct.type = dynamic_cast<semantics::type*> (
+ u.find (TYPE_MAIN_VARIANT (ct.type_node)));
+ ct.type_hint = u.find_hint (ct.type_node);
+
+ // as
+ //
+ if (ct.as_node == 0)
+ {
+ error (ct.loc) << "'as' clause expected in db pragma map" << endl;
+ throw operation_failed ();
+ }
+
+ ct.as = dynamic_cast<semantics::type*> (
+ u.find (TYPE_MAIN_VARIANT (ct.as_node)));
+ ct.as_hint = u.find_hint (ct.as_node);
+
+ // to
+ //
+ {
+ cxx_tokens& e (ct.to);
+
+ if (e.empty ())
+ {
+ e.push_back (cxx_token (0, CPP_OPEN_PAREN));
+ e.push_back (cxx_token (0, CPP_QUERY));
+ e.push_back (cxx_token (0, CPP_CLOSE_PAREN));
+ ct.to_move = true;
+ }
+ else
+ ct.to_move = check_to_from (e, "to", ct.loc);
+ }
+
+ // from
+ //
+ {
+ cxx_tokens& e (ct.from);
+
+ if (e.empty ())
+ {
+ e.push_back (cxx_token (0, CPP_OPEN_PAREN));
+ e.push_back (cxx_token (0, CPP_QUERY));
+ e.push_back (cxx_token (0, CPP_CLOSE_PAREN));
+ ct.from_move = true;
+ }
+ else
+ ct.from_move = check_to_from (e, "from", ct.loc);
+ }
+
+ // Resolve mapping scope.
+ //
+ semantics::scope* s (dynamic_cast<semantics::scope*> (u.find (ct.scope)));
+ if (s == 0)
+ {
+ error (ct.loc) << "unable to resolve db pragma map scope" << endl;
+ throw operation_failed ();
+ }
+
+ if (semantics::namespace_* ns = dynamic_cast<semantics::namespace_*> (s))
+ {
+ if (ns->extension ())
+ s = &ns->original ();
+ }
+
+ // Enter into the map.
+ //
+ if (!s->count ("custom-cxx-type-map"))
+ s->set ("custom-cxx-type-map", custom_cxx_type_map ());
+
+ s->get<custom_cxx_type_map> ("custom-cxx-type-map")[ct.type] = &ct;
+ }
+}
+
+static void
+process2 (options const& ops, features& f, semantics::unit& u)
+{
+ unique_ptr<context> ctx (create_context (cerr, u, ops, f, 0));
+
+ // Common processing.
+ //
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (true);
+ traversal::namespace_ ns;
+ class_ c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (true);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (ctx->unit);
+ }
+
+ // Database-specific processing.
+ //
+ switch (ops.database ()[0])
+ {
+ case database::common:
+ {
+ break;
+ }
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ relational::process ();
+ break;
+ }
+ }
+}
+
+void
+process (options const& ops,
+ features& f,
+ semantics::unit& u,
+ semantics::path const&,
+ unsigned short pass)
+{
+ try
+ {
+ if (pass == 1)
+ process1 (u);
+ else if (pass == 2)
+ process2 (ops, f, u);
+ }
+ catch (operation_failed const&)
+ {
+ // Processing failed. Diagnostics has already been issued.
+ //
+ throw processor_failed ();
+ }
+}
diff --git a/odb/odb/processor.hxx b/odb/odb/processor.hxx
new file mode 100644
index 0000000..1e70cab
--- /dev/null
+++ b/odb/odb/processor.hxx
@@ -0,0 +1,23 @@
+// file : odb/processor.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_PROCESSOR_HXX
+#define ODB_PROCESSOR_HXX
+
+#include <odb/options.hxx>
+#include <odb/features.hxx>
+#include <odb/semantics/unit.hxx>
+
+class processor_failed {};
+
+// Pass one is very early processing before any validation has been
+// done.
+//
+void
+process (options const&,
+ features&,
+ semantics::unit&,
+ semantics::path const&,
+ unsigned short pass);
+
+#endif // ODB_PROCESSOR_HXX
diff --git a/odb/odb/profile.cxx b/odb/odb/profile.cxx
new file mode 100644
index 0000000..1a10bfb
--- /dev/null
+++ b/odb/odb/profile.cxx
@@ -0,0 +1,86 @@
+// file : odb/profile.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <unistd.h> // stat
+#include <sys/types.h> // stat
+#include <sys/stat.h> // stat
+
+#include <iostream>
+
+#include <odb/profile.hxx>
+
+using namespace std;
+
+static bool
+exist (profile_data::path const& p)
+{
+ struct stat info;
+
+ // Just check that the file exist without checking for permissions, etc.
+ //
+ return stat (p.string ().c_str (), &info) == 0 && S_ISREG (info.st_mode);
+}
+
+string
+profile_search (char const* prof, void* arg)
+{
+ typedef profile_data::path path;
+ typedef profile_data::paths paths;
+
+ profile_data* pd (static_cast<profile_data*> (arg));
+ paths const& ps (pd->search_paths);
+
+ path p (prof), odb ("odb"), r;
+ p.normalize (); // Convert '/' to the canonical path separator form.
+ path p_db (p);
+ p_db += "-";
+ p_db += pd->db.string ();
+ p += ".options";
+ p_db += ".options";
+
+ paths::const_iterator i (ps.begin ()), end (ps.end ());
+ for (; i != end; ++i)
+ {
+ // First check for the database-specific version in the search directory
+ // itself and then try the odb/ subdirectory.
+ //
+ if (exist (r = *i / p_db))
+ break;
+
+ if (exist (r = *i / odb / p_db))
+ break;
+
+ // Then try the same with the database-independent version.
+ //
+ if (exist (r = *i / p))
+ break;
+
+ if (exist (r = *i / odb / p))
+ break;
+ }
+
+ if (i == end)
+ {
+ // Ignore the case where we didn't find the profile and this is the
+ // common database.
+ //
+ if (pd->db == database::common)
+ return string ();
+
+ cerr << pd->name << ": error: unable to locate options file for profile '"
+ << prof << "'" << endl;
+ throw profile_failure ();
+ }
+
+ if (pd->loaded.find (r) != pd->loaded.end ())
+ return string ();
+
+ pd->loaded.insert (r);
+ return r.string ();
+}
+
+string
+profile_search_ignore (char const*, void*)
+{
+ return string ();
+}
diff --git a/odb/odb/profile.hxx b/odb/odb/profile.hxx
new file mode 100644
index 0000000..b6e8e53
--- /dev/null
+++ b/odb/odb/profile.hxx
@@ -0,0 +1,39 @@
+// file : odb/profile.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_PROFILE_HXX
+#define ODB_PROFILE_HXX
+
+#include <set>
+#include <vector>
+#include <string>
+
+#include <libcutl/fs/path.hxx>
+
+#include <odb/option-types.hxx>
+
+struct profile_data
+{
+ typedef cutl::fs::path path;
+ typedef std::vector<path> paths;
+
+ profile_data (paths const& p, database d, char const* n)
+ : search_paths (p), db (d), name (n)
+ {
+ }
+
+ paths const& search_paths;
+ database db;
+ char const* name;
+ std::set<path> loaded;
+};
+
+struct profile_failure {};
+
+std::string
+profile_search (char const* profile, void* arg);
+
+std::string
+profile_search_ignore (char const* profile, void* arg);
+
+#endif // ODB_PROFILE_HXX
diff --git a/odb/odb/relational/changelog.cxx b/odb/odb/relational/changelog.cxx
new file mode 100644
index 0000000..99f72da
--- /dev/null
+++ b/odb/odb/relational/changelog.cxx
@@ -0,0 +1,1239 @@
+// file : odb/relational/changelog.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <map>
+
+#include <odb/diagnostics.hxx>
+
+#include <odb/semantics/relational.hxx>
+#include <odb/traversal/relational.hxx>
+
+#include <odb/relational/context.hxx>
+#include <odb/relational/generate.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace changelog
+ {
+ using namespace sema_rel;
+ using sema_rel::model;
+ using sema_rel::changelog;
+
+ typedef map<qname, semantics::node*> deleted_table_map;
+ typedef map<uname, semantics::data_member*> deleted_column_map;
+
+ namespace
+ {
+ //
+ // diff
+ //
+
+ struct diff_table: trav_rel::column,
+ trav_rel::primary_key,
+ trav_rel::foreign_key,
+ trav_rel::index
+ {
+ enum mode_type {mode_add, mode_drop};
+
+ diff_table (table& o,
+ mode_type m,
+ alter_table& a,
+ graph& gr,
+ options const& op,
+ model_version const* v)
+ : other (o), mode (m), at (a), g (gr), ops (op), version (v) {}
+
+ virtual void
+ traverse (sema_rel::column& c)
+ {
+ using sema_rel::column;
+
+ if (mode == mode_add)
+ {
+ if (column* oc = other.find<column> (c.name ()))
+ {
+ if (c.type () != oc->type ())
+ diagnose_column (c, "type", oc->type (), c.type ());
+
+ if (c.null () != oc->null ())
+ {
+ alter_column& ac (g.new_node<alter_column> (c.id ()));
+
+ // Set the alters edge.
+ //
+ column* b (at.lookup<column, drop_column> (c.name ()));
+ assert (b != 0);
+ g.new_edge<alters> (ac, *b);
+
+ ac.null (c.null ());
+ g.new_edge<unames> (at, ac, c.name ());
+ }
+
+ if (c.default_ () != oc->default_ ())
+ diagnose_column (
+ c, "default value", oc->default_ (), c.default_ ());
+
+ if (c.options () != oc->options ())
+ diagnose_column (c, "options", oc->options (), c.options ());
+ }
+ else
+ {
+ if (version != 0)
+ {
+ data_member_path const& mp (
+ c.get<data_member_path> ("member-path"));
+
+ semantics::data_member* m (context::added_member (mp));
+ if (m != 0)
+ {
+ // Make sure the addition version is the current version.
+ //
+ if (context::added (*m) != version->current)
+ {
+ location l (m->get<location_t> ("added-location"));
+ error (l) << "member addition version is not the same " <<
+ "as the current model version" << endl;
+ throw operation_failed ();
+ }
+ }
+ // Warn about hard additions. If the version is closed, then
+ // we have already seen these warnings so no need to repeat
+ // them.
+ //
+ else if (ops.warn_hard_add () && version->open)
+ {
+ // Issue nicer diagnostics for the direct data member case.
+ //
+ if (mp.size () == 1)
+ {
+ location l (mp.back ()->location ());
+ warn (l) << "data member is hard-added" << endl;
+ }
+ else
+ {
+ semantics::class_& s (
+ dynamic_cast<semantics::class_&> (
+ mp.front ()->scope ()));
+
+ warn (s.location ()) << "column '" << c.name () << "' " <<
+ "in class '" << s.name () << "' is hard-added" << endl;
+
+ for (data_member_path::const_iterator i (mp.begin ());
+ i != mp.end (); ++i)
+ {
+ info ((*i)->location ()) << "corresponding hard-" <<
+ "added data member could be '" << (*i)->name () <<
+ "'" << endl;
+ }
+ }
+ }
+ }
+
+ add_column& ac (g.new_node<add_column> (c, at, g));
+ g.new_edge<unames> (at, ac, c.name ());
+ }
+ }
+ else
+ {
+ if (other.find<column> (c.name ()) == 0)
+ {
+ if (version != 0)
+ {
+ // See if we have an entry for this column in the soft-
+ // deleted map.
+ //
+ deleted_column_map const& dm (
+ other.get<deleted_column_map> ("deleted-map"));
+ deleted_column_map::const_iterator i (dm.find (c.name ()));
+
+ if (i != dm.end ())
+ {
+ if (context::deleted (*i->second) != version->current)
+ {
+ location l (i->second->get<location_t> ("deleted-location"));
+ error (l) << "member deletion version is not the same " <<
+ "as the current model version" << endl;
+ throw operation_failed ();
+ }
+ }
+ // Warn about hard deletions. If the version is closed, then
+ // we have already seen these warnings so no need to repeat
+ // them.
+ //
+ else if (ops.warn_hard_delete () && version->open)
+ {
+ // Here all we have is a column name and a class (object)
+ // or data member (container) corresponding to the table.
+ //
+ if (semantics::class_* s =
+ other.get<semantics::class_*> ("class", 0))
+ {
+ warn (s->location ()) << "column '" << c.name () << "' " <<
+ "in class '" << s->name () << "' is hard-deleted" << endl;
+ }
+ else
+ {
+ data_member_path const& mp (
+ other.get<data_member_path> ("member-path"));
+
+ warn (mp.back ()->location ()) << "column '" <<
+ c.name () << "' in container '" <<
+ mp.back ()->name () << "' is hard-deleted" << endl;
+ }
+ }
+ }
+
+ drop_column& dc (g.new_node<drop_column> (c.id ()));
+ g.new_edge<unames> (at, dc, c.name ());
+ }
+ }
+ }
+
+ virtual void
+ traverse (sema_rel::primary_key& pk)
+ {
+ using sema_rel::primary_key;
+
+ if (mode == mode_add)
+ {
+ if (primary_key* opk = other.find<primary_key> (pk.name ()))
+ {
+ if (pk.auto_ () != opk->auto_ ())
+ diagnose_primary_key (pk, "auto kind");
+
+ // Database-specific information.
+ //
+ for (primary_key::extra_map::const_iterator i (
+ pk.extra ().begin ()); i != pk.extra ().end (); ++i)
+ {
+ if (opk->extra ().count (i->first) == 0 ||
+ opk->extra ()[i->first] != i->second)
+ diagnose_primary_key (pk, i->first.c_str ());
+ }
+
+ for (primary_key::extra_map::const_iterator i (
+ opk->extra ().begin ()); i != opk->extra ().end (); ++i)
+ {
+ if (pk.extra ().count (i->first) == 0 ||
+ pk.extra ()[i->first] != i->second)
+ diagnose_primary_key (pk, i->first.c_str ());
+ }
+
+ if (pk.contains_size () != opk->contains_size ())
+ diagnose_primary_key (pk, "member set");
+
+ for (primary_key::contains_size_type i (0);
+ i != pk.contains_size (); ++i)
+ {
+ sema_rel::contains& c (pk.contains_at (i));
+ sema_rel::contains& oc (opk->contains_at (i));
+
+ if (c.column ().name () != oc.column ().name ())
+ diagnose_primary_key (pk, "member set");
+ }
+ }
+ else
+ {
+ location const& l (pk.get<location> ("cxx-location"));
+ error (l) << "adding object id to an existing class is " <<
+ "not supported" << endl;
+ info (l) << "consider re-implementing this change by adding " <<
+ "a new class with the object id, migrating the data, and " <<
+ "deleteing the old class" << endl;
+ throw operation_failed ();
+ }
+ }
+ else
+ {
+ if (other.find<primary_key> (pk.name ()) == 0)
+ {
+ location const& l (other.get<location> ("cxx-location"));
+ error (l) << "deleting object id from an existing class is " <<
+ "not supported" << endl;
+ info (l) << "consider re-implementing this change by adding " <<
+ "a new class without the object id, migrating the data, " <<
+ "and deleteing the old class" << endl;
+ throw operation_failed ();
+ }
+ }
+ }
+
+ virtual void
+ traverse (sema_rel::foreign_key& fk)
+ {
+ using sema_rel::foreign_key;
+
+ if (mode == mode_add)
+ {
+ if (foreign_key* ofk = other.find<foreign_key> (fk.name ()))
+ {
+ if (fk.deferrable () != ofk->deferrable ())
+ diagnose_foreign_key (fk, "deferrable mode");
+
+ if (fk.on_delete () != ofk->on_delete ())
+ diagnose_foreign_key (fk, "on delete action");
+
+ if (fk.referenced_table () != ofk->referenced_table ())
+ // See diagnose_foreign_key() if changing this name.
+ //
+ diagnose_foreign_key (fk, "pointed-to class");
+
+ if (fk.referenced_columns () != ofk->referenced_columns ())
+ diagnose_foreign_key (fk, "id member set");
+
+ if (fk.contains_size () != ofk->contains_size ())
+ diagnose_foreign_key (fk, "id member set");
+
+ for (foreign_key::contains_size_type i (0);
+ i != fk.contains_size (); ++i)
+ {
+ sema_rel::contains& c (fk.contains_at (i));
+ sema_rel::contains& oc (ofk->contains_at (i));
+
+ if (c.column ().name () != oc.column ().name ())
+ diagnose_foreign_key (fk, "id member set");
+ }
+ }
+ else
+ {
+ add_foreign_key& afk (g.new_node<add_foreign_key> (fk, at, g));
+ g.new_edge<unames> (at, afk, fk.name ());
+ }
+ }
+ else
+ {
+ if (other.find<foreign_key> (fk.name ()) == 0)
+ {
+ drop_foreign_key& dfk (g.new_node<drop_foreign_key> (fk.id ()));
+ g.new_edge<unames> (at, dfk, fk.name ());
+ }
+ }
+ }
+
+ virtual void
+ traverse (sema_rel::index& i)
+ {
+ using sema_rel::index;
+
+ if (mode == mode_add)
+ {
+ if (index* oi = other.find<index> (i.name ()))
+ {
+ if (i.type () != oi->type ())
+ diagnose_index (i, "type", oi->type (), i.type ());
+
+ if (i.method () != oi->method ())
+ diagnose_index (i, "method", oi->method (), i.method ());
+
+ if (i.options () != oi->options ())
+ diagnose_index (i, "options", oi->options (), i.options ());
+
+ if (i.contains_size () != oi->contains_size ())
+ diagnose_index (i, "member set", "", "");
+
+ for (index::contains_size_type j (0);
+ j != i.contains_size (); ++j)
+ {
+ sema_rel::contains& c (i.contains_at (j));
+ sema_rel::contains& oc (oi->contains_at (j));
+
+ if (c.column ().name () != oc.column ().name ())
+ diagnose_index (i, "member set", "", "");
+
+ if (c.options () != oc.options ())
+ diagnose_index (
+ i, "member options", oc.options (), c.options ());
+ }
+ }
+ else
+ {
+ add_index& ai (g.new_node<add_index> (i, at, g));
+ g.new_edge<unames> (at, ai, i.name ());
+ }
+ }
+ else
+ {
+ if (other.find<index> (i.name ()) == 0)
+ {
+ drop_index& di (g.new_node<drop_index> (i.id ()));
+ g.new_edge<unames> (at, di, i.name ());
+ }
+ }
+ }
+
+ void
+ diagnose_column (sema_rel::column& c,
+ char const* name,
+ string const& ov,
+ string const& nv)
+ {
+ table& t (c.table ());
+ location const& tl (t.get<location> ("cxx-location"));
+ location const& cl (c.get<location> ("cxx-location"));
+
+ error (cl) << "change to data member results in the change of " <<
+ "the corresponding column " << name;
+
+ if (!ov.empty () || !nv.empty ())
+ cerr << " (old: '" << ov << "', new: '" << nv << "')";
+
+ cerr << endl;
+
+ error (cl) << "this change is not yet handled automatically" << endl;
+ info (cl) << "corresponding column '" << c.name () << "' " <<
+ "originates here" << endl;
+ info (tl) << "corresponding table '" << t.name () << "' " <<
+ "originates here" << endl;
+ info (cl) << "consider re-implementing this change by adding " <<
+ "a new data member with the desired " << name << ", migrating " <<
+ "the data, and deleting the old data member" << endl;
+
+ throw operation_failed ();
+ }
+
+ void
+ diagnose_primary_key (sema_rel::primary_key& pk, char const* name)
+ {
+ location const& l (pk.get<location> ("cxx-location"));
+
+ error (l) << "changing object id " << name << " in an existing " <<
+ "class is not supported" << endl;
+ info (l) << "consider re-implementing this change by adding " <<
+ "a new class with the desired object id " << name << ", " <<
+ "migrating the data, and deleteing the old class" << endl;
+
+ throw operation_failed ();
+ }
+
+ void
+ diagnose_foreign_key (sema_rel::foreign_key& fk, char const* name)
+ {
+ // This can be an object pointer or a polymorphic base link.
+ // The latter will trigger this call if we change one base
+ // to another.
+ //
+ using sema_rel::table;
+ using sema_rel::foreign_key;
+
+ // Polymorphic base link is the first foreign key.
+ //
+ table& t (fk.table ());
+ table::names_iterator p (t.find (fk.name ()));
+
+ if (t.extra ()["kind"] == "polymorphic derived object" &&
+ (p == t.names_begin () || !(--p)->is_a<foreign_key> ()))
+ {
+ location const& l (t.get<location> ("cxx-location"));
+
+ if (name == string ("pointed-to class"))
+ {
+ error (l) << "changing polymorphic base is not " <<
+ "supported" << endl;
+ info (l) << "consider re-implementing this change by adding " <<
+ "a new derived class with the desired base, migrating the " <<
+ "data, and deleteing the old class" << endl;
+ }
+ else
+ {
+ error (l) << "changing polymorphic base " << name <<
+ " is not supported" << endl;
+ info (l) << "consider re-implementing this change by adding " <<
+ "a new derived class with the desired " << name << ", " <<
+ "migrating the data, and deleteing the old class" << endl;
+ }
+ }
+ else
+ {
+ location const& l (fk.get<location> ("cxx-location"));
+
+ error (l) << "changing object pointer " << name << " is not " <<
+ "supported" << endl;
+ info (l) << "consider re-implementing this change by adding " <<
+ "a new object pointer with the desired " << name << ", " <<
+ "migrating the data, and deleteing the old pointer" << endl;
+ }
+
+ throw operation_failed ();
+ }
+
+ void
+ diagnose_index (sema_rel::index& i,
+ char const* name,
+ string const& ov,
+ string const& nv)
+ {
+ table& t (i.table ());
+ location const& tl (t.get<location> ("cxx-location"));
+ location const& il (i.get<location> ("cxx-location"));
+
+ error (il) << "change to index " << name;
+
+ if (!ov.empty () || !nv.empty ())
+ cerr << " (old: '" << ov << "', new: '" << nv << "')";
+
+ cerr << " is not yet handled automatically" << endl;
+
+ info (il) << "corresponding index '" << i.name () << "' " <<
+ "originates here" << endl;
+ info (tl) << "corresponding table '" << t.name () << "' " <<
+ "originates here" << endl;
+ info (il) << "consider re-implementing this change by adding " <<
+ "a new index with the desired " << name << " and deleting the " <<
+ "old one" << endl;
+
+ throw operation_failed ();
+ }
+
+ protected:
+ table& other;
+ mode_type mode;
+ alter_table& at;
+ graph& g;
+ options const& ops;
+ model_version const* version;
+ };
+
+ struct diff_model: trav_rel::table
+ {
+ enum mode_type {mode_add, mode_drop};
+
+ diff_model (model& o,
+ mode_type m,
+ changeset& s,
+ graph& gr,
+ string const& in,
+ options const& op,
+ model_version const* v)
+ : other (o),
+ mode (m),
+ cs (s),
+ g (gr),
+ in_name (in),
+ ops (op),
+ version (v) {}
+
+ virtual void
+ traverse (sema_rel::table& t)
+ {
+ using sema_rel::table;
+
+ if (mode == mode_add)
+ {
+ if (table* ot = other.find<table> (t.name ()))
+ {
+ // See if there are any changes to the table.
+ //
+ alter_table& at (g.new_node<alter_table> (t.id ()));
+
+ // Set the alters edge for lookup.
+ //
+ table* bt (cs.lookup<table, drop_table> (t.name ()));
+ assert (bt != 0);
+ alters& ae (g.new_edge<alters> (at, *bt));
+
+ if (t.options () != ot->options ())
+ diagnose_table (t, "options", ot->options (), t.options ());
+
+ if (ot->extra ()["kind"] != t.extra ()["kind"])
+ diagnose_table (t, "kind",
+ ot->extra ()["kind"], t.extra ()["kind"]);
+
+ {
+ trav_rel::table table;
+ trav_rel::unames names;
+ diff_table dtable (
+ *ot, diff_table::mode_add, at, g, ops, version);
+ table >> names >> dtable;
+ table.traverse (t);
+ }
+
+ {
+ trav_rel::table table;
+ trav_rel::unames names;
+ diff_table dtable (
+ t, diff_table::mode_drop, at, g, ops, version);
+ table >> names >> dtable;
+ table.traverse (*ot);
+ }
+
+ if (!at.names_empty ())
+ g.new_edge<qnames> (cs, at, t.name ());
+ else
+ {
+ g.delete_edge (at, *bt, ae);
+ g.delete_node (at);
+ }
+ }
+ else
+ {
+ // Soft-add is only applicable to containers.
+ //
+ if (version != 0 && t.count ("member-path"))
+ {
+ data_member_path const& mp (
+ t.get<data_member_path> ("member-path"));
+
+ // Make sure the addition version is the current version.
+ //
+ semantics::data_member* m (context::added_member (mp));
+ if (m != 0)
+ {
+ if (context::added (*m) != version->current)
+ {
+ location l (m->get<location_t> ("added-location"));
+ error (l) << "member addition version is not the same " <<
+ "as the current model version" << endl;
+ throw operation_failed ();
+ }
+ }
+ // Warn about hard additions. If the version is closed, then
+ // we have already seen these warnings so no need to repeat
+ // them.
+ //
+ else if (ops.warn_hard_add () && version->open)
+ {
+ // Issue nicer diagnostics for the direct data member case.
+ //
+ if (mp.size () == 1)
+ {
+ location l (mp.back ()->location ());
+ warn (l) << "data member is hard-added" << endl;
+ }
+ else
+ {
+ semantics::class_& s (
+ dynamic_cast<semantics::class_&> (
+ mp.front ()->scope ()));
+
+ warn (s.location ()) << "container table '" <<
+ t.name () << "' in class '" << s.name () << "' is " <<
+ "hard-added" << endl;
+
+ for (data_member_path::const_iterator i (mp.begin ());
+ i != mp.end (); ++i)
+ {
+ info ((*i)->location ()) << "corresponding hard-" <<
+ "added data member could be '" << (*i)->name () <<
+ "'" << endl;
+ }
+ }
+ }
+ }
+
+ add_table& at (g.new_node<add_table> (t, cs, g));
+ g.new_edge<qnames> (cs, at, t.name ());
+ }
+ }
+ else
+ {
+ if (other.find<table> (t.name ()) == 0)
+ {
+ if (version != 0)
+ {
+ // See if we have an entry for this table in the soft-
+ // deleted map.
+ //
+ deleted_table_map const& dm (
+ other.get<deleted_table_map> ("deleted-map"));
+ deleted_table_map::const_iterator i (dm.find (t.name ()));
+
+ if (i != dm.end ())
+ {
+ // This table could be derived either from a class (object)
+ // or data member (container).
+ //
+ semantics::class_* c (
+ dynamic_cast<semantics::class_*> (i->second));
+ if (c != 0 && context::deleted (*c) != version->current)
+ {
+ location l (c->get<location_t> ("deleted-location"));
+ error (l) << "class deletion version is not the same " <<
+ "as the current model version" << endl;
+ throw operation_failed ();
+ }
+
+ semantics::data_member* m (
+ dynamic_cast<semantics::data_member*> (i->second));
+ if (m != 0 && context::deleted (*m) != version->current)
+ {
+ location l (m->get<location_t> ("deleted-location"));
+ error (l) << "member deletion version is not the same " <<
+ "as the current model version" << endl;
+ throw operation_failed ();
+ }
+ }
+ // Warn about hard deletions. If the version is closed, then
+ // we have already seen these warnings so no need to repeat
+ // them.
+ //
+ // At first, it may seem like a good idea not to warn about
+ // class deletions since it is not possible to do anything
+ // useful with a class without referencing it from the code
+ // (in C++ sense). However, things get tricky once we consider
+ // polymorphism since the migration code could be "working"
+ // with the hard-deleted derived class via its base. So we
+ // are going to warn about polymorphic derived tables.
+ //
+ else if (ops.warn_hard_delete () && version->open)
+ {
+ string k (t.extra ()["kind"]);
+
+ // We don't have any sensible location.
+ //
+ if (k == "container")
+ {
+ // We will issue a useless warning if the object table
+ // that this container references is also deleted.
+ // While we could detect this, it is going to require
+ // some effort since we would have to delay the warning
+ // and check later once we know all the deleted tables.
+ //
+ cerr << in_name << ": warning: container table '" <<
+ t.name () << "' is hard-deleted" << endl;
+ }
+ else if (k == "polymorphic derived object")
+ {
+ // The same as above: the warning will be useless if
+ // we are dropping the whole hierarchy.
+ //
+ cerr << in_name << ": warning: polymorphic derived " <<
+ "object table '" << t.name () << "' is hard-deleted" <<
+ endl;
+ }
+ }
+ }
+
+ drop_table& dt (g.new_node<drop_table> (t.id ()));
+ g.new_edge<qnames> (cs, dt, t.name ());
+ }
+ }
+ }
+
+ void
+ diagnose_table (sema_rel::table& t,
+ char const* name,
+ string const& ov,
+ string const& nv)
+ {
+ location const& tl (t.get<location> ("cxx-location"));
+
+ error (tl) << "change to object or container member results in "
+ "the change of the corresponding table " << name;
+
+ if (!ov.empty () || !nv.empty ())
+ cerr << " (old: '" << ov << "', new: '" << nv << "')";
+
+ cerr << endl;
+
+ error (tl) << "this change is not yet handled automatically" << endl;
+ info (tl) << "consider re-implementing this change by adding a " <<
+ "new object or container member with the desired " << name <<
+ ", migrating the data, and deleting the old object or member" <<
+ endl;
+
+ throw operation_failed ();
+ }
+
+ protected:
+ model& other;
+ mode_type mode;
+ changeset& cs;
+ graph& g;
+ string in_name;
+ options const& ops;
+ model_version const* version;
+ };
+
+ // Assumes the new model has cxx-location set. If version is not 0,
+ // then assume it is the current model version and the new model is
+ // the current model which has member paths and deleted maps set.
+ //
+ changeset&
+ diff (model& o,
+ model& n,
+ changelog& l,
+ string const& in_name,
+ options const& ops,
+ model_version const* version)
+ {
+ changeset& r (l.new_node<changeset> (n.version ()));
+
+ // Set the alters edge for lookup. If we are diff'ing two models of
+ // the same version, then use the old model as a base. Otherwise use
+ // the tip of changelog (it should correspond to the old model).
+ //
+ if (o.version () == n.version ())
+ l.new_edge<alters> (r, o);
+ else
+ {
+ if (l.contains_changeset_empty ())
+ {
+ model& m (l.model ());
+
+ // The changelog model version may also be equal to the new model
+ // version if the new base model version is greater than the
+ // latest changeset.
+ //
+ assert (m.version () == o.version () ||
+ m.version () == n.version ());
+
+ l.new_edge<alters> (r, m);
+ }
+ else
+ {
+ changeset& c (l.contains_changeset_back ().changeset ());
+ assert (o.version () == c.version ());
+ l.new_edge<alters> (r, c);
+ }
+ }
+
+ {
+ trav_rel::model model;
+ trav_rel::qnames names;
+ diff_model dmodel (
+ o, diff_model::mode_add, r, l, in_name, ops, version);
+ model >> names >> dmodel;
+ model.traverse (n);
+ }
+
+ {
+ trav_rel::model model;
+ trav_rel::qnames names;
+ diff_model dmodel (
+ n, diff_model::mode_drop, r, l, in_name, ops, version);
+ model >> names >> dmodel;
+ model.traverse (o);
+ }
+
+ return r;
+ }
+
+ //
+ // patch
+ //
+
+ struct patch_table: trav_rel::add_column,
+ trav_rel::drop_column,
+ trav_rel::alter_column,
+ trav_rel::add_index,
+ trav_rel::drop_index,
+ trav_rel::add_foreign_key,
+ trav_rel::drop_foreign_key
+ {
+ patch_table (table& tl, graph& gr): t (tl), g (gr) {}
+
+ virtual void
+ traverse (sema_rel::add_column& ac)
+ {
+ try
+ {
+ column& c (g.new_node<column> (ac, t, g));
+ g.new_edge<unames> (t, c, ac.name ());
+ }
+ catch (duplicate_name const&)
+ {
+ cerr << "error: invalid changelog: column '" << ac.name () <<
+ "' already exists in table '" << t.name () << "'" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ virtual void
+ traverse (sema_rel::drop_column& dc)
+ {
+ table::names_iterator i (t.find (dc.name ()));
+
+ if (i == t.names_end () || !i->nameable ().is_a<column> ())
+ {
+ cerr << "error: invalid changelog: column '" << dc.name () <<
+ "' does not exist in table '" << t.name () << "'" << endl;
+ throw operation_failed ();
+ }
+
+ g.delete_edge (t, i->nameable (), *i);
+ }
+
+ virtual void
+ traverse (sema_rel::alter_column& ac)
+ {
+ if (column* c = t.find<column> (ac.name ()))
+ {
+ if (ac.null_altered ())
+ c->null (ac.null ());
+ }
+ else
+ {
+ cerr << "error: invalid changelog: column '" << ac.name () <<
+ "' does not exist in table '" << t.name () << "'" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ virtual void
+ traverse (sema_rel::add_index& ai)
+ {
+ using sema_rel::index;
+
+ try
+ {
+ index& i (g.new_node<index> (ai, t, g));
+ g.new_edge<unames> (t, i, ai.name ());
+ }
+ catch (duplicate_name const&)
+ {
+ cerr << "error: invalid changelog: index '" << ai.name () <<
+ "' already exists in table '" << t.name () << "'" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ virtual void
+ traverse (sema_rel::drop_index& di)
+ {
+ using sema_rel::index;
+ table::names_iterator i (t.find (di.name ()));
+
+ if (i == t.names_end () || !i->nameable ().is_a<index> ())
+ {
+ cerr << "error: invalid changelog: index '" << di.name () <<
+ "' does not exist in table '" << t.name () << "'" << endl;
+ throw operation_failed ();
+ }
+
+ g.delete_edge (t, i->nameable (), *i);
+ }
+
+ virtual void
+ traverse (sema_rel::add_foreign_key& afk)
+ {
+ using sema_rel::foreign_key;
+
+ try
+ {
+ foreign_key& fk (g.new_node<foreign_key> (afk, t, g));
+ g.new_edge<unames> (t, fk, afk.name ());
+ }
+ catch (duplicate_name const&)
+ {
+ cerr << "error: invalid changelog: foreign key '" << afk.name () <<
+ "' already exists in table '" << t.name () << "'" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ virtual void
+ traverse (sema_rel::drop_foreign_key& dfk)
+ {
+ using sema_rel::foreign_key;
+ table::names_iterator i (t.find (dfk.name ()));
+
+ if (i == t.names_end () || !i->nameable ().is_a<foreign_key> ())
+ {
+ cerr << "error: invalid changelog: foreign key '" << dfk.name () <<
+ "' does not exist in table '" << t.name () << "'" << endl;
+ throw operation_failed ();
+ }
+
+ g.delete_edge (t, i->nameable (), *i);
+ }
+
+ protected:
+ table& t;
+ graph& g;
+ };
+
+ struct patch_model: trav_rel::add_table,
+ trav_rel::drop_table,
+ trav_rel::alter_table
+ {
+ patch_model (model& ml, graph& gr): m (ml), g (gr) {}
+
+ virtual void
+ traverse (sema_rel::add_table& at)
+ {
+ try
+ {
+ table& t (g.new_node<table> (at, m, g));
+ g.new_edge<qnames> (m, t, at.name ());
+ }
+ catch (duplicate_name const&)
+ {
+ cerr << "error: invalid changelog: table '" << at.name () <<
+ "' already exists in model version " << m.version () << endl;
+ throw operation_failed ();
+ }
+ }
+
+ virtual void
+ traverse (sema_rel::drop_table& dt)
+ {
+ model::names_iterator i (m.find (dt.name ()));
+
+ if (i == m.names_end () || !i->nameable ().is_a<table> ())
+ {
+ cerr << "error: invalid changelog: table '" << dt.name () <<
+ "' does not exist in model version " << m.version () << endl;
+ throw operation_failed ();
+ }
+
+ g.delete_edge (m, i->nameable (), *i);
+ }
+
+ virtual void
+ traverse (sema_rel::alter_table& at)
+ {
+ if (table* t = m.find<table> (at.name ()))
+ {
+ trav_rel::alter_table atable;
+ trav_rel::unames names;
+ patch_table ptable (*t, g);
+ atable >> names >> ptable;
+ atable.traverse (at);
+ }
+ else
+ {
+ cerr << "error: invalid changelog: table '" << at.name () <<
+ "' does not exist in model version " << m.version () << endl;
+ throw operation_failed ();
+ }
+ }
+
+ protected:
+ model& m;
+ graph& g;
+ };
+
+ model&
+ patch (model& m, changeset& c, graph& g)
+ {
+ model& r (g.new_node<model> (m, g));
+
+ trav_rel::changeset changeset;
+ trav_rel::qnames names;
+ patch_model pmodel (r, g);
+ changeset >> names >> pmodel;
+ changeset.traverse (c);
+
+ r.version (c.version ());
+ return r;
+ }
+ }
+
+ cutl::shared_ptr<changelog>
+ generate (model& m,
+ model_version const& mv,
+ changelog* old,
+ string const& in_name,
+ string const& out_name,
+ options const& ops)
+ {
+ database db (ops.database ()[0]);
+ cutl::shared_ptr<changelog> cl (
+ new (shared) changelog (db.string (), ops.schema_name ()[db]));
+ graph& g (*cl);
+
+ if (old == 0)
+ {
+ // Don't allow changelog initialization if the version is closed.
+ // This will prevent adding new files to an existing object model
+ // with a closed version.
+ //
+ if (!mv.open)
+ {
+ cerr << out_name << ": error: unable to initialize changelog " <<
+ "because current version is closed" << endl;
+ throw operation_failed ();
+ }
+
+ if (!ops.init_changelog ())
+ cerr << out_name << ": info: initializing changelog with base " <<
+ "version " << mv.base << endl;
+
+ if (mv.base == mv.current)
+ g.new_edge<contains_model> (*cl, g.new_node<model> (m, g));
+ else
+ {
+ // In this case we have to create an empty model at the base
+ // version and a changeset. We do it this way instead of putting
+ // everything into the base model in order to support adding new
+ // header files to the project.
+ //
+ cerr << out_name << ": warning: base and current versions " <<
+ "differ; assuming base model is empty" << endl;
+
+ model& nm (g.new_node<model> (mv.base));
+ g.new_edge<contains_model> (*cl, nm);
+ changeset& c (diff (nm, m, *cl, in_name, ops, &mv));
+
+ if (!c.names_empty ())
+ {
+ g.new_edge<alters_model> (c, nm);
+ g.new_edge<contains_changeset> (*cl, c);
+ }
+ }
+
+ return cl;
+ }
+
+ // Get the changelog base and current versions and do some sanity
+ // checks.
+ //
+ version bver (old->model ().version ());
+ version cver (
+ old->contains_changeset_empty ()
+ ? bver
+ : old->contains_changeset_back ().changeset ().version ());
+
+ if (mv.base < bver)
+ {
+ cerr << in_name << ": error: latest changelog base version is " <<
+ "greater than model base version" << endl;
+ throw operation_failed ();
+ }
+
+ if (mv.current < cver)
+ {
+ cerr << in_name << ": error: latest changelog current version is " <<
+ "greater than model current version" << endl;
+ throw operation_failed ();
+ }
+
+ // Build the new changelog.
+ //
+ model& oldm (old->model ());
+
+ // Now we have a case with a "real" old model (i.e., non-empty
+ // and with version older than current) as well as zero or more
+ // changeset.
+ //
+ //
+ model* last (&g.new_node<model> (oldm, g));
+ model* base (bver == mv.base && mv.base != mv.current ? last : 0);
+ if (base != 0)
+ g.new_edge<contains_model> (*cl, *base);
+
+ for (changelog::contains_changeset_iterator i (
+ old->contains_changeset_begin ());
+ i != old->contains_changeset_end (); ++i)
+ {
+ changeset& cs (i->changeset ());
+
+ // Don't copy the changeset for the current version. Instead, we
+ // will re-create it from scratch.
+ //
+ if (cs.version () == mv.current)
+ break;
+
+ model& prev (*last);
+ last = &patch (prev, cs, g);
+
+ if (base == 0)
+ {
+ if (last->version () == mv.base)
+ {
+ base = last;
+ g.new_edge<contains_model> (*cl, *base);
+ }
+ else if (last->version () > mv.base)
+ {
+ // We have a gap. Plug it with an empty base model. We will
+ // also need to create a new changeset for this step.
+ //
+ base = &g.new_node<model> (mv.base);
+ g.new_edge<contains_model> (*cl, *base);
+
+ changeset& c (diff (*base, *last, *cl, in_name, ops, 0));
+ if (!c.names_empty ())
+ {
+ g.new_edge<alters_model> (c, *base);
+ g.new_edge<contains_changeset> (*cl, c);
+ }
+
+ continue;
+ }
+ }
+
+ // Copy the changeset unless it is below or at our base version.
+ //
+ if (last->version () <= mv.base)
+ continue;
+
+ changeset& c (
+ g.new_node<changeset> (
+ cs,
+ cl->contains_changeset_empty ()
+ ? static_cast<qscope&> (*base) // Cannot be NULL.
+ : cl->contains_changeset_back ().changeset (),
+ g));
+
+ g.new_edge<alters_model> (c, prev);
+ g.new_edge<contains_changeset> (*cl, c);
+ }
+
+ // If we still haven't found the new base model, then it means it
+ // has version greater than any changeset we have seen.
+ //
+ if (base == 0)
+ {
+ if (mv.base == mv.current)
+ base = &g.new_node<model> (m, g);
+ else
+ {
+ // Fast-forward the latest model to the new base.
+ //
+ base = last;
+ base->version (mv.base);
+ }
+
+ g.new_edge<contains_model> (*cl, *base);
+ }
+
+ // If the current version is closed, make sure the model hasn't
+ // changed.
+ //
+ if (!mv.open)
+ {
+ // If the last changeset has the current version, then apply it.
+ //
+ model* om (last);
+ if (!old->contains_changeset_empty ())
+ {
+ changeset& c (old->contains_changeset_back ().changeset ());
+ if (c.version () == mv.current)
+ om = &patch (*last, c, g);
+ }
+
+ changeset& c (diff (*om, m, *cl, in_name, ops, &mv));
+
+ if (!c.names_empty ())
+ {
+ qnames& n (*c.names_begin ());
+
+ cerr << out_name << ": error: current version is closed" << endl;
+ cerr << out_name << ": info: first new change is " <<
+ n.nameable ().kind () << " '" << n.name () << "'" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ // Add a changeset for the current version unless it is the same
+ // as the base version.
+ //
+ if (mv.base != mv.current)
+ {
+ // Add it even if it is empty. This can be useful, for example,
+ // for data-only migrations were the user relies on the database
+ // version being updated in the version table.
+ //
+ changeset& c (diff (*last, m, *cl, in_name, ops, &mv));
+ g.new_edge<alters_model> (c, *last);
+ g.new_edge<contains_changeset> (*cl, c);
+ }
+
+ return cl;
+ }
+ }
+}
diff --git a/odb/odb/relational/common-query.cxx b/odb/odb/relational/common-query.cxx
new file mode 100644
index 0000000..53321ce
--- /dev/null
+++ b/odb/odb/relational/common-query.cxx
@@ -0,0 +1,169 @@
+// file : odb/relational/common-query.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/common-query.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ // query_alias_traits
+ //
+
+ void query_alias_traits::
+ generate_decl_body ()
+ {
+ os << "static const char table_name[];";
+ }
+
+ void query_alias_traits::
+ generate_def (semantics::data_member& m, semantics::class_& c)
+ {
+ // Come up with a table alias. Generally, we want it to be based
+ // on the column name. This is straightforward for single-column
+ // references. In case of a composite id, we will need to use the
+ // column prefix which is based on the data member name, unless
+ // overridden by the user. In the latter case the prefix can be
+ // empty, in which case we will just fall back on the member's
+ // public name.
+ //
+ string alias;
+ {
+ string n;
+
+ if (composite_wrapper (utype (*id_member (c))))
+ {
+ n = column_prefix (m, key_prefix_, default_name_).prefix;
+
+ if (n.empty ())
+ n = public_name_db (m);
+ else if (n[n.size () - 1] == '_')
+ n.resize (n.size () - 1); // Remove trailing underscore.
+ }
+ else
+ {
+ bool dummy;
+ n = column_name (m, key_prefix_, default_name_, dummy);
+ }
+
+ alias = column_prefix_.prefix + n;
+ }
+
+ generate_def (public_name (m), c, alias);
+ }
+
+ void query_alias_traits::
+ generate_def (string const& tag, semantics::class_& c, string const& alias)
+ {
+ semantics::class_* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+ semantics::class_* poly_base (poly_derived ? &polymorphic_base (c) : 0);
+
+ if (poly_derived)
+ generate_def (tag, *poly_base, alias);
+
+ os << "const char alias_traits<"
+ << " " << class_fq_name (c) << "," << endl
+ << " id_" << db << "," << endl
+ << " " << scope_ << "::" << tag << "_tag>::" << endl
+ << "table_name[] = ";
+
+ if (poly_root != 0)
+ os << strlit (quote_id (alias + "_" + table_name (c).uname ()));
+ else
+ os << strlit (quote_id (alias));
+
+ os << ";"
+ << endl;
+ }
+
+ entry<query_alias_traits> query_alias_traits_;
+
+
+ // query_columns_base
+ //
+
+ entry<query_columns_base> query_columns_base_;
+
+ // query_columns
+ //
+
+ void query_columns::
+ column_ctor (string const& type, string const& name, string const& base)
+ {
+ os << name << " (";
+
+ if (multi_dynamic)
+ os << "odb::query_column< " << type << " >& qc," << endl;
+
+ os << "const char* t, const char* c, const char* conv)" << endl
+ << " : " << base << " (" << (multi_dynamic ? "qc, " : "") <<
+ "t, c, conv)"
+ << "{"
+ << "}";
+ }
+
+ void query_columns::
+ column_ctor_args_extra (semantics::data_member&)
+ {
+ }
+
+ void query_columns::
+ column_common (semantics::data_member& m,
+ string const& type,
+ string const& column,
+ string const& suffix)
+ {
+ string name (public_name (m));
+
+ if (decl_)
+ {
+ string type_id (database_type_id (m));
+
+ os << "// " << name << endl
+ << "//" << endl;
+
+ os << "typedef" << endl
+ << db << "::query_column<" << endl
+ << " " << db << "::value_traits<" << endl
+ << " " << type << "," << endl
+ << " " << type_id << " >::query_type," << endl
+ << " " << type_id << " >" << endl
+ << name << suffix << ";"
+ << endl;
+ }
+ else
+ {
+ // Note that here we don't use suffix.
+ //
+ string tmpl (ptr_ ? "pointer_query_columns" : "query_columns");
+ tmpl += "< " + fq_name_ + ", id_" + db.string () + ", A >" + scope_;
+
+ os << "template <typename A>" << endl
+ << "const typename " << tmpl << "::" << name << "_type_" << endl
+ << tmpl << "::" << endl
+ << name << " (";
+
+ // Pass common query column for registration.
+ //
+ if (multi_dynamic)
+ {
+ string tmpl (ptr_ ? "pointer_query_columns" : "query_columns");
+ tmpl += "< " + fq_name_ + ", id_common, typename A::common_traits >" +
+ scope_;
+
+ os << tmpl << "::" << name << "," << endl;
+ }
+
+ os << "A::" << "table_name, " << strlit (quote_id (column));
+
+ string const& conv (convert_to_expr (column_type (), m));
+ os << ", " << (conv.empty () ? "0" : strlit (conv));
+
+ column_ctor_args_extra (m);
+
+ os << ");"
+ << endl;
+ }
+ }
+}
diff --git a/odb/odb/relational/common-query.hxx b/odb/odb/relational/common-query.hxx
new file mode 100644
index 0000000..c29df6b
--- /dev/null
+++ b/odb/odb/relational/common-query.hxx
@@ -0,0 +1,63 @@
+// file : odb/relational/common-query.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_COMMON_QUERY_HXX
+#define ODB_RELATIONAL_COMMON_QUERY_HXX
+
+#include <odb/relational/common.hxx>
+
+namespace relational
+{
+ //
+ //
+ struct query_alias_traits: ::query_alias_traits, virtual context
+ {
+ typedef query_alias_traits base_impl;
+
+ query_alias_traits (base const& x): base (x) {}
+
+ virtual void
+ generate_decl_body ();
+
+ virtual void
+ generate_def (semantics::data_member&, semantics::class_&);
+
+ virtual void
+ generate_def (string const& tag, semantics::class_&, string const& alias);
+ };
+
+ //
+ //
+ struct query_columns_base: ::query_columns_base, virtual context
+ {
+ typedef query_columns_base base_impl;
+
+ query_columns_base (base const& x): base (x) {const_ = "const ";}
+ };
+
+ //
+ //
+ struct query_columns: ::query_columns, virtual context
+ {
+ typedef query_columns base_impl;
+
+ query_columns (base const& x): base (x) {const_ = "const ";}
+
+ virtual string
+ database_type_id (semantics::data_member&) = 0;
+
+ virtual void
+ column_ctor (string const& type, string const& name, string const& base);
+
+ virtual void
+ column_ctor_args_extra (semantics::data_member&);
+
+ virtual void
+ column_common (semantics::data_member&,
+ string const& type,
+ string const& column,
+ string const& suffix);
+ };
+}
+
+#endif // ODB_RELATIONAL_COMMON_QUERY_HXX
diff --git a/odb/odb/relational/common.cxx b/odb/odb/relational/common.cxx
new file mode 100644
index 0000000..5c9126c
--- /dev/null
+++ b/odb/odb/relational/common.cxx
@@ -0,0 +1,27 @@
+// file : odb/relational/common.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/common.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ // member_image_type
+ //
+ string member_image_type::
+ image_type (semantics::data_member&)
+ {
+ assert (false);
+ return string ();
+ }
+
+ // member_database_type_id
+ //
+ string member_database_type_id::
+ database_type_id (semantics::data_member&)
+ {
+ assert (false);
+ return string ();
+ }
+}
diff --git a/odb/odb/relational/common.hxx b/odb/odb/relational/common.hxx
new file mode 100644
index 0000000..01266a0
--- /dev/null
+++ b/odb/odb/relational/common.hxx
@@ -0,0 +1,273 @@
+// file : odb/relational/common.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_COMMON_HXX
+#define ODB_RELATIONAL_COMMON_HXX
+
+#include <set>
+#include <cassert>
+
+#include <odb/common.hxx>
+#include <odb/relational/context.hxx>
+
+namespace relational
+{
+ struct member_base: traversal::data_member, virtual context
+ {
+ typedef member_base base;
+
+ member_base (semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix,
+ object_section* section = 0)
+ : type_override_ (type),
+ custom_override_ (ct),
+ fq_type_override_ (fq_type),
+ key_prefix_ (key_prefix),
+ section_ (section),
+ top_level_ (false)
+ {
+ }
+
+ member_base (string const& var,
+ semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix,
+ object_section* section = 0)
+ : var_override_ (var),
+ type_override_ (type),
+ custom_override_ (ct),
+ fq_type_override_ (fq_type),
+ key_prefix_ (key_prefix),
+ section_ (section),
+ top_level_ (false)
+ {
+ }
+
+ protected:
+ // For virtual inheritance only. Should not be actually called.
+ //
+ member_base ();
+
+ protected:
+ string var_override_;
+ semantics::type* type_override_;
+ const custom_cxx_type* custom_override_;
+ string fq_type_override_;
+ string key_prefix_;
+ object_section* section_;
+
+ // True during the top-level call of pre() and post() below. Note that
+ // call via another tarverser (e.g., for a class) is not considered top-
+ // level.
+ //
+ bool top_level_;
+ };
+
+ // Template argument is the database SQL type (sql_type).
+ //
+ template <typename T>
+ struct member_base_impl: virtual member_base
+ {
+ typedef member_base_impl base_impl;
+
+ member_base_impl (base const& x): base (x) {}
+
+ protected:
+ member_base_impl () {}
+
+ public:
+ virtual T const&
+ member_sql_type (semantics::data_member&) = 0;
+
+ void
+ traverse (semantics::data_member& m, bool top_level)
+ {
+ top_level_ = top_level;
+ traverse (m);
+ top_level_ = false;
+ }
+
+ struct member_info
+ {
+ semantics::data_member& m; // Member.
+ semantics::type& t; // Cvr-unqualified member C++ type, note
+ // that m.type () may not be the same as t.
+ const custom_cxx_type* ct; // Translation used for t, if any.
+ semantics::class_* ptr; // Pointed-to object if m is an object
+ // pointer. In this case t is the id type
+ // while fq_type_ is the pointer fq-type.
+ semantics::type* wrapper; // Wrapper type if member is a composite or
+ // container wrapper, also cvr-unqualified.
+ // In this case t is the wrapped type.
+ bool cq; // True if the original (wrapper) type
+ // is const-qualified.
+ T const* st; // Member SQL type (only simple values).
+ string& var; // Member variable name with trailing '_'.
+
+ // C++ type fq-name.
+ //
+ string
+ fq_type (bool unwrap = true) const
+ {
+ semantics::names* hint;
+
+ if (wrapper != 0 && unwrap)
+ {
+ // Use the hint from the wrapper unless the wrapped type
+ // is qualified.
+ //
+ hint = wrapper->get<semantics::names*> ("wrapper-hint");
+ utype (*context::wrapper (*wrapper), hint);
+ return t.fq_name (hint);
+ }
+
+ // Use the original type from 'm' instead of 't' since the hint may
+ // be invalid for a different type. Plus, if a type is overriden,
+ // then the fq_type must be as well.
+ //
+ if (ptr != 0)
+ {
+ semantics::type& t (utype (*id_member (*ptr), hint));
+ return t.fq_name (hint);
+ }
+ else if (fq_type_.empty ())
+ {
+ semantics::type& t (utype (m, hint));
+ return t.fq_name (hint);
+ }
+ else
+ // If we are translated, then fq_type_ contains the original type.
+ //
+ return ct == 0 ? fq_type_ : t.fq_name (ct->as_hint);
+ }
+
+ string
+ ptr_fq_type () const
+ {
+ assert (ptr != 0);
+
+ if (fq_type_.empty ())
+ {
+ // If type is overridden so should fq_type so it is safe to
+ // get the type from the member.
+ //
+ semantics::names* hint;
+ semantics::type& t (utype (m, hint));
+ return t.fq_name (hint);
+ }
+ else
+ return fq_type_;
+ }
+
+ string const& fq_type_;
+
+ member_info (semantics::data_member& m_,
+ semantics::type& t_,
+ const custom_cxx_type* ct_,
+ semantics::type* wrapper_,
+ bool cq_,
+ string& var_,
+ string const& fq_type)
+ : m (m_),
+ t (t_),
+ ct (ct_),
+ ptr (0),
+ wrapper (wrapper_),
+ cq (cq_),
+ st (0),
+ var (var_),
+ fq_type_ (fq_type)
+ {
+ }
+ };
+
+ bool
+ container (member_info& mi)
+ {
+ // This cannot be a container if we have a type override.
+ //
+ return type_override_ == 0 && context::container (mi.m);
+ }
+
+ // The false return value indicates that no further callbacks should be
+ // called for this member.
+ //
+ virtual bool
+ pre (member_info&) {return true;}
+
+ virtual void
+ post (member_info&) {}
+
+ // Note: calling these directly will mess up the top_level logic.
+ //
+ protected:
+ virtual void
+ traverse_composite (member_info&) {}
+
+ virtual void
+ traverse_container (member_info&) {}
+
+ // Note that by default traverse_pointer() will traverse the
+ // pointed-to object id type.
+ //
+ virtual void
+ traverse_pointer (member_info&);
+
+ virtual void
+ traverse_simple (member_info&) {}
+
+ private:
+ virtual void
+ traverse (semantics::data_member&);
+ };
+
+ //
+ //
+ struct member_image_type: virtual member_base
+ {
+ typedef member_image_type base;
+
+ member_image_type (): member_base (0, 0, string (), string ()) {}
+
+ member_image_type (semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type = string (),
+ string const& key_prefix = string ())
+ : member_base (type, ct, fq_type, key_prefix) {}
+
+ // Has to be overriden.
+ //
+ virtual string
+ image_type (semantics::data_member&);
+ };
+
+ //
+ //
+ struct member_database_type_id: virtual member_base
+ {
+ typedef member_database_type_id base;
+
+ member_database_type_id (): member_base (0, 0, string (), string ()) {}
+ member_database_type_id (semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type = string (),
+ string const& key_prefix = string ())
+ : member_base (type, ct, fq_type, key_prefix) {}
+
+ // Has to be overriden.
+ //
+ virtual string
+ database_type_id (semantics::data_member&);
+ };
+}
+
+#include <odb/relational/common.txx>
+
+// Other common parts.
+//
+#include <odb/relational/common-query.hxx>
+
+#endif // ODB_RELATIONAL_COMMON_HXX
diff --git a/odb/odb/relational/common.txx b/odb/odb/relational/common.txx
new file mode 100644
index 0000000..82a4a4a
--- /dev/null
+++ b/odb/odb/relational/common.txx
@@ -0,0 +1,125 @@
+// file : odb/relational/common.txx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+namespace relational
+{
+ //
+ // member_base_impl
+ //
+
+ template <typename T>
+ void member_base_impl<T>::
+ traverse (semantics::data_member& m)
+ {
+ if (transient (m))
+ return;
+
+ string var;
+
+ if (!var_override_.empty ())
+ var = var_override_;
+ else
+ {
+ string const& name (m.name ());
+ var = name + (name[name.size () - 1] == '_' ? "" : "_");
+ }
+
+ bool cq (type_override_ != 0 ? false : const_member (m));
+ const custom_cxx_type* ct (type_override_ != 0 ? custom_override_ : 0);
+ semantics::type& t (type_override_ != 0
+ ? *type_override_
+ : utype (m, &ct));
+
+ semantics::type* cont;
+ if (semantics::class_* c = object_pointer (t))
+ {
+ // A pointer in view might point to an object without id.
+ //
+ data_member_path* id (id_member (*c));
+ semantics::type& t (id != 0 ? utype (*id, &ct) : utype (m, &ct));
+ semantics::class_* comp (id != 0 ? composite_wrapper (t) : 0);
+
+ member_info mi (m,
+ (comp != 0 ? *comp : t),
+ ct,
+ (comp != 0 && wrapper (t) ? &t : 0),
+ cq,
+ var,
+ fq_type_override_); // Pointer type.
+
+ mi.ptr = c;
+
+ // Pointer in views aren't really a "column".
+ //
+ if (!view_member (m) && comp == 0)
+ mi.st = &member_sql_type (m);
+
+ if (pre (mi))
+ {
+ traverse_pointer (mi);
+ post (mi);
+ }
+ }
+ else if (semantics::class_* c = composite_wrapper (t))
+ {
+ // If t is a wrapper, pass the wrapped type. Also pass the
+ // original, wrapper type.
+ //
+ member_info mi (m,
+ *c,
+ ct,
+ (wrapper (t) ? &t : 0),
+ cq,
+ var,
+ fq_type_override_);
+ if (pre (mi))
+ {
+ traverse_composite (mi);
+ post (mi);
+ }
+ }
+ // This cannot be a container if we have a type override.
+ //
+ else if (type_override_ == 0 && (cont = context::container (m)))
+ {
+ // The same unwrapping logic as for composite values.
+ //
+ member_info mi (m,
+ *cont,
+ 0, // Cannot be mapped.
+ (wrapper (t) ? &t : 0),
+ cq,
+ var,
+ fq_type_override_);
+ if (pre (mi))
+ {
+ traverse_container (mi);
+ post (mi);
+ }
+ }
+ else
+ {
+ member_info mi (m, t, ct, 0, cq, var, fq_type_override_);
+ mi.st = &member_sql_type (m);
+
+ if (pre (mi))
+ {
+ traverse_simple (mi);
+ post (mi);
+ }
+ }
+ }
+
+ template <typename T>
+ void member_base_impl<T>::
+ traverse_pointer (member_info& mi)
+ {
+ if (!view_member (mi.m)) // Not really "as if" pointed-to id member.
+ {
+ if (composite (mi.t)) // Already unwrapped.
+ traverse_composite (mi);
+ else
+ traverse_simple (mi);
+ }
+ }
+}
diff --git a/odb/odb/relational/context.cxx b/odb/odb/relational/context.cxx
new file mode 100644
index 0000000..3fba69b
--- /dev/null
+++ b/odb/odb/relational/context.cxx
@@ -0,0 +1,169 @@
+// file : odb/relational/context.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/relational/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ context* context::current_;
+
+ context::
+ ~context ()
+ {
+ if (current_ == this)
+ current_ = 0;
+ }
+
+ context::
+ context ()
+ : data_ (current ().data_),
+ model (current ().model),
+ generate_grow (current ().generate_grow),
+ need_alias_as (current ().need_alias_as),
+ insert_send_auto_id (current ().insert_send_auto_id),
+ delay_freeing_statement_result (current ().delay_freeing_statement_result),
+ need_image_clone (current ().need_image_clone),
+ generate_bulk (current ().generate_bulk),
+ global_index (current ().global_index),
+ global_fkey (current ().global_fkey),
+ bind_vector (data_->bind_vector_),
+ truncated_vector (data_->truncated_vector_)
+ {
+ }
+
+ context::
+ context (data* d, sema_rel::model* m)
+ : data_ (d),
+ model (m),
+ bind_vector (data_->bind_vector_),
+ truncated_vector (data_->truncated_vector_)
+ {
+ assert (current_ == 0);
+ current_ = this;
+ }
+
+ string context::
+ index_name (qname const& table, string const& base)
+ {
+ string n;
+
+ if (options.index_suffix ().count (db) != 0)
+ n = base + options.index_suffix ()[db];
+ else
+ n = compose_name (base, "i");
+
+ // If this database has global index names, then add the table
+ // name as a prefix (the schema, if needed, will be added by
+ // database-specific create_index overrides).
+ //
+ if (global_index)
+ n = compose_name (table.uname (), n);
+
+ return transform_name (n, sql_name_index);
+ }
+
+ string context::
+ fkey_name (qname const& table, string const& base)
+ {
+ string n;
+
+ if (options.fkey_suffix ().count (db) != 0)
+ n = base + options.fkey_suffix ()[db];
+ else
+ n = compose_name (base, "fk");
+
+ // If this database has global index names, then add the table
+ // name as a prefix (the schema, if needed, will be added by
+ // database-specific create_foreign_key overrides).
+ //
+ if (global_fkey)
+ n = compose_name (table.uname (), n);
+
+ return transform_name (n, sql_name_fkey);
+ }
+
+ string context::
+ convert (string const& e, string const& c)
+ {
+ size_t p (c.find ("(?)"));
+ string r (c, 0, p);
+ r += e;
+ r.append (c, p + 3, string::npos);
+ return r;
+ }
+
+ string const& context::
+ convert_expr (string const&, semantics::data_member&, bool)
+ {
+ assert (false);
+ throw operation_failed ();
+ }
+
+ bool context::
+ grow_impl (semantics::class_&, user_section*)
+ {
+ return false;
+ }
+
+ bool context::
+ grow_impl (semantics::data_member&)
+ {
+ return false;
+ }
+
+ bool context::
+ grow_impl (semantics::data_member&,
+ semantics::type&,
+ const custom_cxx_type*,
+ string const&)
+ {
+ return false;
+ }
+
+ string context::
+ quote_string_impl (string const& s) const
+ {
+ string r;
+ r.reserve (s.size ());
+ r += '\'';
+
+ for (string::size_type i (0), n (s.size ()); i < n; ++i)
+ {
+ if (s[i] == '\'')
+ r += "''";
+ else
+ r += s[i];
+ }
+
+ r += '\'';
+ return r;
+ }
+
+ string context::
+ quote_id_impl (qname const& id) const
+ {
+ string r;
+
+ bool f (true);
+ for (qname::iterator i (id.begin ()); i < id.end (); ++i)
+ {
+ if (i->empty ())
+ continue;
+
+ if (f)
+ f = false;
+ else
+ r += '.';
+
+ r += '"';
+ r += *i;
+ r += '"';
+ }
+
+ return r;
+ }
+}
diff --git a/odb/odb/relational/context.hxx b/odb/odb/relational/context.hxx
new file mode 100644
index 0000000..db9b5be
--- /dev/null
+++ b/odb/odb/relational/context.hxx
@@ -0,0 +1,289 @@
+// file : odb/relational/context.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_CONTEXT_HXX
+#define ODB_RELATIONAL_CONTEXT_HXX
+
+#include <odb/context.hxx>
+
+#include <odb/semantics/relational.hxx>
+#include <odb/traversal/relational.hxx>
+
+namespace relational
+{
+ namespace sema_rel = semantics::relational;
+ namespace trav_rel = traversal::relational;
+
+ enum statement_kind
+ {
+ statement_select,
+ statement_insert,
+ statement_update,
+ statement_delete,
+ statement_where // WHERE clause.
+ };
+
+ // Index.
+ //
+ struct index
+ {
+ location_t loc; // Location of this index definition.
+ std::string name; // If empty, then derive from the member name.
+ std::string type; // E.g., "UNIQUE", etc.
+ std::string method; // E.g., "BTREE", etc.
+ std::string options; // Database-specific index options.
+
+ struct member
+ {
+ location_t loc; // Location of this member specifier.
+ std::string name; // Member name, e.g., foo_, foo_.bar_.
+ data_member_path path; // Member path.
+ std::string options; // Member options, e.g., "ASC", etc.
+ };
+ typedef std::vector<member> members_type;
+
+ members_type members;
+ };
+
+ typedef std::vector<index> indexes;
+
+ // Indexes in the above vector are in location order.
+ //
+ struct index_comparator
+ {
+ bool
+ operator() (index const& x, index const& y) const
+ {
+ return x.loc < y.loc;
+ }
+ };
+
+ // Custom database type mapping.
+ //
+ struct custom_db_type
+ {
+ regex type;
+ std::string as;
+ std::string to;
+ std::string from;
+ location_t loc;
+ };
+
+ typedef std::vector<custom_db_type> custom_db_types;
+
+ class context: public virtual ::context
+ {
+ public:
+ // Return true if an object or value type has members for which
+ // the image can grow. If section is not specified, then ignore
+ // separately loaded members. Otherwise ignore members that do
+ // not belong to the section.
+ //
+ bool
+ grow (semantics::class_&, user_section* = 0);
+
+ // The same for a member's value type.
+ //
+ bool
+ grow (semantics::data_member&);
+
+ bool
+ grow (semantics::data_member&,
+ semantics::type&,
+ const custom_cxx_type*,
+ string const& key_prefix);
+
+ public:
+ // Quote SQL string.
+ //
+ string
+ quote_string (string const&) const;
+
+ // Quote SQL identifier.
+ //
+ string
+ quote_id (string const&) const;
+
+ string
+ quote_id (qname const&) const;
+
+ // Quoted column and table names.
+ //
+ string
+ column_qname (semantics::data_member& m, column_prefix const& cp) const
+ {
+ return quote_id (column_name (m, cp));
+ }
+
+ string
+ column_qname (data_member_path const& mp) const
+ {
+ return quote_id (column_name (mp));
+ }
+
+ string
+ column_qname (semantics::data_member& m,
+ string const& key_prefix,
+ string const& default_name,
+ column_prefix const& cp) const
+ {
+ return quote_id (column_name (m, key_prefix, default_name, cp));
+ }
+
+ string
+ table_qname (semantics::class_& c) const
+ {
+ return quote_id (table_name (c));
+ }
+
+ string
+ table_qname (semantics::class_& obj, data_member_path const& mp) const
+ {
+ return quote_id (table_name (obj, mp));
+ }
+
+ string
+ table_qname (semantics::data_member& m, table_prefix const& p) const
+ {
+ return quote_id (table_name (m, p));
+ }
+
+ public:
+ string
+ index_name (qname const& table, string const& base);
+
+ string
+ fkey_name (qname const& table, string const& base);
+
+ // Custom database type conversion.
+ //
+ public:
+ string
+ convert_to (string const& expr,
+ string const& sqlt,
+ semantics::data_member& m)
+ {
+ string const& conv (current ().convert_expr (sqlt, m, true));
+ return conv.empty () ? expr : convert (expr, conv);
+ }
+
+ string
+ convert_from (string const& expr,
+ string const& sqlt,
+ semantics::data_member& m)
+ {
+ string const& conv (current ().convert_expr (sqlt, m, false));
+ return conv.empty () ? expr : convert (expr, conv);
+ }
+
+ // These shortcut versions should only be used on special members
+ // (e.g., auto id, version, etc) since they may not determine the
+ // proper SQL type in other cases (prefixes, composite ids, etc).
+ //
+ string
+ convert_to (string const& expr, semantics::data_member& m)
+ {
+ return convert_to (expr, column_type (m), m);
+ }
+
+ string
+ convert_from (string const& expr, semantics::data_member& m)
+ {
+ return convert_from (expr, column_type (m), m);
+ }
+
+ // Return the conversion expression itself.
+ //
+ string const&
+ convert_to_expr (string const& sqlt, semantics::data_member& m)
+ {
+ return current ().convert_expr (sqlt, m, true);
+ }
+
+ protected:
+ virtual string const&
+ convert_expr (string const& sqlt, semantics::data_member&, bool to);
+
+ string
+ convert (string const& expr, string const& conv);
+
+ protected:
+ // The default implementation returns false.
+ //
+ virtual bool
+ grow_impl (semantics::class_&, user_section*);
+
+ virtual bool
+ grow_impl (semantics::data_member&);
+
+ virtual bool
+ grow_impl (semantics::data_member&,
+ semantics::type&,
+ const custom_cxx_type*,
+ string const&);
+
+ // The default implementation uses the ISO quoting ('') and
+ // escapes singe quotes inside the string by double-quoting
+ // (' -> ''). Some (most?) database systems support escape
+ // sequences. We may want to provide utilize that to support
+ // things like \n, \t, etc.
+ //
+ virtual string
+ quote_string_impl (string const&) const;
+
+ // The default implementation uses the ISO quoting ("").
+ //
+ virtual string
+ quote_id_impl (qname const&) const;
+
+ public:
+ virtual
+ ~context ();
+ context ();
+
+ static context&
+ current ()
+ {
+ return *current_;
+ }
+
+ protected:
+ struct data;
+ typedef context base_context;
+
+ context (data*, sema_rel::model*);
+
+ private:
+ static context* current_;
+
+ protected:
+ struct data: root_context::data
+ {
+ data (std::ostream& os): root_context::data (os) {}
+
+ string bind_vector_;
+ string truncated_vector_;
+ };
+ data* data_;
+
+ public:
+ sema_rel::model* model;
+
+ bool generate_grow;
+ bool need_alias_as;
+ bool insert_send_auto_id;
+ bool delay_freeing_statement_result;
+ bool need_image_clone;
+ bool generate_bulk;
+
+ bool global_index;
+ bool global_fkey;
+
+ string const& bind_vector;
+ string const& truncated_vector;
+ };
+}
+
+#include <odb/relational/context.ixx>
+
+#endif // ODB_RELATIONAL_CONTEXT_HXX
diff --git a/odb/odb/relational/context.ixx b/odb/odb/relational/context.ixx
new file mode 100644
index 0000000..abf1fb5
--- /dev/null
+++ b/odb/odb/relational/context.ixx
@@ -0,0 +1,44 @@
+// file : odb/relational/context.ixx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+namespace relational
+{
+ inline bool context::
+ grow (semantics::class_& c, user_section* s)
+ {
+ return current ().grow_impl (c, s);
+ }
+
+ inline bool context::
+ grow (semantics::data_member& m)
+ {
+ return current ().grow_impl (m);
+ }
+
+ inline bool context::
+ grow (semantics::data_member& m,
+ semantics::type& t,
+ const custom_cxx_type* ct,
+ string const& kp)
+ {
+ return current ().grow_impl (m, t, ct, kp);
+ }
+
+ inline context::string context::
+ quote_string (string const& str) const
+ {
+ return current ().quote_string_impl (str);
+ }
+
+ inline context::string context::
+ quote_id (string const& id) const
+ {
+ return current ().quote_id_impl (qname (id));
+ }
+
+ inline context::string context::
+ quote_id (qname const& id) const
+ {
+ return current ().quote_id_impl (id);
+ }
+}
diff --git a/odb/odb/relational/generate.hxx b/odb/odb/relational/generate.hxx
new file mode 100644
index 0000000..e597fb8
--- /dev/null
+++ b/odb/odb/relational/generate.hxx
@@ -0,0 +1,81 @@
+// file : odb/relational/generate.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_GENERATE_HXX
+#define ODB_RELATIONAL_GENERATE_HXX
+
+#include <string>
+#include <libcutl/shared-ptr.hxx>
+
+#include <odb/context.hxx>
+#include <odb/semantics/relational/model.hxx>
+#include <odb/semantics/relational/changeset.hxx>
+#include <odb/semantics/relational/changelog.hxx>
+
+namespace relational
+{
+ namespace header
+ {
+ void
+ generate ();
+ }
+
+ namespace inline_
+ {
+ void
+ generate ();
+ }
+
+ namespace source
+ {
+ void
+ generate ();
+ }
+
+ namespace model
+ {
+ cutl::shared_ptr<semantics::relational::model>
+ generate ();
+ }
+
+ namespace changelog
+ {
+ // Returns NULL if the changelog is unchanged.
+ //
+ cutl::shared_ptr<semantics::relational::changelog>
+ generate (semantics::relational::model&,
+ model_version const&,
+ semantics::relational::changelog* old, // Can be NULL.
+ std::string const& in_name,
+ std::string const& out_name,
+ options const&);
+ }
+
+ namespace schema
+ {
+ void
+ generate_prologue ();
+
+ void
+ generate_epilogue ();
+
+ void
+ generate_drop ();
+
+ void
+ generate_create ();
+
+ void
+ generate_migrate_pre (semantics::relational::changeset&);
+
+ void
+ generate_migrate_post (semantics::relational::changeset&);
+
+ // Generate embedded schema.
+ //
+ void
+ generate_source (semantics::relational::changelog*);
+ }
+}
+
+#endif // ODB_RELATIONAL_GENERATE_HXX
diff --git a/odb/odb/relational/header.cxx b/odb/odb/relational/header.cxx
new file mode 100644
index 0000000..364d48e
--- /dev/null
+++ b/odb/odb/relational/header.cxx
@@ -0,0 +1,1146 @@
+// file : odb/relational/header.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/header.hxx>
+#include <odb/relational/generate.hxx>
+
+using namespace std;
+
+void relational::header::class1::
+traverse_object (type& c)
+{
+ using semantics::data_member;
+
+ data_member_path* id (id_member (c));
+ data_member* idf (id ? id->front () : 0);
+ data_member* idb (id ? id->back () : 0);
+ bool auto_id (id && auto_ (*id));
+ bool base_id (id && &idf->scope () != &c); // Comes from base.
+
+ data_member* opt (context::optimistic (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+ data_member* discriminator (poly ? context::discriminator (*poly_root) : 0);
+
+ bool abst (abstract (c));
+ bool reuse_abst (abst && !poly);
+
+ bool versioned (context::versioned (c));
+
+ string const& type (class_fq_name (c));
+
+ // Sections.
+ //
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl;
+
+ // pointer_query_columns & query_columns
+ //
+ if (options.generate_query ())
+ {
+ // If we don't have object pointers, then also generate
+ // query_columns (in this case pointer_query_columns and
+ // query_columns are the same and the former inherits from
+ // the latter). Otherwise we have to postpone query_columns
+ // generation until the second pass to deal with forward-
+ // declared objects.
+ //
+ if (!has_a (c, test_pointer | include_base))
+ query_columns_type_->traverse (c);
+
+ pointer_query_columns_type_->traverse (c);
+ }
+
+ // object_traits_impl
+ //
+ os << "template <>" << endl
+ << "class " << exp << "access::object_traits_impl< " << type << ", " <<
+ "id_" << db << " >:" << endl
+ << " public access::object_traits< " << type << " >"
+ << "{"
+ << "public:" << endl;
+
+ object_public_extra_pre (c);
+
+ // For dynamic multi-database support also generate common traits
+ // alias (used in query aliasing).
+ //
+ if (options.generate_query () && multi_dynamic)
+ {
+ os << "typedef access::object_traits_impl< " << type << ", " <<
+ "id_common > common_traits;"
+ << endl;
+ }
+
+ // Polymorphic root_traits, base_traits, and discriminator_image_type.
+ //
+ if (poly)
+ {
+ if (!abst)
+ os << "typedef polymorphic_entry<object_type, id_" << db <<
+ "> entry_type;";
+
+ os << "typedef object_traits_impl<root_type, id_" << db << "> " <<
+ "root_traits;";
+
+ if (poly_derived)
+ {
+ os << "typedef object_traits_impl<base_type, id_" << db << "> " <<
+ "base_traits;"
+ << endl;
+ }
+ else
+ {
+ os << endl
+ << "struct discriminator_image_type"
+ << "{";
+
+ discriminator_image_member_->traverse (*discriminator);
+
+ if (opt != 0)
+ version_image_member_->traverse (*opt);
+
+ os << "std::size_t version;"
+ << "};";
+ }
+ }
+
+ // id_image_type
+ //
+ if (id != 0)
+ {
+ if (base_id)
+ {
+ if (poly_derived)
+ os << "typedef root_traits::id_image_type id_image_type;"
+ << endl;
+ else
+ {
+ semantics::class_& b (
+ dynamic_cast<semantics::class_&> (idf->scope ()));
+
+ os << "typedef object_traits_impl< " << class_fq_name (b) << ", " <<
+ "id_" << db << " >::id_image_type id_image_type;"
+ << endl;
+ }
+ }
+ else
+ {
+ os << "struct id_image_type"
+ << "{";
+
+ id_image_member_->traverse (*idb);
+
+ if (opt != 0)
+ version_image_member_->traverse (*opt);
+
+ os << "std::size_t version;"
+ << "};";
+ }
+ }
+
+ // Polymorphic map.
+ //
+ if (poly)
+ {
+ if (!poly_derived)
+ os << "static map_type* map;";
+
+ os << "static const " << (abst ? "abstract_" : "") << "info_type info;"
+ << endl;
+ }
+
+ // image_type
+ //
+ image_type_->traverse (c);
+
+ // Extra (container, section) statement cache (forward declaration).
+ //
+ if (!reuse_abst && id != 0)
+ os << "struct extra_statement_cache_type;"
+ << endl;
+
+ //
+ // Containers (abstract and concrete).
+ //
+
+ {
+ instance<container_traits> t (c);
+ t->traverse (c);
+ }
+
+ //
+ // Sections (abstract and concrete).
+ //
+
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ instance<section_traits> t (c);
+ t->traverse (*i);
+ }
+
+ //
+ // Query (abstract and concrete).
+ //
+
+ if (options.generate_query ())
+ {
+ // Generate object pointer tags here unless we are generating dynamic
+ // multi-database support, in which case they generated in object_traits.
+ //
+ if (!multi_dynamic && has_a (c, test_pointer | exclude_base))
+ {
+ query_tags t; // Not customizable.
+ t.traverse (c);
+ }
+ }
+
+ //
+ // Functions (abstract and concrete).
+ //
+
+ if (id != 0 || !reuse_abst)
+ os << "using object_traits<object_type>::id;"
+ << endl;
+
+ if (opt != 0)
+ os << "using object_traits<object_type>::version;"
+ << endl;
+
+ if (!poly_derived && id != 0)
+ {
+ if (auto_id)
+ os << "static id_type" << endl
+ << "id (const id_image_type&);"
+ << endl;
+
+ if (options.generate_query ())
+ os << "static id_type" << endl
+ << "id (const image_type&);"
+ << endl;
+
+ if (opt != 0)
+ os << "static version_type" << endl
+ << "version (const image_type&);"
+ << endl;
+ }
+
+ // discriminator()
+ //
+ if (poly && !poly_derived)
+ os << "static discriminator_type" << endl
+ << "discriminator (const image_type&);"
+ << endl;
+
+ // grow ()
+ //
+ if (generate_grow)
+ {
+ // For derived classes in a polymorphic hierarchy, grow() will
+ // check bases up to the specified depth. If one of the base
+ // images has grown, then it will increment its version. But
+ // the return value only indicates the state of this image,
+ // excluding polymorphic bases (in other words, it is possible
+ // that one of the bases has grown but this function returns
+ // false).
+ //
+ os << "static bool" << endl
+ << "grow (image_type&," << endl
+ << truncated_vector;
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ if (poly_derived)
+ os << "," << endl
+ << "std::size_t = depth";
+
+ os << ");"
+ << endl;
+ }
+
+ // bind (image_type)
+ //
+ os << "static void" << endl
+ << "bind (" << bind_vector << "," << endl;
+
+ // If we are a derived type in a polymorphic hierarchy, then
+ // we get the the external id binding.
+ //
+ if (poly_derived)
+ os << "const " << bind_vector << " id," << endl
+ << "std::size_t id_size," << endl;
+
+ os << "image_type&," << endl
+ << db << "::statement_kind";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+
+ // bind (id_image_type)
+ //
+ if (id != 0)
+ {
+ os << "static void" << endl
+ << "bind (" << bind_vector << ", id_image_type&" <<
+ (opt != 0 ? ", bool bind_version = true" : "") << ");"
+ << endl;
+ }
+
+ // init (image, object)
+ //
+ os << "static " << (generate_grow ? "bool" : "void") << endl
+ << "init (image_type&," << endl
+ << "const object_type&," << endl
+ << db << "::statement_kind";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+
+ // init (object, image)
+ //
+ os << "static void" << endl
+ << "init (object_type&," << endl
+ << "const image_type&," << endl
+ << "database*";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ if (poly_derived)
+ os << "," << endl
+ << "std::size_t = depth";
+
+ os << ");"
+ << endl;
+
+ // init (id_image, id)
+ //
+ if (id != 0)
+ {
+ os << "static void" << endl
+ << "init (id_image_type&, const id_type&" <<
+ (opt != 0 ? ", const version_type* = 0" : "") << ");"
+ << endl;
+ }
+
+ if (poly_derived)
+ {
+ // check_version
+ //
+ os << "static bool" << endl
+ << "check_version (const std::size_t*, const image_type&);"
+ << endl;
+
+ // update_version
+ //
+ os << "static void" << endl
+ << "update_version (std::size_t*, const image_type&, " <<
+ db << "::binding*);"
+ << endl;
+ }
+
+ // The rest does not apply to reuse-abstract objects.
+ //
+ if (reuse_abst)
+ {
+ object_public_extra_post (c);
+ os << "};";
+ return;
+ }
+
+ column_count_type const& cc (column_count (c));
+
+ // Statements typedefs.
+ //
+ if (poly)
+ {
+ if (poly_derived)
+ os << "typedef" << endl
+ << db << "::polymorphic_derived_object_statements" <<
+ "<object_type>" << endl
+ << "statements_type;"
+ << endl
+ << "typedef" << endl
+ << db << "::polymorphic_root_object_statements<root_type>" << endl
+ << "root_statements_type;"
+ << endl;
+ else
+ os << "typedef" << endl
+ << db << "::polymorphic_root_object_statements<object_type>" << endl
+ << "statements_type;"
+ << endl
+ << "typedef statements_type root_statements_type;"
+ << endl;
+ }
+ else
+ {
+ if (id != 0)
+ os << "typedef " << db << "::object_statements<object_type> " <<
+ "statements_type;"
+ << endl;
+ else
+ os << "typedef " << db << "::no_id_object_statements<object_type> " <<
+ "statements_type;"
+ << endl;
+ }
+
+ //
+ // Query (concrete).
+ //
+
+ if (options.generate_query ())
+ {
+ // query_base_type
+ //
+ os << "typedef " << db << "::query_base query_base_type;"
+ << endl;
+ }
+
+ //
+ // Containers (concrete).
+ //
+
+ //
+ // Sections (concrete).
+ //
+
+ // column_count
+ //
+ os << "static const std::size_t column_count = " << cc.total << "UL;"
+ << "static const std::size_t id_column_count = " << cc.id << "UL;"
+ << "static const std::size_t inverse_column_count = " <<
+ cc.inverse << "UL;"
+ << "static const std::size_t readonly_column_count = " <<
+ cc.readonly << "UL;"
+ << "static const std::size_t managed_optimistic_column_count = " <<
+ cc.optimistic_managed << "UL;";
+
+ if (poly && !poly_derived)
+ os << "static const std::size_t discriminator_column_count = " <<
+ cc.discriminator << "UL;";
+
+ os << endl
+ << "static const std::size_t separate_load_column_count = " <<
+ cc.separate_load << "UL;"
+ << "static const std::size_t separate_update_column_count = " <<
+ cc.separate_update << "UL;"
+ << endl;
+
+ os << "static const bool versioned = " << versioned << ";"
+ << endl;
+
+ // Statements.
+ //
+ os << "static const char persist_statement[];";
+
+ if (id != 0)
+ {
+ if (poly_derived)
+ {
+ char const* n (abst ? "1" : "depth");
+
+ os << "static const char* const find_statements[" << n << "];"
+ << "static const std::size_t find_column_counts[" << n << "];";
+ }
+ else
+ {
+ os << "static const char find_statement[];";
+
+ if (poly)
+ os << "static const char find_discriminator_statement[];";
+ }
+
+ if (cc.total != cc.id + cc.inverse + cc.readonly + cc.separate_update)
+ os << "static const char update_statement[];";
+
+ os << "static const char erase_statement[];";
+
+ if (opt != 0 && !poly_derived)
+ os << "static const char optimistic_erase_statement[];";
+ }
+
+ if (options.generate_query ())
+ {
+ os << "static const char query_statement[];"
+ << "static const char erase_query_statement[];"
+ << endl
+ << "static const char table_name[];";
+ }
+
+ os << endl;
+
+ //
+ // Functions (concrete).
+ //
+
+ // persist ()
+ //
+ os << "static void" << endl
+ << "persist (database&, " << (auto_id ? "" : "const ") << "object_type&";
+
+ if (poly)
+ os << ", bool top = true, bool dyn = true";
+
+ os << ");"
+ << endl;
+
+ if (c.count ("bulk-persist"))
+ os << "static void" << endl
+ << "persist (database&, " << (auto_id ? "" : "const ") <<
+ "object_type**, std::size_t, multiple_exceptions&);"
+ << endl;
+
+ if (id != 0)
+ {
+ // find (id)
+ //
+ if (c.default_ctor ())
+ os << "static pointer_type" << endl
+ << "find (database&, const id_type&);"
+ << endl;
+
+ // find (id, obj)
+ //
+ os << "static bool" << endl
+ << "find (database&, const id_type&, object_type&";
+
+ if (poly)
+ os << ", bool dyn = true";
+
+ os << ");"
+ << endl;
+
+ // reload ()
+ //
+ os << "static bool" << endl
+ << "reload (database&, object_type&";
+
+ if (poly)
+ os << ", bool dyn = true";
+
+ os << ");"
+ << endl;
+
+ // update ()
+ //
+ // In case of a polymorphic object, we generate update() even if it is
+ // readonly since the potentially-readwrite base will rely on it to
+ // initialize the id image.
+ //
+ //
+ if (!readonly (c) || poly)
+ {
+ os << "static void" << endl
+ << "update (database&, const object_type&";
+
+ if (poly)
+ os << ", bool top = true, bool dyn = true";
+
+ os << ");"
+ << endl;
+
+ if (c.count ("bulk-update"))
+ os << "static void" << endl
+ << "update (database&, const object_type**, std::size_t, " <<
+ "multiple_exceptions&);"
+ << endl;
+ }
+
+ // erase ()
+ //
+ os << "static void" << endl
+ << "erase (database&, const id_type&";
+
+ if (poly)
+ os << ", bool top = true, bool dyn = true";
+
+ os << ");"
+ << endl;
+
+ os << "static void" << endl
+ << "erase (database&, const object_type&";
+
+ if (poly)
+ os << ", bool top = true, bool dyn = true";
+
+ os << ");"
+ << endl;
+
+ if (c.count ("bulk-erase"))
+ {
+ os << "static std::size_t" << endl
+ << "erase (database&, const id_type**, std::size_t, " <<
+ "multiple_exceptions&);"
+ << endl;
+
+ os << "static void" << endl
+ << "erase (database&, const object_type**, std::size_t, " <<
+ "multiple_exceptions&);"
+ << endl;
+ }
+
+ // Sections.
+ //
+ // We treat all polymorphic sections as (potentially) having something
+ // to load or to update since we cannot predict what will be added to
+ // them in overrides.
+ //
+ if (uss.count (user_sections::count_total |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0)
+ os << "static bool" << endl
+ << "load (connection&, object_type&, section&" <<
+ (poly ? ", const info_type* = 0" : "") << ");"
+ << endl;
+
+ if (uss.count (user_sections::count_total |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) != 0)
+ os << "static bool" << endl
+ << "update (connection&, const object_type&, const section&" <<
+ (poly ? ", const info_type* = 0" : "") << ");"
+ << endl;
+ }
+
+ // query ()
+ //
+ if (options.generate_query ())
+ {
+ if (!options.omit_unprepared ())
+ {
+ os << "static result<object_type>" << endl
+ << "query (database&, const query_base_type&);"
+ << endl;
+
+ if (multi_dynamic)
+ os << "static result<object_type>" << endl
+ << "query (database&, const odb::query_base&);"
+ << endl;
+ }
+
+ os << "static unsigned long long" << endl
+ << "erase_query (database&, const query_base_type&);"
+ << endl;
+
+ if (multi_dynamic)
+ os << "static unsigned long long" << endl
+ << "erase_query (database&, const odb::query_base&);"
+ << endl;
+
+ if (options.generate_prepared ())
+ {
+ os << "static odb::details::shared_ptr<prepared_query_impl>" << endl
+ << "prepare_query (connection&, const char*, const query_base_type&);"
+ << endl;
+
+ if (multi_dynamic)
+ os << "static odb::details::shared_ptr<prepared_query_impl>" << endl
+ << "prepare_query (connection&, const char*, " <<
+ "const odb::query_base&);"
+ << endl;
+
+ os << "static odb::details::shared_ptr<result_impl>" << endl
+ << "execute_query (prepared_query_impl&);"
+ << endl;
+ }
+ }
+
+ object_public_extra_post (c);
+
+ // Implementation details.
+ //
+ os << "public:" << endl;
+
+ if (id != 0)
+ {
+ // Load the object image.
+ //
+ os << "static bool" << endl
+ << "find_ (statements_type&," << endl
+ << "const id_type*";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ if (poly_derived && !abst)
+ os << "," << endl
+ << "std::size_t = depth";
+
+ os << ");"
+ << endl;
+
+ // Load the rest of the object (containers, etc). Expects the id
+ // image in the object statements to be initialized to the object
+ // id.
+ //
+ os << "static void" << endl
+ << "load_ (statements_type&," << endl
+ << "object_type&," << endl
+ << "bool reload";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ if (poly_derived)
+ os << "," << endl
+ << "std::size_t = depth";
+
+ os << ");"
+ << endl;
+ }
+
+ // discriminator_ ()
+ //
+ if (poly && !poly_derived)
+ {
+ os << "static void" << endl
+ << "discriminator_ (statements_type&," << endl
+ << "const id_type&," << endl
+ << "discriminator_type*";
+
+ if (opt != 0)
+ os << "," << endl
+ << "version_type* = 0";
+
+ os << ");"
+ << endl;
+ }
+
+ // Load the dynamic part of the object. Depth inidicates where
+ // the dynamic part starts. Expects the id image in the object
+ // statements to be initialized to the object id. We don't need
+ // it if we are poly-abstract.
+ //
+ if (poly_derived && !abst)
+ os << "static void" << endl
+ << "load_ (database&, root_type&, std::size_t);"
+ << endl;
+
+ // Image chain manipulation.
+ //
+ if (poly && need_image_clone && options.generate_query ())
+ {
+ os << "static root_traits::image_type&" << endl
+ << "root_image (image_type&);"
+ << endl;
+
+ // Note that the original image is non-const since for some databases
+ // the copy "steals" stuff from the original (e.g., LOB descriptors in
+ // Oracle).
+ //
+ os << "static image_type*" << endl
+ << "clone_image (image_type&);"
+ << endl;
+
+ os << "static void" << endl
+ << "copy_image (image_type&, image_type&);"
+ << endl;
+
+ os << "static void" << endl
+ << "free_image (image_type*);"
+ << endl;
+ }
+
+ os << "};";
+
+
+ // object_traits_impl< , id_common>
+ //
+ // Note that it is not generated for reuse-abstract classes.
+ //
+ if (options.default_database_specified () &&
+ options.default_database () == db)
+ {
+ os << "template <>" << endl
+ << "class access::object_traits_impl< " << type << ", " <<
+ "id_common >:" << endl
+ << " public access::object_traits_impl< " << type << ", " <<
+ "id_" << db << " >"
+ << "{"
+ << "};";
+ }
+}
+
+void relational::header::class1::
+traverse_view (type& c)
+{
+ bool versioned (context::versioned (c));
+
+ string const& type (class_fq_name (c));
+ size_t columns (column_count (c).total);
+ size_t obj_count (c.get<size_t> ("object-count"));
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl;
+
+ // view_traits_impl
+ //
+ os << "template <>" << endl
+ << "class " << exp << "access::view_traits_impl< " << type << ", " <<
+ "id_" << db << " >:" << endl
+ << " public access::view_traits< " << type << " >"
+ << "{"
+ << "public:" << endl;
+
+ view_public_extra_pre (c);
+
+ // For dynamic multi-database support also generate common traits
+ // alias (used in query aliasing).
+ //
+ if (multi_dynamic)
+ {
+ os << "typedef access::view_traits_impl< " << type << ", " <<
+ "id_common > common_traits;"
+ << endl;
+ }
+
+ // image_type
+ //
+ image_type_->traverse (c);
+
+ os << "typedef " << db << "::view_statements<view_type> statements_type;"
+ << endl;
+
+ //
+ // Query.
+ //
+
+ // Generate associated object tags here unless we are generating dynamic
+ // multi-database support, in which case they generated in object_traits.
+ //
+ if (!multi_dynamic)
+ {
+ query_tags t; // Not customizable.
+ t.traverse (c);
+ }
+
+ // query_base_type and query_columns (definition generated by class2).
+ //
+ os << "typedef " << db << "::query_base query_base_type;"
+ << "struct query_columns";
+
+ if (obj_count == 0)
+ os << "{"
+ << "};";
+ else
+ os << ";"
+ << endl;
+
+ os << "static const bool versioned = " << versioned << ";"
+ << endl;
+
+ //
+ // Functions.
+ //
+
+ // grow ()
+ //
+ if (generate_grow)
+ {
+ os << "static bool" << endl
+ << "grow (image_type&," << endl
+ << truncated_vector;
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ")" << (columns != 0 ? ";\n" : "{}");
+ }
+
+ // bind (image_type)
+ //
+ os << "static void" << endl
+ << "bind (" << bind_vector << "," << endl
+ << "image_type&";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ")" << (columns != 0 ? ";\n" : "{}");
+
+ // init (view, image)
+ //
+ os << "static void" << endl
+ << "init (view_type&," << endl
+ << "const image_type&," << endl
+ << "database*";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ")" << (columns != 0 ? ";\n" : "{}");
+
+ // column_count
+ //
+ os << "static const std::size_t column_count = " << columns << "UL;"
+ << endl;
+
+ // Statements.
+ //
+ view_query& vq (c.get<view_query> ("query"));
+
+ if (vq.kind != view_query::runtime)
+ {
+ os << "static query_base_type" << endl
+ << "query_statement (const query_base_type&);"
+ << endl;
+ }
+
+ //
+ // Functions.
+ //
+
+ // query ()
+ //
+ if (!options.omit_unprepared ())
+ {
+ os << "static result<view_type>" << endl
+ << "query (database&, const query_base_type&);"
+ << endl;
+
+ if (multi_dynamic)
+ os << "static result<view_type>" << endl
+ << "query (database&, const odb::query_base&);"
+ << endl;
+ }
+
+ if (options.generate_prepared ())
+ {
+ os << "static odb::details::shared_ptr<prepared_query_impl>" << endl
+ << "prepare_query (connection&, const char*, const query_base_type&);"
+ << endl;
+
+ if (multi_dynamic)
+ os << "static odb::details::shared_ptr<prepared_query_impl>" << endl
+ << "prepare_query (connection&, const char*, " <<
+ "const odb::query_base&);"
+ << endl;
+
+ os << "static odb::details::shared_ptr<result_impl>" << endl
+ << "execute_query (prepared_query_impl&);"
+ << endl;
+ }
+
+ view_public_extra_post (c);
+
+ os << "};";
+
+ // view_traits_impl< , id_common>
+ //
+ if (options.default_database_specified () &&
+ options.default_database () == db)
+ {
+ os << "template <>" << endl
+ << "class access::view_traits_impl< " << type << ", " <<
+ "id_common >:" << endl
+ << " public access::view_traits_impl< " << type << ", " <<
+ "id_" << db << " >"
+ << "{"
+ << "};";
+ }
+}
+
+void relational::header::class1::
+traverse_composite (type& c)
+{
+ bool versioned (context::versioned (c));
+
+ string const& type (class_fq_name (c));
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl;
+
+ // While composite_value_traits is not used directly by user code, we
+ // still need to export it if the generated code for the same database
+ // is split into several DLLs.
+ //
+ os << "template <>" << endl
+ << "class " << exp << "access::composite_value_traits< " << type <<
+ ", id_" << db << " >"
+ << "{"
+ << "public:" << endl;
+
+ // value_type
+ //
+ os << "typedef " << type << " value_type;"
+ << endl;
+
+ // image_type
+ //
+ image_type_->traverse (c);
+
+ // Containers.
+ //
+ {
+ instance<container_traits> t (c);
+ t->traverse (c);
+ }
+
+ // grow ()
+ //
+ if (generate_grow)
+ {
+ os << "static bool" << endl
+ << "grow (image_type&," << endl
+ << truncated_vector;
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ // bind (image_type)
+ //
+ os << "static void" << endl
+ << "bind (" << bind_vector << "," << endl
+ << "image_type&," << endl
+ << db << "::statement_kind";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+
+ // init (image, value)
+ //
+ os << "static " << (generate_grow ? "bool" : "void") << endl
+ << "init (image_type&," << endl
+ << "const value_type&," << endl
+ << db << "::statement_kind";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+
+ // init (value, image)
+ //
+ os << "static void" << endl
+ << "init (value_type&," << endl
+ << "const image_type&," << endl
+ << "database*";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+
+ if (!has_a (c, test_container))
+ {
+ // get_null (image)
+ //
+ os << "static bool" << endl
+ << "get_null (const image_type&";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+
+ // set_null (image)
+ //
+ os << "static void" << endl
+ << "set_null (image_type&," << endl
+ << db << "::statement_kind";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ column_count_type const& cc (column_count (c));
+ os << "static const std::size_t column_count = " << cc.total << "UL;";
+
+ os << "};";
+}
+
+void relational::header::
+generate ()
+{
+ context ctx;
+ ostream& os (ctx.os);
+
+ instance<include> i;
+ i->generate ();
+
+ os << "namespace odb"
+ << "{";
+
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (false);
+ traversal::namespace_ ns;
+ instance<class1> c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (false);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (ctx.unit);
+ }
+
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (false);
+ traversal::namespace_ ns;
+ instance<class2> c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (false);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (ctx.unit);
+ }
+
+ os << "}";
+}
diff --git a/odb/odb/relational/header.hxx b/odb/odb/relational/header.hxx
new file mode 100644
index 0000000..964aff2
--- /dev/null
+++ b/odb/odb/relational/header.hxx
@@ -0,0 +1,1466 @@
+// file : odb/relational/header.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_HEADER_HXX
+#define ODB_RELATIONAL_HEADER_HXX
+
+#include <odb/relational/context.hxx>
+#include <odb/relational/common.hxx>
+
+namespace relational
+{
+ namespace header
+ {
+ //
+ // image_type
+ //
+
+ struct image_member: virtual member_base
+ {
+ typedef image_member base;
+
+ image_member (string const& var = string ())
+ : member_base (var, 0, 0, string (), string ()) {}
+
+ image_member (string const& var,
+ semantics::type& t,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base (var, &t, ct, fq_type, key_prefix) {}
+ };
+
+ template <typename T>
+ struct image_member_impl: image_member, virtual member_base_impl<T>
+ {
+ typedef image_member_impl base_impl;
+
+ image_member_impl (base const& x)
+ : member_base::base (x), // virtual base
+ base (x),
+ member_image_type_ (base::type_override_,
+ base::custom_override_,
+ base::fq_type_override_,
+ base::key_prefix_)
+ {
+ }
+
+ typedef typename member_base_impl<T>::member_info member_info;
+
+ using member_base_impl<T>::container;
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ if (container (mi))
+ return false;
+
+ image_type = member_image_type_->image_type (mi.m);
+
+ if (var_override_.empty ())
+ os << "// " << mi.m.name () << endl
+ << "//" << endl;
+
+ return true;
+ }
+
+ virtual void
+ traverse_pointer (member_info& mi)
+ {
+ // Object pointers in views require special treatment.
+ //
+ if (view_member (mi.m))
+ {
+ using semantics::class_;
+
+ class_& c (*mi.ptr);
+ class_* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ if (poly_derived)
+ // Use a helper to create a complete chain of images all
+ // the way to the root (see libodb/odb/view-image.hxx).
+ //
+ os << "view_object_image<" << endl
+ << " " << class_fq_name (c) << "," << endl
+ << " " << class_fq_name (*poly_root) << "," << endl
+ << " id_" << db << " >";
+ else
+ os << "object_traits_impl< " << class_fq_name (c) << ", " <<
+ "id_" << db << " >::image_type";
+
+ os << " " << mi.var << "value;"
+ << endl;
+ }
+ else
+ member_base_impl<T>::traverse_pointer (mi);
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << endl;
+ }
+
+ protected:
+ string image_type;
+ instance<member_image_type> member_image_type_;
+ };
+
+ struct image_base: traversal::class_, virtual context
+ {
+ typedef image_base base;
+
+ image_base (): first_ (true) {}
+
+ virtual void
+ traverse (type& c)
+ {
+ bool obj (object (c));
+
+ // Ignore transient bases. Not used for views.
+ //
+ if (!(obj || composite (c)))
+ return;
+
+ if (first_)
+ {
+ os << ": ";
+ first_ = false;
+ }
+ else
+ {
+ os << "," << endl
+ << " ";
+ }
+
+ string const& type (class_fq_name (c));
+
+ if (obj)
+ os << "object_traits_impl< " << type << ", id_" << db <<
+ " >::image_type";
+ else
+ os << "composite_value_traits< " << type << ", id_" << db <<
+ " >::image_type";
+ }
+
+ private:
+ bool first_;
+ };
+
+ struct image_type: traversal::class_, virtual context
+ {
+ typedef image_type base;
+
+ image_type ()
+ {
+ *this >> names_member_ >> member_;
+ }
+
+ image_type (image_type const&)
+ : root_context (), context () //@@ -Wextra
+ {
+ *this >> names_member_ >> member_;
+ }
+
+ virtual void
+ image_extra (type&)
+ {
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ type* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ os << "struct image_type";
+
+ if (!view (c))
+ {
+ // Don't go into the base if we are a derived type in a
+ // polymorphic hierarchy.
+ //
+ if (!poly_derived)
+ {
+ instance<image_base> b;
+ traversal::inherits i (*b);
+ inherits (c, i);
+ }
+ }
+
+ os << "{";
+
+ if (poly_derived)
+ os << "base_traits::image_type* base;"
+ << endl;
+
+ names (c);
+
+ // We don't need a version if this is a composite value type
+ // or reuse-abstract object.
+ //
+ if (!(composite (c) || (abstract (c) && !polymorphic (c))))
+ os << "std::size_t version;"
+ << endl;
+
+ image_extra (c);
+
+ os << "};";
+ }
+
+ private:
+ instance<image_member> member_;
+ traversal::names names_member_;
+ };
+
+ // Member-specific traits types for container members.
+ //
+ struct container_traits: object_members_base, virtual context
+ {
+ typedef container_traits base;
+
+ container_traits (semantics::class_& c)
+ : object_members_base (true, false, false), c_ (c)
+ {
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&)
+ {
+ // We don't want to traverse composite id.
+ }
+
+ virtual void
+ traverse_composite (semantics::data_member* m, semantics::class_& c)
+ {
+ if (object (c_))
+ object_members_base::traverse_composite (m, c);
+ else
+ {
+ // If we are generating traits for a composite value type, then
+ // we don't want to go into its bases or it composite members.
+ //
+ if (m == 0 && &c == &c_)
+ names (c);
+ }
+ }
+
+ virtual void
+ container_public_extra_pre (semantics::data_member&, semantics::type&)
+ {
+ }
+
+ virtual void
+ container_public_extra_post (semantics::data_member&, semantics::type&)
+ {
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type& c)
+ {
+ using semantics::type;
+ using semantics::class_;
+
+ // Figure out if this member is from a base object or composite
+ // value and if it's from an object, whether it is reuse-abstract.
+ //
+ bool base, reuse_abst;
+
+ if (object (c_))
+ {
+ base = cur_object != &c_ ||
+ !object (dynamic_cast<type&> (m.scope ()));
+ reuse_abst = abstract (c_) && !polymorphic (c_);
+ }
+ else
+ {
+ base = false; // We don't go into bases.
+ reuse_abst = true; // Always abstract.
+ }
+
+ container_kind_type ck (container_kind (c));
+
+ const custom_cxx_type* vct (0);
+ const custom_cxx_type* ict (0);
+ const custom_cxx_type* kct (0);
+
+ type& vt (container_vt (m, &vct));
+ type* it (0);
+ type* kt (0);
+
+ bool ordered (false);
+ bool inverse (context::inverse (m, "value"));
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (!unordered (m))
+ {
+ it = &container_it (m, &ict);
+ ordered = true;
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ kt = &container_kt (m, &kct);
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ bool smart (!inverse &&
+ (ck != ck_ordered || ordered) &&
+ container_smart (c));
+
+ string name (flat_prefix_ + public_name (m) + "_traits");
+
+ // Figure out column counts.
+ //
+ size_t id_columns, value_columns, data_columns, cond_columns;
+ bool versioned (context::versioned (m));
+
+ if (!reuse_abst)
+ {
+ type& idt (container_idt (m));
+
+ if (class_* idc = composite_wrapper (idt))
+ id_columns = column_count (*idc).total;
+ else
+ id_columns = 1;
+
+ data_columns = cond_columns = id_columns;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ // Add one for the index.
+ //
+ if (ordered)
+ {
+ data_columns++;
+
+ if (smart)
+ cond_columns++;
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ // Add some for the key.
+ //
+ size_t n;
+
+ class_* ptr (object_pointer (*kt));
+ semantics::type& t (ptr == 0 ? *kt : utype (*id_member (*ptr)));
+
+ if (class_* comp = composite_wrapper (t))
+ n = column_count (*comp).total;
+ else
+ n = 1;
+
+ data_columns += n;
+
+ // Key is not currently used (see also bind()).
+ //
+ // cond_columns += n;
+
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ // Not currently used (see also bind())
+ //
+ // Value is also a key.
+ //
+ // class_* ptr (object_pointer (vt));
+ // semantics::type& t (ptr == 0 ? vt : utype (*id_member (*ptr)));
+ //
+ // if (class_* comp = composite_wrapper (t))
+ // cond_columns += column_count (*comp).total;
+ // else
+ // cond_columns++;
+ //
+ break;
+ }
+ }
+
+ {
+ class_* ptr (object_pointer (vt));
+ semantics::type& t (ptr == 0 ? vt : utype (*id_member (*ptr)));
+
+ if (class_* comp = composite_wrapper (t))
+ value_columns = column_count (*comp).total;
+ else
+ value_columns = 1;
+
+ data_columns += value_columns;
+ }
+
+ // Store column counts for the source generator.
+ //
+ m.set ("id-column-count", id_columns);
+ m.set ("value-column-count", value_columns);
+ m.set ("cond-column-count", cond_columns);
+ m.set ("data-column-count", data_columns);
+ }
+
+ os << "// " << m.name () << endl
+ << "//" << endl
+ << "struct " << exp << name;
+
+ if (base)
+ {
+ semantics::class_& b (dynamic_cast<semantics::class_&> (m.scope ()));
+ string const& type (class_fq_name (b));
+
+ if (object (b))
+ os << ": access::object_traits_impl< " << type << ", id_" <<
+ db << " >::" << name;
+ else
+ os << ": access::composite_value_traits< " << type << ", id_" <<
+ db << " >::" << public_name (m) << "_traits"; // No prefix_.
+ }
+
+ os << "{";
+
+ container_public_extra_pre (m, c);
+
+ if (!reuse_abst)
+ {
+ // column_count
+ //
+ os << "static const std::size_t id_column_count = " <<
+ id_columns << "UL;";
+
+ if (smart)
+ os << "static const std::size_t value_column_count = " <<
+ value_columns << "UL;"
+ << "static const std::size_t cond_column_count = " <<
+ cond_columns << "UL;";
+
+ os << "static const std::size_t data_column_count = " <<
+ data_columns << "UL;"
+ << endl;
+
+ os << "static const bool versioned = " << versioned << ";"
+ << endl;
+
+ // Statements.
+ //
+ os << "static const char insert_statement[];"
+ << "static const char select_statement[];";
+
+ if (smart)
+ os << "static const char update_statement[];";
+
+ os << "static const char delete_statement[];"
+ << endl;
+ }
+
+ if (base)
+ {
+ container_public_extra_post (m, c);
+ os << "};";
+
+ return;
+ }
+
+ // container_type
+ // container_traits
+ // index_type
+ // key_type
+ // value_type
+ //
+ os << "typedef ";
+
+ {
+ semantics::names* hint;
+ semantics::type& t (utype (m, hint));
+
+ if (semantics::type* wt = wrapper (t))
+ {
+ // Use the hint from the wrapper unless the wrapped type is
+ // qualified. In this case use the hint for the unqualified
+ // type.
+ //
+ hint = t.get<semantics::names*> ("wrapper-hint");
+ utype (*wt, hint);
+
+ os << c.fq_name (hint);
+ }
+ else
+ // t and c are the same.
+ //
+ os << t.fq_name (hint);
+ }
+
+ os << " container_type;";
+
+ os << "typedef" << endl
+ << "odb::access::container_traits<container_type>" << endl
+ << "container_traits_type;";
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "typedef container_traits_type::index_type index_type;";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "typedef container_traits_type::key_type key_type;";
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ os << "typedef container_traits_type::value_type value_type;"
+ << endl;
+
+ // functions_type
+ //
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "typedef " << (smart ? "smart_" : "") <<
+ "ordered_functions<index_type, value_type> functions_type;";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "typedef map_functions<key_type, value_type> " <<
+ "functions_type;";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "typedef set_functions<value_type> functions_type;";
+ break;
+ }
+ }
+
+ os << "typedef " << db << "::" << (smart ? "smart_" : "")
+ << "container_statements< " << name << " > statements_type;"
+ << endl;
+
+ // cond_image_type (object id is taken from the object image).
+ //
+ // For dumb containers we use the id binding directly.
+ //
+ if (smart)
+ {
+ os << "struct cond_image_type"
+ << "{";
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl;
+ instance<image_member> im (
+ "index_", *it, ict, "index_type", "index");
+ im->traverse (m);
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "// key" << endl
+ << "//" << endl;
+ instance<image_member> im ("key_", *kt, kct, "key_type", "key");
+ im->traverse (m);
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "// value" << endl
+ << "//" << endl;
+ instance<image_member> im (
+ "value_", vt, vct, "value_type", "value");
+ im->traverse (m);
+ break;
+ }
+ }
+
+ os << "std::size_t version;"
+ << "};";
+ }
+
+ // data_image_type (object id is taken from the object image)
+ //
+ os << "struct data_image_type"
+ << "{";
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl;
+ instance<image_member> im (
+ "index_", *it, ict, "index_type", "index");
+ im->traverse (m);
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "// key" << endl
+ << "//" << endl;
+ instance<image_member> im ("key_", *kt, kct, "key_type", "key");
+ im->traverse (m);
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ os << "// value" << endl
+ << "//" << endl;
+ instance<image_member> im ("value_", vt, vct, "value_type", "value");
+ im->traverse (m);
+
+ os << "std::size_t version;"
+ << "};";
+
+ // bind (cond_image)
+ //
+ if (smart)
+ os << "static void" << endl
+ << "bind (" << bind_vector << "," << endl
+ << "const " << bind_vector << " id," << endl
+ << "std::size_t id_size," << endl
+ << "cond_image_type&);"
+ << endl;
+
+ // bind (data_image)
+ //
+ os << "static void" << endl
+ << "bind (" << bind_vector << "," << endl
+ << "const " << bind_vector << " id," << endl
+ << "std::size_t id_size," << endl
+ << "data_image_type&";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+
+ // bind (cond_image, data_image) (update)
+ //
+ if (smart)
+ {
+ os << "static void" << endl
+ << "bind (" << bind_vector << "," << endl
+ << "const " << bind_vector << " id," << endl
+ << "std::size_t id_size," << endl
+ << "cond_image_type&," << endl
+ << "data_image_type&";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ // grow ()
+ //
+ if (generate_grow)
+ {
+ os << "static void" << endl
+ << "grow (data_image_type&," << endl
+ << truncated_vector;
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ // init (data_image)
+ //
+ if (!inverse)
+ {
+ os << "static void" << endl
+ << "init (data_image_type&," << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ os << "index_type*," << endl;
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "const key_type*," << endl;
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ break;
+ }
+
+ os << "const value_type&";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ // init (cond_image)
+ //
+ if (smart)
+ {
+ os << "static void" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "init (cond_image_type&, index_type);";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ // os << "init (data_image_type&, const key_type&);";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ // os << "init (data_image_type&, const value_type&);";
+ break;
+ }
+ }
+
+ os << endl;
+ }
+
+ // init (data)
+ //
+ os << "static void" << endl
+ << "init (";
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ os << "index_type&," << endl;
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "key_type&," << endl;
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ break;
+ }
+
+ os << "value_type&," << endl;
+ os << "const data_image_type&," << endl
+ << "database*";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+
+ // insert
+ //
+ os << "static void" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "insert (index_type, const value_type&, void*);";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "insert (const key_type&, const value_type&, void*);";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "insert (const value_type&, void*);";
+ break;
+ }
+ }
+
+ os << endl;
+
+ // select
+ //
+ os << "static bool" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "select (index_type&, value_type&, void*);";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "select (key_type&, value_type&, void*);";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "select (value_type&, void*);";
+ break;
+ }
+ }
+
+ os << endl;
+
+ // update
+ //
+ if (smart)
+ {
+ os << "static void" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "update (index_type, const value_type&, void*);";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ //os << "update (const key_type&, const value_type&, void*);";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ //os << "update (const value_type&, const value_type&, void*);";
+ break;
+ }
+ }
+
+ os << endl;
+ }
+
+ // delete_
+ //
+ os << "static void" << endl
+ << "delete_ (";
+
+ if (smart)
+ {
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "index_type, ";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+ }
+
+ os << "void*);"
+ << endl;
+
+ // persist
+ //
+ if (!inverse)
+ {
+ os << "static void" << endl
+ << "persist (const container_type&," << endl
+ << "statements_type&";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ // load
+ //
+ os << "static void" << endl
+ << "load (container_type&," << endl
+ << "statements_type&";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+
+ // update
+ //
+ if (!(inverse || readonly (member_path_, member_scope_)))
+ {
+ os << "static void" << endl
+ << "update (const container_type&," << endl
+ << "statements_type&";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ // erase
+ //
+ if (!inverse)
+ {
+ os << "static void" << endl
+ << "erase (";
+
+ if (smart)
+ os << "const container_type*, ";
+
+ os << "statements_type&);"
+ << endl;
+ }
+
+ container_public_extra_post (m, c);
+
+ os << "};";
+ }
+
+ protected:
+ semantics::class_& c_;
+ };
+
+ //
+ //
+ struct section_traits: virtual context
+ {
+ typedef section_traits base;
+
+ section_traits (semantics::class_& c): c_ (c) {}
+
+ virtual void
+ section_public_extra_pre (user_section&)
+ {
+ }
+
+ virtual void
+ section_public_extra_post (user_section&)
+ {
+ }
+
+ virtual void
+ traverse (user_section& s)
+ {
+ semantics::class_* poly_root (polymorphic (c_));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c_);
+
+ semantics::data_member* opt (optimistic (c_));
+
+ // Treat the special version update sections as abstract in reuse
+ // inheritance.
+ //
+ bool reuse_abst (!poly &&
+ (abstract (c_) ||
+ s.special == user_section::special_version));
+
+ bool load (s.total != 0 && s.separate_load ());
+ bool load_con (s.containers && s.separate_load ());
+ bool load_opt (s.optimistic () && s.separate_load ());
+
+ bool update (s.total != s.inverse + s.readonly); // Always separate.
+ bool update_con (s.readwrite_containers);
+ bool update_opt (s.optimistic () && (s.readwrite_containers || poly));
+
+ // Don't generate anything for empty sections.
+ //
+ if (!(load || load_con || load_opt ||
+ update || update_con || update_opt))
+ return;
+
+ // If we are adding a new section to a derived class in an optimistic
+ // hierarchy, then pretend it inherits from the special version update
+ // section.
+ //
+ user_section* rs (0);
+ if (opt != 0)
+ {
+ // Skip overrides and get to the new section if polymorphic.
+ //
+ for (rs = &s; poly && rs->base != 0; rs = rs->base) ;
+
+ if (rs != 0)
+ {
+ if (rs->object != &opt->scope ())
+ rs->base = &(poly ? poly_root : &opt->scope ())->
+ get<user_sections> ("user-sections").back ();
+ else
+ rs = 0;
+ }
+ }
+
+ string name (public_name (*s.member) + "_traits");
+
+ os << "// " << s.member->name () << endl
+ << "//" << endl
+ << "struct " << exp << name
+ << "{";
+
+ os << "typedef object_traits_impl<object_type, id_" << db <<
+ ">::image_type image_type;"
+ << "typedef object_traits_impl<object_type, id_" << db <<
+ ">::id_image_type id_image_type;"
+ << endl;
+
+ section_public_extra_pre (s);
+
+ // bind (id, image_type)
+ //
+ // If id is NULL, then id is ignored (select). Otherwise, it is
+ // copied at the end (update).
+ //
+ if (load || load_opt || update || update_opt)
+ {
+ os << "static std::size_t" << endl
+ << "bind (" << bind_vector << "," << endl
+ << "const " << bind_vector << " id," << endl
+ << "std::size_t id_size," << endl
+ << "image_type&," << endl
+ << db << "::statement_kind";
+
+ if (s.versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ // grow ()
+ //
+ // We have to have out own version because the truncated vector
+ // will have different number of elements.
+ //
+ if (generate_grow && (load || load_opt))
+ {
+ os << "static bool" << endl
+ << "grow (image_type&," << endl
+ << truncated_vector;
+
+ if (s.versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ // init (object, image)
+ //
+ if (load)
+ {
+ os << "static void" << endl
+ << "init (object_type&," << endl
+ << "const image_type&," << endl
+ << "database*";
+
+ if (s.versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ // init (image, object)
+ //
+ if (update)
+ {
+ os << "static " << (generate_grow ? "bool" : "void") << endl
+ << "init (image_type&," << endl
+ << "const object_type&";
+
+ if (s.versioned)
+ os << "," << endl
+ << "const schema_version_migration&";
+
+ os << ");"
+ << endl;
+ }
+
+ // The rest does not apply to reuse-abstract sections.
+ //
+ if (reuse_abst)
+ {
+ section_public_extra_post (s);
+ os << "};";
+ return;
+ }
+
+ // column_count
+ //
+ column_count_type const& cc (column_count (poly ? *poly_root : c_));
+
+ // Generate load and update column counts even when they are zero so
+ // that we can instantiate section_statements.
+ //
+ os << "static const std::size_t id_column_count = " << cc.id << "UL;";
+
+ os << "static const std::size_t managed_optimistic_load_column_count" <<
+ " = " << cc.optimistic_managed << "UL;"
+ << "static const std::size_t load_column_count = " <<
+ (load ? s.total_total () : 0) << "UL;";
+
+ os << "static const std::size_t managed_optimistic_update_column_count" <<
+ " = " << (poly_derived ? 0 : cc.optimistic_managed) << "UL;"
+ << "static const std::size_t update_column_count = " <<
+ (update ? s.total - s.inverse - s.readonly : 0) << "UL;"
+ << endl;
+
+ os << "static const bool versioned = " << s.versioned << ";"
+ << endl;
+
+ // Statements.
+ //
+ if (load || load_opt)
+ os << "static const char select_statement[];"
+ << endl;
+
+ if (update || update_opt)
+ os << "static const char update_statement[];"
+ << endl;
+
+ // Section statements.
+ //
+ if (load || load_opt || update || update_opt)
+ os << "typedef " << db << "::section_statements< object_type, " <<
+ name << " > statements_type;"
+ << endl;
+
+ // We pass statement cache instead of just statements because
+ // we may also need statements for containers.
+ //
+
+ // load ()
+ //
+ if (load || load_opt || load_con)
+ os << "static void" << endl
+ << "load (extra_statement_cache_type&, object_type&" <<
+ (poly ? ", bool top = true" : "") << ");"
+ << endl;
+
+ // update ()
+ //
+ if (update || update_opt || update_con)
+ os << "static void" << endl
+ << "update (extra_statement_cache_type&, const object_type&" <<
+ (poly_derived && s.base != 0 ? ", bool base = true" : "") << ");"
+ << endl;
+
+ section_public_extra_post (s);
+
+ os << "};";
+
+ if (rs != 0)
+ rs->base = 0;
+ }
+
+ protected:
+ semantics::class_& c_;
+ };
+
+ // First pass over objects, views, and composites. Some code must be
+ // split into two parts to deal with yet undefined types.
+ //
+ struct class1: traversal::class_, virtual context
+ {
+ typedef class1 base;
+
+ class1 ()
+ : typedefs_ (false),
+ id_image_member_ ("id_"),
+ version_image_member_ ("version_"),
+ discriminator_image_member_ ("discriminator_"),
+ query_columns_type_ (false, true, false),
+ pointer_query_columns_type_ (true, true, false)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+ }
+
+ class1 (class1 const&)
+ : root_context (), //@@ -Wextra
+ context (),
+ typedefs_ (false),
+ id_image_member_ ("id_"),
+ version_image_member_ ("version_"),
+ discriminator_image_member_ ("discriminator_"),
+ query_columns_type_ (false, true, false),
+ pointer_query_columns_type_ (true, true, false)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck == class_other ||
+ (!options.at_once () && class_file (c) != unit.file ()))
+ return;
+
+ names (c);
+
+ switch (ck)
+ {
+ case class_object: traverse_object (c); break;
+ case class_view: traverse_view (c); break;
+ case class_composite: traverse_composite (c); break;
+ default: break;
+ }
+ }
+
+ virtual void
+ object_public_extra_pre (type&)
+ {
+ }
+
+ virtual void
+ object_public_extra_post (type&)
+ {
+ }
+
+ virtual void
+ traverse_object (type&);
+
+ virtual void
+ view_public_extra_pre (type&)
+ {
+ }
+
+ virtual void
+ view_public_extra_post (type&)
+ {
+ }
+
+ virtual void
+ traverse_view (type&);
+
+ virtual void
+ traverse_composite (type&);
+
+ private:
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ instance<image_type> image_type_;
+ instance<image_member> id_image_member_;
+ instance<image_member> version_image_member_;
+ instance<image_member> discriminator_image_member_;
+
+ instance<query_columns_type> query_columns_type_;
+ instance<query_columns_type> pointer_query_columns_type_;
+ };
+
+ // Second pass over objects, views, and composites.
+ //
+ struct class2: traversal::class_, virtual context
+ {
+ typedef class2 base;
+
+ class2 ()
+ : typedefs_ (false),
+ query_columns_type_ (false, true, false),
+ query_columns_type_inst_ (false, false, true),
+ view_query_columns_type_ (true)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+ }
+
+ class2 (class2 const&)
+ : root_context (), //@@ -Wextra
+ context (),
+ typedefs_ (false),
+ query_columns_type_ (false, true, false),
+ query_columns_type_inst_ (false, false, true),
+ view_query_columns_type_ (true)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck == class_other ||
+ (!options.at_once () && class_file (c) != unit.file ()))
+ return;
+
+ names (c);
+
+ switch (ck)
+ {
+ case class_object: traverse_object (c); break;
+ case class_view: traverse_view (c); break;
+ case class_composite: traverse_composite (c); break;
+ default: break;
+ }
+ }
+
+ virtual void
+ traverse_object (type& c)
+ {
+ if (options.generate_query ())
+ {
+ os << "// " << class_name (c) << endl
+ << "//" << endl;
+
+ // query_columns
+ //
+ // If we don't have any pointers, then query_columns is generated
+ // in pass 1 (see the comment in class1 for details).
+ //
+ if (has_a (c, test_pointer | include_base))
+ query_columns_type_->traverse (c);
+
+ // Generate extern template declarations.
+ //
+ if (multi_dynamic)
+ query_columns_type_inst_->traverse (c);
+ }
+
+ // Move header comment out of if-block if adding any code here.
+ }
+
+ virtual void
+ traverse_view (type& c)
+ {
+ // query_columns
+ //
+ if (c.get<size_t> ("object-count") != 0)
+ {
+ os << "// " << class_name (c) << endl
+ << "//" << endl;
+
+ view_query_columns_type_->traverse (c);
+ }
+
+ // Move header comment out of if-block if adding any code here.
+ }
+
+ virtual void
+ traverse_composite (type&)
+ {
+ }
+
+ private:
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ instance<query_columns_type> query_columns_type_;
+ instance<query_columns_type> query_columns_type_inst_;
+ instance<view_query_columns_type> view_query_columns_type_;
+ };
+
+ struct include: virtual context
+ {
+ typedef include base;
+
+ virtual void
+ generate ()
+ {
+ os << "#include <odb/details/buffer.hxx>" << endl
+ << endl;
+
+ os << "#include <odb/" << db << "/version.hxx>" << endl
+ << "#include <odb/" << db << "/forward.hxx>" << endl
+ << "#include <odb/" << db << "/binding.hxx>" << endl
+ << "#include <odb/" << db << "/" << db << "-types.hxx>" << endl;
+
+ if (options.generate_query ())
+ {
+ os << "#include <odb/" << db << "/query.hxx>" << endl;
+
+ if (multi_dynamic)
+ os << "#include <odb/" << db << "/query-dynamic.hxx>" << endl;
+ }
+
+ os << endl;
+ }
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_HEADER_HXX
diff --git a/odb/odb/relational/inline.cxx b/odb/odb/relational/inline.cxx
new file mode 100644
index 0000000..5e60705
--- /dev/null
+++ b/odb/odb/relational/inline.cxx
@@ -0,0 +1,47 @@
+// file : odb/relational/inline.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/inline.hxx>
+#include <odb/relational/generate.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace inline_
+ {
+ void
+ generate ()
+ {
+ context ctx;
+ ostream& os (ctx.os);
+
+ instance<include> i;
+ i->generate ();
+
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (false);
+ traversal::namespace_ ns;
+ class_ c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (false);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ os << "namespace odb"
+ << "{";
+
+ unit.dispatch (ctx.unit);
+
+ os << "}";
+ }
+ }
+}
diff --git a/odb/odb/relational/inline.hxx b/odb/odb/relational/inline.hxx
new file mode 100644
index 0000000..a609cc1
--- /dev/null
+++ b/odb/odb/relational/inline.hxx
@@ -0,0 +1,693 @@
+// file : odb/relational/inline.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_INLINE_HXX
+#define ODB_RELATIONAL_INLINE_HXX
+
+#include <odb/diagnostics.hxx>
+#include <odb/relational/context.hxx>
+#include <odb/relational/common.hxx>
+
+namespace relational
+{
+ namespace inline_
+ {
+ //
+ // get/set null (composite value only)
+ //
+
+ struct null_member: virtual member_base
+ {
+ typedef null_member base;
+
+ null_member (bool get)
+ : member_base (0, 0, string (), string ()), get_ (get) {}
+
+ protected:
+ bool get_;
+ };
+
+ template <typename T>
+ struct null_member_impl: null_member, virtual member_base_impl<T>
+ {
+ typedef null_member_impl base_impl;
+
+ null_member_impl (base const& x): base (x) {}
+
+ typedef typename member_base_impl<T>::member_info member_info;
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ // If the member is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (mi.m));
+ unsigned long long dv (deleted (mi.m));
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")"
+ << "{";
+ }
+
+ // If the whole value type is readonly, then set will never be
+ // called with sk == statement_update.
+ //
+ if (!get_ && !readonly (*context::top_object))
+ {
+ semantics::class_* c;
+
+ if (readonly (mi.m) || ((c = composite (mi.t)) && readonly (*c)))
+ os << "if (sk == statement_insert)" << endl;
+ }
+
+ return true;
+ }
+
+ virtual void
+ post (member_info& mi)
+ {
+ if (added (mi.m) || deleted (mi.m))
+ os << "}";
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ string traits ("composite_value_traits< " + mi.fq_type () + ", id_" +
+ db.string () + " >");
+
+ if (get_)
+ os << "r = r && " << traits << "::get_null (" <<
+ "i." << mi.var << "value";
+ else
+ os << traits << "::set_null (i." << mi.var << "value, sk";
+
+ if (versioned (*composite (mi.t)))
+ os << ", svm";
+
+ os << ");";
+ }
+ };
+
+ struct null_base: traversal::class_, virtual context
+ {
+ typedef null_base base;
+
+ null_base (bool get): get_ (get) {}
+
+ virtual void
+ traverse (type& c)
+ {
+ // Ignore transient bases.
+ //
+ if (!composite (c))
+ return;
+
+ string traits ("composite_value_traits< " + class_fq_name (c) +
+ ", id_" + db.string () + " >");
+
+ // If the derived value type is readonly, then set will never be
+ // called with sk == statement_update.
+ //
+ if (!get_ && readonly (c) && !readonly (*context::top_object))
+ os << "if (sk == statement_insert)" << endl;
+
+ if (get_)
+ os << "r = r && " << traits << "::get_null (i";
+ else
+ os << traits << "::set_null (i, sk";
+
+ if (versioned (c))
+ os << ", svm";
+
+ os << ");";
+ }
+
+ protected:
+ bool get_;
+ };
+
+ //
+ //
+ struct class_: traversal::class_, virtual context
+ {
+ typedef class_ base;
+
+ class_ ()
+ : typedefs_ (false),
+ get_null_base_ (true),
+ get_null_member_ (true),
+ set_null_base_ (false),
+ set_null_member_ (false)
+ {
+ init ();
+ }
+
+ class_ (class_ const&)
+ : root_context (), //@@ -Wextra
+ context (),
+ typedefs_ (false),
+ get_null_base_ (true),
+ get_null_member_ (true),
+ set_null_base_ (false),
+ set_null_member_ (false)
+ {
+ init ();
+ }
+
+ void
+ init ()
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+
+ get_null_base_inherits_ >> get_null_base_;
+ get_null_member_names_ >> get_null_member_;
+
+ set_null_base_inherits_ >> set_null_base_;
+ set_null_member_names_ >> set_null_member_;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck == class_other ||
+ (!options.at_once () && class_file (c) != unit.file ()))
+ return;
+
+ names (c);
+
+ context::top_object = context::cur_object = &c;
+
+ switch (ck)
+ {
+ case class_object: traverse_object (c); break;
+ case class_view: traverse_view (c); break;
+ case class_composite: traverse_composite (c); break;
+ default: break;
+ }
+
+ context::top_object = context::cur_object = 0;
+ }
+
+ virtual void
+ object_extra (type&)
+ {
+ }
+
+ virtual void
+ traverse_object (type& c)
+ {
+ using semantics::data_member;
+
+ data_member_path* id (id_member (c));
+ data_member* idf (id ? id->front () : 0);
+ bool auto_id (id && auto_ (*id));
+ bool base_id (id && &idf->scope () != &c); // Comes from base.
+ data_member* optimistic (context::optimistic (c));
+
+ // Base class that contains the object id and version for optimistic
+ // concurrency.
+ //
+ type* base (base_id ? dynamic_cast<type*> (&idf->scope ()) : 0);
+
+ type* poly_root (context::polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+
+ bool abst (abstract (c));
+ bool reuse_abst (abst && !poly);
+
+ bool versioned (context::versioned (c));
+
+ string const& type (class_fq_name (c));
+ string traits ("access::object_traits_impl< " + type + ", id_" +
+ db.string () + " >");
+
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl
+ << endl;
+
+ object_extra (c);
+
+ if (id != 0 && base_id)
+ {
+ if (!poly_derived)
+ {
+ // id (id_image_type)
+ //
+ if (auto_id)
+ {
+ os << "inline" << endl
+ << traits << "::id_type" << endl
+ << traits << "::" << endl
+ << "id (const id_image_type& i)"
+ << "{"
+ << "return object_traits_impl< " << class_fq_name (*base) <<
+ ", id_" << db << " >::id (i);"
+ << "}";
+ }
+
+ // id (image_type)
+ //
+ if (options.generate_query ())
+ {
+ os << "inline" << endl
+ << traits << "::id_type" << endl
+ << traits << "::" << endl
+ << "id (const image_type& i)"
+ << "{"
+ << "return object_traits_impl< " << class_fq_name (*base) <<
+ ", id_" << db << " >::id (i);"
+ << "}";
+ }
+
+ // version (image_type)
+ //
+ if (optimistic != 0)
+ {
+ os << "inline" << endl
+ << traits << "::version_type" << endl
+ << traits << "::" << endl
+ << "version (const image_type& i)"
+ << "{"
+ << "return object_traits_impl< " << class_fq_name (*base) <<
+ ", id_" << db << " >::version (i);"
+ << "}";
+ }
+ }
+
+ // bind (id_image_type)
+ //
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "bind (" << bind_vector << " b, id_image_type& i" <<
+ (optimistic != 0 ? ", bool bv" : "") << ")"
+ << "{"
+ << "object_traits_impl< " << class_fq_name (*base) << ", id_" <<
+ db << " >::bind (b, i" << (optimistic != 0 ? ", bv" : "") << ");"
+ << "}";
+
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "init (id_image_type& i, const id_type& id" <<
+ (optimistic != 0 ? ", const version_type* v" : "") << ")"
+ << "{"
+ << "object_traits_impl< " << class_fq_name (*base) << ", id_" <<
+ db << " >::init (i, id" << (optimistic != 0 ? ", v" : "") << ");"
+ << "}";
+ }
+
+ if (poly_derived)
+ {
+ size_t depth (polymorphic_depth (c));
+
+ // check_version
+ //
+ os << "inline" << endl
+ << "bool " << traits << "::" << endl
+ << "check_version (const std::size_t* v, const image_type& i)"
+ << "{"
+ << "return ";
+
+ string image ("i.");
+ for (size_t i (0); i < depth; ++i)
+ {
+ os << (i == 0 ? "" : " ||") << endl
+ << " v[" << i << "UL] != " << image << "version";
+
+ image += "base->";
+ }
+
+ os << ";"
+ << "}";
+
+ // update_version
+ //
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "update_version (std::size_t* v, const image_type& i, " <<
+ db << "::binding* b)"
+ << "{";
+
+ image = "i.";
+ for (size_t i (0); i < depth; ++i)
+ {
+ os << "v[" << i << "UL] = " << image << "version;";
+ image += "base->";
+ }
+
+ // A poly-abstract class always has only one entry in the
+ // bindings array.
+ //
+ if (abst)
+ os << "b[0].version++;";
+ else
+ for (size_t i (0); i < depth; ++i)
+ os << "b[" << i << "UL].version++;";
+
+ os << "}";
+ }
+
+ // The rest does not apply to reuse-abstract objects.
+ //
+ if (reuse_abst)
+ return;
+
+ // erase (object_type)
+ //
+ if (id != 0 && !poly && optimistic == 0 &&
+ !has_a (c, test_smart_container))
+ {
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "erase (database& db, const object_type& obj)"
+ << "{"
+ << "callback (db, obj, callback_event::pre_erase);"
+ << "erase (db, id (obj));";
+
+ // Note that we don't reset sections since the object is now
+ // transient and the state of a section in a transient object
+ // is undefined.
+
+ os << "callback (db, obj, callback_event::post_erase);"
+ << "}";
+ }
+
+ // load (section) [thunk version; poly_derived is true]
+ //
+ if (uss.count (user_sections::count_total |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0 &&
+ uss.count (user_sections::count_new |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) == 0)
+ {
+ os << "inline" << endl
+ << "bool " << traits << "::" << endl
+ << "load (connection& conn, object_type& obj, section& s, " <<
+ "const info_type* pi)"
+ << "{"
+ << "return base_traits::load (conn, obj, s, pi);"
+ << "}";
+ }
+
+ // update (section) [thunk version; poly_derived is true]
+ //
+ if (uss.count (user_sections::count_total |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) != 0 &&
+ uss.count (user_sections::count_new |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) == 0)
+ {
+ os << "inline" << endl
+ << "bool " << traits << "::" << endl
+ << "update (connection& conn, const object_type& obj, " <<
+ "const section& s, const info_type* pi)"
+ << "{"
+ << "return base_traits::update (conn, obj, s, pi);"
+ << "}";
+ }
+
+ // load_()
+ //
+ if (id != 0 &&
+ !(poly_derived ||
+ has_a (c, test_container | include_eager_load, &main_section) ||
+ uss.count (user_sections::count_new |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0))
+ {
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "load_ (statements_type& sts," << endl
+ << "object_type& obj," << endl
+ << "bool";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (sts);"
+ << "ODB_POTENTIALLY_UNUSED (obj);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl;
+
+ // Mark eager sections as loaded.
+ //
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // Skip special sections.
+ //
+ if (i->special == user_section::special_version)
+ continue;
+
+ data_member& m (*i->member);
+
+ // If the section is soft- added or deleted, check the version.
+ // We can only end up here if the object itself is versioned
+ // (simple value section).
+ //
+ unsigned long long av (added (m));
+ unsigned long long dv (deleted (m));
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")" << endl;
+ }
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << ma.translate ("obj") << ".reset (true, false);"
+ << endl;
+ }
+
+ os << "}";
+ }
+
+ if (poly && need_image_clone && options.generate_query ())
+ {
+ // root_image ()
+ //
+ os << "inline" << endl
+ << traits << "::root_traits::image_type&" << endl
+ << traits << "::" << endl
+ << "root_image (image_type& i)"
+ << "{";
+
+ if (poly_derived)
+ os << "return base_traits::root_image (*i.base);";
+ else
+ os << "return i;";
+
+ os << "}";
+
+ // clone_image ()
+ //
+ os << "inline" << endl
+ << traits << "::image_type*" << endl
+ << traits << "::" << endl
+ << "clone_image (image_type& i)"
+ << "{";
+
+ if (poly_derived)
+ os << "details::unique_ptr<base_traits::image_type> p (" << endl
+ << "base_traits::clone_image (*i.base));"
+ << "image_type* c (new image_type (i));"
+ << "c->base = p.release ();"
+ << "return c;";
+ else
+ os << "return new image_type (i);";
+
+ os << "}";
+
+ // copy_image ()
+ //
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "copy_image (image_type& d, image_type& s)"
+ << "{";
+
+ if (poly_derived)
+ os << "base_traits::image_type* b (d.base);"
+ << "base_traits::copy_image (*b, *s.base);"
+ << "d = s;" // Overwrites the base pointer.
+ << "d.base = b;";
+ else
+ os << "d = s;";
+
+ os << "}";
+
+ // free_image ()
+ //
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "free_image (image_type* i)"
+ << "{";
+
+ if (poly_derived)
+ os << "base_traits::free_image (i->base);";
+
+ os << "delete i;"
+ << "}";
+ }
+ }
+
+ virtual void
+ view_extra (type&)
+ {
+ }
+
+ virtual void
+ traverse_view (type& c)
+ {
+ string const& type (class_fq_name (c));
+ string traits ("access::view_traits_impl< " + type + ", id_" +
+ db.string () + " >");
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl
+ << endl;
+
+ view_extra (c);
+ }
+
+ virtual void
+ traverse_composite (type& c)
+ {
+ bool versioned (context::versioned (c));
+
+ string const& type (class_fq_name (c));
+ string traits ("access::composite_value_traits< " + type + ", id_" +
+ db.string () + " >");
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl
+ << endl;
+
+ if (!has_a (c, test_container))
+ {
+ // get_null (image)
+ //
+ os << "inline" << endl
+ << "bool " << traits << "::" << endl
+ << "get_null (const image_type& i";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);"
+ << endl;
+
+ os << "bool r (true);";
+
+ inherits (c, get_null_base_inherits_);
+ names (c, get_null_member_names_);
+
+ os << "return r;"
+ << "}";
+
+ // set_null (image)
+ //
+ os << "inline" << endl
+ << "void " << traits << "::" << endl
+ << "set_null (image_type& i," << endl
+ << db << "::statement_kind sk";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (sk);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "using namespace " << db << ";"
+ << endl;
+
+ inherits (c, set_null_base_inherits_);
+ names (c, set_null_member_names_);
+
+ os << "}";
+ }
+ }
+
+ private:
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ instance<null_base> get_null_base_;
+ traversal::inherits get_null_base_inherits_;
+ instance<null_member> get_null_member_;
+ traversal::names get_null_member_names_;
+
+ instance<null_base> set_null_base_;
+ traversal::inherits set_null_base_inherits_;
+ instance<null_member> set_null_member_;
+ traversal::names set_null_member_names_;
+ };
+
+ struct include: virtual context
+ {
+ typedef include base;
+
+ virtual void
+ generate ()
+ {
+ if (versioned ())
+ os << "#include <odb/schema-version.hxx>" << endl
+ << endl;
+
+ if (features.polymorphic_object && options.generate_query ())
+ os << "#include <odb/details/unique-ptr.hxx>" << endl
+ << endl;
+ }
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_INLINE_HXX
diff --git a/odb/odb/relational/model.cxx b/odb/odb/relational/model.cxx
new file mode 100644
index 0000000..45d555a
--- /dev/null
+++ b/odb/odb/relational/model.cxx
@@ -0,0 +1,121 @@
+// file : odb/relational/model.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/diagnostics.hxx>
+
+#include <odb/relational/model.hxx>
+#include <odb/relational/generate.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace model
+ {
+ // object_columns
+ //
+ string object_columns::
+ default_ (semantics::data_member& m)
+ {
+ default_value* dv (0);
+
+ semantics::type& t (utype (m));
+
+ if (m.count ("default"))
+ dv = &m.get<default_value> ("default");
+ else if (t.count ("default"))
+ dv = &t.get<default_value> ("default");
+ else
+ return ""; // No default value for this column.
+
+ switch (dv->kind)
+ {
+ case default_value::reset:
+ {
+ return ""; // No default value.
+ }
+ case default_value::null:
+ {
+ return default_null (m);
+ }
+ case default_value::boolean:
+ {
+ return default_bool (m, dv->literal == "true");
+ }
+ case default_value::integer:
+ {
+ return default_integer (m, dv->int_value, dv->literal == "-");
+ }
+ case default_value::floating:
+ {
+ return default_float (m, dv->float_value);
+ }
+ case default_value::string:
+ {
+ return default_string (m, dv->literal);
+ }
+ case default_value::enumerator:
+ {
+ return default_enum (m, dv->enum_value, dv->literal);
+ }
+ }
+
+ return "";
+ }
+
+ cutl::shared_ptr<sema_rel::model>
+ generate ()
+ {
+ context ctx;
+ cutl::shared_ptr<sema_rel::model> m (
+ new (shared) sema_rel::model (
+ ctx.versioned () ? ctx.version ().current : 0));
+ m->set ("deleted-map", deleted_table_map ());
+
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (false);
+ traversal::namespace_ ns;
+ instance<class_> c (*m);
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (false);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ try
+ {
+ unit.dispatch (ctx.unit);
+ }
+ catch (sema_rel::duplicate_name const& e)
+ {
+ location const& o (e.orig.get<location> ("cxx-location"));
+ location const& d (e.dup.get<location> ("cxx-location"));
+
+ error (d) << e.dup.kind () << " name '" << e.name << "' conflicts "
+ << "with an already defined " << e.orig.kind () << " name"
+ << endl;
+
+ info (o) << "conflicting " << e.orig.kind () << " is defined here"
+ << endl;
+
+ if (e.dup.kind () == "index")
+ info (d) << "use #pragma db index to change its name" << endl;
+ else if (e.dup.kind () == "table")
+ info (d) << "use #pragma db table to change its name" << endl;
+ else
+ info (d) << "use #pragma db column to change its name" << endl;
+
+ throw operation_failed ();
+ }
+
+ return m;
+ }
+ }
+}
diff --git a/odb/odb/relational/model.hxx b/odb/odb/relational/model.hxx
new file mode 100644
index 0000000..fdfa8fd
--- /dev/null
+++ b/odb/odb/relational/model.hxx
@@ -0,0 +1,868 @@
+// file : odb/relational/model.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_MODEL_HXX
+#define ODB_RELATIONAL_MODEL_HXX
+
+#include <map>
+#include <set>
+#include <cassert>
+#include <sstream>
+
+#include <odb/semantics/relational.hxx>
+
+#include <odb/relational/common.hxx>
+#include <odb/relational/context.hxx>
+
+namespace relational
+{
+ namespace model
+ {
+ typedef std::set<qname> tables;
+ typedef std::map<qname, semantics::node*> deleted_table_map;
+ typedef std::map<uname, semantics::data_member*> deleted_column_map;
+
+ struct object_columns: object_columns_base, virtual context
+ {
+ typedef object_columns base;
+
+ object_columns (sema_rel::model& model,
+ sema_rel::table& table,
+ bool object)
+ : model_ (model),
+ table_ (table),
+ object_ (object),
+ pkey_ (0),
+ id_override_ (false)
+ {
+ }
+
+ virtual void
+ traverse_object (semantics::class_& c)
+ {
+ if (context::top_object != &c)
+ {
+ // We are in one of the bases. Set the id_prefix to its
+ // (unqualified) name.
+ //
+ string t (id_prefix_);
+ id_prefix_ = class_name (c) + "::";
+ object_columns_base::traverse_object (c);
+ id_prefix_ = t;
+ }
+ else
+ object_columns_base::traverse_object (c);
+ }
+
+ virtual void
+ traverse_composite (semantics::data_member* m, semantics::class_& c)
+ {
+ string t (id_prefix_);
+
+ if (m != 0)
+ // Member of a composite type. Add the data member to id_prefix.
+ //
+ if (!id_override_)
+ id_prefix_ += m->name () + ".";
+ else
+ id_override_ = false;
+ else
+ // Composite base. Add its unqualified name to id_prefix.
+ //
+ id_prefix_ += class_name (c) + "::";
+
+ object_columns_base::traverse_composite (m, c);
+
+ id_prefix_ = t;
+ }
+
+ virtual void
+ traverse (semantics::data_member& m,
+ semantics::type& t,
+ string const& kp,
+ string const& dn,
+ semantics::class_* to = 0)
+ {
+ // This overrides the member name for a composite container value
+ // or key.
+ //
+ if (!kp.empty ())
+ {
+ semantics::class_* c (object_pointer (t));
+ if (composite_wrapper (c == 0 ? t : utype (*id_member (*c))))
+ {
+ id_prefix_ = kp + ".";
+ id_override_ = true;
+ }
+ }
+
+ object_columns_base::traverse (m, t, kp, dn, to);
+ }
+
+ using object_columns_base::traverse;
+
+ virtual bool
+ traverse_column (semantics::data_member& m, string const& name, bool)
+ {
+ if (semantics::data_member* m = deleted_member (member_path_))
+ {
+ table_.get<deleted_column_map> ("deleted-map")[name] = m;
+ return false;
+ }
+
+ string col_id (id_prefix_ +
+ (key_prefix_.empty () ? m.name () : key_prefix_));
+
+ sema_rel::column& c (
+ model_.new_node<sema_rel::column> (col_id, type (m), null (m)));
+ c.set ("cxx-location", m.location ());
+ c.set ("member-path", member_path_);
+ model_.new_edge<sema_rel::unames> (table_, c, name);
+
+ // An id member cannot have a default value.
+ //
+ if (!object_columns_base::id ())
+ {
+ string const& d (default_ (m));
+
+ if (!d.empty ())
+ c.default_ (d);
+ }
+
+ // If we have options, add them.
+ //
+ string const& o (column_options (m, key_prefix_));
+
+ if (!o.empty ())
+ c.options (o);
+
+ constraints (m, name, col_id, c);
+ return true;
+ }
+
+ virtual string
+ type (semantics::data_member&)
+ {
+ return object_columns_base::column_type ();
+ }
+
+ virtual bool
+ null (semantics::data_member&)
+ {
+ return !object_columns_base::id () && object_columns_base::null ();
+ }
+
+ virtual string
+ default_null (semantics::data_member&)
+ {
+ return "NULL";
+ }
+
+ virtual string
+ default_bool (semantics::data_member&, bool v)
+ {
+ // Most databases do not support boolean literals. Those that
+ // do should override this.
+ //
+ return (v ? "1" : "0");
+ }
+
+ virtual string
+ default_integer (semantics::data_member&, unsigned long long v, bool neg)
+ {
+ std::ostringstream ostr;
+ ostr << (neg ? "-" : "") << v;
+ return ostr.str ();
+ }
+
+ virtual string
+ default_float (semantics::data_member&, double v)
+ {
+ std::ostringstream ostr;
+ ostr << v;
+ return ostr.str ();
+ }
+
+ virtual string
+ default_string (semantics::data_member&, string const& v)
+ {
+ return quote_string (v);
+ }
+
+ virtual string
+ default_enum (semantics::data_member&,
+ tree /*enumerator*/,
+ string const& /*name*/)
+ {
+ // Has to be implemented by the database-specific override.
+ //
+ assert (false);
+ return string ();
+ }
+
+ virtual void
+ primary_key (sema_rel::primary_key&)
+ {
+ }
+
+ virtual void
+ constraints (semantics::data_member& m,
+ string const& /* name */,
+ string const& /* id */,
+ sema_rel::column& c)
+ {
+ if (object_)
+ {
+ if (semantics::data_member* idm = id ())
+ {
+ if (pkey_ == 0)
+ {
+ pkey_ = &model_.new_node<sema_rel::primary_key> (
+ m.count ("auto"));
+ pkey_->set ("cxx-location", idm->location ());
+
+ // In most databases the primary key constraint can be
+ // manipulated without an explicit name. So we use the special
+ // empty name for primary keys in order not to clash with
+ // columns and other constraints. If the target database does
+ // not support unnamed primary key manipulation, then the
+ // database-specific code will have to come up with a suitable
+ // name.
+ //
+ model_.new_edge<sema_rel::unames> (table_, *pkey_, "");
+ primary_key (*pkey_);
+ }
+
+ model_.new_edge<sema_rel::contains> (*pkey_, c);
+ }
+ }
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ // Ignore inverse object pointers.
+ //
+ if (inverse (m, key_prefix_))
+ return;
+
+ if (deleted (member_path_))
+ {
+ // Still traverse it as columns so that we can populate the
+ // deleted map.
+ //
+ object_columns_base::traverse_pointer (m, c);
+ return;
+ }
+
+ // Get the position of the last column.
+ //
+ sema_rel::table::names_iterator i (table_.names_end ());
+
+ while (i != table_.names_begin ())
+ {
+ --i;
+ if (i->nameable ().is_a<sema_rel::column> ())
+ break;
+ }
+
+ // Traverse the object pointer as columns.
+ //
+ object_columns_base::traverse_pointer (m, c);
+
+ // Get to the first column that we have added.
+ //
+ if (i != table_.names_end ())
+ ++i; // Next column.
+ else
+ i = table_.names_begin ();
+
+ foreign_key (m, c, i);
+ }
+
+ virtual void
+ traverse_points_to (semantics::data_member& m, semantics::class_& c)
+ {
+ if (deleted (member_path_))
+ {
+ // Still traverse it as columns so that we can populate the
+ // deleted map.
+ //
+ object_columns_base::traverse_points_to (m, c);
+ return;
+ }
+
+ // Get the position of the last column.
+ //
+ sema_rel::table::names_iterator i (table_.names_end ());
+
+ while (i != table_.names_begin ())
+ {
+ --i;
+ if (i->nameable ().is_a<sema_rel::column> ())
+ break;
+ }
+
+ // Traverse the data member as columns.
+ //
+ object_columns_base::traverse_points_to (m, c);
+
+ // Get to the first column that we have added.
+ //
+ if (i != table_.names_end ())
+ ++i; // Next column.
+ else
+ i = table_.names_begin ();
+
+ foreign_key (m, c, i);
+ }
+
+ virtual void
+ foreign_key (semantics::data_member& m,
+ semantics::class_& c,
+ sema_rel::table::names_iterator i)
+ {
+ using sema_rel::column;
+ using sema_rel::foreign_key;
+
+ string id (id_prefix_ +
+ (key_prefix_.empty () ? m.name () : key_prefix_));
+
+ deferrable def (
+ m.get<deferrable> ("deferrable",
+ options.fkeys_deferrable_mode ()[db]));
+
+ foreign_key::action_type on_delete (
+ m.get<foreign_key::action_type> (
+ "on-delete", foreign_key::no_action));
+
+ foreign_key& fk (
+ model_.new_node<foreign_key> (id, table_name (c), def, on_delete));
+
+ fk.set ("cxx-location", m.location ());
+
+ bool simple;
+
+ // Get referenced columns.
+ //
+ {
+ data_member_path& id (*id_member (c));
+
+ instance<object_columns_list> ocl;
+ ocl->traverse (id);
+
+ for (object_columns_list::iterator i (ocl->begin ());
+ i != ocl->end (); ++i)
+ fk.referenced_columns ().push_back (i->name);
+
+ simple = (fk.referenced_columns ().size () == 1);
+ }
+
+ // Get referencing columns.
+ //
+ for (; i != table_.names_end (); ++i)
+ {
+ if (column* c = dynamic_cast<column*> (&i->nameable ()))
+ model_.new_edge<sema_rel::contains> (fk, *c);
+ else
+ break;
+ }
+
+ // Derive the constraint name. Generally, we want it to be based
+ // on the column name. This is straightforward for single-column
+ // references. In case of a composite id, we will need to use the
+ // column prefix which is based on the data member name, unless
+ // overridden by the user. In the latter case the prefix can be
+ // empty, in which case we will just fall back on the member's
+ // public name.
+ //
+ string name;
+
+ if (simple)
+ name = fk.contains_begin ()->column ().name ();
+ else
+ {
+ string p (column_prefix (m, key_prefix_, default_name_).prefix);
+
+ if (p.empty ())
+ p = public_name_db (m);
+ else if (p[p.size () - 1] == '_')
+ p.resize (p.size () - 1); // Remove trailing underscore.
+
+ name = column_prefix_.prefix + p;
+ }
+
+ model_.new_edge<sema_rel::unames> (
+ table_, fk, fkey_name (table_.name (), name));
+ }
+
+ protected:
+ string
+ default_ (semantics::data_member&);
+
+ protected:
+ sema_rel::model& model_;
+ sema_rel::table& table_;
+ bool object_;
+ sema_rel::primary_key* pkey_;
+ string id_prefix_;
+ bool id_override_;
+ };
+
+ struct object_indexes: traversal::class_, virtual context
+ {
+ typedef object_indexes base;
+
+ object_indexes (sema_rel::model& model, sema_rel::table& table)
+ : model_ (model), table_ (table)
+ {
+ *this >> inherits_ >> *this;
+ }
+
+ object_indexes (object_indexes const& x)
+ : root_context (), context (), //@@ -Wextra
+ model_ (x.model_), table_ (x.table_)
+ {
+ *this >> inherits_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ if (!object (c)) // Ignore transient bases.
+ return;
+
+ // Polymorphic bases get their own tables.
+ //
+ if (!polymorphic (c))
+ inherits (c);
+
+ indexes& ins (c.get<indexes> ("index"));
+
+ for (indexes::iterator i (ins.begin ()); i != ins.end (); ++i)
+ {
+ // Using index name as its id.
+ //
+ sema_rel::index& in (
+ model_.new_node<sema_rel::index> (
+ i->name, i->type, i->method, i->options));
+ in.set ("cxx-location", location (i->loc));
+ model_.new_edge<sema_rel::unames> (table_, in, i->name);
+
+ for (index::members_type::iterator j (i->members.begin ());
+ j != i->members.end (); ++j)
+ {
+ using sema_rel::column;
+
+ index::member& im (*j);
+
+ semantics::type* t (&utype (*im.path.back ()));
+
+ if (semantics::class_* ptr = object_pointer (*t))
+ t = &utype (*id_member (*ptr));
+
+ if (type* comp = composite_wrapper (*t))
+ {
+ // Composite value. Get the list of the columns. Note that
+ // the column prefix needs to contain all the components.
+ //
+ instance<object_columns_list> ocl (
+ column_prefix (im.path, true));
+ ocl->traverse (*comp);
+
+ for (object_columns_list::iterator i (ocl->begin ());
+ i != ocl->end (); ++i)
+ {
+ column* c (table_.find<column> (i->name));
+ assert (c != 0);
+ model_.new_edge<sema_rel::contains> (in, *c, im.options);
+ }
+ }
+ else
+ {
+ // Simple value. Get the column name and look it up in the
+ // table.
+ //
+ column* c (table_.find<column> (column_name (im.path)));
+ assert (c != 0);
+ model_.new_edge<sema_rel::contains> (in, *c, im.options);
+ }
+ }
+ }
+ }
+
+ private:
+ sema_rel::model& model_;
+ sema_rel::table& table_;
+
+ traversal::inherits inherits_;
+ };
+
+ struct member_create: object_members_base, virtual context
+ {
+ typedef member_create base;
+
+ member_create (sema_rel::model& model)
+ : object_members_base (false, true, false), model_ (model)
+ {
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&)
+ {
+ // We don't want to traverse composite id.
+ }
+
+ virtual void
+ traverse_object (semantics::class_& c)
+ {
+ if (context::top_object != &c)
+ {
+ // We are in one of the bases. Set the id_prefix to its
+ // (unqualified) name.
+ //
+ string t (id_prefix_);
+ id_prefix_ = class_name (c) + "::";
+ object_members_base::traverse_object (c);
+ id_prefix_ = t;
+ }
+ else
+ {
+ // Top-level object. Set its id as a prefix.
+ //
+ id_prefix_ = string (class_fq_name (c), 2) + "::";
+ object_members_base::traverse_object (c);
+ }
+ }
+
+ virtual void
+ traverse_composite (semantics::data_member* m, semantics::class_& c)
+ {
+ string t (id_prefix_);
+
+ if (m != 0)
+ // Member of a composite type. Add the data member to id_prefix.
+ //
+ id_prefix_ += m->name () + ".";
+ else
+ // Composite base. Add its unqualified name to id_prefix.
+ //
+ id_prefix_ += class_name (c) + "::";
+
+ object_members_base::traverse_composite (m, c);
+
+ id_prefix_ = t;
+ }
+
+ virtual string
+ table_options (semantics::data_member& m, semantics::type& ct)
+ {
+ return context::table_options (m, ct);
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type& ct)
+ {
+ using semantics::type;
+ using semantics::data_member;
+
+ using sema_rel::column;
+
+ // Ignore inverse containers of object pointers.
+ //
+ if (inverse (m, "value"))
+ return;
+
+ container_kind_type ck (container_kind (ct));
+ qname const& name (table_name (m, table_prefix_));
+
+ // Ignore deleted container members.
+ //
+ if (semantics::data_member* m = deleted_member (member_path_))
+ {
+ model_.get<deleted_table_map> ("deleted-map")[name] = m;
+ return;
+ }
+
+ // Add the [] decorator to distinguish this id from non-container
+ // ids (we don't want to ever end up comparing, for example, an
+ // object table to a container table).
+ //
+ string id (id_prefix_ + m.name () + "[]");
+
+ sema_rel::table& t (model_.new_node<sema_rel::table> (id));
+ t.set ("cxx-location", m.location ());
+ t.set ("member-path", member_path_);
+ t.set ("deleted-map", deleted_column_map ());
+ model_.new_edge<sema_rel::qnames> (model_, t, name);
+
+ t.options (table_options (m, ct));
+ t.extra ()["kind"] = "container";
+
+ // object_id
+ //
+ {
+ bool f (false); //@@ (im)persfect forwarding.
+ instance<object_columns> oc (model_, t, f);
+ oc->traverse (m, container_idt (m), "id", "object_id");
+ }
+
+ // Foreign key and index for the object id. Keep this foreign
+ // key first since we reply on this information to lookup the
+ // corresponding object table.
+ //
+ {
+ // Derive the name prefix. See the comment for the other foreign
+ // key code above.
+ //
+ // Note also that id_name can be a column prefix (if id is
+ // composite), in which case it can be empty. In this case
+ // we just fallback on the default name.
+ //
+ // Finally, this is a top-level column, so there is no column
+ // prefix.
+ //
+ string id_name (
+ column_name (m, "id", "object_id", column_prefix ()));
+
+ if (id_name.empty ())
+ id_name = "object_id";
+
+ // Foreign key.
+ //
+ sema_rel::foreign_key& fk (
+ model_.new_node<sema_rel::foreign_key> (
+ id + ".id",
+ table_name (*context::top_object),
+ sema_rel::deferrable::not_deferrable,
+ sema_rel::foreign_key::cascade));
+ fk.set ("cxx-location", m.location ());
+ model_.new_edge<sema_rel::unames> (
+ t, fk, fkey_name (t.name (), id_name));
+
+ // Get referenced columns.
+ //
+ {
+ data_member_path& id (*id_member (*context::top_object));
+
+ instance<object_columns_list> ocl;
+ ocl->traverse (id);
+
+ for (object_columns_list::iterator i (ocl->begin ());
+ i != ocl->end (); ++i)
+ fk.referenced_columns ().push_back (i->name);
+ }
+
+ // All the columns we have in this table so far are for the
+ // object id. Add them to the foreign key.
+ //
+ for (sema_rel::table::names_iterator i (t.names_begin ());
+ i != t.names_end ();
+ ++i)
+ {
+ if (column* c = dynamic_cast<column*> (&i->nameable ()))
+ model_.new_edge<sema_rel::contains> (fk, *c);
+ }
+
+ // Index. See if we have a custom index.
+ //
+ index* sin (m.count ("id-index") ? &m.get<index> ("id-index") : 0);
+ sema_rel::index* in (0);
+
+ if (sin != 0)
+ {
+ in = &model_.new_node<sema_rel::index> (
+ id + ".id", sin->type, sin->method, sin->options);
+ in->set ("cxx-location", location (sin->loc));
+ }
+ else
+ {
+ in = &model_.new_node<sema_rel::index> (id + ".id");
+ in->set ("cxx-location", m.location ());
+ }
+
+ model_.new_edge<sema_rel::unames> (
+ t,
+ *in,
+ sin != 0 && !sin->name.empty ()
+ ? sin->name
+ : index_name (name, id_name));
+
+ // All the columns we have in this table so far are for the
+ // object id. Add them to the index.
+ //
+ for (sema_rel::table::names_iterator i (t.names_begin ());
+ i != t.names_end ();
+ ++i)
+ {
+ if (column* c = dynamic_cast<column*> (&i->nameable ()))
+ model_.new_edge<sema_rel::contains> (
+ *in, *c, (sin != 0 ? sin->members.back ().options : ""));
+ }
+ }
+
+ // index (simple value)
+ //
+ bool ordered (ck == ck_ordered && !unordered (m));
+ if (ordered)
+ {
+ // Column.
+ //
+ {
+ bool f (false); //@@ (im)persfect forwarding.
+ instance<object_columns> oc (model_, t, f);
+ oc->traverse (m, container_it (m), "index", "index");
+ }
+
+ // This is a simple value so the name cannot be empty. It is
+ // also a top-level column, so there is no column prefix.
+ //
+ string col (column_name (m, "index", "index", column_prefix ()));
+
+ // Index. See if we have a custom index.
+ //
+ index* sin (m.count ("index-index")
+ ? &m.get<index> ("index-index")
+ : 0);
+ sema_rel::index* in (0);
+
+ if (sin != 0)
+ {
+ in = &model_.new_node<sema_rel::index> (
+ id + ".index", sin->type, sin->method, sin->options);
+ in->set ("cxx-location", location (sin->loc));
+ }
+ else
+ {
+ in = &model_.new_node<sema_rel::index> (id + ".index");
+ in->set ("cxx-location", m.location ());
+ }
+
+ model_.new_edge<sema_rel::unames> (
+ t,
+ *in,
+ sin != 0 && !sin->name.empty ()
+ ? sin->name
+ : index_name (name, col));
+
+ column* c (t.find<column> (col));
+ assert (c != 0);
+
+ model_.new_edge<sema_rel::contains> (
+ *in,
+ *c,
+ (sin != 0 ? sin->members.back ().options : ""));
+ }
+
+ // key
+ //
+ if (ck == ck_map || ck == ck_multimap)
+ {
+ bool f (false); //@@ (im)persfect forwarding.
+ instance<object_columns> oc (model_, t, f);
+ oc->traverse (m, container_kt (m), "key", "key");
+ }
+
+ // value
+ //
+ {
+ bool f (false); //@@ (im)persfect forwarding.
+ instance<object_columns> oc (model_, t, f);
+ oc->traverse (m, container_vt (m), "value", "value");
+ }
+ }
+
+ protected:
+ sema_rel::model& model_;
+ string id_prefix_;
+ };
+
+ struct class_: traversal::class_, virtual context
+ {
+ typedef class_ base;
+
+ class_ (sema_rel::model& model): model_ (model) {}
+
+ virtual string
+ table_options (type& c)
+ {
+ return context::table_options (c);
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ if (!options.at_once () && class_file (c) != unit.file ())
+ return;
+
+ if (!object (c))
+ return;
+
+ semantics::class_* poly (polymorphic (c));
+
+ if (abstract (c) && poly == 0)
+ return;
+
+ qname const& name (table_name (c));
+
+ // If the table with this name was already seen, assume the
+ // user knows what they are doing and skip it.
+ //
+ if (tables_.count (name))
+ return;
+
+ if (deleted (c))
+ {
+ model_.get<deleted_table_map> ("deleted-map")[name] = &c;
+ return;
+ }
+
+ string id (class_fq_name (c), 2); // Remove leading '::'.
+
+ sema_rel::table& t (model_.new_node<sema_rel::table> (id));
+ t.set ("cxx-location", c.location ());
+ t.set ("class", &c);
+ t.set ("deleted-map", deleted_column_map ());
+ model_.new_edge<sema_rel::qnames> (model_, t, name);
+
+ t.options (table_options (c));
+
+ t.extra ()["kind"] =(poly == 0
+ ? "object"
+ : (poly == &c
+ ? "polymorphic root object"
+ : "polymorphic derived object"));
+
+ // Add columns.
+ //
+ {
+ bool tr (true); //@@ (im)persfect forwarding.
+ instance<object_columns> oc (model_, t, tr);
+ oc->traverse (c);
+ }
+
+ // Add indexes.
+ //
+ {
+ instance<object_indexes> oi (model_, t);
+ oi->traverse (c);
+ }
+
+ tables_.insert (name);
+
+ // Create tables for members.
+ //
+ {
+ instance<member_create> mc (model_);
+ mc->traverse (c);
+ }
+ }
+
+ protected:
+ sema_rel::model& model_;
+ tables tables_;
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_MODEL_HXX
diff --git a/odb/odb/relational/mssql/common.cxx b/odb/odb/relational/mssql/common.cxx
new file mode 100644
index 0000000..1070d21
--- /dev/null
+++ b/odb/odb/relational/mssql/common.cxx
@@ -0,0 +1,603 @@
+// file : odb/relational/mssql/common.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/relational/mssql/common.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace mssql
+ {
+ //
+ // member_base
+ //
+
+ sql_type const& member_base::
+ member_sql_type (semantics::data_member& m)
+ {
+ return parse_sql_type (column_type (m, key_prefix_), m);
+ }
+
+ void member_base::
+ traverse_simple (member_info& mi)
+ {
+ const sql_type& st (*mi.st);
+
+ // The same long/short data test as in context.cxx:long_data().
+ //
+ switch (st.type)
+ {
+ // Integral types.
+ //
+ case sql_type::BIT:
+ case sql_type::TINYINT:
+ case sql_type::SMALLINT:
+ case sql_type::INT:
+ case sql_type::BIGINT:
+ {
+ traverse_integer (mi);
+ break;
+ }
+
+ // Fixed and floating point types.
+ //
+ case sql_type::DECIMAL:
+ {
+ traverse_decimal (mi);
+ break;
+ }
+ case sql_type::SMALLMONEY:
+ {
+ traverse_smallmoney (mi);
+ break;
+ }
+ case sql_type::MONEY:
+ {
+ traverse_money (mi);
+ break;
+ }
+ case sql_type::FLOAT:
+ {
+ if (st.prec > 24)
+ traverse_float8 (mi);
+ else
+ traverse_float4 (mi);
+
+ break;
+ }
+
+ // String and binary types.
+ //
+ case sql_type::CHAR:
+ case sql_type::VARCHAR:
+ {
+ // Zero precision means max in VARCHAR(max).
+ //
+ if (st.prec == 0 || st.prec > options.mssql_short_limit ())
+ traverse_long_string (mi);
+ else
+ traverse_string (mi);
+
+ break;
+ }
+ case sql_type::TEXT:
+ {
+ traverse_long_string (mi);
+ break;
+ }
+ case sql_type::NCHAR:
+ case sql_type::NVARCHAR:
+ {
+ // Zero precision means max in NVARCHAR(max). Note that
+ // the precision is in 2-byte UCS-2 characters, not bytes.
+ //
+ if (st.prec == 0 || st.prec * 2 > options.mssql_short_limit ())
+ traverse_long_nstring (mi);
+ else
+ traverse_nstring (mi);
+
+ break;
+ }
+ case sql_type::NTEXT:
+ {
+ traverse_long_nstring (mi);
+ break;
+ }
+ case sql_type::BINARY:
+ case sql_type::VARBINARY:
+ {
+ // Zero precision means max in VARCHAR(max).
+ //
+ if (st.prec == 0 || st.prec > options.mssql_short_limit ())
+ traverse_long_binary (mi);
+ else
+ traverse_binary (mi);
+
+ break;
+ }
+ case sql_type::IMAGE:
+ {
+ traverse_long_binary (mi);
+ break;
+ }
+
+ // Date-time types.
+ //
+ case sql_type::DATE:
+ {
+ traverse_date (mi);
+ break;
+ }
+ case sql_type::TIME:
+ {
+ traverse_time (mi);
+ break;
+ }
+ case sql_type::DATETIME:
+ case sql_type::DATETIME2:
+ case sql_type::SMALLDATETIME:
+ {
+ traverse_datetime (mi);
+ break;
+ }
+ case sql_type::DATETIMEOFFSET:
+ {
+ traverse_datetimeoffset (mi);
+ break;
+ }
+
+ // Other types.
+ //
+ case sql_type::UNIQUEIDENTIFIER:
+ {
+ traverse_uniqueidentifier (mi);
+ break;
+ }
+ case sql_type::ROWVERSION:
+ {
+ traverse_rowversion (mi);
+ break;
+ }
+ case sql_type::invalid:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+
+ //
+ // member_image_type
+ //
+
+ static const char* integer_types[] =
+ {
+ "unsigned char",
+ "unsigned char",
+ "short",
+ "int",
+ "long long"
+ };
+
+ member_image_type::
+ member_image_type (base const& x)
+ : member_base::base (x), // virtual base
+ base (x) {}
+
+ member_image_type::
+ member_image_type ()
+ : relational::member_base (0, 0, string (), string ()) {}
+
+ member_image_type::
+ member_image_type (semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : relational::member_base (type, ct, fq_type, key_prefix) {}
+
+ string member_image_type::
+ image_type (semantics::data_member& m)
+ {
+ type_.clear ();
+ member_base::traverse (m, true);
+ return type_;
+ }
+
+ void member_image_type::
+ traverse_composite (member_info& mi)
+ {
+ type_ = "composite_value_traits< " + mi.fq_type () +
+ ", id_mssql >::image_type";
+ }
+
+ void member_image_type::
+ traverse_integer (member_info& mi)
+ {
+ type_ = integer_types[mi.st->type - sql_type::BIT];
+ }
+
+ void member_image_type::
+ traverse_decimal (member_info&)
+ {
+ type_ = "mssql::decimal";
+ }
+
+ void member_image_type::
+ traverse_smallmoney (member_info&)
+ {
+ type_ = "mssql::smallmoney";
+ }
+
+ void member_image_type::
+ traverse_money (member_info&)
+ {
+ type_ = "mssql::money";
+ }
+
+ void member_image_type::
+ traverse_float4 (member_info&)
+ {
+ type_ = "float";
+ }
+
+ void member_image_type::
+ traverse_float8 (member_info&)
+ {
+ type_ = "double";
+ }
+
+ void member_image_type::
+ traverse_string (member_info&)
+ {
+ type_ = "char*";
+ }
+
+ void member_image_type::
+ traverse_long_string (member_info&)
+ {
+ type_ = "mssql::long_callback";
+ }
+
+ void member_image_type::
+ traverse_nstring (member_info&)
+ {
+ type_ = "mssql::ucs2_char*";
+ }
+
+ void member_image_type::
+ traverse_long_nstring (member_info&)
+ {
+ type_ = "mssql::long_callback";
+ }
+
+ void member_image_type::
+ traverse_binary (member_info&)
+ {
+ type_ = "char*";
+ }
+
+ void member_image_type::
+ traverse_long_binary (member_info&)
+ {
+ type_ = "mssql::long_callback";
+ }
+
+ void member_image_type::
+ traverse_date (member_info&)
+ {
+ type_ = "mssql::date";
+ }
+
+ void member_image_type::
+ traverse_time (member_info&)
+ {
+ type_ = "mssql::time";
+ }
+
+ void member_image_type::
+ traverse_datetime (member_info&)
+ {
+ type_ = "mssql::datetime";
+ }
+
+ void member_image_type::
+ traverse_datetimeoffset (member_info&)
+ {
+ type_ = "mssql::datetimeoffset";
+ }
+
+ void member_image_type::
+ traverse_uniqueidentifier (member_info&)
+ {
+ type_ = "mssql::uniqueidentifier";
+ }
+
+ void member_image_type::
+ traverse_rowversion (member_info&)
+ {
+ type_ = "unsigned char*";
+ }
+
+ entry<member_image_type> member_image_type_;
+
+ //
+ // member_database_type
+ //
+
+ static const char* integer_database_id[] =
+ {
+ "mssql::id_bit",
+ "mssql::id_tinyint",
+ "mssql::id_smallint",
+ "mssql::id_int",
+ "mssql::id_bigint"
+ };
+
+ member_database_type_id::
+ member_database_type_id (base const& x)
+ : member_base::base (x), // virtual base
+ base (x)
+ {
+ }
+
+ member_database_type_id::
+ member_database_type_id ()
+ : member_base::base (0, 0, string (), string ()), // virtual base
+ base (0, 0, string (), string ())
+ {
+ }
+
+ member_database_type_id::
+ member_database_type_id (semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base::base (type, ct, fq_type, key_prefix), // virtual base
+ base (type, ct, fq_type, key_prefix)
+ {
+ }
+
+ string member_database_type_id::
+ database_type_id (semantics::data_member& m)
+ {
+ type_id_.clear ();
+ member_base::traverse (m, true);
+ return type_id_;
+ }
+
+ void member_database_type_id::
+ traverse_composite (member_info&)
+ {
+ assert (false);
+ }
+
+ void member_database_type_id::
+ traverse_integer (member_info& mi)
+ {
+ type_id_ = integer_database_id[mi.st->type - sql_type::BIT];
+ }
+
+ void member_database_type_id::
+ traverse_decimal (member_info&)
+ {
+ type_id_ = "mssql::id_decimal";
+ }
+
+ void member_database_type_id::
+ traverse_smallmoney (member_info&)
+ {
+ type_id_ = "mssql::id_smallmoney";
+ }
+
+ void member_database_type_id::
+ traverse_money (member_info&)
+ {
+ type_id_ = "mssql::id_money";
+ }
+
+ void member_database_type_id::
+ traverse_float4 (member_info&)
+ {
+ type_id_ = "mssql::id_float4";
+ }
+
+ void member_database_type_id::
+ traverse_float8 (member_info&)
+ {
+ type_id_ = "mssql::id_float8";
+ }
+
+ void member_database_type_id::
+ traverse_string (member_info&)
+ {
+ type_id_ = "mssql::id_string";
+ }
+
+ void member_database_type_id::
+ traverse_long_string (member_info&)
+ {
+ type_id_ = "mssql::id_long_string";
+ }
+
+ void member_database_type_id::
+ traverse_nstring (member_info&)
+ {
+ type_id_ = "mssql::id_nstring";
+ }
+
+ void member_database_type_id::
+ traverse_long_nstring (member_info&)
+ {
+ type_id_ = "mssql::id_long_nstring";
+ }
+
+ void member_database_type_id::
+ traverse_binary (member_info&)
+ {
+ type_id_ = "mssql::id_binary";
+ }
+
+ void member_database_type_id::
+ traverse_long_binary (member_info&)
+ {
+ type_id_ = "mssql::id_long_binary";
+ }
+
+ void member_database_type_id::
+ traverse_date (member_info&)
+ {
+ type_id_ = "mssql::id_date";
+ }
+
+ void member_database_type_id::
+ traverse_time (member_info&)
+ {
+ type_id_ = "mssql::id_time";
+ }
+
+ void member_database_type_id::
+ traverse_datetime (member_info&)
+ {
+ type_id_ = "mssql::id_datetime";
+ }
+
+ void member_database_type_id::
+ traverse_datetimeoffset (member_info&)
+ {
+ type_id_ = "mssql::id_datetimeoffset";
+ }
+
+ void member_database_type_id::
+ traverse_uniqueidentifier (member_info&)
+ {
+ type_id_ = "mssql::id_uniqueidentifier";
+ }
+
+ void member_database_type_id::
+ traverse_rowversion (member_info&)
+ {
+ type_id_ = "mssql::id_rowversion";
+ }
+
+ entry<member_database_type_id> member_database_type_id_;
+
+ //
+ // query_columns
+ //
+
+ struct query_columns: relational::query_columns, context
+ {
+ query_columns (base const& x): base_impl (x) {}
+
+ virtual string
+ database_type_id (semantics::data_member& m)
+ {
+ return member_database_type_id_.database_type_id (m);
+ }
+
+ virtual void
+ column_ctor (string const& type, string const& name, string const& base)
+ {
+ os << name << " (";
+
+ if (multi_dynamic)
+ os << "odb::query_column< " << type << " >& qc," << endl;
+
+ os << "const char* t," << endl
+ << "const char* c," << endl
+ << "const char* conv," << endl
+ << "unsigned short p = 0," << endl
+ << "unsigned short s = 0xFFFF)" << endl
+ << " : " << base << " (" << (multi_dynamic ? "qc, " : "") <<
+ "t, c, conv, p, s)"
+ << "{"
+ << "}";
+ }
+
+ virtual void
+ column_ctor_args_extra (semantics::data_member& m)
+ {
+ // For some types we need to pass precision and scale.
+ //
+ sql_type const& st (parse_sql_type (column_type (), m));
+
+ switch (st.type)
+ {
+ case sql_type::DECIMAL:
+ {
+ os << ", " << st.prec << ", " << st.scale;
+ break;
+ }
+ case sql_type::FLOAT:
+ {
+ os << ", " << st.prec;
+ break;
+ }
+ case sql_type::CHAR:
+ case sql_type::VARCHAR:
+ {
+ os << ", " << st.prec;
+ break;
+ }
+ case sql_type::TEXT:
+ {
+ os << ", 0"; // Unlimited.
+ break;
+ }
+ case sql_type::NCHAR:
+ case sql_type::NVARCHAR:
+ {
+ os << ", " << st.prec; // In 2-byte characters.
+ break;
+ }
+ case sql_type::NTEXT:
+ {
+ os << ", 0"; // Unlimited.
+ break;
+ }
+ case sql_type::BINARY:
+ case sql_type::VARBINARY:
+ {
+ os << ", " << st.prec;
+ break;
+ }
+ case sql_type::IMAGE:
+ {
+ os << ", 0"; // Unlimited.
+ break;
+ }
+ // Date-time types.
+ //
+ case sql_type::TIME:
+ case sql_type::DATETIME2:
+ case sql_type::DATETIMEOFFSET:
+ {
+ os << ", 0, " << st.scale; // Fractional seconds (scale).
+ break;
+ }
+ case sql_type::DATETIME:
+ {
+ os << ", 0, 3";
+ break;
+ }
+ case sql_type::SMALLDATETIME:
+ {
+ os << ", 0, 8";
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ private:
+ member_database_type_id member_database_type_id_;
+ };
+ entry<query_columns> query_columns_;
+ }
+}
diff --git a/odb/odb/relational/mssql/common.hxx b/odb/odb/relational/mssql/common.hxx
new file mode 100644
index 0000000..42ea412
--- /dev/null
+++ b/odb/odb/relational/mssql/common.hxx
@@ -0,0 +1,293 @@
+// file : odb/relational/mssql/common.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_MSSQL_COMMON_HXX
+#define ODB_RELATIONAL_MSSQL_COMMON_HXX
+
+#include <odb/relational/common.hxx>
+#include <odb/relational/mssql/context.hxx>
+
+namespace relational
+{
+ namespace mssql
+ {
+ struct member_base: virtual relational::member_base_impl<sql_type>, context
+ {
+ member_base (base const& x): base (x), base_impl (x) {}
+
+ // This c-tor is for the direct use inside the mssql namespace.
+ // If you do use this c-tor, you should also explicitly call
+ // relational::member_base (aka base).
+ //
+ member_base () {}
+
+ virtual sql_type const&
+ member_sql_type (semantics::data_member&);
+
+ virtual void
+ traverse_simple (member_info&);
+
+ virtual void
+ traverse_integer (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_decimal (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_smallmoney (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_money (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_float4 (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_float8 (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_string (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_long_string (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_nstring (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_long_nstring (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_binary (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_long_binary (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_date (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_time (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_datetime (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_datetimeoffset (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_uniqueidentifier (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_rowversion (member_info&)
+ {
+ }
+ };
+
+ struct member_image_type: relational::member_image_type,
+ member_base
+ {
+ member_image_type (base const&);
+ member_image_type ();
+ member_image_type (semantics::type* type,
+ const custom_cxx_type*,
+ string const& fq_type = string (),
+ string const& key_prefix = string ());
+ virtual string
+ image_type (semantics::data_member&);
+
+ virtual void
+ traverse_composite (member_info&);
+
+ virtual void
+ traverse_integer (member_info&);
+
+ virtual void
+ traverse_decimal (member_info&);
+
+ virtual void
+ traverse_smallmoney (member_info&);
+
+ virtual void
+ traverse_money (member_info&);
+
+ virtual void
+ traverse_float4 (member_info&);
+
+ virtual void
+ traverse_float8 (member_info&);
+
+ virtual void
+ traverse_string (member_info&);
+
+ virtual void
+ traverse_long_string (member_info&);
+
+ virtual void
+ traverse_nstring (member_info&);
+
+ virtual void
+ traverse_long_nstring (member_info&);
+
+ virtual void
+ traverse_binary (member_info&);
+
+ virtual void
+ traverse_long_binary (member_info&);
+
+ virtual void
+ traverse_date (member_info&);
+
+ virtual void
+ traverse_time (member_info&);
+
+ virtual void
+ traverse_datetime (member_info&);
+
+ virtual void
+ traverse_datetimeoffset (member_info&);
+
+ virtual void
+ traverse_uniqueidentifier (member_info&);
+
+ virtual void
+ traverse_rowversion (member_info&);
+
+ private:
+ string type_;
+ };
+
+ struct member_database_type_id: relational::member_database_type_id,
+ member_base
+ {
+ member_database_type_id (base const&);
+ member_database_type_id ();
+ member_database_type_id (semantics::type* type,
+ const custom_cxx_type*,
+ string const& fq_type = string (),
+ string const& key_prefix = string ());
+
+ virtual string
+ database_type_id (semantics::data_member&);
+
+ virtual void
+ traverse_composite (member_info&);
+
+ virtual void
+ traverse_integer (member_info&);
+
+ virtual void
+ traverse_decimal (member_info&);
+
+ virtual void
+ traverse_smallmoney (member_info&);
+
+ virtual void
+ traverse_money (member_info&);
+
+ virtual void
+ traverse_float4 (member_info&);
+
+ virtual void
+ traverse_float8 (member_info&);
+
+ virtual void
+ traverse_string (member_info&);
+
+ virtual void
+ traverse_long_string (member_info&);
+
+ virtual void
+ traverse_nstring (member_info&);
+
+ virtual void
+ traverse_long_nstring (member_info&);
+
+ virtual void
+ traverse_binary (member_info&);
+
+ virtual void
+ traverse_long_binary (member_info&);
+
+ virtual void
+ traverse_date (member_info&);
+
+ virtual void
+ traverse_time (member_info&);
+
+ virtual void
+ traverse_datetime (member_info&);
+
+ virtual void
+ traverse_datetimeoffset (member_info&);
+
+ virtual void
+ traverse_uniqueidentifier (member_info&);
+
+ virtual void
+ traverse_rowversion (member_info&);
+
+ private:
+ string type_id_;
+ };
+
+ struct has_long_data: object_columns_base, context
+ {
+ has_long_data (bool& r): r_ (r) {}
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ if (!inverse (m, key_prefix_))
+ object_columns_base::traverse_pointer (m, c);
+ }
+
+ virtual bool
+ traverse_column (semantics::data_member& m, string const&, bool)
+ {
+ if (long_data (parse_sql_type (column_type (), m)))
+ r_ = true;
+
+ return true;
+ }
+
+ private:
+ bool& r_;
+ };
+ }
+}
+#endif // ODB_RELATIONAL_MSSQL_COMMON_HXX
diff --git a/odb/odb/relational/mssql/context.cxx b/odb/odb/relational/mssql/context.cxx
new file mode 100644
index 0000000..afe1aa5
--- /dev/null
+++ b/odb/odb/relational/mssql/context.cxx
@@ -0,0 +1,766 @@
+// file : odb/relational/mssql/context.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cassert>
+#include <sstream>
+
+#include <odb/sql-token.hxx>
+#include <odb/sql-lexer.hxx>
+
+#include <odb/relational/mssql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace mssql
+ {
+ namespace
+ {
+ struct type_map_entry
+ {
+ char const* const cxx_type;
+ char const* const db_type;
+ char const* const db_id_type;
+ bool const null;
+ };
+
+ type_map_entry type_map[] =
+ {
+ {"bool", "BIT", 0, false},
+
+ {"char", "CHAR(1)", 0, false},
+ {"wchar_t", "NCHAR(1)", 0, false},
+ {"signed char", "TINYINT", 0, false},
+ {"unsigned char", "TINYINT", 0, false},
+
+ {"short int", "SMALLINT", 0, false},
+ {"short unsigned int", "SMALLINT", 0, false},
+
+ {"int", "INT", 0, false},
+ {"unsigned int", "INT", 0, false},
+
+ {"long int", "BIGINT", 0, false},
+ {"long unsigned int", "BIGINT", 0, false},
+
+ {"long long int", "BIGINT", 0, false},
+ {"long long unsigned int", "BIGINT", 0, false},
+
+ {"float", "REAL", 0, false},
+ {"double", "FLOAT", 0, false},
+
+ {"::std::string", "VARCHAR(512)", "VARCHAR(256)", false},
+ {"::std::wstring", "NVARCHAR(512)", "NVARCHAR(256)", false},
+
+ {"::size_t", "BIGINT", 0, false},
+ {"::std::size_t", "BIGINT", 0, false},
+
+ // Windows GUID/UUID (typedef struct _GUID {...} GUID, UUID;).
+ //
+ {"::_GUID", "UNIQUEIDENTIFIER", 0, false}
+ };
+ }
+
+ context* context::current_;
+
+ context::
+ ~context ()
+ {
+ if (current_ == this)
+ current_ = 0;
+ }
+
+ context::
+ context (ostream& os,
+ semantics::unit& u,
+ options_type const& ops,
+ features_type& f,
+ sema_rel::model* m)
+ : root_context (os, u, ops, f, data_ptr (new (shared) data (os))),
+ base_context (static_cast<data*> (root_context::data_.get ()), m),
+ data_ (static_cast<data*> (base_context::data_))
+ {
+ assert (current_ == 0);
+ current_ = this;
+
+ generate_grow = false;
+ need_alias_as = true;
+ insert_send_auto_id = false;
+ delay_freeing_statement_result = true;
+ need_image_clone = true;
+ generate_bulk = true;
+ global_index = false;
+ global_fkey = true;
+ data_->bind_vector_ = "mssql::bind*";
+
+ // Populate the C++ type to DB type map.
+ //
+ for (size_t i (0); i < sizeof (type_map) / sizeof (type_map_entry); ++i)
+ {
+ type_map_entry const& e (type_map[i]);
+
+ type_map_type::value_type v (
+ e.cxx_type,
+ db_type_type (
+ e.db_type, e.db_id_type ? e.db_id_type : e.db_type, e.null));
+
+ data_->type_map_.insert (v);
+ }
+ }
+
+ context::
+ context ()
+ : data_ (current ().data_)
+ {
+ }
+
+ string const& context::
+ convert_expr (string const& sqlt, semantics::data_member& m, bool to)
+ {
+ sql_type const& t (parse_sql_type (sqlt, m));
+ return to ? t.to : t.from;
+ }
+
+ string context::
+ quote_id_impl (qname const& id) const
+ {
+ string r;
+
+ bool f (true);
+ for (qname::iterator i (id.begin ()); i < id.end (); ++i)
+ {
+ if (i->empty ())
+ continue;
+
+ // Warn if the name is greater than the 128 limit.
+ //
+ if (i->size () > 128)
+ {
+ cerr << "warning: SQL name '" << *i << "' is longer than the "
+ << "SQL Server name limit of 128 characters and will be "
+ << "truncated" << endl;
+
+ cerr << "info: consider shortening it using #pragma db "
+ << "table/column/index or --*-regex options" << endl;
+ }
+
+ if (f)
+ f = false;
+ else
+ r += '.';
+
+ r += '[';
+ r.append (*i, 0, 128); // Max identifier length is 128.
+ r += ']';
+ }
+
+ return r;
+ }
+
+ string context::
+ database_type_impl (semantics::type& t,
+ semantics::names* hint,
+ bool id,
+ bool* null)
+ {
+ string r (base_context::database_type_impl (t, hint, id, null));
+
+ if (!r.empty ())
+ return r;
+
+ using semantics::array;
+
+ // char[N] mapping.
+ //
+ if (array* a = dynamic_cast<array*> (&t))
+ {
+ semantics::type& bt (a->base_type ());
+ bool c (bt.is_a<semantics::fund_char> ());
+
+ if (c || bt.is_a<semantics::fund_wchar> ())
+ {
+ unsigned long long n (a->size ());
+
+ if (n == 0)
+ return r;
+ if (n == 1)
+ r = c ? "CHAR(" : "NCHAR(";
+ else
+ {
+ r = c ? "VARCHAR(" : "NVARCHAR(";
+ n--;
+ }
+
+ if (n > (c ? 8000 : 4000))
+ r += "max)";
+ else
+ {
+ ostringstream ostr;
+ ostr << n;
+ r += ostr.str ();
+ r += ')';
+ }
+ }
+ }
+
+ return r;
+ }
+
+ bool context::
+ long_data (sql_type const& st)
+ {
+ bool r (false);
+
+ // The same test as in common.cxx:traverse_simple().
+ //
+ switch (st.type)
+ {
+ case sql_type::CHAR:
+ case sql_type::VARCHAR:
+ case sql_type::BINARY:
+ case sql_type::VARBINARY:
+ {
+ // Zero precision means max in VARCHAR(max).
+ //
+ if (st.prec == 0 || st.prec > options.mssql_short_limit ())
+ r = true;
+
+ break;
+ }
+ case sql_type::NCHAR:
+ case sql_type::NVARCHAR:
+ {
+ // Zero precision means max in NVARCHAR(max). Note that
+ // the precision is in 2-byte UCS-2 characters, not bytes.
+ //
+ if (st.prec == 0 || st.prec * 2 > options.mssql_short_limit ())
+ r = true;
+
+ break;
+ }
+ case sql_type::TEXT:
+ case sql_type::NTEXT:
+ case sql_type::IMAGE:
+ {
+ r = true;
+ break;
+ }
+ default:
+ break;
+ }
+
+ return r;
+ }
+
+ //
+ // SQL type parsing.
+ //
+
+ namespace
+ {
+ struct sql_parser
+ {
+ typedef context::invalid_sql_type invalid_sql_type;
+
+ sql_parser (custom_db_types const* ct): ct_ (ct) {}
+
+ sql_type
+ parse (std::string sql)
+ {
+ r_ = sql_type ();
+ m_.clear ();
+
+ // First run the type through the custom mapping, if requested.
+ //
+ if (ct_ != 0)
+ {
+ for (custom_db_types::const_iterator i (ct_->begin ());
+ i != ct_->end (); ++i)
+ {
+ custom_db_type const& t (*i);
+
+ if (t.type.match (sql))
+ {
+ r_.to = t.type.replace (sql, t.to);
+ r_.from = t.type.replace (sql, t.from);
+ sql = t.type.replace (sql, t.as);
+ break;
+ }
+ }
+ }
+
+ l_.lex (sql);
+
+ bool ok (true);
+
+ try
+ {
+ ok = parse_name ();
+ }
+ catch (sql_lexer::invalid_input const& e)
+ {
+ ok = false;
+ m_ = "invalid SQL Server type declaration: " + e.message;
+ }
+
+ if (!ok)
+ {
+ if (ct_ == 0)
+ return sql_type ();
+ else
+ throw invalid_sql_type (m_);
+ }
+
+ return r_;
+ }
+
+ bool
+ parse_name ()
+ {
+ sql_token t (l_.next ());
+
+ if (t.type () != sql_token::t_identifier)
+ {
+ m_ = "expected SQL Server type name instead of '" +
+ t.string () + "'";
+ return false;
+ }
+
+ string id (upcase (t.identifier ()));
+
+ if (id == "BIT")
+ {
+ r_.type = sql_type::BIT;
+ }
+ else if (id == "TINYINT")
+ {
+ r_.type = sql_type::TINYINT;
+ }
+ else if (id == "SMALLINT")
+ {
+ r_.type = sql_type::SMALLINT;
+ }
+ else if (id == "INT" ||
+ id == "INTEGER")
+ {
+ r_.type = sql_type::INT;
+ }
+ else if (id == "BIGINT")
+ {
+ r_.type = sql_type::BIGINT;
+ }
+ else if (id == "DECIMAL" ||
+ id == "NUMERIC" ||
+ id == "DEC")
+ {
+ r_.type = sql_type::DECIMAL;
+
+ r_.has_prec = true;
+ r_.prec = 18;
+
+ r_.has_scale = true;
+ r_.scale = 0;
+
+ if (!parse_precision (l_.next ()))
+ return false;
+ }
+ else if (id == "SMALLMONEY")
+ {
+ r_.type = sql_type::SMALLMONEY;
+ }
+ else if (id == "MONEY")
+ {
+ r_.type = sql_type::MONEY;
+ }
+ else if (id == "REAL")
+ {
+ r_.type = sql_type::FLOAT;
+
+ r_.has_prec = true;
+ r_.prec = 24;
+ }
+ else if (id == "FLOAT")
+ {
+ r_.type = sql_type::FLOAT;
+
+ r_.has_prec = true;
+ r_.prec = 53;
+
+ if (!parse_precision (l_.next ()))
+ return false;
+ }
+ else if (id == "DOUBLE")
+ {
+ t = l_.next ();
+
+ if (t.type () != sql_token::t_identifier ||
+ upcase (t.identifier ()) != "PRECISION")
+ {
+ m_ = "expected 'PRECISION' instead of '" + t.string () + "'";
+ return false;
+ }
+
+ r_.type = sql_type::FLOAT;
+
+ r_.has_prec = true;
+ r_.prec = 53;
+
+ // It appears that DOUBLE PRECISION can be followed by the
+ // precision specification.
+ //
+ if (!parse_precision (l_.next ()))
+ return false;
+ }
+ else if (id == "CHAR" ||
+ id == "CHARACTER")
+ {
+ if (!parse_char_trailer (false))
+ return false;
+ }
+ else if (id == "VARCHAR")
+ {
+ r_.type = sql_type::VARCHAR;
+
+ r_.has_prec = true;
+ r_.prec = 1;
+
+ if (!parse_precision (l_.next ()))
+ return false;
+ }
+ else if (id == "TEXT")
+ {
+ r_.type = sql_type::TEXT;
+ r_.has_prec = true;
+ r_.prec = 0;
+ }
+ else if (id == "NCHAR")
+ {
+ r_.type = sql_type::NCHAR;
+
+ r_.has_prec = true;
+ r_.prec = 1;
+
+ if (!parse_precision (l_.next ()))
+ return false;
+ }
+ else if (id == "NVARCHAR")
+ {
+ r_.type = sql_type::NVARCHAR;
+
+ r_.has_prec = true;
+ r_.prec = 1;
+
+ if (!parse_precision (l_.next ()))
+ return false;
+ }
+ else if (id == "NTEXT")
+ {
+ r_.type = sql_type::NTEXT;
+ r_.has_prec = true;
+ r_.prec = 0;
+ }
+ else if (id == "NATIONAL")
+ {
+ t = l_.next ();
+
+ if (t.type () == sql_token::t_identifier)
+ id = upcase (t.identifier ());
+
+ if (id == "TEXT")
+ {
+ r_.type = sql_type::NTEXT;
+ r_.has_prec = true;
+ r_.prec = 0;
+ }
+ else if (id == "CHAR" ||
+ id == "CHARACTER")
+ {
+ if (!parse_char_trailer (true))
+ return false;
+ }
+ else
+ {
+ m_ = "expected 'CHAR', 'CHARACTER', or 'TEXT' instead of '"
+ + t.string () + "'";
+ return false;
+ }
+ }
+ else if (id == "BINARY")
+ {
+ // Can be just BINARY or BINARY VARYING.
+ //
+ t = l_.next ();
+
+ if (t.type () == sql_token::t_identifier)
+ id = upcase (t.identifier ());
+
+ if (id == "VARYING")
+ {
+ r_.type = sql_type::VARBINARY;
+ t = l_.next ();
+ }
+ else
+ r_.type = sql_type::BINARY;
+
+ r_.has_prec = true;
+ r_.prec = 1;
+
+ if (!parse_precision (t))
+ return false;
+ }
+ else if (id == "VARBINARY")
+ {
+ r_.type = sql_type::VARBINARY;
+
+ r_.has_prec = true;
+ r_.prec = 1;
+
+ if (!parse_precision (l_.next ()))
+ return false;
+ }
+ else if (id == "IMAGE")
+ {
+ r_.type = sql_type::IMAGE;
+ r_.has_prec = true;
+ r_.prec = 0;
+ }
+ else if (id == "DATE")
+ {
+ r_.type = sql_type::DATE;
+ }
+ else if (id == "TIME")
+ {
+ r_.type = sql_type::TIME;
+
+ r_.has_scale = true;
+ r_.scale = 7;
+
+ if (!parse_precision (l_.next ()))
+ return false;
+ }
+ else if (id == "DATETIME")
+ {
+ r_.type = sql_type::DATETIME;
+ }
+ else if (id == "DATETIME2")
+ {
+ r_.type = sql_type::DATETIME2;
+
+ r_.has_scale = true;
+ r_.scale = 7;
+
+ if (!parse_precision (l_.next ()))
+ return false;
+ }
+ else if (id == "SMALLDATETIME")
+ {
+ r_.type = sql_type::SMALLDATETIME;
+ }
+ else if (id == "DATETIMEOFFSET")
+ {
+ r_.type = sql_type::DATETIMEOFFSET;
+
+ r_.has_scale = true;
+ r_.scale = 7;
+
+ if (!parse_precision (l_.next ()))
+ return false;
+ }
+ else if (id == "UNIQUEIDENTIFIER")
+ {
+ r_.type = sql_type::UNIQUEIDENTIFIER;
+ }
+ else if (id == "ROWVERSION" ||
+ id == "TIMESTAMP")
+ {
+ r_.type = sql_type::ROWVERSION;
+ }
+ else
+ {
+ m_ = "unexpected SQL Server type name '" + t.identifier () + "'";
+ return false;
+ }
+
+ return true;
+ }
+
+ bool
+ parse_precision (sql_token t)
+ {
+ if (t.punctuation () == sql_token::p_lparen)
+ {
+ // Parse the precision.
+ //
+ t = l_.next ();
+
+ if (t.type () == sql_token::t_identifier &&
+ upcase (t.identifier ()) == "MAX")
+ {
+ r_.prec = 0;
+ r_.has_prec = true;
+ }
+ else if (t.type () == sql_token::t_int_lit)
+ {
+ unsigned short v;
+ istringstream is (t.literal ());
+
+ if (!(is >> v && is.eof ()))
+ {
+ m_ = "invalid precision value '" + t.literal () + "' in SQL "
+ "Server type declaration";
+ return false;
+ }
+
+ switch (r_.type)
+ {
+ case sql_type::TIME:
+ case sql_type::DATETIME2:
+ case sql_type::DATETIMEOFFSET:
+ {
+ r_.scale = v;
+ r_.has_scale = true;
+ break;
+ }
+ default:
+ {
+ r_.prec = v;
+ r_.has_prec = true;
+ break;
+ }
+ }
+ }
+ else
+ {
+ m_ = "integer precision expected in SQL Server type declaration";
+ return false;
+ }
+
+ // Parse the scale if present.
+ //
+ t = l_.next ();
+
+ if (t.punctuation () == sql_token::p_comma)
+ {
+ // Scale can only be specified for the DECIMAL type.
+ //
+ if (r_.type != sql_type::DECIMAL)
+ {
+ m_ = "unexpected scale in SQL Server type declaration";
+ return false;
+ }
+
+ t = l_.next ();
+
+ if (t.type () != sql_token::t_int_lit)
+ {
+ m_ = "integer scale expected in SQL Server type declaration";
+ return false;
+ }
+
+ istringstream is (t.literal ());
+
+ if (!(is >> r_.scale && is.eof ()))
+ {
+ m_ = "invalid scale value '" + t.literal () + "' in SQL "
+ "Server type declaration";
+ return false;
+ }
+
+ r_.has_scale = true;
+ t = l_.next ();
+ }
+
+ if (t.punctuation () != sql_token::p_rparen)
+ {
+ m_ = "expected ')' in SQL Server type declaration";
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ bool
+ parse_char_trailer (bool nat)
+ {
+ sql_token t (l_.next ());
+
+ string id;
+
+ if (t.type () == sql_token::t_identifier)
+ id = upcase (t.identifier ());
+
+ if (id == "VARYING")
+ {
+ r_.type = nat ? sql_type::NVARCHAR : sql_type::VARCHAR;
+ t = l_.next ();
+ }
+ else
+ r_.type = nat ? sql_type::NCHAR : sql_type::CHAR;
+
+ r_.has_prec = true;
+ r_.prec = 1;
+
+ return parse_precision (t);
+ }
+
+ private:
+ string
+ upcase (string const& s)
+ {
+ return context::upcase (s);
+ }
+
+ private:
+ custom_db_types const* ct_;
+ sql_lexer l_;
+ sql_type r_;
+ string m_; // Error message.
+ };
+ }
+
+ sql_type const& context::
+ parse_sql_type (string const& t, semantics::data_member& m, bool custom)
+ {
+ // If this proves to be too expensive, we can maintain a cache of
+ // parsed types across contexts.
+ //
+ data::sql_type_cache::iterator i (data_->sql_type_cache_.find (t));
+
+ if (i != data_->sql_type_cache_.end ()
+ && (custom ? i->second.custom_cached : i->second.straight_cached))
+ {
+ return (custom ? i->second.custom : i->second.straight);
+ }
+ else
+ {
+ try
+ {
+ sql_type st (
+ parse_sql_type (
+ t,
+ custom ? &unit.get<custom_db_types> ("custom-db-types") : 0));
+
+ if (custom)
+ return data_->sql_type_cache_[t].cache_custom (st);
+ else
+ return data_->sql_type_cache_[t].cache_straight (st);
+ }
+ catch (invalid_sql_type const& e)
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: " << e.message () << endl;
+
+ throw operation_failed ();
+ }
+ }
+ }
+
+ sql_type context::
+ parse_sql_type (string const& sqlt, custom_db_types const* ct)
+ {
+ sql_parser p (ct);
+ return p.parse (sqlt);
+ }
+ }
+}
diff --git a/odb/odb/relational/mssql/context.hxx b/odb/odb/relational/mssql/context.hxx
new file mode 100644
index 0000000..7701aaa
--- /dev/null
+++ b/odb/odb/relational/mssql/context.hxx
@@ -0,0 +1,194 @@
+// file : odb/relational/mssql/context.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_MSSQL_CONTEXT_HXX
+#define ODB_RELATIONAL_MSSQL_CONTEXT_HXX
+
+#include <map>
+
+#include <odb/relational/context.hxx>
+
+namespace relational
+{
+ namespace mssql
+ {
+ struct sql_type
+ {
+ // Keep the order in each block of types.
+ //
+ enum core_type
+ {
+ // Integral types.
+ //
+ BIT,
+ TINYINT,
+ SMALLINT,
+ INT,
+ BIGINT,
+
+ // Fixed and floating point types.
+ //
+ DECIMAL,
+ SMALLMONEY,
+ MONEY,
+ FLOAT,
+
+ // String and binary types.
+ //
+ CHAR,
+ VARCHAR,
+ TEXT,
+
+ NCHAR,
+ NVARCHAR,
+ NTEXT,
+
+ BINARY,
+ VARBINARY,
+ IMAGE,
+
+ // Date-time types.
+ //
+ DATE,
+ TIME,
+ DATETIME,
+ DATETIME2,
+ SMALLDATETIME,
+ DATETIMEOFFSET,
+
+ // Other types.
+ //
+ UNIQUEIDENTIFIER,
+ ROWVERSION,
+
+ // Invalid type.
+ //
+ invalid
+ };
+
+ sql_type () :
+ type (invalid),
+ has_prec (false), prec (0),
+ has_scale (false), scale (0)
+ {
+ }
+
+ core_type type;
+
+ bool has_prec;
+ unsigned short prec; // Max numeric value is 8000. 0 indicates
+ // 'max' as in VARCHAR(max).
+ bool has_scale;
+ unsigned short scale; // Max value is 38.
+
+ // Conversion expressions for custom database types.
+ //
+ std::string to;
+ std::string from;
+ };
+
+ class context: public virtual relational::context
+ {
+ public:
+ sql_type const&
+ parse_sql_type (string const&,
+ semantics::data_member&,
+ bool custom = true);
+
+ // Return true if this type is long data.
+ //
+ bool
+ long_data (sql_type const&);
+
+ public:
+ struct invalid_sql_type
+ {
+ invalid_sql_type (string const& message): message_ (message) {}
+
+ string const&
+ message () const {return message_;}
+
+ private:
+ string message_;
+ };
+
+ // If custom_db_types is NULL, then this function returns
+ // invalid type instead of throwing in case an unknown type
+ // is encountered.
+ //
+ static sql_type
+ parse_sql_type (string const&, custom_db_types const* = 0);
+
+ protected:
+ virtual string const&
+ convert_expr (string const&, semantics::data_member&, bool);
+
+ virtual string
+ quote_id_impl (qname const&) const;
+
+ protected:
+ virtual string
+ database_type_impl (semantics::type&, semantics::names*, bool, bool*);
+
+ public:
+ virtual
+ ~context ();
+
+ context ();
+ context (std::ostream&,
+ semantics::unit&,
+ options_type const&,
+ features_type&,
+ sema_rel::model*);
+
+ static context&
+ current ()
+ {
+ return *current_;
+ }
+
+ private:
+ static context* current_;
+
+ private:
+ struct data: base_context::data
+ {
+ data (std::ostream& os): base_context::data (os) {}
+
+ struct sql_type_cache_entry
+ {
+ sql_type_cache_entry ()
+ : custom_cached (false), straight_cached (false) {}
+
+ sql_type const&
+ cache_custom (sql_type const& t)
+ {
+ custom = t;
+ custom_cached = true;
+ return custom;
+ }
+
+ sql_type const&
+ cache_straight (sql_type const& t)
+ {
+ straight = t;
+ straight_cached = true;
+ return straight;
+ }
+
+ sql_type custom; // With custom mapping.
+ sql_type straight; // Without custom mapping.
+
+ bool custom_cached;
+ bool straight_cached;
+ };
+
+ typedef std::map<string, sql_type_cache_entry> sql_type_cache;
+ sql_type_cache sql_type_cache_;
+ };
+ data* data_;
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_MSSQL_CONTEXT_HXX
diff --git a/odb/odb/relational/mssql/header.cxx b/odb/odb/relational/mssql/header.cxx
new file mode 100644
index 0000000..ebdc734
--- /dev/null
+++ b/odb/odb/relational/mssql/header.cxx
@@ -0,0 +1,312 @@
+// file : odb/relational/mssql/header.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/header.hxx>
+
+#include <odb/relational/mssql/common.hxx>
+#include <odb/relational/mssql/context.hxx>
+
+namespace relational
+{
+ namespace mssql
+ {
+ namespace header
+ {
+ namespace relational = relational::header;
+
+ struct class1: relational::class1, context
+ {
+ class1 (base const& x): base (x) {}
+
+ virtual void
+ object_public_extra_pre (type& c)
+ {
+ bool abst (abstract (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+
+ if (poly_derived || (abst && !poly))
+ return;
+
+ // Bulk operations batch size.
+ //
+ {
+ unsigned long long b (c.count ("bulk")
+ ? c.get<unsigned long long> ("bulk")
+ : 1);
+
+ os << "static const std::size_t batch = " << b << "UL;"
+ << endl;
+ }
+
+ // rowvesion
+ //
+ bool rv (false);
+ if (semantics::data_member* m = optimistic (c))
+ {
+ sql_type t (parse_sql_type (column_type (*m), *m));
+ rv = (t.type == sql_type::ROWVERSION);
+ }
+
+ os << "static const bool rowversion = " << rv << ";"
+ << endl;
+
+ // Disable bulk update if we have ROWVERSION since we don't
+ // yet support batch extraction of the version.
+ //
+ if (rv && c.count ("bulk-update"))
+ c.remove ("bulk-update");
+ }
+
+ virtual void
+ object_public_extra_post (type& c)
+ {
+ bool abst (abstract (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+
+ if (poly_derived || (abst && !poly))
+ return;
+
+ if (semantics::data_member* m = optimistic (c))
+ {
+ sql_type t (parse_sql_type (column_type (*m), *m));
+ if (t.type == sql_type::ROWVERSION)
+ {
+ os << "static version_type" << endl
+ << "version (const id_image_type&);"
+ << endl;
+ }
+ }
+ }
+ };
+ entry<class1> class1_entry_;
+
+ struct section_traits: relational::section_traits, context
+ {
+ section_traits (base const& x): base (x) {}
+
+ virtual void
+ section_public_extra_pre (user_section&)
+ {
+ if (abstract (c_) && !polymorphic (c_))
+ return;
+
+ // rowvesion
+ //
+ bool rv (false);
+ if (semantics::data_member* m = optimistic (c_))
+ {
+ sql_type t (parse_sql_type (column_type (*m), *m));
+ rv = (t.type == sql_type::ROWVERSION);
+ }
+
+ os << "static const bool rowversion = " << rv << ";"
+ << endl;
+ }
+ };
+ entry<section_traits> section_traits_;
+
+ struct image_type: relational::image_type, context
+ {
+ image_type (base const& x): base (x) {};
+
+ virtual void
+ image_extra (type& c)
+ {
+ if (!(composite (c) || (abstract (c) && !polymorphic (c))))
+ {
+ type* poly_root (polymorphic (c));
+
+ // If this is a polymorphic type, only add callback to the root.
+ //
+ if (poly_root == 0 || poly_root == &c)
+ {
+ bool gc (options.generate_query ());
+
+ if (gc)
+ os << "mssql::change_callback change_callback_;"
+ << endl;
+
+ os << "mssql::change_callback*" << endl
+ << "change_callback ()"
+ << "{";
+
+ if (gc)
+ os << "return &change_callback_;";
+ else
+ os << "return 0;";
+
+ os << "}";
+ }
+ }
+ }
+ };
+ entry<image_type> image_type_;
+
+ struct image_member: relational::image_member_impl<sql_type>,
+ member_base
+ {
+ image_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x) {}
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_decimal (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_smallmoney (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_money (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_float4 (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_float8 (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ // Extra character for the null-terminator that ODBC always adds.
+ //
+ os << "char " << mi.var << "value[" << mi.st->prec + 1 << "];"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_long_string (member_info& mi)
+ {
+ os << "mutable " << image_type << " " << mi.var << "callback;"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_nstring (member_info& mi)
+ {
+ // Extra character for the null-terminator that ODBC always adds.
+ //
+ os << "mssql::ucs2_char " << mi.var << "value[" <<
+ mi.st->prec + 1 << "];"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_long_nstring (member_info& mi)
+ {
+ os << "mutable " << image_type << " " << mi.var << "callback;"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_binary (member_info& mi)
+ {
+ os << "char " << mi.var << "value[" << mi.st->prec << "];"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_long_binary (member_info& mi)
+ {
+ os << "mutable " << image_type << " " << mi.var << "callback;"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_date (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_time (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_datetime (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_datetimeoffset (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_uniqueidentifier (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+
+ virtual void
+ traverse_rowversion (member_info& mi)
+ {
+ os << "unsigned char " << mi.var << "value[8];"
+ << "SQLLEN " << mi.var << "size_ind;"
+ << endl;
+ }
+ };
+ entry<image_member> image_member_;
+ }
+ }
+}
diff --git a/odb/odb/relational/mssql/inline.cxx b/odb/odb/relational/mssql/inline.cxx
new file mode 100644
index 0000000..eb581d6
--- /dev/null
+++ b/odb/odb/relational/mssql/inline.cxx
@@ -0,0 +1,42 @@
+// file : odb/relational/mssql/inline.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/inline.hxx>
+
+#include <odb/relational/mssql/common.hxx>
+#include <odb/relational/mssql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace mssql
+ {
+ namespace inline_
+ {
+ namespace relational = relational::inline_;
+
+ struct null_member: relational::null_member_impl<sql_type>,
+ member_base
+ {
+ null_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ traverse_simple (member_info& mi)
+ {
+ if (get_)
+ os << "r = r && i." << mi.var << "size_ind == SQL_NULL_DATA;";
+ else
+ os << "i." << mi.var << "size_ind = SQL_NULL_DATA;";
+ }
+ };
+ entry<null_member> null_member_;
+ }
+ }
+}
diff --git a/odb/odb/relational/mssql/model.cxx b/odb/odb/relational/mssql/model.cxx
new file mode 100644
index 0000000..0f5a85c
--- /dev/null
+++ b/odb/odb/relational/mssql/model.cxx
@@ -0,0 +1,66 @@
+// file : odb/relational/mssql/model.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <odb/relational/model.hxx>
+
+#include <odb/relational/mssql/common.hxx>
+#include <odb/relational/mssql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace mssql
+ {
+ namespace model
+ {
+ namespace relational = relational::model;
+
+ struct object_columns: relational::object_columns, context
+ {
+ object_columns (base const& x): base (x) {}
+
+ virtual string
+ default_enum (semantics::data_member& m, tree en, string const&)
+ {
+ // Make sure the column is mapped to an integer or DECIMAL type.
+ //
+ switch (parse_sql_type (column_type (), m, false).type)
+ {
+ case sql_type::BIT:
+ case sql_type::TINYINT:
+ case sql_type::SMALLINT:
+ case sql_type::INT:
+ case sql_type::BIGINT:
+ case sql_type::DECIMAL:
+ break;
+ default:
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: column with default value specified as C++ "
+ << "enumerator must map to SQL Server integer type" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ using semantics::enumerator;
+
+ enumerator& e (dynamic_cast<enumerator&> (*unit.find (en)));
+
+ ostringstream ostr;
+
+ if (e.enum_ ().unsigned_ ())
+ ostr << e.value ();
+ else
+ ostr << static_cast<long long> (e.value ());
+
+ return ostr.str ();
+ }
+ };
+ entry<object_columns> object_columns_;
+ }
+ }
+}
diff --git a/odb/odb/relational/mssql/schema.cxx b/odb/odb/relational/mssql/schema.cxx
new file mode 100644
index 0000000..c5f6bc1
--- /dev/null
+++ b/odb/odb/relational/mssql/schema.cxx
@@ -0,0 +1,651 @@
+// file : odb/relational/mssql/schema.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/schema.hxx>
+
+#include <odb/relational/mssql/common.hxx>
+#include <odb/relational/mssql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace mssql
+ {
+ namespace schema
+ {
+ namespace relational = relational::schema;
+ using relational::table_set;
+
+ struct sql_emitter: relational::sql_emitter
+ {
+ sql_emitter (const base& x): base (x) {}
+
+ virtual void
+ post ()
+ {
+ if (!first_) // Ignore empty statements.
+ {
+ os << ';' << endl
+ << "GO" << endl
+ << endl;
+ }
+ }
+ };
+ entry<sql_emitter> sql_emitter_;
+
+ //
+ // File.
+ //
+
+ struct sql_file: relational::sql_file, context
+ {
+ sql_file (const base& x): base (x) {}
+
+ virtual void
+ prologue ()
+ {
+ // Suppress the (x rows affected) messages from sqlcmd for DML
+ // statements. We only use DML for schema version management.
+ //
+ if ((model == 0 || model->version () != 0) &&
+ !options.suppress_schema_version ())
+ os << "SET NOCOUNT ON;" << endl
+ << endl;
+ }
+ };
+ entry<sql_file> sql_file_;
+
+ //
+ // Drop.
+ //
+
+ struct drop_column: relational::drop_column, context
+ {
+ drop_column (base const& x): base (x) {}
+
+ virtual void
+ traverse (sema_rel::drop_column& dc)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << "," << endl
+ << " ";
+
+ os << quote_id (dc.name ());
+ }
+ };
+ entry<drop_column> drop_column_;
+
+ struct drop_foreign_key: relational::drop_foreign_key, context
+ {
+ drop_foreign_key (base const& x): base (x) {}
+
+ virtual void
+ drop (sema_rel::table& t, sema_rel::foreign_key& fk)
+ {
+ bool migration (dropped_ == 0);
+
+ if (migration)
+ {
+ if (fk.not_deferrable ())
+ pre_statement ();
+ else
+ {
+ if (format_ != schema_format::sql)
+ return;
+
+ os << "/*" << endl;
+ }
+ }
+ else
+ {
+ // Here we drop potentially deferrable keys and also need to
+ // test if the key exists.
+ //
+ pre_statement ();
+
+ os << "IF OBJECT_ID(" << quote_string (fk.name ()) << ", " <<
+ quote_string ("F") << ") IS NOT NULL" << endl
+ << " ";
+ }
+
+ os << "ALTER TABLE " << quote_id (t.name ()) << endl
+ << (migration ? " " : " ") << "DROP CONSTRAINT " <<
+ quote_id (fk.name ()) << endl;
+
+
+ if (!migration || fk.not_deferrable ())
+ post_statement ();
+ else
+ os << "*/" << endl
+ << endl;
+ }
+
+ virtual void
+ traverse (sema_rel::drop_foreign_key& dfk)
+ {
+ // Find the foreign key we are dropping in the base model.
+ //
+ sema_rel::foreign_key& fk (find<sema_rel::foreign_key> (dfk));
+
+ bool c (!fk.not_deferrable () && !in_comment);
+
+ if (c && format_ != schema_format::sql)
+ return;
+
+ if (!first_)
+ os << (c ? "" : ",") << endl
+ << " ";
+
+ if (c)
+ os << "/* ";
+
+ os << quote_id (fk.name ());
+
+ if (c)
+ os << " */";
+
+ if (first_)
+ {
+ if (c)
+ // There has to be a real name otherwise the whole statement
+ // would have been commented out.
+ //
+ os << endl
+ << " ";
+ else
+ first_ = false;
+ }
+ }
+ };
+ entry<drop_foreign_key> drop_foreign_key_;
+
+ struct drop_index: relational::drop_index, context
+ {
+ drop_index (base const& x): base (x) {}
+
+ virtual void
+ drop (sema_rel::index& in)
+ {
+ sema_rel::table& t (static_cast<sema_rel::table&> (in.scope ()));
+
+ os << "DROP INDEX " << name (in) << " ON " <<
+ quote_id (t.name ()) << endl;
+ }
+ };
+ entry<drop_index> drop_index_;
+
+ struct drop_table: relational::drop_table, context
+ {
+ drop_table (base const& x): base (x) {}
+
+ virtual void
+ drop (sema_rel::table& t, bool migration)
+ {
+ // SQL Server has no IF EXISTS conditional for dropping tables.
+ // The following approach appears to be the recommended way to
+ // drop a table if it exists.
+ //
+ sema_rel::qname const& name (t.name ());
+
+ pre_statement ();
+
+ if (!migration)
+ os << "IF OBJECT_ID(" << quote_string (name.string ()) <<
+ ", " << quote_string ("U") << ") IS NOT NULL" << endl
+ << " ";
+
+ os << "DROP TABLE " << quote_id (name) << endl;
+
+ post_statement ();
+ }
+ };
+ entry<drop_table> drop_table_;
+
+ //
+ // Create.
+ //
+
+ struct create_column: relational::create_column, context
+ {
+ create_column (base const& x): base (x) {}
+
+ virtual void
+ traverse (sema_rel::add_column& ac)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << "," << endl
+ << " ";
+
+ create (ac);
+ }
+
+ virtual void
+ auto_ (sema_rel::primary_key&)
+ {
+ os << " IDENTITY";
+ }
+ };
+ entry<create_column> create_column_;
+
+ struct create_foreign_key: relational::create_foreign_key, context
+ {
+ create_foreign_key (base const& x): base (x) {}
+
+ void
+ diagnose (sema_rel::foreign_key& fk)
+ {
+ if (fk.on_delete () != sema_rel::foreign_key::no_action)
+ {
+ cerr << "warning: foreign key '" << fk.name () << "' has " <<
+ "ON DELETE clause but is disabled in SQL Server due to lack "
+ "of deferrable constraint support" << endl;
+
+ cerr << "info: consider using non-deferrable foreign keys (" <<
+ "--fkeys-deferrable-mode)" << endl;
+ }
+ }
+
+ virtual void
+ traverse_create (sema_rel::foreign_key& fk)
+ {
+ // SQL Server does not support deferrable constraint checking.
+ // Output such foreign keys as comments, for documentation,
+ // unless we are generating embedded schema.
+ //
+ if (fk.not_deferrable ())
+ base::traverse_create (fk);
+ else
+ {
+ diagnose (fk);
+
+ // Don't bloat C++ code with comment strings if we are
+ // generating embedded schema.
+ //
+ if (format_ != schema_format::sql)
+ return;
+
+ os << endl
+ << " /*" << endl
+ << " CONSTRAINT ";
+ create (fk);
+ os << endl
+ << " */";
+ }
+ }
+
+ virtual void
+ traverse_add (sema_rel::foreign_key& fk)
+ {
+ bool c (!fk.not_deferrable () && !in_comment);
+
+ if (c)
+ diagnose (fk);
+
+ if (c && format_ != schema_format::sql)
+ return;
+
+ if (!first_)
+ os << (c ? "" : ",") << endl
+ << " ";
+
+ if (c)
+ os << "/*" << endl
+ << " ";
+
+ os << "CONSTRAINT ";
+ create (fk);
+
+ if (c)
+ os << endl
+ << " */";
+
+ if (first_)
+ {
+ if (c)
+ // There has to be a real key otherwise the whole statement
+ // would have been commented out.
+ //
+ os << endl
+ << " ";
+ else
+ first_ = false;
+ }
+ }
+
+ virtual void
+ deferrable (sema_rel::deferrable)
+ {
+ // This will still be called to output the comment.
+ }
+ };
+ entry<create_foreign_key> create_foreign_key_;
+
+ struct create_table: relational::create_table, context
+ {
+ create_table (base const& x): base (x) {}
+
+ // See if there are any undefined foreign keys that are not
+ // deferrable.
+ //
+ bool
+ check_undefined_fk_deferrable_only (sema_rel::table& t)
+ {
+ for (sema_rel::table::names_iterator i (t.names_begin ());
+ i != t.names_end (); ++i)
+ {
+ using sema_rel::foreign_key;
+
+ if (foreign_key* fk = dynamic_cast<foreign_key*> (&i->nameable ()))
+ {
+ if (!fk->count ("mssql-fk-defined") &&
+ fk->not_deferrable ())
+ return false;
+ }
+ }
+ return true;
+ }
+
+ virtual void
+ traverse (sema_rel::table& t)
+ {
+ if (pass_ == 1)
+ base::traverse (t);
+ else
+ {
+ // Add undefined foreign keys.
+ //
+ if (check_undefined_fk (t))
+ {
+ bool deferrable (check_undefined_fk_deferrable_only (t));
+
+ if (!deferrable || format_ == schema_format::sql)
+ {
+ if (deferrable)
+ {
+ os << "/*" << endl;
+ in_comment = true;
+ }
+ else
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (t.name ()) << endl
+ << " ADD ";
+
+ instance<create_foreign_key> cfk (*this);
+ trav_rel::unames n (*cfk);
+ names (t, n);
+ os << endl;
+
+ if (deferrable)
+ {
+ in_comment = false;
+ os << "*/" << endl
+ << endl;
+ }
+ else
+ post_statement ();
+ }
+ }
+ }
+ }
+ };
+ entry<create_table> create_table_;
+
+ //
+ // Alter.
+ //
+
+ struct alter_column: relational::alter_column, context
+ {
+ alter_column (base const& x): base (x) {}
+
+ virtual void
+ traverse (sema_rel::column& c)
+ {
+ // Relax (NULL) in pre and tighten (NOT NULL) in post.
+ //
+ if (pre_ != c.null ())
+ return;
+
+ using sema_rel::table;
+ table& at (static_cast<table&> (c.scope ()));
+
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (at.name ()) << endl
+ << " ALTER COLUMN ";
+ alter (c);
+ os << endl;
+
+ post_statement ();
+ }
+ };
+ entry<alter_column> alter_column_;
+
+ struct alter_table_pre: relational::alter_table_pre, context
+ {
+ alter_table_pre (base const& x): base (x) {}
+
+ // Check if we are only dropping deferrable foreign keys.
+ //
+ bool
+ check_drop_deferrable_only (sema_rel::alter_table& at)
+ {
+ for (sema_rel::alter_table::names_iterator i (at.names_begin ());
+ i != at.names_end (); ++i)
+ {
+ using sema_rel::foreign_key;
+ using sema_rel::drop_foreign_key;
+
+ if (drop_foreign_key* dfk =
+ dynamic_cast<drop_foreign_key*> (&i->nameable ()))
+ {
+ foreign_key& fk (find<foreign_key> (*dfk));
+
+ if (fk.not_deferrable ())
+ return false;
+ }
+ }
+ return true;
+ }
+
+ virtual void
+ alter (sema_rel::alter_table& at)
+ {
+ // SQL Server can only alter one kind of thing at a time.
+ //
+ if (check<sema_rel::drop_foreign_key> (at))
+ {
+ bool deferrable (check_drop_deferrable_only (at));
+
+ if (!deferrable || format_ == schema_format::sql)
+ {
+ if (deferrable)
+ {
+ os << "/*" << endl;
+ in_comment = true;
+ }
+ else
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (at.name ()) << endl
+ << " DROP CONSTRAINT ";
+
+ instance<drop_foreign_key> dfc (*this);
+ trav_rel::unames n (*dfc);
+ names (at, n);
+ os << endl;
+
+ if (deferrable)
+ {
+ in_comment = false;
+ os << "*/" << endl
+ << endl;
+ }
+ else
+ post_statement ();
+ }
+ }
+
+ if (check<sema_rel::add_column> (at))
+ {
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (at.name ()) << endl
+ << " ADD ";
+
+ instance<create_column> cc (*this);
+ trav_rel::unames n (*cc);
+ names (at, n);
+ os << endl;
+
+ post_statement ();
+ }
+
+ // For ALTER COLUMN, SQL Server can only have one per ALTER TABLE.
+ //
+ {
+ bool tl (true); // (Im)perfect forwarding.
+ instance<alter_column> ac (*this, tl);
+ trav_rel::unames n (*ac);
+ names (at, n);
+ }
+ }
+ };
+ entry<alter_table_pre> alter_table_pre_;
+
+ struct alter_table_post: relational::alter_table_post, context
+ {
+ alter_table_post (base const& x): base (x) {}
+
+ // Check if we are only adding deferrable foreign keys.
+ //
+ bool
+ check_add_deferrable_only (sema_rel::alter_table& at)
+ {
+ for (sema_rel::alter_table::names_iterator i (at.names_begin ());
+ i != at.names_end (); ++i)
+ {
+ using sema_rel::add_foreign_key;
+
+ if (add_foreign_key* afk =
+ dynamic_cast<add_foreign_key*> (&i->nameable ()))
+ {
+ if (afk->not_deferrable ())
+ return false;
+ }
+ }
+ return true;
+ }
+
+ virtual void
+ alter (sema_rel::alter_table& at)
+ {
+ // SQL Server can only alter one kind of thing at a time.
+ //
+ if (check<sema_rel::drop_column> (at))
+ {
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (at.name ()) << endl
+ << " DROP COLUMN ";
+
+ instance<drop_column> dc (*this);
+ trav_rel::unames n (*dc);
+ names (at, n);
+ os << endl;
+
+ post_statement ();
+ }
+
+ // For ALTER COLUMN, SQL Server can only have one per ALTER TABLE.
+ //
+ {
+ bool fl (false); // (Im)perfect forwarding.
+ instance<alter_column> ac (*this, fl);
+ trav_rel::unames n (*ac);
+ names (at, n);
+ }
+
+ if (check<sema_rel::add_foreign_key> (at))
+ {
+ bool deferrable (check_add_deferrable_only (at));
+
+ if (!deferrable || format_ == schema_format::sql)
+ {
+ if (deferrable)
+ {
+ os << "/*" << endl;
+ in_comment = true;
+ }
+ else
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (at.name ()) << endl
+ << " ADD ";
+
+ instance<create_foreign_key> cfc (*this);
+ trav_rel::unames n (*cfc);
+ names (at, n);
+ os << endl;
+
+ if (deferrable)
+ {
+ in_comment = false;
+ os << "*/" << endl
+ << endl;
+ }
+ else
+ post_statement ();
+ }
+ }
+ }
+ };
+ entry<alter_table_post> alter_table_post_;
+
+ //
+ // Schema version table.
+ //
+
+ struct version_table: relational::version_table, context
+ {
+ version_table (base const& x): base (x) {}
+
+ virtual void
+ create_table ()
+ {
+ pre_statement ();
+
+ os << "IF OBJECT_ID(" << quote_string (table_.string ()) <<
+ ", " << quote_string ("U") << ") IS NULL" << endl
+ << " CREATE TABLE " << qt_ << " (" << endl
+ << " " << qn_ << " VARCHAR(256) NOT NULL PRIMARY KEY," << endl
+ << " " << qv_ << " BIGINT NOT NULL," << endl
+ << " " << qm_ << " BIT NOT NULL)" << endl;
+
+ post_statement ();
+ }
+
+ virtual void
+ create (sema_rel::version v)
+ {
+ pre_statement ();
+
+ os << "IF NOT EXISTS (SELECT 1 FROM " << qt_ << " WHERE " << qn_ <<
+ " = " << qs_ << ")" << endl
+ << " INSERT INTO " << qt_ << " (" << endl
+ << " " << qn_ << ", " << qv_ << ", " << qm_ << ")" << endl
+ << " VALUES (" << qs_ << ", " << v << ", 0)" << endl;
+
+ post_statement ();
+ }
+ };
+ entry<version_table> version_table_;
+ }
+ }
+}
diff --git a/odb/odb/relational/mssql/source.cxx b/odb/odb/relational/mssql/source.cxx
new file mode 100644
index 0000000..573104d
--- /dev/null
+++ b/odb/odb/relational/mssql/source.cxx
@@ -0,0 +1,1201 @@
+// file : odb/relational/mssql/source.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/source.hxx>
+
+#include <odb/relational/mssql/common.hxx>
+#include <odb/relational/mssql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace mssql
+ {
+ namespace source
+ {
+ namespace relational = relational::source;
+
+ //
+ //
+ struct query_parameters: relational::query_parameters
+ {
+ query_parameters (base const& x): base (x) {}
+
+ virtual string
+ auto_id (semantics::data_member&, const string&, const string&)
+ {
+ return "";
+ }
+ };
+ entry<query_parameters> query_parameters_;
+
+ //
+ //
+ struct object_columns: relational::object_columns, context
+ {
+ object_columns (base const& x)
+ : base (x), rowversion_ (false), column_count_ (0) {}
+
+ virtual bool
+ column (semantics::data_member& m,
+ string const& table,
+ string const& column)
+ {
+ // Don't add a column for auto id in the INSERT statement.
+ // Only simple, direct id can be auto.
+ //
+ if (sk_ == statement_insert && key_prefix_.empty () && auto_ (m))
+ return false;
+
+ // Don't update the ROWVERSION column explicitly.
+ //
+ if (sk_ == statement_update)
+ {
+ sql_type t (parse_sql_type (column_type (), m));
+ if (t.type == sql_type::ROWVERSION)
+ {
+ rowversion_ = true;
+ return false;
+ }
+ }
+
+ bool r (base::column (m, table, column));
+
+ // Count the number of columns in the UPDATE statement, but
+ // excluding soft-deleted.
+ //
+ if (sk_ == statement_update && r && !deleted (member_path_))
+ column_count_++;
+
+ return r;
+ }
+
+ virtual void
+ traverse_post (semantics::nameable& n)
+ {
+ if (rowversion_ && column_count_ == 0)
+ {
+ location l (n.location ());
+ error (l) << "ROWVERSION in an object without any readwrite "
+ "data members" << endl;
+ error (l) << "UPDATE statement will be empty" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ private:
+ bool rowversion_;
+ size_t column_count_;
+ };
+ entry<object_columns> object_columns_;
+
+ //
+ //
+ struct persist_statement_params: relational::persist_statement_params,
+ context
+ {
+ persist_statement_params (base const& x): base (x) {}
+
+ virtual string
+ version_value (semantics::data_member& m)
+ {
+ sql_type t (parse_sql_type (column_type (), m));
+ return t.type == sql_type::ROWVERSION ? "DEFAULT" : "1";
+ }
+ };
+ entry<persist_statement_params> persist_statement_params_;
+
+ //
+ // bind
+ //
+
+ static const char* integer_buffer_types[] =
+ {
+ "mssql::bind::bit",
+ "mssql::bind::tinyint",
+ "mssql::bind::smallint",
+ "mssql::bind::int_",
+ "mssql::bind::bigint"
+ };
+
+ struct bind_member: relational::bind_member_impl<sql_type>,
+ member_base
+ {
+ bind_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << b << ".type = " <<
+ integer_buffer_types[mi.st->type - sql_type::BIT] << ";"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;";
+ }
+
+ virtual void
+ traverse_decimal (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::decimal;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ // Encode precision (p) and scale (s) as (p * 100 + s).
+ //
+ << b << ".capacity = " << mi.st->prec * 100 + mi.st->scale << ";";
+ }
+
+ virtual void
+ traverse_smallmoney (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::smallmoney;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;";
+ }
+
+ virtual void
+ traverse_money (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::money;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;";
+ }
+
+ virtual void
+ traverse_float4 (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::float4;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ << b << ".capacity = " << mi.st->prec << ";";
+ }
+
+ virtual void
+ traverse_float8 (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::float8;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ << b << ".capacity = " << mi.st->prec << ";";
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::string;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ << b << ".capacity = static_cast<SQLLEN> (sizeof (" <<
+ arg << "." << mi.var << "value));";
+ }
+
+ virtual void
+ traverse_long_string (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::long_string;"
+ << b << ".buffer = &" << arg << "." << mi.var << "callback;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ // Encode the column size with 0 indicating unlimited.
+ //
+ << b << ".capacity = " << mi.st->prec << ";";
+ }
+
+ virtual void
+ traverse_nstring (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::nstring;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ << b << ".capacity = static_cast<SQLLEN> (sizeof (" <<
+ arg << "." << mi.var << "value));";
+ }
+
+ virtual void
+ traverse_long_nstring (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::long_nstring;"
+ << b << ".buffer = &" << arg << "." << mi.var << "callback;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ // Encode the column size (in bytes) with 0 indicating unlimited.
+ //
+ << b << ".capacity = " << mi.st->prec * 2 << ";";
+ }
+
+ virtual void
+ traverse_binary (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::binary;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ << b << ".capacity = static_cast<SQLLEN> (sizeof (" <<
+ arg << "." << mi.var << "value));";
+ }
+
+ virtual void
+ traverse_long_binary (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::long_binary;"
+ << b << ".buffer = &" << arg << "." << mi.var << "callback;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ // Encode the column size with 0 indicating unlimited.
+ //
+ << b << ".capacity = " << mi.st->prec << ";";
+ }
+
+ virtual void
+ traverse_date (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::date;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;";
+ }
+
+ virtual void
+ traverse_time (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::time;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ // Encode fractional seconds (scale).
+ //
+ << b << ".capacity = " << mi.st->scale << ";";
+ }
+
+ virtual void
+ traverse_datetime (member_info& mi)
+ {
+ unsigned short scale (0);
+
+ switch (mi.st->type)
+ {
+ case sql_type::DATETIME:
+ {
+ // Looks like it is 3 (rounded to 0.000, 0.003, or 0.007).
+ //
+ scale = 3;
+ break;
+ }
+ case sql_type::DATETIME2:
+ {
+ scale = mi.st->scale;
+ break;
+ }
+ case sql_type::SMALLDATETIME:
+ {
+ // No seconds in SMALLDATATIME. Encode it a special precision
+ // value (8).
+ //
+ scale = 8;
+ break;
+ }
+ default:
+ {
+ assert (false);
+ break;
+ }
+ }
+
+ os << b << ".type = mssql::bind::datetime;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ // Encode fractional seconds (scale).
+ //
+ << b << ".capacity = " << scale << ";";
+ }
+
+ virtual void
+ traverse_datetimeoffset (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::datetimeoffset;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;"
+ // Encode fractional seconds (scale).
+ //
+ << b << ".capacity = " << mi.st->scale << ";";
+ }
+
+ virtual void
+ traverse_uniqueidentifier (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::uniqueidentifier;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;";
+ }
+
+ virtual void
+ traverse_rowversion (member_info& mi)
+ {
+ os << b << ".type = mssql::bind::rowversion;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size_ind = &" << arg << "." << mi.var << "size_ind;";
+ }
+ };
+ entry<bind_member> bind_member_;
+
+ //
+ // init image
+ //
+
+ struct init_image_member: relational::init_image_member_impl<sql_type>,
+ member_base
+ {
+ init_image_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ set_null (member_info& mi)
+ {
+ os << "i." << mi.var << "size_ind = SQL_NULL_DATA;";
+ }
+
+ virtual void
+ check_accessor (member_info& mi, member_access& ma)
+ {
+ // We cannot use accessors that return by-value for long data
+ // members.
+ //
+ if (long_data (*mi.st) && ma.by_value)
+ {
+ error (ma.loc) << "accessor returning a value cannot be used "
+ << "for a data member of SQL Server long data "
+ << "type" << endl;
+ info (ma.loc) << "accessor returning a const reference is required"
+ << endl;
+ info (mi.m.location ()) << "data member is defined here" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 0;";
+ }
+
+ virtual void
+ traverse_decimal (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 0;";
+ }
+
+ virtual void
+ traverse_smallmoney (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 4;";
+ }
+
+ virtual void
+ traverse_money (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 8;";
+ }
+
+ virtual void
+ traverse_float4 (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 0;";
+ }
+
+ virtual void
+ traverse_float8 (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 0;";
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ // Don't mention the extra character for the null-terminator.
+ << "sizeof (i." << mi.var << "value) - 1," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "size_ind =" << endl
+ << " is_null ? SQL_NULL_DATA : static_cast<SQLLEN> (size);";
+ }
+
+ virtual void
+ traverse_long_string (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "callback.callback.param," << endl
+ << "i." << mi.var << "callback.context.param," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? " <<
+ "SQL_NULL_DATA : SQL_DATA_AT_EXEC;";
+ }
+
+ virtual void
+ traverse_nstring (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ // Don't mention the extra character for the null-terminator.
+ << "sizeof (i." << mi.var << "value) / 2 - 1," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "size_ind =" << endl
+ << " is_null ? SQL_NULL_DATA : static_cast<SQLLEN> (size * 2);";
+ }
+
+ virtual void
+ traverse_long_nstring (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "callback.callback.param," << endl
+ << "i." << mi.var << "callback.context.param," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? " <<
+ "SQL_NULL_DATA : SQL_DATA_AT_EXEC;";
+ }
+
+ virtual void
+ traverse_binary (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "sizeof (i." << mi.var << "value)," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "size_ind =" << endl
+ << " is_null ? SQL_NULL_DATA : static_cast<SQLLEN> (size);";
+ }
+
+ virtual void
+ traverse_long_binary (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "callback.callback.param," << endl
+ << "i." << mi.var << "callback.context.param," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? " <<
+ "SQL_NULL_DATA : SQL_DATA_AT_EXEC;";
+ }
+
+ virtual void
+ traverse_date (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 0;";
+ }
+
+ virtual void
+ traverse_time (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, " << mi.st->scale << ", " <<
+ "is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null" << endl
+ << " ? SQL_NULL_DATA" << endl
+ << " : static_cast<SQLLEN> (sizeof (i." << mi.var << "value));";
+ }
+
+ virtual void
+ traverse_datetime (member_info& mi)
+ {
+ // The same code as in bind.
+ //
+ unsigned short scale (0);
+
+ switch (mi.st->type)
+ {
+ case sql_type::DATETIME:
+ {
+ scale = 3;
+ break;
+ }
+ case sql_type::DATETIME2:
+ {
+ scale = mi.st->scale;
+ break;
+ }
+ case sql_type::SMALLDATETIME:
+ {
+ scale = 8;
+ break;
+ }
+ default:
+ {
+ assert (false);
+ break;
+ }
+ }
+
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, " << scale << ", " <<
+ "is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 0;";
+ }
+
+ virtual void
+ traverse_datetimeoffset (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, " << mi.st->scale << ", " <<
+ "is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null" << endl
+ << " ? SQL_NULL_DATA" << endl
+ << " : static_cast<SQLLEN> (sizeof (i." << mi.var << "value));";
+ }
+
+ virtual void
+ traverse_uniqueidentifier (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 0;";
+ }
+
+ virtual void
+ traverse_rowversion (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "size_ind = is_null ? SQL_NULL_DATA : 8;";
+ }
+ };
+ entry<init_image_member> init_image_member_;
+
+ //
+ // init value
+ //
+
+ struct init_value_member: relational::init_value_member_impl<sql_type>,
+ member_base
+ {
+ init_value_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ get_null (string const& var) const
+ {
+ os << "i." << var << "size_ind == SQL_NULL_DATA";
+ }
+
+ virtual void
+ check_modifier (member_info& mi, member_access& ma)
+ {
+ // We cannot use by-value modifier for long data members.
+ //
+ if (long_data (*mi.st) && ma.placeholder ())
+ {
+ error (ma.loc) << "modifier accepting a value cannot be used "
+ << "for a data member of SQL Server long data "
+ << "type" << endl;
+ info (ma.loc) << "modifier returning a non-const reference is "
+ << "required" << endl;
+ info (mi.m.location ()) << "data member is defined here" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_decimal (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_smallmoney (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_money (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_float4 (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_float8 (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "static_cast<std::size_t> (i." << mi.var << "size_ind)," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_long_string (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "callback.callback.result," << endl
+ << "i." << mi.var << "callback.context.result);"
+ << endl;
+ }
+
+ virtual void
+ traverse_nstring (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "static_cast<std::size_t> (" <<
+ "i." << mi.var << "size_ind / 2)," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_long_nstring (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "callback.callback.result," << endl
+ << "i." << mi.var << "callback.context.result);"
+ << endl;
+ }
+
+ virtual void
+ traverse_binary (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "static_cast<std::size_t> (i." << mi.var << "size_ind)," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_long_binary (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "callback.callback.result," << endl
+ << "i." << mi.var << "callback.context.result);"
+ << endl;
+ }
+
+ virtual void
+ traverse_date (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_time (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_datetime (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_datetimeoffset (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_uniqueidentifier (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+
+ virtual void
+ traverse_rowversion (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size_ind == SQL_NULL_DATA);"
+ << endl;
+ }
+ };
+ entry<init_value_member> init_value_member_;
+
+ struct statement_columns_common: context
+ {
+ void
+ process (relational::statement_columns& cols,
+ statement_kind sk,
+ bool dynamic)
+ {
+ using relational::statement_columns;
+
+ // Long data columns must come last in the SELECT statement. If
+ // this statement is going to be processed at runtime, then this
+ // will be taken care of then.
+ //
+ if (sk != statement_select || dynamic)
+ return;
+
+ // Go over the columns list while keeping track of how many
+ // columns we have examined. If the current column is long data,
+ // then move it to the back. Stop once we have examined all the
+ // columns.
+ //
+ size_t n (cols.size ());
+ for (statement_columns::iterator i (cols.begin ()); n != 0; --n)
+ {
+ if (long_data (parse_sql_type (i->type, *i->member)))
+ {
+ cols.push_back (*i);
+ i = cols.erase (i);
+ }
+ else
+ ++i;
+ }
+ }
+ };
+
+ struct container_traits: relational::container_traits,
+ statement_columns_common
+ {
+ container_traits (base const& x): base (x) {}
+
+ virtual void
+ cache_result (string const&)
+ {
+ // Caching is not necessary since with MARS enabled SQL Server
+ // can execute several interleaving statements.
+ //
+ }
+
+ virtual void
+ init_value_extra ()
+ {
+ os << "sts.select_statement ().stream_result ();"
+ << endl;
+ }
+
+ virtual void
+ process_statement_columns (relational::statement_columns& cols,
+ statement_kind sk,
+ bool dynamic)
+ {
+ statement_columns_common::process (cols, sk, dynamic);
+ }
+ };
+ entry<container_traits> container_traits_;
+
+ struct section_traits: relational::section_traits,
+ statement_columns_common
+ {
+ section_traits (base const& x): base (x) {}
+
+ virtual void
+ init_value_extra ()
+ {
+ os << "st.stream_result ();";
+ }
+
+ virtual void
+ process_statement_columns (relational::statement_columns& cols,
+ statement_kind sk,
+ bool dynamic)
+ {
+ statement_columns_common::process (cols, sk, dynamic);
+ }
+
+ virtual string
+ optimistic_version_increment (semantics::data_member& m)
+ {
+ sql_type t (parse_sql_type (column_type (m), m));
+ return t.type != sql_type::ROWVERSION
+ ? "1"
+ : "version (sts.id_image ())";
+ }
+
+ virtual string
+ update_statement_extra (user_section&)
+ {
+ string r;
+
+ semantics::data_member* ver (optimistic (c_));
+
+ if (ver == 0 ||
+ parse_sql_type (column_type (*ver), *ver).type !=
+ sql_type::ROWVERSION)
+ return r;
+
+ // ROWVERSION & SQL Server 2005 incompatibility is detected
+ // in persist_statement_extra.
+ //
+ r = "OUTPUT INSERTED." +
+ convert_from (column_qname (*ver, column_prefix ()), *ver);
+
+ return r;
+ }
+ };
+ entry<section_traits> section_traits_;
+
+ struct class_: relational::class_, statement_columns_common
+ {
+ class_ (base const& x):
+ base (x), init_version_value_member_id_image_ ("v", "version_") {}
+
+ virtual void
+ init_image_pre (type& c)
+ {
+ if (options.generate_query () &&
+ !(composite (c) || (abstract (c) && !polymorphic (c))))
+ {
+ type* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ if (poly_derived)
+ os << "{"
+ << "root_traits::image_type& ri (root_image (i));"
+ << endl;
+
+ string i (poly_derived ? "ri" : "i");
+
+ os << "if (" << i << ".change_callback_.callback != 0)" << endl
+ << "(" << i << ".change_callback_.callback) (" <<
+ i << ".change_callback_.context);";
+
+ if (poly_derived)
+ os << "}";
+ else
+ os << endl;
+ }
+ }
+
+ virtual void
+ init_value_extra ()
+ {
+ os << "st.stream_result ();";
+ }
+
+ virtual string
+ persist_statement_extra (type& c,
+ relational::query_parameters&,
+ persist_position p)
+ {
+ string r;
+
+ type* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ // If we are a derived type in a polymorphic hierarchy, then
+ // auto id/version are handled by the root.
+ //
+ if (poly_derived)
+ return r;
+
+ // See if we have auto id or ROWVERSION version.
+ //
+ data_member_path* id (id_member (c));
+ semantics::data_member* ver (optimistic (c));
+
+ if (id != 0 && !auto_ (*id))
+ id = 0;
+
+ if (ver != 0)
+ {
+ sql_type t (parse_sql_type (column_type (*ver), *ver));
+ if (t.type != sql_type::ROWVERSION)
+ ver = 0;
+ }
+
+ if (id == 0 && ver == 0)
+ return r;
+
+ // SQL Server 2005 has a bug that causes it to fail on an
+ // INSERT statement with the OUTPUT clause if data for one
+ // of the inserted columns is supplied at execution (long
+ // data). To work around this problem we use the less
+ // efficient batch of INSERT and SELECT statements.
+ //
+ if (options.mssql_server_version () <= mssql_version (9, 0))
+ {
+ bool ld (false);
+
+ if (c.count ("mssql-has-long-data"))
+ ld = c.get<bool> ("mssql-has-long-data");
+ else
+ {
+ has_long_data t (ld);
+ t.traverse (c);
+ c.set ("mssql-has-long-data", ld);
+ }
+
+ if (ld)
+ {
+ if (p == persist_after_values)
+ {
+ // SQL Server 2005 has no eqivalent of SCOPE_IDENTITY for
+ // ROWVERSION.
+ //
+ if (ver != 0)
+ {
+ error (c.location ()) << "in SQL Server 2005 ROWVERSION " <<
+ "value cannot be retrieved for a persistent class " <<
+ "containing long data" << endl;
+ throw operation_failed ();
+ }
+
+ // We also cannot support bulk INSERT.
+ //
+ if (c.count ("bulk-persist"))
+ {
+ error (c.location ()) << "in SQL Server 2005 bulk " <<
+ "persist operation cannot be implemented for a " <<
+ "persistent class containing long data" << endl;
+ throw operation_failed ();
+ }
+
+ r = "; SELECT " +
+ convert_from ("SCOPE_IDENTITY()", *id->back ());
+ }
+
+ return r;
+ }
+ }
+
+ if (p == persist_after_columns)
+ {
+ r = "OUTPUT ";
+
+ // Top-level auto id column.
+ //
+ if (id != 0)
+ r += "INSERTED." +
+ convert_from (column_qname (*id), *id->back ());
+
+ // Top-level version column.
+ //
+ if (ver != 0)
+ {
+ if (id != 0)
+ r += ',';
+
+ r += "INSERTED." + convert_from (
+ column_qname (*ver, column_prefix ()), *ver);
+ }
+ }
+
+ return r;
+ }
+
+ virtual string
+ update_statement_extra (type& c)
+ {
+ string r;
+
+ type* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ // If we are a derived type in a polymorphic hierarchy, then
+ // version is handled by the root.
+ //
+ if (poly_derived)
+ return r;
+
+ semantics::data_member* ver (optimistic (c));
+
+ if (ver == 0 ||
+ parse_sql_type (column_type (*ver), *ver).type !=
+ sql_type::ROWVERSION)
+ return r;
+
+ // Long data & SQL Server 2005 incompatibility is detected
+ // in persist_statement_extra.
+ //
+ r = "OUTPUT INSERTED." +
+ convert_from (column_qname (*ver, column_prefix ()), *ver);
+
+ return r;
+ }
+
+ virtual void
+ process_statement_columns (relational::statement_columns& cols,
+ statement_kind sk,
+ bool dynamic)
+ {
+ statement_columns_common::process (cols, sk, dynamic);
+ }
+
+ virtual string
+ optimistic_version_init (semantics::data_member& m, bool index)
+ {
+ sql_type t (parse_sql_type (column_type (m), m));
+ return t.type != sql_type::ROWVERSION
+ ? "1"
+ : (index
+ ? "version (sts.id_image (i))"
+ : "version (sts.id_image ())");
+ }
+
+ virtual string
+ optimistic_version_increment (semantics::data_member& m, bool index)
+ {
+ sql_type t (parse_sql_type (column_type (m), m));
+ return t.type != sql_type::ROWVERSION
+ ? "1"
+ : (index
+ ? "version (sts.id_image (i))"
+ : "version (sts.id_image ())");
+ }
+
+ virtual bool
+ optimistic_insert_bind_version (semantics::data_member& m)
+ {
+ sql_type t (parse_sql_type (column_type (m), m));
+ return t.type == sql_type::ROWVERSION;
+ }
+
+ virtual void
+ object_extra (type& c)
+ {
+ bool abst (abstract (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+
+ if (poly_derived || (abst && !poly))
+ return;
+
+ if (semantics::data_member* m = optimistic (c))
+ {
+ sql_type t (parse_sql_type (column_type (*m), *m));
+ if (t.type == sql_type::ROWVERSION)
+ {
+ string const& type (class_fq_name (c));
+ string traits ("access::object_traits_impl< " + type + ", id_" +
+ db.string () + " >");
+
+ os << traits << "::version_type" << endl
+ << traits << "::" << endl
+ << "version (const id_image_type& i)"
+ << "{"
+ << "version_type v;";
+ init_version_value_member_id_image_->traverse (*m);
+ os << "return v;"
+ << "}";
+ }
+ }
+ }
+
+ virtual string
+ from_trailer (type& c)
+ {
+ return c.get<view_query> ("query").for_update
+ ? " WITH (UPDLOCK)"
+ : "";
+ }
+
+ virtual string
+ select_trailer (type&) {return "";}
+
+ private:
+ // Go via the dynamic creation to get access to the constructor.
+ //
+ instance<relational::init_value_member>
+ init_version_value_member_id_image_;
+ };
+ entry<class_> class_entry_;
+ }
+ }
+}
diff --git a/odb/odb/relational/mysql/common.cxx b/odb/odb/relational/mysql/common.cxx
new file mode 100644
index 0000000..d049443
--- /dev/null
+++ b/odb/odb/relational/mysql/common.cxx
@@ -0,0 +1,400 @@
+// file : odb/relational/mysql/common.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/relational/mysql/common.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace mysql
+ {
+ //
+ // member_base
+ //
+
+ sql_type const& member_base::
+ member_sql_type (semantics::data_member& m)
+ {
+ return parse_sql_type (column_type (m, key_prefix_), m);
+ }
+
+ void member_base::
+ traverse_simple (member_info& mi)
+ {
+ switch (mi.st->type)
+ {
+ // Integral types.
+ //
+ case sql_type::TINYINT:
+ case sql_type::SMALLINT:
+ case sql_type::MEDIUMINT:
+ case sql_type::INT:
+ case sql_type::BIGINT:
+ {
+ traverse_integer (mi);
+ break;
+ }
+
+ // Float types.
+ //
+ case sql_type::FLOAT:
+ case sql_type::DOUBLE:
+ {
+ traverse_float (mi);
+ break;
+ }
+ case sql_type::DECIMAL:
+ {
+ traverse_decimal (mi);
+ break;
+ }
+
+ // Data-time types.
+ //
+ case sql_type::DATE:
+ case sql_type::TIME:
+ case sql_type::DATETIME:
+ case sql_type::TIMESTAMP:
+ case sql_type::YEAR:
+ {
+ traverse_date_time (mi);
+ break;
+ }
+
+ // String and binary types.
+ //
+ case sql_type::CHAR:
+ case sql_type::VARCHAR:
+ case sql_type::TINYTEXT:
+ case sql_type::TEXT:
+ case sql_type::MEDIUMTEXT:
+ case sql_type::LONGTEXT:
+ {
+ // For string types the limit is in characters rather
+ // than in bytes. The fixed-length pre-allocated buffer
+ // optimization can only be used for 1-byte encodings.
+ // To support this we will need the character encoding
+ // in sql_type.
+ //
+ traverse_long_string (mi);
+ break;
+ }
+ case sql_type::BINARY:
+ case sql_type::TINYBLOB:
+ {
+ // BINARY's range is always 255 or less from MySQL 5.0.3.
+ // TINYBLOB can only store up to 255 bytes.
+ //
+ traverse_short_string (mi);
+ break;
+ }
+ case sql_type::VARBINARY:
+ case sql_type::BLOB:
+ case sql_type::MEDIUMBLOB:
+ case sql_type::LONGBLOB:
+ {
+ if (mi.st->range && mi.st->range_value <= 255)
+ traverse_short_string (mi);
+ else
+ traverse_long_string (mi);
+
+ break;
+ }
+
+ // Other types.
+ //
+ case sql_type::BIT:
+ {
+ traverse_bit (mi);
+ break;
+ }
+ case sql_type::ENUM:
+ {
+ traverse_enum (mi);
+ break;
+ }
+ case sql_type::SET:
+ {
+ traverse_set (mi);
+ break;
+ }
+ case sql_type::invalid:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+
+ //
+ // member_image_type
+ //
+
+ static const char* integer_types[] =
+ {
+ "char",
+ "short",
+ "int",
+ "int",
+ "long long"
+ };
+
+ static const char* float_types[] =
+ {
+ "float",
+ "double"
+ };
+
+ member_image_type::
+ member_image_type (base const& x)
+ : member_base::base (x), // virtual base
+ base (x) {}
+
+ member_image_type::
+ member_image_type ()
+ : relational::member_base (0, 0, string (), string ()) {}
+
+ member_image_type::
+ member_image_type (semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : relational::member_base (type, ct, fq_type, key_prefix) {}
+
+ string member_image_type::
+ image_type (semantics::data_member& m)
+ {
+ type_.clear ();
+ member_base::traverse (m, true);
+ return type_;
+ }
+
+ void member_image_type::
+ traverse_composite (member_info& mi)
+ {
+ type_ = "composite_value_traits< " + mi.fq_type () +
+ ", id_mysql >::image_type";
+ }
+
+ void member_image_type::
+ traverse_integer (member_info& mi)
+ {
+ if (mi.st->unsign)
+ type_ = "unsigned ";
+ else if (mi.st->type == sql_type::TINYINT)
+ type_ = "signed ";
+
+ type_ += integer_types[mi.st->type - sql_type::TINYINT];
+ }
+
+ void member_image_type::
+ traverse_float (member_info& mi)
+ {
+ type_ = float_types[mi.st->type - sql_type::FLOAT];
+ }
+
+ void member_image_type::
+ traverse_decimal (member_info&)
+ {
+ type_ = "details::buffer";
+ }
+
+ void member_image_type::
+ traverse_date_time (member_info& mi)
+ {
+ if (mi.st->type == sql_type::YEAR)
+ type_ = "short";
+ else
+ type_ = "MYSQL_TIME";
+ }
+
+ void member_image_type::
+ traverse_string (member_info&)
+ {
+ type_ = "details::buffer";
+ }
+
+ void member_image_type::
+ traverse_bit (member_info&)
+ {
+ type_ = "unsigned char*";
+ }
+
+ void member_image_type::
+ traverse_enum (member_info& mi)
+ {
+ // Represented as either integer or string.
+ //
+ type_ = "mysql::value_traits< " + mi.fq_type () +
+ ", mysql::id_enum >::image_type";
+ }
+
+ void member_image_type::
+ traverse_set (member_info&)
+ {
+ // Represented as string.
+ //
+ type_ = "details::buffer";
+ }
+
+ entry<member_image_type> member_image_type_;
+
+ //
+ // member_database_type
+ //
+
+ static const char* integer_database_id[] =
+ {
+ "id_tiny",
+ "id_utiny",
+ "id_short",
+ "id_ushort",
+ "id_long", // INT24
+ "id_ulong", // INT24 UNSIGNED
+ "id_long",
+ "id_ulong",
+ "id_longlong",
+ "id_ulonglong"
+ };
+
+ static const char* float_database_id[] =
+ {
+ "id_float",
+ "id_double"
+ };
+
+ static const char* date_time_database_id[] =
+ {
+ "id_date",
+ "id_time",
+ "id_datetime",
+ "id_timestamp",
+ "id_year"
+ };
+
+ static const char* char_bin_database_id[] =
+ {
+ "id_string", // CHAR
+ "id_blob", // BINARY,
+ "id_string", // VARCHAR
+ "id_blob", // VARBINARY
+ "id_string", // TINYTEXT
+ "id_blob", // TINYBLOB
+ "id_string", // TEXT
+ "id_blob", // BLOB
+ "id_string", // MEDIUMTEXT
+ "id_blob", // MEDIUMBLOB
+ "id_string", // LONGTEXT
+ "id_blob" // LONGBLOB
+ };
+
+ member_database_type_id::
+ member_database_type_id (base const& x)
+ : member_base::base (x), // virtual base
+ base (x) {}
+
+ member_database_type_id::
+ member_database_type_id ()
+ : member_base::base (0, 0, string (), string ()), // virtual base
+ base (0, 0, string (), string ()) {}
+
+ member_database_type_id::
+ member_database_type_id (semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base::base (type, ct, fq_type, key_prefix), // virtual base
+ base (type, ct, fq_type, key_prefix) {}
+
+ string member_database_type_id::
+ database_type_id (type& m)
+ {
+ type_id_.clear ();
+ member_base::traverse (m, true);
+ return type_id_;
+ }
+
+ void member_database_type_id::
+ traverse_composite (member_info&)
+ {
+ assert (false);
+ }
+
+ void member_database_type_id::
+ traverse_integer (member_info& mi)
+ {
+ size_t i (
+ (mi.st->type - sql_type::TINYINT) * 2 + (mi.st->unsign ? 1 : 0));
+ type_id_ = string ("mysql::") + integer_database_id[i];
+ }
+
+ void member_database_type_id::
+ traverse_float (member_info& mi)
+ {
+ type_id_ = string ("mysql::") +
+ float_database_id[mi.st->type - sql_type::FLOAT];
+ }
+
+ void member_database_type_id::
+ traverse_decimal (member_info&)
+ {
+ type_id_ = "mysql::id_decimal";
+ }
+
+ void member_database_type_id::
+ traverse_date_time (member_info& mi)
+ {
+ type_id_ = string ("mysql::") +
+ date_time_database_id[mi.st->type - sql_type::DATE];
+ }
+
+ void member_database_type_id::
+ traverse_string (member_info& mi)
+ {
+ type_id_ = string ("mysql::") +
+ char_bin_database_id[mi.st->type - sql_type::CHAR];
+ }
+
+ void member_database_type_id::
+ traverse_bit (member_info&)
+ {
+ type_id_ = "mysql::id_bit";
+ }
+
+ void member_database_type_id::
+ traverse_enum (member_info&)
+ {
+ type_id_ = "mysql::id_enum";
+ }
+
+ void member_database_type_id::
+ traverse_set (member_info&)
+ {
+ type_id_ = "mysql::id_set";
+ }
+
+ entry<member_database_type_id> member_database_type_id_;
+
+ //
+ // query_columns
+ //
+
+ struct query_columns: relational::query_columns, context
+ {
+ query_columns (base const& x): base_impl (x) {}
+
+ virtual string
+ database_type_id (semantics::data_member& m)
+ {
+ return member_database_type_id_.database_type_id (m);
+ }
+
+ private:
+ member_database_type_id member_database_type_id_;
+ };
+ entry<query_columns> query_columns_;
+ }
+}
diff --git a/odb/odb/relational/mysql/common.hxx b/odb/odb/relational/mysql/common.hxx
new file mode 100644
index 0000000..b43dc0d
--- /dev/null
+++ b/odb/odb/relational/mysql/common.hxx
@@ -0,0 +1,171 @@
+// file : odb/relational/mysql/common.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_MYSQL_COMMON_HXX
+#define ODB_RELATIONAL_MYSQL_COMMON_HXX
+
+#include <odb/relational/common.hxx>
+#include <odb/relational/mysql/context.hxx>
+
+namespace relational
+{
+ namespace mysql
+ {
+ struct member_base: virtual relational::member_base_impl<sql_type>, context
+ {
+ member_base (base const& x): base (x), base_impl (x) {}
+
+ // This c-tor is for the direct use inside the mysql namespace.
+ // If you do use this c-tor, you should also explicitly call
+ // relational::member_base (aka base).
+ //
+ member_base () {}
+
+ virtual sql_type const&
+ member_sql_type (semantics::data_member&);
+
+ virtual void
+ traverse_simple (member_info&);
+
+ virtual void
+ traverse_integer (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_float (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_decimal (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_date_time (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_string (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_short_string (member_info& mi)
+ {
+ traverse_string (mi);
+ }
+
+ virtual void
+ traverse_long_string (member_info& mi)
+ {
+ traverse_string (mi);
+ }
+
+ virtual void
+ traverse_bit (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_enum (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_set (member_info&)
+ {
+ }
+ };
+
+ struct member_image_type: relational::member_image_type,
+ member_base
+ {
+ member_image_type (base const&);
+ member_image_type ();
+ member_image_type (semantics::type* type,
+ const custom_cxx_type*,
+ string const& fq_type = string (),
+ string const& key_prefix = string ());
+ virtual string
+ image_type (semantics::data_member&);
+
+ virtual void
+ traverse_composite (member_info&);
+
+ virtual void
+ traverse_integer (member_info&);
+
+ virtual void
+ traverse_float (member_info&);
+
+ virtual void
+ traverse_decimal (member_info&);
+
+ virtual void
+ traverse_date_time (member_info&);
+
+ virtual void
+ traverse_string (member_info&);
+
+ virtual void
+ traverse_bit (member_info&);
+
+ virtual void
+ traverse_enum (member_info&);
+
+ virtual void
+ traverse_set (member_info&);
+
+ private:
+ string type_;
+ };
+
+ struct member_database_type_id: relational::member_database_type_id,
+ member_base
+ {
+ member_database_type_id (base const&);
+ member_database_type_id ();
+ member_database_type_id (semantics::type* type,
+ const custom_cxx_type*,
+ string const& fq_type = string (),
+ string const& key_prefix = string ());
+
+ virtual string
+ database_type_id (type&);
+
+ virtual void
+ traverse_composite (member_info&);
+
+ virtual void
+ traverse_integer (member_info&);
+
+ virtual void
+ traverse_float (member_info&);
+
+ virtual void
+ traverse_decimal (member_info&);
+
+ virtual void
+ traverse_date_time (member_info&);
+
+ virtual void
+ traverse_string (member_info&);
+
+ virtual void
+ traverse_bit (member_info&);
+
+ virtual void
+ traverse_enum (member_info&);
+
+ virtual void
+ traverse_set (member_info&);
+
+ private:
+ string type_id_;
+ };
+ }
+}
+#endif // ODB_RELATIONAL_MYSQL_COMMON_HXX
diff --git a/odb/odb/relational/mysql/context.cxx b/odb/odb/relational/mysql/context.cxx
new file mode 100644
index 0000000..8b3d983
--- /dev/null
+++ b/odb/odb/relational/mysql/context.cxx
@@ -0,0 +1,868 @@
+// file : odb/relational/mysql/context.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cassert>
+#include <sstream>
+
+#include <odb/sql-token.hxx>
+#include <odb/sql-lexer.hxx>
+
+#include <odb/relational/mysql/context.hxx>
+#include <odb/relational/mysql/common.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace mysql
+ {
+ namespace
+ {
+ struct type_map_entry
+ {
+ char const* const cxx_type;
+ char const* const db_type;
+ char const* const db_id_type;
+ bool const null;
+ };
+
+ type_map_entry type_map[] =
+ {
+ {"bool", "TINYINT(1)", 0, false},
+
+ {"char", "CHAR(1)", 0, false},
+ {"signed char", "TINYINT", 0, false},
+ {"unsigned char", "TINYINT UNSIGNED", 0, false},
+
+ {"short int", "SMALLINT", 0, false},
+ {"short unsigned int", "SMALLINT UNSIGNED", 0, false},
+
+ {"int", "INT", 0, false},
+ {"unsigned int", "INT UNSIGNED", 0, false},
+
+ {"long int", "BIGINT", 0, false},
+ {"long unsigned int", "BIGINT UNSIGNED", 0, false},
+
+ {"long long int", "BIGINT", 0, false},
+ {"long long unsigned int", "BIGINT UNSIGNED", 0, false},
+
+ {"float", "FLOAT", 0, false},
+ {"double", "DOUBLE", 0, false},
+
+ {"::std::string", "TEXT", "VARCHAR(128)", false},
+
+ {"::size_t", "BIGINT UNSIGNED", 0, false},
+ {"::std::size_t", "BIGINT UNSIGNED", 0, false}
+ };
+ }
+
+ context* context::current_;
+
+ context::
+ ~context ()
+ {
+ if (current_ == this)
+ current_ = 0;
+ }
+
+ context::
+ context (ostream& os,
+ semantics::unit& u,
+ options_type const& ops,
+ features_type& f,
+ sema_rel::model* m)
+ : root_context (os, u, ops, f, data_ptr (new (shared) data (os))),
+ base_context (static_cast<data*> (root_context::data_.get ()), m),
+ data_ (static_cast<data*> (base_context::data_))
+ {
+ assert (current_ == 0);
+ current_ = this;
+
+ generate_grow = true;
+ need_alias_as = true;
+ insert_send_auto_id = true;
+ delay_freeing_statement_result = false;
+ need_image_clone = false;
+ generate_bulk = false;
+ global_index = false;
+ global_fkey = true;
+ data_->bind_vector_ = "MYSQL_BIND*";
+ data_->truncated_vector_ = "my_bool*";
+
+ // Populate the C++ type to DB type map.
+ //
+ for (size_t i (0); i < sizeof (type_map) / sizeof (type_map_entry); ++i)
+ {
+ type_map_entry const& e (type_map[i]);
+
+ type_map_type::value_type v (
+ e.cxx_type,
+ db_type_type (
+ e.db_type, e.db_id_type ? e.db_id_type : e.db_type, e.null));
+
+ data_->type_map_.insert (v);
+ }
+ }
+
+ context::
+ context ()
+ : data_ (current ().data_)
+ {
+ }
+
+ string const& context::
+ convert_expr (string const& sqlt, semantics::data_member& m, bool to)
+ {
+ sql_type const& t (parse_sql_type (sqlt, m));
+ return to ? t.to : t.from;
+ }
+
+ string context::
+ quote_id_impl (qname const& id) const
+ {
+ string r;
+
+ bool f (true);
+ for (qname::iterator i (id.begin ()); i < id.end (); ++i)
+ {
+ if (i->empty ())
+ continue;
+
+ // Warn if the name is greater than the 64 limit.
+ //
+ if (i->size () > 64)
+ {
+ cerr << "warning: SQL name '" << *i << "' is longer than "
+ << "the MySQL name limit of 64 characters and will "
+ << "be truncated" << endl;
+
+ cerr << "info: consider shortening it using #pragma db "
+ << "table/column/index or --*-regex options" << endl;
+ }
+
+ if (f)
+ f = false;
+ else
+ r += '.';
+
+ r += '`';
+ r.append (*i, 0, 64); // Max identifier length is 64.
+ r += '`';
+ }
+
+ return r;
+ }
+
+ namespace
+ {
+ struct has_grow: traversal::class_
+ {
+ has_grow (bool& r, user_section* s)
+ : r_ (r), section_ (s)
+ {
+ *this >> inherits_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ bool view (context::view (c));
+
+ // Ignore transient bases.
+ //
+ if (!(context::object (c) || view || context::composite (c)))
+ return;
+
+ if (section_ == 0 && c.count ("mysql-grow"))
+ r_ = c.get<bool> ("mysql-grow");
+ else
+ {
+ // r_ should be false.
+ //
+ if (!view)
+ inherits (c);
+
+ if (!r_)
+ names (c);
+
+ if (section_ == 0)
+ c.set ("mysql-grow", r_);
+ }
+ }
+
+ private:
+ bool& r_;
+ user_section* section_;
+ traversal::inherits inherits_;
+ };
+
+ struct has_grow_member: member_base
+ {
+ has_grow_member (bool& r, user_section* section = 0)
+ : relational::member_base (0, 0, string (), string (), section),
+ r_ (r) {}
+
+ has_grow_member (bool& r,
+ user_section* section,
+ semantics::type* t,
+ const custom_cxx_type* ct,
+ string const& key_prefix = string ())
+ : relational::member_base (t, ct, string (), key_prefix, section),
+ r_ (r) {}
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ // If we have a key prefix (container), then it can't be in a
+ // section (while mi.m can). The same for top-level -- if we got
+ // called, then we shouldn't ignore it.
+ //
+ return !key_prefix_.empty () || top_level_ ||
+ (section_ == 0 && !separate_load (mi.m)) ||
+ (section_ != 0 && *section_ == section (mi.m));
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ // By calling grow() instead of recursing, we reset any overrides.
+ // We also don't pass section since they don't apply inside
+ // composites.
+ //
+ r_ = r_ || context::grow (dynamic_cast<semantics::class_&> (mi.t));
+ }
+
+ virtual void
+ traverse_decimal (member_info&)
+ {
+ r_ = true;
+ }
+
+ virtual void
+ traverse_long_string (member_info&)
+ {
+ r_ = true;
+ }
+
+ virtual void
+ traverse_short_string (member_info&)
+ {
+ r_ = true; // @@ Short string optimization disabled.
+ }
+
+ virtual void
+ traverse_enum (member_info&)
+ {
+ r_ = true;
+ }
+
+ virtual void
+ traverse_set (member_info&)
+ {
+ r_ = true;
+ }
+
+ private:
+ bool& r_;
+ };
+ }
+
+ bool context::
+ grow_impl (semantics::class_& c, user_section* section)
+ {
+ if (section == 0 && c.count ("mysql-grow"))
+ return c.get<bool> ("mysql-grow");
+
+ bool r (false);
+ has_grow ct (r, section);
+ has_grow_member mt (r, section);
+ traversal::names names;
+ ct >> names >> mt;
+ ct.traverse (c);
+ return r;
+ }
+
+ bool context::
+ grow_impl (semantics::data_member& m)
+ {
+ bool r (false);
+ has_grow_member mt (r);
+ mt.traverse (m, true);
+ return r;
+ }
+
+ bool context::
+ grow_impl (semantics::data_member& m,
+ semantics::type& t,
+ const custom_cxx_type* ct,
+ string const& kp)
+ {
+ bool r (false);
+ has_grow_member mt (r, 0, &t, ct, kp);
+ mt.traverse (m, true);
+ return r;
+ }
+
+ string context::
+ database_type_impl (semantics::type& t,
+ semantics::names* hint,
+ bool id,
+ bool* null)
+ {
+ using semantics::enum_;
+ using semantics::enumerator;
+ using semantics::array;
+
+ string r;
+
+ // Enum mapping.
+ //
+ if (enum_* e = dynamic_cast<enum_*> (&t))
+ {
+ // We can only map to ENUM if the C++ enumeration is contiguous
+ // and starts with 0.
+ //
+ enum_::enumerates_iterator i (e->enumerates_begin ()),
+ end (e->enumerates_end ());
+
+ if (i != end)
+ {
+ r += "ENUM(";
+
+ for (unsigned long long j (0); i != end; ++i, ++j)
+ {
+ enumerator const& er (i->enumerator ());
+
+ if (er.value () != j)
+ break;
+
+ if (j != 0)
+ r += ", ";
+
+ r += quote_string (er.name ());
+ }
+
+ if (i == end)
+ r += ")";
+ else
+ r.clear ();
+ }
+
+ if (!r.empty ())
+ return r;
+ }
+
+ r = base_context::database_type_impl (t, hint, id, null);
+
+ if (!r.empty ())
+ return r;
+
+ // char[N] mapping.
+ //
+ else if (array* a = dynamic_cast<array*> (&t))
+ {
+ semantics::type& bt (a->base_type ());
+ if (bt.is_a<semantics::fund_char> ())
+ {
+ unsigned long long n (a->size ());
+
+ if (n == 0)
+ return r;
+ else if (n == 1)
+ r = "CHAR(";
+ else
+ {
+ r = "VARCHAR(";
+ n--;
+ }
+
+ ostringstream ostr;
+ ostr << n;
+ r += ostr.str ();
+ r += ')';
+ }
+ }
+
+ return r;
+ }
+
+ //
+ // SQL type parsing.
+ //
+
+ sql_type const& context::
+ parse_sql_type (string const& t, semantics::data_member& m, bool custom)
+ {
+ // If this proves to be too expensive, we can maintain a cache of
+ // parsed types across contexts.
+ //
+ data::sql_type_cache::iterator i (data_->sql_type_cache_.find (t));
+
+ if (i != data_->sql_type_cache_.end ()
+ && (custom ? i->second.custom_cached : i->second.straight_cached))
+ {
+ return (custom ? i->second.custom : i->second.straight);
+ }
+ else
+ {
+ try
+ {
+ sql_type st (
+ parse_sql_type (
+ t,
+ custom ? &unit.get<custom_db_types> ("custom-db-types") : 0));
+
+ if (custom)
+ return data_->sql_type_cache_[t].cache_custom (st);
+ else
+ return data_->sql_type_cache_[t].cache_straight (st);
+ }
+ catch (invalid_sql_type const& e)
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: " << e.message () << endl;
+
+ throw operation_failed ();
+ }
+ }
+ }
+
+ inline sql_type
+ error (bool fail, string const& m)
+ {
+ if (!fail)
+ return sql_type ();
+ else
+ throw context::invalid_sql_type (m);
+ }
+
+ sql_type context::
+ parse_sql_type (string sqlt, custom_db_types const* ct)
+ {
+ try
+ {
+ sql_type r;
+
+ // First run the type through the custom mapping, if requested.
+ //
+ if (ct != 0)
+ {
+ for (custom_db_types::const_iterator i (ct->begin ());
+ i != ct->end (); ++i)
+ {
+ custom_db_type const& t (*i);
+
+ if (t.type.match (sqlt))
+ {
+ r.to = t.type.replace (sqlt, t.to);
+ r.from = t.type.replace (sqlt, t.from);
+ sqlt = t.type.replace (sqlt, t.as);
+ break;
+ }
+ }
+ }
+
+ sql_lexer l (sqlt);
+
+ // While most type names use single identifier, there are
+ // a couple of exceptions to this rule:
+ //
+ // NATIONAL CHAR|VARCHAR
+ // CHAR BYTE (BINARY)
+ // CHARACTER VARYING (VARCHAR)
+ // LONG VARBINARY (MEDIUMBLOB)
+ // LONG VARCHAR (MEDIUMTEXT)
+ //
+ //
+ enum state
+ {
+ parse_prefix,
+ parse_name,
+ parse_range,
+ parse_sign,
+ parse_done
+ };
+
+ state s (parse_prefix);
+ string prefix;
+ bool flt (false);
+
+ for (sql_token t (l.next ());
+ s != parse_done && t.type () != sql_token::t_eos;
+ t = l.next ())
+ {
+ sql_token::token_type tt (t.type ());
+
+ switch (s)
+ {
+ case parse_prefix:
+ {
+ if (tt == sql_token::t_identifier)
+ {
+ string const& id (context::upcase (t.identifier ()));
+
+ if (id == "NATIONAL" ||
+ id == "CHAR" ||
+ id == "CHARACTER" ||
+ id == "LONG")
+ {
+ prefix = id;
+ s = parse_name;
+ continue;
+ }
+ }
+
+ s = parse_name;
+ }
+ // Fall through.
+ case parse_name:
+ {
+ if (tt == sql_token::t_identifier)
+ {
+ bool match (true);
+ string const& id (context::upcase (t.identifier ()));
+
+ // Numeric types.
+ //
+ if (id == "BIT")
+ {
+ r.type = sql_type::BIT;
+ }
+ else if (id == "TINYINT" || id == "INT1")
+ {
+ r.type = sql_type::TINYINT;
+ }
+ else if (id == "BOOL" || id == "BOOLEAN")
+ {
+ r.type = sql_type::TINYINT;
+ r.range = true;
+ r.range_value = 1;
+ }
+ else if (id == "SMALLINT" || id == "INT2")
+ {
+ r.type = sql_type::SMALLINT;
+ }
+ else if (id == "MEDIUMINT" ||
+ id == "INT3" ||
+ id == "MIDDLEINT")
+ {
+ r.type = sql_type::MEDIUMINT;
+ }
+ else if (id == "INT" || id == "INTEGER" || id == "INT4")
+ {
+ r.type = sql_type::INT;
+ }
+ else if (id == "BIGINT" || id == "INT8")
+ {
+ r.type = sql_type::BIGINT;
+ }
+ else if (id == "SERIAL")
+ {
+ r.type = sql_type::BIGINT;
+ r.unsign = true;
+ }
+ else if (id == "FLOAT")
+ {
+ // Assign a type only once we know the precision of the
+ // float; it can be either 4 or 8 byte.
+ //
+ flt = true;
+ }
+ else if (id == "FLOAT4")
+ {
+ r.type = sql_type::FLOAT;
+ }
+ else if (id == "DOUBLE" || id == "FLOAT8")
+ {
+ r.type = sql_type::DOUBLE;
+ }
+ else if (id == "DECIMAL" ||
+ id == "DEC" ||
+ id == "NUMERIC" ||
+ id == "FIXED")
+ {
+ r.type = sql_type::DECIMAL;
+ }
+ //
+ // Date-time types.
+ //
+ else if (id == "DATE")
+ {
+ r.type = sql_type::DATE;
+ }
+ else if (id == "TIME")
+ {
+ r.type = sql_type::TIME;
+ }
+ else if (id == "DATETIME")
+ {
+ r.type = sql_type::DATETIME;
+ }
+ else if (id == "TIMESTAMP")
+ {
+ r.type = sql_type::TIMESTAMP;
+ }
+ else if (id == "YEAR")
+ {
+ r.type = sql_type::YEAR;
+ }
+ //
+ // String and binary types.
+ //
+ else if (id == "NCHAR")
+ {
+ r.type = sql_type::CHAR;
+ }
+ else if (id == "VARCHAR")
+ {
+ r.type = prefix == "LONG"
+ ? sql_type::MEDIUMTEXT
+ : sql_type::VARCHAR;
+ }
+ else if (id == "NVARCHAR")
+ {
+ r.type = sql_type::VARCHAR;
+ }
+ else if (id == "VARYING" && prefix == "CHARACTER")
+ {
+ r.type = sql_type::VARCHAR;
+ }
+ else if (id == "BINARY")
+ {
+ r.type = sql_type::BINARY;
+ }
+ else if (id == "BYTE" && prefix == "CHAR")
+ {
+ r.type = sql_type::BINARY;
+ }
+ else if (id == "VARBINARY")
+ {
+ r.type = prefix == "LONG"
+ ? sql_type::MEDIUMBLOB
+ : sql_type::VARBINARY;
+ }
+ else if (id == "TINYBLOB")
+ {
+ r.type = sql_type::TINYBLOB;
+ }
+ else if (id == "TINYTEXT")
+ {
+ r.type = sql_type::TINYTEXT;
+ }
+ else if (id == "BLOB")
+ {
+ r.type = sql_type::BLOB;
+ }
+ else if (id == "TEXT")
+ {
+ r.type = sql_type::TEXT;
+ }
+ else if (id == "MEDIUMBLOB")
+ {
+ r.type = sql_type::MEDIUMBLOB;
+ }
+ else if (id == "MEDIUMTEXT")
+ {
+ r.type = sql_type::MEDIUMTEXT;
+ }
+ else if (id == "LONGBLOB")
+ {
+ r.type = sql_type::LONGBLOB;
+ }
+ else if (id == "LONGTEXT")
+ {
+ r.type = sql_type::LONGTEXT;
+ }
+ else if (id == "ENUM")
+ {
+ r.type = sql_type::ENUM;
+ }
+ else if (id == "SET")
+ {
+ r.type = sql_type::SET;
+ }
+ else
+ match = false;
+
+ if (match)
+ {
+ s = parse_range;
+ continue;
+ }
+ }
+
+ // Some prefixes can also be type names if not followed
+ // by the actual type name.
+ //
+ if (!prefix.empty ())
+ {
+ if (prefix == "CHAR" || prefix == "CHARACTER")
+ {
+ r.type = sql_type::CHAR;
+ }
+ else if (prefix == "LONG")
+ {
+ r.type = sql_type::MEDIUMTEXT;
+ }
+ }
+
+ if (r.type == sql_type::invalid)
+ {
+ if (tt == sql_token::t_identifier)
+ {
+ return error (ct, "unknown MySQL type '" + t.identifier () +
+ "'");
+ }
+ else
+ return error (ct, "expected MySQL type name");
+ }
+
+ s = parse_range;
+ }
+ // Fall through.
+ case parse_range:
+ {
+ if (t.punctuation () == sql_token::p_lparen)
+ {
+ t = l.next ();
+
+ // ENUM and SET have a list of members instead of the range.
+ //
+ if (r.type == sql_type::ENUM || r.type == sql_type::SET)
+ {
+ while (true)
+ {
+ if (t.type () != sql_token::t_string_lit)
+ {
+ return error (ct, "string literal expected in MySQL "
+ "ENUM or SET declaration");
+ }
+
+ if (r.type == sql_type::ENUM)
+ r.enumerators.push_back (t.literal ());
+
+ t = l.next ();
+
+ if (t.punctuation () == sql_token::p_rparen)
+ break;
+ else if (t.punctuation () != sql_token::p_comma)
+ {
+ return error (ct, "comma expected in MySQL ENUM or "
+ "SET declaration");
+ }
+
+ t = l.next ();
+ }
+ }
+ else
+ {
+ if (t.type () != sql_token::t_int_lit)
+ {
+ return error (ct, "integer range expected in MySQL type "
+ "declaration");
+ }
+
+ unsigned int v;
+ istringstream is (t.literal ());
+
+ if (!(is >> v && is.eof ()))
+ {
+ return error (ct, "invalid range value '" + t.literal () +
+ "' in MySQL type declaration");
+ }
+
+ r.range = true;
+ r.range_value = v;
+
+ t = l.next ();
+
+ if (t.punctuation () == sql_token::p_comma)
+ {
+ // We have the second range value. Skip it.
+ //
+ // In FLOAT the two-value range means something
+ // completely different than the single-value.
+ // Pretend we don't have the range in the former
+ // case.
+ //
+ if (flt)
+ r.range = false;
+
+ l.next ();
+ t = l.next ();
+ }
+ }
+
+ if (t.punctuation () != sql_token::p_rparen)
+ {
+ return error (ct, "expected ')' in MySQL type declaration");
+ }
+
+ s = parse_sign;
+ continue;
+ }
+
+ s = parse_sign;
+ }
+ // Fall through.
+ case parse_sign:
+ {
+ if (tt == sql_token::t_identifier &&
+ context::upcase (t.identifier ()) == "UNSIGNED")
+ {
+ r.unsign = true;
+ }
+
+ s = parse_done;
+ break;
+ }
+ case parse_done:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+
+ if (s == parse_name && !prefix.empty ())
+ {
+ // Some prefixes can also be type names if not followed
+ // by the actual type name.
+ //
+ if (prefix == "CHAR" || prefix == "CHARACTER")
+ {
+ r.type = sql_type::CHAR;
+ }
+ else if (prefix == "LONG")
+ {
+ r.type = sql_type::MEDIUMTEXT;
+ }
+ }
+
+ if (flt)
+ {
+ r.type = !r.range || r.range_value < 24
+ ? sql_type::FLOAT
+ : sql_type::DOUBLE;
+ }
+
+ if (r.type == sql_type::invalid)
+ return error (ct, "incomplete MySQL type declaration");
+
+ // If range is omitted for CHAR or BIT types, it defaults to 1.
+ //
+ if ((r.type == sql_type::CHAR || r.type == sql_type::BIT) && !r.range)
+ {
+ r.range = true;
+ r.range_value = 1;
+ }
+
+ return r;
+ }
+ catch (sql_lexer::invalid_input const& e)
+ {
+ return error (ct, "invalid MySQL type declaration: " + e.message);
+ }
+ }
+ }
+}
diff --git a/odb/odb/relational/mysql/context.hxx b/odb/odb/relational/mysql/context.hxx
new file mode 100644
index 0000000..98574f2
--- /dev/null
+++ b/odb/odb/relational/mysql/context.hxx
@@ -0,0 +1,194 @@
+// file : odb/relational/mysql/context.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_MYSQL_CONTEXT_HXX
+#define ODB_RELATIONAL_MYSQL_CONTEXT_HXX
+
+#include <map>
+#include <vector>
+
+#include <odb/relational/context.hxx>
+
+namespace relational
+{
+ namespace mysql
+ {
+ struct sql_type
+ {
+ // Keep the order in each block of types.
+ //
+ enum core_type
+ {
+ // Integral types.
+ //
+ TINYINT,
+ SMALLINT,
+ MEDIUMINT,
+ INT,
+ BIGINT,
+
+ // Float types.
+ //
+ FLOAT,
+ DOUBLE,
+ DECIMAL,
+
+ // Data-time types.
+ //
+ DATE,
+ TIME,
+ DATETIME,
+ TIMESTAMP,
+ YEAR,
+
+ // String and binary types.
+ //
+ CHAR,
+ BINARY,
+ VARCHAR,
+ VARBINARY,
+ TINYTEXT,
+ TINYBLOB,
+ TEXT,
+ BLOB,
+ MEDIUMTEXT,
+ MEDIUMBLOB,
+ LONGTEXT,
+ LONGBLOB,
+
+ // Other types.
+ //
+ BIT,
+ ENUM,
+ SET,
+
+ // Invalid type.
+ //
+ invalid
+ };
+
+ sql_type () : type (invalid), unsign (false), range (false) {}
+
+ core_type type;
+ bool unsign;
+ bool range;
+ unsigned int range_value; // MySQL max value is 2^32 - 1 (LONGBLOG/TEXT).
+ std::vector<std::string> enumerators; // Enumerator strings for ENUM.
+
+ // Conversion expressions for custom database types.
+ //
+ std::string to;
+ std::string from;
+ };
+
+ class context: public virtual relational::context
+ {
+ public:
+ sql_type const&
+ parse_sql_type (string const&,
+ semantics::data_member&,
+ bool custom = true);
+ public:
+ struct invalid_sql_type
+ {
+ invalid_sql_type (string const& message): message_ (message) {}
+
+ string const&
+ message () const {return message_;}
+
+ private:
+ string message_;
+ };
+
+ // If custom_db_types is NULL, then this function returns
+ // invalid type instead of throwing in case an unknown type
+ // is encountered.
+ //
+ static sql_type
+ parse_sql_type (string, custom_db_types const* = 0);
+
+ protected:
+ virtual string const&
+ convert_expr (string const&, semantics::data_member&, bool);
+
+ virtual bool
+ grow_impl (semantics::class_&, user_section*);
+
+ virtual bool
+ grow_impl (semantics::data_member&);
+
+ virtual bool
+ grow_impl (semantics::data_member&,
+ semantics::type&,
+ const custom_cxx_type*,
+ string const&);
+
+ protected:
+ virtual string
+ quote_id_impl (qname const&) const;
+
+ protected:
+ virtual string
+ database_type_impl (semantics::type&, semantics::names*, bool, bool*);
+
+ public:
+ virtual
+ ~context ();
+ context ();
+ context (std::ostream&,
+ semantics::unit&,
+ options_type const&,
+ features_type&,
+ sema_rel::model*);
+
+ static context&
+ current ()
+ {
+ return *current_;
+ }
+
+ private:
+ static context* current_;
+
+ private:
+ struct data: base_context::data
+ {
+ data (std::ostream& os): base_context::data (os) {}
+
+ struct sql_type_cache_entry
+ {
+ sql_type_cache_entry ()
+ : custom_cached (false), straight_cached (false) {}
+
+ sql_type const&
+ cache_custom (sql_type const& t)
+ {
+ custom = t;
+ custom_cached = true;
+ return custom;
+ }
+
+ sql_type const&
+ cache_straight (sql_type const& t)
+ {
+ straight = t;
+ straight_cached = true;
+ return straight;
+ }
+
+ sql_type custom; // With custom mapping.
+ sql_type straight; // Without custom mapping.
+
+ bool custom_cached;
+ bool straight_cached;
+ };
+
+ typedef std::map<string, sql_type_cache_entry> sql_type_cache;
+ sql_type_cache sql_type_cache_;
+ };
+ data* data_;
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_MYSQL_CONTEXT_HXX
diff --git a/odb/odb/relational/mysql/header.cxx b/odb/odb/relational/mysql/header.cxx
new file mode 100644
index 0000000..27bae48
--- /dev/null
+++ b/odb/odb/relational/mysql/header.cxx
@@ -0,0 +1,136 @@
+// file : odb/relational/mysql/header.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/header.hxx>
+
+#include <odb/relational/mysql/common.hxx>
+#include <odb/relational/mysql/context.hxx>
+
+namespace relational
+{
+ namespace mysql
+ {
+ namespace header
+ {
+ namespace relational = relational::header;
+
+ struct image_member: relational::image_member_impl<sql_type>,
+ member_base
+ {
+ image_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x) {}
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "my_bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "my_bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_decimal (member_info& mi)
+ {
+ // Exchanged as strings. Can have up to 65 digits not counting
+ // '-' and '.'. If range is not specified, the default is 10.
+ //
+
+ /*
+ @@ Disabled.
+ os << "char " << mi.var << "value[" <<
+ (t.range ? t.range_value : 10) + 3 << "];"
+ */
+
+ os << image_type << " " << mi.var << "value;"
+ << "unsigned long " << mi.var << "size;"
+ << "my_bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_date_time (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "my_bool " << mi.var << "null;"
+ << endl;
+
+ }
+
+ virtual void
+ traverse_short_string (member_info& mi)
+ {
+ // If range is not specified, the default buffer size is 255.
+ //
+ /*
+ @@ Disabled.
+ os << "char " << mi.var << "value[" <<
+ (t.range ? t.range_value : 255) + 1 << "];"
+ */
+
+ os << image_type << " " << mi.var << "value;"
+ << "unsigned long " << mi.var << "size;"
+ << "my_bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_long_string (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "unsigned long " << mi.var << "size;"
+ << "my_bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_bit (member_info& mi)
+ {
+ // Valid range is 1 to 64.
+ //
+ unsigned int n (mi.st->range / 8 + (mi.st->range % 8 ? 1 : 0));
+
+ os << "unsigned char " << mi.var << "value[" << n << "];"
+ << "unsigned long " << mi.var << "size;"
+ << "my_bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_enum (member_info& mi)
+ {
+ // Represented as either integer or string. Since we don't know
+ // at the code generation time which one it is, we have to always
+ // keep size in case it is a string.
+ //
+ os << image_type << " " << mi.var << "value;"
+ << "unsigned long " << mi.var << "size;"
+ << "my_bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_set (member_info& mi)
+ {
+ // Represented as string.
+ //
+ os << image_type << " " << mi.var << "value;"
+ << "unsigned long " << mi.var << "size;"
+ << "my_bool " << mi.var << "null;"
+ << endl;
+ }
+ };
+ entry<image_member> image_member_;
+ }
+ }
+}
diff --git a/odb/odb/relational/mysql/inline.cxx b/odb/odb/relational/mysql/inline.cxx
new file mode 100644
index 0000000..bfa2c94
--- /dev/null
+++ b/odb/odb/relational/mysql/inline.cxx
@@ -0,0 +1,42 @@
+// file : odb/relational/mysql/inline.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/inline.hxx>
+
+#include <odb/relational/mysql/common.hxx>
+#include <odb/relational/mysql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace mysql
+ {
+ namespace inline_
+ {
+ namespace relational = relational::inline_;
+
+ struct null_member: relational::null_member_impl<sql_type>,
+ member_base
+ {
+ null_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ traverse_simple (member_info& mi)
+ {
+ if (get_)
+ os << "r = r && i." << mi.var << "null;";
+ else
+ os << "i." << mi.var << "null = 1;";
+ }
+ };
+ entry<null_member> null_member_;
+ }
+ }
+}
diff --git a/odb/odb/relational/mysql/model.cxx b/odb/odb/relational/mysql/model.cxx
new file mode 100644
index 0000000..17ed4c0
--- /dev/null
+++ b/odb/odb/relational/mysql/model.cxx
@@ -0,0 +1,161 @@
+// file : odb/relational/mysql/model.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <odb/relational/model.hxx>
+#include <odb/relational/mysql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace mysql
+ {
+ namespace model
+ {
+ namespace relational = relational::model;
+
+ struct object_columns: relational::object_columns, context
+ {
+ object_columns (base const& x): base (x) {}
+
+ virtual string
+ default_bool (semantics::data_member&, bool v)
+ {
+ // MySQL has TRUE and FALSE as just aliases for 1 and 0. Still
+ // use them for self-documentation.
+ //
+ return v ? "TRUE" : "FALSE";
+ }
+
+ virtual string
+ default_enum (semantics::data_member& m, tree en, string const& name)
+ {
+ // Make sure the column is mapped to an ENUM or integer type.
+ //
+ sql_type const& t (parse_sql_type (column_type (), m, false));
+
+ switch (t.type)
+ {
+ case sql_type::ENUM:
+ case sql_type::TINYINT:
+ case sql_type::SMALLINT:
+ case sql_type::MEDIUMINT:
+ case sql_type::INT:
+ case sql_type::BIGINT:
+ break;
+ default:
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: column with default value specified as C++ "
+ << "enumerator must map to MySQL ENUM or integer type"
+ << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ using semantics::enum_;
+ using semantics::enumerator;
+
+ enumerator& er (dynamic_cast<enumerator&> (*unit.find (en)));
+ enum_& e (er.enum_ ());
+
+ if (t.type == sql_type::ENUM)
+ {
+ // Assuming the enumerators in the C++ enum and MySQL ENUM are
+ // in the same order, calculate the poistion of the C++
+ // enumerator and use that as an index in the MySQL ENUM.
+ //
+ size_t pos (0);
+
+ for (enum_::enumerates_iterator i (e.enumerates_begin ()),
+ end (e.enumerates_end ()); i != end; ++i)
+ {
+ if (&i->enumerator () == &er)
+ break;
+
+ pos++;
+ }
+
+ if (pos < t.enumerators.size ())
+ return t.enumerators[pos];
+ else
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: unable to map C++ enumerator '" << name
+ << "' to MySQL ENUM value" << endl;
+
+ throw operation_failed ();
+ }
+ }
+ else
+ {
+ ostringstream ostr;
+
+ if (e.unsigned_ ())
+ ostr << er.value ();
+ else
+ ostr << static_cast<long long> (er.value ());
+
+ return ostr.str ();
+ }
+ }
+ };
+ entry<object_columns> object_columns_;
+
+ struct member_create: relational::member_create, context
+ {
+ member_create (base const& x): base (x) {}
+
+ virtual string
+ table_options (semantics::data_member& m, semantics::type& c)
+ {
+ string r (relational::member_create::table_options (m, c));
+
+ string const& engine (options.mysql_engine ());
+ if (engine != "default")
+ {
+ // Note: MySQL table options can be separated with spaces.
+ //
+ if (!r.empty ())
+ r += ' ';
+
+ r += "ENGINE=";
+ r += engine;
+ }
+
+ return r;
+ }
+ };
+ entry<member_create> member_create_;
+
+ struct class_: relational::class_, context
+ {
+ class_ (base const& x): base (x) {}
+
+ virtual string
+ table_options (type& c)
+ {
+ string r (relational::class_::table_options (c));
+
+ string const& engine (options.mysql_engine ());
+ if (engine != "default")
+ {
+ // Note: MySQL table options can be separated with spaces.
+ //
+ if (!r.empty ())
+ r += ' ';
+
+ r += "ENGINE=";
+ r += engine;
+ }
+
+ return r;
+ }
+ };
+ entry<class_> class__;
+ }
+ }
+}
diff --git a/odb/odb/relational/mysql/schema.cxx b/odb/odb/relational/mysql/schema.cxx
new file mode 100644
index 0000000..60dc95b
--- /dev/null
+++ b/odb/odb/relational/mysql/schema.cxx
@@ -0,0 +1,489 @@
+// file : odb/relational/mysql/schema.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/schema.hxx>
+
+#include <odb/relational/mysql/common.hxx>
+#include <odb/relational/mysql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace mysql
+ {
+ namespace schema
+ {
+ namespace relational = relational::schema;
+ using relational::table_set;
+
+ //
+ // Drop.
+ //
+
+ struct drop_foreign_key: relational::drop_foreign_key, context
+ {
+ drop_foreign_key (base const& x): base (x) {}
+
+ virtual void
+ drop (sema_rel::table& t, sema_rel::foreign_key& fk)
+ {
+ /*
+ // @@ This does not work: in MySQL control statements can only
+ // be used in stored procedures. It seems the only way to
+ // implement this is to define, execute, and drop a stored
+ // procedure, which is just too ugly.
+ //
+ // Another option would be to use CREATE TABLE IF NOT EXISTS
+ // to create a dummy table with a dummy constraint that makes
+ // the following DROP succeed. Note, however, that MySQL issues
+ // a notice if the table already exist so would need to suppress
+ // that as well. Still not sure that the utility of this support
+ // justifies this kind of a hack.
+ //
+ os << "IF EXISTS (SELECT NULL FROM information_schema.TABLE_CONSTRAINTS" << endl
+ << " WHERE CONSTRAINT_TYPE = " << quote_string ("FOREIGN KEY") << "AND" << endl
+ << " CONSTRAINT_SCHEMA = DATABASE() AND" << endl
+ << " CONSTRAINT_NAME = " << quote_string (fk.name ()) << ") THEN" << endl
+ << " ALTER TABLE " << quote_id (t.name ()) << " DROP FOREIGN KEY " << quote_id (fk.name ()) << ";" << endl
+ << "END IF;" << endl;
+ */
+
+ // So for now we only do this in migration.
+ //
+ if (dropped_ == 0)
+ {
+ if (fk.not_deferrable ())
+ pre_statement ();
+ else
+ {
+ if (format_ != schema_format::sql)
+ return;
+
+ os << "/*" << endl;
+ }
+
+ os << "ALTER TABLE " << quote_id (t.name ()) << endl
+ << " DROP FOREIGN KEY " << quote_id (fk.name ()) << endl;
+
+ if (fk.not_deferrable ())
+ post_statement ();
+ else
+ os << "*/" << endl
+ << endl;
+ }
+ }
+
+ using base::drop;
+
+ virtual void
+ traverse (sema_rel::drop_foreign_key& dfk)
+ {
+ // Find the foreign key we are dropping in the base model.
+ //
+ sema_rel::foreign_key& fk (find<sema_rel::foreign_key> (dfk));
+
+ if (fk.not_deferrable () || in_comment)
+ base::traverse (dfk);
+ else
+ {
+ if (format_ != schema_format::sql)
+ return;
+
+ os << endl
+ << " /*"
+ << endl;
+
+ drop (dfk);
+
+ os << endl
+ << " */";
+ }
+ }
+
+ virtual void
+ drop_header ()
+ {
+ os << "DROP FOREIGN KEY ";
+ }
+ };
+ entry<drop_foreign_key> drop_foreign_key_;
+
+ struct drop_index: relational::drop_index, context
+ {
+ drop_index (base const& x): base (x) {}
+
+ virtual void
+ drop (sema_rel::index& in)
+ {
+ sema_rel::table& t (static_cast<sema_rel::table&> (in.scope ()));
+
+ os << "DROP INDEX " << name (in) << " ON " <<
+ quote_id (t.name ()) << endl;
+ }
+ };
+ entry<drop_index> drop_index_;
+
+ //
+ // Create.
+ //
+
+ struct create_column: relational::create_column, context
+ {
+ create_column (base const& x): base (x) {}
+
+ virtual void
+ auto_ (sema_rel::primary_key&)
+ {
+ os << " AUTO_INCREMENT";
+ }
+ };
+ entry<create_column> create_column_;
+
+ struct create_foreign_key: relational::create_foreign_key, context
+ {
+ create_foreign_key (base const& x): base (x) {}
+
+ void
+ diagnose (sema_rel::foreign_key& fk)
+ {
+ if (fk.on_delete () != sema_rel::foreign_key::no_action)
+ {
+ cerr << "warning: foreign key '" << fk.name () << "' has " <<
+ "ON DELETE clause but is disabled in MySQL due to lack "
+ "of deferrable constraint support" << endl;
+
+ cerr << "info: consider using non-deferrable foreign keys (" <<
+ "--fkeys-deferrable-mode)" << endl;
+ }
+ }
+
+ virtual void
+ traverse_create (sema_rel::foreign_key& fk)
+ {
+ // MySQL does not support deferrable constraint checking. Output
+ // such foreign keys as comments, for documentation, unless we
+ // are generating embedded schema.
+ //
+ if (fk.not_deferrable ())
+ base::traverse_create (fk);
+ else
+ {
+ diagnose (fk);
+
+ // Don't bloat C++ code with comment strings if we are
+ // generating embedded schema.
+ //
+ if (format_ != schema_format::sql)
+ return;
+
+ os << endl
+ << " /*" << endl
+ << " CONSTRAINT ";
+ create (fk);
+ os << endl
+ << " */";
+ }
+ }
+
+ virtual void
+ traverse_add (sema_rel::foreign_key& fk)
+ {
+ if (fk.not_deferrable () || in_comment)
+ base::traverse_add (fk);
+ else
+ {
+ diagnose (fk);
+
+ if (format_ != schema_format::sql)
+ return;
+
+ os << endl
+ << " /*"
+ << endl;
+
+ add (fk);
+
+ os << endl
+ << " */";
+ }
+ }
+
+ virtual void
+ deferrable (sema_rel::deferrable)
+ {
+ // This will still be called to output the comment.
+ }
+ };
+ entry<create_foreign_key> create_foreign_key_;
+
+ struct create_index: relational::create_index, context
+ {
+ create_index (base const& x): base (x) {}
+
+ virtual void
+ create (sema_rel::index& in)
+ {
+ os << "CREATE ";
+
+ if (!in.type ().empty ())
+ os << in.type () << ' ';
+
+ os << "INDEX " << name (in);
+
+ if (!in.method ().empty ())
+ os << " USING " << in.method ();
+
+ os << endl
+ << " ON " << table_name (in) << " (";
+
+ columns (in);
+
+ os << ")" << endl;
+
+ if (!in.options ().empty ())
+ os << ' ' << in.options () << endl;
+ }
+ };
+ entry<create_index> create_index_;
+
+ struct create_table: relational::create_table, context
+ {
+ create_table (base const& x): base (x) {}
+
+ // See if there are any undefined foreign keys that are not
+ // deferrable.
+ //
+ bool
+ check_undefined_fk_deferrable_only (sema_rel::table& t)
+ {
+ for (sema_rel::table::names_iterator i (t.names_begin ());
+ i != t.names_end (); ++i)
+ {
+ using sema_rel::foreign_key;
+
+ if (foreign_key* fk = dynamic_cast<foreign_key*> (&i->nameable ()))
+ {
+ if (!fk->count ("mysql-fk-defined") &&
+ fk->not_deferrable ())
+ return false;
+ }
+ }
+ return true;
+ }
+
+ virtual void
+ traverse (sema_rel::table& t)
+ {
+ if (pass_ == 1)
+ base::traverse (t);
+ else
+ {
+ // Add undefined foreign keys.
+ //
+ if (check_undefined_fk (t))
+ {
+ bool deferrable (check_undefined_fk_deferrable_only (t));
+
+ if (!deferrable || format_ == schema_format::sql)
+ {
+ if (deferrable)
+ {
+ os << "/*" << endl;
+ in_comment = true;
+ }
+ else
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (t.name ());
+
+ instance<create_foreign_key> cfk (*this);
+ trav_rel::unames n (*cfk);
+ names (t, n);
+ os << endl;
+
+ if (deferrable)
+ {
+ in_comment = false;
+ os << "*/" << endl
+ << endl;
+ }
+ else
+ post_statement ();
+ }
+ }
+ }
+ }
+ };
+ entry<create_table> create_table_;
+
+ //
+ // Alter.
+ //
+
+ struct alter_column: relational::alter_column, context
+ {
+ alter_column (base const& x): base (x) {}
+
+ virtual void
+ alter_header ()
+ {
+ os << "MODIFY COLUMN ";
+ }
+ };
+ entry<alter_column> alter_column_;
+
+ struct alter_table_pre: relational::alter_table_pre, context
+ {
+ alter_table_pre (base const& x): base (x) {}
+
+ // Check if we are only dropping deferrable foreign keys.
+ //
+ bool
+ check_drop_deferrable_only (sema_rel::alter_table& at)
+ {
+ if (check<sema_rel::add_column> (at) ||
+ check_alter_column_null (at, true))
+ return false;
+
+ for (sema_rel::alter_table::names_iterator i (at.names_begin ());
+ i != at.names_end (); ++i)
+ {
+ using sema_rel::foreign_key;
+ using sema_rel::drop_foreign_key;
+
+ if (drop_foreign_key* dfk =
+ dynamic_cast<drop_foreign_key*> (&i->nameable ()))
+ {
+ foreign_key& fk (find<foreign_key> (*dfk));
+
+ if (fk.not_deferrable ())
+ return false;
+ }
+ }
+ return true;
+ }
+
+ virtual void
+ alter (sema_rel::alter_table& at)
+ {
+ if (check_drop_deferrable_only (at))
+ {
+ if (format_ != schema_format::sql)
+ return;
+
+ os << "/*" << endl;
+ in_comment = true;
+
+ os << "ALTER TABLE " << quote_id (at.name ());
+ instance<drop_foreign_key> dfk (*this);
+ trav_rel::unames n (*dfk);
+ names (at, n);
+ os << endl;
+
+ in_comment = false;
+ os << "*/" << endl
+ << endl;
+ }
+ else
+ base::alter (at);
+ }
+ };
+ entry<alter_table_pre> alter_table_pre_;
+
+ struct alter_table_post: relational::alter_table_post, context
+ {
+ alter_table_post (base const& x): base (x) {}
+
+ // Check if we are only adding deferrable foreign keys.
+ //
+ bool
+ check_add_deferrable_only (sema_rel::alter_table& at)
+ {
+ if (check<sema_rel::drop_column> (at) ||
+ check_alter_column_null (at, false))
+ return false;
+
+ for (sema_rel::alter_table::names_iterator i (at.names_begin ());
+ i != at.names_end (); ++i)
+ {
+ using sema_rel::add_foreign_key;
+
+ if (add_foreign_key* afk =
+ dynamic_cast<add_foreign_key*> (&i->nameable ()))
+ {
+ if (afk->not_deferrable ())
+ return false;
+ }
+ }
+ return true;
+ }
+
+ virtual void
+ alter (sema_rel::alter_table& at)
+ {
+ if (check_add_deferrable_only (at))
+ {
+ if (format_ != schema_format::sql)
+ return;
+
+ os << "/*" << endl;
+ in_comment = true;
+
+ os << "ALTER TABLE " << quote_id (at.name ());
+ instance<create_foreign_key> cfk (*this);
+ trav_rel::unames n (*cfk);
+ names (at, n);
+ os << endl;
+
+ in_comment = false;
+ os << "*/" << endl
+ << endl;
+ }
+ else
+ base::alter (at);
+ }
+ };
+ entry<alter_table_post> alter_table_post_;
+
+ //
+ // Schema version table.
+ //
+
+ struct version_table: relational::version_table, context
+ {
+ version_table (base const& x): base (x) {}
+
+ virtual void
+ create_table ()
+ {
+ pre_statement ();
+
+ os << "CREATE TABLE IF NOT EXISTS " << qt_ << " (" << endl
+ << " " << qn_ << " VARCHAR(128) NOT NULL PRIMARY KEY," << endl
+ << " " << qv_ << " BIGINT UNSIGNED NOT NULL," << endl
+ << " " << qm_ << " TINYINT(1) NOT NULL)" << endl;
+
+ string const& engine (options.mysql_engine ());
+ if (engine != "default")
+ os << " ENGINE=" << engine << endl;
+
+ post_statement ();
+ }
+
+ virtual void
+ create (sema_rel::version v)
+ {
+ pre_statement ();
+
+ os << "INSERT IGNORE INTO " << qt_ << " (" << endl
+ << " " << qn_ << ", " << qv_ << ", " << qm_ << ")" << endl
+ << " VALUES (" << qs_ << ", " << v << ", 0)" << endl;
+
+ post_statement ();
+ }
+ };
+ entry<version_table> version_table_;
+ }
+ }
+}
diff --git a/odb/odb/relational/mysql/source.cxx b/odb/odb/relational/mysql/source.cxx
new file mode 100644
index 0000000..9131ea7
--- /dev/null
+++ b/odb/odb/relational/mysql/source.cxx
@@ -0,0 +1,724 @@
+// file : odb/relational/mysql/source.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/source.hxx>
+
+#include <odb/relational/mysql/common.hxx>
+#include <odb/relational/mysql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace mysql
+ {
+ namespace source
+ {
+ namespace relational = relational::source;
+
+ namespace
+ {
+ const char* integer_buffer_types[] =
+ {
+ "MYSQL_TYPE_TINY",
+ "MYSQL_TYPE_SHORT",
+ "MYSQL_TYPE_LONG", // *_bind_param() doesn't support INT24.
+ "MYSQL_TYPE_LONG",
+ "MYSQL_TYPE_LONGLONG"
+ };
+
+ const char* float_buffer_types[] =
+ {
+ "MYSQL_TYPE_FLOAT",
+ "MYSQL_TYPE_DOUBLE"
+ };
+
+ const char* date_time_buffer_types[] =
+ {
+ "MYSQL_TYPE_DATE",
+ "MYSQL_TYPE_TIME",
+ "MYSQL_TYPE_DATETIME",
+ "MYSQL_TYPE_TIMESTAMP",
+ "MYSQL_TYPE_SHORT"
+ };
+
+ const char* char_bin_buffer_types[] =
+ {
+ "MYSQL_TYPE_STRING", // CHAR
+ "MYSQL_TYPE_BLOB", // BINARY,
+ "MYSQL_TYPE_STRING", // VARCHAR
+ "MYSQL_TYPE_BLOB", // VARBINARY
+ "MYSQL_TYPE_STRING", // TINYTEXT
+ "MYSQL_TYPE_BLOB", // TINYBLOB
+ "MYSQL_TYPE_STRING", // TEXT
+ "MYSQL_TYPE_BLOB", // BLOB
+ "MYSQL_TYPE_STRING", // MEDIUMTEXT
+ "MYSQL_TYPE_BLOB", // MEDIUMBLOB
+ "MYSQL_TYPE_STRING", // LONGTEXT
+ "MYSQL_TYPE_BLOB" // LONGBLOB
+ };
+ }
+
+ //
+ //
+ struct object_columns: relational::object_columns, context
+ {
+ object_columns (base const& x): base (x) {}
+
+ virtual bool
+ column (semantics::data_member& m,
+ string const& table,
+ string const& column)
+ {
+ // When we store a ENUM column in the MySQL database, if we bind
+ // an integer parameter, then it is treated as an index and if we
+ // bind a string, then it is treated as a enumerator. Everything
+ // would have worked well if the same logic applied to the select
+ // operation. That is, if we bind integer, then the database sends
+ // the index and if we bind string then the database sends the
+ // enumerator. Unfortunately, MySQL always sends the enumerator
+ // and to get the index one has to resort to the enum+0 hack.
+ //
+ // This causes the following problem: at code generation time we
+ // do not yet know which format we want. This is determined at
+ // C++ compile time by traits (the reason we don't know this is
+ // because we don't want to drag database-specific runtimes,
+ // which define the necessary traits, as well as their
+ // prerequisites into the ODB compilation process). As a result,
+ // we cannot decide at code generation time whether we need the
+ // +0 hack or not. One way to overcome this would be to construct
+ // the SELECT statements at runtime, something along these lines:
+ //
+ // "enum" + enum_traits<type>::hack + ","
+ //
+ // However, this complicates the code generator quite a bit: we
+ // either have to move to std::string storage for all the
+ // statements and all the databases, which is kind of a waste,
+ // or do some deep per-database customizations, which is hairy.
+ // So, instead, we are going to use another hack (hey, what the
+ // hell, right?) by loading both the index and enumerator
+ // combined into a string:
+ //
+ // CONCAT (enum+0, ' ', enum)
+ //
+ // For cases where we need the index, everything works since
+ // MySQL will convert the leading number and stop at the space.
+ // For cases where we need the enumerator, we do a bit of pre-
+ // processing (see enum_traits) before handing the value off
+ // to value_traits.
+ //
+
+ string const& type (column_type ());
+
+ if (sk_ != statement_select ||
+ parse_sql_type (type, m).type != sql_type::ENUM)
+ {
+ return base::column (m, table, column);
+ }
+
+ // Qualified column and conversion expression.
+ //
+ string qc;
+ if (!table.empty ())
+ {
+ qc += table;
+ qc += '.';
+ }
+ qc += column;
+ qc = convert_from (qc, type, m);
+
+ string r ("CONCAT(" + qc + "+0,' '," + qc + ")");
+
+ sc_.push_back (
+ relational::statement_column (table, r, type, m, key_prefix_));
+ return true;
+ }
+ };
+ entry<object_columns> object_columns_;
+
+ struct view_columns: relational::view_columns, context
+ {
+ view_columns (base const& x): base (x) {}
+
+ virtual bool
+ column (semantics::data_member& m,
+ string const& table,
+ string const& column)
+ {
+ // The same idea as in object_columns.
+ //
+ string const& type (column_type ());
+
+ if (parse_sql_type (type, m).type != sql_type::ENUM)
+ {
+ return base::column (m, table, column);
+ }
+
+ // Column and conversion expression.
+ //
+ string c (convert_from (column, type, m));
+
+ string r ("CONCAT(" + c + "+0,' '," + c + ")");
+ sc_.push_back (relational::statement_column (table, r, type, m));
+ return true;
+ }
+ };
+ entry<view_columns> view_columns_;
+
+ //
+ // bind
+ //
+
+ struct bind_member: relational::bind_member_impl<sql_type>,
+ member_base
+ {
+ bind_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ // While the is_unsigned should indicate whether the
+ // buffer variable is unsigned, rather than whether the
+ // database type is unsigned, in case of the image types,
+ // this is the same.
+ //
+ os << b << ".buffer_type = " <<
+ integer_buffer_types[mi.st->type - sql_type::TINYINT] << ";"
+ << b << ".is_unsigned = " << (mi.st->unsign ? "1" : "0") << ";"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << b << ".buffer_type = " <<
+ float_buffer_types[mi.st->type - sql_type::FLOAT] << ";"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_decimal (member_info& mi)
+ {
+ os << b << ".buffer_type = MYSQL_TYPE_NEWDECIMAL;"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data ();"
+ << b << ".buffer_length = static_cast<unsigned long> (" << endl
+ << arg << "." << mi.var << "value.capacity ());"
+ << b << ".length = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_date_time (member_info& mi)
+ {
+ os << b << ".buffer_type = " <<
+ date_time_buffer_types[mi.st->type - sql_type::DATE] << ";"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;";
+
+ if (mi.st->type == sql_type::YEAR)
+ os << b << ".is_unsigned = 0;";
+
+ os << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_short_string (member_info& mi)
+ {
+ // MySQL documentation is quite confusing about the use of
+ // buffer_length and length when it comes to input parameters.
+ // Source code, however, tells us that it uses buffer_length
+ // only if length is NULL.
+ //
+ os << b << ".buffer_type = " <<
+ char_bin_buffer_types[mi.st->type - sql_type::CHAR] << ";"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data ();"
+ << b << ".buffer_length = static_cast<unsigned long> (" << endl
+ << arg << "." << mi.var << "value.capacity ());"
+ << b << ".length = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_long_string (member_info& mi)
+ {
+ os << b << ".buffer_type = " <<
+ char_bin_buffer_types[mi.st->type - sql_type::CHAR] << ";"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data ();"
+ << b << ".buffer_length = static_cast<unsigned long> (" << endl
+ << arg << "." << mi.var << "value.capacity ());"
+ << b << ".length = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_bit (member_info& mi)
+ {
+ // Treated as a BLOB.
+ //
+ os << b << ".buffer_type = MYSQL_TYPE_BLOB;"
+ << b << ".buffer = " << arg << "." << mi.var << "value;"
+ << b << ".buffer_length = static_cast<unsigned long> (" << endl
+ << "sizeof (" << arg << "." << mi.var << "value));"
+ << b << ".length = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_enum (member_info& mi)
+ {
+ // Represented as either integer or string.
+ //
+ os << "mysql::enum_traits::bind (" << b << "," << endl
+ << arg << "." << mi.var << "value," << endl
+ << arg << "." << mi.var << "size," << endl
+ << "&" << arg << "." << mi.var << "null);";
+ }
+
+ virtual void
+ traverse_set (member_info& mi)
+ {
+ // Represented as a string.
+ //
+ os << b << ".buffer_type = MYSQL_TYPE_STRING;"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data ();"
+ << b << ".buffer_length = static_cast<unsigned long> (" << endl
+ << arg << "." << mi.var << "value.capacity ());"
+ << b << ".length = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+ };
+ entry<bind_member> bind_member_;
+
+ //
+ // grow
+ //
+
+ struct grow_member: relational::grow_member_impl<sql_type>,
+ member_base
+ {
+ grow_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x) {}
+
+ virtual void
+ traverse_integer (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+
+ virtual void
+ traverse_float (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+
+ virtual void
+ traverse_decimal (member_info& mi)
+ {
+ // @@ Optimization disabled.
+ //
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
+ << "grew = true;"
+ << "}";
+ }
+
+ virtual void
+ traverse_date_time (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+
+ virtual void
+ traverse_short_string (member_info& mi)
+ {
+ // @@ Optimization disabled.
+ //
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
+ << "grew = true;"
+ << "}";
+ }
+
+ virtual void
+ traverse_long_string (member_info& mi)
+ {
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
+ << "grew = true;"
+ << "}";
+ }
+
+ virtual void
+ traverse_bit (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+
+ virtual void
+ traverse_enum (member_info& mi)
+ {
+ // Represented as either integer or string (and we don't know
+ // at the code generation time which one it is).
+ //
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "if (mysql::enum_traits::grow (" <<
+ "i." << mi.var << "value, " <<
+ "i." << mi.var << "size))" << endl
+ << "grew = true;" // String
+ << "else" << endl
+ << e << " = 0;" // Integer.
+ << "}";
+ }
+
+ virtual void
+ traverse_set (member_info& mi)
+ {
+ // Represented as a string.
+ //
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
+ << "grew = true;"
+ << "}";
+ }
+ };
+ entry<grow_member> grow_member_;
+
+ //
+ // init image
+ //
+
+ struct init_image_member: relational::init_image_member_impl<sql_type>,
+ member_base
+ {
+ init_image_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ set_null (member_info& mi)
+ {
+ os << "i." << mi.var << "null = 1;";
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+
+ virtual void
+ traverse_decimal (member_info& mi)
+ {
+ // @@ Optimization: can remove growth check if buffer is fixed.
+ //
+ os << "std::size_t size (0);"
+ << "std::size_t cap (i." << mi.var << "value.capacity ());"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;"
+ << "i." << mi.var << "size = static_cast<unsigned long> (size);"
+ << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ }
+
+ virtual void
+ traverse_date_time (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+
+ virtual void
+ traverse_short_string (member_info& mi)
+ {
+ // @@ Optimization: can remove growth check if buffer is fixed.
+ //
+ os << "std::size_t size (0);"
+ << "std::size_t cap (i." << mi.var << "value.capacity ());"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;"
+ << "i." << mi.var << "size = static_cast<unsigned long> (size);"
+ << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ }
+
+ virtual void
+ traverse_long_string (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << "std::size_t cap (i." << mi.var << "value.capacity ());"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;"
+ << "i." << mi.var << "size = static_cast<unsigned long> (size);"
+ << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ }
+
+ virtual void
+ traverse_bit (member_info& mi)
+ {
+ // Represented as a BLOB.
+ //
+ os << "std::size_t size (0);"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "sizeof (i." << mi.var << "value)," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;"
+ << "i." << mi.var << "size = static_cast<unsigned long> (size);";
+ }
+
+ virtual void
+ traverse_enum (member_info& mi)
+ {
+ // Represented as either integer or string.
+ //
+ os << "if (mysql::enum_traits::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "is_null," << endl
+ << member << "))" << endl
+ << "grew = true;"
+ << endl
+ << "i." << mi.var << "null = is_null;";
+ }
+
+ virtual void
+ traverse_set (member_info& mi)
+ {
+ // Represented as a string.
+ //
+ os << "std::size_t size (0);"
+ << "std::size_t cap (i." << mi.var << "value.capacity ());"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;"
+ << "i." << mi.var << "size = static_cast<unsigned long> (size);"
+ << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ }
+ };
+ entry<init_image_member> init_image_member_;
+
+ //
+ // init value
+ //
+
+ struct init_value_member: relational::init_value_member_impl<sql_type>,
+ member_base
+ {
+ init_value_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ get_null (string const& var) const
+ {
+ os << "i." << var << "null";
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_decimal (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_date_time (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_short_string (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_long_string (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_bit (member_info& mi)
+ {
+ // Represented as a BLOB.
+ //
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_enum (member_info& mi)
+ {
+ // Represented as either integer or string.
+ //
+ os << "mysql::enum_traits::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_set (member_info& mi)
+ {
+ // Represented as a string.
+ //
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+ };
+ entry<init_value_member> init_value_member_;
+
+ struct class_: relational::class_, context
+ {
+ class_ (base const& x): base (x) {}
+
+ virtual void
+ init_auto_id (semantics::data_member& m, string const& im)
+ {
+ // Don't set the id value to 0 if this is a nullable wrapper. This
+ // will allow the user to use the NO_AUTO_VALUE_ON_ZERO mode by
+ // making it NULL when they want the auto semantics:
+ //
+ // #pragma db auto
+ // odb::nullable<int64_t> id;
+ //
+ semantics::type& t (utype (m));
+ if (wrapper (t) && t.template get<bool> ("wrapper-null-handler"))
+ return;
+
+ os << im << "value = 0;"
+ << endl;
+ }
+
+ virtual string
+ join_syntax (view_object const& vo)
+ {
+ if (vo.join == view_object::full)
+ {
+ error (vo.loc)
+ << "FULL OUTER JOIN is not supported by MySQL" << endl;
+ throw operation_failed ();
+ }
+
+ return base::join_syntax (vo);
+ }
+ };
+ entry<class_> class_entry_;
+
+ struct include: relational::include, context
+ {
+ include (base const& x): base (x) {}
+
+ virtual void
+ extra_post ()
+ {
+ os << "#include <odb/mysql/enum.hxx>" << endl;
+ }
+ };
+ entry<include> include_;
+ }
+ }
+}
diff --git a/odb/odb/relational/oracle/common.cxx b/odb/odb/relational/oracle/common.cxx
new file mode 100644
index 0000000..7caafc9
--- /dev/null
+++ b/odb/odb/relational/oracle/common.cxx
@@ -0,0 +1,522 @@
+// file : odb/relational/oracle/common.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/relational/oracle/common.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace oracle
+ {
+ //
+ // member_base
+ //
+
+ sql_type const& member_base::
+ member_sql_type (semantics::data_member& m)
+ {
+ return parse_sql_type (column_type (m, key_prefix_), m);
+ }
+
+ void member_base::
+ traverse_simple (member_info& mi)
+ {
+ switch (mi.st->type)
+ {
+ // Numeric types.
+ //
+ case sql_type::NUMBER:
+ {
+ const sql_type& st (*mi.st);
+
+ if (st.prec)
+ {
+ unsigned short r (st.prec_value);
+
+ if (!st.scale)
+ {
+ if (r <= 10)
+ traverse_int32 (mi);
+ // Only OCI versions 11.2 and later support insertion and
+ // extraction into a 64 bit integer.
+ //
+ else if (
+ (options.oracle_client_version () >= oracle_version (11, 2)) &&
+ (r <= 19 || (r == 20 && unsigned_integer (mi.t))))
+ traverse_int64 (mi);
+ else
+ traverse_big_int (mi);
+ }
+ else
+ {
+ // We can calculate the decimal exponent of the normalised
+ // floating point equivalent of the fixed point number using
+ // e = p - s, where p is the precision, s is the scale, and
+ // e the exponent. We can then use this to determine whether
+ // or not a value of Oracle SQL type NUMBER can be completely
+ // stored in the native floating point type.
+ //
+
+ // The maximum decimal precision of a float is 7 significant
+ // digits. The minimum and maximum decimal exponents
+ // representable by a float are -37 and 38 respectively.
+ //
+ if (r <= 7)
+ {
+ int e = r - st.scale_value;
+
+ if (e >= -37 && e <= 38)
+ traverse_float (mi);
+ else
+ traverse_double (mi);
+ }
+
+ // The maximum decimal precision of a double is 15 significant
+ // digits. The minimum and maximum decimal exponent representable
+ // by a double exceeds that of the Oracle NUMBER type.
+ //
+ else if (r <= 15)
+ traverse_double (mi);
+ else
+ traverse_big_float (mi);
+ }
+ }
+ else
+ // If there is no precision, then this is a floating-point number.
+ //
+ traverse_double (mi);
+
+ break;
+ }
+ case sql_type::FLOAT:
+ {
+ // We map FLOAT types based exclusively on their binary precision
+ // seeing that in 99% of cases it is the precision that is the
+ // limiting factor and not the exponent.
+ //
+ if (mi.st->prec_value <= 24)
+ traverse_float (mi);
+ else if (mi.st->prec_value <= 53)
+ traverse_double (mi);
+ else
+ traverse_big_float (mi);
+
+ break;
+ }
+ case sql_type::BINARY_FLOAT:
+ {
+ traverse_float (mi);
+ break;
+ }
+ case sql_type::BINARY_DOUBLE:
+ {
+ traverse_double (mi);
+ break;
+ }
+ // Data-time types.
+ //
+ case sql_type::DATE:
+ {
+ traverse_date (mi);
+ break;
+ }
+ case sql_type::TIMESTAMP:
+ {
+ traverse_timestamp (mi);
+ break;
+ }
+ case sql_type::INTERVAL_YM:
+ {
+ traverse_interval_ym (mi);
+ break;
+ }
+ case sql_type::INTERVAL_DS:
+ {
+ traverse_interval_ds (mi);
+ break;
+ }
+ // String and binary types.
+ //
+ case sql_type::CHAR:
+ case sql_type::NCHAR:
+ case sql_type::VARCHAR2:
+ case sql_type::NVARCHAR2:
+ case sql_type::RAW:
+ {
+ traverse_string (mi);
+ break;
+ }
+ case sql_type::BLOB:
+ case sql_type::CLOB:
+ case sql_type::NCLOB:
+ {
+ traverse_lob (mi);
+ break;
+ }
+ case sql_type::invalid:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+
+ //
+ // member_image_type
+ //
+
+ member_image_type::
+ member_image_type (base const& x)
+ : member_base::base (x), // virtual base
+ base (x) {}
+
+ member_image_type::
+ member_image_type ()
+ : relational::member_base (0, 0, string (), string ()) {}
+
+ member_image_type::
+ member_image_type (semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : relational::member_base (type, ct, fq_type, key_prefix) {}
+
+ string member_image_type::
+ image_type (semantics::data_member& m)
+ {
+ type_.clear ();
+ member_base::traverse (m, true);
+ return type_;
+ }
+
+ void member_image_type::
+ traverse_composite (member_info& mi)
+ {
+ type_ = "composite_value_traits< " + mi.fq_type () +
+ ", id_oracle >::image_type";
+ }
+
+ void member_image_type::
+ traverse_int32 (member_info& mi)
+ {
+ if (unsigned_integer (mi.t))
+ type_ = "unsigned int";
+ else
+ type_ = "int";
+ }
+
+ void member_image_type::
+ traverse_int64 (member_info& mi)
+ {
+ if (unsigned_integer (mi.t))
+ type_ = "unsigned long long";
+ else
+ type_ = "long long";
+ }
+
+ void member_image_type::
+ traverse_big_int (member_info&)
+ {
+ type_ = "char*";
+ }
+
+ void member_image_type::
+ traverse_float (member_info&)
+ {
+ type_ = "float";
+ }
+
+ void member_image_type::
+ traverse_double (member_info&)
+ {
+ type_ = "double";
+ }
+
+ void member_image_type::
+ traverse_big_float (member_info&)
+ {
+ type_ = "char*";
+ }
+
+ void member_image_type::
+ traverse_date (member_info&)
+ {
+ type_ = "char*";
+ }
+
+ void member_image_type::
+ traverse_timestamp (member_info&)
+ {
+ type_ = "oracle::datetime";
+ }
+
+ void member_image_type::
+ traverse_interval_ym (member_info&)
+ {
+ type_ = "oracle::interval_ym";
+ }
+
+ void member_image_type::
+ traverse_interval_ds (member_info&)
+ {
+ type_ = "oracle::interval_ds";
+ }
+
+ void member_image_type::
+ traverse_string (member_info&)
+ {
+ type_ = "char*";
+ }
+
+ void member_image_type::
+ traverse_lob (member_info&)
+ {
+ type_ = "oracle::lob_callback";
+ }
+
+ entry<member_image_type> member_image_type_;
+
+ //
+ // member_database_type
+ //
+
+ namespace
+ {
+ const char* string_bin_database_id[] =
+ {
+ "id_string", // CHAR
+ "id_nstring", // NCHAR
+ "id_string", // VARCHAR2
+ "id_nstring", // NVARCHAR2
+ "id_raw" // RAW
+ };
+
+ const char* lob_database_id[] =
+ {
+ "id_blob",
+ "id_clob",
+ "id_nclob"
+ };
+ }
+
+ member_database_type_id::
+ member_database_type_id (base const& x)
+ : member_base::base (x), // virtual base
+ base (x) {}
+
+ member_database_type_id::
+ member_database_type_id ()
+ : member_base::base (0, 0, string (), string ()), // virtual base
+ base (0, 0, string (), string ()) {}
+
+ member_database_type_id::
+ member_database_type_id (semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base::base (type, ct, fq_type, key_prefix), // virtual base
+ base (type, ct, fq_type, key_prefix) {}
+
+ string member_database_type_id::
+ database_type_id (type& m)
+ {
+ type_id_.clear ();
+ member_base::traverse (m, true);
+ return type_id_;
+ }
+
+ void member_database_type_id::
+ traverse_composite (member_info&)
+ {
+ assert (false);
+ }
+
+ void member_database_type_id::
+ traverse_int32 (member_info&)
+ {
+ type_id_ = "oracle::id_int32";
+ }
+
+ void member_database_type_id::
+ traverse_int64 (member_info&)
+ {
+ type_id_ = "oracle::id_int64";
+ }
+
+ void member_database_type_id::
+ traverse_big_int (member_info&)
+ {
+ type_id_ = "oracle::id_big_int";
+ }
+
+ void member_database_type_id::
+ traverse_float (member_info&)
+ {
+ type_id_ = "oracle::id_float";
+ }
+
+ void member_database_type_id::
+ traverse_double (member_info&)
+ {
+ type_id_ = "oracle::id_double";
+ }
+
+ void member_database_type_id::
+ traverse_big_float (member_info&)
+ {
+ type_id_ = "oracle::id_big_float";
+ }
+
+ void member_database_type_id::
+ traverse_date (member_info&)
+ {
+ type_id_ = "oracle::id_date";
+ }
+
+ void member_database_type_id::
+ traverse_timestamp (member_info&)
+ {
+ type_id_ = "oracle::id_timestamp";
+ }
+
+ void member_database_type_id::
+ traverse_interval_ym (member_info&)
+ {
+ type_id_ = "oracle::id_interval_ym";
+ }
+
+ void member_database_type_id::
+ traverse_interval_ds (member_info&)
+ {
+ type_id_ = "oracle::id_interval_ds";
+ }
+
+ void member_database_type_id::
+ traverse_string (member_info& mi)
+ {
+ type_id_ = string ("oracle::") +
+ string_bin_database_id[mi.st->type - sql_type::CHAR];
+ }
+
+ void member_database_type_id::
+ traverse_lob (member_info& mi)
+ {
+ type_id_ = string ("oracle::") +
+ lob_database_id[mi.st->type - sql_type::BLOB];
+ }
+
+ entry<member_database_type_id> member_database_type_id_;
+
+ //
+ // query_columns
+ //
+
+ struct query_columns: relational::query_columns, context
+ {
+ query_columns (base const& x): base_impl (x) {}
+
+ void
+ column_ctor (string const& type, string const& name, string const& base)
+ {
+ os << name << " (";
+
+ if (multi_dynamic)
+ os << "odb::query_column< " << type << " >& qc," << endl;
+
+ os << "const char* t," << endl
+ << "const char* c," << endl
+ << "const char* conv," << endl
+ << "unsigned short p = 0xFFF," << endl
+ << "short s = 0xFFF)" << endl
+ << " : " << base << " (" << (multi_dynamic ? "qc, " : "") <<
+ "t, c, conv, p, s)"
+ << "{"
+ << "}";
+ }
+
+ virtual void
+ column_ctor_args_extra (semantics::data_member& m)
+ {
+ // For some types we need to pass precision and scale.
+ //
+ sql_type const& st (parse_sql_type (column_type (), m));
+
+ switch (st.type)
+ {
+ case sql_type::NUMBER:
+ {
+ if (st.prec)
+ {
+ os << ", " << st.prec_value;
+
+ if (st.scale)
+ os << ", " << st.scale_value;
+ }
+ break;
+ }
+ case sql_type::FLOAT:
+ {
+ os << ", " << st.prec_value;
+ break;
+ }
+ case sql_type::TIMESTAMP:
+ {
+ os << ", " << st.prec_value;
+ break;
+ }
+ case sql_type::INTERVAL_YM:
+ {
+ os << ", " << st.prec_value;
+ break;
+ }
+ case sql_type::INTERVAL_DS:
+ {
+ // INTERVAL DAY TO SECOND has two precisions.
+ //
+ os << ", " << st.prec_value << ", " << st.scale_value;
+ break;
+ }
+ case sql_type::CHAR:
+ case sql_type::NCHAR:
+ case sql_type::VARCHAR2:
+ case sql_type::NVARCHAR2:
+ case sql_type::RAW:
+ {
+ // The same logic as in header.cxx.
+ //
+ size_t n (st.prec ? st.prec_value : 1);
+
+ if (!st.byte_semantics)
+ n *= 4;
+
+ if (st.type == sql_type::VARCHAR2 ||
+ st.type == sql_type::NVARCHAR2)
+ n = n > 4000 ? 4000 : n;
+ else
+ n = n > 2000 ? 2000 : n;
+
+ os << ", " << n;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ virtual string
+ database_type_id (semantics::data_member& m)
+ {
+ return member_database_type_id_.database_type_id (m);
+ }
+
+ private:
+ member_database_type_id member_database_type_id_;
+ };
+ entry<query_columns> query_columns_;
+ }
+}
diff --git a/odb/odb/relational/oracle/common.hxx b/odb/odb/relational/oracle/common.hxx
new file mode 100644
index 0000000..1958aab
--- /dev/null
+++ b/odb/odb/relational/oracle/common.hxx
@@ -0,0 +1,203 @@
+// file : odb/relational/oracle/common.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_ORACLE_COMMON_HXX
+#define ODB_RELATIONAL_ORACLE_COMMON_HXX
+
+#include <odb/relational/common.hxx>
+#include <odb/relational/oracle/context.hxx>
+
+namespace relational
+{
+ namespace oracle
+ {
+ struct member_base: virtual relational::member_base_impl<sql_type>, context
+ {
+ member_base (base const& x): base (x), base_impl (x) {}
+
+ // This c-tor is for the direct use inside the oracle namespace.
+ // If you do use this c-tor, you should also explicitly call
+ // relational::member_base (aka base).
+ //
+ member_base () {}
+
+ virtual sql_type const&
+ member_sql_type (semantics::data_member&);
+
+ virtual void
+ traverse_simple (member_info&);
+
+ virtual void
+ traverse_int32 (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_int64 (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_big_int (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_float (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_double (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_big_float (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_date (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_timestamp (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_interval_ym (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_interval_ds (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_string (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_lob (member_info&)
+ {
+ }
+ };
+
+ struct member_image_type: relational::member_image_type,
+ member_base
+ {
+ member_image_type (base const&);
+ member_image_type ();
+ member_image_type (semantics::type* type,
+ const custom_cxx_type*,
+ string const& fq_type = string (),
+ string const& key_prefix = string ());
+ virtual string
+ image_type (semantics::data_member&);
+
+ virtual void
+ traverse_composite (member_info&);
+
+ virtual void
+ traverse_int32 (member_info&);
+
+ virtual void
+ traverse_int64 (member_info&);
+
+ virtual void
+ traverse_big_int (member_info&);
+
+ virtual void
+ traverse_float (member_info&);
+
+ virtual void
+ traverse_double (member_info&);
+
+ virtual void
+ traverse_big_float (member_info&);
+
+ virtual void
+ traverse_date (member_info&);
+
+ virtual void
+ traverse_timestamp (member_info&);
+
+ virtual void
+ traverse_interval_ym (member_info&);
+
+ virtual void
+ traverse_interval_ds (member_info&);
+
+ virtual void
+ traverse_string (member_info&);
+
+ virtual void
+ traverse_lob (member_info&);
+
+ private:
+ string type_;
+ };
+
+ struct member_database_type_id: relational::member_database_type_id,
+ member_base
+ {
+ member_database_type_id (base const&);
+ member_database_type_id ();
+ member_database_type_id (semantics::type* type,
+ const custom_cxx_type*,
+ string const& fq_type = string (),
+ string const& key_prefix = string ());
+
+ virtual string
+ database_type_id (type&);
+
+ virtual void
+ traverse_composite (member_info&);
+
+ virtual void
+ traverse_int32 (member_info&);
+
+ virtual void
+ traverse_int64 (member_info&);
+
+ virtual void
+ traverse_big_int (member_info&);
+
+ virtual void
+ traverse_float (member_info&);
+
+ virtual void
+ traverse_double (member_info&);
+
+ virtual void
+ traverse_big_float (member_info&);
+
+ virtual void
+ traverse_date (member_info&);
+
+ virtual void
+ traverse_timestamp (member_info&);
+
+ virtual void
+ traverse_interval_ym (member_info&);
+
+ virtual void
+ traverse_interval_ds (member_info&);
+
+ virtual void
+ traverse_string (member_info&);
+
+ virtual void
+ traverse_lob (member_info&);
+
+ private:
+ string type_id_;
+ };
+ }
+}
+#endif // ODB_RELATIONAL_ORACLE_COMMON_HXX
diff --git a/odb/odb/relational/oracle/context.cxx b/odb/odb/relational/oracle/context.cxx
new file mode 100644
index 0000000..12ce0aa
--- /dev/null
+++ b/odb/odb/relational/oracle/context.cxx
@@ -0,0 +1,795 @@
+// file : odb/relational/oracle/context.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cassert>
+#include <sstream>
+
+#include <odb/sql-token.hxx>
+#include <odb/sql-lexer.hxx>
+
+#include <odb/relational/oracle/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace oracle
+ {
+ namespace
+ {
+ struct type_map_entry
+ {
+ char const* const cxx_type;
+ char const* const db_type;
+ char const* const db_id_type;
+ bool const null;
+ };
+
+ type_map_entry type_map[] =
+ {
+ {"bool", "NUMBER(1)", 0, false},
+
+ {"char", "CHAR(1)", 0, false},
+ {"signed char", "NUMBER(3)", 0, false},
+ {"unsigned char", "NUMBER(3)", 0, false},
+
+ {"short int", "NUMBER(5)", 0, false},
+ {"short unsigned int", "NUMBER(5)", 0, false},
+
+ {"int", "NUMBER(10)", 0, false},
+ {"unsigned int", "NUMBER(10)", 0, false},
+
+ {"long int", "NUMBER(19)", 0, false},
+ {"long unsigned int", "NUMBER(20)", 0, false},
+
+ {"long long int", "NUMBER(19)", 0, false},
+ {"long long unsigned int", "NUMBER(20)", 0, false},
+
+ {"float", "BINARY_FLOAT", 0, false},
+ {"double", "BINARY_DOUBLE", 0, false},
+
+ // Oracle treats empty VARCHAR2 (and NVARCHAR2) strings as NULL.
+ //
+ {"::std::string", "VARCHAR2(512)", 0, true},
+
+ {"::size_t", "NUMBER(20)", 0, false},
+ {"::std::size_t", "NUMBER(20)", 0, false}
+ };
+ }
+
+ context* context::current_;
+
+ context::
+ ~context ()
+ {
+ if (current_ == this)
+ current_ = 0;
+ }
+
+ context::
+ context (ostream& os,
+ semantics::unit& u,
+ options_type const& ops,
+ features_type& f,
+ sema_rel::model* m)
+ : root_context (os, u, ops, f, data_ptr (new (shared) data (os))),
+ base_context (static_cast<data*> (root_context::data_.get ()), m),
+ data_ (static_cast<data*> (base_context::data_))
+ {
+ assert (current_ == 0);
+ current_ = this;
+
+ generate_grow = false;
+ need_alias_as = false;
+ insert_send_auto_id = false;
+ delay_freeing_statement_result = false;
+ need_image_clone = true;
+ generate_bulk = true;
+ global_index = true;
+ global_fkey = true;
+ data_->bind_vector_ = "oracle::bind*";
+
+ // Populate the C++ type to DB type map.
+ //
+ for (size_t i (0); i < sizeof (type_map) / sizeof (type_map_entry); ++i)
+ {
+ type_map_entry const& e (type_map[i]);
+
+ type_map_type::value_type v (
+ e.cxx_type,
+ db_type_type (
+ e.db_type, e.db_id_type ? e.db_id_type : e.db_type, e.null));
+
+ data_->type_map_.insert (v);
+ }
+ }
+
+ context::
+ context ()
+ : data_ (current ().data_)
+ {
+ }
+
+ string const& context::
+ convert_expr (string const& sqlt, semantics::data_member& m, bool to)
+ {
+ sql_type const& t (parse_sql_type (sqlt, m));
+ return to ? t.to : t.from;
+ }
+
+ string context::
+ quote_id_impl (qname const& id) const
+ {
+ string r;
+
+ bool f (true);
+ for (qname::iterator i (id.begin ()); i < id.end (); ++i)
+ {
+ if (i->empty ())
+ continue;
+
+ if (f)
+ f = false;
+ else
+ r += '.';
+
+ r += '"';
+ r.append (*i, 0, 30); // Max identifier length is 30.
+ r += '"';
+ }
+
+ return r;
+ }
+
+ string context::
+ database_type_impl (semantics::type& t,
+ semantics::names* hint,
+ bool id,
+ bool* null)
+ {
+ string r (base_context::database_type_impl (t, hint, id, null));
+
+ if (!r.empty ())
+ return r;
+
+ using semantics::array;
+
+ // char[N] mapping.
+ //
+ if (array* a = dynamic_cast<array*> (&t))
+ {
+ semantics::type& bt (a->base_type ());
+ if (bt.is_a<semantics::fund_char> ())
+ {
+ unsigned long long n (a->size ());
+
+ if (n == 0)
+ return r;
+ else if (n == 1)
+ r = "CHAR";
+ else
+ {
+ r = "VARCHAR2";
+ n--;
+ }
+
+ // Oracle VARCHAR2 limit is 4000 bytes. Since there are no good
+ // alternatives (CLOB?), let the user specify the mapping.
+ //
+ if (n > 4000)
+ return "";
+
+ // Allow empty VARCHAR2 values.
+ //
+ if (null != 0 && r == "VARCHAR2")
+ *null = true;
+
+ ostringstream ostr;
+ ostr << n;
+ r += '(';
+ r += ostr.str ();
+ r += ')';
+ }
+ }
+
+ return r;
+ }
+
+ bool context::
+ unsigned_integer (semantics::type& t)
+ {
+ semantics::type* wt (wrapper (t));
+ const string& s ((wt == 0 ? t : utype (*wt)).name ());
+
+ return s == "bool" ||
+ s == "unsigned char" ||
+ s == "short unsigned int" ||
+ s == "unsigned int" ||
+ s == "long unsigned int" ||
+ s == "long long unsigned int";
+ }
+
+ qname context::
+ sequence_name (qname const& table)
+ {
+ string n;
+
+ if (options.sequence_suffix ().count (db) != 0)
+ n = table.uname () + options.sequence_suffix ()[db];
+ else
+ n = compose_name (table.uname (), "seq");
+
+ n = transform_name (n, sql_name_sequence);
+
+ qname r (table.qualifier ());
+ r.append (n);
+ return r;
+ }
+
+ //
+ // SQL type parsing.
+ //
+
+ sql_type const& context::
+ parse_sql_type (string const& t, semantics::data_member& m, bool custom)
+ {
+ // If this proves to be too expensive, we can maintain a cache of
+ // parsed types across contexts.
+ //
+ data::sql_type_cache::iterator i (data_->sql_type_cache_.find (t));
+
+ if (i != data_->sql_type_cache_.end ()
+ && (custom ? i->second.custom_cached : i->second.straight_cached))
+ {
+ return (custom ? i->second.custom : i->second.straight);
+ }
+ else
+ {
+ try
+ {
+ sql_type st (
+ parse_sql_type (
+ t,
+ custom ? &unit.get<custom_db_types> ("custom-db-types") : 0));
+
+ if (custom)
+ return data_->sql_type_cache_[t].cache_custom (st);
+ else
+ return data_->sql_type_cache_[t].cache_straight (st);
+ }
+ catch (invalid_sql_type const& e)
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: " << e.message () << endl;
+
+ throw operation_failed ();
+ }
+ }
+ }
+
+ inline sql_type
+ error (bool fail, string const& m)
+ {
+ if (!fail)
+ return sql_type ();
+ else
+ throw context::invalid_sql_type (m);
+ }
+
+ sql_type context::
+ parse_sql_type (string sqlt, custom_db_types const* ct)
+ {
+ try
+ {
+ sql_type r;
+
+ // First run the type through the custom mapping, if requested.
+ //
+ if (ct != 0)
+ {
+ for (custom_db_types::const_iterator i (ct->begin ());
+ i != ct->end (); ++i)
+ {
+ custom_db_type const& t (*i);
+
+ if (t.type.match (sqlt))
+ {
+ r.to = t.type.replace (sqlt, t.to);
+ r.from = t.type.replace (sqlt, t.from);
+ sqlt = t.type.replace (sqlt, t.as);
+ break;
+ }
+ }
+ }
+
+ sql_lexer l (sqlt);
+
+ // While most type names use single identifier, there are
+ // a couple of exceptions to this rule:
+ //
+ // CHARACTER VARYING (VARCHAR2)
+ // CHAR VARYING (VARCHAR2)
+ // NATIONAL CHARACTER (NCHAR)
+ // NATIONAL CHAR (NCHAR)
+ // NCHAR VARYING (NVARCHAR2)
+ // NATIONAL CHARACTER VARYING (NVARCHAR2)
+ // NATIONAL CHAR VARYING (NVARCHAR2)
+ // NCHAR VARYING (NVARCHAR2)
+ // DOUBLE PRECISION (FLOAT(126))
+ // INTERVAL YEAR TO MONTH
+ // INTERVAL DAY TO SECOND
+ //
+ enum state
+ {
+ parse_identifier,
+ parse_prec,
+ parse_done
+ };
+
+ state s (parse_identifier);
+ string prefix;
+ sql_token t (l.next ());
+
+ while (t.type () != sql_token::t_eos)
+ {
+ sql_token::token_type tt (t.type ());
+
+ switch (s)
+ {
+ case parse_identifier:
+ {
+ if (tt == sql_token::t_identifier)
+ {
+ string const& id (context::upcase (t.identifier ()));
+
+ //
+ // Numeric types.
+ //
+ if ((id == "NUMBER") && prefix.empty ())
+ {
+ // If NUMBER has no precision/scale, then it is a floating-
+ // point number. We indicate this by having no precision.
+ //
+ r.type = sql_type::NUMBER;
+ s = parse_prec;
+ }
+ else if ((id == "DEC" || id == "DECIMAL" || id == "NUMERIC")
+ && prefix.empty ())
+ {
+ // DEC, DECIMAL, and NUMERIC are equivalent to NUMBER in
+ // all ways except that they may not represent a floating
+ // point number. The scale defaults to zero.
+ //
+ r.type = sql_type::NUMBER;
+ s = parse_prec;
+ }
+ else if ((id == "INT" || id == "INTEGER" || id == "SMALLINT")
+ && prefix.empty ())
+ {
+ // INT, INTEGER, and SMALLINT map to NUMBER(38). They may not
+ // have precision or scale explicitly specified.
+ //
+ r.type = sql_type::NUMBER;
+ r.prec = true;
+ r.prec_value = 38;
+
+ s = parse_done;
+ }
+ //
+ // Floating point types.
+ //
+ else if (id == "FLOAT" && prefix.empty ())
+ {
+ r.type = sql_type::FLOAT;
+ r.prec = true;
+ r.prec_value = 126;
+
+ s = parse_prec;
+ }
+ else if (id == "DOUBLE" && prefix.empty ())
+ {
+ prefix = id;
+ }
+ else if (id == "PRECISION" && prefix == "DOUBLE")
+ {
+ r.type = sql_type::FLOAT;
+ r.prec = true;
+ r.prec_value = 126;
+
+ s = parse_done;
+ }
+ else if (id == "REAL" && prefix.empty ())
+ {
+ r.type = sql_type::FLOAT;
+ r.prec = true;
+ r.prec_value = 63;
+
+ s = parse_done;
+ }
+ else if (id == "BINARY_FLOAT" && prefix.empty ())
+ {
+ r.type = sql_type::BINARY_FLOAT;
+ s = parse_done;
+ }
+ else if (id == "BINARY_DOUBLE" && prefix.empty ())
+ {
+ r.type = sql_type::BINARY_DOUBLE;
+ s = parse_done;
+ }
+ //
+ // Date-time types.
+ //
+ else if (id == "DATE" && prefix.empty ())
+ {
+ r.type = sql_type::DATE;
+ s = parse_done;
+ }
+ else if (id == "TIMESTAMP" && prefix.empty ())
+ {
+ prefix = id;
+ }
+ else if (id == "INTERVAL" && prefix.empty ())
+ {
+ prefix = id;
+ }
+ else if (id == "YEAR" && prefix == "INTERVAL")
+ {
+ prefix += " ";
+ prefix += id;
+
+ r.prec = true;
+ r.prec_value = 2;
+ s = parse_prec;
+ }
+ else if (id == "DAY" && prefix == "INTERVAL")
+ {
+ prefix += " ";
+ prefix += id;
+
+ r.prec = true;
+ r.prec_value = 2;
+ s = parse_prec;
+ }
+ else if (id == "TO" &&
+ (prefix == "INTERVAL YEAR" ||
+ prefix == "INTERVAL DAY"))
+ {
+ prefix += " ";
+ prefix += id;
+ }
+ else if (id == "MONTH" && prefix == "INTERVAL YEAR TO")
+ {
+ r.type = sql_type::INTERVAL_YM;
+ s = parse_done;
+ }
+ else if (id == "SECOND" && prefix == "INTERVAL DAY TO")
+ {
+ r.type = sql_type::INTERVAL_DS;
+
+ // Store seconds precision in scale since prec holds
+ // the days precision.
+ //
+ r.scale = true;
+ r.scale_value = 6;
+ s = parse_prec;
+ }
+ //
+ // Timestamp with time zone (not supported).
+ //
+ else if (id == "WITH" && prefix == "TIMESTAMP")
+ {
+ prefix += " ";
+ prefix += id;
+ }
+ else if (id == "TIME" &&
+ (prefix == "TIMESTAMP WITH" ||
+ prefix == "TIMESTAMP WITH LOCAL"))
+ {
+ prefix += " ";
+ prefix += id;
+ }
+ else if (id == "LOCAL" && prefix == "TIMESTAMP WITH")
+ {
+ prefix += " ";
+ prefix += id;
+ }
+ else if (id == "ZONE" &&
+ (prefix == "TIMESTAMP WITH LOCAL TIME" ||
+ prefix == "TIMESTAMP WITH TIME"))
+ {
+ return error (ct, "Oracle timestamps with time zones are "
+ "not currently supported");
+ }
+ //
+ // String and binary types.
+ //
+ else if (id == "CHAR")
+ {
+ prefix += prefix.empty () ? "" : " ";
+ prefix += id;
+ }
+ else if (id == "CHARACTER")
+ {
+ prefix += prefix.empty () ? "" : " ";
+ prefix += id;
+ }
+ else if (id == "NCHAR")
+ {
+ prefix += prefix.empty () ? "" : " ";
+ prefix += id;
+ }
+ else if (id == "VARCHAR" || id == "VARCHAR2")
+ {
+ // VARCHAR is currently mapped to VARCHAR2 in Oracle server.
+ // However, this may change in future versions.
+ //
+ r.type = sql_type::VARCHAR2;
+ r.byte_semantics = true;
+ s = parse_prec;
+ }
+ else if (id == "NVARCHAR2")
+ {
+ r.type = sql_type::NVARCHAR2;
+ r.byte_semantics = false;
+ s = parse_prec;
+ }
+ else if (id == "VARYING")
+ {
+ // VARYING always appears at the end of an identifier.
+ //
+ if (prefix == "CHAR" || prefix == "CHARACTER")
+ {
+ r.type = sql_type::VARCHAR2;
+ r.byte_semantics = true;
+ }
+ else if (prefix == "NCHAR" ||
+ prefix == "NATIONAL CHAR" ||
+ prefix == "NATIONAL CHARACTER")
+ {
+ r.type = sql_type::NVARCHAR2;
+ r.byte_semantics = false;
+ }
+
+ s = parse_prec;
+ }
+ else if (id == "NATIONAL" && prefix.empty ())
+ {
+ prefix = id;
+ }
+ else if (id == "RAW" && prefix.empty ())
+ {
+ r.type = sql_type::RAW;
+ s = parse_prec;
+ }
+ //
+ // LOB types.
+ //
+ else if (id == "BLOB" && prefix.empty ())
+ {
+ r.type = sql_type::BLOB;
+ s = parse_done;
+ }
+ else if (id == "CLOB" && prefix.empty ())
+ {
+ r.type = sql_type::CLOB;
+ s = parse_done;
+ }
+ else if (id == "NCLOB" && prefix.empty ())
+ {
+ r.type = sql_type::NCLOB;
+ s = parse_done;
+ }
+ //
+ // LONG types.
+ //
+ else if (id == "LONG")
+ return error (ct, "Oracle LONG types are not supported");
+ else
+ return error (ct, "unknown Oracle type '" +
+ t.identifier () + "'");
+
+ t = l.next ();
+ continue;
+ }
+ else if (!prefix.empty ())
+ {
+ // Some prefixes can also be type names if not followed
+ // by the actual type name.
+ //
+
+ if (prefix == "CHAR" || prefix == "CHARACTER")
+ {
+ r.type = sql_type::CHAR;
+ r.byte_semantics = true;
+ r.prec = true;
+ r.prec_value = 1;
+ }
+ else if (prefix == "NCHAR" ||
+ prefix == "NATIONAL CHAR" ||
+ prefix == "NATIONAL CHARACTER")
+ {
+ r.type = sql_type::NCHAR;
+ r.byte_semantics = false;
+ r.prec = true;
+ r.prec_value = 1;
+ }
+ else if (prefix == "TIMESTAMP")
+ {
+ r.type = sql_type::TIMESTAMP;
+ r.prec = true;
+ r.prec_value = 6;
+ }
+ else
+ return error (ct, "incomplete Oracle type declaration: '" +
+ prefix + "'");
+
+ // All of the possible types handled in this block can take
+ // an optional precision specifier. Set the state and fall
+ // through to the parse_prec handler.
+ //
+ s = parse_prec;
+ }
+ else
+ {
+ assert (r.type == sql_type::invalid);
+ return error (ct, "unexepected '" + t.literal () +
+ "' in Oracle type declaration");
+ }
+ }
+ // Fall through.
+ case parse_prec:
+ {
+ if (t.punctuation () == sql_token::p_lparen)
+ {
+ t = l.next ();
+
+ if (t.type () != sql_token::t_int_lit)
+ {
+ return error (ct, "integer size/precision expected in "
+ "Oracle type declaration");
+ }
+
+ // Parse the precision.
+ //
+ {
+ unsigned short v;
+ istringstream is (t.literal ());
+
+ if (!(is >> v && is.eof ()))
+ {
+ return error (ct, "invalid prec value '" + t.literal () +
+ "' in Oracle type declaration");
+ }
+
+ // Store seconds precision in scale since prec holds
+ // the days precision for INTERVAL DAY TO SECOND.
+ //
+ if (r.type == sql_type::INTERVAL_DS)
+ {
+ r.scale = true;
+ r.scale_value = static_cast<short> (v);
+ }
+ else
+ {
+ r.prec = true;
+ r.prec_value = v;
+ }
+
+ t = l.next ();
+ }
+
+ // Parse the scale if present.
+ //
+ if (t.punctuation () == sql_token::p_comma)
+ {
+ // Scale can only be specified for NUMBER.
+ //
+ if (r.type != sql_type::NUMBER)
+ {
+ return error (ct, "invalid scale in Oracle type "
+ "declaration");
+ }
+
+ t = l.next ();
+
+ if (t.type () != sql_token::t_int_lit)
+ {
+ return error (ct, "integer scale expected in Oracle type "
+ "declaration");
+ }
+
+ short v;
+ istringstream is (t.literal ());
+
+ if (!(is >> v && is.eof ()))
+ {
+ return error (ct, "invalid scale value '" + t.literal () +
+ "' in Oracle type declaration");
+ }
+
+ r.scale = true;
+ r.scale_value = v;
+
+ t = l.next ();
+ }
+ else if (t.type () == sql_token::t_identifier)
+ {
+ const string& id (context::upcase (t.identifier ()));
+
+ if (id == "CHAR")
+ r.byte_semantics = false;
+ else if (id != "BYTE")
+ {
+ return error (ct, "invalid keyword '" + t.literal () +
+ "' in Oracle type declaration");
+ }
+
+ t = l.next ();
+ }
+
+ if (t.punctuation () != sql_token::p_rparen)
+ {
+ return error (ct, "expected ')' in Oracle type declaration");
+ }
+ else
+ t = l.next ();
+ }
+
+ s = r.type == sql_type::invalid ? parse_identifier : parse_done;
+ continue;
+ }
+ case parse_done:
+ {
+ return error (ct, "unexepected '" + t.literal () + "' in Oracle "
+ "type declaration");
+ break;
+ }
+ }
+ }
+
+ // Some prefixes can also be type names if not followed by the actual
+ // type name.
+ //
+ if (r.type == sql_type::invalid)
+ {
+ if (!prefix.empty ())
+ {
+ if (prefix == "CHAR" || prefix == "CHARACTER")
+ {
+ r.type = sql_type::CHAR;
+ r.byte_semantics = true;
+ r.prec = true;
+ r.prec_value = 1;
+ }
+ else if (prefix == "NCHAR" ||
+ prefix == "NATIONAL CHAR" ||
+ prefix == "NATIONAL CHARACTER")
+ {
+ r.type = sql_type::NCHAR;
+ r.byte_semantics = false;
+ r.prec = true;
+ r.prec_value = 1;
+ }
+ else if (prefix == "TIMESTAMP")
+ {
+ r.type = sql_type::TIMESTAMP;
+ r.prec = true;
+ r.prec_value = 6;
+ }
+ else
+ return error (ct, "incomplete Oracle type declaration: '" +
+ prefix + "'");
+ }
+ else
+ return error (ct, "invalid Oracle type declaration");
+ }
+
+ return r;
+ }
+ catch (sql_lexer::invalid_input const& e)
+ {
+ return error (ct, "invalid Oracle type declaration: " + e.message);
+ }
+ }
+ }
+}
diff --git a/odb/odb/relational/oracle/context.hxx b/odb/odb/relational/oracle/context.hxx
new file mode 100644
index 0000000..6c55853
--- /dev/null
+++ b/odb/odb/relational/oracle/context.hxx
@@ -0,0 +1,188 @@
+// file : odb/relational/oracle/context.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_ORACLE_CONTEXT_HXX
+#define ODB_RELATIONAL_ORACLE_CONTEXT_HXX
+
+#include <map>
+
+#include <odb/relational/context.hxx>
+
+namespace relational
+{
+ namespace oracle
+ {
+ struct sql_type
+ {
+ // Keep the order in each block of types.
+ //
+ enum core_type
+ {
+ // Numeric types.
+ //
+ NUMBER,
+ FLOAT,
+
+ // Floating point types.
+ //
+ BINARY_FLOAT,
+ BINARY_DOUBLE,
+
+ // Date-time types.
+ //
+ DATE,
+ TIMESTAMP,
+ INTERVAL_YM,
+ INTERVAL_DS,
+
+ // String and binary types.
+ //
+ CHAR,
+ NCHAR,
+ VARCHAR2,
+ NVARCHAR2,
+ RAW,
+
+ // LOB types.
+ //
+ BLOB,
+ CLOB,
+ NCLOB,
+
+ // Invalid type.
+ //
+ invalid
+ };
+
+ sql_type () :
+ type (invalid), prec (false), scale (false), byte_semantics (true)
+ {
+ }
+
+ core_type type;
+
+ bool prec;
+ unsigned short prec_value; // Oracle max value is 4000.
+
+ bool scale;
+ short scale_value; // Oracle min value is -84. Max value is 127.
+
+ bool byte_semantics;
+
+ // Conversion expressions for custom database types.
+ //
+ std::string to;
+ std::string from;
+ };
+
+ class context: public virtual relational::context
+ {
+ public:
+ sql_type const&
+ parse_sql_type (string const&,
+ semantics::data_member&,
+ bool custom = true);
+ public:
+ struct invalid_sql_type
+ {
+ invalid_sql_type (string const& message): message_ (message) {}
+
+ string const&
+ message () const {return message_;}
+
+ private:
+ string message_;
+ };
+
+ // If custom_db_types is NULL, then this function returns
+ // invalid type instead of throwing in case an unknown type
+ // is encountered.
+ //
+ static sql_type
+ parse_sql_type (string, custom_db_types const* = 0);
+
+ public:
+ // If necessary, unwraps.
+ //
+ static bool
+ unsigned_integer (semantics::type&);
+
+ public:
+ // Construct sequence name from a given table name.
+ //
+ qname
+ sequence_name (qname const& table);
+
+ protected:
+ virtual string const&
+ convert_expr (string const&, semantics::data_member&, bool);
+
+ virtual string
+ quote_id_impl (qname const&) const;
+
+ protected:
+ virtual string
+ database_type_impl (semantics::type&, semantics::names*, bool, bool*);
+
+ public:
+ virtual
+ ~context ();
+
+ context ();
+ context (std::ostream&,
+ semantics::unit&,
+ options_type const&,
+ features_type&,
+ sema_rel::model*);
+
+ static context&
+ current ()
+ {
+ return *current_;
+ }
+
+ private:
+ static context* current_;
+
+ private:
+ struct data: base_context::data
+ {
+ data (std::ostream& os): base_context::data (os) {}
+
+ struct sql_type_cache_entry
+ {
+ sql_type_cache_entry ()
+ : custom_cached (false), straight_cached (false) {}
+
+ sql_type const&
+ cache_custom (sql_type const& t)
+ {
+ custom = t;
+ custom_cached = true;
+ return custom;
+ }
+
+ sql_type const&
+ cache_straight (sql_type const& t)
+ {
+ straight = t;
+ straight_cached = true;
+ return straight;
+ }
+
+ sql_type custom; // With custom mapping.
+ sql_type straight; // Without custom mapping.
+
+ bool custom_cached;
+ bool straight_cached;
+ };
+
+ typedef std::map<string, sql_type_cache_entry> sql_type_cache;
+ sql_type_cache sql_type_cache_;
+ };
+ data* data_;
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_ORACLE_CONTEXT_HXX
diff --git a/odb/odb/relational/oracle/header.cxx b/odb/odb/relational/oracle/header.cxx
new file mode 100644
index 0000000..bf50bb2
--- /dev/null
+++ b/odb/odb/relational/oracle/header.cxx
@@ -0,0 +1,230 @@
+// file : odb/relational/oracle/header.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/header.hxx>
+
+#include <odb/relational/oracle/common.hxx>
+#include <odb/relational/oracle/context.hxx>
+
+namespace relational
+{
+ namespace oracle
+ {
+ namespace header
+ {
+ namespace relational = relational::header;
+
+ struct image_type: relational::image_type, context
+ {
+ image_type (base const& x): base (x) {};
+
+ virtual void
+ image_extra (type& c)
+ {
+ if (!(composite (c) || (abstract (c) && !polymorphic (c))))
+ {
+ type* poly_root (polymorphic (c));
+
+ // If this is a polymorphic type, only add callback to the root.
+ //
+ if (poly_root == 0 || poly_root == &c)
+ {
+ bool gc (options.generate_query ());
+
+ if (gc)
+ os << "oracle::change_callback change_callback_;"
+ << endl;
+
+ os << "oracle::change_callback*" << endl
+ << "change_callback ()"
+ << "{";
+
+ if (gc)
+ os << "return &change_callback_;";
+ else
+ os << "return 0;";
+
+ os << "}";
+ }
+ }
+ }
+ };
+ entry<image_type> image_type_;
+
+ struct image_member: relational::image_member_impl<sql_type>,
+ member_base
+ {
+ image_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x) {}
+
+ virtual void
+ traverse_int32 (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "sb2 " << mi.var << "indicator;"
+ << endl;
+ }
+
+ virtual void
+ traverse_int64 (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "sb2 " << mi.var << "indicator;"
+ << endl;
+ }
+
+ virtual void
+ traverse_big_int (member_info& mi)
+ {
+ // Each significant base-100 digit requires a byte of storage
+ // in the manitissa. The default precision is 38 decimal digits,
+ // which is equivalent to 19 base-100 digits.
+ //
+ size_t n (19);
+
+ if (mi.st->prec)
+ n = mi.st->prec_value / 2 + mi.st->prec_value % 2;
+
+ // We require an additional byte for each of the exponent and
+ // negative value terminator values.
+ //
+ n += 2;
+
+ os << "char " << mi.var << "value[" << n << "];"
+ << "ub2 " << mi.var << "size;"
+ << "sb2 " << mi.var << "indicator;"
+ << endl;
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "sb2 " << mi.var << "indicator;"
+ << endl;
+ }
+
+ virtual void
+ traverse_double (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "sb2 " << mi.var << "indicator;"
+ << endl;
+ }
+
+ virtual void
+ traverse_big_float (member_info& mi)
+ {
+ // big_float is mapped to the OCI type SQLT_NUM, which requires 21
+ // bytes of storage.
+ //
+ os << "char " << mi.var << "value[21];"
+ << "ub2 " << mi.var << "size;"
+ << "sb2 " << mi.var << "indicator;"
+ << endl;
+ }
+
+ virtual void
+ traverse_date (member_info& mi)
+ {
+ os << "char " << mi.var << "value[7];"
+ << "sb2 " << mi.var << "indicator;"
+ << endl;
+ }
+
+ virtual void
+ traverse_timestamp (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "sb2 " << mi.var << "indicator;"
+ << endl;
+ }
+
+ virtual void
+ traverse_interval_ym (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "sb2 " << mi.var << "indicator;"
+ << endl;
+ }
+
+ virtual void
+ traverse_interval_ds (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "sb2 " << mi.var << "indicator;"
+ << endl;
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ size_t n (mi.st->prec ? mi.st->prec_value : 1);
+
+ // National characters can be either UTF-8 or UTF-16 encoded,
+ // both of which have a maximum character encoding size of 4
+ // bytes. Database character set can also be UTF-8 so if the
+ // size is specified in characters, then conservatively assume
+ // each character can take up to 4 bytes.
+ //
+ if (!mi.st->byte_semantics) // N*CHAR always has CHAR semantics.
+ n *= 4;
+
+ if (mi.st->type == sql_type::VARCHAR2 ||
+ mi.st->type == sql_type::NVARCHAR2)
+ n = n > 4000 ? 4000 : n;
+ else
+ n = n > 2000 ? 2000 : n;
+
+ os << "char " << mi.var << "value[" << n << "];"
+ << "ub2 " << mi.var << "size;"
+ << "sb2 " << mi.var << "indicator;"
+ << endl;
+ }
+
+ virtual void
+ traverse_lob (member_info& mi)
+ {
+ os << "mutable " << image_type << " " << mi.var << "callback;"
+ << "sb2 " << mi.var << "indicator;"
+ << "oracle::lob " << mi.var << "lob;"
+ << endl;
+ }
+ };
+ entry<image_member> image_member_;
+
+ struct class1: relational::class1
+ {
+ class1 (base const& x): base (x) {}
+
+ virtual void
+ object_public_extra_pre (type& c)
+ {
+ bool abst (abstract (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+
+ if (poly_derived || (abst && !poly))
+ return;
+
+ // Bulk operations batch size.
+ //
+ {
+ unsigned long long b (c.count ("bulk")
+ ? c.get<unsigned long long> ("bulk")
+ : 1);
+
+ os << "static const std::size_t batch = " << b << "UL;"
+ << endl;
+ }
+ }
+ };
+ entry<class1> class1_entry_;
+ }
+ }
+}
diff --git a/odb/odb/relational/oracle/inline.cxx b/odb/odb/relational/oracle/inline.cxx
new file mode 100644
index 0000000..1b6d606
--- /dev/null
+++ b/odb/odb/relational/oracle/inline.cxx
@@ -0,0 +1,42 @@
+// file : odb/relational/oracle/inline.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/inline.hxx>
+
+#include <odb/relational/oracle/common.hxx>
+#include <odb/relational/oracle/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace oracle
+ {
+ namespace inline_
+ {
+ namespace relational = relational::inline_;
+
+ struct null_member: relational::null_member_impl<sql_type>,
+ member_base
+ {
+ null_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ traverse_simple (member_info& mi)
+ {
+ if (get_)
+ os << "r = r && i." << mi.var << "indicator == -1;";
+ else
+ os << "i." << mi.var << "indicator = -1;";
+ }
+ };
+ entry<null_member> null_member_;
+ }
+ }
+}
diff --git a/odb/odb/relational/oracle/model.cxx b/odb/odb/relational/oracle/model.cxx
new file mode 100644
index 0000000..b65e201
--- /dev/null
+++ b/odb/odb/relational/oracle/model.cxx
@@ -0,0 +1,64 @@
+// file : odb/relational/oracle/model.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <odb/relational/model.hxx>
+
+#include <odb/relational/oracle/common.hxx>
+#include <odb/relational/oracle/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace oracle
+ {
+ namespace model
+ {
+ namespace relational = relational::model;
+
+ struct object_columns: relational::object_columns, context
+ {
+ object_columns (base const& x): base (x) {}
+
+ virtual string
+ default_enum (semantics::data_member& m, tree en, string const&)
+ {
+ // Make sure the column is mapped to Oracle NUMBER.
+ //
+ sql_type const& t (parse_sql_type (column_type (), m, false));
+ if (t.type != sql_type::NUMBER)
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: column with default value specified as C++ "
+ << "enumerator must map to Oracle NUMBER" << endl;
+
+ throw operation_failed ();
+ }
+
+ using semantics::enumerator;
+
+ enumerator& e (dynamic_cast<enumerator&> (*unit.find (en)));
+
+ ostringstream ostr;
+
+ if (e.enum_ ().unsigned_ ())
+ ostr << e.value ();
+ else
+ ostr << static_cast<long long> (e.value ());
+
+ return ostr.str ();
+ }
+
+ virtual void
+ primary_key (sema_rel::primary_key& pk)
+ {
+ if (pk.auto_ ())
+ pk.extra ()["sequence"] = sequence_name (table_.name ()).string ();
+ }
+ };
+ entry<object_columns> object_columns_;
+ }
+ }
+}
diff --git a/odb/odb/relational/oracle/schema.cxx b/odb/odb/relational/oracle/schema.cxx
new file mode 100644
index 0000000..75100b1
--- /dev/null
+++ b/odb/odb/relational/oracle/schema.cxx
@@ -0,0 +1,696 @@
+// file : odb/relational/oracle/schema.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <map>
+#include <utility> // pair
+
+#include <odb/diagnostics.hxx>
+
+#include <odb/relational/schema.hxx>
+
+#include <odb/relational/oracle/common.hxx>
+#include <odb/relational/oracle/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace oracle
+ {
+ namespace schema
+ {
+ namespace relational = relational::schema;
+ using relational::table_set;
+
+ struct sql_emitter: relational::sql_emitter
+ {
+ sql_emitter (const base& x): base (x) {}
+
+ virtual void
+ line (const std::string& l)
+ {
+ // SQLPlus doesn't like empty line in the middle of a statement.
+ //
+ if (!l.empty ())
+ {
+ base::line (l);
+ last_ = l;
+ }
+ }
+
+ virtual void
+ post ()
+ {
+ if (!first_) // Ignore empty statements.
+ {
+ if (last_ == "END;")
+ os << endl
+ << '/' << endl
+ << endl;
+
+ else
+ os << ';' << endl
+ << endl;
+ }
+ }
+
+ private:
+ string last_;
+ };
+ entry<sql_emitter> sql_emitter_;
+
+ //
+ // File.
+ //
+
+ struct sql_file: relational::sql_file, context
+ {
+ sql_file (const base& x): base (x) {}
+
+ virtual void
+ prologue ()
+ {
+ // Quiet down SQLPlus and make sure it exits with an error
+ // code if there is an error.
+ //
+ os << "SET FEEDBACK OFF;" << endl
+ << "WHENEVER SQLERROR EXIT FAILURE;" << endl
+ << "WHENEVER OSERROR EXIT FAILURE;" << endl
+ << endl;
+ }
+
+ virtual void
+ epilogue ()
+ {
+ os << "EXIT;" << endl;
+ }
+ };
+ entry<sql_file> sql_file_;
+
+ //
+ // Drop.
+ //
+
+ struct drop_column: relational::drop_column, context
+ {
+ drop_column (base const& x): base (x) {}
+
+ virtual void
+ traverse (sema_rel::drop_column& dc)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << "," << endl
+ << " ";
+
+ os << quote_id (dc.name ());
+ }
+ };
+ entry<drop_column> drop_column_;
+
+ struct drop_foreign_key: relational::drop_foreign_key, context
+ {
+ drop_foreign_key (base const& x): base (x) {}
+
+ virtual void
+ traverse (sema_rel::drop_foreign_key& dfk)
+ {
+ os << endl;
+ drop (dfk);
+ }
+ };
+ entry<drop_foreign_key> drop_foreign_key_;
+
+ struct drop_index: relational::drop_index, context
+ {
+ drop_index (base const& x): base (x) {}
+
+ virtual string
+ name (sema_rel::index& in)
+ {
+ // In Oracle, index names can be qualified with the schema.
+ //
+ sema_rel::table& t (static_cast<sema_rel::table&> (in.scope ()));
+ sema_rel::qname n (t.name ().qualifier ());
+ n.append (in.name ());
+ return quote_id (n);
+ }
+ };
+ entry<drop_index> drop_index_;
+
+ struct drop_table: relational::drop_table, context
+ {
+ drop_table (base const& x): base (x) {}
+
+ virtual void
+ drop (sema_rel::table& t, bool migration)
+ {
+ using sema_rel::primary_key;
+
+ sema_rel::table::names_iterator i (t.find ("")); // Special name.
+ primary_key* pk (i != t.names_end ()
+ ? &dynamic_cast<primary_key&> (i->nameable ())
+ : 0);
+
+ string qt (quote_id (t.name ()));
+ string qs (pk != 0 && pk->auto_ ()
+ ? quote_id (qname::from_string (pk->extra ()["sequence"]))
+ : "");
+
+ if (migration)
+ {
+ pre_statement ();
+ os << "DROP TABLE " << qt << endl;
+ post_statement ();
+
+ // Drop the sequence if we have auto primary key.
+ //
+ if (!qs.empty ())
+ {
+ pre_statement ();
+ os << "DROP SEQUENCE " << qs << endl;
+ post_statement ();
+ }
+ }
+ else
+ {
+ // Oracle has no IF EXISTS conditional for dropping objects. The
+ // PL/SQL approach below seems to be the least error-prone and the
+ // most widely used of the alternatives.
+ //
+ pre_statement ();
+ os << "BEGIN" << endl
+ << " BEGIN" << endl
+ << " EXECUTE IMMEDIATE 'DROP TABLE " << qt << " CASCADE " <<
+ "CONSTRAINTS';" << endl
+ << " EXCEPTION" << endl
+ << " WHEN OTHERS THEN" << endl
+ << " IF SQLCODE != -942 THEN RAISE; END IF;" << endl
+ << " END;" << endl;
+
+ // Drop the sequence if we have auto primary key.
+ //
+ if (!qs.empty ())
+ {
+ os << " BEGIN" << endl
+ << " EXECUTE IMMEDIATE 'DROP SEQUENCE " << qs <<
+ "';" << endl
+ << " EXCEPTION" << endl
+ << " WHEN OTHERS THEN" << endl
+ << " IF SQLCODE != -2289 THEN RAISE; END IF;" << endl
+ << " END;" << endl;
+ }
+
+ os << "END;" << endl;
+ post_statement ();
+ }
+ }
+
+ virtual void
+ traverse (sema_rel::table& t, bool migration)
+ {
+ // For migration drop foreign keys explicitly in pre-migration.
+ //
+ if (migration)
+ {
+ base::traverse (t, migration);
+ return;
+ }
+
+ // For schema creation we use the CASCADE clause to drop foreign
+ // keys.
+ //
+ if (pass_ != 2)
+ return;
+
+ drop (t, migration);
+ }
+ };
+ entry<drop_table> drop_table_;
+
+ //
+ // Create.
+ //
+ static sema_rel::uname
+ truncate (location const& l, const char* kind, sema_rel::uname n, bool w)
+ {
+ if (n.size () > 30)
+ {
+ if (w)
+ warn (l) << kind << " name '" << n << "' is longer than 30 "
+ << "characters and will be truncated" << endl;
+
+ n.resize (30);
+ }
+
+ return n;
+ }
+
+ static sema_rel::qname
+ truncate (location const& l,
+ const char* kind,
+ sema_rel::qname const& n,
+ bool w)
+ {
+ // Don't bother verifying the schema name since that is
+ // specified explicitly and in a single place.
+ //
+ qname r (n.qualifier ());
+ r.append (truncate (l, kind, n.uname (), w));
+ return r;
+ }
+
+ template <typename N>
+ struct scope
+ {
+ typedef std::map<N, pair<N, location> > map;
+
+ scope (const char* k, const char* p, bool w)
+ : kind_ (k), prag_ (p), warn_ (w) {}
+
+ void
+ check (location const& l, N const& n)
+ {
+ N tn (truncate (l, kind_, n, warn_));
+
+ pair<typename map::iterator, bool> r (
+ map_.insert (make_pair (tn, make_pair (n, l))));
+
+ if (r.second)
+ return;
+
+ error (l) << kind_ << " name '" << tn << "' conflicts with an "
+ << "already defined " << kind_ << " name" << endl;
+
+ if (tn != n)
+ info (l) << kind_ << " name '" << tn << "' is truncated '"
+ << n << "'" << endl;
+
+ N const& n1 (r.first->second.first);
+ location const& l1 (r.first->second.second);
+
+ info (l1) << "conflicting " << kind_ << " is defined here" << endl;
+
+ if (tn != n)
+ info (l1) << "conflicting " << kind_ << " name '" << tn
+ << "' is truncated '" << n1 << "'" << endl;
+
+ info (l) << "use #pragma db " << prag_ << " to change one of "
+ << "the names" << endl;
+
+ throw operation_failed ();
+ }
+
+ void
+ clear () {map_.clear ();}
+
+ const char* kind_;
+ const char* prag_;
+ bool warn_;
+ map map_;
+ };
+
+ struct scopes
+ {
+ scopes (bool warn)
+ : tables ("table", "table", warn),
+ fkeys ("foreign key", "column", warn), // Change column name.
+ indexes ("index", "index", warn),
+ sequences ("sequence", "table", warn), // Change table name.
+ columns ("column", "column", warn) {}
+
+ // In Oracle, all these entities are in their own name spaces,
+ // as in an index and a foreign key with the same name do not
+ // conflict.
+ //
+ scope<sema_rel::qname> tables;
+ scope<sema_rel::uname> fkeys; // Global but can't have schema.
+ scope<sema_rel::qname> indexes;
+ scope<sema_rel::qname> sequences;
+ scope<sema_rel::uname> columns;
+ };
+
+ struct create_column: relational::create_column, context
+ {
+ create_column (base const& x): base (x) {}
+
+ virtual void
+ traverse (sema_rel::column& c)
+ {
+ // Check name trunction and conflicts.
+ //
+ if (scopes* s = static_cast<scopes*> (context::extra))
+ s->columns.check (c.get<location> ("cxx-location"), c.name ());
+
+ base::traverse (c);
+ }
+
+ virtual void
+ traverse (sema_rel::add_column& ac)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << "," << endl
+ << " ";
+
+ create (ac);
+ }
+
+ virtual void
+ constraints (sema_rel::column& c, sema_rel::primary_key* pk)
+ {
+ // Oracle wants DEFAULT before NULL even though we can end
+ // up with mouthfulls like DEFAULT NULL NULL.
+ //
+ if (!c.default_ ().empty ())
+ os << " DEFAULT " << c.default_ ();
+
+ null (c);
+
+ // If this is a single-column primary key, generate it inline.
+ //
+ if (pk != 0 && pk->contains_size () == 1)
+ primary_key ();
+
+ if (pk != 0 && pk->auto_ ())
+ auto_ (*pk);
+ }
+ };
+ entry<create_column> create_column_;
+
+ struct create_foreign_key: relational::create_foreign_key, context
+ {
+ create_foreign_key (base const& x): base (x) {}
+
+ virtual void
+ traverse_create (sema_rel::foreign_key& fk)
+ {
+ // Check name trunction and conflicts.
+ //
+ if (scopes* s = static_cast<scopes*> (context::extra))
+ s->fkeys.check (fk.get<location> ("cxx-location"), fk.name ());
+
+ base::traverse_create (fk);
+ }
+
+ virtual void
+ traverse_add (sema_rel::foreign_key& fk)
+ {
+ // Check name trunction and conflicts.
+ //
+ if (scopes* s = static_cast<scopes*> (context::extra))
+ s->fkeys.check (fk.get<location> ("cxx-location"), fk.name ());
+
+ os << endl
+ << " ADD CONSTRAINT ";
+ create (fk);
+ }
+ };
+ entry<create_foreign_key> create_foreign_key_;
+
+ struct create_index: relational::create_index, context
+ {
+ create_index (base const& x): base (x) {}
+
+ virtual string
+ name (sema_rel::index& in)
+ {
+ // In Oracle, index names can be qualified with the schema.
+ //
+ sema_rel::table& t (static_cast<sema_rel::table&> (in.scope ()));
+ sema_rel::qname n (t.name ().qualifier ());
+ n.append (in.name ());
+
+ // Check name trunction and conflicts.
+ //
+ if (scopes* s = static_cast<scopes*> (context::extra))
+ s->indexes.check (in.get<location> ("cxx-location"), n);
+
+ return quote_id (n);
+ }
+ };
+ entry<create_index> create_index_;
+
+ struct create_table: relational::create_table, context
+ {
+ create_table (base const& x): base (x) {}
+
+ void
+ traverse (sema_rel::table& t)
+ {
+ // Check name trunction and conflicts.
+ //
+ if (scopes* s = static_cast<scopes*> (context::extra))
+ {
+ if (pass_ == 1)
+ {
+ s->tables.check (t.get<location> ("cxx-location"), t.name ());
+ s->columns.clear ();
+ }
+ }
+
+ base::traverse (t);
+
+ if (pass_ == 1)
+ {
+ // Create the sequence if we have auto primary key.
+ //
+ using sema_rel::primary_key;
+
+ sema_rel::table::names_iterator i (t.find ("")); // Special name.
+ primary_key* pk (i != t.names_end ()
+ ? &dynamic_cast<primary_key&> (i->nameable ())
+ : 0);
+
+ if (pk != 0 && pk->auto_ ())
+ {
+ // Already qualified with the table's schema, if any.
+ //
+ sema_rel::qname n (
+ qname::from_string (pk->extra ()["sequence"]));
+
+ if (scopes* s = static_cast<scopes*> (context::extra))
+ s->sequences.check (pk->get<location> ("cxx-location"), n);
+
+ pre_statement ();
+ os_ << "CREATE SEQUENCE " << quote_id (n) << endl
+ << " START WITH 1 INCREMENT BY 1" << endl;
+ post_statement ();
+ }
+ }
+ }
+ };
+ entry<create_table> create_table_;
+
+ struct create_model: relational::create_model, context
+ {
+ create_model (base const& x): base (x) {}
+
+ void
+ traverse (sema_rel::model& m)
+ {
+ scopes s (options.oracle_warn_truncation ());
+ context::extra = &s;
+ base::traverse (m);
+ context::extra = 0;
+ }
+ };
+ entry<create_model> create_model_;
+
+ //
+ // Alter.
+ //
+
+ struct alter_column: relational::alter_column, context
+ {
+ alter_column (base const& x): base (x) {}
+
+ virtual void
+ traverse (sema_rel::column& c)
+ {
+ // Relax (NULL) in pre and tighten (NOT NULL) in post.
+ //
+ if (pre_ != c.null ())
+ return;
+
+ if (first_)
+ first_ = false;
+ else
+ os << "," << endl
+ << " ";
+
+ os << quote_id (c.name ()) << (c.null () ? " NULL" : " NOT NULL");
+ }
+ };
+ entry<alter_column> alter_column_;
+
+ struct alter_table_pre: relational::alter_table_pre, context
+ {
+ alter_table_pre (base const& x): base (x) {}
+
+ virtual void
+ alter (sema_rel::alter_table& at)
+ {
+ // Oracle can only alter certain kinds of things together but
+ // grouped one at a time.
+ //
+ if (check<sema_rel::drop_foreign_key> (at))
+ {
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (at.name ());
+
+ instance<drop_foreign_key> dfc (*this);
+ trav_rel::unames n (*dfc);
+ names (at, n);
+ os << endl;
+
+ post_statement ();
+ }
+
+ if (check<sema_rel::add_column> (at))
+ {
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (at.name ()) << endl
+ << " ADD (";
+
+ instance<create_column> cc (*this);
+ trav_rel::unames n (*cc);
+ names (at, n);
+ os << ")" << endl;
+
+ post_statement ();
+ }
+
+ if (check_alter_column_null (at, true))
+ {
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (at.name ()) << endl
+ << " MODIFY (";
+
+ bool tl (true); // (Im)perfect forwarding.
+ instance<alter_column> ac (*this, tl);
+ trav_rel::unames n (*ac);
+ names (at, n);
+ os << ")" << endl;
+
+ post_statement ();
+ }
+ }
+ };
+ entry<alter_table_pre> alter_table_pre_;
+
+ struct alter_table_post: relational::alter_table_post, context
+ {
+ alter_table_post (base const& x): base (x) {}
+
+ virtual void
+ alter (sema_rel::alter_table& at)
+ {
+ // Oracle can only alter certain kinds of things together but
+ // grouped one at a time.
+ //
+ if (check<sema_rel::drop_column> (at))
+ {
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (at.name ()) << endl
+ << " DROP (";
+
+ instance<drop_column> dc (*this);
+ trav_rel::unames n (*dc);
+ names (at, n);
+ os << ")" << endl;
+
+ post_statement ();
+ }
+
+ if (check_alter_column_null (at, false))
+ {
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (at.name ()) << endl
+ << " MODIFY (";
+
+ bool fl (false); // (Im)perfect forwarding.
+ instance<alter_column> ac (*this, fl);
+ trav_rel::unames n (*ac);
+ names (at, n);
+ os << ")" << endl;
+
+ post_statement ();
+ }
+
+ if (check<sema_rel::add_foreign_key> (at))
+ {
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (at.name ());
+
+ instance<create_foreign_key> cfc (*this);
+ trav_rel::unames n (*cfc);
+ names (at, n);
+ os << endl;
+
+ post_statement ();
+ }
+ }
+ };
+ entry<alter_table_post> alter_table_post_;
+
+ //
+ // Schema version table.
+ //
+
+ struct version_table: relational::version_table, context
+ {
+ version_table (base const& x)
+ : base (x)
+ {
+ // If the schema name is empty, replace it with a single space
+ // to workaround the VARCHAR2 empty/NULL issue.
+ //
+ if (qs_ == "''")
+ qs_ = "' '";
+ }
+
+ virtual void
+ create_table ()
+ {
+ pre_statement ();
+
+ os << "BEGIN" << endl
+ << " EXECUTE IMMEDIATE 'CREATE TABLE " << qt_ << " (" << endl
+ << " " << qn_ << " VARCHAR2(512) NOT NULL PRIMARY KEY," << endl
+ << " " << qv_ << " NUMBER(20) NOT NULL," << endl
+ << " " << qm_ << " NUMBER(1) NOT NULL)';" << endl
+ << "EXCEPTION" << endl
+ << " WHEN OTHERS THEN" << endl
+ << " IF SQLCODE != -955 THEN RAISE; END IF;" << endl
+ << "END;" << endl;
+
+ post_statement ();
+ }
+
+ virtual void
+ create (sema_rel::version v)
+ {
+ pre_statement ();
+
+ os << "MERGE INTO " << qt_ << " USING DUAL ON (" << qn_ << " = " <<
+ qs_ << ")" << endl
+ << " WHEN NOT MATCHED THEN INSERT (" << endl
+ << " " << qn_ << ", " << qv_ << ", " << qm_ << ")" << endl
+ << " VALUES (" << qs_ << ", " << v << ", 0)" << endl;
+
+ post_statement ();
+ }
+ };
+ entry<version_table> version_table_;
+ }
+ }
+}
diff --git a/odb/odb/relational/oracle/source.cxx b/odb/odb/relational/oracle/source.cxx
new file mode 100644
index 0000000..adf9864
--- /dev/null
+++ b/odb/odb/relational/oracle/source.cxx
@@ -0,0 +1,646 @@
+// file : odb/relational/oracle/source.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/source.hxx>
+
+#include <odb/relational/oracle/common.hxx>
+#include <odb/relational/oracle/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace oracle
+ {
+ namespace source
+ {
+ namespace relational = relational::source;
+
+ struct query_parameters: relational::query_parameters, context
+ {
+ query_parameters (base const& x): base (x), i_ (0) {}
+
+ virtual string
+ next (semantics::data_member&, const string&, const string&)
+ {
+ ostringstream ss;
+ ss << ":" << ++i_;
+
+ return ss.str ();
+ }
+
+ virtual string
+ auto_id (semantics::data_member&, const string&, const string&)
+ {
+ return quote_id (sequence_name (table_)) + ".nextval";
+ }
+
+ private:
+ size_t i_;
+ };
+ entry<query_parameters> query_parameters_;
+
+ namespace
+ {
+ const char* string_buffer_types[] =
+ {
+ "oracle::bind::string", // CHAR
+ "oracle::bind::nstring", // NCHAR
+ "oracle::bind::string", // VARCHAR2
+ "oracle::bind::nstring", // NVARCHAR2
+ "oracle::bind::raw" // RAW
+ };
+
+ const char* lob_buffer_types[] =
+ {
+ "oracle::bind::blob",
+ "oracle::bind::clob",
+ "oracle::bind::nclob"
+ };
+ }
+
+ //
+ // bind
+ //
+
+ struct bind_member: relational::bind_member_impl<sql_type>,
+ member_base
+ {
+ bind_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ traverse_int32 (member_info& mi)
+ {
+ os << b << ".type = oracle::bind::" <<
+ (unsigned_integer (mi.t) ? "uinteger" : "integer") << ";"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".capacity = 4;"
+ << b << ".size = 0;"
+ << b << ".indicator = &" << arg << "." << mi.var << "indicator;";
+ }
+
+ virtual void
+ traverse_int64 (member_info& mi)
+ {
+ os << b << ".type = oracle::bind::" <<
+ (unsigned_integer (mi.t) ? "uinteger" : "integer") << ";"
+ << b << ".buffer= &" << arg << "." << mi.var << "value;"
+ << b << ".capacity = 8;"
+ << b << ".size = 0;"
+ << b << ".indicator = &" << arg << "." << mi.var << "indicator;";
+ }
+
+ virtual void
+ traverse_big_int (member_info& mi)
+ {
+ os << b << ".type = oracle::bind::number;"
+ << b << ".buffer = " << arg << "." << mi.var << "value;"
+ << b << ".capacity = static_cast<ub4> (sizeof (" << arg <<
+ "." << mi.var << "value));"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".indicator = &" << arg << "." << mi.var << "indicator;";
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << b << ".type = oracle::bind::binary_float;"
+ << b << ".buffer= &" << arg << "." << mi.var << "value;"
+ << b << ".capacity = 4;"
+ << b << ".size = 0;"
+ << b << ".indicator = &" << arg << "." << mi.var << "indicator;";
+ }
+
+ virtual void
+ traverse_double (member_info& mi)
+ {
+ os << b << ".type = oracle::bind::binary_double;"
+ << b << ".buffer= &" << arg << "." << mi.var << "value;"
+ << b << ".capacity = 8;"
+ << b << ".size = 0;"
+ << b << ".indicator = &" << arg << "." << mi.var << "indicator;";
+ }
+
+ virtual void
+ traverse_big_float (member_info& mi)
+ {
+ os << b << ".type = oracle::bind::number;"
+ << b << ".buffer = " << arg << "." << mi.var << "value;"
+ << b << ".capacity = static_cast<ub4> (sizeof (" << arg << "." <<
+ mi.var << "value));"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".indicator = &" << arg << "." << mi.var << "indicator;";
+ }
+
+ virtual void
+ traverse_date (member_info& mi)
+ {
+ os << b << ".type = oracle::bind::date;"
+ << b << ".buffer = " << arg << "." << mi.var << "value;"
+ << b << ".capacity = static_cast<ub4> (sizeof (" << arg << "." <<
+ mi.var << "value));"
+ << b << ".size = 0;"
+ << b << ".indicator = &" << arg << "." << mi.var << "indicator;";
+ }
+
+ virtual void
+ traverse_timestamp (member_info& mi)
+ {
+ os << b << ".type = oracle::bind::timestamp;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".indicator = &" << arg << "." << mi.var << "indicator;";
+ }
+
+ virtual void
+ traverse_interval_ym (member_info& mi)
+ {
+ os << b << ".type = oracle::bind::interval_ym;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".indicator = &" << arg << "." << mi.var << "indicator;";
+ }
+
+ virtual void
+ traverse_interval_ds (member_info& mi)
+ {
+ os << b << ".type = oracle::bind::interval_ds;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".indicator = &" << arg << "." << mi.var << "indicator;";
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << b << ".type = " <<
+ string_buffer_types[mi.st->type - sql_type::CHAR] << ";"
+ << b << ".buffer = " << arg << "." << mi.var << "value;"
+ << b << ".capacity = static_cast<ub4> (sizeof (" << arg <<
+ "." << mi.var << "value));"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".indicator = &" << arg << "." << mi.var << "indicator;";
+ }
+
+ virtual void
+ traverse_lob (member_info& mi)
+ {
+ os << b << ".type = " <<
+ lob_buffer_types[mi.st->type - sql_type::BLOB] << ";"
+ << b << ".buffer = &" << arg << "." << mi.var << "lob;"
+ << b << ".indicator = &" << arg << "." << mi.var << "indicator;"
+ << b << ".callback = &" << arg << "." << mi.var << "callback;"
+ << endl;
+ }
+ };
+ entry<bind_member> bind_member_;
+
+ //
+ // init image
+ //
+
+ struct init_image_member: relational::init_image_member_impl<sql_type>,
+ member_base
+ {
+ init_image_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ check_accessor (member_info& mi, member_access& ma)
+ {
+ // We cannot use accessors that return by-value for LOB
+ // members.
+ //
+ if ((mi.st->type == sql_type::BLOB ||
+ mi.st->type == sql_type::CLOB ||
+ mi.st->type == sql_type::NCLOB) &&
+ ma.by_value)
+ {
+ error (ma.loc) << "accessor returning a value cannot be used "
+ << "for a data member of Oracle LOB type" << endl;
+ info (ma.loc) << "accessor returning a const reference is required"
+ << endl;
+ info (mi.m.location ()) << "data member is defined here" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ virtual void
+ set_null (member_info& mi)
+ {
+ os << "i." << mi.var << "indicator = -1;";
+ }
+
+ virtual void
+ traverse_int32 (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;";
+ }
+
+ virtual void
+ traverse_int64 (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;";
+ }
+
+ virtual void
+ traverse_big_int (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;"
+ << "i." << mi.var << "size = static_cast<ub2> (size);";
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;";
+ }
+
+ virtual void
+ traverse_double (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;";
+ }
+
+ virtual void
+ traverse_big_float (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "sizeof (i." << mi.var << "value)," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;"
+ << "i." << mi.var << "size = static_cast<ub2> (size);";
+ }
+
+ virtual void
+ traverse_date (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;";
+ }
+
+ virtual void
+ traverse_timestamp (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;";
+ }
+
+ virtual void
+ traverse_interval_ym (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;";
+ }
+
+ virtual void
+ traverse_interval_ds (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;";
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "sizeof (i." << mi.var << "value)," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;"
+ << "i." << mi.var << "size = static_cast<ub2> (size);";
+ }
+
+ virtual void
+ traverse_lob (member_info& mi)
+ {
+ os << "i." << mi.var << "lob.position = 0;"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "callback.callback.param," << endl
+ << "i." << mi.var << "callback.context.param," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "indicator = is_null ? -1 : 0;";
+ }
+ };
+ entry<init_image_member> init_image_member_;
+
+ //
+ // init value
+ //
+
+ struct init_value_member: relational::init_value_member_impl<sql_type>,
+ member_base
+ {
+ init_value_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ get_null (string const& var) const
+ {
+ os << "i." << var << "indicator == -1";
+ }
+
+ virtual void
+ check_modifier (member_info& mi, member_access& ma)
+ {
+ // We cannot use by-value modifier for LOB members.
+ //
+ if ((mi.st->type == sql_type::BLOB ||
+ mi.st->type == sql_type::CLOB ||
+ mi.st->type == sql_type::NCLOB) &&
+ ma.placeholder ())
+ {
+ error (ma.loc) << "modifier accepting a value cannot be used "
+ << "for a data member of Oracle LOB type" << endl;
+ info (ma.loc) << "modifier returning a non-const reference is "
+ << "required" << endl;
+ info (mi.m.location ()) << "data member is defined here" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ virtual void
+ traverse_int32 (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "indicator == -1);"
+ << endl;
+ }
+
+ virtual void
+ traverse_int64 (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "indicator == -1);"
+ << endl;
+ }
+
+ virtual void
+ traverse_big_int (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "indicator == -1);"
+ << endl;
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "indicator == -1);"
+ << endl;
+ }
+
+ virtual void
+ traverse_double (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "indicator == -1);"
+ << endl;
+ }
+
+ virtual void
+ traverse_big_float (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "indicator == -1);"
+ << endl;
+ }
+
+ virtual void
+ traverse_date (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "indicator == -1);"
+ << endl;
+ }
+
+ virtual void
+ traverse_timestamp (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "indicator == -1);"
+ << endl;
+ }
+
+ virtual void
+ traverse_interval_ym (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "indicator == -1);"
+ << endl;
+ }
+
+ virtual void
+ traverse_interval_ds (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "indicator == -1);"
+ << endl;
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "indicator == -1);"
+ << endl;
+ }
+
+ virtual void
+ traverse_lob (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "callback.callback.result," << endl
+ << "i." << mi.var << "callback.context.result," << endl
+ << "i." << mi.var << "indicator == -1);"
+ << endl;
+ }
+ };
+ entry<init_value_member> init_value_member_;
+
+ struct container_traits: relational::container_traits, context
+ {
+ container_traits (base const& x): base (x) {}
+
+ virtual void
+ cache_result (string const&)
+ {
+ // Caching is not necessary since Oracle can execute several
+ // interleaving statements.
+ //
+ }
+
+ virtual void
+ init_value_extra ()
+ {
+ os << "sts.select_statement ().stream_result ();"
+ << endl;
+ }
+ };
+ entry<container_traits> container_traits_;
+
+ struct section_traits: relational::section_traits, context
+ {
+ section_traits (base const& x): base (x) {}
+
+ virtual void
+ init_value_extra ()
+ {
+ os << "st.stream_result ();";
+ }
+ };
+ entry<section_traits> section_traits_;
+
+ struct class_: relational::class_, context
+ {
+ class_ (base const& x): base (x) {}
+
+ virtual void
+ init_image_pre (type& c)
+ {
+ if (options.generate_query () &&
+ !(composite (c) || (abstract (c) && !polymorphic (c))))
+ {
+ type* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ if (poly_derived)
+ os << "{"
+ << "root_traits::image_type& ri (root_image (i));"
+ << endl;
+
+ string i (poly_derived ? "ri" : "i");
+
+ os << "if (" << i << ".change_callback_.callback != 0)" << endl
+ << "(" << i << ".change_callback_.callback) (" <<
+ i << ".change_callback_.context);";
+
+ if (poly_derived)
+ os << "}";
+ else
+ os << endl;
+ }
+ }
+
+ virtual void
+ init_value_extra ()
+ {
+ os << "st.stream_result ();";
+ }
+
+ virtual string
+ persist_statement_extra (type& c,
+ relational::query_parameters& qp,
+ persist_position p)
+ {
+ string r;
+
+ if (p == persist_after_values)
+ {
+ data_member_path* id (id_member (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ // Top-level auto id.
+ //
+ if (id != 0 && !poly_derived && auto_ (*id))
+ {
+ semantics::data_member& idb (*id->back ());
+
+ const string& name (column_qname (*id));
+ const string& type (column_type (idb));
+
+ r = "RETURNING " + convert_from (name, type, idb) +
+ " INTO " + qp.next (idb, name, type);
+ }
+ }
+
+ return r;
+ }
+
+ virtual string
+ select_trailer (type& c)
+ {
+ view_query const& vq (c.get<view_query> ("query"));
+
+ if (vq.for_update && vq.distinct)
+ {
+ error (vq.loc)
+ << "Oracle does not support FOR UPDATE with DISTINCT" << endl;
+ throw operation_failed ();
+ }
+
+ return base::select_trailer (c);
+ }
+ };
+ entry<class_> class_entry_;
+ }
+ }
+}
diff --git a/odb/odb/relational/pgsql/common.cxx b/odb/odb/relational/pgsql/common.cxx
new file mode 100644
index 0000000..6a59954
--- /dev/null
+++ b/odb/odb/relational/pgsql/common.cxx
@@ -0,0 +1,351 @@
+// file : odb/relational/pgsql/common.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/relational/pgsql/common.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace pgsql
+ {
+ //
+ // member_base
+ //
+
+ sql_type const& member_base::
+ member_sql_type (semantics::data_member& m)
+ {
+ return parse_sql_type (column_type (m, key_prefix_), m);
+ }
+
+ void member_base::
+ traverse_simple (member_info& mi)
+ {
+ switch (mi.st->type)
+ {
+ // Integral types.
+ //
+ case sql_type::BOOLEAN:
+ case sql_type::SMALLINT:
+ case sql_type::INTEGER:
+ case sql_type::BIGINT:
+ {
+ traverse_integer (mi);
+ break;
+ }
+
+ // Float types.
+ //
+ case sql_type::REAL:
+ case sql_type::DOUBLE:
+ {
+ traverse_float (mi);
+ break;
+ }
+ case sql_type::NUMERIC:
+ {
+ traverse_numeric (mi);
+ break;
+ }
+
+ // Data-time types.
+ //
+ case sql_type::DATE:
+ case sql_type::TIME:
+ case sql_type::TIMESTAMP:
+ {
+ traverse_date_time (mi);
+ break;
+ }
+
+ // String and binary types.
+ //
+ case sql_type::CHAR:
+ case sql_type::VARCHAR:
+ case sql_type::TEXT:
+ case sql_type::BYTEA:
+ {
+ traverse_string (mi);
+ break;
+ }
+ case sql_type::BIT:
+ {
+ traverse_bit (mi);
+ break;
+ }
+ case sql_type::VARBIT:
+ {
+ traverse_varbit (mi);
+ break;
+ }
+ // Other types.
+ //
+ case sql_type::UUID:
+ {
+ traverse_uuid (mi);
+ break;
+ }
+ case sql_type::invalid:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+
+ //
+ // member_image_type
+ //
+
+ namespace
+ {
+ const char* integer_types[] =
+ {
+ "bool",
+ "short",
+ "int",
+ "long long"
+ };
+
+ const char* float_types[] =
+ {
+ "float",
+ "double"
+ };
+
+ const char* date_time_types[] =
+ {
+ "int",
+ "long long",
+ "long long"
+ };
+ }
+
+ member_image_type::
+ member_image_type (base const& x)
+ : member_base::base (x), // virtual base
+ base (x)
+ {
+ }
+
+ member_image_type::
+ member_image_type ()
+ : relational::member_base (0, 0, string (), string ()) {}
+
+ member_image_type::
+ member_image_type (semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : relational::member_base (type, ct, fq_type, key_prefix) {}
+
+ string member_image_type::
+ image_type (semantics::data_member& m)
+ {
+ type_.clear ();
+ member_base::traverse (m, true);
+ return type_;
+ }
+
+ void member_image_type::
+ traverse_composite (member_info& mi)
+ {
+ type_ = "composite_value_traits< " + mi.fq_type () +
+ ", id_pgsql >::image_type";
+ }
+
+ void member_image_type::
+ traverse_integer (member_info& mi)
+ {
+ type_ += integer_types[mi.st->type - sql_type::BOOLEAN];
+ }
+
+ void member_image_type::
+ traverse_float (member_info& mi)
+ {
+ type_ = float_types[mi.st->type - sql_type::REAL];
+ }
+
+ void member_image_type::
+ traverse_numeric (member_info&)
+ {
+ type_ = "details::buffer";
+ }
+
+ void member_image_type::
+ traverse_date_time (member_info& mi)
+ {
+ type_ = date_time_types[mi.st->type - sql_type::DATE];
+ }
+
+ void member_image_type::
+ traverse_string (member_info&)
+ {
+ type_ = "details::buffer";
+ }
+
+ void member_image_type::
+ traverse_bit (member_info&)
+ {
+ type_ = "unsigned char*";
+ }
+
+ void member_image_type::
+ traverse_varbit (member_info&)
+ {
+ type_ = "details::ubuffer";
+ }
+
+ void member_image_type::
+ traverse_uuid (member_info&)
+ {
+ type_ = "unsigned char*";
+ }
+
+ entry<member_image_type> member_image_type_;
+
+ //
+ // member_database_type
+ //
+
+ namespace
+ {
+ const char* integer_database_id[] =
+ {
+ "id_boolean",
+ "id_smallint",
+ "id_integer",
+ "id_bigint"
+ };
+
+ const char* float_database_id[] =
+ {
+ "id_real",
+ "id_double"
+ };
+
+ const char* date_time_database_id[] =
+ {
+ "id_date",
+ "id_time",
+ "id_timestamp"
+ };
+
+ const char* char_bin_database_id[] =
+ {
+ "id_string", // CHAR
+ "id_string", // VARCHAR
+ "id_string", // TEXT,
+ "id_bytea" // BYTEA
+ };
+ }
+
+ member_database_type_id::
+ member_database_type_id (base const& x)
+ : member_base::base (x), // virtual base
+ base (x) {}
+
+ member_database_type_id::
+ member_database_type_id ()
+ : member_base::base (0, 0, string (), string ()), // virtual base
+ base (0, 0, string (), string ()) {}
+
+ member_database_type_id::
+ member_database_type_id (semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base::base (type, ct, fq_type, key_prefix), // virtual base
+ base (type, ct, fq_type, key_prefix) {}
+
+ string member_database_type_id::
+ database_type_id (type& m)
+ {
+ type_id_.clear ();
+ member_base::traverse (m, true);
+ return type_id_;
+ }
+
+ void member_database_type_id::
+ traverse_composite (member_info&)
+ {
+ assert (false);
+ }
+
+ void member_database_type_id::
+ traverse_integer (member_info& mi)
+ {
+ type_id_ = string ("pgsql::") +
+ integer_database_id[mi.st->type - sql_type::BOOLEAN];
+ }
+
+ void member_database_type_id::
+ traverse_float (member_info& mi)
+ {
+ type_id_ = string ("pgsql::") +
+ float_database_id[mi.st->type - sql_type::REAL];
+ }
+
+ void member_database_type_id::
+ traverse_numeric (member_info&)
+ {
+ type_id_ = "pgsql::id_numeric";
+ }
+
+ void member_database_type_id::
+ traverse_date_time (member_info& mi)
+ {
+ type_id_ = string ("pgsql::") +
+ date_time_database_id[mi.st->type - sql_type::DATE];
+ }
+
+ void member_database_type_id::
+ traverse_string (member_info& mi)
+ {
+ type_id_ = string ("pgsql::") +
+ char_bin_database_id[mi.st->type - sql_type::CHAR];
+ }
+
+ void member_database_type_id::
+ traverse_bit (member_info&)
+ {
+ type_id_ = "pgsql::id_bit";
+ }
+
+ void member_database_type_id::
+ traverse_varbit (member_info&)
+ {
+ type_id_ = "pgsql::id_varbit";
+ }
+
+ void member_database_type_id::
+ traverse_uuid (member_info&)
+ {
+ type_id_ = "pgsql::id_uuid";
+ }
+
+ entry<member_database_type_id> member_database_type_id_;
+
+ //
+ // query_columns
+ //
+
+ struct query_columns: relational::query_columns, context
+ {
+ query_columns (base const& x): base_impl (x) {}
+
+ virtual string
+ database_type_id (semantics::data_member& m)
+ {
+ return member_database_type_id_.database_type_id (m);
+ }
+
+ private:
+ member_database_type_id member_database_type_id_;
+ };
+ entry<query_columns> query_columns_;
+ }
+}
diff --git a/odb/odb/relational/pgsql/common.hxx b/odb/odb/relational/pgsql/common.hxx
new file mode 100644
index 0000000..1d383bf
--- /dev/null
+++ b/odb/odb/relational/pgsql/common.hxx
@@ -0,0 +1,159 @@
+// file : odb/relational/pgsql/common.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_PGSQL_COMMON_HXX
+#define ODB_RELATIONAL_PGSQL_COMMON_HXX
+
+#include <odb/relational/common.hxx>
+#include <odb/relational/pgsql/context.hxx>
+
+namespace relational
+{
+ namespace pgsql
+ {
+ struct member_base: virtual relational::member_base_impl<sql_type>, context
+ {
+ member_base (base const& x): base (x), base_impl (x) {}
+
+ // This c-tor is for the direct use inside the pgsql namespace.
+ // If you do use this c-tor, you should also explicitly call
+ // relational::member_base (aka base).
+ //
+ member_base () {}
+
+ virtual sql_type const&
+ member_sql_type (semantics::data_member&);
+
+ virtual void
+ traverse_simple (member_info&);
+
+ virtual void
+ traverse_integer (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_float (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_numeric (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_date_time (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_string (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_bit (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_varbit (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_uuid (member_info&)
+ {
+ }
+ };
+
+ struct member_image_type: relational::member_image_type,
+ member_base
+ {
+ member_image_type (base const&);
+ member_image_type ();
+ member_image_type (semantics::type* type,
+ const custom_cxx_type*,
+ string const& fq_type = string (),
+ string const& key_prefix = string ());
+ virtual string
+ image_type (semantics::data_member&);
+
+ virtual void
+ traverse_composite (member_info&);
+
+ virtual void
+ traverse_integer (member_info&);
+
+ virtual void
+ traverse_float (member_info&);
+
+ virtual void
+ traverse_numeric (member_info&);
+
+ virtual void
+ traverse_date_time (member_info&);
+
+ virtual void
+ traverse_string (member_info&);
+
+ virtual void
+ traverse_bit (member_info&);
+
+ virtual void
+ traverse_varbit (member_info&);
+
+ virtual void
+ traverse_uuid (member_info&);
+
+ private:
+ string type_;
+ };
+
+ struct member_database_type_id: relational::member_database_type_id,
+ member_base
+ {
+ member_database_type_id (base const&);
+ member_database_type_id ();
+ member_database_type_id (semantics::type* type,
+ const custom_cxx_type*,
+ string const& fq_type = string (),
+ string const& key_prefix = string ());
+
+ virtual string
+ database_type_id (type&);
+
+ virtual void
+ traverse_composite (member_info&);
+
+ virtual void
+ traverse_integer (member_info&);
+
+ virtual void
+ traverse_float (member_info&);
+
+ virtual void
+ traverse_numeric (member_info&);
+
+ virtual void
+ traverse_date_time (member_info&);
+
+ virtual void
+ traverse_string (member_info&);
+
+ virtual void
+ traverse_bit (member_info&);
+
+ virtual void
+ traverse_varbit (member_info&);
+
+ virtual void
+ traverse_uuid (member_info&);
+
+ private:
+ string type_id_;
+ };
+ }
+}
+#endif // ODB_RELATIONAL_PGSQL_COMMON_HXX
diff --git a/odb/odb/relational/pgsql/context.cxx b/odb/odb/relational/pgsql/context.cxx
new file mode 100644
index 0000000..7f99f5d
--- /dev/null
+++ b/odb/odb/relational/pgsql/context.cxx
@@ -0,0 +1,786 @@
+// file : odb/relational/pgsql/context.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cassert>
+#include <sstream>
+
+#include <odb/diagnostics.hxx>
+
+#include <odb/sql-token.hxx>
+#include <odb/sql-lexer.hxx>
+
+#include <odb/relational/pgsql/context.hxx>
+#include <odb/relational/pgsql/common.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace pgsql
+ {
+ namespace
+ {
+ struct type_map_entry
+ {
+ char const* const cxx_type;
+ char const* const db_type;
+ char const* const db_id_type;
+ bool const null;
+ };
+
+ type_map_entry type_map[] =
+ {
+ {"bool", "BOOLEAN", 0, false},
+
+ {"char", "CHAR(1)", 0, false},
+ {"signed char", "SMALLINT", 0, false},
+ {"unsigned char", "SMALLINT", 0, false},
+
+ {"short int", "SMALLINT", 0, false},
+ {"short unsigned int", "SMALLINT", 0, false},
+
+ {"int", "INTEGER", 0, false},
+ {"unsigned int", "INTEGER", 0, false},
+
+ {"long int", "BIGINT", 0, false},
+ {"long unsigned int", "BIGINT", 0, false},
+
+ {"long long int", "BIGINT", 0, false},
+ {"long long unsigned int", "BIGINT", 0, false},
+
+ {"float", "REAL", 0, false},
+ {"double", "DOUBLE PRECISION", 0, false},
+
+ {"::std::string", "TEXT", 0, false},
+
+ {"::size_t", "BIGINT", 0, false},
+ {"::std::size_t", "BIGINT", 0, false}
+ };
+ }
+
+ context* context::current_;
+
+ context::
+ ~context ()
+ {
+ if (current_ == this)
+ current_ = 0;
+ }
+
+ context::
+ context (ostream& os,
+ semantics::unit& u,
+ options_type const& ops,
+ features_type& f,
+ sema_rel::model* m)
+ : root_context (os, u, ops, f, data_ptr (new (shared) data (os))),
+ base_context (static_cast<data*> (root_context::data_.get ()), m),
+ data_ (static_cast<data*> (base_context::data_))
+ {
+ assert (current_ == 0);
+ current_ = this;
+
+ generate_grow = true;
+ need_alias_as = true;
+ insert_send_auto_id = false;
+ delay_freeing_statement_result = false;
+ need_image_clone = false;
+ generate_bulk = true;
+ global_index = true;
+ global_fkey = false;
+ data_->bind_vector_ = "pgsql::bind*";
+ data_->truncated_vector_ = "bool*";
+
+ // Populate the C++ type to DB type map.
+ //
+ for (size_t i (0); i < sizeof (type_map) / sizeof (type_map_entry); ++i)
+ {
+ type_map_entry const& e (type_map[i]);
+
+ type_map_type::value_type v (
+ e.cxx_type,
+ db_type_type (
+ e.db_type, e.db_id_type ? e.db_id_type : e.db_type, e.null));
+
+ data_->type_map_.insert (v);
+ }
+ }
+
+ context::
+ context ()
+ : data_ (current ().data_)
+ {
+ }
+
+ namespace
+ {
+ struct has_grow: traversal::class_
+ {
+ has_grow (bool& r, user_section* s)
+ : r_ (r), section_ (s)
+ {
+ *this >> inherits_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ // Ignore transient bases.
+ //
+ if (!(context::object (c) || context::composite (c)))
+ return;
+
+ if (section_ == 0 && c.count ("pgsql-grow"))
+ r_ = c.get<bool> ("pgsql-grow");
+ else
+ {
+ // r_ should be false.
+ //
+ inherits (c);
+
+ if (!r_)
+ names (c);
+
+ if (section_ == 0)
+ c.set ("pgsql-grow", r_);
+ }
+ }
+
+ private:
+ bool& r_;
+ user_section* section_;
+ traversal::inherits inherits_;
+ };
+
+ struct has_grow_member: member_base
+ {
+ has_grow_member (bool& r, user_section* section = 0)
+ : relational::member_base (0, 0, string (), string (), section),
+ r_ (r) {}
+
+ has_grow_member (bool& r,
+ user_section* section,
+ semantics::type* t,
+ const custom_cxx_type* ct,
+ string const& key_prefix = string ())
+ : relational::member_base (t, ct, string (), key_prefix, section),
+ r_ (r) {}
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ // If we have a key prefix (container), then it can't be in a
+ // section (while mi.m can). The same for top-level -- if we got
+ // called, then we shouldn't ignore it.
+ //
+ return !key_prefix_.empty () || top_level_ ||
+ (section_ == 0 && !separate_load (mi.m)) ||
+ (section_ != 0 && *section_ == section (mi.m));
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ // By calling grow() instead of recursing, we reset any overrides.
+ // We also don't pass section since they don't apply inside
+ // composites.
+ //
+ r_ = r_ || context::grow (dynamic_cast<semantics::class_&> (mi.t));
+ }
+
+ virtual void
+ traverse_numeric (member_info&)
+ {
+ r_ = true;
+ }
+
+ virtual void
+ traverse_string (member_info&)
+ {
+ r_ = true;
+ }
+
+ virtual void
+ traverse_varbit (member_info&)
+ {
+ r_ = true;
+ }
+
+ private:
+ bool& r_;
+ };
+ }
+
+ bool context::
+ grow_impl (semantics::class_& c, user_section* section)
+ {
+ if (section == 0 && c.count ("pgsql-grow"))
+ return c.get<bool> ("pgsql-grow");
+
+ bool r (false);
+ has_grow ct (r, section);
+ has_grow_member mt (r, section);
+ traversal::names names;
+ ct >> names >> mt;
+ ct.traverse (c);
+ return r;
+ }
+
+ bool context::
+ grow_impl (semantics::data_member& m)
+ {
+ bool r (false);
+ has_grow_member mt (r);
+ mt.traverse (m, true);
+ return r;
+ }
+
+ bool context::
+ grow_impl (semantics::data_member& m,
+ semantics::type& t,
+ const custom_cxx_type* ct,
+ string const& kp)
+ {
+ bool r (false);
+ has_grow_member mt (r, 0, &t, ct, kp);
+ mt.traverse (m, true);
+ return r;
+ }
+
+ string const& context::
+ convert_expr (string const& sqlt, semantics::data_member& m, bool to)
+ {
+ sql_type const& t (parse_sql_type (sqlt, m));
+ return to ? t.to : t.from;
+ }
+
+ string context::
+ quote_id_impl (qname const& id) const
+ {
+ string r;
+
+ bool f (true);
+ for (qname::iterator i (id.begin ()); i < id.end (); ++i)
+ {
+ if (i->empty ())
+ continue;
+
+ // Warn if the name is greater than the (NAMEDATALEN - 1) limit,
+ // which is 63 in the default PG build.
+ //
+ if (i->size () > 63)
+ {
+ cerr << "warning: SQL name '" << *i << "' is longer than "
+ << "the default PostgreSQL name limit of 63 characters "
+ << "and may be truncated" << endl;
+
+ cerr << "info: consider shortening it using #pragma db "
+ << "table/column/index or --*-regex options" << endl;
+ }
+
+ if (f)
+ f = false;
+ else
+ r += '.';
+
+ r += '"';
+ r += *i;
+ r += '"';
+ }
+
+ return r;
+ }
+
+ string context::
+ statement_name (string const& type, string const& name, semantics::node& n)
+ {
+ // Put the type first so that in the case of truncation it
+ // remains thus lowering the chance of a clash.
+ //
+ string r (type);
+ r += '_';
+ r += name;
+
+ r = transform_name (r, sql_name_statement);
+
+ // Warn if the name is greater than the (NAMEDATALEN - 1) limit,
+ // which is 63 in the default PG build.
+ //
+ // Note that we have to do it in addition to the above since this
+ // name doesn't go through quote_id().
+ //
+ if (r.size () > 63)
+ {
+ location const& l (n.location ());
+
+ warn (l) << "prepared statement name '" << r << "' is longer than "
+ << "the default PostgreSQL name limit of 63 characters "
+ << "and may be truncated" << endl;
+
+ info (l) << "consider shortening the corresponding namespace "
+ << "name, class name, or data member name" << endl;
+
+ info (l) << "or shortening the statement name itself using the "
+ << "--statement-regex option" << endl;
+ }
+
+ return r;
+ }
+
+ string context::
+ database_type_impl (semantics::type& t,
+ semantics::names* hint,
+ bool id,
+ bool* null)
+ {
+ string r (base_context::database_type_impl (t, hint, id, null));
+
+ if (!r.empty ())
+ return r;
+
+ using semantics::array;
+
+ // char[N] mapping.
+ //
+ if (array* a = dynamic_cast<array*> (&t))
+ {
+ semantics::type& bt (a->base_type ());
+ if (bt.is_a<semantics::fund_char> ())
+ {
+ unsigned long long n (a->size ());
+
+ if (n == 0)
+ return r;
+ else if (n == 1)
+ r = "CHAR(";
+ else
+ {
+ r = "VARCHAR(";
+ n--;
+ }
+
+ ostringstream ostr;
+ ostr << n;
+ r += ostr.str ();
+ r += ')';
+ }
+ }
+
+ return r;
+ }
+
+ //
+ // SQL type parsing.
+ //
+
+ sql_type const& context::
+ parse_sql_type (string const& t, semantics::data_member& m, bool custom)
+ {
+ // If this proves to be too expensive, we can maintain a cache of
+ // parsed types across contexts.
+ //
+ data::sql_type_cache::iterator i (data_->sql_type_cache_.find (t));
+
+ if (i != data_->sql_type_cache_.end ()
+ && (custom ? i->second.custom_cached : i->second.straight_cached))
+ {
+ return (custom ? i->second.custom : i->second.straight);
+ }
+ else
+ {
+ try
+ {
+ sql_type st (
+ parse_sql_type (
+ t,
+ custom ? &unit.get<custom_db_types> ("custom-db-types") : 0));
+
+ if (custom)
+ return data_->sql_type_cache_[t].cache_custom (st);
+ else
+ return data_->sql_type_cache_[t].cache_straight (st);
+ }
+ catch (invalid_sql_type const& e)
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: " << e.message () << endl;
+
+ throw operation_failed ();
+ }
+ }
+ }
+
+ inline sql_type
+ error (bool fail, string const& m)
+ {
+ if (!fail)
+ return sql_type ();
+ else
+ throw context::invalid_sql_type (m);
+ }
+
+ sql_type context::
+ parse_sql_type (string sqlt, custom_db_types const* ct)
+ {
+ try
+ {
+ sql_type r;
+
+ // First run the type through the custom mapping, if requested.
+ //
+ if (ct != 0)
+ {
+ for (custom_db_types::const_iterator i (ct->begin ());
+ i != ct->end (); ++i)
+ {
+ custom_db_type const& t (*i);
+
+ if (t.type.match (sqlt))
+ {
+ r.to = t.type.replace (sqlt, t.to);
+ r.from = t.type.replace (sqlt, t.from);
+ sqlt = t.type.replace (sqlt, t.as);
+ break;
+ }
+ }
+ }
+
+ sql_lexer l (sqlt);
+
+ // While most type names use single identifier, there are
+ // a couple of exceptions to this rule:
+ //
+ // BIT VARYING (VARBIT)
+ // CHARACTER VARYING (VARRCHAR)
+ // DOUBLE PRECISION (DOUBLE)
+ // TIME WITH TIME ZONE (not currently supported)
+ // TIMESTAMP WITH TIME ZONE (not currently supported)
+ //
+
+ enum state
+ {
+ parse_prefix,
+ parse_name,
+ parse_range,
+ parse_suffix,
+ parse_done
+ };
+
+ state s (parse_prefix);
+ string prefix;
+ bool flt (false);
+
+ for (sql_token t (l.next ());
+ s != parse_done && t.type () != sql_token::t_eos;
+ t = l.next ())
+ {
+ sql_token::token_type tt (t.type ());
+
+ switch (s)
+ {
+ case parse_prefix:
+ {
+ if (tt == sql_token::t_identifier)
+ {
+ string const& id (context::upcase (t.identifier ()));
+
+ if (id == "BIT" ||
+ id == "CHARACTER" ||
+ id == "DOUBLE")
+ {
+ prefix = id;
+ s = parse_name;
+ continue;
+ }
+ }
+
+ s = parse_name;
+ }
+ // Fall through.
+ case parse_name:
+ {
+ if (tt == sql_token::t_identifier)
+ {
+ bool match (true);
+ string const& id (context::upcase (t.identifier ()));
+
+ //
+ // Numeric types.
+ //
+ if (id == "BOOL" || id == "BOOLEAN")
+ {
+ r.type = sql_type::BOOLEAN;
+ }
+ else if (id == "SMALLINT" || id == "INT2")
+ {
+ r.type = sql_type::SMALLINT;
+ }
+ else if (id == "INT" ||
+ id == "INTEGER" ||
+ id == "INT4")
+ {
+ r.type = sql_type::INTEGER;
+ }
+ else if (id == "BIGINT")
+ {
+ r.type = sql_type::BIGINT;
+ }
+ else if (id == "REAL" || id == "FLOAT4")
+ {
+ r.type = sql_type::REAL;
+ }
+ else if ((id == "PRECISION" && prefix == "DOUBLE") ||
+ id == "FLOAT8")
+ {
+ r.type = sql_type::DOUBLE;
+ }
+ else if (id == "FLOAT")
+ {
+ // Assign a type only once we know the precision of the
+ // float.
+ //
+ flt = true;
+ }
+ else if (id == "NUMERIC" || id == "DECIMAL")
+ {
+ r.type = sql_type::NUMERIC;
+ }
+ //
+ // Date-time types.
+ //
+ else if (id == "DATE")
+ {
+ r.type = sql_type::DATE;
+ }
+ else if (id == "TIME")
+ {
+ r.type = sql_type::TIME;
+ }
+ else if (id == "TIMETZ")
+ {
+ return error (ct, "PostgreSQL time zones are not currently "
+ "supported");
+ }
+ else if (id == "TIMESTAMP")
+ {
+ r.type = sql_type::TIMESTAMP;
+ }
+ else if (id == "TIMESTAMPTZ")
+ {
+ return error (ct, "PostgreSQL time zones are not currently "
+ "supported");
+ }
+ //
+ // String and binary types.
+ //
+ else if (id == "CHAR")
+ {
+ r.type = sql_type::CHAR;
+ }
+ else if (id == "VARCHAR")
+ {
+ r.type = sql_type::VARCHAR;
+ }
+ else if (id == "TEXT")
+ {
+ r.type = sql_type::TEXT;
+ }
+ else if (id == "VARYING")
+ {
+ if (prefix == "BIT")
+ r.type = sql_type::VARBIT;
+ else if (prefix == "CHARACTER")
+ r.type = sql_type::VARCHAR;
+ }
+ else if (id == "BYTEA")
+ {
+ r.type = sql_type::BYTEA;
+ }
+ else if (id == "VARBIT")
+ {
+ r.type = sql_type::VARBIT;
+ }
+ //
+ // Other types.
+ //
+ else if (id == "UUID")
+ {
+ r.type = sql_type::UUID;
+ }
+ else
+ match = false;
+
+ if (match)
+ {
+ s = parse_range;
+ continue;
+ }
+ }
+
+ // Some prefixes can also be type names if not followed
+ // by the actual type name.
+ //
+ if (!prefix.empty ())
+ {
+ if (prefix == "BIT")
+ {
+ r.type = sql_type::BIT;
+ }
+ else if (prefix == "CHARACTER")
+ {
+ r.type = sql_type::CHAR;
+ }
+ }
+
+ if (r.type == sql_type::invalid)
+ {
+ return error (
+ ct,
+ tt == sql_token::t_identifier
+ ? "unknown PostgreSQL type '" + t.identifier () + "'"
+ : "expected PostgreSQL type name");
+ }
+
+ s = parse_range;
+ }
+ // Fall through.
+ case parse_range:
+ {
+ if (t.punctuation () == sql_token::p_lparen)
+ {
+ t = l.next ();
+
+ if (t.type () != sql_token::t_int_lit)
+ {
+ return error (ct, "integer range expected in PostgreSQL "
+ "type declaration");
+ }
+
+ unsigned int v;
+ istringstream is (t.literal ());
+
+ if (!(is >> v && is.eof ()))
+ {
+ return error (ct, "invalid range value '" + t.literal () +
+ "' in PostgreSQL type declaration");
+ }
+
+ r.range = true;
+ r.range_value = v;
+
+ t = l.next ();
+
+ if (t.punctuation () == sql_token::p_comma)
+ {
+ // We have the second range value. Skip it.
+ //
+ l.next ();
+ t = l.next ();
+ }
+
+ if (t.punctuation () != sql_token::p_rparen)
+ {
+ return error (ct, "expected ')' in PostgreSQL type "
+ "declaration");
+ }
+
+ s = parse_suffix;
+ continue;
+ }
+
+ s = parse_suffix;
+ }
+ // Fall through.
+ case parse_suffix:
+ {
+ if (r.type == sql_type::TIME || r.type == sql_type::TIMESTAMP)
+ {
+ string const& id1 (context::upcase (t.identifier ()));
+
+ if (id1 == "WITH")
+ {
+ t = l.next ();
+ tt = t.type ();
+
+ if (tt == sql_token::t_identifier)
+ {
+ string const& id2 (context::upcase (t.identifier ()));
+
+ if (id2 == "TIME")
+ {
+ t = l.next ();
+ tt = t.type ();
+
+ if (tt == sql_token::t_identifier)
+ {
+ string const& id3 (context::upcase (t.identifier ()));
+
+ if (id3 == "ZONE")
+ {
+ // This code shall not fall through.
+ //
+ return error (ct, "PostgreSQL time zones are not "
+ "currently supported");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return error (
+ ct,
+ tt == sql_token::t_identifier
+ ? "unknown PostgreSQL type '" + t.identifier () + "'"
+ : "unknown PostgreSQL type");
+ }
+ case parse_done:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+
+ if (s == parse_name && !prefix.empty ())
+ {
+ // Some prefixes can also be type names if not followed
+ // by the actual type name.
+ //
+ if (prefix == "BIT")
+ {
+ r.type = sql_type::BIT;
+ }
+ else if (prefix == "CHARACTER")
+ {
+ r.type = sql_type::CHAR;
+ }
+ }
+
+ if (flt)
+ {
+ r.type = r.range && r.range_value < 25 ?
+ sql_type::REAL :
+ sql_type::DOUBLE;
+ }
+
+ if (r.type == sql_type::invalid)
+ return error (ct, "incomplete PostgreSQL type declaration");
+
+ // If range is omitted for CHAR or BIT types, it defaults to 1.
+ //
+ if ((r.type == sql_type::CHAR || r.type == sql_type::BIT) && !r.range)
+ {
+ r.range = true;
+ r.range_value = 1;
+ }
+
+ return r;
+ }
+ catch (sql_lexer::invalid_input const& e)
+ {
+ return error (ct, "invalid PostgreSQL type declaration: " + e.message);
+ }
+ }
+ }
+}
diff --git a/odb/odb/relational/pgsql/context.hxx b/odb/odb/relational/pgsql/context.hxx
new file mode 100644
index 0000000..64e0b1a
--- /dev/null
+++ b/odb/odb/relational/pgsql/context.hxx
@@ -0,0 +1,192 @@
+// file : odb/relational/pgsql/context.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_PGSQL_CONTEXT_HXX
+#define ODB_RELATIONAL_PGSQL_CONTEXT_HXX
+
+#include <map>
+
+#include <odb/relational/context.hxx>
+
+namespace relational
+{
+ namespace pgsql
+ {
+ struct sql_type
+ {
+ // Keep the order in each block of types.
+ //
+ enum core_type
+ {
+ // Integral types.
+ //
+ BOOLEAN,
+ SMALLINT,
+ INTEGER,
+ BIGINT,
+
+ // Float types.
+ //
+ REAL,
+ DOUBLE,
+ NUMERIC,
+
+ // Data-time types.
+ //
+ DATE,
+ TIME,
+ TIMESTAMP,
+
+ // String and binary types.
+ //
+ CHAR,
+ VARCHAR,
+ TEXT,
+ BYTEA,
+ BIT,
+ VARBIT,
+
+ // Other types.
+ //
+ UUID,
+
+ // Invalid type.
+ //
+ invalid
+ };
+
+ sql_type () : type (invalid), range (false) {}
+
+ core_type type;
+
+ // VARBIT maximum length is 2^31 - 1 bit. String types can hold a
+ // maximum of 1GB of data.
+ //
+ bool range;
+ unsigned int range_value;
+
+ // Conversion expressions for custom database types.
+ //
+ std::string to;
+ std::string from;
+ };
+
+ class context: public virtual relational::context
+ {
+ public:
+ sql_type const&
+ parse_sql_type (string const&,
+ semantics::data_member&,
+ bool custom = true);
+ public:
+ struct invalid_sql_type
+ {
+ invalid_sql_type (string const& message): message_ (message) {}
+
+ string const&
+ message () const {return message_;}
+
+ private:
+ string message_;
+ };
+
+ // If custom_db_types is NULL, then this function returns
+ // invalid type instead of throwing in case an unknown type
+ // is encountered.
+ //
+ static sql_type
+ parse_sql_type (string, custom_db_types const* = 0);
+
+ public:
+ // Construct statement name from a given type and name.
+ //
+ string
+ statement_name (string const& type,
+ string const& name,
+ semantics::node&);
+
+ protected:
+ virtual string const&
+ convert_expr (string const&, semantics::data_member&, bool);
+
+ virtual string
+ quote_id_impl (qname const&) const;
+
+ virtual bool
+ grow_impl (semantics::class_&, user_section*);
+
+ virtual bool
+ grow_impl (semantics::data_member&);
+
+ virtual bool
+ grow_impl (semantics::data_member&,
+ semantics::type&,
+ const custom_cxx_type*,
+ string const&);
+
+ protected:
+ virtual string
+ database_type_impl (semantics::type&, semantics::names*, bool, bool*);
+
+ public:
+ virtual
+ ~context ();
+
+ context ();
+ context (std::ostream&,
+ semantics::unit&,
+ options_type const&,
+ features_type&,
+ sema_rel::model*);
+
+ static context&
+ current ()
+ {
+ return *current_;
+ }
+
+ private:
+ static context* current_;
+
+ private:
+ struct data: base_context::data
+ {
+ data (std::ostream& os): base_context::data (os) {}
+
+ struct sql_type_cache_entry
+ {
+ sql_type_cache_entry ()
+ : custom_cached (false), straight_cached (false) {}
+
+ sql_type const&
+ cache_custom (sql_type const& t)
+ {
+ custom = t;
+ custom_cached = true;
+ return custom;
+ }
+
+ sql_type const&
+ cache_straight (sql_type const& t)
+ {
+ straight = t;
+ straight_cached = true;
+ return straight;
+ }
+
+ sql_type custom; // With custom mapping.
+ sql_type straight; // Without custom mapping.
+
+ bool custom_cached;
+ bool straight_cached;
+ };
+
+ typedef std::map<string, sql_type_cache_entry> sql_type_cache;
+ sql_type_cache sql_type_cache_;
+ };
+ data* data_;
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_PGSQL_CONTEXT_HXX
diff --git a/odb/odb/relational/pgsql/header.cxx b/odb/odb/relational/pgsql/header.cxx
new file mode 100644
index 0000000..c3efc3e
--- /dev/null
+++ b/odb/odb/relational/pgsql/header.cxx
@@ -0,0 +1,285 @@
+// file : odb/relational/pgsql/header.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/header.hxx>
+
+#include <odb/relational/pgsql/common.hxx>
+#include <odb/relational/pgsql/context.hxx>
+
+namespace relational
+{
+ namespace pgsql
+ {
+ namespace header
+ {
+ namespace relational = relational::header;
+
+ struct class1: relational::class1
+ {
+ class1 (base const& x): base (x) {}
+
+ virtual void
+ object_public_extra_post (type& c)
+ {
+ bool abst (abstract (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+
+ if (abst && !poly)
+ return;
+
+ data_member_path* id (id_member (c));
+ semantics::data_member* optimistic (context::optimistic (c));
+
+ column_count_type const& cc (column_count (c));
+
+ size_t update_columns (
+ cc.total - cc.id - cc.inverse - cc.readonly - cc.separate_update);
+
+ // Statement names.
+ //
+ os << "static const char persist_statement_name[];";
+
+ if (id != 0)
+ {
+ if (poly_derived)
+ os << "static const char* const find_statement_names[" <<
+ (abst ? "1" : "depth") << "];";
+ else
+ os << "static const char find_statement_name[];";
+
+ if (poly && !poly_derived)
+ os << "static const char find_discriminator_statement_name[];";
+
+ if (update_columns != 0)
+ os << "static const char update_statement_name[];";
+
+ os << "static const char erase_statement_name[];";
+
+ if (optimistic != 0)
+ os << "static const char optimistic_erase_statement_name[];";
+ }
+
+ // Query statement name.
+ //
+ if (options.generate_query ())
+ os << "static const char query_statement_name[];"
+ << "static const char erase_query_statement_name[];";
+
+ os << endl;
+
+ // Statement types.
+ //
+ os << "static const unsigned int persist_statement_types[];";
+
+ if (id != 0)
+ {
+ os << "static const unsigned int find_statement_types[];";
+
+ if (update_columns != 0)
+ os << "static const unsigned int update_statement_types[];";
+
+ if (optimistic != 0)
+ os << "static const unsigned int " <<
+ "optimistic_erase_statement_types[];";
+ }
+
+ os << endl;
+
+ if (poly_derived)
+ return;
+
+ // Bulk operations batch size.
+ //
+ {
+ unsigned long long b (c.count ("bulk")
+ ? c.get<unsigned long long> ("bulk")
+ : 1);
+
+ os << "static const std::size_t batch = " << b << "UL;"
+ << endl;
+ }
+ }
+
+ virtual void
+ view_public_extra_post (type&)
+ {
+ // Statement names.
+ //
+ os << "static const char query_statement_name[];"
+ << endl;
+ }
+ };
+ entry<class1> class1_entry_;
+
+ struct container_traits: relational::container_traits, context
+ {
+ container_traits (base const& x): base (x) {}
+
+ virtual void
+ container_public_extra_pre (semantics::data_member& m,
+ semantics::type& t)
+ {
+ if (!object (c_) || (abstract (c_) && !polymorphic (c_)))
+ return;
+
+ bool smart (!inverse (m, "value") && !unordered (m) &&
+ container_smart (t));
+
+ // Container statement names.
+ //
+ os << "static const char select_name[];"
+ << "static const char insert_name[];";
+
+ if (smart)
+ os << "static const char update_name[];";
+
+ os << "static const char delete_name[];"
+ << endl;
+
+ // Container statement types.
+ //
+ os << "static const unsigned int insert_types[];";
+
+ if (smart)
+ os << "static const unsigned int update_types[];"
+ << "static const unsigned int delete_types[];";
+
+ os << endl;
+ }
+ };
+ entry<container_traits> container_traits_;
+
+ struct section_traits: relational::section_traits, context
+ {
+ section_traits (base const& x): base (x) {}
+
+ virtual void
+ section_public_extra_post (user_section& s)
+ {
+ semantics::class_* poly_root (polymorphic (c_));
+ bool poly (poly_root != 0);
+
+ if (!poly && (abstract (c_) ||
+ s.special == user_section::special_version))
+ return;
+
+ bool load (s.total != 0 && s.separate_load ());
+ bool load_opt (s.optimistic () && s.separate_load ());
+
+ bool update (s.total != s.inverse + s.readonly); // Always separate.
+ bool update_opt (s.optimistic () && (s.readwrite_containers || poly));
+
+ // Statement names.
+ //
+ if (load || load_opt)
+ os << "static const char select_name[];"
+ << endl;
+
+ if (update || update_opt)
+ os << "static const char update_name[];"
+ << endl;
+
+ // Statement types.
+ //
+ if (update || update_opt)
+ os << "static const unsigned int update_types[];";
+ }
+ };
+ entry<section_traits> section_traits_;
+
+ struct image_member: relational::image_member_impl<sql_type>,
+ member_base
+ {
+ image_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x) {}
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_numeric (member_info& mi)
+ {
+ // Exchanged as strings. Can have up to 1000 digits not counting
+ // '-' and '.'.
+ //
+
+ os << image_type << " " << mi.var << "value;"
+ << "std::size_t " << mi.var << "size;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_date_time (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "std::size_t " << mi.var << "size;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_bit (member_info& mi)
+ {
+ // Additional 4 bytes at the beginning of the array specify
+ // the number of significant bits in the image. This number
+ // is stored in network byte order.
+ //
+ unsigned int n (4 + mi.st->range / 8 + (mi.st->range % 8 ? 1 : 0));
+
+ os << "unsigned char " << mi.var << "value[" << n << "];"
+ << "std::size_t " << mi.var << "size;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_varbit (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "std::size_t " << mi.var << "size;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_uuid (member_info& mi)
+ {
+ // UUID is a 16-byte sequence.
+ //
+ os << "unsigned char " << mi.var << "value[16];"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+ };
+ entry<image_member> image_member_;
+ }
+ }
+}
diff --git a/odb/odb/relational/pgsql/inline.cxx b/odb/odb/relational/pgsql/inline.cxx
new file mode 100644
index 0000000..08688c3
--- /dev/null
+++ b/odb/odb/relational/pgsql/inline.cxx
@@ -0,0 +1,42 @@
+// file : odb/relational/pgsql/inline.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/inline.hxx>
+
+#include <odb/relational/pgsql/common.hxx>
+#include <odb/relational/pgsql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace pgsql
+ {
+ namespace inline_
+ {
+ namespace relational = relational::inline_;
+
+ struct null_member: relational::null_member_impl<sql_type>,
+ member_base
+ {
+ null_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ traverse_simple (member_info& mi)
+ {
+ if (get_)
+ os << "r = r && i." << mi.var << "null;";
+ else
+ os << "i." << mi.var << "null = true;";
+ }
+ };
+ entry<null_member> null_member_;
+ }
+ }
+}
diff --git a/odb/odb/relational/pgsql/model.cxx b/odb/odb/relational/pgsql/model.cxx
new file mode 100644
index 0000000..092f8bb
--- /dev/null
+++ b/odb/odb/relational/pgsql/model.cxx
@@ -0,0 +1,101 @@
+// file : odb/relational/pgsql/model.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <odb/diagnostics.hxx>
+
+#include <odb/relational/model.hxx>
+
+#include <odb/relational/pgsql/common.hxx>
+#include <odb/relational/pgsql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace pgsql
+ {
+ namespace model
+ {
+ namespace relational = relational::model;
+
+ struct object_columns: relational::object_columns, context
+ {
+ object_columns (base const& x): base (x) {}
+
+ virtual void
+ traverse_object (semantics::class_& c)
+ {
+ base::traverse_object (c);
+
+ if (context::top_object == &c)
+ {
+ // Make sure that the auto id type is INTEGER or BIGINT.
+ //
+ if (pkey_ != 0 && pkey_->auto_ ())
+ {
+ // Should be a single column.
+ //
+ sema_rel::column& c (pkey_->contains_begin ()->column ());
+
+ // This should never fail since we have already parsed this.
+ //
+ sql_type const& t (parse_sql_type (c.type ()));
+
+ if (t.type != sql_type::INTEGER && t.type != sql_type::BIGINT)
+ {
+ location const& l (c.get<location> ("cxx-location"));
+ error (l) << "automatically assigned object id must map "
+ << "to PostgreSQL INTEGER or BIGINT" << endl;
+ throw operation_failed ();
+ }
+ }
+ }
+ }
+
+ virtual string
+ default_bool (semantics::data_member&, bool v)
+ {
+ return v ? "TRUE" : "FALSE";
+ }
+
+ virtual string
+ default_enum (semantics::data_member& m, tree en, string const&)
+ {
+ // Make sure the column is mapped to an integer type.
+ //
+ switch (parse_sql_type (column_type (), m, false).type)
+ {
+ case sql_type::SMALLINT:
+ case sql_type::INTEGER:
+ case sql_type::BIGINT:
+ break;
+ default:
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: column with default value specified as C++ "
+ << "enumerator must map to PostgreSQL integer type" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ using semantics::enumerator;
+
+ enumerator& e (dynamic_cast<enumerator&> (*unit.find (en)));
+
+ ostringstream ostr;
+
+ if (e.enum_ ().unsigned_ ())
+ ostr << e.value ();
+ else
+ ostr << static_cast<long long> (e.value ());
+
+ return ostr.str ();
+ }
+ };
+ entry<object_columns> object_columns_;
+ }
+ }
+}
diff --git a/odb/odb/relational/pgsql/schema.cxx b/odb/odb/relational/pgsql/schema.cxx
new file mode 100644
index 0000000..b9c3f2e
--- /dev/null
+++ b/odb/odb/relational/pgsql/schema.cxx
@@ -0,0 +1,266 @@
+// file : odb/relational/pgsql/schema.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/schema.hxx>
+
+#include <odb/relational/pgsql/common.hxx>
+#include <odb/relational/pgsql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace pgsql
+ {
+ namespace schema
+ {
+ namespace relational = relational::schema;
+ using relational::table_set;
+
+ //
+ // Drop.
+ //
+
+ struct drop_table: relational::drop_table, context
+ {
+ drop_table (base const& x): base (x) {}
+
+ virtual void
+ traverse (sema_rel::table& t, bool migration)
+ {
+ // For migration drop foreign keys explicitly in pre-migration.
+ //
+ if (migration)
+ {
+ base::traverse (t, migration);
+ return;
+ }
+
+ // For schema creation we use the CASCADE clause to drop foreign
+ // keys.
+ //
+ if (pass_ != 2)
+ return;
+
+ pre_statement ();
+ os << "DROP TABLE " << (migration ? "" : "IF EXISTS ") <<
+ quote_id (t.name ()) << " CASCADE" << endl;
+ post_statement ();
+ }
+ };
+ entry<drop_table> drop_table_;
+
+ //
+ // Create.
+ //
+
+ struct create_column: relational::create_column, context
+ {
+ create_column (base const& x): base (x) {}
+
+ virtual void
+ type (sema_rel::column& c, bool auto_)
+ {
+ if (auto_)
+ {
+ // This should never fail since we have already parsed this.
+ //
+ sql_type const& t (parse_sql_type (c.type ()));
+
+ // The model creation code makes sure it is one of these type.
+ //
+ if (t.type == sql_type::INTEGER)
+ os << "SERIAL";
+ else if (t.type == sql_type::BIGINT)
+ os << "BIGSERIAL";
+ }
+ else
+ base::type (c, auto_);
+ }
+ };
+ entry<create_column> create_column_;
+
+ struct create_foreign_key: relational::create_foreign_key, context
+ {
+ create_foreign_key (base const& x): base (x) {}
+
+ virtual void
+ deferrable (sema_rel::deferrable d)
+ {
+ os << endl
+ << " INITIALLY " << d;
+ }
+ };
+ entry<create_foreign_key> create_foreign_key_;
+
+ struct create_index: relational::create_index, context
+ {
+ create_index (base const& x): base (x) {}
+
+ virtual void
+ create (sema_rel::index& in)
+ {
+ os << "CREATE ";
+
+ if (!in.type ().empty ())
+ {
+ // Handle the CONCURRENTLY keyword.
+ //
+ string const& t (in.type ());
+
+ if (t == "concurrently" || t == "CONCURRENTLY")
+ {
+ os << "INDEX " << t;
+ }
+ else
+ {
+ size_t p (t.rfind (' '));
+ string s (t, (p != string::npos ? p + 1 : 0), string::npos);
+
+ if (s == "concurrently" || s == "CONCURRENTLY")
+ os << string (t, 0, p) << " INDEX " << s;
+ else
+ os << t << " INDEX";
+ }
+ }
+ else
+ os << "INDEX";
+
+ os << " " << name (in) << endl
+ << " ON " << table_name (in);
+
+ if (!in.method ().empty ())
+ os << " USING " << in.method ();
+
+ os << " (";
+ columns (in);
+ os << ")" << endl;
+
+ if (!in.options ().empty ())
+ os << ' ' << in.options () << endl;
+ }
+ };
+ entry<create_index> create_index_;
+
+ //
+ // Alter.
+ //
+
+ struct alter_column: relational::alter_column, context
+ {
+ alter_column (base const& x): base (x) {}
+
+ virtual void
+ alter (sema_rel::column& c)
+ {
+ os << quote_id (c.name ()) << " " <<
+ (c.null () ? "DROP" : "SET") << " NOT NULL";
+ }
+ };
+ entry<alter_column> alter_column_;
+
+ //
+ // Schema version table.
+ //
+
+ struct version_table: relational::version_table, context
+ {
+ version_table (base const& x): base (x) {}
+
+ // PostgreSQL prior to 9.1 doesn't support IF NOT EXISTS in
+ // CREATE TABLE. We also cannot use IF-ELSE construct in plain
+ // SQL. To make it at least work for a single schema, we are
+ // going to drop the schema version table after the DROP
+ // statements and then unconditionally create it after CREATE.
+ //
+ virtual void
+ create_table ()
+ {
+ if (options.pgsql_server_version () >= pgsql_version (9, 1))
+ {
+ pre_statement ();
+
+ os << "CREATE TABLE IF NOT EXISTS " << qt_ << " (" << endl
+ << " " << qn_ << " TEXT NOT NULL PRIMARY KEY," << endl
+ << " " << qv_ << " BIGINT NOT NULL," << endl
+ << " " << qm_ << " BOOLEAN NOT NULL)" << endl;
+
+ post_statement ();
+ }
+ }
+
+ virtual void
+ drop ()
+ {
+ pre_statement ();
+
+ if (options.pgsql_server_version () >= pgsql_version (9, 1))
+ os << "DELETE FROM " << qt_ << endl
+ << " WHERE " << qn_ << " = " << qs_ << endl;
+ else
+ os << "DROP TABLE IF EXISTS " << qt_ << endl;
+
+ post_statement ();
+ }
+
+ virtual void
+ create (sema_rel::version v)
+ {
+ pre_statement ();
+
+ if (options.pgsql_server_version () >= pgsql_version (9, 1))
+ {
+ os << "INSERT INTO " << qt_ << " (" << endl
+ << " " << qn_ << ", " << qv_ << ", " << qm_ << ")" << endl
+ << " SELECT " << qs_ << ", " << v << ", FALSE" << endl
+ << " WHERE NOT EXISTS (" << endl
+ << " SELECT 1 FROM " << qt_ << " WHERE " << qn_ << " = " <<
+ qs_ << ")" << endl;
+ }
+ else
+ {
+ os << "CREATE TABLE " << qt_ << " (" << endl
+ << " " << qn_ << " TEXT NOT NULL PRIMARY KEY," << endl
+ << " " << qv_ << " BIGINT NOT NULL," << endl
+ << " " << qm_ << " BOOLEAN NOT NULL)" << endl;
+
+ post_statement ();
+ pre_statement ();
+
+ os << "INSERT INTO " << qt_ << " (" << endl
+ << " " << qn_ << ", " << qv_ << ", " << qm_ << ")" << endl
+ << " VALUES (" << qs_ << ", " << v << ", FALSE)" << endl;
+ }
+
+ post_statement ();
+ }
+
+ virtual void
+ migrate_pre (sema_rel::version v)
+ {
+ pre_statement ();
+
+ os << "UPDATE " << qt_ << endl
+ << " SET " << qv_ << " = " << v << ", " << qm_ << " = TRUE" << endl
+ << " WHERE " << qn_ << " = " << qs_ << endl;
+
+ post_statement ();
+ }
+
+ virtual void
+ migrate_post ()
+ {
+ pre_statement ();
+
+ os << "UPDATE " << qt_ << endl
+ << " SET " << qm_ << " = FALSE" << endl
+ << " WHERE " << qn_ << " = " << qs_ << endl;
+
+ post_statement ();
+ }
+
+ };
+ entry<version_table> version_table_;
+ }
+ }
+}
diff --git a/odb/odb/relational/pgsql/source.cxx b/odb/odb/relational/pgsql/source.cxx
new file mode 100644
index 0000000..b881e48
--- /dev/null
+++ b/odb/odb/relational/pgsql/source.cxx
@@ -0,0 +1,1140 @@
+// file : odb/relational/pgsql/source.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <odb/relational/source.hxx>
+
+#include <odb/relational/pgsql/common.hxx>
+#include <odb/relational/pgsql/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace pgsql
+ {
+ namespace source
+ {
+ namespace relational = relational::source;
+
+ struct query_parameters: relational::query_parameters
+ {
+ query_parameters (base const& x): base (x), i_ (0) {}
+
+ virtual string
+ next (semantics::data_member&, const string&, const string&)
+ {
+ ostringstream ss;
+ ss << "$" << ++i_;
+
+ return ss.str ();
+ }
+
+ virtual string
+ auto_id (semantics::data_member&, const string&, const string&)
+ {
+ return "DEFAULT";
+ }
+
+ private:
+ size_t i_;
+ };
+ entry<query_parameters> query_parameters_;
+
+ namespace
+ {
+ const char* integer_buffer_types[] =
+ {
+ "pgsql::bind::boolean_",
+ "pgsql::bind::smallint",
+ "pgsql::bind::integer",
+ "pgsql::bind::bigint"
+ };
+
+ const char* float_buffer_types[] =
+ {
+ "pgsql::bind::real",
+ "pgsql::bind::double_"
+ };
+
+ const char* char_bin_buffer_types[] =
+ {
+ "pgsql::bind::text", // CHAR
+ "pgsql::bind::text", // VARCHAR
+ "pgsql::bind::text", // TEXT
+ "pgsql::bind::bytea" // BYTEA
+ };
+
+ const char* date_time_buffer_types[] =
+ {
+ "pgsql::bind::date",
+ "pgsql::bind::time",
+ "pgsql::bind::timestamp"
+ };
+
+ const char* oids[] =
+ {
+ "pgsql::bool_oid", // BOOLEAN
+ "pgsql::int2_oid", // SMALLINT
+ "pgsql::int4_oid", // INTEGER
+ "pgsql::int8_oid", // BIGINT
+ "pgsql::float4_oid", // REAL
+ "pgsql::float8_oid", // DOUBLE
+ "pgsql::numeric_oid", // NUMERIC
+ "pgsql::date_oid", // DATE
+ "pgsql::time_oid", // TIME
+ "pgsql::timestamp_oid", // TIMESTAMP
+ "pgsql::text_oid", // CHAR
+ "pgsql::text_oid", // VARCHAR
+ "pgsql::text_oid", // TEXT
+ "pgsql::bytea_oid", // BYTEA
+ "pgsql::bit_oid", // BIT
+ "pgsql::varbit_oid", // VARBIT
+ "pgsql::uuid_oid" // UUID
+ };
+ }
+
+ struct statement_oids: object_columns_base, context
+ {
+ statement_oids (statement_kind sk,
+ bool first = true,
+ object_section* section = 0)
+ : object_columns_base (first, column_prefix (), section), sk_ (sk)
+ {
+ }
+
+ virtual bool
+ section_test (data_member_path const& mp)
+ {
+ object_section& s (section (mp));
+
+ // Include eager loaded members into the main section for
+ // SELECT statements.
+ //
+ return section_ == 0 ||
+ *section_ == s ||
+ (sk_ == statement_select &&
+ *section_ == main_section &&
+ !s.separate_load ());
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ // Ignore certain columns depending on what kind statement we are
+ // generating. See object_columns in common source generator for
+ // details.
+ //
+ if (!(inverse (m, key_prefix_) && sk_ != statement_select))
+ object_columns_base::traverse_pointer (m, c);
+ }
+
+ virtual bool
+ traverse_column (semantics::data_member& m,
+ string const&,
+ bool first)
+ {
+ // Ignore certain columns depending on what kind statement we are
+ // generating. See object_columns in common source generator for
+ // details.
+ //
+ if (id ())
+ {
+ if (sk_ == statement_update ||
+ (sk_ == statement_insert && auto_ (m)))
+ return false;
+ }
+
+ if (sk_ == statement_update &&
+ readonly (member_path_, member_scope_))
+ return false;
+
+ if ((sk_ == statement_insert || sk_ == statement_update) &&
+ version (m))
+ return false;
+
+ if (!first)
+ os << ',' << endl;
+
+ os << oids[parse_sql_type (column_type (), m).type];
+
+ return true;
+ }
+
+ private:
+ statement_kind sk_;
+ };
+
+ //
+ // bind
+ //
+
+ struct bind_member: relational::bind_member_impl<sql_type>,
+ member_base
+ {
+ bind_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << b << ".type = " <<
+ integer_buffer_types[mi.st->type - sql_type::BOOLEAN] << ";"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << b << ".type = " <<
+ float_buffer_types[mi.st->type - sql_type::REAL] << ";"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_numeric (member_info& mi)
+ {
+ os << b << ".type = pgsql::bind::numeric;"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data_ptr ();"
+ << b << ".capacity = " << arg << "." << mi.var <<
+ "value.capacity ();"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_date_time (member_info& mi)
+ {
+ os << b << ".type = " <<
+ date_time_buffer_types[mi.st->type - sql_type::DATE] << ";"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << b << ".type = " <<
+ char_bin_buffer_types[mi.st->type - sql_type::CHAR] << ";"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data_ptr ();"
+ << b << ".capacity = " << arg << "." << mi.var <<
+ "value.capacity ();"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_bit (member_info& mi)
+ {
+ os << b << ".type = pgsql::bind::bit;"
+ << b << ".buffer = " << arg << "." << mi.var << "value;"
+ << b << ".capacity = sizeof (" << arg << "." << mi.var << "value);"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_varbit (member_info& mi)
+ {
+ os << b << ".type = pgsql::bind::varbit;"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data_ptr ();"
+ << b << ".capacity = " << arg << "." << mi.var <<
+ "value.capacity ();"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_uuid (member_info& mi)
+ {
+ os << b << ".type = pgsql::bind::uuid;"
+ << b << ".buffer = " << arg << "." << mi.var << "value;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+ };
+ entry<bind_member> bind_member_;
+
+ //
+ // grow
+ //
+
+ struct grow_member: relational::grow_member_impl<sql_type>,
+ member_base
+ {
+ grow_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x) {}
+
+ virtual void
+ traverse_integer (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+
+ virtual void
+ traverse_float (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+
+ virtual void
+ traverse_numeric (member_info& mi)
+ {
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
+ << "grew = true;"
+ << "}";
+ }
+
+ virtual void
+ traverse_date_time (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
+ << "grew = true;"
+ << "}";
+ }
+
+ virtual void
+ traverse_bit (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+
+ virtual void
+ traverse_varbit (member_info& mi)
+ {
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
+ << "grew = true;"
+ << "}";
+ }
+
+ virtual void
+ traverse_uuid (member_info&)
+ {
+ os << e << " = 0;"
+ << endl;
+ }
+ };
+ entry<grow_member> grow_member_;
+
+ //
+ // init image
+ //
+
+ struct init_image_member: relational::init_image_member_impl<sql_type>,
+ member_base
+ {
+ init_image_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ set_null (member_info& mi)
+ {
+ os << "i." << mi.var << "null = true;";
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+
+ virtual void
+ traverse_numeric (member_info& mi)
+ {
+ // @@ Optimization: can remove growth check if buffer is fixed.
+ //
+ os << "std::size_t size (0);"
+ << "std::size_t cap (i." << mi.var << "value.capacity ());"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;"
+ << "i." << mi.var << "size = size;"
+ << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ }
+
+ virtual void
+ traverse_date_time (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << "std::size_t cap (i." << mi.var << "value.capacity ());"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;"
+ << "i." << mi.var << "size = size;"
+ << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ }
+
+ virtual void
+ traverse_bit (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "sizeof (i." << mi.var << "value)," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;"
+ << "i." << mi.var << "size = size;";
+ }
+
+ virtual void
+ traverse_varbit (member_info& mi)
+ {
+ os << "std::size_t size (0);"
+ << "std::size_t cap (i." << mi.var << "value.capacity ());"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;"
+ << "i." << mi.var << "size = size;"
+ << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ }
+
+ virtual void
+ traverse_uuid (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value, is_null, " << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+ };
+ entry<init_image_member> init_image_member_;
+
+ //
+ // init value
+ //
+
+ struct init_value_member: relational::init_value_member_impl<sql_type>,
+ member_base
+ {
+ init_value_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ get_null (string const& var) const
+ {
+ os << "i." << var << "null";
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_float (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_numeric (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_date_time (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_bit (member_info& mi)
+ {
+ // Presented as byte.
+ //
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_varbit (member_info& mi)
+ {
+ // Presented as bytea.
+ //
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_uuid (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+ };
+ entry<init_value_member> init_value_member_;
+
+ struct class_: relational::class_, context
+ {
+ class_ (base const& x): base (x) {}
+
+ virtual string
+ persist_statement_extra (type& c,
+ relational::query_parameters&,
+ persist_position p)
+ {
+ string r;
+
+ if (p == persist_after_values)
+ {
+ data_member_path* id (id_member (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ // Top-level auto id.
+ //
+ if (id != 0 && !poly_derived && auto_ (*id))
+ r = "RETURNING " +
+ convert_from (column_qname (*id), *id->back ());
+ }
+
+ return r;
+ }
+
+ virtual void
+ object_extra (type& c)
+ {
+ bool abst (abstract (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+
+ if (abst && !poly)
+ return;
+
+ data_member_path* id (id_member (c));
+ semantics::data_member* optimistic (context::optimistic (c));
+
+ column_count_type const& cc (column_count (c));
+
+ size_t update_columns (
+ cc.total - cc.id - cc.inverse - cc.readonly - cc.separate_update);
+
+ string const& n (class_fq_name (c));
+ string const& fn (flat_name (n));
+ string traits ("access::object_traits_impl< " + n + ", id_pgsql >");
+
+ os << "const char " << traits << "::" << endl
+ << "persist_statement_name[] = " <<
+ strlit (statement_name ("persist", fn, c)) << ";"
+ << endl;
+
+ if (id != 0)
+ {
+ if (poly_derived)
+ {
+ os << "const char* const " << traits << "::" << endl
+ << "find_statement_names[] ="
+ << "{";
+
+ for (size_t i (0), n (abst ? 1 : polymorphic_depth (c));
+ i < n;
+ ++i)
+ {
+ if (i != 0)
+ os << "," << endl;
+
+ ostringstream ostr;
+ ostr << "find_" << i;
+ os << strlit (statement_name (ostr.str (), fn, c));
+ }
+
+ os << "};";
+ }
+ else
+ os << "const char " << traits << "::" << endl
+ << "find_statement_name[] = " <<
+ strlit (statement_name ("find", fn, c)) << ";"
+ << endl;
+
+ if (poly && !poly_derived)
+ os << "const char " << traits << "::" << endl
+ << "find_discriminator_statement_name[] = " <<
+ strlit (statement_name ("find_discriminator", fn, c)) << ";"
+ << endl;
+
+ if (update_columns != 0)
+ os << "const char " << traits << "::" << endl
+ << "update_statement_name[] = " <<
+ strlit (statement_name ("update", fn, c)) << ";"
+ << endl;
+
+ os << "const char " << traits << "::" << endl
+ << "erase_statement_name[] = " <<
+ strlit (statement_name ("erase", fn, c)) << ";"
+ << endl;
+
+ if (optimistic != 0)
+ os << "const char " << traits << "::" << endl
+ << "optimistic_erase_statement_name[] = " <<
+ strlit (statement_name ("erase_optimistic", fn, c)) << ";"
+ << endl;
+ }
+
+ // Query statement name.
+ //
+ if (options.generate_query ())
+ {
+ os << "const char " << traits << "::" << endl
+ << "query_statement_name[] = " <<
+ strlit (statement_name ("query", fn, c)) << ";"
+ << endl
+ << "const char " << traits << "::" << endl
+ << "erase_query_statement_name[] = " <<
+ strlit (statement_name ("erase_query", fn, c)) << ";"
+ << endl;
+ }
+
+ // Statement types.
+ //
+
+ // persist_statement_types.
+ //
+ {
+ os << "const unsigned int " << traits << "::" << endl
+ << "persist_statement_types[] ="
+ << "{";
+
+ statement_oids st (statement_insert);
+ st.traverse (c);
+
+ // Empty array is not portable. So add a dummy member if we
+ // are not sending anything with the insert statement.
+ //
+ if (cc.total == cc.inverse + cc.optimistic_managed +
+ (id != 0 && !poly_derived && auto_ (*id) ? cc.id : 0))
+ os << "0";
+
+ os << "};";
+ }
+
+ // find_statement_types.
+ //
+ if (id != 0)
+ {
+ os << "const unsigned int " << traits << "::" << endl
+ << "find_statement_types[] ="
+ << "{";
+
+ statement_oids st (statement_select, true);
+ st.traverse (*id);
+
+ os << "};";
+ }
+
+ // update_statement_types.
+ //
+ if (id != 0 && update_columns != 0)
+ {
+ os << "const unsigned int " << traits << "::" << endl
+ << "update_statement_types[] ="
+ << "{";
+
+ {
+ statement_oids st (statement_update, true, &main_section);
+ st.traverse (c);
+ }
+
+ // Not the same as update_columns.
+ //
+ bool first (cc.total == cc.id + cc.inverse + cc.readonly +
+ cc.separate_update + cc.optimistic_managed);
+
+ statement_oids st (statement_where, first);
+ st.traverse (*id);
+
+ if (optimistic != 0)
+ st.traverse (*optimistic);
+
+ os << "};";
+ }
+
+ if (id != 0 && optimistic != 0)
+ {
+ os << "const unsigned int " << traits << "::" << endl
+ << "optimistic_erase_statement_types[] ="
+ << "{";
+
+ statement_oids st (statement_where);
+ st.traverse (*id);
+ st.traverse (*optimistic);
+
+ os << "};";
+ }
+ }
+
+ virtual void
+ extra_statement_cache_extra_args (bool c, bool s)
+ {
+ bool u (c || s);
+
+ os << "," << endl
+ << db << "::native_binding&" << (u ? " idn" : "") << "," << endl
+ << "const unsigned int*" << (u ? " idt" : "");
+ }
+
+ virtual void
+ view_extra (type& c)
+ {
+ string const& n (class_fq_name (c));
+ string const& fn (flat_name (n));
+ string traits ("access::view_traits_impl< " + n + ", id_pgsql >");
+
+ os << "const char " << traits << "::" << endl
+ << "query_statement_name[] = " <<
+ strlit (statement_name ("query", fn, c)) << ";"
+ << endl;
+ }
+
+ virtual void
+ object_query_statement_ctor_args (type&,
+ string const& q,
+ bool process,
+ bool prep)
+ {
+ os << "sts.connection ()," << endl;
+
+ if (prep)
+ os << "n," << endl;
+ else
+ os << "query_statement_name," << endl;
+
+ os << "text," << endl
+ << process << "," << endl // Process.
+ << "true," << endl // Optimize.
+ << q << ".parameter_types ()," << endl
+ << q << ".parameter_count ()," << endl
+ << q << ".parameters_binding ()," << endl
+ << "imb";
+ }
+
+ virtual void
+ object_erase_query_statement_ctor_args (type&)
+ {
+ os << "conn," << endl
+ << "erase_query_statement_name," << endl
+ << "text," << endl
+ << "q.parameter_types ()," << endl
+ << "q.parameter_count ()," << endl
+ << "q.parameters_binding ()";
+ }
+
+ virtual void
+ view_query_statement_ctor_args (type&,
+ string const& q,
+ bool process,
+ bool prep)
+ {
+ os << "sts.connection ()," << endl;
+
+ if (prep)
+ os << "n," << endl;
+ else
+ os << "query_statement_name," << endl;
+
+ os << q << ".clause ()," << endl
+ << process << "," << endl // Process.
+ << "true," << endl // Optimize.
+ << q << ".parameter_types ()," << endl
+ << q << ".parameter_count ()," << endl
+ << q << ".parameters_binding ()," << endl
+ << "imb";
+ }
+
+ virtual void
+ post_query_ (type&, bool once_off)
+ {
+ if (once_off)
+ os << "st->deallocate ();";
+ }
+ };
+ entry<class_> class_entry_;
+
+ struct container_traits : relational::container_traits, context
+ {
+ container_traits (base const& x): base (x) {}
+
+ virtual void
+ container_extra (semantics::data_member& m, semantics::type& t)
+ {
+ if (!object (c_) || (abstract (c_) && !polymorphic (c_)))
+ return;
+
+ container_kind_type ck (container_kind (t));
+
+ string const& pn (public_name (m));
+ string scope (scope_ + "::" + flat_prefix_ + pn + "_traits");
+
+ data_member_path* imp (inverse (m, "value"));
+ bool inv (imp != 0);
+
+ bool smart (!inv && !unordered (m) && container_smart (t));
+
+ // Statment names.
+ //
+
+ // Prefix top-object name to avoid conflicts with inherited
+ // member statement names.
+ //
+ string fn (
+ flat_name (
+ class_fq_name (*top_object) + "_" + flat_prefix_ + pn));
+
+ os << "const char " << scope << "::" << endl
+ << "select_name[] = " <<
+ strlit (statement_name ("select", fn, m)) << ";"
+ << endl
+ << "const char " << scope << "::" << endl
+ << "insert_name[] = " <<
+ strlit (statement_name ("insert", fn, m)) << ";"
+ << endl;
+
+ if (smart)
+ os << "const char " << scope << "::" << endl
+ << "update_name[] = " <<
+ strlit (statement_name ("update", fn, m)) << ";"
+ << endl;
+
+ os << "const char " << scope << "::" << endl
+ << "delete_name[] = " <<
+ strlit (statement_name ("delete", fn, m)) << ";"
+ << endl;
+
+ // Statement types.
+ //
+
+ semantics::type& vt (container_vt (m));
+ semantics::type& idt (container_idt (m));
+
+ // insert statement types.
+ //
+ {
+ os << "const unsigned int " << scope << "::" << endl
+ << "insert_types[] ="
+ << "{";
+
+ if (!inv)
+ {
+ statement_oids so (statement_insert);
+
+ so.traverse (m, idt, "id", "object_id");
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (!unordered (m))
+ so.traverse (m, container_it (m), "index", "index");
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ so.traverse (m, container_kt (m), "key", "key");
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ so.traverse (m, vt, "value", "value");
+ }
+ else
+ // MSVC does not allow zero length arrays or uninitialized
+ // non-extern const values.
+ //
+ os << "0";
+
+ os << "};";
+ }
+
+ // update statement types.
+ //
+ if (smart)
+ {
+ os << "const unsigned int " << scope << "::" << endl
+ << "update_types[] ="
+ << "{";
+
+ {
+ // Use insert instead of update to include read-only members.
+ //
+ statement_oids so (statement_insert);
+ so.traverse (m, vt, "value", "value");
+ }
+
+ statement_oids so (statement_where, false);
+ so.traverse (m, idt, "id", "object_id");
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (!unordered (m))
+ so.traverse (m, container_it (m), "index", "index");
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ //so.traverse (m, container_kt (t), "key", "key");
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ //so.traverse (m, vt, "value", "value");
+ break;
+ }
+ }
+
+ os << "};";
+ }
+
+ // delete statement types.
+ //
+ if (smart)
+ {
+ os << "const unsigned int " << scope << "::" << endl
+ << "delete_types[] ="
+ << "{";
+
+ statement_oids so (statement_where);
+ so.traverse (m, idt, "id", "object_id");
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (!unordered (m))
+ so.traverse (m, container_it (m), "index", "index");
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ //so.traverse (m, container_kt (t), "key", "key");
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ //so.traverse (m, vt, "value", "value");
+ break;
+ }
+ }
+
+ os << "};";
+ }
+ }
+ };
+ entry<container_traits> container_traits_;
+
+ struct section_traits : relational::section_traits, context
+ {
+ section_traits (base const& x): base (x) {}
+
+ virtual void
+ section_extra (user_section& s)
+ {
+ semantics::class_* poly_root (polymorphic (c_));
+ bool poly (poly_root != 0);
+
+ if (!poly && (abstract (c_) ||
+ s.special == user_section::special_version))
+ return;
+
+ semantics::data_member* opt (optimistic (c_));
+
+ bool load (s.total != 0 && s.separate_load ());
+ bool load_opt (s.optimistic () && s.separate_load ());
+
+ bool update (s.total != s.inverse + s.readonly); // Always separate.
+ bool update_opt (s.optimistic () && (s.readwrite_containers || poly));
+
+ string name (public_name (*s.member));
+ string scope (scope_ + "::" + name + "_traits");
+
+ // Statment names.
+ //
+
+ // Prefix object name to avoid conflicts with inherited member
+ // statement names.
+ //
+ string fn (flat_name (class_fq_name (c_) + "_" + name));
+
+ if (load || load_opt)
+ os << "const char " << scope << "::" << endl
+ << "select_name[] = " <<
+ strlit (statement_name ("select", fn, *s.member)) << ";"
+ << endl;
+
+ if (update || update_opt)
+ os << "const char " << scope << "::" << endl
+ << "update_name[] = " <<
+ strlit (statement_name ("update", fn, *s.member)) << ";"
+ << endl;
+
+ // Statement types.
+ //
+ if (update || update_opt)
+ {
+ os << "const unsigned int " << scope << "::" << endl
+ << "update_types[] ="
+ << "{";
+
+ {
+ statement_oids st (statement_update, true, &s);
+ st.traverse (c_);
+ }
+
+ statement_oids st (statement_where, !update);
+ st.traverse (*id_member (c_));
+
+ if (s.optimistic ()) // Note: not update_opt.
+ st.traverse (*opt);
+
+ os << "};";
+ }
+ }
+ };
+ entry<section_traits> section_traits_;
+
+ struct container_cache_init_members:
+ relational::container_cache_init_members
+ {
+ container_cache_init_members (base const& x): base (x) {}
+
+ virtual void
+ extra_members ()
+ {
+ os << ", idn, idt";
+ }
+ };
+ entry<container_cache_init_members> container_cache_init_members_;
+
+ struct section_cache_init_members:
+ relational::section_cache_init_members
+ {
+ section_cache_init_members (base const& x): base (x) {}
+
+ virtual void
+ extra_members ()
+ {
+ os << ", idn, idt";
+ }
+ };
+ entry<section_cache_init_members> section_cache_init_members_;
+ }
+ }
+}
diff --git a/odb/odb/relational/processor.cxx b/odb/odb/relational/processor.cxx
new file mode 100644
index 0000000..0f60359
--- /dev/null
+++ b/odb/odb/relational/processor.cxx
@@ -0,0 +1,1564 @@
+// file : odb/relational/processor.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <vector>
+#include <algorithm>
+
+#include <odb/diagnostics.hxx>
+#include <odb/lookup.hxx>
+#include <odb/cxx-lexer.hxx>
+#include <odb/common.hxx>
+
+#include <odb/relational/context.hxx>
+#include <odb/relational/processor.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace
+ {
+ // Indirect (dynamic) context values.
+ //
+ static string
+ id_column_type ()
+ {
+ context& c (context::current ());
+ data_member_path& id (*context::id_member (*c.top_object));
+ return id.back ()->get<string> ("column-id-type");
+ }
+
+ struct data_member: traversal::data_member, context
+ {
+ virtual void
+ traverse (semantics::data_member& m)
+ {
+ if (transient (m))
+ return;
+
+ semantics::names* hint;
+ semantics::type& t (utype (m, hint));
+
+ semantics::type* wt;
+ semantics::names* whint (0);
+ if ((wt = wrapper (t, whint)))
+ wt = &utype (*wt, whint);
+
+ // Determine the member kind.
+ //
+ enum {simple, composite, container, unknown} kind (unknown);
+
+ // See if this is a composite value type.
+ //
+ if (composite_wrapper (t))
+ kind = composite;
+
+ // If not, see if it is a simple value.
+ //
+ if (kind == unknown)
+ {
+ string type, id_type;
+
+ if (m.count ("id-type"))
+ id_type = m.get<string> ("id-type");
+
+ if (m.count ("type"))
+ {
+ type = m.get<string> ("type");
+
+ if (id_type.empty ())
+ id_type = type;
+ }
+
+ if (semantics::class_* c = object_pointer (t))
+ {
+ // An object pointer in view doesn't really have a "column"
+ // so pretend that it has already been handled.
+ //
+ if (view_member (m))
+ kind = simple;
+ else
+ {
+ // This is an object pointer. The column type is the pointed-to
+ // object id type.
+ //
+ semantics::data_member& id (*id_member (*c)->back ());
+
+ semantics::names* idhint;
+ semantics::type& idt (utype (id, idhint));
+
+ // The id type can be a composite value type.
+ //
+ if (composite_wrapper (idt))
+ kind = composite;
+ else
+ {
+ semantics::type* wt;
+ semantics::names* whint (0);
+ if ((wt = wrapper (idt, whint)))
+ wt = &utype (*wt, whint);
+
+ if (type.empty () && id.count ("id-type"))
+ type = id.get<string> ("id-type");
+
+ if (type.empty () && id.count ("type"))
+ type = id.get<string> ("type");
+
+ // The rest should be identical to the code for the id_type in
+ // the else block.
+ //
+ if (type.empty () && idt.count ("id-type"))
+ type = idt.get<string> ("id-type");
+
+ if (type.empty () && wt != 0 && wt->count ("id-type"))
+ type = wt->get<string> ("id-type");
+
+ if (type.empty () && idt.count ("type"))
+ type = idt.get<string> ("type");
+
+ if (type.empty () && wt != 0 && wt->count ("type"))
+ type = wt->get<string> ("type");
+
+ if (type.empty ())
+ type = database_type (idt, idhint, true);
+
+ if (type.empty () && wt != 0)
+ type = database_type (*wt, whint, true);
+
+ id_type = type;
+ }
+ }
+ }
+ else
+ {
+ if (id_type.empty () && t.count ("id-type"))
+ id_type = t.get<string> ("id-type");
+
+ if (id_type.empty () && wt != 0 && wt->count ("id-type"))
+ id_type = wt->get<string> ("id-type");
+
+ if (type.empty () && t.count ("type"))
+ type = t.get<string> ("type");
+
+ if (type.empty () && wt != 0 && wt->count ("type"))
+ type = wt->get<string> ("type");
+
+ if (id_type.empty ())
+ id_type = type;
+
+ if (id_type.empty ())
+ id_type = database_type (t, hint, true);
+
+ if (id_type.empty () && wt != 0)
+ id_type = database_type (*wt, whint, true);
+
+ bool null (false);
+ if (type.empty ())
+ type = database_type (t, hint, false, &null);
+
+ if (type.empty () && wt != 0)
+ type = database_type (*wt, whint, false, &null);
+
+ // Use id mapping for discriminators.
+ //
+ if (id (m) || discriminator (m))
+ type = id_type;
+ // Allow NULL if requested by the default mapping.
+ //
+ else if (null && !m.count ("not-null"))
+ m.set ("null", true);
+ }
+
+ if (kind == unknown && !type.empty ())
+ {
+ m.set ("column-type", type);
+ m.set ("column-id-type", id_type);
+
+ // Issue a warning if we are relaxing null-ness.
+ //
+ if (m.count ("null") && t.count ("not-null"))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " warning: data member declared null while its type is "
+ << "declared not null" << endl;
+ }
+
+ kind = simple;
+ }
+ }
+
+ // If not a simple value, see if this is a container.
+ //
+ if (kind == unknown && context::container (m))
+ {
+ process_container (m, (wt != 0 ? *wt : t));
+ kind = container;
+ }
+
+ // If it is none of the above then we have an error.
+ //
+ if (kind == unknown)
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: unable to map C++ type '" << t.fq_name (hint)
+ << "' used in data member '" << m.name () << "' to a "
+ << db.name () << " database type" << endl;
+
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " info: use '#pragma db type' to specify the database type"
+ << endl;
+
+ throw operation_failed ();
+ }
+
+ if (m.count ("polymorphic-ref"))
+ {
+ // Copy the column name from the root's id member, if specified.
+ //
+ {
+ semantics::class_& r (*object_pointer (t));
+ semantics::data_member& id (*id_member (r)->front ());
+
+ if (id.count ("column"))
+ m.set ("column", id.get<table_column> ("column"));
+ }
+
+ m.set ("not-null", true);
+ m.set ("deferrable",
+ sema_rel::deferrable (sema_rel::deferrable::not_deferrable));
+ m.set ("on-delete", sema_rel::foreign_key::cascade);
+ }
+
+ process_index (m);
+ }
+
+ // Convert index/unique specifiers to the index entry in the object.
+ //
+ void
+ process_index (semantics::data_member& m)
+ {
+ bool ip (m.count ("index"));
+ bool up (m.count ("unique"));
+
+ if (ip || up)
+ {
+ using semantics::class_;
+ class_& c (dynamic_cast<class_&> (m.scope ()));
+
+ indexes& ins (c.count ("index")
+ ? c.get<indexes> ("index")
+ : c.set ("index", indexes ()));
+
+ index in;
+ in.loc = m.get<location_t> (
+ ip ? "index-location" : "unique-location");
+
+ if (up)
+ in.type = "UNIQUE";
+
+ index::member im;
+ im.loc = in.loc;
+ im.name = m.name ();
+ im.path.push_back (&m);
+ in.members.push_back (im);
+
+ // Insert it in the location order.
+ //
+ ins.insert (
+ lower_bound (ins.begin (), ins.end (), in, index_comparator ()),
+ in);
+ }
+ }
+
+ void
+ process_container_value (semantics::type& t,
+ semantics::names* hint,
+ semantics::data_member& m,
+ string const& prefix,
+ bool obj_ptr)
+ {
+ if (composite_wrapper (t))
+ return;
+
+ semantics::names* wh (0);
+ semantics::type* wt (wrapper (t, wh));
+
+ string type;
+ semantics::type& ct (utype (m));
+
+ // Custom mapping can come from these places (listed in the order
+ // of priority): member, container type, value type. To complicate
+ // things a bit, for object references, it can also come from the
+ // member and value type of the id member.
+ //
+ if (m.count (prefix + "-type"))
+ type = m.get<string> (prefix + "-type");
+
+ if (type.empty () && ct.count (prefix + "-type"))
+ type = ct.get<string> (prefix + "-type");
+
+ semantics::class_* c;
+ if (obj_ptr && (c = object_pointer (t)))
+ {
+ // This is an object pointer. The column type is the pointed-to
+ // object id type.
+ //
+ semantics::data_member& id (*id_member (*c)->back ());
+
+ semantics::names* idhint;
+ semantics::type& idt (utype (id, idhint));
+
+ // Nothing to do if this is a composite value type.
+ //
+ if (composite_wrapper (idt))
+ return;
+
+ semantics::type* wt (0);
+ semantics::names* whint (0);
+ if ((wt = wrapper (idt, whint)))
+ wt = &utype (*wt, whint);
+
+ if (type.empty () && id.count ("id-type"))
+ type = id.get<string> ("id-type");
+
+ if (type.empty () && id.count ("type"))
+ type = id.get<string> ("type");
+
+ // The rest of the code is identical to the else block except here
+ // we have to check for "id-type" before checking for "type".
+ //
+
+ if (type.empty () && idt.count ("id-type"))
+ type = idt.get<string> ("id-type");
+
+ if (type.empty () && wt != 0 && wt->count ("id-type"))
+ type = wt->get<string> ("id-type");
+
+ if (type.empty () && idt.count ("type"))
+ type = idt.get<string> ("type");
+
+ if (type.empty () && wt != 0 && wt->count ("type"))
+ type = wt->get<string> ("type");
+
+ if (type.empty ())
+ type = database_type (idt, idhint, true);
+
+ if (type.empty () && wt != 0)
+ type = database_type (*wt, whint, true);
+ }
+ else
+ {
+ if (type.empty () && t.count ("type"))
+ type = t.get<string> ("type");
+
+ if (type.empty () && wt != 0 && wt->count ("type"))
+ type = wt->get<string> ("type");
+
+ bool null (false);
+ if (type.empty ())
+ type = database_type (t, hint, false, &null);
+
+ if (type.empty () && wt != 0)
+ type = database_type (*wt, wh, false, &null);
+
+ // Allow NULL if requested by the default mapping.
+ //
+ if (null && !m.count (prefix + "-not-null"))
+ m.set (prefix + "-null", true);
+ }
+
+ if (!type.empty ())
+ {
+ m.set (prefix + "-column-type", type);
+ m.set (prefix + "-column-id-type", type);
+ return;
+ }
+
+ // We do not support nested containers so skip that test.
+ //
+
+ // If it is none of the above then we have an error.
+ //
+ string fq_type (t.fq_anonymous () ? "<anonymous>" : t.fq_name ());
+
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: unable to map C++ type '" << fq_type << "' used in "
+ << "data member '" << m.name () << "' to a " << db.name ()
+ << " database type" << endl;
+
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " info: use '#pragma db " << prefix << "_type' to specify the "
+ << "database type" << endl;
+
+ throw operation_failed ();
+ }
+
+ void
+ process_container (semantics::data_member& m, semantics::type& t)
+ {
+ container_kind_type ck (t.get<container_kind_type> ("container-kind"));
+
+ semantics::names* vh (0);
+ semantics::names* ih (0);
+ semantics::names* kh (0);
+
+ semantics::type* vt (&utype (m, vh, "value"));
+ semantics::type* it (ck == ck_ordered ? &utype (m, ih, "index") : 0);
+ semantics::type* kt (ck == ck_map || ck == ck_multimap
+ ? &utype (m, kh, "key")
+ : 0);
+
+ // Process member data.
+ //
+ m.set ("id-column-type", &id_column_type);
+
+ process_container_value (*vt, vh, m, "value", true);
+
+ if (it != 0)
+ process_container_value (*it, ih, m, "index", false);
+
+ if (kt != 0)
+ process_container_value (*kt, kh, m, "key", true);
+ }
+ };
+
+ //
+ //
+ struct view_data_member: traversal::data_member, context
+ {
+ view_data_member (semantics::class_& c)
+ : view_ (c),
+ query_ (c.get<view_query> ("query")),
+ amap_ (c.get<view_alias_map> ("alias-map")),
+ omap_ (c.get<view_object_map> ("object-map"))
+ {
+ }
+
+ struct assoc_member
+ {
+ semantics::data_member* m;
+ view_object* vo;
+ };
+
+ typedef vector<assoc_member> assoc_members;
+
+ virtual void
+ traverse (semantics::data_member& m)
+ {
+ using semantics::data_member;
+
+ if (transient (m))
+ return;
+
+ semantics::type& t (utype (m));
+
+ // Object pointers are associated with objects.
+ //
+ if (object_pointer (t))
+ return;
+
+ data_member* src_m (0); // Source member.
+
+ // Resolve member references in column expressions.
+ //
+ if (m.count ("column"))
+ {
+ // Column literal.
+ //
+ if (query_.kind != view_query::condition)
+ {
+ warn (m.get<location_t> ("column-location"))
+ << "db pragma column ignored in a view with "
+ << (query_.kind == view_query::runtime ? "runtime" : "complete")
+ << " query" << endl;
+ }
+
+ return;
+ }
+ else if (m.count ("column-expr"))
+ {
+ column_expr& e (m.get<column_expr> ("column-expr"));
+
+ if (query_.kind != view_query::condition)
+ {
+ warn (e.loc)
+ << "db pragma column ignored in a view with "
+ << (query_.kind == view_query::runtime ? "runtime" : "complete")
+ << " query" << endl;
+ return;
+ }
+
+ for (column_expr::iterator i (e.begin ()); i != e.end (); ++i)
+ {
+ // This code is quite similar to translate_expression in the
+ // source generator.
+ //
+ try
+ {
+ using semantics::scope;
+ using semantics::class_;
+
+ if (i->kind != column_expr_part::reference)
+ continue;
+
+ lex_.start (i->value);
+
+ string tl;
+ tree tn;
+ cpp_ttype tt (lex_.next (tl, &tn));
+
+ data_member* m (0);
+ view_object* vo (0);
+
+ // Check if this is an alias.
+ //
+ if (tt == CPP_NAME)
+ {
+ view_alias_map::iterator j (amap_.find (tl));
+
+ if (j != amap_.end ())
+ {
+ vo = j->second;
+
+ // Skip '::'.
+ //
+ if (lex_.next (tl, &tn) != CPP_SCOPE)
+ {
+ error (i->loc) << "member name expected after an alias " <<
+ "in db pragma column" << endl;
+ throw operation_failed ();
+ }
+
+ if (lex_.next (tl, &tn) != CPP_NAME)
+ throw lookup::invalid_name ();
+
+ m = &vo->obj->lookup<data_member> (
+ tl, scope::include_hidden);
+
+ tt = lex_.next (tl, &tn);
+ }
+ }
+
+ // If it is not an alias, do the normal lookup.
+ //
+ if (vo == 0)
+ {
+ // Also get the object type. We need to do it so that
+ // we can get the correct (derived) table name (the
+ // member itself can come from a base class).
+ //
+ scope* s;
+ string name;
+ cpp_ttype ptt; // Not used.
+ m = &lookup::resolve_scoped_name<data_member> (
+ lex_, tt, tl, tn, ptt,
+ dynamic_cast<scope&> (*unit.find (i->scope)),
+ name,
+ false,
+ &s);
+
+ view_object_map::iterator j (
+ omap_.find (dynamic_cast<class_*> (s)));
+
+ if (j == omap_.end ())
+ {
+ error (i->loc) << "name '" << name << "' in db pragma " <<
+ "column does not refer to a data member of a " <<
+ "persistent class that is used in this view" << endl;
+ throw operation_failed ();
+ }
+
+ vo = j->second;
+ }
+
+ i->member_path.push_back (m);
+
+ // Figure out the table name/alias for this member.
+ //
+ if (class_* root = polymorphic (*vo->obj))
+ {
+ // If the object is polymorphic, then figure out which of the
+ // bases this member comes from and use the corresponding
+ // table.
+ //
+ class_* c (&static_cast<class_&> (m->scope ()));
+
+ // If this member's class is not polymorphic (root uses reuse
+ // inheritance), then use the root table.
+ //
+ if (!polymorphic (*c))
+ c = root;
+
+ // In a polymorphic hierarchy we have several tables and the
+ // provided alias is used as a prefix together with the table
+ // name to form the actual alias.
+ //
+ qname const& t (table_name (*c));
+
+ if (vo->alias.empty ())
+ i->table = t;
+ else
+ i->table = qname (vo->alias + "_" + t.uname ());
+ }
+ else
+ i->table = vo->alias.empty ()
+ ? table_name (*vo->obj)
+ : qname (vo->alias);
+
+ // Finally, resolve nested members if any.
+ //
+ for (; tt == CPP_DOT; tt = lex_.next (tl, &tn))
+ {
+ lex_.next (tl, &tn); // Get CPP_NAME.
+
+ // Check that the outer member is composite and also
+ // unwrap it while at it.
+ //
+ class_* comp (composite_wrapper (utype (*m)));
+ if (comp == 0)
+ {
+ error (i->loc) << "data member '" << m->name () << "' " <<
+ "specified in db pragma column is not composite" << endl;
+ throw operation_failed ();
+ }
+
+ m = &comp->lookup<data_member> (tl, class_::include_hidden);
+ i->member_path.push_back (m);
+ }
+
+ // If the expression is just this reference, then we have
+ // a source member.
+ //
+ if (e.size () == 1)
+ src_m = m;
+ }
+ catch (lookup::invalid_name const&)
+ {
+ error (i->loc) << "invalid name in db pragma column" << endl;
+ throw operation_failed ();
+ }
+ catch (semantics::unresolved const& e)
+ {
+ if (e.type_mismatch)
+ error (i->loc) << "name '" << e.name << "' in db pragma " <<
+ "column does not refer to a data member" << endl;
+ else
+ error (i->loc) << "unable to resolve data member '" <<
+ e.name << "' specified with db pragma column" << endl;
+
+ throw operation_failed ();
+ }
+ catch (semantics::ambiguous const& e)
+ {
+ error (i->loc) << "data member name '" << e.first.name () <<
+ "' specified with db pragma column is ambiguous" << endl;
+
+ info (e.first.named ().location ()) << "could resolve to " <<
+ "this data member" << endl;
+
+ info (e.second.named ().location ()) << "or could resolve " <<
+ "to this data member" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ // Check that the source member is not transient or inverse. Also
+ // check that the C++ types are the same (sans cvr-qualification
+ // and wrapping) and issue a warning if they differ. In rare cases
+ // where this is not a mistake, the user can use a phony expression
+ // (e.g., "" + person:name) to disable the warning. Note that in
+ // this case there will be no type pragma copying, which is probably
+ // ok seeing that the C++ types are different.
+ //
+ //
+ if (src_m != 0)
+ {
+ string reason;
+
+ if (transient (*src_m))
+ reason = "transient";
+ else if (inverse (*src_m))
+ reason = "inverse";
+
+ if (!reason.empty ())
+ {
+ error (e.loc)
+ << "object data member '" << src_m->name () << "' specified "
+ << "in db pragma column is " << reason << endl;
+ throw operation_failed ();
+ }
+
+ if (!member_resolver::check_types (utype (*src_m), utype (m)))
+ {
+ warn (e.loc)
+ << "object data member '" << src_m->name () << "' specified "
+ << "in db pragma column has a different type compared to the "
+ << "view data member" << endl;
+
+ info (src_m->file (), src_m->line (), src_m->column ())
+ << "object data member is defined here" << endl;
+
+ info (m.file (), m.line (), m.column ())
+ << "view data member is defined here" << endl;
+ }
+ }
+ }
+ // This member has no column information. If we are generating our
+ // own query, try to find a member with the same (or similar) name
+ // in one of the associated objects.
+ //
+ else if (query_.kind == view_query::condition)
+ {
+ view_objects& objs (view_.get<view_objects> ("objects"));
+
+ assoc_members exact_members, pub_members;
+ member_resolver resolver (exact_members, pub_members, m);
+
+ for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
+ {
+ if (i->kind == view_object::object)
+ resolver.traverse (*i);
+ }
+
+ assoc_members& members (
+ !exact_members.empty () ? exact_members : pub_members);
+
+ // Issue diagnostics if we didn't find any or found more
+ // than one.
+ //
+ if (members.empty ())
+ {
+ error (m.file (), m.line (), m.column ())
+ << "unable to find a corresponding data member for '"
+ << m.name () << "' in any of the associated objects" << endl;
+
+ info (m.file (), m.line (), m.column ())
+ << "use db pragma column to specify the corresponding data "
+ << "member or column name" << endl;
+
+ throw operation_failed ();
+ }
+ else if (members.size () > 1)
+ {
+ error (m.file (), m.line (), m.column ())
+ << "corresponding data member for '" << m.name () << "' is "
+ << "ambiguous" << endl;
+
+ info (m.file (), m.line (), m.column ())
+ << "candidates are:" << endl;
+
+ for (assoc_members::const_iterator i (members.begin ());
+ i != members.end ();
+ ++i)
+ {
+ info (i->m->file (), i->m->line (), i->m->column ())
+ << " '" << i->m->name () << "' in object '"
+ << i->vo->name () << "'" << endl;
+ }
+
+ info (m.file (), m.line (), m.column ())
+ << "use db pragma column to resolve this ambiguity" << endl;
+
+ throw operation_failed ();
+ }
+
+ // Synthesize the column expression for this member.
+ //
+ assoc_member const& am (members.back ());
+
+ column_expr& e (m.set ("column-expr", column_expr ()));
+ e.push_back (column_expr_part ());
+ column_expr_part& ep (e.back ());
+
+ ep.kind = column_expr_part::reference;
+
+
+ // If this object is polymorphic, then figure out which of the
+ // bases this member comes from and use the corresponding table.
+ //
+ using semantics::class_;
+
+ if (class_* root = polymorphic (*am.vo->obj))
+ {
+ class_* c (&static_cast<class_&> (am.m->scope ()));
+
+ // If this member's class is not polymorphic (root uses reuse
+ // inheritance), then use the root table.
+ //
+ if (!polymorphic (*c))
+ c = root;
+
+ // In a polymorphic hierarchy we have several tables and the
+ // provided alias is used as a prefix together with the table
+ // name to form the actual alias.
+ //
+ qname const& t (table_name (*c));
+
+ if (am.vo->alias.empty ())
+ ep.table = t;
+ else
+ ep.table = qname (am.vo->alias + "_" + t.uname ());
+ }
+ else
+ ep.table = am.vo->alias.empty ()
+ ? table_name (*am.vo->obj)
+ : qname (am.vo->alias);
+
+ ep.member_path.push_back (am.m);
+
+ src_m = am.m;
+ }
+
+ // If we have the source member and don't have the type pragma of
+ // our own, but the source member does, then copy the columnt type
+ // over. In case the source member is a pointer, also check the id
+ // member.
+ //
+ if (src_m != 0 && !m.count ("type"))
+ {
+ if (src_m->count ("type"))
+ m.set ("column-type", src_m->get<string> ("column-type"));
+ else if (semantics::class_* c = object_pointer (utype (*src_m)))
+ {
+ semantics::data_member& id (*id_member (*c)->back ());
+
+ if (id.count ("type"))
+ m.set ("column-type", id.get<string> ("column-type"));
+ }
+ }
+
+ // Check the return statements above if you add any extra logic
+ // here.
+ }
+
+ struct member_resolver: traversal::class_
+ {
+ member_resolver (assoc_members& members,
+ assoc_members& pub_members,
+ semantics::data_member& m)
+ : member_ (members, pub_members, m)
+ {
+ *this >> names_ >> member_;
+ *this >> inherits_ >> *this;
+ }
+
+ void
+ traverse (view_object& vo)
+ {
+ member_.vo_ = &vo;
+
+ // First look for an exact match.
+ //
+ {
+ member_.exact_ = true;
+ member_.found_ = false;
+ traverse (*vo.obj);
+ }
+
+ // If we didn't find an exact match, then look for a public
+ // name match.
+ //
+ if (!member_.found_)
+ {
+ member_.exact_ = false;
+ traverse (*vo.obj);
+ }
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ if (!object (c))
+ return; // Ignore transient bases.
+
+ names (c);
+
+ // If we already found a match in one of the derived classes,
+ // don't go into bases to get the standard "hiding" behavior.
+ //
+ if (!member_.found_)
+ inherits (c);
+ }
+
+ public:
+ static bool
+ check_types (semantics::type& ot, semantics::type& vt)
+ {
+ using semantics::type;
+
+ // Require that the types be the same sans the wrapping and
+ // cvr-qualification. If the object member type is a pointer,
+ // use the id type of the pointed-to object.
+ //
+ type* t1;
+
+ if (semantics::class_* c = object_pointer (ot))
+ t1 = &utype (*id_member (*c));
+ else
+ t1 = &ot;
+
+ type* t2 (&vt);
+
+ if (type* wt1 = context::wrapper (*t1))
+ t1 = &utype (*wt1);
+
+ if (type* wt2 = context::wrapper (*t2))
+ t2 = &utype (*wt2);
+
+ if (t1 != t2)
+ return false;
+
+ return true;
+ }
+
+ private:
+ struct data_member: traversal::data_member
+ {
+ data_member (assoc_members& members,
+ assoc_members& pub_members,
+ semantics::data_member& m)
+ : members_ (members),
+ pub_members_ (pub_members),
+ name_ (m.name ()),
+ pub_name_ (context::current ().public_name (m)),
+ type_ (utype (m))
+ {
+ }
+
+ virtual void
+ traverse (type& m)
+ {
+ if (exact_)
+ {
+ if (name_ == m.name () && check (m))
+ {
+ assoc_member am;
+ am.m = &m;
+ am.vo = vo_;
+ members_.push_back (am);
+ found_ = true;
+ }
+ }
+ else
+ {
+ if (pub_name_ == context::current ().public_name (m) &&
+ check (m))
+ {
+ assoc_member am;
+ am.m = &m;
+ am.vo = vo_;
+ pub_members_.push_back (am);
+ found_ = true;
+ }
+ }
+ }
+
+ bool
+ check (semantics::data_member& m)
+ {
+ // Make sure that the found node can possibly match.
+ //
+ if (context::transient (m) ||
+ context::inverse (m) ||
+ m.count ("polymorphic-ref"))
+ return false;
+
+ return check_types (utype (m), type_);
+ }
+
+ assoc_members& members_;
+ assoc_members& pub_members_;
+
+ string name_;
+ string pub_name_;
+ semantics::type& type_;
+
+ view_object* vo_;
+ bool exact_;
+ bool found_;
+ };
+
+ traversal::names names_;
+ data_member member_;
+ traversal::inherits inherits_;
+ };
+
+ private:
+ semantics::class_& view_;
+ view_query& query_;
+ view_alias_map& amap_;
+ view_object_map& omap_;
+ cxx_string_lexer lex_;
+ };
+
+ struct class_: traversal::class_, context
+ {
+ class_ ()
+ : typedefs_ (true)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+
+ member_names_ >> member_;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type k (class_kind (c));
+
+ if (k == class_other)
+ return;
+
+ names (c); // Process nested classes.
+ names (c, member_names_);
+
+ if (k == class_object)
+ traverse_object (c);
+ else if (k == class_view)
+ traverse_view (c);
+ }
+
+ //
+ // Object.
+ //
+
+ virtual void
+ traverse_object (type& c)
+ {
+ // Remove the bulk pragma if this database doesn't support bulk
+ // operations.
+ //
+ if (c.count ("bulk") && !generate_bulk)
+ c.remove ("bulk");
+
+ // Process indexes. Here we need to do two things: resolve member
+ // names to member paths and assign names to unnamed indexes. We
+ // are also going to handle the special container indexes.
+ //
+ indexes& ins (c.count ("index")
+ ? c.get<indexes> ("index")
+ : c.set ("index", indexes ()));
+
+ for (indexes::iterator i (ins.begin ()); i != ins.end ();)
+ {
+ index& in (*i);
+
+ // This should never happen since a db index pragma without
+ // the member specifier will be treated as a member pragma.
+ //
+ assert (!in.members.empty ());
+
+ // First resolve member names.
+ //
+ index::members_type::iterator j (in.members.begin ());
+ for (; j != in.members.end (); ++j)
+ {
+ index::member& im (*j);
+
+ if (!im.path.empty ())
+ continue; // Already resolved.
+
+ im.path = resolve_data_members (c, im.name, im.loc, lex_);
+
+ if (container (*im.path.back ()))
+ break;
+ }
+
+ // Add the table prefix if this database has global index names.
+ //
+ if (!in.name.empty () && global_index)
+ in.name = table_name_prefix (class_scope (c)) + in.name;
+
+ // Handle container indexes.
+ //
+ if (j != in.members.end ())
+ {
+ // Do some sanity checks.
+ //
+ if (in.members.size () != 1)
+ {
+ error (in.loc) << "multiple data members specified for a "
+ << "container index" << endl;
+ throw operation_failed ();
+ }
+
+ string tl;
+ if (lex_.next (tl) != CPP_DOT || lex_.next (tl) != CPP_NAME ||
+ (tl != "id" && tl != "index"))
+ {
+ error (j->loc) << ".id or .index special member expected in a "
+ << "container index" << endl;
+ throw operation_failed ();
+ }
+
+ string n (tl);
+
+ if (lex_.next (tl) != CPP_EOF)
+ {
+ error (j->loc) << "unexpected text after ." << n << " in "
+ << "db pragma member" << endl;
+ throw operation_failed ();
+ }
+
+ // Move this index to the container member.
+ //
+ j->path.back ()->set (n + "-index", *i);
+ i = ins.erase (i);
+ continue;
+ }
+
+ // Now assign the name if the index is unnamed. We have to
+ // add table name as a prefix here since there is not way
+ // to distinguish between user-assigned and auto-derived
+ // names in the model.
+ //
+ if (in.name.empty ())
+ {
+ // Make sure there is only one member.
+ //
+ if (in.members.size () > 1)
+ {
+ error (in.loc) << "unnamed index with more than one data "
+ << "member" << endl;
+ throw operation_failed ();
+ }
+
+ // Generally, we want the index name to be based on the column
+ // name. This is straightforward for single-column members. In
+ // case of a composite member, we will need to use the column
+ // prefix which is based on the data member name, unless
+ // overridden by the user. In the latter case the prefix can
+ // be empty, in which case we will just fall back on the
+ // member's public name.
+ //
+ string n (column_prefix (in.members.front ().path, true).prefix);
+
+ if (n.empty ())
+ n = public_name_db (*in.members.front ().path.back ());
+ else if (n[n.size () - 1] == '_')
+ n.resize (n.size () - 1); // Remove trailing underscore.
+
+ in.name = index_name (table_name (c), n);
+ }
+
+ ++i;
+ }
+ }
+
+ //
+ // View.
+ //
+
+ struct relationship
+ {
+ semantics::data_member* member;
+ string name;
+ view_object* pointer;
+ view_object* pointee;
+ };
+
+ typedef vector<relationship> relationships;
+
+ virtual void
+ traverse_view (type& c)
+ {
+ bool has_q (c.count ("query"));
+ bool has_o (c.count ("objects"));
+
+ // Determine the kind of query template we've got.
+ //
+ view_query& vq (has_q
+ ? c.get<view_query> ("query")
+ : c.set ("query", view_query ()));
+ if (has_q)
+ {
+ if (!vq.literal.empty ())
+ {
+ string q (upcase (vq.literal));
+
+ //@@ We need to recognize database-specific list of prefixes. For
+ // example, PG has WITH. Alternatively (or in addition) we could
+ // do the same comment trick (e.g., /*SELECT*/ to treat it as a
+ // SELECT-like queiry).
+ //
+ if (q.compare (0, 7, "SELECT ") == 0)
+ vq.kind = view_query::complete_select;
+ else if (q.compare (0, 5, "EXEC ") == 0 ||
+ q.compare (0, 5, "CALL ") == 0 ||
+ q.compare (0, 8, "EXECUTE ") == 0)
+ vq.kind = view_query::complete_execute;
+ //
+ // Hint for databases that use SELECT for stored procedure
+ // calls (e.g., PostgreSQL).
+ //
+ else if (q.compare (0, 8, "/*CALL*/") == 0)
+ {
+ vq.literal = string (vq.literal, q[8] == ' ' ? 9 : 8);
+ vq.kind = view_query::complete_execute;
+ }
+ else
+ vq.kind = view_query::condition;
+ }
+ else if (!vq.expr.empty ())
+ {
+ // If the first token in the expression is a string and
+ // it starts with "SELECT " or is equal to "SELECT" or
+ // one of the stored procedure call keywords, then we
+ // have a complete query.
+ //
+ if (vq.expr.front ().type == CPP_STRING)
+ {
+ string q (upcase (vq.expr.front ().literal));
+
+ if (q.compare (0, 7, "SELECT ") == 0 || q == "SELECT")
+ vq.kind = view_query::complete_select;
+ else if (q.compare (0, 5, "EXEC ") == 0 || q == "EXEC" ||
+ q.compare (0, 5, "CALL ") == 0 || q == "CALL" ||
+ q.compare (0, 8, "EXECUTE ") == 0 || q == "EXECUTE")
+ vq.kind = view_query::complete_execute;
+ else if (q.compare (0, 8, "/*CALL*/") == 0)
+ {
+ vq.expr.front ().literal =
+ string (vq.expr.front ().literal, q[8] == ' ' ? 9 : 8);
+ vq.kind = view_query::complete_execute;
+ }
+ else
+ vq.kind = view_query::condition;
+ }
+ else
+ vq.kind = view_query::condition;
+ }
+ else
+ vq.kind = (vq.distinct || vq.for_update)
+ ? view_query::condition // The query(distinct) case.
+ : view_query::runtime;
+ }
+ else
+ vq.kind = has_o ? view_query::condition : view_query::runtime;
+
+ if ((vq.distinct || vq.for_update) && vq.kind != view_query::condition)
+ {
+ error (vq.loc)
+ << "result modifier specified for "
+ << (vq.kind == view_query::runtime ? "runtime" : "native")
+ << " query" << endl;
+
+ throw operation_failed ();
+ }
+
+ // We cannot have an incomplete query if there are not objects
+ // to derive the rest from.
+ //
+ if (vq.kind == view_query::condition && !has_o)
+ {
+ error (c.file (), c.line (), c.column ())
+ << "view '" << class_fq_name (c) << "' has an incomplete query "
+ << "template and no associated objects" << endl;
+
+ info (c.file (), c.line (), c.column ())
+ << "use db pragma query to provide a complete query template"
+ << endl;
+
+ info (c.file (), c.line (), c.column ())
+ << "or use db pragma object to associate one or more objects "
+ << "with the view"
+ << endl;
+
+ throw operation_failed ();
+ }
+
+ // Process join conditions.
+ //
+ if (has_o)
+ {
+ view_objects& objs (c.get<view_objects> ("objects"));
+
+ for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
+ {
+ if (i == objs.begin () && i->join != view_object::left)
+ {
+ error (i->loc)
+ << "no join type can be specified for the first associated "
+ << (i->kind == view_object::object ? "object" : "table")
+ << endl;
+ throw operation_failed ();
+ }
+
+ if (i->kind != view_object::object)
+ {
+ // Make sure we have join conditions for tables unless it
+ // is the first entry.
+ //
+ if (i != objs.begin () && i->cond.empty ())
+ {
+ error (i->loc)
+ << "missing join condition in db pragma table" << endl;
+
+ throw operation_failed ();
+ }
+
+ continue;
+ }
+
+ // If we have to generate the query and there was no JOIN
+ // condition specified by the user, try to come up with one
+ // automatically based on object relationships. CROSS JOIN
+ // has no condition.
+ //
+ if (vq.kind == view_query::condition &&
+ i->cond.empty () &&
+ i != objs.begin () &&
+ i->join != view_object::cross)
+ {
+ relationships rs;
+
+ // Check objects specified prior to this one for any
+ // relationships. We don't examine objects that were
+ // specified after this one because that would require
+ // rearranging the JOIN order.
+ //
+ for (view_objects::iterator j (objs.begin ()); j != i; ++j)
+ {
+ if (j->kind != view_object::object)
+ continue; // Skip tables.
+
+ // First see if any of the objects that were specified
+ // prior to this object point to it.
+ //
+ {
+ relationship_resolver r (rs, *i, true);
+ r.traverse (*j);
+ }
+
+ // Now see if this object points to any of the objects
+ // specified prior to it.
+ //
+ {
+ relationship_resolver r (rs, *j, false);
+ r.traverse (*i);
+ }
+ }
+
+ // Issue diagnostics if we didn't find any or found more
+ // than one.
+ //
+ if (rs.empty ())
+ {
+ error (i->loc)
+ << "unable to find an object relationship involving "
+ << "object '" << i->name () << "' and any of the previously "
+ << "associated objects" << endl;
+
+ info (i->loc)
+ << "use the join condition clause in db pragma object "
+ << "to specify a custom join condition" << endl;
+
+ throw operation_failed ();
+ }
+ else if (rs.size () > 1)
+ {
+ error (i->loc)
+ << "object relationship for object '" << i->name () << "' "
+ << "is ambiguous" << endl;
+
+ info (i->loc)
+ << "candidates are:" << endl;
+
+ for (relationships::const_iterator j (rs.begin ());
+ j != rs.end ();
+ ++j)
+ {
+ semantics::data_member& m (*j->member);
+
+ info (m.file (), m.line (), m.column ())
+ << " '" << j->name << "' "
+ << "in object '" << j->pointer->name () << "' "
+ << "pointing to '" << j->pointee->name () << "'"
+ << endl;
+ }
+
+ info (i->loc)
+ << "use the join condition clause in db pragma object "
+ << "to resolve this ambiguity" << endl;
+
+ throw operation_failed ();
+ }
+
+ // Synthesize the condition.
+ //
+ relationship const& r (rs.back ());
+
+ string name (r.pointer->alias.empty ()
+ ? class_fq_name (*r.pointer->obj)
+ : r.pointer->alias);
+ name += "::";
+ name += r.name;
+
+ lex_.start (name);
+
+ string t;
+ for (cpp_ttype tt (lex_.next (t));
+ tt != CPP_EOF;
+ tt = lex_.next (t))
+ {
+ i->cond.push_back (cxx_token (lex_.location (), tt, t));
+ }
+ }
+ }
+ }
+
+ // Handle forced versioning. When versioning is forced, ignore
+ // it for native views.
+ //
+ if (force_versioned && vq.kind == view_query::condition)
+ c.set ("versioned", true);
+
+ // Handle data members.
+ //
+ {
+ view_data_member t (c);
+ traversal::names n (t);
+ names (c, n);
+ }
+ }
+
+ struct relationship_resolver: object_members_base
+ {
+ relationship_resolver (relationships& rs,
+ view_object& pointee,
+ bool forward)
+ // Look in polymorphic bases only for previously-associated
+ // objects since backward pointers from bases will result in
+ // the pathological case (we will have to join the base table
+ // first, which means we will get both bases and derived objects
+ // instead of just derived).
+ //
+ : object_members_base (false, false, true, forward),
+ relationships_ (rs),
+ // Ignore self-references if we are looking for backward
+ // pointers since they were already added to the list in
+ // the previous pass.
+ //
+ self_pointer_ (forward),
+ pointer_ (0),
+ pointee_ (pointee)
+ {
+ }
+
+ void
+ traverse (view_object& pointer)
+ {
+ pointer_ = &pointer;
+ object_members_base::traverse (*pointer.obj);
+ }
+
+ using object_members_base::traverse; // Unhide.
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ // Ignore polymorphic id references.
+ //
+ if (m.count ("polymorphic-ref"))
+ return;
+
+ // Ignore inverse sides of the same relationship to avoid
+ // phony conflicts caused by the direct side that will end
+ // up in the relationship list as well. Unless the inverse
+ // member is in the polymorphic base in which case we will
+ // miss it since we don't examine inside poly bases on the
+ // backwards scan (see above).
+ //
+ if (data_member_path* imp = inverse (m))
+ {
+ if (&imp->front ()->scope () == &c) // Direct member.
+ return;
+ }
+
+ // Ignore self-pointers if requested.
+ //
+ if (!self_pointer_ && pointer_->obj == &c)
+ return;
+
+ if (pointee_.obj == &c)
+ {
+ relationships_.push_back (relationship ());
+ relationships_.back ().member = &m;
+ relationships_.back ().name = member_prefix_ + m.name ();
+ relationships_.back ().pointer = pointer_;
+ relationships_.back ().pointee = &pointee_;
+ }
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type&)
+ {
+ if (semantics::class_* c =
+ object_pointer (context::container_vt (m)))
+ {
+ if (inverse (m, "value"))
+ return;
+
+ // Ignore self-pointers if requested.
+ //
+ if (!self_pointer_ && pointer_->obj == c)
+ return;
+
+ if (pointee_.obj == c)
+ {
+ relationships_.push_back (relationship ());
+ relationships_.back ().member = &m;
+ relationships_.back ().name = member_prefix_ + m.name ();
+ relationships_.back ().pointer = pointer_;
+ relationships_.back ().pointee = &pointee_;
+ }
+ }
+ }
+
+ private:
+ relationships& relationships_;
+ bool self_pointer_;
+ view_object* pointer_;
+ view_object& pointee_;
+ };
+
+ private:
+ cxx_string_lexer lex_;
+
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ data_member member_;
+ traversal::names member_names_;
+ };
+ }
+
+ void
+ process ()
+ {
+ context ctx;
+
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (true);
+ traversal::namespace_ ns;
+ class_ c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (true);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (ctx.unit);
+ }
+}
diff --git a/odb/odb/relational/processor.hxx b/odb/odb/relational/processor.hxx
new file mode 100644
index 0000000..71b8643
--- /dev/null
+++ b/odb/odb/relational/processor.hxx
@@ -0,0 +1,15 @@
+// file : odb/relational/processor.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_PROCESSOR_HXX
+#define ODB_RELATIONAL_PROCESSOR_HXX
+
+namespace relational
+{
+ // Issues diagnostics and throws operation_failed in case of an error.
+ //
+ void
+ process ();
+}
+
+#endif // ODB_RELATIONAL_PROCESSOR_HXX
diff --git a/odb/odb/relational/schema-source.cxx b/odb/odb/relational/schema-source.cxx
new file mode 100644
index 0000000..5659485
--- /dev/null
+++ b/odb/odb/relational/schema-source.cxx
@@ -0,0 +1,281 @@
+// file : odb/relational/schema-source.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/schema-source.hxx>
+#include <odb/relational/generate.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace schema
+ {
+ void
+ generate_source (sema_rel::changelog* log)
+ {
+ context ctx;
+ ostream& os (ctx.os);
+ database db (ctx.db);
+ options const& ops (ctx.options);
+ sema_rel::model& model (*ctx.model);
+ string const& schema_name (ops.schema_name ()[db]);
+
+ if (log != 0 && ops.suppress_migration ())
+ log = 0;
+
+ bool schema_version (
+ model.version () != 0 && !ctx.options.suppress_schema_version ());
+
+ instance<cxx_emitter> emitter;
+ emitter_ostream emitter_os (*emitter);
+ schema_format format (schema_format::embedded);
+
+ if (!model.names_empty () || log != 0 || schema_version)
+ os << "namespace odb"
+ << "{";
+
+ // Schema.
+ //
+ if (!model.names_empty () || schema_version)
+ {
+ os << "static bool" << endl
+ << "create_schema (database& db, unsigned short pass, bool drop)"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << "ODB_POTENTIALLY_UNUSED (pass);"
+ << "ODB_POTENTIALLY_UNUSED (drop);"
+ << endl;
+
+ // Drop.
+ //
+ if (!ops.omit_drop ())
+ {
+ bool close (false);
+
+ os << "if (drop)"
+ << "{";
+
+ instance<drop_model> dmodel (*emitter, emitter_os, format);
+ instance<drop_table> dtable (*emitter, emitter_os, format);
+ trav_rel::qnames names;
+ dmodel >> names >> dtable;
+
+ for (unsigned short pass (1); pass < 3; ++pass)
+ {
+ emitter->pass (pass);
+ dmodel->pass (pass);
+ dtable->pass (pass);
+
+ dmodel->traverse (model);
+
+ close = close || !emitter->empty ();
+ }
+
+ if (schema_version)
+ {
+ instance<version_table> vt (*emitter, emitter_os, format);
+ vt->create_table ();
+ vt->drop ();
+ close = true;
+ }
+
+ if (close) // Close the last case and the switch block.
+ os << "return false;"
+ << "}" // case
+ << "}"; // switch
+
+ os << "}";
+ }
+
+ // Create.
+ //
+ if (!ops.omit_create ())
+ {
+ bool close (false);
+
+ if (ops.omit_drop ())
+ os << "if (!drop)";
+ else
+ os << "else";
+
+ os << "{";
+
+ instance<create_model> cmodel (*emitter, emitter_os, format);
+ instance<create_table> ctable (*emitter, emitter_os, format);
+ trav_rel::qnames names;
+ cmodel >> names >> ctable;
+
+ for (unsigned short pass (1); pass < 3; ++pass)
+ {
+ emitter->pass (pass);
+ cmodel->pass (pass);
+ ctable->pass (pass);
+
+ cmodel->traverse (model);
+
+ close = close || !emitter->empty ();
+ }
+
+ if (schema_version)
+ {
+ instance<version_table> vt (*emitter, emitter_os, format);
+ vt->create_table ();
+ vt->create (model.version ());
+ close = true;
+ }
+
+ if (close) // Close the last case and the switch block.
+ os << "return false;"
+ << "}" // case
+ << "}"; // switch
+
+ os << "}";
+ }
+
+ os << "return false;"
+ << "}";
+
+ os << "static const schema_catalog_create_entry" << endl
+ << "create_schema_entry_ (" << endl
+ << "id_" << db << "," << endl
+ << context::strlit (schema_name) << "," << endl
+ << "&create_schema);"
+ << endl;
+ }
+
+ // Migration.
+ //
+ if (log != 0)
+ {
+ // Create NULL migration entry for the base version so that we
+ // get the complete version range (base, current) at runtime.
+ // Code in schema_catalog relies on this.
+ //
+ os << "static const schema_catalog_migrate_entry" << endl
+ << "migrate_schema_entry_" << log->model ().version () <<
+ "_ (" << endl
+ << "id_" << db << "," << endl
+ << context::strlit (schema_name) << "," << endl
+ << log->model ().version () << "ULL," << endl
+ << "0);"
+ << endl;
+
+ for (sema_rel::changelog::contains_changeset_iterator i (
+ log->contains_changeset_begin ());
+ i != log->contains_changeset_end (); ++i)
+ {
+ sema_rel::changeset& cs (i->changeset ());
+
+ os << "static bool" << endl
+ << "migrate_schema_" << cs.version () << " (database& db, " <<
+ "unsigned short pass, bool pre)"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << "ODB_POTENTIALLY_UNUSED (pass);"
+ << "ODB_POTENTIALLY_UNUSED (pre);"
+ << endl;
+
+ // Pre.
+ //
+ {
+ bool close (false);
+
+ os << "if (pre)"
+ << "{";
+
+ instance<changeset_pre> changeset (*emitter, emitter_os, format);
+ instance<create_table> ctable (*emitter, emitter_os, format);
+ instance<alter_table_pre> atable (*emitter, emitter_os, format);
+ trav_rel::qnames names;
+ changeset >> names;
+ names >> ctable;
+ names >> atable;
+
+ for (unsigned short pass (1); pass < 3; ++pass)
+ {
+ emitter->pass (pass);
+ changeset->pass (pass);
+ ctable->pass (pass);
+ atable->pass (pass);
+
+ changeset->traverse (cs);
+
+ close = close || !emitter->empty ();
+ }
+
+ if (!ctx.options.suppress_schema_version ())
+ {
+ instance<version_table> vt (*emitter, emitter_os, format);
+ vt->migrate_pre (cs.version ());
+ close = true;
+ }
+
+ if (close) // Close the last case and the switch block.
+ os << "return false;"
+ << "}" // case
+ << "}"; // switch
+
+ os << "}";
+ }
+
+ // Post.
+ //
+ {
+ bool close (false);
+
+ os << "else"
+ << "{";
+
+ instance<changeset_post> changeset (*emitter, emitter_os, format);
+ instance<drop_table> dtable (*emitter, emitter_os, format);
+ instance<alter_table_post> atable (*emitter, emitter_os, format);
+ trav_rel::qnames names;
+ changeset >> names;
+ names >> dtable;
+ names >> atable;
+
+ for (unsigned short pass (1); pass < 3; ++pass)
+ {
+ emitter->pass (pass);
+ changeset->pass (pass);
+ dtable->pass (pass);
+ atable->pass (pass);
+
+ changeset->traverse (cs);
+
+ close = close || !emitter->empty ();
+ }
+
+ if (!ctx.options.suppress_schema_version ())
+ {
+ instance<version_table> vt (*emitter, emitter_os, format);
+ vt->migrate_post ();
+ close = true;
+ }
+
+ if (close) // Close the last case and the switch block.
+ os << "return false;"
+ << "}" // case
+ << "}"; // switch
+
+ os << "}";
+ }
+
+ os << "return false;"
+ << "}";
+
+ os << "static const schema_catalog_migrate_entry" << endl
+ << "migrate_schema_entry_" << cs.version () << "_ (" << endl
+ << "id_" << db << "," << endl
+ << context::strlit (schema_name) << "," << endl
+ << cs.version () << "ULL," << endl
+ << "&migrate_schema_" << cs.version () << ");"
+ << endl;
+ }
+ }
+
+ if (!model.names_empty () || log != 0 || schema_version)
+ os << "}"; // namespace odb
+ }
+ }
+}
diff --git a/odb/odb/relational/schema-source.hxx b/odb/odb/relational/schema-source.hxx
new file mode 100644
index 0000000..d2235f5
--- /dev/null
+++ b/odb/odb/relational/schema-source.hxx
@@ -0,0 +1,126 @@
+// file : odb/relational/schema-source.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_SCHEMA_SOURCE_HXX
+#define ODB_RELATIONAL_SCHEMA_SOURCE_HXX
+
+#include <odb/diagnostics.hxx>
+
+#include <odb/emitter.hxx>
+#include <odb/relational/context.hxx>
+#include <odb/relational/schema.hxx>
+
+namespace relational
+{
+ namespace schema
+ {
+ struct cxx_emitter: emitter, virtual context
+ {
+ typedef cxx_emitter base;
+
+ void
+ pass (unsigned short p)
+ {
+ empty_ = true;
+ pass_ = p;
+ new_pass_ = true;
+
+ if (pass_ == 1)
+ empty_passes_ = 0; // New set of passes.
+
+ // Assume this pass is empty.
+ //
+ empty_passes_++;
+ }
+
+ // Did this pass produce anything?
+ //
+ bool
+ empty () const
+ {
+ return empty_;
+ }
+
+ virtual void
+ pre ()
+ {
+ first_ = true;
+ }
+
+ virtual void
+ line (const string& l)
+ {
+ if (l.empty ())
+ return; // Ignore empty lines.
+
+ if (first_)
+ {
+ first_ = false;
+
+ // If this line starts a new pass, then output the switch/case
+ // blocks.
+ //
+ if (new_pass_)
+ {
+ new_pass_ = false;
+ empty_ = false;
+ empty_passes_--; // This pass is not empty.
+
+ // Output case statements for empty preceeding passes, if any.
+ //
+ if (empty_passes_ != 0)
+ {
+ unsigned short s (pass_ - empty_passes_);
+
+ if (s == 1)
+ os << "switch (pass)"
+ << "{";
+ else
+ os << "return true;" // One more pass.
+ << "}";
+
+ for (; s != pass_; ++s)
+ os << "case " << s << ":" << endl;
+
+ os << "{";
+ empty_passes_ = 0;
+ }
+
+ if (pass_ == 1)
+ os << "switch (pass)"
+ << "{";
+ else
+ os << "return true;" // One more pass.
+ << "}";
+
+ os << "case " << pass_ << ":" << endl
+ << "{";
+ }
+
+ os << "db.execute (";
+ }
+ else
+ os << strlit (line_ + '\n') << endl;
+
+ line_ = l;
+ }
+
+ virtual void
+ post ()
+ {
+ if (!first_) // Ignore empty statements.
+ os << strlit (line_) << ");";
+ }
+
+ private:
+ std::string line_;
+ bool first_;
+ bool empty_;
+ bool new_pass_;
+ unsigned short pass_;
+ unsigned short empty_passes_; // Number of preceding empty passes.
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_SCHEMA_SOURCE_HXX
diff --git a/odb/odb/relational/schema.cxx b/odb/odb/relational/schema.cxx
new file mode 100644
index 0000000..dd70bfa
--- /dev/null
+++ b/odb/odb/relational/schema.cxx
@@ -0,0 +1,174 @@
+// file : odb/relational/schema.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cassert>
+#include <limits>
+#include <sstream>
+
+#include <odb/relational/schema.hxx>
+#include <odb/relational/generate.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace schema
+ {
+ void
+ generate_prologue ()
+ {
+ instance<sql_file> file;
+ file->prologue ();
+ }
+
+ void
+ generate_epilogue ()
+ {
+ instance<sql_file> file;
+ file->epilogue ();
+ }
+
+ void
+ generate_drop ()
+ {
+ context ctx;
+ instance<sql_emitter> em;
+ emitter_ostream emos (*em);
+
+ schema_format f (schema_format::sql);
+
+ instance<drop_model> model (*em, emos, f);
+ instance<drop_table> table (*em, emos, f);
+ trav_rel::qnames names;
+
+ model >> names >> table;
+
+ // Pass 1 and 2.
+ //
+ for (unsigned short pass (1); pass < 3; ++pass)
+ {
+ model->pass (pass);
+ table->pass (pass);
+
+ model->traverse (*ctx.model);
+ }
+
+ if (ctx.model->version () != 0 &&
+ !ctx.options.suppress_schema_version ())
+ {
+ instance<version_table> vt (*em, emos, f);
+ vt->create_table ();
+ vt->drop ();
+ }
+ }
+
+ void
+ generate_create ()
+ {
+ context ctx;
+ instance<sql_emitter> em;
+ emitter_ostream emos (*em);
+
+ schema_format f (schema_format::sql);
+
+ instance<create_model> model (*em, emos, f);
+ instance<create_table> table (*em, emos, f);
+ trav_rel::qnames names;
+
+ model >> names >> table;
+
+ // Pass 1 and 2.
+ //
+ for (unsigned short pass (1); pass < 3; ++pass)
+ {
+ model->pass (pass);
+ table->pass (pass);
+
+ model->traverse (*ctx.model);
+ }
+
+ if (ctx.model->version () != 0 &&
+ !ctx.options.suppress_schema_version ())
+ {
+ instance<version_table> vt (*em, emos, f);
+
+ if (ctx.options.omit_drop ())
+ vt->create_table ();
+
+ vt->create (ctx.model->version ());
+ }
+ }
+
+ void
+ generate_migrate_pre (sema_rel::changeset& cs)
+ {
+ context ctx;
+ instance<sql_emitter> em;
+ emitter_ostream emos (*em);
+
+ schema_format f (schema_format::sql);
+
+ instance<changeset_pre> changeset (*em, emos, f);
+ instance<create_table> ctable (*em, emos, f);
+ instance<alter_table_pre> atable (*em, emos, f);
+ trav_rel::qnames names;
+
+ changeset >> names;
+ names >> ctable;
+ names >> atable;
+
+ // Pass 1 and 2.
+ //
+ for (unsigned short pass (1); pass < 3; ++pass)
+ {
+ changeset->pass (pass);
+ ctable->pass (pass);
+ atable->pass (pass);
+
+ changeset->traverse (cs);
+ }
+
+ if (!ctx.options.suppress_schema_version ())
+ {
+ instance<version_table> vt (*em, emos, f);
+ vt->migrate_pre (cs.version ());
+ }
+ }
+
+ void
+ generate_migrate_post (sema_rel::changeset& cs)
+ {
+ context ctx;
+ instance<sql_emitter> em;
+ emitter_ostream emos (*em);
+
+ schema_format f (schema_format::sql);
+
+ instance<changeset_post> changeset (*em, emos, f);
+ instance<drop_table> dtable (*em, emos, f);
+ instance<alter_table_post> atable (*em, emos, f);
+ trav_rel::qnames names;
+
+ changeset >> names;
+ names >> dtable;
+ names >> atable;
+
+ // Pass 1 and 2.
+ //
+ for (unsigned short pass (1); pass < 3; ++pass)
+ {
+ changeset->pass (pass);
+ dtable->pass (pass);
+ atable->pass (pass);
+
+ changeset->traverse (cs);
+ }
+
+ if (!ctx.options.suppress_schema_version ())
+ {
+ instance<version_table> vt (*em, emos, f);
+ vt->migrate_post ();
+ }
+ }
+ }
+}
diff --git a/odb/odb/relational/schema.hxx b/odb/odb/relational/schema.hxx
new file mode 100644
index 0000000..cd975b7
--- /dev/null
+++ b/odb/odb/relational/schema.hxx
@@ -0,0 +1,1606 @@
+// file : odb/relational/schema.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_SCHEMA_HXX
+#define ODB_RELATIONAL_SCHEMA_HXX
+
+#include <set>
+#include <vector>
+#include <cassert>
+
+#include <odb/emitter.hxx>
+#include <odb/relational/common.hxx>
+#include <odb/relational/context.hxx>
+
+namespace relational
+{
+ namespace schema
+ {
+ typedef std::set<qname> table_set;
+
+ struct common: virtual context
+ {
+ typedef ::emitter emitter_type;
+
+ common (emitter_type& e, ostream& os, schema_format f)
+ : e_ (e), os_ (os), format_ (f) {}
+
+ void
+ pre_statement ()
+ {
+ e_.pre ();
+ diverge (os_);
+ }
+
+ void
+ post_statement ()
+ {
+ restore ();
+ e_.post ();
+ }
+
+ emitter_type&
+ emitter () const
+ {
+ return e_;
+ }
+
+ ostream&
+ stream () const
+ {
+ return os_;
+ }
+
+ public:
+ // Find an entity corresponding to the drop node in alter_table.
+ //
+ template <typename T, typename D>
+ T&
+ find (D& d)
+ {
+ using sema_rel::model;
+ using sema_rel::changeset;
+ using sema_rel::table;
+ using sema_rel::alter_table;
+
+ alter_table& at (dynamic_cast<alter_table&> (d.scope ()));
+ changeset& cs (dynamic_cast<changeset&> (at.scope ()));
+ model& bm (cs.base_model ());
+ table* bt (bm.find<table> (at.name ()));
+ assert (bt != 0);
+ T* b (bt->find<T> (d.name ()));
+ assert (b != 0);
+ return *b;
+ }
+
+ protected:
+ emitter_type& e_;
+ ostream& os_;
+ schema_format format_;
+ };
+
+ //
+ // Drop.
+ //
+
+ // Only used in migration.
+ //
+ struct drop_column: trav_rel::drop_column, common
+ {
+ typedef drop_column base;
+
+ drop_column (common const& c, bool* first = 0)
+ : common (c),
+ first_ (first != 0 ? *first : first_data_),
+ first_data_ (true)
+ {
+ }
+
+ drop_column (drop_column const& c)
+ : root_context (), // @@ -Wextra
+ context (),
+ common (c),
+ first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_),
+ first_data_ (c.first_data_)
+ {
+ }
+
+ virtual void
+ drop_header ()
+ {
+ // By default ADD COLUMN though some databases use just ADD.
+ //
+ os << "DROP COLUMN ";
+ }
+
+ virtual void
+ traverse (sema_rel::drop_column& dc)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << ",";
+
+ os << endl
+ << " ";
+ drop_header ();
+ os << quote_id (dc.name ());
+ }
+
+ protected:
+ bool& first_;
+ bool first_data_;
+ };
+
+ // Normally only used in migration but some databases use it as a
+ // base to also drop foreign keys in schema.
+ //
+ struct drop_foreign_key: trav_rel::foreign_key,
+ trav_rel::drop_foreign_key,
+ trav_rel::add_foreign_key, // Override.
+ common
+ {
+ typedef drop_foreign_key base;
+
+ // Schema constructor.
+ //
+ drop_foreign_key (common const& c, table_set& dropped, bool* first = 0)
+ : common (c),
+ dropped_ (&dropped),
+ first_ (first != 0 ? *first : first_data_),
+ first_data_ (true)
+ {
+ }
+
+ // Migration constructor.
+ //
+ drop_foreign_key (common const& c, bool* first = 0)
+ : common (c),
+ dropped_ (0),
+ first_ (first != 0 ? *first : first_data_),
+ first_data_ (true)
+ {
+ }
+
+ drop_foreign_key (drop_foreign_key const& c)
+ : root_context (), // @@ -Wextra
+ context (),
+ common (c),
+ dropped_ (c.dropped_),
+ first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_),
+ first_data_ (c.first_data_)
+ {
+ }
+
+ virtual void
+ drop_header ()
+ {
+ os << "DROP CONSTRAINT ";
+ }
+
+ virtual void
+ traverse (sema_rel::foreign_key& fk)
+ {
+ // If the table which we reference is droped before us, then
+ // we need to drop the constraint first. Similarly, if the
+ // referenced table is not part if this model, then assume
+ // it is dropped before us. In migration we always do this
+ // first.
+ //
+ sema_rel::table& t (dynamic_cast<sema_rel::table&> (fk.scope ()));
+
+ if (dropped_ != 0)
+ {
+ sema_rel::qname const& rt (fk.referenced_table ());
+ sema_rel::model& m (dynamic_cast<sema_rel::model&> (t.scope ()));
+
+ if (dropped_->find (rt) == dropped_->end () &&
+ m.find (rt) != m.names_end ())
+ return;
+ }
+
+ drop (t, fk);
+ }
+
+ virtual void
+ drop (sema_rel::table& t, sema_rel::foreign_key& fk)
+ {
+ // When generating schema we would need to check if the key exists.
+ // So this implementation will need to be customize on the per-
+ // database level.
+ //
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (t.name ()) << endl
+ << " ";
+ drop_header ();
+ os << quote_id (fk.name ()) << endl;
+
+ post_statement ();
+ }
+
+ virtual void
+ traverse (sema_rel::drop_foreign_key& dfk)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << ",";
+
+ os << endl;
+ drop (dfk);
+ }
+
+ virtual void
+ drop (sema_rel::drop_foreign_key& dfk)
+ {
+ os << " ";
+ drop_header ();
+ os << quote_id (dfk.name ());
+ }
+
+ protected:
+ table_set* dropped_;
+ bool& first_;
+ bool first_data_;
+ };
+
+ // Currently only used in migration.
+ //
+ struct drop_index: trav_rel::drop_index, common
+ {
+ typedef drop_index base;
+
+ enum index_type {unique, non_unique, all};
+
+ drop_index (common const& c, index_type t = all)
+ : common (c), type_ (t) {}
+
+ virtual void
+ traverse (sema_rel::drop_index& di)
+ {
+ // Find the index we are dropping in the base model.
+ //
+ traverse (find<sema_rel::index> (di));
+ }
+
+ virtual void
+ traverse (sema_rel::index& in)
+ {
+ if (type_ == unique &&
+ in.type ().find ("UNIQUE") == string::npos &&
+ in.type ().find ("unique") == string::npos)
+ return;
+
+ if (type_ == non_unique && (
+ in.type ().find ("UNIQUE") != string::npos ||
+ in.type ().find ("unique") != string::npos))
+ return;
+
+ pre_statement ();
+ drop (in);
+ post_statement ();
+ }
+
+ virtual string
+ name (sema_rel::index& in)
+ {
+ return quote_id (in.name ());
+ }
+
+ virtual void
+ drop (sema_rel::index& in)
+ {
+ os << "DROP INDEX " << name (in) << endl;
+ }
+
+ protected:
+ index_type type_;
+ };
+
+ struct drop_table: trav_rel::table,
+ trav_rel::drop_table,
+ trav_rel::add_table, // Override.
+ trav_rel::alter_table, // Override.
+ common
+ {
+ typedef drop_table base;
+
+ drop_table (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os, f) {}
+
+ virtual void
+ drop (sema_rel::table& t, bool migration)
+ {
+ pre_statement ();
+ os << "DROP TABLE " << (migration ? "" : "IF EXISTS ") <<
+ quote_id (t.name ()) << endl;
+ post_statement ();
+ }
+
+ virtual void
+ delete_ (sema_rel::qname const& rtable,
+ sema_rel::qname const& dtable,
+ sema_rel::primary_key& rkey,
+ sema_rel::primary_key& dkey)
+ {
+ pre_statement ();
+
+ // This might not be the most efficient way for every database.
+ //
+ os << "DELETE FROM " << quote_id (rtable) << endl
+ << " WHERE EXISTS (SELECT 1 FROM " << quote_id (dtable) << endl
+ << " WHERE ";
+
+ for (size_t i (0); i != rkey.contains_size (); ++i)
+ {
+ if (i != 0)
+ os << endl
+ << " AND ";
+
+ os << quote_id (rtable) << "." <<
+ quote_id (rkey.contains_at (i).column ().name ()) << " = " <<
+ quote_id (dtable) << "." <<
+ quote_id (dkey.contains_at (i).column ().name ());
+ }
+
+ os << ")" << endl;
+
+ post_statement ();
+ }
+
+ virtual void
+ traverse (sema_rel::table& t, bool migration)
+ {
+ // By default drop foreign keys referencing tables that would
+ // have already been dropped on the first pass.
+ //
+ if (pass_ == 1)
+ {
+ // Drop constraints. In migration this is always done on pass 1.
+ //
+ if (migration)
+ {
+ instance<drop_foreign_key> dfk (*this);
+ trav_rel::unames n (*dfk);
+ names (t, n);
+ }
+ else
+ {
+ dropped_.insert (t.name ()); // Add it before to cover self-refs.
+
+ instance<drop_foreign_key> dfk (*this, dropped_);
+ trav_rel::unames n (*dfk);
+ names (t, n);
+ }
+ }
+ else
+ {
+ if (migration && t.extra ()["kind"] == "polymorphic derived object")
+ {
+ // If we are dropping a polymorphic derived object, then we
+ // also have to clean the base tables. Note that this won't
+ // trigger cascade deletion since we have dropped all the
+ // keys on pass 1. But we still need to do this in the base
+ // to root order in order not to trigger other cascades.
+ //
+ using sema_rel::model;
+ using sema_rel::table;
+ using sema_rel::primary_key;
+ using sema_rel::foreign_key;
+
+ model& m (dynamic_cast<model&> (t.scope ()));
+
+ table* p (&t);
+ do
+ {
+ // The polymorphic link is the first primary key.
+ //
+ for (table::names_iterator i (p->names_begin ());
+ i != p->names_end (); ++i)
+ {
+ if (foreign_key* fk = dynamic_cast<foreign_key*> (
+ &i->nameable ()))
+ {
+ p = m.find<table> (fk->referenced_table ());
+ assert (p != 0); // Base table should be there.
+ break;
+ }
+ }
+
+ primary_key& rkey (*p->find<primary_key> (""));
+ primary_key& dkey (*t.find<primary_key> (""));
+ assert (rkey.contains_size () == dkey.contains_size ());
+ delete_ (p->name (), t.name (), rkey, dkey);
+ }
+ while (p->extra ()["kind"] != "polymorphic root object");
+ }
+
+ drop (t, migration);
+ }
+ }
+
+ virtual void
+ traverse (sema_rel::table& t)
+ {
+ traverse (t, false);
+ }
+
+ virtual void
+ traverse (sema_rel::drop_table& dt)
+ {
+ using sema_rel::model;
+ using sema_rel::changeset;
+ using sema_rel::table;
+
+ // Find the table we are dropping in the base model.
+ //
+ changeset& cs (dynamic_cast<changeset&> (dt.scope ()));
+ model& bm (cs.base_model ());
+ table* t (bm.find<table> (dt.name ()));
+ assert (t != 0);
+ traverse (*t, true);
+ }
+
+ using add_table::traverse; // Unhide.
+ using alter_table::traverse; // Unhide.
+
+ using table::names;
+
+ void
+ pass (unsigned short p)
+ {
+ pass_ = p;
+ }
+
+ protected:
+ unsigned short pass_;
+ table_set dropped_;
+ };
+
+ struct drop_model: trav_rel::model, common
+ {
+ typedef drop_model base;
+
+ drop_model (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os, f)
+ {
+ }
+
+ virtual void
+ traverse (sema_rel::model& m)
+ {
+ // Traverse named entities in the reverse order. This way we
+ // drop them in the order opposite to creating.
+ //
+ for (sema_rel::model::names_iterator begin (m.names_begin ()),
+ end (m.names_end ()); begin != end;)
+ dispatch (*--end);
+ }
+
+ void
+ pass (unsigned short p)
+ {
+ pass_ = p;
+ }
+
+ protected:
+ unsigned short pass_;
+ };
+
+ //
+ // Create.
+ //
+
+ struct create_column: trav_rel::column,
+ trav_rel::add_column,
+ trav_rel::alter_column, // Override.
+ common
+ {
+ typedef create_column base;
+
+ create_column (common const& c,
+ bool override_null = true,
+ bool* first = 0)
+ : common (c),
+ override_null_ (override_null),
+ first_ (first != 0 ? *first : first_data_),
+ first_data_ (true)
+ {
+ }
+
+ create_column (create_column const& c)
+ : root_context (), // @@ -Wextra
+ context (),
+ common (c),
+ override_null_ (c.override_null_),
+ first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_),
+ first_data_ (c.first_data_)
+ {
+ }
+
+ virtual void
+ traverse (sema_rel::column& c)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << ",";
+
+ os << endl
+ << " ";
+ create (c);
+ }
+
+ virtual void
+ add_header ()
+ {
+ // By default ADD COLUMN though some databases use just ADD.
+ //
+ os << "ADD COLUMN ";
+ }
+
+ virtual void
+ traverse (sema_rel::add_column& ac)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << ",";
+
+ os << endl
+ << " ";
+ add_header ();
+ create (ac);
+ }
+
+ virtual void
+ create (sema_rel::column& c)
+ {
+ using sema_rel::column;
+
+ // See if this column is (part of) a primary key.
+ //
+ sema_rel::primary_key* pk (0);
+
+ for (column::contained_iterator i (c.contained_begin ());
+ i != c.contained_end ();
+ ++i)
+ {
+ if ((pk = dynamic_cast<sema_rel::primary_key*> (&i->key ())))
+ break;
+ }
+
+ os << quote_id (c.name ()) << " ";
+
+ type (c, pk != 0 && pk->auto_ ());
+ constraints (c, pk);
+
+ if (!c.options ().empty ())
+ os << " " << c.options ();
+ }
+
+ virtual void
+ constraints (sema_rel::column& c, sema_rel::primary_key* pk)
+ {
+ null (c);
+
+ if (!c.default_ ().empty ())
+ os << " DEFAULT " << c.default_ ();
+
+ // If this is a single-column primary key, generate it inline.
+ //
+ if (pk != 0 && pk->contains_size () == 1)
+ primary_key ();
+
+ if (pk != 0 && pk->auto_ ())
+ auto_ (*pk);
+ }
+
+ virtual void
+ type (sema_rel::column& c, bool /*auto*/)
+ {
+ os << c.type ();
+ }
+
+ virtual void
+ null (sema_rel::column& c)
+ {
+ bool n (c.null ());
+
+ // If we are adding a new column that doesn't allow NULL nor has
+ // a default value, add it as NULL. Later, after migration, we
+ // will convert it to NOT NULL.
+ //
+ if (override_null_ && c.is_a<sema_rel::add_column> () &&
+ !n && c.default_ ().empty ())
+ n = true;
+
+ // Specify both cases explicitly for better readability,
+ // especially in ALTER COLUMN clauses.
+ //
+ os << (n ? " NULL" : " NOT NULL");
+ }
+
+ virtual void
+ primary_key ()
+ {
+ os << " PRIMARY KEY";
+ }
+
+ virtual void
+ auto_ (sema_rel::primary_key&)
+ {
+ }
+
+ protected:
+ bool override_null_; // Override NOT NULL in add_column.
+ bool& first_;
+ bool first_data_;
+ bool add_;
+ };
+
+ struct create_primary_key: trav_rel::primary_key, common
+ {
+ typedef create_primary_key base;
+
+ create_primary_key (common const& c): common (c) {}
+
+ virtual void
+ traverse (sema_rel::primary_key& pk)
+ {
+ // Single-column primary keys are generated inline in the
+ // column declaration.
+ //
+ if (pk.contains_size () == 1)
+ return;
+
+ // We will always follow a column.
+ //
+ os << "," << endl;
+
+ create (pk);
+ }
+
+ virtual void
+ create (sema_rel::primary_key& pk)
+ {
+ using sema_rel::primary_key;
+
+ // By default we create unnamed primary key constraint.
+ //
+
+ os << " PRIMARY KEY (";
+
+ for (primary_key::contains_iterator i (pk.contains_begin ());
+ i != pk.contains_end ();
+ ++i)
+ {
+ if (i != pk.contains_begin ())
+ os << "," << endl
+ << " ";
+
+ os << quote_id (i->column ().name ());
+ }
+
+ os << ")";
+ }
+ };
+
+ struct create_foreign_key: trav_rel::foreign_key,
+ trav_rel::add_foreign_key,
+ common
+ {
+ typedef create_foreign_key base;
+
+ // Schema constructor, pass 1.
+ //
+ create_foreign_key (common const& c, table_set& created, bool* first = 0)
+ : common (c),
+ created_ (&created),
+ first_ (first != 0 ? *first : first_data_),
+ first_data_ (true)
+ {
+ }
+
+ // Schema constructor, pass 2 and migration constructor.
+ //
+ create_foreign_key (common const& c, bool* first = 0)
+ : common (c),
+ created_ (0),
+ first_ (first != 0 ? *first : first_data_),
+ first_data_ (true)
+ {
+ }
+
+ create_foreign_key (create_foreign_key const& c)
+ : root_context (), // @@ -Wextra
+ context (),
+ common (c),
+ created_ (c.created_),
+ first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_),
+ first_data_ (c.first_data_)
+ {
+ }
+
+ virtual void
+ traverse (sema_rel::foreign_key& fk)
+ {
+ if (created_ != 0)
+ {
+ // Pass 1.
+ //
+ // If the referenced table has already been defined, do the
+ // foreign key definition in the table definition. Otherwise
+ // postpone it until pass 2 where we do it via ALTER TABLE.
+ //
+ if (created_->find (fk.referenced_table ()) != created_->end ())
+ {
+ traverse_create (fk);
+ fk.set (db.string () + "-fk-defined", true); // Mark it as defined.
+ }
+ }
+ else
+ {
+ // Pass 2.
+ //
+ if (!fk.count (db.string () + "-fk-defined"))
+ traverse_add (fk);
+ }
+ }
+
+ virtual void
+ traverse_create (sema_rel::foreign_key& fk)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << ",";
+
+ os << endl
+ << " CONSTRAINT ";
+ create (fk);
+ }
+
+ virtual void
+ traverse_add (sema_rel::foreign_key& fk)
+ {
+ if (first_)
+ first_ = false;
+ else
+ os << ",";
+
+ os << endl;
+ add (fk);
+ }
+
+ virtual void
+ traverse (sema_rel::add_foreign_key& afk)
+ {
+ traverse_add (afk);
+ }
+
+ virtual void
+ add_header ()
+ {
+ os << "ADD CONSTRAINT ";
+ }
+
+ virtual void
+ add (sema_rel::foreign_key& fk)
+ {
+ os << " ";
+ add_header ();
+ create (fk);
+ }
+
+ virtual void
+ create (sema_rel::foreign_key& fk)
+ {
+ using sema_rel::foreign_key;
+
+ os << name (fk) << endl
+ << " FOREIGN KEY (";
+
+ for (foreign_key::contains_iterator i (fk.contains_begin ());
+ i != fk.contains_end ();
+ ++i)
+ {
+ if (i != fk.contains_begin ())
+ os << "," << endl
+ << " ";
+
+ os << quote_id (i->column ().name ());
+ }
+
+ string tn (table_name (fk));
+ string tn_pad (tn.size (), ' ');
+
+ os << ")" << endl
+ << " REFERENCES " << tn << " (";
+
+ foreign_key::columns const& refs (fk.referenced_columns ());
+ for (foreign_key::columns::const_iterator i (refs.begin ());
+ i != refs.end ();
+ ++i)
+ {
+ if (i != refs.begin ())
+ os << "," << endl
+ << " " << tn_pad;
+
+ os << quote_id (*i);
+ }
+
+ os << ")";
+
+ if (fk.on_delete () != foreign_key::no_action)
+ on_delete (fk.on_delete ());
+
+ if (!fk.not_deferrable ())
+ deferrable (fk.deferrable ());
+ }
+
+ virtual string
+ name (sema_rel::foreign_key& fk)
+ {
+ return quote_id (fk.name ());
+ }
+
+ virtual string
+ table_name (sema_rel::foreign_key& fk)
+ {
+ return quote_id (fk.referenced_table ());
+ }
+
+ virtual void
+ on_delete (sema_rel::foreign_key::action_type a)
+ {
+ using sema_rel::foreign_key;
+
+ switch (a)
+ {
+ case foreign_key::no_action:
+ break;
+ case foreign_key::cascade:
+ {
+ os << endl
+ << " ON DELETE CASCADE";
+ break;
+ }
+ case foreign_key::set_null:
+ {
+ os << endl
+ << " ON DELETE SET NULL";
+ break;
+ }
+ }
+ }
+
+ virtual void
+ deferrable (sema_rel::deferrable d)
+ {
+ os << endl
+ << " DEFERRABLE INITIALLY " << d;
+ }
+
+ protected:
+ table_set* created_;
+ bool& first_;
+ bool first_data_;
+ };
+
+ struct create_index: trav_rel::index, common
+ {
+ typedef create_index base;
+
+ enum index_type {unique, non_unique, all};
+
+ create_index (common const& c, index_type t = all)
+ : common (c), type_ (t) {}
+
+ virtual void
+ traverse (sema_rel::index& in)
+ {
+ if (type_ == unique &&
+ in.type ().find ("UNIQUE") == string::npos &&
+ in.type ().find ("unique") == string::npos)
+ return;
+
+ if (type_ == non_unique && (
+ in.type ().find ("UNIQUE") != string::npos ||
+ in.type ().find ("unique") != string::npos))
+ return;
+
+ pre_statement ();
+ create (in);
+ post_statement ();
+ }
+
+ virtual string
+ name (sema_rel::index& in)
+ {
+ return quote_id (in.name ());
+ }
+
+ virtual string
+ table_name (sema_rel::index& in)
+ {
+ return quote_id (static_cast<sema_rel::table&> (in.scope ()).name ());
+ }
+
+ virtual void
+ columns (sema_rel::index& in)
+ {
+ using sema_rel::index;
+
+ for (index::contains_iterator i (in.contains_begin ());
+ i != in.contains_end ();
+ ++i)
+ {
+ if (in.contains_size () > 1)
+ {
+ if (i != in.contains_begin ())
+ os << ",";
+
+ os << endl
+ << " ";
+ }
+
+ os << quote_id (i->column ().name ());
+
+ if (!i->options ().empty ())
+ os << ' ' << i->options ();
+ }
+ }
+
+ virtual void
+ create (sema_rel::index& in)
+ {
+ // Default implementation that ignores the method.
+ //
+ os << "CREATE ";
+
+ if (!in.type ().empty ())
+ os << in.type () << ' ';
+
+ os << "INDEX " << name (in) << endl
+ << " ON " << table_name (in) << " (";
+
+ columns (in);
+
+ os << ")" << endl;
+
+ if (!in.options ().empty ())
+ os << ' ' << in.options () << endl;
+ }
+
+ protected:
+ index_type type_;
+ };
+
+ struct create_table: trav_rel::table,
+ trav_rel::alter_table, // Override.
+ common
+ {
+ typedef create_table base;
+
+ using trav_rel::table::names;
+
+ create_table (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os, f) {}
+
+ virtual void
+ create_pre (sema_rel::qname const& table)
+ {
+ os << "CREATE TABLE " << quote_id (table) << " (";
+ }
+
+ virtual void
+ create_post (sema_rel::table& t)
+ {
+ os << ")" << endl;
+
+ if (!t.options ().empty ())
+ os << " " << t.options () << endl;
+ }
+
+ virtual void
+ create (sema_rel::table& t)
+ {
+ pre_statement ();
+ create_pre (t.name ());
+
+ instance<create_column> c (*this);
+ instance<create_primary_key> pk (*this);
+
+ // We will always follow a column, so set first to false.
+ //
+ bool f (false); // (Im)perfect forwarding.
+ bool* pf (&f); // (Im)perfect forwarding.
+ instance<create_foreign_key> fk (*this, created_, pf);
+
+ trav_rel::unames n;
+ n >> c;
+ n >> pk;
+ n >> fk;
+
+ names (t, n);
+
+ create_post (t);
+ post_statement ();
+
+ // Create indexes.
+ //
+ {
+ instance<create_index> in (*this);
+ trav_rel::unames n (*in);
+ names (t, n);
+ }
+ }
+
+ // See if there are any undefined foreign keys that we need to
+ // add with ALTER TABLE.
+ //
+ bool
+ check_undefined_fk (sema_rel::table& t)
+ {
+ for (sema_rel::table::names_iterator i (t.names_begin ());
+ i != t.names_end (); ++i)
+ {
+ if (i->nameable ().is_a<sema_rel::foreign_key> () &&
+ !i->nameable ().count (db.string () + "-fk-defined"))
+ return true;
+ }
+ return false;
+ }
+
+ virtual void
+ traverse (sema_rel::table& t)
+ {
+ // By default add foreign keys referencing tables that haven't
+ // yet been defined on the second pass.
+ //
+ if (pass_ == 1)
+ {
+ // In migration we always add foreign keys on pass 2.
+ //
+ if (!t.is_a<sema_rel::add_table> ())
+ created_.insert (t.name ()); // Add it before to cover self-refs.
+
+ create (t);
+ }
+ else
+ {
+ // Add undefined foreign keys.
+ //
+ if (check_undefined_fk (t))
+ {
+ pre_statement ();
+ os << "ALTER TABLE " << quote_id (t.name ());
+
+ instance<create_foreign_key> cfk (*this);
+ trav_rel::unames n (*cfk);
+ names (t, n);
+ os << endl;
+
+ post_statement ();
+ }
+ }
+ }
+
+ void
+ pass (unsigned short p)
+ {
+ pass_ = p;
+ }
+
+ protected:
+ unsigned short pass_;
+ table_set created_;
+ };
+
+ struct create_model: trav_rel::model, common
+ {
+ typedef create_model base;
+
+ create_model (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os, f) {}
+
+ void
+ pass (unsigned short p)
+ {
+ pass_ = p;
+ }
+
+ protected:
+ unsigned short pass_;
+ };
+
+ //
+ // Alter.
+ //
+
+ struct alter_column: trav_rel::alter_column,
+ trav_rel::add_column,
+ common
+ {
+ typedef alter_column base;
+
+ alter_column (common const& c, bool pre, bool* first = 0)
+ : common (c),
+ pre_ (pre),
+ first_ (first != 0 ? *first : first_data_),
+ first_data_ (true),
+ fl_ (false),
+ def_ (c, fl_)
+ {
+ }
+
+ alter_column (alter_column const& c)
+ : root_context (), // @@ -Wextra
+ context (),
+ common (c),
+ pre_ (c.pre_),
+ first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_),
+ first_data_ (c.first_data_),
+ fl_ (false),
+ def_ (c, fl_)
+ {
+ }
+
+ virtual void
+ alter_header ()
+ {
+ os << "ALTER COLUMN ";
+ }
+
+ virtual void
+ alter (sema_rel::column& c)
+ {
+ // By default use the whole definition.
+ //
+ def_->create (c);
+ }
+
+ virtual void
+ traverse (sema_rel::column& c)
+ {
+ // Relax (NULL) in pre and tighten (NOT NULL) in post.
+ //
+ if (pre_ != c.null ())
+ return;
+
+ if (first_)
+ first_ = false;
+ else
+ os << ",";
+
+ os << endl
+ << " ";
+ alter_header ();
+ alter (c);
+ }
+
+ virtual void
+ traverse (sema_rel::alter_column& ac)
+ {
+ assert (ac.null_altered ());
+ traverse (static_cast<sema_rel::column&> (ac));
+ }
+
+ virtual void
+ traverse (sema_rel::add_column& ac)
+ {
+ // We initially add NOT NULL columns without default values as
+ // NULL. Now, after the migration, we convert them to NOT NULL.
+ //
+ if (!ac.null () && ac.default_ ().empty ())
+ traverse (static_cast<sema_rel::column&> (ac));
+ }
+
+ protected:
+ bool pre_;
+ bool& first_;
+ bool first_data_;
+ bool fl_; // (Im)perfect forwarding.
+ instance<create_column> def_;
+ };
+
+ struct alter_table_common: trav_rel::alter_table, common
+ {
+ alter_table_common (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os, f) {}
+
+ template <typename T>
+ T*
+ check (sema_rel::alter_table& at)
+ {
+ for (sema_rel::alter_table::names_iterator i (at.names_begin ());
+ i != at.names_end (); ++i)
+ {
+ if (T* x = dynamic_cast<T*> (&i->nameable ()))
+ return x;
+ }
+ return 0;
+ }
+
+ sema_rel::column*
+ check_alter_column_null (sema_rel::alter_table& at, bool v)
+ {
+ for (sema_rel::alter_table::names_iterator i (at.names_begin ());
+ i != at.names_end (); ++i)
+ {
+ using sema_rel::add_column;
+ using sema_rel::alter_column;
+
+ if (alter_column* ac = dynamic_cast<alter_column*> (&i->nameable ()))
+ {
+ if (ac->null_altered () && ac->null () == v)
+ return ac;
+ }
+
+ // If we are testing for NOT NULL, also look for new columns that
+ // we initially add as NULL and later convert to NOT NULL.
+ //
+ if (!v)
+ {
+ if (add_column* ac = dynamic_cast<add_column*> (&i->nameable ()))
+ {
+ if (!ac->null () && ac->default_ ().empty ())
+ return ac;
+ }
+ }
+ }
+ return 0;
+ }
+
+ void
+ pass (unsigned short p)
+ {
+ pass_ = p;
+ }
+
+ protected:
+ unsigned short pass_;
+ };
+
+ struct alter_table_pre: alter_table_common
+ {
+ typedef alter_table_pre base;
+
+ alter_table_pre (emitter_type& e, ostream& os, schema_format f)
+ : alter_table_common (e, os, f) {}
+
+ // Check if there will be any clauses in ALTER TABLE.
+ //
+ using alter_table_common::check;
+
+ virtual bool
+ check (sema_rel::alter_table& at)
+ {
+ // If changing the below test, make sure to also update tests
+ // in database-specific code.
+ //
+ return
+ check<sema_rel::drop_foreign_key> (at) ||
+ check<sema_rel::add_column> (at) ||
+ check_alter_column_null (at, true);
+ }
+
+ virtual void
+ alter (sema_rel::alter_table& at)
+ {
+ // By default we generate all the alterations in a single ALTER TABLE
+ // statement. Quite a few databases don't support this.
+ //
+ pre_statement ();
+ os << "ALTER TABLE " << quote_id (at.name ());
+
+ bool f (true); // Shared first flag.
+ bool* pf (&f); // (Im)perfect forwarding.
+ bool tl (true); // (Im)perfect forwarding.
+ instance<create_column> cc (*this, tl, pf);
+ instance<alter_column> ac (*this, tl, pf);
+ instance<drop_foreign_key> dfk (*this, pf);
+ trav_rel::unames n;
+ n >> cc;
+ n >> ac;
+ n >> dfk;
+ names (at, n);
+ os << endl;
+
+ post_statement ();
+ }
+
+ virtual void
+ traverse (sema_rel::alter_table& at)
+ {
+ if (pass_ == 1)
+ {
+ // Drop unique indexes.
+ //
+ {
+ drop_index::index_type it (drop_index::unique);
+ instance<drop_index> in (*this, it);
+ trav_rel::unames n (*in);
+ names (at, n);
+ }
+
+ if (check (at))
+ alter (at);
+ }
+ else
+ {
+ // Add non-unique indexes.
+ //
+ {
+ create_index::index_type it (create_index::non_unique);
+ instance<create_index> in (*this, it);
+ trav_rel::unames n (*in);
+ names (at, n);
+ }
+ }
+ }
+ };
+
+ struct changeset_pre: trav_rel::changeset, common
+ {
+ typedef changeset_pre base;
+
+ changeset_pre (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os, f) {}
+
+ void
+ pass (unsigned short p)
+ {
+ pass_ = p;
+ }
+
+ protected:
+ unsigned short pass_;
+ };
+
+ struct alter_table_post: alter_table_common
+ {
+ typedef alter_table_post base;
+
+ alter_table_post (emitter_type& e, ostream& os, schema_format f)
+ : alter_table_common (e, os, f) {}
+
+ // Check if there will be any clauses in ALTER TABLE.
+ //
+ using alter_table_common::check;
+
+ virtual bool
+ check (sema_rel::alter_table& at)
+ {
+ // If changing the below test, make sure to also update tests
+ // in database-specific code.
+ //
+ return
+ check<sema_rel::add_foreign_key> (at) ||
+ check<sema_rel::drop_column> (at) ||
+ check_alter_column_null (at, false);
+ }
+
+ virtual void
+ alter (sema_rel::alter_table& at)
+ {
+ // By default we generate all the alterations in a single ALTER TABLE
+ // statement. Quite a few databases don't support this.
+ //
+ pre_statement ();
+ os << "ALTER TABLE " << quote_id (at.name ());
+
+ bool f (true); // Shared first flag.
+ bool* pf (&f); // (Im)perfect forwarding.
+ bool fl (false); // (Im)perfect forwarding.
+ instance<drop_column> dc (*this, pf);
+ instance<alter_column> ac (*this, fl, pf);
+ instance<create_foreign_key> fk (*this, pf);
+
+ trav_rel::unames n;
+ n >> dc;
+ n >> ac;
+ n >> fk;
+ names (at, n);
+ os << endl;
+
+ post_statement ();
+ }
+
+ virtual void
+ traverse (sema_rel::alter_table& at)
+ {
+ if (pass_ == 1)
+ {
+ // Drop non-unique indexes.
+ //
+ {
+ drop_index::index_type it (drop_index::non_unique);
+ instance<drop_index> in (*this, it);
+ trav_rel::unames n (*in);
+ names (at, n);
+ }
+ }
+ else
+ {
+ if (check (at))
+ alter (at);
+
+ // Add unique indexes.
+ //
+ {
+ create_index::index_type it (create_index::unique);
+ instance<create_index> in (*this, it);
+ trav_rel::unames n (*in);
+ names (at, n);
+ }
+ }
+ }
+ };
+
+ struct changeset_post: trav_rel::changeset, common
+ {
+ typedef changeset_post base;
+
+ changeset_post (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os, f) {}
+
+ virtual void
+ traverse (sema_rel::changeset& m)
+ {
+ // Traverse named entities in the reverse order. This way we
+ // drop them in the order opposite to creating.
+ //
+ for (sema_rel::changeset::names_iterator begin (m.names_begin ()),
+ end (m.names_end ()); begin != end;)
+ dispatch (*--end);
+ }
+
+ void
+ pass (unsigned short p)
+ {
+ pass_ = p;
+ }
+
+ protected:
+ unsigned short pass_;
+ };
+
+ //
+ // Schema version table.
+ //
+
+ struct version_table: common
+ {
+ typedef version_table base;
+
+ version_table (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os, f),
+ table_ (options.schema_version_table ()[db]),
+ qt_ (quote_id (table_)),
+ qs_ (quote_string (options.schema_name ()[db])),
+ qn_ (quote_id ("name")),
+ qv_ (quote_id ("version")),
+ qm_ (quote_id ("migration"))
+ {
+ }
+
+ // Create the version table if it doesn't exist.
+ //
+ virtual void
+ create_table () {}
+
+ // Remove the version entry. Called after the DROP statements.
+ //
+ virtual void
+ drop ()
+ {
+ pre_statement ();
+
+ os << "DELETE FROM " << qt_ << endl
+ << " WHERE " << qn_ << " = " << qs_ << endl;
+
+ post_statement ();
+ }
+
+ // Set the version. Called after the CREATE statements.
+ //
+ virtual void
+ create (sema_rel::version) {}
+
+ // Set the version and migration state to true. Called after
+ // the pre migration statements.
+ //
+ virtual void
+ migrate_pre (sema_rel::version v)
+ {
+ pre_statement ();
+
+ os << "UPDATE " << qt_ << endl
+ << " SET " << qv_ << " = " << v << ", " << qm_ << " = 1" << endl
+ << " WHERE " << qn_ << " = " << qs_ << endl;
+
+ post_statement ();
+ }
+
+ // Set migration state to false. Called after the post migration
+ // statements.
+ //
+ virtual void
+ migrate_post ()
+ {
+ pre_statement ();
+
+ os << "UPDATE " << qt_ << endl
+ << " SET " << qm_ << " = 0" << endl
+ << " WHERE " << qn_ << " = " << qs_ << endl;
+
+ post_statement ();
+ }
+
+ protected:
+ sema_rel::qname table_;
+ string qt_; // Quoted table.
+ string qs_; // Quoted schema name string.
+ string qn_; // Quoted name column.
+ string qv_; // Quoted version column.
+ string qm_; // Quoted migration column.
+ };
+
+ //
+ // SQL output.
+ //
+
+ struct sql_emitter: emitter, virtual context
+ {
+ typedef sql_emitter base;
+
+ virtual void
+ pre ()
+ {
+ first_ = true;
+ }
+
+ virtual void
+ line (const std::string& l)
+ {
+ if (first_ && !l.empty ())
+ first_ = false;
+ else
+ os << endl;
+
+ os << l;
+ }
+
+ virtual void
+ post ()
+ {
+ if (!first_) // Ignore empty statements.
+ os << ';' << endl
+ << endl;
+ }
+
+ protected:
+ bool first_;
+ };
+
+ struct sql_file: virtual context
+ {
+ typedef sql_file base;
+
+ virtual void
+ prologue ()
+ {
+ }
+
+ virtual void
+ epilogue ()
+ {
+ }
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_SCHEMA_HXX
diff --git a/odb/odb/relational/source.cxx b/odb/odb/relational/source.cxx
new file mode 100644
index 0000000..abc0a46
--- /dev/null
+++ b/odb/odb/relational/source.cxx
@@ -0,0 +1,6345 @@
+// file : odb/relational/source.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <map>
+
+#include <odb/gcc.hxx>
+
+#include <odb/lookup.hxx>
+#include <odb/cxx-lexer.hxx>
+
+#include <odb/relational/source.hxx>
+#include <odb/relational/generate.hxx>
+
+using namespace std;
+
+void relational::source::class_::
+traverse_object (type& c)
+{
+ using semantics::data_member;
+
+ data_member_path* id (id_member (c));
+ data_member* idf (id ? id->front () : 0);
+ data_member* idb (id ? id->back () : 0);
+ bool auto_id (id && auto_ (*id));
+ bool base_id (id && &idf->scope () != &c); // Comes from base.
+
+ data_member* opt (optimistic (c));
+
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+ type* poly_base (poly_derived ? &polymorphic_base (c) : 0);
+ size_t poly_depth (poly_derived ? polymorphic_depth (c) : 1);
+ data_member* discriminator (poly ? context::discriminator (*poly_root) : 0);
+
+ bool abst (abstract (c));
+ bool reuse_abst (abst && !poly);
+ bool readonly (context::readonly (c));
+
+ bool grow (false);
+ bool grow_id (false);
+
+ if (generate_grow)
+ {
+ grow = context::grow (c);
+ grow_id = (id ? context::grow (*idb) : false) ||
+ (opt ? context::grow (*opt) : false);
+ }
+
+ column_count_type const& cc (column_count (c));
+ bool versioned (context::versioned (c));
+
+ // Schema name as a string literal or empty.
+ //
+ string schema_name (options.schema_name ()[db]);
+ if (!schema_name.empty ())
+ schema_name = strlit (schema_name);
+
+ string const& type (class_fq_name (c));
+ string traits ("access::object_traits_impl< " + type + ", id_" +
+ db.string () + " >");
+
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+ user_sections* buss (poly_base != 0
+ ? &poly_base->get<user_sections> ("user-sections")
+ : 0);
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl
+ << endl;
+
+ object_extra (c);
+
+ //
+ // Query (abstract and concrete).
+ //
+
+ // query_columns
+ //
+ if (options.generate_query ())
+ query_columns_type_->traverse (c);
+
+ // Statement cache (definition).
+ //
+ if (!reuse_abst && id != 0)
+ {
+ bool sections (false);
+ bool containers (has_a (c, test_container));
+
+ os << "struct " << traits << "::extra_statement_cache_type"
+ << "{";
+
+ instance<container_cache_members> cm;
+ cm->traverse (c);
+
+ if (containers)
+ os << endl;
+
+ instance<section_cache_members> sm;
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // Skip the special version update section in reuse inheritance (we
+ // always treat it as abstract).
+ //
+ if (i->special == user_section::special_version && !poly)
+ continue;
+
+ // Generate an entry for a readonly section in optimistic
+ // polymorphic root since it can be overridden and we may
+ // need to update the version.
+ //
+ if (!i->empty () || (poly && i->optimistic ()))
+ {
+ sm->traverse (*i);
+ sections = true;
+ }
+ }
+
+ if (sections)
+ os << endl;
+
+ os << "extra_statement_cache_type (" << endl
+ << db << "::connection&" << (containers || sections ? " c" : "") <<
+ "," << endl
+ << "image_type&" << (sections ? " im" : "") << "," << endl
+ << "id_image_type&" << (sections ? " idim" : "") << "," << endl
+ << db << "::binding&" << (containers || sections ? " id" : "") <<
+ "," << endl
+ << db << "::binding&" << (sections ? " idv" : "");
+
+ extra_statement_cache_extra_args (containers, sections);
+
+ os << ")";
+
+ instance<container_cache_init_members> cim;
+ cim->traverse (c);
+
+ instance<section_cache_init_members> sim (!containers);
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ if (i->special == user_section::special_version && !poly)
+ continue;
+
+ if (!i->empty () || (poly && i->optimistic ()))
+ sim->traverse (*i);
+ }
+
+ os << "{"
+ << "}"
+ << "};";
+ }
+
+ //
+ // Containers (abstract and concrete).
+ //
+
+ {
+ instance<container_traits> t (c);
+ t->traverse (c);
+ }
+
+ //
+ // Sections (abstract and concrete).
+ //
+
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ instance<section_traits> t (c);
+ t->traverse (*i);
+ }
+
+ //
+ // Functions (abstract and concrete).
+ //
+
+ // id(), version()
+ //
+ if (!poly_derived && id != 0 && !base_id)
+ {
+ // id (id_image_type)
+ //
+ if (auto_id)
+ {
+ os << traits << "::id_type" << endl
+ << traits << "::" << endl
+ << "id (const id_image_type& i)"
+ << "{"
+ << db << "::database* db (0);"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << endl
+ << "id_type id;";
+ init_id_value_member_id_image_->traverse (*idb);
+ os << "return id;"
+ << "}";
+ }
+
+ // id (image)
+ //
+ if (options.generate_query ())
+ {
+ os << traits << "::id_type" << endl
+ << traits << "::" << endl
+ << "id (const image_type& i)"
+ << "{"
+ << db << "::database* db (0);"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << endl
+ << "id_type id;";
+
+ // Handle nested id.
+ //
+ if (id->size () > 1)
+ {
+ string var;
+
+ for (data_member_path::const_iterator i (id->begin ());
+ i != id->end ();
+ ++i)
+ {
+ // The same logic as in member_base.
+ //
+ if (!var.empty ())
+ var += "value."; // Composite.
+
+ string const& name ((*i)->name ());
+ var += name;
+
+ if (name[name.size () - 1] != '_')
+ var += '_';
+ }
+
+ instance<init_value_member> t ("id", var);
+ t->traverse (*idb);
+ }
+ else
+ init_id_value_member_->traverse (*idb);
+
+ os << "return id;"
+ << "}";
+ }
+
+ // version (image)
+ //
+ if (opt != 0)
+ {
+ os << traits << "::version_type" << endl
+ << traits << "::" << endl
+ << "version (const image_type& i)"
+ << "{"
+ << "version_type v;";
+ init_version_value_member_->traverse (*opt);
+ os << "return v;"
+ << "}";
+ }
+ }
+
+ // discriminator(image)
+ //
+ if (poly && !poly_derived)
+ {
+ os << traits << "::discriminator_type" << endl
+ << traits << "::" << endl
+ << "discriminator (const image_type& i)"
+ << "{"
+ << db << "::database* db (0);"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << endl
+ << "discriminator_type d;";
+ init_discriminator_value_member_->traverse (*discriminator);
+ os << "return d;"
+ << "}";
+ }
+
+ // grow ()
+ //
+ if (generate_grow)
+ {
+ os << "bool " << traits << "::" << endl
+ << "grow (image_type& i," << endl
+ << truncated_vector << " t";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ if (poly_derived)
+ os << "," << endl
+ << "std::size_t d";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (t);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "bool grew (false);"
+ << endl;
+
+ index_ = 0;
+
+ if (poly_derived)
+ {
+ // Select column count for this class.
+ //
+ size_t cols (cc.total - cc.id);
+
+ os << "// " << class_name (*poly_base) << " base" << endl
+ << "//" << endl
+ << "if (--d != 0)"
+ << "{"
+ << "if (base_traits::grow (*i.base, " <<
+ "t + " << cols << "UL" <<
+ (context::versioned (*poly_base) ? ", svm" : "") <<
+ (poly_base != poly_root ? ", d" : "") << "))" << endl
+ << "i.base->version++;"
+ << "}";
+ }
+ else
+ inherits (c, grow_base_inherits_);
+
+ names (c, grow_member_names_);
+
+ os << "return grew;"
+ << "}";
+ }
+
+ // bind (image_type)
+ //
+ os << "void " << traits << "::" << endl
+ << "bind (" << bind_vector << " b," << endl;
+
+ // If we are a derived type in a polymorphic hierarchy, then
+ // we get the external id binding.
+ //
+ if (poly_derived)
+ os << "const " << bind_vector << " id," << endl
+ << "std::size_t id_size," << endl;
+
+ os << "image_type& i," << endl
+ << db << "::statement_kind sk";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (sk);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "using namespace " << db << ";"
+ << endl;
+
+ if (readonly)
+ os << "assert (sk != statement_update);"
+ << endl;
+
+ os << "std::size_t n (0);"
+ << endl;
+
+ if (poly_derived)
+ {
+ // The id reference comes first in the insert statement.
+ //
+ os << "// " << idf->name () << endl
+ << "//" << endl
+ << "if (sk == statement_insert)"
+ << "{"
+ << "if (id != 0)" << endl
+ << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
+ << "n += id_size;" // Not in if for "id unchanged" optimization.
+ << "}";
+ }
+ else
+ inherits (c, bind_base_inherits_);
+
+ names (c, bind_member_names_);
+
+ if (poly_derived)
+ {
+ // The id reference comes last in the update statement.
+ //
+ if (!readonly)
+ os << "// " << idf->name () << endl
+ << "//" << endl
+ << "if (sk == statement_update)"
+ << "{"
+ << "if (id != 0)" << endl
+ << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
+ << "n += id_size;" // Not in if for "id unchanged" optimization.
+ << "}";
+
+ // Bind the image chain for the select statement. Seeing that
+ // this is the last statement in the function, we don't care
+ // about updating n.
+ //
+ os << "// " << class_name (*poly_base) << " base" << endl
+ << "//" << endl
+ << "if (sk == statement_select)" << endl
+ << "base_traits::bind (b + n, ";
+
+ if (poly_base != poly_root)
+ os << "id, id_size, ";
+
+ os << "*i.base, sk" <<
+ (context::versioned (*poly_base) ? ", svm" : "") << ");";
+ }
+
+ os << "}";
+
+ // bind (id_image_type)
+ //
+ if (!poly_derived && id != 0 && !base_id)
+ {
+ os << "void " << traits << "::" << endl
+ << "bind (" << bind_vector << " b, id_image_type& i" <<
+ (opt != 0 ? ", bool bv" : "") << ")"
+ << "{"
+ << "std::size_t n (0);";
+
+ if (composite_wrapper (utype (*id)))
+ os << db << "::statement_kind sk (" << db << "::statement_select);";
+
+ bind_id_member_->traverse (*idb);
+
+ if (opt != 0)
+ {
+ os << "if (bv)"
+ << "{"
+ << "n += " << column_count (c).id << ";"
+ << endl;
+
+ bind_version_member_->traverse (*opt);
+ os << "}";
+ }
+
+ os << "}";
+ }
+
+ // init (image, object)
+ //
+ os << (generate_grow ? "bool " : "void ") << traits << "::" << endl
+ << "init (image_type& i," << endl
+ << "const object_type& o," << endl
+ << db << "::statement_kind sk";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (o);"
+ << "ODB_POTENTIALLY_UNUSED (sk);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "using namespace " << db << ";"
+ << endl;
+
+ if (readonly)
+ os << "assert (sk != statement_update);"
+ << endl;
+
+ init_image_pre (c);
+
+ if (generate_grow)
+ os << "bool grew (false);"
+ << endl;
+
+ if (!poly_derived)
+ inherits (c, init_image_base_inherits_);
+
+ names (c, init_image_member_names_);
+
+ if (generate_grow)
+ os << "return grew;";
+
+ os << "}";
+
+ // init (object, image)
+ //
+ os << "void " << traits << "::" << endl
+ << "init (object_type& o," << endl
+ << "const image_type& i," << endl
+ << "database* db";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ if (poly_derived)
+ os << "," << endl
+ << "std::size_t d";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (o);"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (db);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl;
+
+ if (poly_derived)
+ {
+ os << "// " << class_name (*poly_base) << " base" << endl
+ << "//" << endl
+ << "if (--d != 0)" << endl
+ << "base_traits::init (o, *i.base, db" <<
+ (context::versioned (*poly_base) ? ", svm" : "") <<
+ (poly_base != poly_root ? ", d" : "") << ");"
+ << endl;
+ }
+ else
+ inherits (c, init_value_base_inherits_);
+
+ names (c, init_value_member_names_);
+
+ os << "}";
+
+ // init (id_image, id)
+ //
+ if (id != 0 && !base_id)
+ {
+ os << "void " << traits << "::" << endl
+ << "init (id_image_type& i, const id_type& id" <<
+ (opt != 0 ? ", const version_type* v" : "") << ")"
+ << "{";
+
+ if (grow_id)
+ os << "bool grew (false);";
+
+ if (composite_wrapper (utype (*id)))
+ os << db << "::statement_kind sk (" << db << "::statement_select);";
+
+ init_id_image_member_->traverse (*idb);
+
+ if (opt != 0)
+ {
+ // Here we rely on the fact that init_image_member
+ // always wraps the statements in a block.
+ //
+ os << "if (v != 0)";
+ init_version_image_member_->traverse (*opt);
+ }
+
+ if (grow_id)
+ os << "if (grew)" << endl
+ << "i.version++;";
+
+ os << "}";
+ }
+
+ // The rest does not apply to reuse-abstract objects.
+ //
+ if (reuse_abst)
+ return;
+
+ //
+ // Containers (concrete).
+ //
+
+ //
+ // Sections (concrete).
+ //
+
+ // Polymorphic map.
+ //
+ if (poly)
+ {
+ if (!poly_derived)
+ os << traits << "::map_type*" << endl
+ << traits << "::map;"
+ << endl;
+
+ // Sections. Do we have any new sections or overrides?
+ //
+ bool sections (
+ uss.count (user_sections::count_new |
+ user_sections::count_override |
+ user_sections::count_load |
+ user_sections::count_update |
+ user_sections::count_special_version |
+ (poly ? user_sections::count_optimistic : 0)) != 0);
+ if (sections)
+ {
+ string const& fn (flat_name (type));
+
+ size_t n (uss.count (user_sections::count_total |
+ user_sections::count_all |
+ user_sections::count_special_version));
+
+ map<size_t, user_section*> m;
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ m[i->index] = &*i;
+
+ os << "static const "<< traits << "::" << (abst ? "abstract_" : "") <<
+ "info_type::section_functions" << endl
+ << "section_functions_for_" << fn << "[] ="
+ << "{";
+
+ for (size_t i (0); i != n; ++i)
+ {
+ os << (i != 0 ? "," : "") << "{";
+
+ map<size_t, user_section*>::iterator j (m.find (i));
+
+ if (j != m.end ())
+ {
+ user_section& s (*j->second);
+ string n (public_name (*s.member));
+
+ // load
+ //
+ if (s.load_empty ())
+ os << "0";
+ else
+ os << "&odb::section_load_impl< " << type << ", id_" << db <<
+ ", " << traits << "::" << n << "_traits >";
+
+ os << "," << endl;
+
+ // update
+ //
+ // Generate an entry for a readonly section in optimistic
+ // polymorphic root since it can be overridden and we may
+ // need to update the version.
+ //
+ if (s.update_empty () && !(poly && s.optimistic ()))
+ os << "0";
+ else
+ os << "&odb::section_update_impl< " << type << ", id_" << db <<
+ ", " << traits << "::" << n << "_traits >";
+ }
+ else
+ os << "0," << endl
+ << "0";
+
+ os << "}";
+ }
+
+ os << "};";
+
+ os << "static const "<< traits << "::" << (abst ? "abstract_" : "") <<
+ "info_type::section_list" << endl
+ << "section_list_for_" << fn << " ="
+ << "{"
+ << n << "UL," << endl
+ << "section_functions_for_" << fn
+ << "};";
+ }
+
+ //
+ //
+ os << "const " << traits << "::" << (abst ? "abstract_" : "") <<
+ "info_type" << endl
+ << traits << "::info (" << endl
+ << "typeid (" << type << ")," << endl;
+
+ if (poly_derived)
+ os << "&object_traits_impl< " << class_fq_name (*poly_base) <<
+ ", id_" << db << " >::info," << endl;
+ else
+ os << "0," << endl;
+
+ // Sections.
+ //
+ if (sections)
+ os << "&section_list_for_" << flat_name (type);
+ else
+ os << "0";
+
+ if (!abst)
+ {
+ os << "," << endl
+ << strlit (string (type, 2, string::npos)) << "," << endl
+ << "&odb::create_impl< " << type << " >," << endl
+ << "&odb::dispatch_impl< " << type << ", id_" << db << " >," << endl;
+
+ if (poly_derived)
+ os << "&statements_type::delayed_loader";
+ else
+ os << "0";
+ }
+
+ os << ");"
+ << endl;
+
+ if (!abst)
+ os << "static const " << traits << "::entry_type" << endl
+ << "polymorphic_entry_for_" << flat_name (type) << ";"
+ << endl;
+ }
+
+ //
+ // Statements.
+ //
+ bool update_columns (
+ cc.total != cc.id + cc.inverse + cc.readonly + cc.separate_update);
+
+ qname table (table_name (c));
+ string qtable (quote_id (table));
+
+ // persist_statement
+ //
+ {
+ string sep (versioned ? "\n" : " ");
+
+ statement_columns sc;
+ {
+ statement_kind sk (statement_insert); // Imperfect forwarding.
+ instance<object_columns> ct (sk, sc);
+ ct->traverse (c);
+ process_statement_columns (sc, statement_insert, versioned);
+ }
+
+ bool dv (sc.empty ()); // The DEFAULT VALUES syntax.
+
+ os << "const char " << traits << "::persist_statement[] =" << endl
+ << strlit ("INSERT INTO " + qtable + sep) << endl;
+
+ for (statement_columns::const_iterator b (sc.begin ()), i (b),
+ e (sc.end ()); i != e;)
+ {
+ string s;
+
+ if (i == b)
+ s += '(';
+ s += i->column;
+ s += (++i != e ? ',' : ')');
+ s += sep;
+
+ os << strlit (s) << endl;
+ }
+
+ instance<query_parameters> qp (statement_insert, table);
+
+ string extra (persist_statement_extra (c, *qp, persist_after_columns));
+
+ if (!extra.empty ())
+ os << strlit (extra + sep) << endl;
+
+ string values;
+ if (!dv)
+ {
+ os << strlit ("VALUES" + sep) << endl;
+
+ values += '(';
+ instance<persist_statement_params> pt (values, *qp, sep);
+ pt->traverse (c);
+ values += ')';
+ }
+ else
+ values = "DEFAULT VALUES";
+
+ extra = persist_statement_extra (c, *qp, persist_after_values);
+
+ if (!extra.empty ())
+ values += sep;
+
+ os << strlit (values);
+
+ if (!extra.empty ())
+ os << endl
+ << strlit (extra);
+
+ os << ";"
+ << endl;
+ }
+
+ // Index of the first empty SELECT statement in poly-derived
+ // statement list. All subsequent statements are also empty.
+ // The first statement can never be empty (contains id and
+ // type_id).
+ //
+ size_t empty_depth (0);
+
+ if (id != 0)
+ {
+ string sep (versioned ? "\n" : " ");
+
+ instance<object_columns_list> id_cols;
+ id_cols->traverse (*id);
+
+ std::vector<size_t> find_column_counts (abst ? 1 : poly_depth);
+
+ // find_statement
+ //
+ if (poly_derived)
+ os << "const char* const " << traits << "::find_statements[] ="
+ << "{";
+ else
+ os << "const char " << traits << "::find_statement[] =" << endl;
+
+ for (size_t d (poly_depth); d != 0;)
+ {
+ statement_columns sc;
+ {
+ statement_kind sk (statement_select); // Imperfect forwarding.
+ object_section* s (&main_section); // Imperfect forwarding.
+ instance<object_columns> t (qtable, sk, sc, d, s);
+ t->traverse (c);
+ process_statement_columns (sc, statement_select, versioned);
+ find_column_counts[poly_depth - d] = sc.size ();
+ }
+
+ if (sc.size () != 0)
+ {
+ os << strlit ("SELECT" + sep) << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "") + sep) << endl;
+ }
+
+ os << strlit ("FROM " + qtable + sep) << endl;
+
+ if (d != 1)
+ {
+ bool f (false); // @@ (im)perfect forwarding
+ size_t d1 (d - 1); //@@ (im)perfect forward.
+ instance<polymorphic_object_joins> j (c, f, d1);
+ j->traverse (polymorphic_base (c));
+
+ for (strings::const_iterator i (j->begin ()); i != j->end (); ++i)
+ os << strlit (*i + sep) << endl;
+ }
+
+ {
+ // For the find statement, don't join section members.
+ //
+ bool f (false); // @@ (im)perfect forwarding
+ object_section* s (&main_section); // @@ (im)perfect forwarding
+ instance<object_joins> j (c, f, d, s);
+ j->traverse (c);
+
+ for (strings::const_iterator i (j->begin ()); i != j->end (); ++i)
+ os << strlit (*i + sep) << endl;
+ }
+
+ string where ("WHERE ");
+ instance<query_parameters> qp (statement_select, table);
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += qtable + "." + quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ os << strlit (where);
+ }
+ else
+ os << strlit (""); // Empty SELECT statement.
+
+ if (abst)
+ break;
+
+ if (--d != 0)
+ os << "," << endl
+ << endl;
+ }
+
+ if (poly_derived)
+ os << "};";
+ else
+ os << ";"
+ << endl;
+
+ // find_column_counts
+ //
+ if (poly_derived)
+ {
+ os << "const std::size_t " << traits << "::find_column_counts[] ="
+ << "{";
+
+ for (std::vector<size_t>::iterator b (find_column_counts.begin ()),
+ i (b), e (find_column_counts.end ()); i != e;)
+ {
+ os << *i << "UL";
+
+ if (*i == 0 && empty_depth == 0)
+ empty_depth = i - b;
+
+ if (++i != e)
+ os << ',' << endl;
+ }
+
+ os << "};";
+ }
+
+ // find_discriminator_statement
+ //
+ if (poly && !poly_derived)
+ {
+ statement_columns sc;
+ {
+ statement_kind sk (statement_select); // Imperfect forwarding.
+ instance<object_columns> t (qtable, sk, sc);
+ t->traverse (*discriminator);
+
+ if (opt != 0)
+ t->traverse (*opt);
+
+ process_statement_columns (sc, statement_select, false);
+ }
+
+ os << "const char " << traits << "::" << endl
+ << "find_discriminator_statement[] =" << endl
+ << strlit ("SELECT ") << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? ", " : " ")) << endl;
+ }
+
+ os << strlit ("FROM " + qtable + " ") << endl;
+
+ string where ("WHERE ");
+ instance<query_parameters> qp (statement_select, table);
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += qtable + "." + quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ os << strlit (where) << ";"
+ << endl;
+ }
+
+ // update_statement
+ //
+ if (update_columns)
+ {
+ string sep (versioned ? "\n" : " ");
+
+ instance<query_parameters> qp (statement_update, table);
+
+ statement_columns sc;
+ {
+ query_parameters* p (qp.get ()); // Imperfect forwarding.
+ statement_kind sk (statement_update); // Imperfect forwarding.
+ object_section* s (&main_section); // Imperfect forwarding.
+ instance<object_columns> t (sk, sc, p, s);
+ t->traverse (c);
+ process_statement_columns (sc, statement_update, versioned);
+ }
+
+ os << "const char " << traits << "::update_statement[] =" << endl
+ << strlit ("UPDATE " + qtable + sep) << endl
+ << strlit ("SET" + sep) << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "") + sep) << endl;
+ }
+
+ // This didn't work out: cannot change the identity column.
+ //
+ //if (sc.empty ())
+ //{
+ // // We can end up with nothing to set if we need to "touch" a row
+ // // in order to increment its optimistic concurrency version. In
+ // // this case just do a dummy assignment based on the id column.
+ // //
+ // string const& c (quote_id (id_cols->begin ()->name));
+ // os << strlit (c + "=" + c) << endl;
+ //}
+
+ string extra (update_statement_extra (c));
+
+ if (!extra.empty ())
+ os << strlit (extra + sep) << endl;
+
+ string where ("WHERE ");
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ // Top-level version column.
+ //
+ if (opt != 0 && !poly_derived)
+ {
+ string name (column_qname (*opt, column_prefix ()));
+ string type (column_type (*opt));
+
+ where += " AND " + name + "=" +
+ convert_to (qp->next (*opt, name, type), type, *opt);
+ }
+
+ os << strlit (where) << ";"
+ << endl;
+ }
+
+ // erase_statement
+ //
+ {
+ instance<query_parameters> qp (statement_delete, table);
+ os << "const char " << traits << "::erase_statement[] =" << endl
+ << strlit ("DELETE FROM " + qtable + " ") << endl;
+
+ string where ("WHERE ");
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ os << strlit (where) << ";"
+ << endl;
+ }
+
+ if (opt != 0 && !poly_derived)
+ {
+ instance<query_parameters> qp (statement_delete, table);
+
+ os << "const char " << traits << "::optimistic_erase_statement[] " <<
+ "=" << endl
+ << strlit ("DELETE FROM " + qtable + " ") << endl;
+
+ string where ("WHERE ");
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ // Top-level version column.
+ //
+ string name (column_qname (*opt, column_prefix ()));
+ string type (column_type (*opt));
+
+ where += " AND " + name + "=" +
+ convert_to (qp->next (*opt, name, type), type, *opt);
+
+ os << strlit (where) << ";"
+ << endl;
+ }
+ }
+
+ // query_statement
+ //
+ bool query_optimize (false);
+ if (options.generate_query ())
+ {
+ // query_statement
+ //
+ strings joins;
+ if (poly_depth != 1)
+ {
+ bool t (true); //@@ (im)perfect forwarding
+ size_t d (poly_depth - 1); //@@ (im)perfect forward.
+ instance<polymorphic_object_joins> j (c, t, d);
+ j->traverse (polymorphic_base (c));
+ joins = j->joins;
+ }
+
+ if (id != 0)
+ {
+ // For the query statement we also join section members so that they
+ // can be used in the WHERE clause.
+ //
+ bool t (true); //@@ (im)perfect forwarding
+ instance<object_joins> j (c, t, poly_depth);
+ j->traverse (c);
+ joins.insert (joins.end (), j->begin (), j->end ());
+ }
+
+ query_optimize = !joins.empty ();
+
+ statement_columns sc;
+ {
+ statement_kind sk (statement_select); //@@ Imperfect forwarding.
+ object_section* s (&main_section); //@@ Imperfect forwarding.
+ instance<object_columns> oc (qtable, sk, sc, poly_depth, s);
+ oc->traverse (c);
+ process_statement_columns (
+ sc, statement_select, versioned || query_optimize);
+ }
+
+ string sep (versioned || query_optimize ? "\n" : " ");
+
+ os << "const char " << traits << "::query_statement[] =" << endl
+ << strlit ("SELECT" + sep) << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "") + sep) << endl;
+ }
+
+ string prev ("FROM " + qtable);
+
+ for (strings::const_iterator i (joins.begin ()); i != joins.end (); ++i)
+ {
+ os << strlit (prev + sep) << endl;
+ prev = *i;
+ }
+
+ os << strlit (prev) << ";"
+ << endl;
+
+ // erase_query_statement
+ //
+ os << "const char " << traits << "::erase_query_statement[] =" << endl
+ << strlit ("DELETE FROM " + qtable) << ";"
+ << endl;
+
+ // table_name
+ //
+ os << "const char " << traits << "::table_name[] =" << endl
+ << strlit (qtable) << ";" // Use quoted name.
+ << endl;
+ }
+
+ // persist ()
+ //
+ size_t persist_containers (has_a (c, test_straight_container));
+ bool persist_versioned_containers (
+ persist_containers >
+ has_a (c,
+ test_straight_container |
+ exclude_deleted | exclude_added | exclude_versioned));
+
+ os << "void " << traits << "::" << endl
+ << "persist (database& db, " << (auto_id ? "" : "const ") <<
+ "object_type& obj";
+
+ if (poly)
+ os << ", bool top, bool dyn";
+
+ os << ")"
+ << "{";
+
+ if (poly)
+ os << "ODB_POTENTIALLY_UNUSED (top);"
+ << endl;
+
+ os << "using namespace " << db << ";"
+ << endl;
+
+ if (poly)
+ os << "if (dyn)" << endl
+ << "{"
+ << "const std::type_info& t (typeid (obj));"
+ << endl
+ << "if (t != info.type)"
+ << "{"
+ << "const info_type& pi (root_traits::map->find (t));"
+ << "pi.dispatch (info_type::call_persist, db, &obj, 0);"
+ << "return;"
+ << "}"
+ << "}";
+
+ // If we are database-poly-abstract but not C++-abstract, then make
+ // sure we are not trying to persist an instance of an abstract class.
+ //
+ if (abst && !c.abstract ())
+ os << "if (top)" << endl
+ << "throw abstract_class ();"
+ << endl;
+
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (versioned ||
+ persist_versioned_containers ||
+ uss.count (user_sections::count_new |
+ user_sections::count_all |
+ user_sections::count_versioned_only) != 0)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ // Call callback (pre_persist).
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)" << endl;
+
+ os << "callback (db," << endl
+ << (auto_id ? "static_cast<const object_type&> (obj)," : "obj,") << endl
+ << "callback_event::pre_persist);"
+ << endl;
+ }
+
+ // Call our base if we are a derived type in a polymorphic
+ // hierarchy.
+ //
+ if (poly_derived)
+ os << "base_traits::persist (db, obj, false, false);"
+ << endl;
+
+ os << "image_type& im (sts.image ());"
+ << "binding& imb (sts.insert_image_binding ());";
+
+ if (poly_derived)
+ os << "const binding& idb (sts.id_image_binding ());";
+
+ os << endl;
+
+ if (generate_grow)
+ os << "if (";
+
+ os << "init (im, obj, statement_insert" << (versioned ? ", svm" : "") << ")";
+
+ if (generate_grow)
+ os << ")" << endl
+ << "im.version++";
+
+ os << ";"
+ << endl;
+
+ if (!poly_derived && auto_id && insert_send_auto_id)
+ {
+ string const& n (idf->name ());
+ string var ("im." + n + (n[n.size () - 1] == '_' ? "" : "_"));
+ init_auto_id (*idf, var); // idf == idb, since auto
+ }
+
+ os << "if (";
+
+ if (poly_derived)
+ os << "idb.version != sts.insert_id_binding_version () ||" << endl;
+
+ os << "im.version != sts.insert_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, ";
+
+ if (poly_derived)
+ os << "idb.bind, idb.count, ";
+
+ os << "im, statement_insert" << (versioned ? ", svm" : "") << ");";
+
+ if (poly_derived)
+ os << "sts.insert_id_binding_version (idb.version);";
+
+ os << "sts.insert_image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+
+ // Bind id image since that's where the returned id and/or version will
+ // be extracted.
+ //
+ if (!poly_derived)
+ {
+ bool bv (opt != 0 && optimistic_insert_bind_version (*opt));
+ if (bv || auto_id)
+ {
+ os << "{"
+ << "id_image_type& i (sts.id_image ());"
+ << "binding& b (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || b.version == 0)"
+ << "{"
+ << "bind (b.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "b.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}"
+ << "}";
+ }
+ }
+
+ os << "insert_statement& st (sts.persist_statement ());"
+ << "if (!st.execute ())" << endl
+ << "throw object_already_persistent ();"
+ << endl;
+
+ if (!poly_derived && auto_id) // idf == idb since auto
+ {
+ set_member (*idf, "obj", "id (sts.id_image ())", "db", "id_type");
+ os << endl;
+ }
+
+ // Set the optimistic concurrency version in the object member.
+ //
+ if (opt != 0 && !poly_derived)
+ {
+ // If we don't have auto id, then obj is a const reference.
+ //
+ set_member (*opt,
+ (auto_id ? "obj" : "const_cast<object_type&> (obj)"),
+ optimistic_version_init (*opt),
+ "", // No database.
+ "version_type");
+ os << endl;
+ }
+
+ // Initialize id_image and binding if we are a root of a polymorphic
+ // hierarchy or if we have non-inverse containers.
+ //
+ if (!poly_derived && (poly || persist_containers))
+ {
+ // If this is a polymorphic root without containers, then we only
+ // need to do this if we are not a top-level call. If we are poly-
+ // abstract, then top will always be false.
+ //
+ if (poly && !persist_containers && !abst)
+ os << "if (!top)"
+ << "{";
+
+ os << "id_image_type& i (sts.id_image ());"
+ << "init (i, id (obj));"
+ << endl
+ << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}";
+
+ if (poly && !persist_containers && !abst)
+ os << "}";
+ }
+
+ if (persist_containers)
+ {
+ os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());"
+ << endl;
+
+ instance<container_calls> t (container_calls::persist_call);
+ t->traverse (c);
+ }
+
+ // Reset sections: loaded, unchanged.
+ //
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // Skip special sections.
+ //
+ if (i->special == user_section::special_version)
+ continue;
+
+ // Skip overridden sections; they have been reset by the base.
+ //
+ if (i->base != 0 && poly_derived)
+ continue;
+
+ data_member& m (*i->member);
+
+ // If the section is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (m));
+ unsigned long long dv (deleted (m));
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")" << endl;
+ }
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << ma.translate ("obj") << ".reset (true, false);"
+ << endl;
+ }
+
+ // Call callback (post_persist).
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)" << endl;
+
+ os << "callback (db," << endl
+ << (auto_id ? "static_cast<const object_type&> (obj)," : "obj,") << endl
+ << "callback_event::post_persist);";
+ }
+
+ os << "}";
+
+ // persist() bulk
+ //
+ if (c.count ("bulk-persist"))
+ {
+ os << "void " << traits << "::" << endl
+ << "persist (database& db," << endl
+ << (auto_id ? "" : "const ") << "object_type** objs," << endl
+ << "std::size_t n," << endl
+ << "multiple_exceptions& mex)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl;
+
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (versioned ||
+ uss.count (user_sections::count_new |
+ user_sections::count_all |
+ user_sections::count_versioned_only) != 0)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ os << "for (std::size_t i (0); i != n; ++i)"
+ << "{"
+ << "const object_type& obj (*objs[i]);"
+ << "callback (db, obj, callback_event::pre_persist);"
+ //@@ assumption: generate_grow is false or it only affects select (like
+ // in pgsql) so all we have to do is to increment image
+ // version if it grew.
+ //@@ assumption: insert_send_auto_id is false
+ << "image_type& im (sts.image (i));";
+
+ if (generate_grow)
+ os << "if (";
+
+ os << "init (im, obj, statement_insert" << (versioned ? ", svm" : "") << ")";
+
+ if (generate_grow)
+ os << " && i == 0)" << endl
+ << "im.version++";
+
+ os << ";"
+ << "}";
+
+ //@@ assumption: generate_grow: as above
+ os << "binding& imb (sts.insert_image_binding ());"
+ << "if (imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, sts.image (), statement_insert" <<
+ (versioned ? ", svm" : "") << ");"
+ << "imb.version++;"
+ << "}";
+
+ // Bind id image since that's where the returned id and/or version will
+ // be extracted.
+ //
+ bool bv (opt != 0 && optimistic_insert_bind_version (*opt));
+ if (bv || auto_id)
+ {
+ os << "binding& idb (sts.id_image_binding ());"
+ //@@ assumption: generate_grow: as above
+ << "if (idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, sts.id_image ());"
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}";
+ }
+
+ os << "insert_statement& st (sts.persist_statement ());"
+ << "n = st.execute (n, mex);" // Actual number of rows attempted.
+ << endl;
+
+ os << "for (std::size_t i (0); i != n; ++i)"
+ << "{"
+ << "bool r (st.result (i));" // Sets current in mex.
+ << endl
+ << "if (mex[i] != 0)" << endl // Pending exception for this position.
+ << "continue;"
+ << endl
+ << "if (!r)"
+ << "{"
+ << "mex.insert (i, object_already_persistent ());"
+ << "continue;"
+ << "}"
+ << "if (mex.fatal ())" << endl // Don't do any extra work.
+ << "continue;"
+ << endl
+ << (auto_id ? "" : "const ") << "object_type& obj (*objs[i]);"
+ << endl;
+
+ // Extract auto id.
+ //
+ if (auto_id) // idb == idf, since auto
+ {
+ set_member (*idf, "obj", "id (sts.id_image (i))", "db", "id_type");
+ os << endl;
+ }
+
+ // Set the optimistic concurrency version.
+ //
+ if (opt != 0)
+ {
+ // If we don't have auto id, then obj is a const reference.
+ //
+ set_member (*opt,
+ (auto_id ? "obj" : "const_cast<object_type&> (obj)"),
+ optimistic_version_init (*opt, true),
+ "", // No database.
+ "version_type");
+ os << endl;
+ }
+
+ // Reset sections: loaded, unchanged.
+ //
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // Skip special sections.
+ //
+ if (i->special == user_section::special_version)
+ continue;
+
+ data_member& m (*i->member);
+
+ // If the section is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (m));
+ unsigned long long dv (deleted (m));
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")" << endl;
+ }
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << ma.translate ("obj") << ".reset (true, false);"
+ << endl;
+ }
+
+ os << "callback (db," << endl
+ << (auto_id ? "static_cast<const object_type&> (obj)," : "obj,") << endl
+ << "callback_event::post_persist);"
+ << "}" // for
+ << "}"; // persist ()
+ }
+
+ // update ()
+ //
+ if (id != 0 && (!readonly || poly))
+ {
+ size_t update_containers (
+ has_a (c, test_readwrite_container, &main_section));
+
+ bool update_versioned_containers (
+ update_containers >
+ has_a (c,
+ test_readwrite_container |
+ exclude_deleted | exclude_added | exclude_versioned,
+ &main_section));
+
+ // See if we have any sections that we might have to update.
+ //
+ bool sections (false);
+ bool versioned_sections (false);
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // This test will automatically skip the special version update section.
+ //
+ if (i->update_empty () || i->update == user_section::update_manual)
+ continue;
+
+ sections = true;
+
+ if (added (*i->member) || deleted (*i->member))
+ versioned_sections = true;
+
+ // For change-updated sections, also check if we have any
+ // versioned smart containers.
+ //
+ if (i->update != user_section::update_change)
+ continue;
+
+ if (has_a (c, test_smart_container, &*i) !=
+ has_a (c, test_smart_container | exclude_deleted | exclude_added, &*i))
+ versioned_sections = true;
+ }
+
+ os << "void " << traits << "::" << endl
+ << "update (database& db, const object_type& obj";
+
+ if (poly)
+ os << ", bool top, bool dyn";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);";
+
+ if (poly)
+ os << "ODB_POTENTIALLY_UNUSED (top);";
+
+ os << endl
+ << "using namespace " << db << ";"
+ << "using " << db << "::update_statement;"
+ << endl;
+
+ if (poly)
+ os << "if (dyn)" << endl
+ << "{"
+ << "const std::type_info& t (typeid (obj));"
+ << endl
+ << "if (t != info.type)"
+ << "{"
+ << "const info_type& pi (root_traits::map->find (t));"
+ << "pi.dispatch (info_type::call_update, db, &obj, 0);"
+ << "return;"
+ << "}"
+ << "}";
+
+ // If we are database-poly-abstract but not C++-abstract, then make
+ // sure we are not trying to update an instance of an abstract class.
+ //
+ if (abst && !c.abstract ())
+ os << "if (top)" << endl
+ << "throw abstract_class ();"
+ << endl;
+
+ // If we are readonly, then there is nothing else to do.
+ //
+ if (!readonly)
+ {
+ // Call callback (pre_update).
+ //
+ if (!abst) // If we are poly-abstract then top will always be false.
+ {
+ if (poly)
+ os << "if (top)" << endl;
+
+ os << "callback (db, obj, callback_event::pre_update);"
+ << endl;
+ }
+
+ bool sts (false);
+
+ if ((versioned && update_columns) ||
+ update_versioned_containers ||
+ versioned_sections)
+ {
+ sts = true;
+ os << db << "::transaction& tr (" << db <<
+ "::transaction::current ());"
+ << db << "::connection& conn (tr.connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl;
+
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));"
+ << endl;
+ }
+
+ // If we have change-updated sections that contain change-tracking
+ // containers, then mark such sections as changed if any of the
+ // containers were changed. Do this before calling the base so that
+ // we account for the whole hierarchy before we actually start
+ // updating any sections (important for optimistic concurrency
+ // version).
+ //
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ user_section& s (*i);
+
+ if (s.update != user_section::update_change)
+ continue;
+
+ if (!has_a (c, test_smart_container, &s))
+ continue;
+
+ data_member& m (*s.member);
+
+ os << "// " << m.name () << endl
+ << "//" << endl;
+
+ // If the section is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (m));
+ unsigned long long dv (deleted (m));
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")";
+ }
+
+ os << "{";
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ // VC++ cannot grok the constructor syntax.
+ //
+ os << "const odb::section& s = " << ma.translate ("obj") << ";"
+ << endl;
+
+ os << "if (";
+
+ // Unless we are always loaded, test for being loaded.
+ //
+ if (s.load != user_section::load_eager)
+ os << "s.loaded () && ";
+
+ // And for not already being changed.
+ //
+ os << "!s.changed ())"
+ << "{";
+
+ instance<container_calls> t (container_calls::section_call, &s);
+ t->traverse (c);
+
+ os << "}" // if
+ << "}";
+ }
+
+ if (poly_derived)
+ {
+ bool readonly_base (context::readonly (*poly_base));
+
+ if (!sts && (readonly_base ||
+ update_columns ||
+ update_containers ||
+ sections))
+ {
+ os << db << "::transaction& tr (" << db <<
+ "::transaction::current ());"
+ << db << "::connection& conn (tr.connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl;
+ }
+
+ // Unless our base is readonly, call it first.
+ //
+ if (!readonly_base)
+ {
+ os << "base_traits::update (db, obj, false, false);"
+ << endl;
+ }
+ else
+ {
+ // Otherwise, we have to initialize the id image ourselves. If
+ // we don't have any columns, containers or sections to update,
+ // then we only have to do it if this is not a top-level call.
+ // If we are abstract, then top is always false.
+ //
+ if (!update_columns && !update_containers && !sections && !abst)
+ os << "if (!top)";
+
+ os << "{"
+ << "id_image_type& i (sts.id_image ());"
+ << "init (i, id (obj));"
+ << endl;
+
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;"
+ // Optimistic poly base cannot be readonly.
+ << "}"
+ << "}";
+ }
+
+ if (update_columns)
+ {
+ // Initialize the object image.
+ //
+ os << "image_type& im (sts.image ());";
+
+ if (generate_grow)
+ os << "if (";
+
+ os << "init (im, obj, statement_update" <<
+ (versioned ? ", svm" : "") << ")";
+
+ if (generate_grow)
+ os << ")" << endl
+ << "im.version++";
+
+ os << ";"
+ << endl;
+
+ os << "const binding& idb (sts.id_image_binding ());"
+ << "binding& imb (sts.update_image_binding ());"
+ << "if (idb.version != sts.update_id_binding_version () ||" << endl
+ << "im.version != sts.update_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, idb.bind, idb.count, im, statement_update" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.update_id_binding_version (idb.version);"
+ << "sts.update_image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+
+ os << "update_statement& st (sts.update_statement ());"
+ << "if (";
+
+ if (versioned)
+ os << "!st.empty () && ";
+
+ os << "st.execute () == 0)" << endl
+ << "throw object_not_persistent ();"
+ << endl;
+ }
+
+ // Otherwise, nothing else to do here if we don't have any columns
+ // to update.
+ }
+ else if (update_columns)
+ {
+ if (!sts)
+ os << db << "::transaction& tr (" << db <<
+ "::transaction::current ());"
+ << db << "::connection& conn (tr.connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl;
+
+ // Initialize id image.
+ //
+ if (opt != 0)
+ os << "const version_type& v (version (obj));";
+
+ os << "id_image_type& idi (sts.id_image ());"
+ << "init (idi, id (obj)" << (opt != 0 ? ", &v" : "") << ");"
+ << endl;
+
+ // Initialize object image.
+ //
+ os << "image_type& im (sts.image ());";
+
+ if (generate_grow)
+ os << "if (";
+
+ os << "init (im, obj, statement_update" <<
+ (versioned ? ", svm" : "") << ")";
+
+ if (generate_grow)
+ os << ")" << endl
+ << "im.version++";
+
+ os << ";"
+ << endl;
+
+ // The update binding is bound to two images (object and id)
+ // so we have to track both versions.
+ //
+ os << "bool u (false);" // Avoid incrementing version twice.
+ << "binding& imb (sts.update_image_binding ());"
+ << "if (im.version != sts.update_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im, statement_update" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.update_image_version (im.version);"
+ << "imb.version++;"
+ << "u = true;"
+ << "}";
+
+ // To update the id part of the update binding we have to do
+ // it indirectly via the id binding, which just points to the
+ // suffix of the update bind array (see object_statements).
+ //
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (idi.version != sts.update_id_image_version () ||" << endl
+ << "idb.version == 0)"
+ << "{"
+ // If the id binding is up-to-date, then that means update
+ // binding is too and we just need to update the versions.
+ //
+ << "if (idi.version != sts.id_image_version () ||" << endl
+ << "idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, idi);"
+ // Update the id binding versions since we may use them later
+ // to update containers.
+ //
+ << "sts.id_image_version (idi.version);"
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}"
+ << "sts.update_id_image_version (idi.version);"
+ << endl
+ << "if (!u)" << endl
+ << "imb.version++;"
+ << "}";
+
+ os << "update_statement& st (sts.update_statement ());"
+ << "if (";
+
+ if (versioned)
+ os << "!st.empty () && ";
+
+ os << "st.execute () == 0)" << endl;
+
+ if (opt == 0)
+ os << "throw object_not_persistent ();";
+ else
+ os << "throw object_changed ();";
+
+ os << endl;
+ }
+ else if (poly || update_containers || sections)
+ {
+ // We don't have any columns to update but we may still need
+ // to initialize the id image if we are polymorphic or have
+ // any containers or sections.
+ //
+ if (!sts)
+ os << db << "::transaction& tr (" << db <<
+ "::transaction::current ());"
+ << db << "::connection& conn (tr.connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl;
+
+ // The same rationale as a couple of screens up.
+ //
+ if (poly && !update_containers && !sections && !abst)
+ os << "if (!top)";
+
+ os << "{"
+ << "id_image_type& i (sts.id_image ());"
+ << "init (i, id (obj));"
+ << endl;
+
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;"
+ // Cannot be optimistic.
+ << "}"
+ << "}";
+ }
+
+ if (update_containers || sections)
+ os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());"
+ << endl;
+
+ if (update_containers)
+ {
+ instance<container_calls> t (container_calls::update_call,
+ &main_section);
+ t->traverse (c);
+ }
+
+ // Update the optimistic concurrency version in the object member.
+ // Do it before updating sections since they will update it as well.
+ // The same code as in section_traits.
+ //
+ if (opt != 0 && !poly_derived)
+ {
+ // Object is passed as const reference so we need to cast away
+ // constness.
+ //
+ const char* obj ("const_cast<object_type&> (obj)");
+ string inc (optimistic_version_increment (*opt));
+
+ if (inc == "1")
+ inc_member (*opt, obj, "obj", "version_type");
+ else
+ set_member (*opt, obj, inc, "", "version_type");
+
+ os << endl;
+ }
+
+ // Update sections that are loaded and need updating.
+ //
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ if (i->update_empty () || i->update == user_section::update_manual)
+ continue;
+
+ // Here is the deal: for polymorphic section overrides, we could
+ // have used the same approach as in load_() where the class that
+ // defines the section data member initiates the section update.
+ // However, if we used this approach, then we would get what can
+ // be called "interleaved" update statements. That is, a section
+ // update would update the section data in "derived" tables before
+ // updating the main section in these derived tables. While this
+ // does not feel right, it can also cause more real problems if,
+ // for example, there are constraints or some such. Generally,
+ // we always want to update the main section data first for each
+ // table in the hierarchy.
+ //
+ // So what we are going to do instead is call section traits
+ // update at each override point (and indicate to it not to
+ // call base). One tricky aspect is who is going to reset the
+ // changed state. We have to do it at the top since otherwise
+ // overrides won't know the section has changed. Finding out
+ // whether we are the final override (in section terms, not
+ // object terms) is tricky.
+ //
+ data_member& m (*i->member);
+
+ // If the section is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (m));
+ unsigned long long dv (deleted (m));
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")"
+ << "{";
+ }
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << "if (";
+
+ // Unless we are always loaded, test for being loaded.
+ //
+ string sep;
+ if (i->load != user_section::load_eager)
+ {
+ os << ma.translate ("obj") << ".loaded ()";
+ sep = " && ";
+ }
+
+ // If change-updated section, check that it has changed.
+ //
+ if (i->update == user_section::update_change)
+ os << sep << ma.translate ("obj") << ".changed ()";
+
+ os << ")"
+ << "{";
+
+ // Re-initialize the id+ver image with new version which may
+ // have changed. Here we take a bit of a shortcut and not
+ // re-bind the image since we know only version may have
+ // changed and that is always an integer (cannot grow).
+ //
+ if (opt != 0)
+ {
+ os << "const version_type& v (version (obj));"
+ << "init (sts.id_image (), id (obj), &v);"
+ << endl;
+ }
+
+ os << public_name (m) << "_traits::update (esc, obj";
+
+ if (poly_derived && i->base != 0)
+ {
+ // Normally we don't want to call base (base object's update()
+ // would have called it; see comment above). There is one
+ // exception, however, and that is a readonly base section
+ // in optimistic class. In this case, base object's update()
+ // won't call it (it is readonly) but it needs to be called
+ // in order to increment the version.
+ //
+ bool base (false);
+ for (user_section* b (i->base); !base && b != 0; b = b->base)
+ {
+ if (b->total != b->inverse + b->readonly ||
+ b->readwrite_containers)
+ break; // We have a readwrite base.
+ else if (b->optimistic ())
+ base = true; // We have a readonly optimistic root.
+ }
+
+ os << ", " << base;
+ }
+
+ os << ");";
+
+ // Clear the change flag and arm the transaction rollback callback.
+ //
+ if (i->update == user_section::update_change)
+ {
+ // If polymorphic, do it only if we are the final override.
+ //
+ if (poly)
+ os << "if (root_traits::map->find (typeid (obj))." <<
+ "final_section_update (info, " << i->index << "UL))" << endl;
+
+ os << ma.translate ("obj") << ".reset (true, false, &tr);";
+ }
+
+ os << "}";
+
+ if (av != 0 || dv != 0)
+ os << "}";
+ }
+
+ // Call callback (post_update).
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)"
+ << "{";
+
+ os << "callback (db, obj, callback_event::post_update);"
+ << "pointer_cache_traits::update (db, obj);";
+
+ if (poly)
+ os << "}";
+ }
+ } // readonly
+
+ os << "}";
+ }
+
+ // update () bulk
+ //
+ if (id != 0 && !readonly && c.count ("bulk-update"))
+ {
+ os << "void " << traits << "::" << endl
+ << "update (database& db," << endl
+ << "const object_type** objs," << endl
+ << "std::size_t n," << endl
+ << "multiple_exceptions& mex)"
+ << "{"
+ << "using namespace " << db << ";"
+ << "using " << db << "::update_statement;"
+ << endl
+ << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl
+ << "for (std::size_t i (0); i != n; ++i)"
+ << "{"
+ << "const object_type& obj (*objs[i]);"
+ << "callback (db, obj, callback_event::pre_update);";
+
+ if (opt != 0)
+ os << "const version_type& v (version (obj));";
+
+ os << "init (sts.id_image (i), id (obj)" << (opt != 0 ? ", &v" : "") << ");";
+
+ //@@ assumption: generate_grow is false or it only affects select (like
+ // in pgsql) so all we have to do is to increment image
+ // version if it grew.
+
+ os << "image_type& im (sts.image (i));";
+
+ if (generate_grow)
+ os << "if (";
+
+ os << "init (im, obj, statement_update" << (versioned ? ", svm" : "") << ")";
+
+ if (generate_grow)
+ os << " && i == 0)" << endl
+ << "im.version++";
+
+ os << ";"
+ << "}";
+
+ // Update bindings.
+ //
+ os << "binding& idb (sts.id_image_binding ());"
+ << "binding& imb (sts.update_image_binding ());"
+ << endl;
+
+ //@@ assumption: generate_grow: as above
+ //
+ os << "bool u (false);" // Avoid incrementing version twice.
+ << "if (imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, sts.image (), statement_update" <<
+ (versioned ? ", svm" : "") << ");"
+ << "imb.version++;"
+ << "u = true;"
+ << "}";
+
+ //@@ assumption: generate_grow: as above
+ //
+ os << "if (idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, sts.id_image ());"
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << endl
+ << "if (!u)" << endl
+ << "imb.version++;"
+ << "}";
+
+ // We don't need the versioned check here since the statement cannot
+ // be empty; otherwise it would have been an empty object which is
+ // illegal.
+ //
+ os << "update_statement& st (sts.update_statement ());"
+ << "n = st.execute (n, mex);"; // Actual number of rows attempted.
+
+ os << endl
+ << "for (std::size_t i (0); i != n; ++i)"
+ << "{"
+ << "unsigned long long r (st.result (i));" // Also sets current in mex.
+ << endl
+ << "if (mex[i] != 0)" << endl // Pending exception from result().
+ << "continue;"
+ << endl
+ << "if (r != 1)"
+ << "{"
+ << "mex.insert (i," << endl
+ //@@ assumption: result_unknown
+ << "(r == update_statement::result_unknown)," << endl
+ << (opt == 0 ? "object_not_persistent" : "object_changed") << " ());"
+ << "continue;"
+ << "}"
+ << "if (mex.fatal ())" << endl // Don't do any extra work.
+ << "continue;"
+ << endl
+ << "const object_type& obj (*objs[i]);";
+
+ // Update the optimistic concurrency version in the object member.
+ //
+ if (opt != 0)
+ {
+ // Object is passed as const reference so we need to cast away
+ // constness.
+ //
+ const char* obj ("const_cast<object_type&> (obj)");
+ string inc (optimistic_version_increment (*opt));
+
+ if (inc == "1")
+ inc_member (*opt, obj, "obj", "version_type");
+ else
+ set_member (*opt, obj, inc, "", "version_type");
+
+ os << endl;
+ }
+
+ os << "callback (db, obj, callback_event::post_update);"
+ << "pointer_cache_traits::update (db, obj);"
+ << "}" // for
+ << "}"; // update()
+ }
+
+ // erase (id)
+ //
+ size_t erase_containers (has_a (c, test_straight_container));
+ bool erase_versioned_containers (
+ erase_containers >
+ has_a (c, test_straight_container | exclude_deleted | exclude_added));
+
+ if (id != 0)
+ {
+ os << "void " << traits << "::" << endl
+ << "erase (database& db, const id_type& id";
+
+ if (poly)
+ os << ", bool top, bool dyn";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";";
+
+ if (poly)
+ os << endl
+ << "ODB_POTENTIALLY_UNUSED (top);";
+
+ os << endl
+ << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl;
+
+ // Get the discriminator and determine the dynamic type of
+ // this object.
+ //
+ if (poly)
+ {
+ os << "if (dyn)" << endl
+ << "{"
+ << "discriminator_type d;"
+ << "root_traits::discriminator_ (sts.root_statements (), id, &d);";
+
+ if (!abst)
+ os << endl
+ << "if (d != info.discriminator)"
+ << "{";
+
+ os << "const info_type& pi (root_traits::map->find (d));"
+ << endl
+ // Check that the dynamic type is derived from the static type.
+ //
+ << "if (!pi.derived (info))" << endl
+ << "throw object_not_persistent ();"
+ << endl
+ << "pi.dispatch (info_type::call_erase, db, 0, &id);"
+ << "return;";
+
+ if (!abst)
+ os << "}";
+
+ os << "}";
+ }
+
+ // Initialize id image.
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)"
+ << "{";
+
+ os << "id_image_type& i (sts.id_image ());"
+ << "init (i, id);"
+ << endl;
+
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}";
+
+ if (poly)
+ os << "}";
+ }
+
+ // Erase containers first so that there are no reference
+ // violations (we don't want to rely on ON DELETE CASCADE
+ // here since in case of a custom schema, it might not be
+ // there).
+ //
+ if (erase_containers)
+ {
+ os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());";
+
+ if (erase_versioned_containers)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ instance<container_calls> t (container_calls::erase_id_call);
+ t->traverse (c);
+ }
+
+ os << "if (sts.erase_statement ().execute () != 1)" << endl
+ << "throw object_not_persistent ();"
+ << endl;
+
+ if (poly_derived)
+ {
+ // Call our base last (we erase polymorphic objects from base
+ // to derived in order not to trigger cascading deletes).
+ //
+ os << "base_traits::erase (db, id, false, false);"
+ << endl;
+ }
+
+ // Remove from the object cache.
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)" << endl;
+
+ os << "pointer_cache_traits::erase (db, id);";
+ }
+
+ os << "}";
+ }
+
+ // erase (id) bulk
+ //
+ if (id != 0 && c.count ("bulk-erase"))
+ {
+ os << "std::size_t " << traits << "::" << endl
+ << "erase (database& db," << endl
+ << "const id_type** ids," << endl
+ << "std::size_t n," << endl
+ << "multiple_exceptions& mex)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl
+ << "for (std::size_t i (0); i != n; ++i)" << endl
+ << "init (sts.id_image (i), *ids[i]);"
+ << endl
+ << "binding& idb (sts.id_image_binding ());"
+ //@@ assumption: generate_grow is false or it only affects select (like
+ // in pgsql).
+ << "if (idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, sts.id_image ());"
+ << "idb.version++;"
+ << (opt != 0 ? "sts.optimistic_id_image_binding ().version++;" : "")
+ << "}"
+ << "delete_statement& st (sts.erase_statement ());"
+ << "n = st.execute (n, mex);" // Set to actual number of rows attempted.
+ << endl
+ << "for (std::size_t i (0); i != n; ++i)"
+ << "{"
+ << "unsigned long long r (st.result (i));" // Sets current in mex.
+ << endl
+ << "if (mex[i] != 0)" << endl // Pending exception from result().
+ << "continue;"
+ << endl
+ << "if (r != 1)"
+ << "{"
+ << "mex.insert (i," << endl
+ //@@ assumption: result_unknown
+ << "(r == delete_statement::result_unknown)," << endl
+ << "object_not_persistent ());"
+ << "continue;"
+ << "}"
+ << "if (mex.fatal ())" << endl // Don't do any extra work.
+ << "continue;"
+ << "pointer_cache_traits::erase (db, *ids[i]);"
+ << "}" // for
+ << "return n;"
+ << "}"; // erase()
+ }
+
+ // erase (object)
+ //
+ bool erase_smart_containers (has_a (c, test_smart_container));
+
+ if (id != 0 && (poly || opt != 0 || erase_smart_containers))
+ {
+ os << "void " << traits << "::" << endl
+ << "erase (database& db, const object_type& obj";
+
+ if (poly)
+ os << ", bool top, bool dyn";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);";
+
+ if (poly)
+ os << "ODB_POTENTIALLY_UNUSED (top);";
+
+ os << endl;
+
+ if (poly)
+ os << "if (dyn)" << endl
+ << "{"
+ << "const std::type_info& t (typeid (obj));"
+ << endl
+ << "if (t != info.type)"
+ << "{"
+ << "const info_type& pi (root_traits::map->find (t));"
+ << "pi.dispatch (info_type::call_erase, db, &obj, 0);"
+ << "return;"
+ << "}"
+ << "}";
+
+ // If we are database-poly-abstract but not C++-abstract, then make
+ // sure we are not trying to erase an instance of an abstract class.
+ //
+ if (abst && !c.abstract ())
+ os << "if (top)" << endl
+ << "throw abstract_class ();"
+ << endl;
+
+ if (opt != 0 || erase_smart_containers)
+ {
+ string rsts (poly_derived ? "rsts" : "sts");
+
+ os << "using namespace " << db << ";"
+ << endl
+ << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (poly_derived)
+ os << endl
+ << "root_statements_type& rsts (sts.root_statements ());"
+ << "ODB_POTENTIALLY_UNUSED (rsts);";
+
+ os << endl;
+
+ // Call callback (pre_erase).
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)" << endl;
+
+ os << "callback (db, obj, callback_event::pre_erase);"
+ << endl;
+ }
+
+ if (!abst || erase_containers)
+ {
+ os << "const id_type& id (object_traits_impl::id (obj));"
+ << endl;
+ }
+
+ // Smart containers case.
+ //
+ if (opt == 0)
+ {
+ // Initialize id image.
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)"
+ << "{";
+
+ os << "id_image_type& i (" << rsts << ".id_image ());"
+ << "init (i, id);"
+ << endl;
+
+ os << "binding& idb (" << rsts << ".id_image_binding ());"
+ << "if (i.version != " << rsts << ".id_image_version () || " <<
+ "idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << rsts << ".id_image_version (i.version);"
+ << "idb.version++;"
+ // Cannot be optimistic.
+ << "}";
+
+ if (poly)
+ os << "}";
+ }
+
+ // Erase containers first so that there are no reference
+ // violations (we don't want to rely on ON DELETE CASCADE
+ // here since in case of a custom schema, it might not be
+ // there).
+ //
+
+ if (erase_containers)
+ {
+ os << "extra_statement_cache_type& esc (" <<
+ "sts.extra_statement_cache ());";
+
+ if (erase_versioned_containers)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ instance<container_calls> t (container_calls::erase_obj_call);
+ t->traverse (c);
+ }
+
+ os << "if (sts.erase_statement ().execute () != 1)" << endl
+ << "throw object_not_persistent ();"
+ << endl;
+ }
+ // Optimistic case.
+ //
+ else
+ {
+ // Initialize id + managed column image.
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)"
+ << "{";
+
+ os << "const version_type& v (version (obj));"
+ << "id_image_type& i (" << rsts << ".id_image ());"
+ << "init (i, id, &v);"
+ << endl;
+
+ os << "binding& idb (" << rsts << ".id_image_binding ());"
+ << "if (i.version != " << rsts << ".id_image_version () ||" << endl
+ << "idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << rsts << ".id_image_version (i.version);"
+ << "idb.version++;"
+ << rsts << ".optimistic_id_image_binding ().version++;"
+ << "}";
+
+ if (poly)
+ os << "}"; // if (top)
+ }
+
+ // If this is a derived type in a polymorphic hierarchy, then we
+ // need to check the version (stored in root) before we go ahead
+ // and start deleting things. Also use the same code for root with
+ // containers since it is more efficient than the find_() method
+ // below.
+ //
+ bool svm (false);
+ if (poly_derived || (poly && erase_containers))
+ {
+ // Only do the check in the top-level call.
+ //
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ os << "if (top)"
+ << "{"
+ << "version_type v;"
+ << "root_traits::discriminator_ (" << rsts << ", id, 0, &v);"
+ << endl;
+
+ os << "if (v != version (obj))" << endl
+ << "throw object_changed ();"
+ << "}";
+ }
+ }
+ else if (erase_containers)
+ {
+ // Things get complicated here: we don't want to trash the
+ // containers and then find out that the versions don't match
+ // and we therefore cannot delete the object. After all, there
+ // is no guarantee that the user will abort the transaction.
+ // In fact, a perfectly reasonable scenario is to reload the
+ // object, re-check the condition, decide not to delete the
+ // object, and then commit the transaction.
+ //
+ // There doesn't seem to be anything better than first making
+ // sure we can delete the object, then deleting the container
+ // data, and then deleting the object. To check that we can
+ // delete the object we are going to use find_() and then
+ // compare the versions. A special-purpose SELECT query would
+ // have been more efficient but it would complicated and bloat
+ // things significantly.
+ //
+
+ if (versioned)
+ {
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));"
+ << endl;
+ svm = true;
+ }
+
+ os << "if (!find_ (sts, &id" <<
+ (versioned ? ", svm" : "") << "))" << endl
+ << "throw object_changed ();"
+ << endl;
+
+ if (delay_freeing_statement_result)
+ os << "sts.find_statement ().free_result ();"
+ << endl;
+
+ os << "if (version (sts.image ()) != version (obj))" << endl
+ << "throw object_changed ();"
+ << endl;
+ }
+
+ // Erase containers first so that there are no reference
+ // violations (we don't want to rely on ON DELETE CASCADE
+ // here since in case of a custom schema, it might not be
+ // there).
+ //
+ if (erase_containers)
+ {
+ os << "extra_statement_cache_type& esc (" <<
+ "sts.extra_statement_cache ());";
+
+ if (erase_versioned_containers && !svm)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ instance<container_calls> t (container_calls::erase_obj_call);
+ t->traverse (c);
+ }
+
+ const char* st (
+ poly_derived ? "erase_statement" : "optimistic_erase_statement");
+
+ os << "if (sts." << st << " ().execute () != 1)" << endl
+ << "throw object_changed ();"
+ << endl;
+ }
+
+ if (poly_derived)
+ {
+ // Call our base last (we erase polymorphic objects from base
+ // to derived in order not to trigger cascading deletes).
+ //
+ os << "base_traits::erase (db, obj, false, false);"
+ << endl;
+ }
+
+ if (!abst) // If we are poly-abstract, then top will always be false.
+ {
+ if (poly)
+ os << "if (top)"
+ << "{";
+
+ // Note that we don't reset sections since the object is now
+ // transient and the state of a section in a transient object
+ // is undefined.
+
+ // Remove from the object cache.
+ //
+ os << "pointer_cache_traits::erase (db, id);";
+
+ // Call callback (post_erase).
+ //
+ os << "callback (db, obj, callback_event::post_erase);";
+
+ if (poly)
+ os << "}";
+ }
+ }
+ else
+ {
+ os << "callback (db, obj, callback_event::pre_erase);"
+ << "erase (db, id (obj), true, false);"
+ << "callback (db, obj, callback_event::post_erase);";
+ }
+
+ os << "}";
+ }
+
+ // erase (object) bulk
+ //
+ if (id != 0 && c.count ("bulk-erase"))
+ {
+ os << "void " << traits << "::" << endl
+ << "erase (database& db," << endl
+ << "const object_type** objs," << endl
+ << "std::size_t n," << endl
+ << "multiple_exceptions& mex)"
+ << "{";
+
+ // In non-optimistic case delegate to erase(id).
+ //
+ if (opt == 0)
+ {
+ os << "id_type a[batch];"
+ << "const id_type* p[batch];"
+ << endl
+ << "for (std::size_t i (0); i != n; ++i)"
+ << "{"
+ << "const object_type& obj (*objs[i]);"
+ << "callback (db, obj, callback_event::pre_erase);"
+ << "a[i] = id (obj);"
+ << "p[i] = a + i;"
+ << "}"
+ << "n = erase (db, p, n, mex);"
+ << endl
+ << "if (mex.fatal ())" << endl // Don't do any extra work.
+ << "return;"
+ << endl
+ << "for (std::size_t i (0); i != n; ++i)"
+ << "{"
+ << "if (mex[i] == 0)" << endl // No pending exception.
+ << "callback (db, *objs[i], callback_event::post_erase);"
+ << "}"; // for
+ }
+ else
+ {
+ os << "using namespace " << db << ";"
+ << endl
+ << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl
+ << "for (std::size_t i (0); i != n; ++i)"
+ << "{"
+ << "const object_type& obj (*objs[i]);"
+ << "callback (db, obj, callback_event::pre_erase);"
+ << "const version_type& v (version (obj));"
+ << "init (sts.id_image (i), id (obj), &v);"
+ << "}";
+
+ os << "binding& idb (sts.id_image_binding ());"
+ //@@ assumption: generate_grow is false or it only affects select
+ // (like in pgsql).
+ << "if (idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, sts.id_image ());"
+ << "idb.version++;"
+ << "sts.optimistic_id_image_binding ().version++;"
+ << "}"
+ << "delete_statement& st (sts.optimistic_erase_statement ());"
+ << "n = st.execute (n, mex);" // Set to actual number of attempted.
+ << endl
+ << "for (std::size_t i (0); i != n; ++i)"
+ << "{"
+ << "unsigned long long r (st.result (i));" // Sets current in mex.
+ << endl
+ << "if (mex[i] != 0)" << endl // Pending exception from result().
+ << "continue;"
+ << endl
+ << "if (r != 1)"
+ << "{"
+ << "mex.insert (i," << endl
+ //@@ assumption: result_unknown
+ << "(r == delete_statement::result_unknown)," << endl
+ << "object_changed ());"
+ << "continue;"
+ << "}"
+ << "if (mex.fatal ())" << endl // Don't do any extra work.
+ << "continue;"
+ << endl
+ << "const object_type& obj (*objs[i]);"
+ << "pointer_cache_traits::erase (db, id (obj));"
+ << "callback (db, obj, callback_event::post_erase);"
+ << "}"; // for
+ }
+
+ os << "}"; // erase()
+ }
+
+ // find (id)
+ //
+ if (id != 0 && c.default_ctor ())
+ {
+ string rsts (poly_derived ? "rsts" : "sts");
+
+ os << traits << "::pointer_type" << endl
+ << traits << "::" << endl
+ << "find (database& db, const id_type& id)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl;
+
+ // First check the session.
+ //
+ os << "{";
+
+ if (poly_derived)
+ os << "root_traits::pointer_type rp (pointer_cache_traits::find (" <<
+ "db, id));"
+ << endl
+ << "if (!root_traits::pointer_traits::null_ptr (rp))" << endl
+ << "return" << endl
+ << " root_traits::pointer_traits::dynamic_pointer_cast<" <<
+ "object_type> (rp);";
+ else
+ os << "pointer_type p (pointer_cache_traits::find (db, id));"
+ << endl
+ << "if (!pointer_traits::null_ptr (p))" << endl
+ << "return p;";
+
+ os << "}";
+
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ if (poly_derived)
+ os << "root_statements_type& rsts (sts.root_statements ());";
+
+ os << endl
+ << "statements_type::auto_lock l (" << rsts << ");";
+
+ if (delay_freeing_statement_result)
+ os << "auto_result ar;";
+
+ if (poly)
+ os << "root_traits::discriminator_type d;";
+
+ os << endl
+ << "if (l.locked ())"
+ << "{"
+ << "if (!find_ (sts, &id" << (versioned ? ", svm" : "") << "))" << endl
+ << "return pointer_type ();";
+
+ if (delay_freeing_statement_result)
+ os << endl
+ << "ar.set (sts.find_statement (" << (poly_derived ? "depth" : "") <<
+ "));";
+
+ if (poly)
+ os << "d = root_traits::discriminator (" << rsts << ".image ());";
+
+ os << "}";
+
+ if (poly)
+ {
+ // If statements are locked, then get the discriminator by
+ // executing a special SELECT statement. We need it to be
+ // able to create an object of the correct dynamic type
+ // which will be loaded later.
+ //
+ os << "else" << endl
+ << "root_traits::discriminator_ (" << rsts << ", id, &d);"
+ << endl;
+
+ if (abst)
+ os << "const info_type& pi (root_traits::map->find (d));"
+ << endl;
+ else
+ os << "const info_type& pi (" << endl
+ << "d == info.discriminator ? info : root_traits::map->find (d));"
+ << endl;
+ }
+
+ // Create the object.
+ //
+ if (poly_derived)
+ {
+ os << "root_traits::pointer_type rp (pi.create ());"
+ << "pointer_type p (" << endl
+ << "root_traits::pointer_traits::static_pointer_cast<object_type> " <<
+ "(rp));"
+ << "pointer_traits::guard pg (p);"
+ << endl;
+
+ // Insert it as a root pointer (for non-unique pointers, rp should
+ // still be valid and for unique pointers this is a no-op).
+ //
+ os << "pointer_cache_traits::insert_guard ig (" << endl
+ << "pointer_cache_traits::insert (db, id, rp));"
+ << endl;
+ }
+ else
+ {
+ if (poly)
+ os << "pointer_type p (pi.create ());";
+ else
+ os << "pointer_type p (" << endl
+ << "access::object_factory<object_type, pointer_type>::create ());";
+
+ os << "pointer_traits::guard pg (p);"
+ << endl;
+
+ os << "pointer_cache_traits::insert_guard ig (" << endl
+ << "pointer_cache_traits::insert (db, id, p));"
+ << endl;
+ }
+
+ os << "object_type& obj (pointer_traits::get_ref (p));"
+ << endl
+ << "if (l.locked ())"
+ << "{"
+ << "select_statement& st (sts.find_statement (" <<
+ (poly_derived ? "depth" : "") << "));"
+ << "ODB_POTENTIALLY_UNUSED (st);"
+ << endl;
+
+ if (poly)
+ os << "callback_event ce (callback_event::pre_load);"
+ << "pi.dispatch (info_type::call_callback, db, &obj, &ce);";
+ else
+ os << "callback (db, obj, callback_event::pre_load);";
+
+ os << "init (obj, sts.image (), &db" << (versioned ? ", svm" : "") << ");";
+
+ init_value_extra ();
+
+ if (delay_freeing_statement_result)
+ os << "ar.free ();";
+
+ os << "load_ (sts, obj, false" << (versioned ? ", svm" : "") << ");";
+
+ if (poly)
+ // Load the dynamic part of the object unless static and dynamic
+ // types are the same.
+ //
+ os << endl
+ << "if (&pi != &info)"
+ << "{"
+ << "std::size_t d (depth);"
+ << "pi.dispatch (info_type::call_load, db, &obj, &d);"
+ << "}";
+
+ os << rsts << ".load_delayed (" << (versioned ? "&svm" : "0") << ");"
+ << "l.unlock ();";
+
+ if (poly)
+ os << "ce = callback_event::post_load;"
+ << "pi.dispatch (info_type::call_callback, db, &obj, &ce);";
+ else
+ os << "callback (db, obj, callback_event::post_load);";
+
+ os << "pointer_cache_traits::load (ig.position ());"
+ << "}"
+ << "else" << endl
+ << rsts << ".delay_load (id, obj, ig.position ()" <<
+ (poly ? ", pi.delayed_loader" : "") << ");"
+ << endl;
+
+ os << "ig.release ();"
+ << "pg.release ();"
+ << "return p;"
+ << "}";
+ }
+
+ // find (id, obj)
+ //
+ if (id != 0)
+ {
+ string rsts (poly_derived ? "rsts" : "sts");
+
+ os << "bool " << traits << "::" << endl
+ << "find (database& db, const id_type& id, object_type& obj";
+
+ if (poly)
+ os << ", bool dyn";
+
+ os << ")"
+ << "{";
+
+ if (poly)
+ os << "ODB_POTENTIALLY_UNUSED (dyn);"
+ << endl;
+
+ os << "using namespace " << db << ";"
+ << endl;
+
+ if (poly)
+ {
+ if (!abst)
+ os << "if (dyn)" << endl
+ << "{";
+
+ os << "const std::type_info& t (typeid (obj));";
+
+ if (!abst)
+ os << endl
+ << "if (t != info.type)"
+ << "{";
+
+ os << "const info_type& pi (root_traits::map->find (t));"
+ << "return pi.dispatch (info_type::call_find, db, &obj, &id);";
+
+ if (!abst)
+ os << "}"
+ << "}";
+ }
+
+ if (!abst)
+ {
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ if (poly_derived)
+ os << "root_statements_type& rsts (sts.root_statements ());";
+
+ // This can only be top-level call so auto_lock must succeed.
+ //
+ os << endl
+ << "statements_type::auto_lock l (" << rsts << ");"
+ << "assert (l.locked ()) /* Must be a top-level call. */;"
+ << endl;
+
+ os << "if (!find_ (sts, &id" <<
+ (versioned ? ", svm" : "") << "))" << endl
+ << "return false;"
+ << endl;
+
+ os << "select_statement& st (sts.find_statement (" <<
+ (poly_derived ? "depth" : "") << "));"
+ << "ODB_POTENTIALLY_UNUSED (st);"
+ << endl;
+
+ if (delay_freeing_statement_result)
+ os << "auto_result ar (st);";
+
+ os << "reference_cache_traits::position_type pos (" << endl
+ << "reference_cache_traits::insert (db, id, obj));"
+ << "reference_cache_traits::insert_guard ig (pos);"
+ << endl
+ << "callback (db, obj, callback_event::pre_load);"
+ << "init (obj, sts.image (), &db" <<
+ (versioned ? ", svm" : "") << ");";
+
+ init_value_extra ();
+
+ if (delay_freeing_statement_result)
+ os << "ar.free ();";
+
+ os << "load_ (sts, obj, false" << (versioned ? ", svm" : "") << ");"
+ << rsts << ".load_delayed (" << (versioned ? "&svm" : "0") << ");"
+ << "l.unlock ();"
+ << "callback (db, obj, callback_event::post_load);"
+ << "reference_cache_traits::load (pos);"
+ << "ig.release ();"
+ << "return true;";
+ }
+
+ os << "}";
+ }
+
+ // reload ()
+ //
+ if (id != 0)
+ {
+ string rsts (poly_derived ? "rsts" : "sts");
+
+ // This implementation is almost exactly the same as find(id, obj)
+ // above except that it doesn't interract with the object cache and,
+ // in case of optimistic concurrency, checks if the object actually
+ // needs to be reloaded.
+ //
+ os << "bool " << traits << "::" << endl
+ << "reload (database& db, object_type& obj";
+
+ if (poly)
+ os << ", bool dyn";
+
+ os << ")"
+ << "{";
+
+ if (poly)
+ os << "ODB_POTENTIALLY_UNUSED (dyn);"
+ << endl;
+
+ os << "using namespace " << db << ";"
+ << endl;
+
+ if (poly)
+ {
+ if (!abst)
+ os << "if (dyn)" << endl
+ << "{";
+
+ os << "const std::type_info& t (typeid (obj));";
+
+ if (!abst)
+ os << endl
+ << "if (t != info.type)"
+ << "{";
+
+ os << "const info_type& pi (root_traits::map->find (t));"
+ << "return pi.dispatch (info_type::call_reload, db, &obj, 0);";
+
+ if (!abst)
+ os << "}"
+ << "}";
+ }
+
+ if (!abst)
+ {
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ if (poly_derived)
+ os << "root_statements_type& rsts (sts.root_statements ());";
+
+ // This can only be top-level call so auto_lock must succeed.
+ //
+ os << endl
+ << "statements_type::auto_lock l (" << rsts << ");"
+ << "assert (l.locked ()) /* Must be a top-level call. */;"
+ << endl;
+
+ os << "const id_type& id (object_traits_impl::id (obj));"
+ << "if (!find_ (sts, &id" <<
+ (versioned ? ", svm" : "") << "))" << endl
+ << "return false;"
+ << endl;
+
+ os << "select_statement& st (sts.find_statement (" <<
+ (poly_derived ? "depth" : "") << "));"
+ << "ODB_POTENTIALLY_UNUSED (st);"
+ << endl;
+
+ if (delay_freeing_statement_result)
+ os << "auto_result ar (st);"
+ << endl;
+
+ if (opt != 0)
+ {
+ const char* tr (poly_derived ? "root_traits::" : "");
+
+ os << "if (" << tr << "version (" << rsts << ".image ()) == " <<
+ tr << "version (obj))" << endl
+ << "return true;"
+ << endl;
+ }
+
+ os << "callback (db, obj, callback_event::pre_load);"
+ << "init (obj, sts.image (), &db" <<
+ (versioned ? ", svm" : "") << ");";
+
+ init_value_extra ();
+
+ if (delay_freeing_statement_result)
+ os << "ar.free ();";
+
+ os << "load_ (sts, obj, true" << (versioned ? ", svm" : "") << ");"
+ << rsts << ".load_delayed (" << (versioned ? "&svm" : "0") << ");"
+ << "l.unlock ();"
+ << "callback (db, obj, callback_event::post_load);"
+ << "return true;";
+ }
+
+ os << "}";
+ }
+
+ // load (section) [non-thunk version]
+ //
+ if (uss.count (user_sections::count_new |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0)
+ {
+ string rsts (poly_derived ? "rsts" : "sts");
+
+ os << "bool " << traits << "::" << endl
+ << "load (connection& conn, object_type& obj, section& s" <<
+ (poly ? ", const info_type* pi" : "") << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl;
+
+ if (poly)
+ {
+ // Resolve type information if we are doing indirect calls.
+ //
+ os << "bool top (pi == 0);" // Top-level call.
+ << "if (top)"
+ << "{"
+ << "const std::type_info& t (typeid (obj));";
+
+ if (!abst)
+ os << endl
+ << "if (t == info.type)" << endl
+ << "pi = &info;"
+ << "else" << endl;
+
+ os << "pi = &root_traits::map->find (t);"
+ << "}";
+ }
+
+ // Lock the statements for the object itself. We need to do this since we
+ // are using the id image and loading of object pointers can overwrite it.
+ //
+ os << db << "::connection& c (static_cast<" << db <<
+ "::connection&> (conn));"
+ << "statements_type& sts (c.statement_cache ()." <<
+ "find_object<object_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ if (poly_derived)
+ os << "root_statements_type& rsts (sts.root_statements ());";
+
+ os << endl
+ << "statements_type::auto_lock l (" << rsts << ");";
+
+ // It this is a top-level call, then auto_lock must succeed.
+ //
+ if (poly)
+ os << "if (top)" << endl;
+
+ os << "assert (l.locked ()) /* Must be a top-level call. */;"
+ << endl;
+
+ os << "bool r (false);"
+ << endl;
+
+ // If our poly-base has load sections, then call the base version
+ // first. Besides checking for sections, it will also initialize
+ // the id image.
+ //
+ if (poly_derived &&
+ buss->count (user_sections::count_total |
+ user_sections::count_load |
+ user_sections::count_load_empty) != 0)
+ {
+ os << "if (base_traits::load (conn, obj, s, pi))" << endl
+ << "r = true;"
+ << endl;
+ }
+ else
+ {
+ // Initialize id image (all this is equivalent to using rsts).
+ //
+ os << "id_image_type& i (sts.id_image ());"
+ << "init (i, id (obj));"
+ << endl;
+
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}";
+ }
+
+ // Resolve extra statements if we are doing direct calls.
+ //
+ if (!poly)
+ os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());"
+ << endl;
+
+ // Dispatch.
+ //
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // Skip special sections.
+ //
+ if (i->special == user_section::special_version)
+ continue;
+
+ if (i->load == user_section::load_eager)
+ continue;
+
+ // Overridden sections are handled by the base.
+ //
+ if (poly_derived && i->base != 0)
+ continue;
+
+ data_member& m (*i->member);
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << "if (!r && &s == &" << ma.translate ("obj") << ")"
+ << "{";
+
+ if (!poly)
+ os << public_name (m) << "_traits::load (esc, obj);";
+ else
+ {
+ // If this is an empty section, then there may not be any
+ // overrides.
+ //
+ os << "info_type::section_load sl (" <<
+ "pi->find_section_load (" << i->index << "UL));";
+
+ if (i->load_empty ())
+ os << "if (sl != 0)" << endl;
+
+ os << "sl (conn, obj, true);";
+ }
+
+ os << "r = true;"
+ << "}";
+ }
+
+ if (poly)
+ os << "if (top)"
+ << "{";
+
+ os << rsts << ".load_delayed (" << (versioned ? "&svm" : "0") << ");"
+ << "l.unlock ();";
+
+ if (poly)
+ os << "}";
+
+ os << "return r;"
+ << "}";
+ }
+
+ // update (section) [non-thunk version]
+ //
+ if (uss.count (user_sections::count_new |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) != 0)
+ {
+ os << "bool " << traits << "::" << endl
+ << "update (connection& conn, const object_type& obj, " <<
+ "const section& s" << (poly ? ", const info_type* pi" : "") << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl;
+
+ if (poly)
+ {
+ // Resolve type information if we are doing indirect calls.
+ //
+ os << "if (pi == 0)" // Top-level call.
+ << "{"
+ << "const std::type_info& t (typeid (obj));";
+
+ if (!abst)
+ os << endl
+ << "if (t == info.type)" << endl
+ << "pi = &info;"
+ << "else" << endl;
+
+ os << "pi = &root_traits::map->find (t);"
+ << "}";
+ }
+
+ // If our poly-base has update sections, then call the base version
+ // first. Besides checking for sections, it will also initialize the
+ // id image.
+ //
+ if (poly_derived &&
+ buss->count (user_sections::count_total |
+ user_sections::count_update |
+ user_sections::count_update_empty) != 0)
+ {
+ os << "if (base_traits::update (conn, obj, s, pi))" << endl
+ << "return true;"
+ << endl;
+ }
+ else
+ {
+ // Resolve extra statements if we are doing direct calls.
+ //
+ os << db << "::connection& c (static_cast<" << db <<
+ "::connection&> (conn));"
+ << "statements_type& sts (c.statement_cache ()." <<
+ "find_object<object_type> ());";
+
+ if (!poly)
+ os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());";
+
+ os << endl;
+
+ // Initialize id image. This is not necessarily the root of the
+ // polymorphic hierarchy.
+ //
+ if (opt != 0)
+ os << "const version_type& v (version (obj));";
+
+ os << "id_image_type& i (sts.id_image ());";
+
+ os << "init (i, id (obj)" << (opt != 0 ? ", &v" : "") << ");"
+ << endl;
+
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}";
+ }
+
+ // Dispatch.
+ //
+ bool e (false);
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // Skip special sections.
+ //
+ if (i->special == user_section::special_version)
+ continue;
+
+ // Overridden sections are handled by the base.
+ //
+ if (poly_derived && i->base != 0)
+ continue;
+
+ // Skip read-only sections. Polymorphic sections are always
+ // (potentially) read-write.
+ //
+ if (!poly && i->update_empty ())
+ continue;
+
+ data_member& m (*i->member);
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ if (e)
+ os << "else ";
+ else
+ e = true;
+
+ os << "if (&s == &" << ma.translate ("obj") << ")";
+
+ if (!poly)
+ os << public_name (m) << "_traits::update (esc, obj);";
+ else
+ {
+ // If this is a readonly section, then there may not be any
+ // overrides.
+ //
+ os << "{"
+ << "info_type::section_update su (" <<
+ "pi->find_section_update (" << i->index << "UL));";
+
+ if (i->update_empty ())
+ {
+ // For optimistic section, also check that we are not the
+ // final override, since otherwise we will increment the
+ // version without updating anything.
+ //
+ if (i->optimistic ())
+ os << "if (su != 0 && su != info.sections->functions[" <<
+ i->index << "UL].update)" << endl;
+ else
+ os << "if (su != 0)" << endl;
+ }
+
+ os << "su (conn, obj);"
+ << "}";
+ }
+ }
+
+ os << "else" << endl
+ << "return false;"
+ << endl
+ << "return true;"
+ << "}";
+ }
+
+ // find_ ()
+ //
+ if (id != 0)
+ {
+ os << "bool " << traits << "::" << endl
+ << "find_ (statements_type& sts," << endl
+ << "const id_type* id";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ if (poly_derived && !abst)
+ os << "," << endl
+ << "std::size_t d";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl;
+
+ // Initialize id image.
+ //
+ if (poly_derived && !abst)
+ os << "if (d == depth)"
+ << "{";
+
+ os << "id_image_type& i (sts.id_image ());"
+ << "init (i, *id);"
+ << endl;
+
+ os << "binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;";
+ if (opt != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}";
+
+ if (poly_derived && !abst)
+ os << "}";
+
+ // Rebind data image.
+ //
+ os << "image_type& im (sts.image ());"
+ << "binding& imb (sts.select_image_binding (" <<
+ (poly_derived ? (abst ? "depth" : "d") : "") << "));"
+ << endl;
+
+ if (poly_derived)
+ {
+ os << "if (imb.version == 0 ||" << endl
+ << "check_version (sts.select_image_versions (), im))"
+ << "{"
+ << "bind (imb.bind, 0, 0, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "update_version (sts.select_image_versions ()," << endl
+ << "im," << endl
+ << "sts.select_image_bindings ());"
+ << "}";
+ }
+ else
+ {
+ os << "if (im.version != sts.select_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.select_image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+ }
+
+ os << "select_statement& st (sts.find_statement (" <<
+ (poly_derived ? (abst ? "depth" : "d") : "") << "));"
+ << endl;
+
+ // The dynamic loader SELECT statement can be dynamically empty.
+ //
+ if (versioned && poly_derived && !abst)
+ os << "if (st.empty ())" << endl
+ << "return true;"
+ << endl;
+
+ os << "st.execute ();"
+ << "auto_result ar (st);"
+ << "select_statement::result r (st.fetch ());"
+ << endl;
+
+ if (grow)
+ {
+ os << "if (r == select_statement::truncated)"
+ << "{"
+ << "if (grow (im, sts.select_image_truncated ()" <<
+ (versioned ? ", svm" : "") <<
+ (poly_derived ? (abst ? ", depth" : ", d") : "") << "))" << endl
+ << "im.version++;"
+ << endl;
+
+ if (poly_derived)
+ {
+ os << "if (check_version (sts.select_image_versions (), im))"
+ << "{"
+ << "bind (imb.bind, 0, 0, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "update_version (sts.select_image_versions ()," << endl
+ << "im," << endl
+ << "sts.select_image_bindings ());"
+ << "st.refetch ();"
+ << "}";
+ }
+ else
+ {
+ os << "if (im.version != sts.select_image_version ())"
+ << "{"
+ << "bind (imb.bind, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.select_image_version (im.version);"
+ << "imb.version++;"
+ << "st.refetch ();"
+ << "}";
+ }
+
+ os << "}";
+ }
+
+ // If we are delaying, only free the result if it is empty.
+ //
+ if (delay_freeing_statement_result)
+ os << "if (r != select_statement::no_data)"
+ << "{"
+ << "ar.release ();"
+ << "return true;"
+ << "}"
+ << "else" << endl
+ << "return false;";
+ else
+ os << "return r != select_statement::no_data;";
+
+ os << "}";
+ }
+
+ // load_()
+ //
+ // Load containers, reset/reload sections.
+ //
+ size_t load_containers (
+ has_a (c, test_container | include_eager_load, &main_section));
+
+ if (poly_derived ||
+ load_containers ||
+ uss.count (user_sections::count_new |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0)
+ {
+ bool load_versioned_containers (
+ load_containers >
+ has_a (c,
+ test_container | include_eager_load |
+ exclude_deleted | exclude_added | exclude_versioned,
+ &main_section));
+
+ os << "void " << traits << "::" << endl
+ << "load_ (statements_type& sts," << endl
+ << "object_type& obj," << endl
+ << "bool reload";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ if (poly_derived)
+ os << "," << endl
+ << "std::size_t d";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (reload);";
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+ os << endl;
+
+ if (poly_derived)
+ os << "if (--d != 0)" << endl
+ << "base_traits::load_ (sts.base_statements (), obj, reload" <<
+ (context::versioned (*poly_base) ? ", svm" : "") <<
+ (poly_base != poly_root ? ", d" : "") << ");"
+ << endl;
+
+ if (load_containers ||
+ (!poly && uss.count (user_sections::count_new |
+ user_sections::count_load) != 0))
+ os << "extra_statement_cache_type& esc (sts.extra_statement_cache ());"
+ << endl;
+
+ if (!versioned && (
+ load_versioned_containers ||
+ uss.count (user_sections::count_new |
+ user_sections::count_all |
+ user_sections::count_versioned_only) != 0))
+ {
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));"
+ << endl;
+ }
+
+ if (load_containers)
+ {
+ instance<container_calls> t (container_calls::load_call, &main_section);
+ t->traverse (c);
+ }
+
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ // Skip special sections.
+ //
+ if (i->special == user_section::special_version)
+ continue;
+
+ // Skip overridden sections; they are handled by the base.
+ //
+ if (i->base != 0 && poly_derived)
+ continue;
+
+ data_member& m (*i->member);
+
+ // If the section is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (m));
+ unsigned long long dv (deleted (m));
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")"
+ << "{";
+ }
+
+ // Section access is always by reference.
+ //
+ member_access& ma (m.get<member_access> ("get"));
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ if (i->load == user_section::load_eager)
+ {
+ // Mark an eager section as loaded.
+ //
+ os << ma.translate ("obj") << ".reset (true, false);"
+ << endl;
+ continue;
+ }
+
+ os << "if (reload)"
+ << "{"
+ // Reload sections that have been loaded, clear change state.
+ //
+ << "if (" << ma.translate ("obj") << ".loaded ())"
+ << "{";
+
+ if (!poly)
+ os << public_name (m) << "_traits::load (esc, obj);";
+ else
+ {
+ os << "info_type::section_load sl (" << endl
+ << "root_traits::map->find (typeid (obj)).find_section_load (" <<
+ i->index << "UL));";
+
+ if (i->load_empty ())
+ os << "if (sl != 0)" << endl;
+
+ os << "sl (sts.connection (), obj, true);";
+ }
+
+ os << ma.translate ("obj") << ".reset (true, false);"
+ << "}"
+ << "}"
+ << "else" << endl
+ // Reset to unloaded, unchanged state.
+ << ma.translate ("obj") << ".reset ();";
+
+ if (av != 0 || dv != 0)
+ os << "}";
+ else
+ os << endl;
+ }
+
+ os << "}";
+ }
+
+ // load_()
+ //
+ // Load the dynamic part of the object. We don't need it if we are
+ // poly-abstract.
+ //
+ if (poly_derived && !abst)
+ {
+ os << "void " << traits << "::" << endl
+ << "load_ (database& db, root_type& r, std::size_t d)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << "object_type& obj (static_cast<object_type&> (r));"
+ << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());"
+ << endl
+ << "d = depth - d;" // Convert to distance from derived.
+ << endl;
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));"
+ << endl;
+
+ // Avoid trying to execute an empty SELECT statement.
+ //
+ if (empty_depth != 0)
+ os << "if (d > " << (poly_depth - empty_depth) << "UL)"
+ << "{";
+
+ os << "if (!find_ (sts, 0" << (versioned ? ", svm" : "") << ", d))" << endl
+ << "throw object_not_persistent ();" // Database inconsistency.
+ << endl;
+
+ os << "select_statement& st (sts.find_statement (d));"
+ << "ODB_POTENTIALLY_UNUSED (st);"
+ << endl;
+
+ // The resulting SELECT statement may end up being dynamically empty.
+ //
+ if (versioned)
+ os << "if (!st.empty ())"
+ << "{";
+
+ if (delay_freeing_statement_result)
+ os << "auto_result ar (st);";
+
+ os << "init (obj, sts.image (), &db" << (versioned ? ", svm" : "") <<
+ ", d);";
+
+ init_value_extra ();
+
+ if (delay_freeing_statement_result)
+ os << "ar.free ();";
+
+ if (versioned)
+ os << "}";
+
+ if (empty_depth != 0)
+ os << "}";
+
+ os << "load_ (sts, obj, false, " << (versioned ? "svm, " : "") << "d);"
+ << "}";
+ }
+
+ // discriminator_ ()
+ //
+ if (poly && !poly_derived)
+ {
+ os << "void " << traits << "::" << endl
+ << "discriminator_ (statements_type& sts," << endl
+ << "const id_type& id," << endl
+ << "discriminator_type* pd";
+
+ if (opt != 0)
+ os << "," << endl
+ << "version_type* pv";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl;
+
+ // Initialize id image.
+ //
+ os << "id_image_type& idi (sts.discriminator_id_image ());"
+ << "init (idi, id);"
+ << endl;
+
+ os << "binding& idb (sts.discriminator_id_image_binding ());"
+ << "if (idi.version != sts.discriminator_id_image_version () ||" << endl
+ << "idb.version == 0)"
+ << "{"
+ << "bind (idb.bind, idi" << (opt != 0 ? ", false" : "") << ");"
+ << "sts.discriminator_id_image_version (idi.version);"
+ << "idb.version++;"
+ << "}";
+
+ // Rebind data image.
+ //
+ os << "discriminator_image_type& i (sts.discriminator_image ());"
+ << "binding& imb (sts.discriminator_image_binding ());"
+ << endl
+ << "if (i.version != sts.discriminator_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ // Generate bind code inline. For now discriminator is simple
+ // value so we don't need statement kind (sk).
+ //
+ << bind_vector << " b (imb.bind);"
+ << "std::size_t n (0);"
+ << "{";
+ bind_discriminator_member_->traverse (*discriminator);
+ os << "}";
+
+ if (opt != 0)
+ {
+ os << "n++;" // For now discriminator is a simple value.
+ << "{";
+ bind_version_member_->traverse (*opt);
+ os << "}";
+ }
+
+ os << "sts.discriminator_image_version (i.version);"
+ << "imb.version++;"
+ << "}";
+
+ os << "{"
+ << "select_statement& st (sts.find_discriminator_statement ());"
+ << "st.execute ();"
+ << "auto_result ar (st);"
+ << "select_statement::result r (st.fetch ());"
+ << endl
+ << "if (r == select_statement::no_data)"
+ << "{";
+
+ if (opt != 0)
+ os << "if (pv != 0)" << endl
+ << "throw object_changed ();"
+ << "else" << endl;
+
+ os << "throw object_not_persistent ();"
+ << "}";
+
+ if (generate_grow &&
+ (context::grow (*discriminator) ||
+ (opt != 0 && context::grow (*opt))))
+ {
+ os << "else if (r == select_statement::truncated)"
+ << "{";
+
+ // Generate grow code inline.
+ //
+ os << "bool grew (false);"
+ << truncated_vector << " t (sts.discriminator_image_truncated ());"
+ << endl;
+
+ index_ = 0;
+ grow_discriminator_member_->traverse (*discriminator);
+
+ if (opt != 0)
+ grow_version_member_->traverse (*opt);
+
+ os << "if (grew)" << endl
+ << "i.version++;"
+ << endl;
+
+ os << "if (i.version != sts.discriminator_image_version ())"
+ << "{"
+ // Generate bind code inline. The same code as above.
+ //
+ << bind_vector << " b (imb.bind);"
+ << "std::size_t n (0);"
+ << "{";
+ bind_discriminator_member_->traverse (*discriminator);
+ os << "}";
+
+ if (opt != 0)
+ {
+ os << "n++;" // For now discriminator is a simple value.
+ << "{";
+ bind_version_member_->traverse (*opt);
+ os << "}";
+ }
+
+ os << "sts.discriminator_image_version (i.version);"
+ << "imb.version++;"
+ << "st.refetch ();"
+ << "}"
+ << "}";
+ }
+
+ // Discriminator cannot be long data (no streaming).
+ //
+ os << "}";
+
+ // Initialize value inline instead of generating a separate
+ // init() function. For now discriminator is simple value so
+ // we don't need the database (db).
+ //
+ os << "if (pd != 0)"
+ << "{"
+ << "discriminator_type& d (*pd);";
+ init_named_discriminator_value_member_->traverse (*discriminator);
+ os << "}";
+
+ if (opt != 0)
+ {
+ os << "if (pv != 0)"
+ << "{"
+ << "version_type& v (*pv);";
+ init_named_version_value_member_->traverse (*opt);
+ os << "}";
+ }
+
+ os << "}";
+ }
+
+ if (options.generate_query ())
+ {
+ char const* result_type;
+ if (poly)
+ result_type = "polymorphic_object_result_impl<object_type>";
+ else if (id != 0)
+ result_type = "object_result_impl<object_type>";
+ else
+ result_type = "no_id_object_result_impl<object_type>";
+
+ string sep (versioned || query_optimize ? "\n" : " ");
+
+ // Unprepared.
+ //
+ if (!options.omit_unprepared ())
+ {
+ // query ()
+ //
+ os << "result< " << traits << "::object_type >" << endl
+ << traits << "::" << endl
+ << "query (database& db, const query_base_type& q)"
+ << "{"
+ << "using namespace " << db << ";"
+ << "using odb::details::shared;"
+ << "using odb::details::shared_ptr;"
+ << endl;
+
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << endl
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ // Rebind the image if necessary.
+ //
+ os << "image_type& im (sts.image ());"
+ << "binding& imb (sts.select_image_binding (" <<
+ (poly_derived ? "depth" : "") << "));"
+ << endl;
+
+ if (poly_derived)
+ {
+ os << "if (imb.version == 0 ||" << endl
+ << "check_version (sts.select_image_versions (), im))"
+ << "{"
+ << "bind (imb.bind, 0, 0, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "update_version (sts.select_image_versions ()," << endl
+ << "im," << endl
+ << "sts.select_image_bindings ());"
+ << "}";
+ }
+ else
+ {
+ os << "if (im.version != sts.select_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.select_image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+ }
+
+ os << "std::string text (query_statement);"
+ << "if (!q.empty ())"
+ << "{"
+ << "text += " << strlit (sep) << ";"
+ << "text += q.clause ();"
+ << "}";
+
+ os << "q.init_parameters ();"
+ << "shared_ptr<select_statement> st (" << endl
+ << "new (shared) select_statement (" << endl;
+ object_query_statement_ctor_args (
+ c, "q", versioned || query_optimize, false);
+ os << "));" << endl
+ << "st->execute ();";
+
+ post_query_ (c, true);
+
+ os << endl
+ << "shared_ptr< odb::" << result_type << " > r (" << endl
+ << "new (shared) " << db << "::" << result_type << " (" << endl
+ << "q, st, sts, " << (versioned ? "&svm" : "0") << "));"
+ << endl
+ << "return result<object_type> (r);"
+ << "}";
+
+ // query(odb::query_base)
+ //
+ if (multi_dynamic)
+ os << "result< " << traits << "::object_type >" << endl
+ << traits << "::" << endl
+ << "query (database& db, const odb::query_base& q)"
+ << "{"
+ << "return query (db, query_base_type (q));"
+ << "}";
+ }
+
+ // erase_query
+ //
+ os << "unsigned long long " << traits << "::" << endl
+ << "erase_query (database& db, const query_base_type& q)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << endl
+ << "std::string text (erase_query_statement);"
+ << "if (!q.empty ())"
+ << "{"
+ << "text += ' ';"
+ << "text += q.clause ();"
+ << "}"
+ << "q.init_parameters ();"
+ << "delete_statement st (" << endl;
+ object_erase_query_statement_ctor_args (c);
+ os << ");"
+ << endl
+ << "return st.execute ();"
+ << "}";
+
+ // erase_query(odb::query_base)
+ //
+ if (multi_dynamic)
+ os << "unsigned long long " << traits << "::" << endl
+ << "erase_query (database& db, const odb::query_base& q)"
+ << "{"
+ << "return erase_query (db, query_base_type (q));"
+ << "}";
+
+ // Prepared. Very similar to unprepared but has some annoying variations
+ // that make it difficult to factor out something common.
+ //
+ if (options.generate_prepared ())
+ {
+ // prepare_query
+ //
+ os << "odb::details::shared_ptr<prepared_query_impl>" << endl
+ << traits << "::" << endl
+ << "prepare_query (connection& c, const char* n, " <<
+ "const query_base_type& q)"
+ << "{"
+ << "using namespace " << db << ";"
+ << "using odb::details::shared;"
+ << "using odb::details::shared_ptr;"
+ << endl;
+
+ os << db << "::connection& conn (" << endl
+ << "static_cast<" << db << "::connection&> (c));"
+ << endl
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_object<object_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ // Rebind the image if necessary.
+ //
+ os << "image_type& im (sts.image ());"
+ << "binding& imb (sts.select_image_binding (" <<
+ (poly_derived ? "depth" : "") << "));"
+ << endl;
+
+ if (poly_derived)
+ {
+ os << "if (imb.version == 0 ||" << endl
+ << "check_version (sts.select_image_versions (), im))"
+ << "{"
+ << "bind (imb.bind, 0, 0, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "update_version (sts.select_image_versions ()," << endl
+ << "im," << endl
+ << "sts.select_image_bindings ());"
+ << "}";
+ }
+ else
+ {
+ os << "if (im.version != sts.select_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.select_image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+ }
+
+ os << "std::string text (query_statement);"
+ << "if (!q.empty ())"
+ << "{"
+ << "text += " << strlit (sep) << ";"
+ << "text += q.clause ();"
+ << "}";
+
+ os << "shared_ptr<" << db << "::prepared_query_impl> r (" << endl
+ << "new (shared) " << db << "::prepared_query_impl (conn));"
+ << "r->name = n;"
+ << "r->execute = &execute_query;"
+ << "r->query = q;"
+ << "r->stmt.reset (" << endl
+ << "new (shared) select_statement (" << endl;
+ object_query_statement_ctor_args (
+ c, "r->query", versioned || query_optimize, true);
+ os << "));"
+ << endl
+ << "return r;"
+ << "}";
+
+ // prepare_query(odb::query_base)
+ //
+ if (multi_dynamic)
+ os << "odb::details::shared_ptr<prepared_query_impl>" << endl
+ << traits << "::" << endl
+ << "prepare_query (connection& c, const char* n, " <<
+ "const odb::query_base& q)"
+ << "{"
+ << "return prepare_query (c, n, query_base_type (q));"
+ << "}";
+
+ // execute_query
+ //
+ os << "odb::details::shared_ptr<result_impl>" << endl
+ << traits << "::" << endl
+ << "execute_query (prepared_query_impl& q)"
+ << "{"
+ << "using namespace " << db << ";"
+ << "using odb::details::shared;"
+ << "using odb::details::shared_ptr;"
+ << endl
+ << db << "::prepared_query_impl& pq (" << endl
+ << "static_cast<" << db << "::prepared_query_impl&> (q));"
+ << "shared_ptr<select_statement> st (" << endl
+ << "odb::details::inc_ref (" << endl
+ << "static_cast<select_statement*> (pq.stmt.get ())));"
+ << endl;
+
+ os << db << "::transaction& tr (" << db << "::transaction::current ());"
+ << "ODB_POTENTIALLY_UNUSED (tr);"
+ << endl
+ << "// The connection used by the current transaction and the" << endl
+ << "// one used to prepare this statement must be the same." << endl
+ << "//" << endl
+ << "assert (q.verify_connection (tr));"
+ << endl
+ << "statements_type& sts (" << endl
+ << "st->connection ().statement_cache ().find_object<object_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ // Rebind the image if necessary.
+ //
+ os << "image_type& im (sts.image ());"
+ << "binding& imb (sts.select_image_binding (" <<
+ (poly_derived ? "depth" : "") << "));"
+ << endl;
+
+ if (poly_derived)
+ {
+ os << "if (imb.version == 0 ||" << endl
+ << "check_version (sts.select_image_versions (), im))"
+ << "{"
+ << "bind (imb.bind, 0, 0, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "update_version (sts.select_image_versions ()," << endl
+ << "im," << endl
+ << "sts.select_image_bindings ());"
+ << "}";
+ }
+ else
+ {
+ os << "if (im.version != sts.select_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im, statement_select" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.select_image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+ }
+
+ os << "pq.query.init_parameters ();"
+ << "st->execute ();";
+ post_query_ (c, false);
+
+ os << endl
+ << "return shared_ptr<result_impl> (" << endl
+ << "new (shared) " << db << "::" << result_type << " (" << endl
+ << "pq.query, st, sts, " << (versioned ? "&svm" : "0") << "));"
+ << "}";
+ }
+ }
+
+ // Generate function table registration for dynamic multi-database
+ // support.
+ //
+ if (multi_dynamic)
+ {
+ string fn (flat_name (type));
+ string dt ("access::object_traits_impl< " + type + ", id_common >");
+
+ os << "static const" << endl
+ << dt << "::" << endl
+ << "function_table_type function_table_" << fn << "_ ="
+ << "{";
+
+ // persist ()
+ //
+ os << "&" << traits << "::persist";
+
+ if (id != 0)
+ {
+ // find (id)
+ //
+ if (c.default_ctor ())
+ os << "," << endl
+ << "&" << traits << "::find";
+
+ // find (id, obj)
+ //
+ os << "," << endl
+ << "&" << traits << "::find";
+
+ // reload ()
+ //
+ os << "," << endl
+ << "&" << traits << "::reload";
+
+ // update ()
+ //
+ if (!readonly || poly)
+ os << "," << endl
+ << "&" << traits << "::update";
+
+ // erase ()
+ //
+ os << "," << endl
+ << "&" << traits << "::erase";
+
+ os << "," << endl
+ << "&" << traits << "::erase";
+
+ // Sections.
+ //
+ if (uss.count (user_sections::count_total |
+ user_sections::count_load |
+ (poly ? user_sections::count_load_empty : 0)) != 0)
+ os << "," << endl
+ << "&" << traits << "::load";
+
+ if (uss.count (user_sections::count_total |
+ user_sections::count_update |
+ (poly ? user_sections::count_update_empty : 0)) != 0)
+ os << "," << endl
+ << "&" << traits << "::update";
+ }
+
+ if (options.generate_query ())
+ {
+ if (!options.omit_unprepared ())
+ os << "," << endl
+ << "&" << traits << "::query";
+
+ os << "," << endl
+ << "&" << traits << "::erase_query";
+
+ if (options.generate_prepared ())
+ {
+ os << "," << endl
+ << "&" << traits << "::prepare_query";
+
+ os << "," << endl
+ << "&" << traits << "::execute_query";
+ }
+ }
+
+ os << "};";
+
+ os << "static const object_function_table_entry< " << type << ", " <<
+ "id_" << db << " >" << endl
+ << "function_table_entry_" << fn << "_ (" << endl
+ << "&function_table_" << fn << "_);"
+ << endl;
+ }
+}
+
+void relational::source::class_::
+traverse_view (type& c)
+{
+ view_query& vq (c.get<view_query> ("query"));
+
+ // Only process the view query if it is versioned since the query text
+ // (e.g., in the native view) must be structured. We also shouldn't try
+ // to optimize JOINs (since the objects are JOINed explicitly by the
+ // user), unless we are adding poly-base/derived JOINs.
+ //
+ bool versioned (context::versioned (c));
+ bool query_optimize (false);
+
+ string const& type (class_fq_name (c));
+ string traits ("access::view_traits_impl< " + type + ", id_" +
+ db.string () + " >");
+
+ size_t columns (column_count (c).total);
+
+ // Schema name as a string literal or empty.
+ //
+ string schema_name (options.schema_name ()[db]);
+ if (!schema_name.empty ())
+ schema_name = strlit (schema_name);
+
+ // Generate the from-list. Also collect relationships via which
+ // the objects are joined.
+ //
+ strings from;
+ view_relationship_map rel_map;
+
+ if (vq.kind == view_query::condition)
+ {
+ view_objects& objs (c.get<view_objects> ("objects"));
+
+ for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
+ {
+ bool first (i == objs.begin ());
+ string l;
+
+ //
+ // Tables.
+ //
+
+ if (i->kind == view_object::table)
+ {
+ if (first)
+ {
+ l = "FROM ";
+ l += quote_id (i->tbl_name);
+
+ if (!i->alias.empty ())
+ l += (need_alias_as ? " AS " : " ") + quote_id (i->alias);
+
+ l += from_trailer (c);
+
+ from.push_back (l);
+ continue;
+ }
+
+ l = join_syntax (*i);
+ l += ' ';
+ l += quote_id (i->tbl_name);
+
+ if (!i->alias.empty ())
+ l += (need_alias_as ? " AS " : " ") + quote_id (i->alias);
+
+ if (i->join == view_object::cross) // No ON condition for CROSS JOIN.
+ {
+ from.push_back (l);
+ continue;
+ }
+
+ semantics::scope& scope (
+ dynamic_cast<semantics::scope&> (*unit.find (i->scope)));
+
+ expression e (
+ translate_expression (
+ c, i->cond, scope, i->loc, "table"));
+
+ if (e.kind != expression::literal)
+ {
+ error (i->loc) << "invalid join condition in db pragma " <<
+ "table" << endl;
+ throw operation_failed ();
+ }
+
+ l += " ON";
+
+ // Add the pragma location for easier error tracking.
+ //
+ from.push_back (l);
+ from.push_back ("// From " + location_string (i->loc, true));
+ from.push_back (e.value);
+ continue;
+ }
+
+ //
+ // Objects.
+ //
+ semantics::class_& o (*i->obj);
+
+ bool poly (polymorphic (o));
+ size_t poly_depth (poly ? polymorphic_depth (o) : 1);
+
+ string alias (i->alias);
+
+ // For polymorphic objects, alias is just a prefix.
+ //
+ if (poly && !alias.empty ())
+ alias += "_" + table_name (o).uname ();
+
+ // First object.
+ //
+ if (first)
+ {
+ l = "FROM ";
+ l += table_qname (o);
+
+ if (!alias.empty ())
+ l += (need_alias_as ? " AS " : " ") + quote_id (alias);
+
+ l += from_trailer (c);
+
+ from.push_back (l);
+
+ if (poly_depth != 1)
+ {
+ bool t (true); //@@ (im)perfect forwarding
+ size_t d (poly_depth - 1); //@@ (im)perfect forward.
+ instance<polymorphic_object_joins> j (o, t, d, i->alias);
+ j->traverse (polymorphic_base (o));
+
+ from.insert (from.end (), j->begin (), j->end ());
+ query_optimize = query_optimize || !j->joins.empty ();
+ }
+ continue;
+ }
+
+ semantics::scope& scope (
+ dynamic_cast<semantics::scope&> (*unit.find (i->scope)));
+
+ expression e (i->join == view_object::cross
+ ? expression ("") // Dummy literal expression.
+ : translate_expression (
+ c, i->cond, scope, i->loc, "object"));
+
+ // Literal expression.
+ //
+ if (e.kind == expression::literal)
+ {
+ l = join_syntax (*i);
+ l += ' ';
+ l += table_qname (o);
+
+ if (!alias.empty ())
+ l += (need_alias_as ? " AS " : " ") + quote_id (alias);
+
+ if (i->join == view_object::cross) // No ON condition for CROSS JOIN.
+ {
+ from.push_back (l);
+ continue;
+ }
+
+ l += " ON";
+
+ // Add the pragma location for easier error tracking.
+ //
+ from.push_back (l);
+ from.push_back ("// From " + location_string (i->loc, true));
+ from.push_back (e.value);
+
+ if (poly_depth != 1)
+ {
+ bool t (true); //@@ (im)perfect forwarding
+ size_t d (poly_depth - 1); //@@ (im)perfect forward.
+ instance<polymorphic_object_joins> j (o, t, d, i->alias);
+ j->traverse (polymorphic_base (o));
+
+ from.insert (from.end (), j->begin (), j->end ());
+ query_optimize = query_optimize || !j->joins.empty ();
+ }
+ continue;
+ }
+
+ // We have an object relationship (pointer) for which we need
+ // to come up with the corresponding JOIN condition. If this
+ // is a to-many relationship, then we first need to JOIN the
+ // container table. This code is similar to object_joins.
+ //
+ // Note that this cannot be CROSS JOIN; we've handled that case
+ // above.
+ //
+ using semantics::data_member;
+
+ data_member& m (*e.member_path.back ());
+ data_member_path* imp (inverse (m));
+
+ // Resolve the pointed-to object to view_object and do
+ // some sanity checks while at it.
+ //
+ semantics::class_* c (0);
+
+ if (container (m))
+ c = object_pointer (container_vt (m));
+ else
+ c = object_pointer (utype (m));
+
+ view_object* vo (0);
+
+ // Check if the pointed-to object has been previously associated
+ // with this view and is unambiguous. A pointer to ourselves is
+ // always assumed to point to this association.
+ //
+ if (&o == c)
+ vo = &*i;
+ else
+ {
+ bool ambig (false);
+
+ for (view_objects::iterator j (objs.begin ()); j != i; ++j)
+ {
+ if (j->obj != c)
+ continue;
+
+ if (vo == 0)
+ {
+ vo = &*j;
+ continue;
+ }
+
+ // If it is the first ambiguous object, issue the
+ // error.
+ //
+ if (!ambig)
+ {
+ error (i->loc) << "pointed-to object '" << class_name (*c) <<
+ "' is ambiguous" << endl;
+ info (i->loc) << "candidates are:" << endl;
+ info (vo->loc) << " '" << vo->name () << "'" << endl;
+ ambig = true;
+ }
+
+ info (j->loc) << " '" << j->name () << "'" << endl;
+ }
+
+ if (ambig)
+ {
+ info (i->loc) << "use the other side of the relationship " <<
+ "or full join condition clause in db pragma object to " <<
+ "resolve this ambiguity" << endl;
+ throw operation_failed ();
+ }
+
+ if (vo == 0)
+ {
+ error (i->loc) << "pointed-to object '" << class_name (*c) <<
+ "' specified in the join condition has not been " <<
+ "previously associated with this view" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ // JOIN relationship points to us:
+ // vo - us
+ // e.vo - other side
+ // e.member_path - in other side
+ //
+ // JOIN relationship points to other side:
+ // vo - other side
+ // e.vo - us
+ // e.member_path - in us
+ //
+ if (imp == 0)
+ rel_map.insert (make_pair (e.member_path, make_pair (e.vo, vo)));
+ else
+ rel_map.insert (make_pair (*imp, make_pair (vo, e.vo)));
+
+ // Left and right-hand side table names.
+ //
+ qname lt;
+ {
+ using semantics::class_;
+
+ class_& o (*e.vo->obj);
+ string const& a (e.vo->alias);
+
+ if (class_* root = polymorphic (o))
+ {
+ // If the object is polymorphic, then figure out which of the
+ // bases this member comes from and use the corresponding
+ // table.
+ //
+ class_* c (
+ &static_cast<class_&> (
+ e.member_path.front ()->scope ()));
+
+ // If this member's class is not polymorphic (root uses reuse
+ // inheritance), then use the root table.
+ //
+ if (!polymorphic (*c))
+ c = root;
+
+ qname const& t (table_name (*c));
+
+ if (a.empty ())
+ lt = t;
+ else
+ lt = qname (a + "_" + t.uname ());
+ }
+ else
+ lt = a.empty () ? table_name (o) : qname (a);
+ }
+
+ qname rt;
+ {
+ qname t (table_name (*vo->obj));
+ string const& a (vo->alias);
+ rt = a.empty ()
+ ? t
+ : qname (polymorphic (*vo->obj) ? a + "_" + t.uname () : a);
+ }
+
+ // First join the container table if necessary.
+ //
+ semantics::type* cont (container (imp != 0 ? *imp->back () : m));
+
+ string ct; // Container table.
+ if (cont != 0)
+ {
+ l = join_syntax (*i);
+ l += ' ';
+
+ // The same relationship can be used by multiple associated
+ // objects. So if the object that contains this container has
+ // an alias, then also construct one for the table that we
+ // are joining.
+ //
+ {
+ using semantics::class_;
+
+ qname t;
+
+ // In a polymorphic hierarchy the member can be in a base (see
+ // above).
+ //
+ if (class_* root = polymorphic (imp != 0 ? *vo->obj : *e.vo->obj))
+ {
+ class_* c;
+ if (imp == 0)
+ {
+ c = &static_cast<class_&> (e.member_path.front ()->scope ());
+
+ if (!polymorphic (*c))
+ c = root;
+
+ t = table_name (*c, e.member_path);
+ }
+ else
+ {
+ c = &static_cast<class_&> (imp->front ()->scope ());
+
+ if (!polymorphic (*c))
+ c = root;
+
+ t = table_name (*c, *imp);
+ }
+ }
+ else
+ t = imp != 0
+ ? table_name (*vo->obj, *imp)
+ : table_name (*e.vo->obj, e.member_path);
+
+ // The tricky part is to figure out which view_object, vo
+ // or e.vo we should use. We want to use the alias from the
+ // object that actually contains this container. The following
+ // might not make intuitive sense, but it has been verified
+ // with the truth table.
+ //
+ string const& a (imp != 0 ? vo->alias : e.vo->alias);
+
+ if (a.empty ())
+ {
+ ct = quote_id (t);
+ l += ct;
+ }
+ else
+ {
+ ct = quote_id (a + '_' + t.uname ());
+ l += quote_id (t);
+ l += (need_alias_as ? " AS " : " ") + ct;
+ }
+ }
+
+ l += " ON";
+ from.push_back (l);
+
+ // If we are the pointed-to object, then we have to turn
+ // things around. This is necessary to have the proper
+ // JOIN order. There seems to be a pattern there but it
+ // is not yet intuitively clear what it means.
+ //
+ instance<object_columns_list> c_cols; // Container columns.
+ instance<object_columns_list> o_cols; // Object columns.
+
+ qname* ot; // Object table (either lt or rt).
+
+ if (imp != 0)
+ {
+ semantics::data_member& imb (*imp->back ());
+
+ if (&o == c)
+ {
+ // container.value = pointer.id
+ //
+ data_member_path& id (*id_member (*e.vo->obj));
+
+ c_cols->traverse (imb, utype (id), "value", "value");
+ o_cols->traverse (id);
+ ot = &lt;
+ }
+ else
+ {
+ // container.id = pointed-to.id
+ //
+ data_member_path& id (*id_member (*vo->obj));
+
+ c_cols->traverse (imb, utype (id), "id", "object_id", vo->obj);
+ o_cols->traverse (id);
+ ot = &rt;
+ }
+ }
+ else
+ {
+ if (&o == c)
+ {
+ // container.id = pointer.id
+ //
+ data_member_path& id (*id_member (*e.vo->obj));
+
+ c_cols->traverse (
+ m, utype (id), "id", "object_id", e.vo->obj);
+ o_cols->traverse (id);
+ ot = &lt;
+ }
+ else
+ {
+ // container.value = pointed-to.id
+ //
+ data_member_path& id (*id_member (*vo->obj));
+
+ c_cols->traverse (m, utype (id), "value", "value");
+ o_cols->traverse (id);
+ ot = &rt;
+ }
+ }
+
+ for (object_columns_list::iterator b (c_cols->begin ()), i (b),
+ j (o_cols->begin ()); i != c_cols->end (); ++i, ++j)
+ {
+ l.clear ();
+
+ if (i != b)
+ l += "AND ";
+
+ l += ct;
+ l += '.';
+ l += quote_id (i->name);
+ l += '=';
+ l += quote_id (*ot);
+ l += '.';
+ l += quote_id (j->name);
+
+ from.push_back (strlit (l));
+ }
+ }
+
+ // If we have already joined the container with the desired
+ // join type, then use LEFT JOIN to join the object to the
+ // container. This is the right thing to do since an entry
+ // in the container can only point (either via id or value)
+ // to a single object.
+ //
+ l = (cont == 0 ? join_syntax (*i) : "LEFT JOIN");
+ l += ' ';
+
+ l += table_qname (o);
+
+ if (!alias.empty ())
+ l += (need_alias_as ? " AS " : " ") + quote_id (alias);
+
+ l += " ON";
+ from.push_back (l);
+
+ if (cont != 0)
+ {
+ instance<object_columns_list> c_cols; // Container columns.
+ instance<object_columns_list> o_cols; // Object columns.
+
+ qname* ot; // Object table (either lt or rt).
+
+ if (imp != 0)
+ {
+ semantics::data_member& imb (*imp->back ());
+
+ if (&o == c)
+ {
+ // container.id = pointed-to.id
+ //
+ data_member_path& id (*id_member (*vo->obj));
+
+ c_cols->traverse (imb, utype (id), "id", "object_id", vo->obj);
+ o_cols->traverse (id);
+ ot = &rt;
+ }
+ else
+ {
+ // container.value = pointer.id
+ //
+ data_member_path& id (*id_member (*e.vo->obj));
+
+ c_cols->traverse (imb, utype (id), "value", "value");
+ o_cols->traverse (id);
+ ot = &lt;
+ }
+ }
+ else
+ {
+ if (&o == c)
+ {
+ // container.value = pointed-to.id
+ //
+ data_member_path& id (*id_member (*vo->obj));
+
+ c_cols->traverse (m, utype (id), "value", "value");
+ o_cols->traverse (id);
+ ot = &rt;
+ }
+ else
+ {
+ // container.id = pointer.id
+ //
+ data_member_path& id (*id_member (*e.vo->obj));
+
+ c_cols->traverse (m, utype (id), "id", "object_id", e.vo->obj);
+ o_cols->traverse (id);
+ ot = &lt;
+ }
+ }
+
+ for (object_columns_list::iterator b (c_cols->begin ()), i (b),
+ j (o_cols->begin ()); i != c_cols->end (); ++i, ++j)
+ {
+ l.clear ();
+
+ if (i != b)
+ l += "AND ";
+
+ l += ct;
+ l += '.';
+ l += quote_id (i->name);
+ l += '=';
+ l += quote_id (*ot);
+ l += '.';
+ l += quote_id (j->name);
+
+ from.push_back (strlit (l));
+ }
+ }
+ else
+ {
+ instance<object_columns_list> l_cols;
+ instance<object_columns_list> r_cols;
+
+ if (imp != 0)
+ {
+ // our.id = pointed-to.pointer
+ //
+ l_cols->traverse (*id_member (*e.vo->obj));
+ r_cols->traverse (*imp->back (), column_prefix (*imp));
+ }
+ else
+ {
+ // our.pointer = pointed-to.id
+ //
+ l_cols->traverse (*e.member_path.back (),
+ column_prefix (e.member_path));
+ r_cols->traverse (*id_member (*vo->obj));
+ }
+
+ for (object_columns_list::iterator b (l_cols->begin ()), i (b),
+ j (r_cols->begin ()); i != l_cols->end (); ++i, ++j)
+ {
+ l.clear ();
+
+ if (i != b)
+ l += "AND ";
+
+ l += quote_id (lt);
+ l += '.';
+ l += quote_id (i->name);
+ l += '=';
+ l += quote_id (rt);
+ l += '.';
+ l += quote_id (j->name);
+
+ from.push_back (strlit (l));
+ }
+ }
+
+ if (poly_depth != 1)
+ {
+ bool t (true); //@@ (im)perfect forwarding
+ size_t d (poly_depth - 1); //@@ (im)perfect forward.
+ instance<polymorphic_object_joins> j (o, t, d, i->alias);
+ j->traverse (polymorphic_base (o));
+
+ from.insert (from.end (), j->begin (), j->end ());
+ query_optimize = query_optimize || !j->joins.empty ();
+ }
+ } // End JOIN-generating for-loop.
+ }
+
+ // Check that pointed-to objects inside objects that we are loading
+ // have session support enabled (required to have a shared copy).
+ // Also see if we need to throw if there is no session.
+ //
+ bool need_session (false);
+ if (vq.kind == view_query::condition)
+ {
+ view_objects& objs (c.get<view_objects> ("objects"));
+ for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
+ {
+ if (i->kind != view_object::object || i->ptr == 0)
+ continue; // Not an object or not loaded.
+
+ instance<view_object_check> t (*i, rel_map);
+ t->traverse (*i->obj);
+ need_session = need_session || t->session_;
+ }
+ }
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl
+ << endl;
+
+ view_extra (c);
+
+ // query_columns
+ //
+ if (c.get<size_t> ("object-count") != 0)
+ view_query_columns_type_->traverse (c);
+
+ //
+ // Functions.
+ //
+
+ // grow ()
+ //
+ if (generate_grow && columns != 0)
+ {
+ os << "bool " << traits << "::" << endl
+ << "grow (image_type& i," << endl
+ << truncated_vector << " t";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (t);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "bool grew (false);"
+ << endl;
+
+ index_ = 0;
+ names (c, grow_member_names_);
+
+ os << "return grew;"
+ << "}";
+ }
+
+ // bind (image_type)
+ //
+ if (columns != 0)
+ {
+ os << "void " << traits << "::" << endl
+ << "bind (" << bind_vector << " b," << endl
+ << "image_type& i";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);"
+ << endl;
+
+ os << "using namespace " << db << ";"
+ << endl
+ << db << "::statement_kind sk (statement_select);"
+ << "ODB_POTENTIALLY_UNUSED (sk);"
+ << endl
+ << "std::size_t n (0);"
+ << endl;
+
+ names (c, bind_member_names_);
+
+ os << "}";
+ }
+
+ // init (view, image)
+ //
+ if (columns != 0)
+ {
+ os << "void " << traits << "::" << endl
+ << "init (view_type& o," << endl
+ << "const image_type& i," << endl
+ << "database* db";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (o);"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (db);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl;
+
+ if (need_session)
+ os << "if (!" << options.session_type () << "::_has_cache ())" << endl
+ << "throw session_required ();"
+ << endl;
+
+ // Note: db must be not NULL in order to load pointers.
+ //
+ if (has_a (c, test_pointer))
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (*db));"
+ << endl;
+
+ names (c, init_view_pointer_member_pre_names_);
+ names (c, init_value_member_names_);
+ names (c, init_view_pointer_member_post_names_);
+
+ os << "}";
+ }
+
+ // query_statement()
+ //
+ if (vq.kind != view_query::runtime)
+ {
+ os << traits << "::query_base_type" << endl
+ << traits << "::" << endl
+ << "query_statement (const query_base_type& q)"
+ << "{";
+
+ if (vq.kind == view_query::complete_select ||
+ vq.kind == view_query::complete_execute)
+ {
+ os << "query_base_type r (" << endl;
+
+ bool ph (false);
+ bool pred (vq.kind == view_query::complete_select);
+
+ if (!vq.literal.empty ())
+ {
+ // See if we have the '(?)' placeholder.
+ //
+ // @@ Ideally we would need to make sure we don't match
+ // this inside strings and quoted identifier. So the
+ // proper way to handle this would be to tokenize the
+ // statement using sql_lexer, once it is complete enough.
+ //
+ string::size_type p (vq.literal.find ("(?)"));
+
+ if (p != string::npos)
+ {
+ ph = true;
+ // For the SELECT query we keep the parenthesis in (?) and
+ // also handle the case where the query expression is empty.
+ //
+ if (pred)
+ os << strlit (string (vq.literal, 0, p + 1)) << " +" << endl
+ << "(q.empty () ? query_base_type::true_expr : q) +" << endl
+ << strlit (string (vq.literal, p + 2));
+ else
+ {
+ os << strlit (string (vq.literal, 0, p)) << " + q";
+
+ p += 3;
+ if (p != vq.literal.size ())
+ os << " + " << strlit (string (vq.literal, p));
+ }
+ }
+ else
+ os << strlit (vq.literal);
+ }
+ else
+ {
+ semantics::scope& scope (
+ dynamic_cast<semantics::scope&> (*unit.find (vq.scope)));
+
+ // Output the pragma location for easier error tracking.
+ //
+ os << "// From " << location_string (vq.loc, true) << endl
+ << translate_expression (
+ c, vq.expr, scope, vq.loc, "query", &ph, pred).value;
+ }
+
+ os << ");";
+
+ // If there was no placeholder, add the query condition
+ // at the end.
+ //
+ if (!ph)
+ os << endl
+ << "if (!q.empty ())"
+ << "{"
+ << "r += " << strlit (versioned ? "\n" : " ") << ";"
+ << "r += q.clause_prefix ();"
+ << "r += q;"
+ << "}";
+ }
+ else // vq.kind == view_query::condition
+ {
+ // Use the from-list generated above.
+ //
+ statement_columns sc;
+ {
+ instance<view_columns> t (sc, from, rel_map);
+ t->traverse (c);
+ process_statement_columns (
+ sc, statement_select, versioned || query_optimize);
+ }
+
+ string sep (versioned || query_optimize ? "\n" : " ");
+
+ os << "query_base_type r (" << endl
+ << strlit ((vq.distinct ? "SELECT DISTINCT" : "SELECT") + sep);
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << endl
+ << strlit (c + (++i != e ? "," : "") + sep);
+ }
+
+ os << ");"
+ << endl;
+
+ // It is much easier to add the separator at the beginning of the
+ // next line since the JOIN condition may not be a string literal.
+ //
+ for (strings::const_iterator i (from.begin ()); i != from.end (); ++i)
+ {
+ if (i->compare (0, 5, "FROM ") == 0)
+ os << "r += " << strlit (*i) << ";";
+ else if (i->compare (0, 3, "// ") == 0)
+ os << *i << endl;
+ else
+ {
+ // See if this is a JOIN. The exact spelling is database-dependent,
+ // but we know there is the JOIN word in there somewhere and before
+ // it we should only have keywords and spaces.
+ //
+ size_t p (i->find ("JOIN "));
+ if (p != string::npos)
+ {
+ // Make sure before it we only have A-Z and spaces.
+ //
+ for (char c; p != 0; --p)
+ {
+ c = (*i)[p - 1];
+ if ((c < 'A' || c > 'Z') && c != ' ')
+ break;
+ }
+
+ if (p == 0)
+ os << endl
+ << "r += " << strlit (sep + *i) << ";";
+ }
+
+ // Something else, assume already a string literal.
+ //
+ if (p != 0)
+ os << "r += " << *i << ";";
+ }
+ }
+
+ // Generate the query condition.
+ //
+ if (!vq.literal.empty () || !vq.expr.empty ())
+ {
+ os << endl
+ << "query_base_type c (" << endl;
+
+ bool ph (false);
+
+ if (!vq.literal.empty ())
+ {
+ // See if we have the '(?)' placeholder.
+ //
+ // @@ Ideally we would need to make sure we don't match
+ // this inside strings and quoted identifier. So the
+ // proper way to handle this would be to tokenize the
+ // statement using sql_lexer, once it is complete enough.
+ //
+ string::size_type p (vq.literal.find ("(?)"));
+
+ if (p != string::npos)
+ {
+ ph = true;
+ os << strlit (string (vq.literal, 0, p + 1))<< " +" << endl
+ << "(q.empty () ? query_base_type::true_expr : q) +" << endl
+ << strlit (string (vq.literal, p + 2));
+ }
+ else
+ os << strlit (vq.literal);
+
+ os << ");";
+ }
+ else
+ {
+ semantics::scope& scope (
+ dynamic_cast<semantics::scope&> (*unit.find (vq.scope)));
+
+ // Output the pragma location for easier error tracking.
+ //
+ os << "// From " << location_string (vq.loc, true) << endl
+ << translate_expression (
+ c, vq.expr, scope, vq.loc, "query", &ph).value;
+
+ os << ");";
+
+ // Optimize the query if it had a placeholder. This gets
+ // rid of useless clauses like WHERE TRUE.
+ //
+ if (ph)
+ os << endl
+ << "c.optimize ();";
+ }
+
+ if (!ph)
+ os << endl
+ << "c += q;";
+
+ os << endl
+ << "if (!c.empty ())"
+ << "{"
+ << "r += " << strlit (sep) << ";"
+ << "r += c.clause_prefix ();"
+ << "r += c;"
+ << "}";
+
+ string st (select_trailer (c));
+ if (!st.empty ())
+ {
+ os << "r += " << strlit (sep) << ";"
+ << "r += " << strlit (st) << ";";
+ }
+ }
+ else
+ {
+ os << endl
+ << "if (!q.empty ())"
+ << "{"
+ << "r += " << strlit (sep) << ";"
+ << "r += q.clause_prefix ();"
+ << "r += q;"
+ << "}";
+ }
+ }
+
+ os << "return r;"
+ << "}";
+ }
+
+ // Unprepared.
+ //
+ if (!options.omit_unprepared ())
+ {
+ os << "result< " << traits << "::view_type >" << endl
+ << traits << "::" << endl
+ << "query (database& db, const query_base_type& q)"
+ << "{"
+ << "using namespace " << db << ";"
+ << "using odb::details::shared;"
+ << "using odb::details::shared_ptr;"
+ << endl;
+
+ os << db << "::connection& conn (" << endl
+ << db << "::transaction::current ().connection (db));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_view<view_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl
+ << "image_type& im (sts.image ());"
+ << "binding& imb (sts.image_binding ());"
+ << endl
+ << "if (im.version != sts.image_version () || imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im" << (versioned ? ", svm" : "") << ");"
+ << "sts.image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+
+ if (vq.kind == view_query::runtime)
+ os << "const query_base_type& qs (q);";
+ else
+ os << "const query_base_type& qs (query_statement (q));";
+
+ os << "qs.init_parameters ();"
+ << "shared_ptr<select_statement> st (" << endl
+ << "new (shared) select_statement (" << endl;
+ view_query_statement_ctor_args (
+ c, "qs", versioned || query_optimize, false);
+ os << "));" << endl
+ << "st->execute ();";
+
+ post_query_ (c, true);
+
+ os << endl
+ << "shared_ptr< odb::view_result_impl<view_type> > r (" << endl
+ << "new (shared) " << db << "::view_result_impl<view_type> (" << endl
+ << "qs, st, sts, " << (versioned ? "&svm" : "0") << "));"
+ << endl
+ << "return result<view_type> (r);"
+ << "}";
+
+ // query(odb::query_base)
+ //
+ if (multi_dynamic)
+ os << "result< " << traits << "::view_type >" << endl
+ << traits << "::" << endl
+ << "query (database& db, const odb::query_base& q)"
+ << "{"
+ << "return query (db, query_base_type (q));"
+ << "}";
+ }
+
+ // Prepared. Very similar to unprepared but has some annoying variations
+ // that make it difficult to factor out something common.
+ //
+ if (options.generate_prepared ())
+ {
+ // prepare_query
+ //
+ os << "odb::details::shared_ptr<prepared_query_impl>" << endl
+ << traits << "::" << endl
+ << "prepare_query (connection& c, const char* n, " <<
+ "const query_base_type& q)"
+ << "{"
+ << "using namespace " << db << ";"
+ << "using odb::details::shared;"
+ << "using odb::details::shared_ptr;"
+ << endl;
+
+ os << db << "::connection& conn (" << endl
+ << "static_cast<" << db << "::connection&> (c));"
+ << "statements_type& sts (" << endl
+ << "conn.statement_cache ().find_view<view_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ // Rebind the image if necessary.
+ //
+ os << "image_type& im (sts.image ());"
+ << "binding& imb (sts.image_binding ());"
+ << endl
+ << "if (im.version != sts.image_version () || imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im" << (versioned ? ", svm" : "") << ");"
+ << "sts.image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+
+ os << "shared_ptr<" << db << "::prepared_query_impl> r (" << endl
+ << "new (shared) " << db << "::prepared_query_impl (conn));"
+ << "r->name = n;"
+ << "r->execute = &execute_query;";
+
+ if (vq.kind == view_query::runtime)
+ os << "r->query = q;";
+ else
+ os << "r->query = query_statement (q);";
+
+ os << "r->stmt.reset (" << endl
+ << "new (shared) select_statement (" << endl;
+ view_query_statement_ctor_args (
+ c, "r->query", versioned || query_optimize, true);
+ os << "));"
+ << endl
+ << "return r;"
+ << "}";
+
+ // prepare_query(odb::query_base)
+ //
+ if (multi_dynamic)
+ os << "odb::details::shared_ptr<prepared_query_impl>" << endl
+ << traits << "::" << endl
+ << "prepare_query (connection& c, const char* n, " <<
+ "const odb::query_base& q)"
+ << "{"
+ << "return prepare_query (c, n, query_base_type (q));"
+ << "}";
+
+ // execute_query
+ //
+ os << "odb::details::shared_ptr<result_impl>" << endl
+ << traits << "::" << endl
+ << "execute_query (prepared_query_impl& q)"
+ << "{"
+ << "using namespace " << db << ";"
+ << "using odb::details::shared;"
+ << "using odb::details::shared_ptr;"
+ << endl
+ << db << "::prepared_query_impl& pq (" << endl
+ << "static_cast<" << db << "::prepared_query_impl&> (q));"
+ << "shared_ptr<select_statement> st (" << endl
+ << "odb::details::inc_ref (" << endl
+ << "static_cast<select_statement*> (pq.stmt.get ())));"
+ << endl;
+
+ os << db << "::transaction& tr (" << db << "::transaction::current ());"
+ << "ODB_POTENTIALLY_UNUSED (tr);"
+ << endl
+ << "// The connection used by the current transaction and the" << endl
+ << "// one used to prepare this statement must be the same." << endl
+ << "//" << endl
+ << "assert (q.verify_connection (tr));"
+ << endl
+ << "statements_type& sts (" << endl
+ << "st->connection ().statement_cache ().find_view<view_type> ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration (" << schema_name << "));";
+
+ os << endl;
+
+ // Rebind the image if necessary.
+ //
+ os << "image_type& im (sts.image ());"
+ << "binding& imb (sts.image_binding ());"
+ << endl
+ << "if (im.version != sts.image_version () || imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, im" << (versioned ? ", svm" : "") << ");"
+ << "sts.image_version (im.version);"
+ << "imb.version++;"
+ << "}";
+
+ os << "pq.query.init_parameters ();"
+ << "st->execute ();";
+
+ post_query_ (c, false);
+
+ os << endl
+ << "return shared_ptr<result_impl> (" << endl
+ << "new (shared) " << db << "::view_result_impl<view_type> (" << endl
+ << "pq.query, st, sts, " << (versioned ? "&svm" : "0") << "));"
+ << "}";
+ }
+
+ // Generate function table registration for dynamic multi-database
+ // support.
+ //
+ if (multi_dynamic)
+ {
+ string fn (flat_name (type));
+ string dt ("access::view_traits_impl< " + type + ", id_common >");
+
+ os << "static const" << endl
+ << dt << "::" << endl
+ << "function_table_type function_table_" << fn << "_ ="
+ << "{";
+
+ if (!options.omit_unprepared ())
+ os << "&" << traits << "::query";
+
+ if (options.generate_prepared ())
+ {
+ if (!options.omit_unprepared ())
+ os << "," << endl;
+
+ os << "&" << traits << "::prepare_query" << "," << endl
+ << "&" << traits << "::execute_query";
+ }
+
+ os << "};";
+
+ os << "static const view_function_table_entry< " << type << ", " <<
+ "id_" << db << " >" << endl
+ << "function_table_entry_" << fn << "_ (" << endl
+ << "&function_table_" << fn << "_);"
+ << endl;
+ }
+}
+
+namespace relational
+{
+ namespace source
+ {
+ static inline void
+ add_space (string& s)
+ {
+ string::size_type n (s.size ());
+ if (n != 0 && s[n - 1] != ' ')
+ s += ' ';
+ }
+
+ static string
+ translate_name_trailer (cxx_lexer& l,
+ cpp_ttype& tt,
+ string& tl,
+ tree& tn,
+ cpp_ttype& ptt)
+ {
+ string r;
+
+ for (; tt != CPP_EOF; ptt = tt, tt = l.next (tl, &tn))
+ {
+ bool done (false);
+
+ switch (tt)
+ {
+ case CPP_SCOPE:
+ case CPP_DOT:
+ {
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ default:
+ {
+ // Handle CPP_KEYWORD here to avoid a warning (it is not
+ // part of the cpp_ttype enumeration).
+ //
+ if (tt == CPP_NAME || tt == CPP_KEYWORD)
+ {
+ // For names like 'foo::template bar'.
+ //
+ if (ptt == CPP_NAME || ptt == CPP_KEYWORD)
+ r += ' ';
+
+ r += tl;
+ }
+ else
+ done = true;
+
+ break;
+ }
+ }
+
+ if (done)
+ break;
+ }
+
+ return r;
+ }
+
+ static class_::expression
+ translate_name (cxx_lexer& l,
+ cpp_ttype& tt,
+ string& tl,
+ tree& tn,
+ cpp_ttype& ptt,
+ semantics::scope& start_scope,
+ location_t loc,
+ string const& prag,
+ bool check_ptr,
+ view_alias_map const& amap,
+ view_object_map const& omap)
+ {
+ using semantics::scope;
+ using semantics::data_member;
+ typedef class_::expression expression;
+
+ bool multi_obj ((amap.size () + omap.size ()) > 1);
+
+ bool fail (false);
+ string name;
+ string r ("query_columns");
+ context& ctx (context::current ());
+
+ // This code is quite similar to view_data_members in the type
+ // processor.
+ //
+ try
+ {
+ data_member* m (0);
+ view_object* vo (0);
+
+ // Check if this is an alias.
+ //
+ if (tt == CPP_NAME)
+ {
+ view_alias_map::const_iterator i (amap.find (tl));
+
+ if (i != amap.end ())
+ {
+ if (multi_obj)
+ {
+ r += "::";
+ r += i->first;
+ }
+
+ vo = i->second;
+ fail = true; // This must be a data member.
+
+ // Skip '::'.
+ //
+ ptt = tt;
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_SCOPE)
+ {
+ error (loc) << "member name expected after an alias in db " <<
+ "pragma " << prag << endl;
+ throw operation_failed ();
+ }
+
+ ptt = tt;
+ if (l.next (tl, &tn) != CPP_NAME)
+ throw lookup::invalid_name ();
+
+ m = &vo->obj->lookup<data_member> (tl, scope::include_hidden);
+
+ tt = l.next (tl, &tn);
+ }
+ }
+
+ // If it is not an alias, do the normal lookup.
+ //
+ if (vo == 0)
+ {
+ // Also get the object type. We need to do it so that
+ // we can get the correct (derived) object name (the
+ // member itself can come from a base class).
+ //
+ scope* s;
+ cpp_ttype ptt; // Not used.
+ m = &lookup::resolve_scoped_name<data_member> (
+ l, tt, tl, tn, ptt,
+ start_scope,
+ name,
+ false,
+ &s);
+
+ view_object_map::const_iterator i (
+ omap.find (dynamic_cast<semantics::class_*> (s)));
+
+ if (i == omap.end ())
+ {
+ // Not an object associated with this view. Assume it
+ // is some other valid name.
+ //
+ return expression (
+ name + translate_name_trailer (l, tt, tl, tn, ptt));
+ }
+
+ vo = i->second;
+
+ if (multi_obj)
+ {
+ r += "::";
+ r += context::class_name (*vo->obj);
+ }
+ }
+
+ expression e (vo);
+ r += "::";
+ r += ctx.public_name (*m);
+
+ // Assemble the member path if we may need to return a pointer
+ // expression.
+ //
+ if (check_ptr)
+ e.member_path.push_back (m);
+
+ fail = true; // Now we definitely fail if anything goes wrong.
+
+ // Finally, resolve nested members if any.
+ //
+ for (; tt == CPP_DOT; ptt = tt, tt = l.next (tl, &tn))
+ {
+ // Check if this member is actually of a composite value type.
+ // This is to handle expressions like "object::member.is_null ()"
+ // correctly. The remaining issue here is that in the future
+ // is_null()/is_not_null() will be valid for composite values
+ // as well.
+ //
+ semantics::class_* comp (
+ context::composite_wrapper (context::utype (*m)));
+ if (comp == 0)
+ break;
+
+ ptt = tt;
+ tt = l.next (tl, &tn);
+
+ if (tt != CPP_NAME)
+ {
+ error (loc) << "name expected after '.' in db pragma " <<
+ prag << endl;
+ throw operation_failed ();
+ }
+
+ m = &comp->lookup<data_member> (tl, scope::include_hidden);
+
+ r += '.';
+ r += ctx.public_name (*m);
+
+ if (check_ptr)
+ e.member_path.push_back (m);
+ }
+
+ // If requested, check if this member is a pointer. We only do this
+ // if there is nothing after this name.
+ //
+ if (check_ptr && tt == CPP_EOF)
+ {
+ using semantics::type;
+
+ type* t;
+
+ if (context::container (*m))
+ t = &context::container_vt (*m);
+ else
+ t = &context::utype (*m);
+
+ if (context::object_pointer (*t))
+ return e;
+ }
+
+ // Read the remainder of the expression (e.g., '.is_null ()') if
+ // the member is not composite and we bailed out from the above
+ // loop.
+ //
+ if (tt == CPP_DOT)
+ r += translate_name_trailer (l, tt, tl, tn, ptt);
+
+ return expression (r);
+ }
+ catch (lookup::invalid_name const&)
+ {
+ if (!fail)
+ return expression (
+ name + translate_name_trailer (l, tt, tl, tn, ptt));
+
+ error (loc) << "invalid name in db pragma " << prag << endl;
+ throw operation_failed ();
+ }
+ catch (semantics::unresolved const& e)
+ {
+ if (!fail)
+ return expression (
+ name + translate_name_trailer (l, tt, tl, tn, ptt));
+
+ if (e.type_mismatch)
+ error (loc) << "name '" << e.name << "' in db pragma " << prag <<
+ " does not refer to a data member" << endl;
+ else
+ error (loc) << "unable to resolve data member '" << e.name <<
+ "' specified with db pragma " << prag << endl;
+
+ throw operation_failed ();
+ }
+ catch (semantics::ambiguous const& e)
+ {
+ error (loc) << "data member name '" << e.first.name () << "' " <<
+ "specified with db pragma " << prag << " is ambiguous" << endl;
+
+ info (e.first.named ().location ()) << "could resolve to this " <<
+ "data member" << endl;
+
+ info (e.second.named ().location ()) << "or could resolve to this " <<
+ "data member" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ class_::expression class_::
+ translate_expression (type& c,
+ cxx_tokens const& ts,
+ semantics::scope& scope,
+ location_t loc,
+ string const& prag,
+ bool* placeholder,
+ bool predicate)
+ {
+ // This code is similar to translate() from context.cxx.
+ //
+
+ // The overall idea is as folows: read in tokens and add them
+ // to the string. If a token starts a name, try to resolve it
+ // to an object member (taking into account aliases). If this
+ // was successful, translate it to the query column reference.
+ // Otherwise, output it as is.
+ //
+ // If the placeholder argument is not NULL, then we need to
+ // detect the special '(?)' token sequence and replace it
+ // with the query variable ('q').
+ //
+ expression e ("");
+ string& r (e.value);
+
+ view_alias_map const& amap (c.get<view_alias_map> ("alias-map"));
+ view_object_map const& omap (c.get<view_object_map> ("object-map"));
+
+ cxx_tokens_lexer l;
+ l.start (ts);
+
+ tree tn;
+ string tl;
+ for (cpp_ttype tt (l.next (tl, &tn)), ptt (CPP_EOF); tt != CPP_EOF;)
+ {
+ // Try to format the expression to resemble the style of the
+ // generated code.
+ //
+ switch (tt)
+ {
+ case CPP_NOT:
+ {
+ add_space (r);
+ r += '!';
+ break;
+ }
+ case CPP_COMMA:
+ {
+ r += ", ";
+ break;
+ }
+ case CPP_OPEN_PAREN:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD)
+ add_space (r);
+
+ r += '(';
+ break;
+ }
+ case CPP_CLOSE_PAREN:
+ {
+ r += ')';
+ break;
+ }
+ case CPP_OPEN_SQUARE:
+ {
+ r += '[';
+ break;
+ }
+ case CPP_CLOSE_SQUARE:
+ {
+ r += ']';
+ break;
+ }
+ case CPP_OPEN_BRACE:
+ {
+ add_space (r);
+ r += "{ ";
+ break;
+ }
+ case CPP_CLOSE_BRACE:
+ {
+ add_space (r);
+ r += '}';
+ break;
+ }
+ case CPP_SEMICOLON:
+ {
+ r += ';';
+ break;
+ }
+ case CPP_ELLIPSIS:
+ {
+ add_space (r);
+ r += "...";
+ break;
+ }
+ case CPP_PLUS:
+ case CPP_MINUS:
+ {
+ bool unary (ptt != CPP_NAME &&
+ ptt != CPP_SCOPE &&
+ ptt != CPP_NUMBER &&
+ ptt != CPP_STRING &&
+ ptt != CPP_CLOSE_PAREN &&
+ ptt != CPP_PLUS_PLUS &&
+ ptt != CPP_MINUS_MINUS);
+
+ if (!unary)
+ add_space (r);
+
+ r += cxx_lexer::token_spelling[tt];
+
+ if (!unary)
+ r += ' ';
+ break;
+ }
+ case CPP_PLUS_PLUS:
+ case CPP_MINUS_MINUS:
+ {
+ if (ptt != CPP_NAME &&
+ ptt != CPP_CLOSE_PAREN &&
+ ptt != CPP_CLOSE_SQUARE)
+ add_space (r);
+
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ case CPP_DEREF:
+ case CPP_DEREF_STAR:
+ case CPP_DOT:
+ case CPP_DOT_STAR:
+ {
+ r += cxx_lexer::token_spelling[tt];
+ break;
+ }
+ case CPP_STRING:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += strlit (tl);
+ break;
+ }
+ case CPP_NUMBER:
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += tl;
+ break;
+ }
+ case CPP_SCOPE:
+ case CPP_NAME:
+ {
+ // Start of a name.
+ //
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ // Check if this is a pointer expression.
+ //
+ // If r is not empty, then it means this is not just the
+ // name. If placeholder is not 0, then we are translating
+ // a query expression, not a join condition.
+ //
+ expression e (
+ translate_name (
+ l, tt, tl, tn, ptt,
+ scope, loc, prag,
+ r.empty () && placeholder == 0, amap, omap));
+
+ if (e.kind == expression::literal)
+ r += e.value;
+ else
+ return e;
+
+ continue; // We have already extracted the next token.
+ }
+ case CPP_QUERY:
+ {
+ if (placeholder != 0 && !*placeholder)
+ {
+ if (ptt == CPP_OPEN_PAREN)
+ {
+ // Get the next token and see if it is ')'.
+ //
+ ptt = tt;
+ tt = l.next (tl, &tn);
+
+ if (tt == CPP_CLOSE_PAREN)
+ {
+ *placeholder = true;
+
+ // Predicate is true if this is a SELECT statement clause.
+ // Otherwise it is a stored procedure parameters.
+ //
+ if (predicate)
+ r += "q.empty () ? query_base_type::true_expr : q";
+ else
+ {
+ r.resize (r.size () - 1); // Remove opening paren.
+ r += "q";
+ break; // Skip the closing paren as well.
+ }
+ }
+ else
+ {
+ // The same as in the default case.
+ //
+ add_space (r);
+ r += "? ";
+ }
+ continue; // We have already gotten the next token.
+ }
+ }
+ }
+ // Fall through.
+ default:
+ {
+ // Handle CPP_KEYWORD here to avoid a warning (it is not
+ // part of the cpp_ttype enumeration).
+ //
+ if (tt == CPP_KEYWORD)
+ {
+ if (ptt == CPP_NAME ||
+ ptt == CPP_KEYWORD ||
+ ptt == CPP_STRING ||
+ ptt == CPP_NUMBER)
+ add_space (r);
+
+ r += tl;
+ }
+ else
+ {
+ // All the other operators.
+ //
+ add_space (r);
+ r += cxx_lexer::token_spelling[tt];
+ r += ' ';
+ }
+ break;
+ }
+ }
+
+ //
+ // Watch out for the continue statements above if you add any
+ // logic here.
+ //
+
+ ptt = tt;
+ tt = l.next (tl, &tn);
+ }
+
+ return e;
+ }
+
+ void
+ generate ()
+ {
+ context ctx;
+ ostream& os (ctx.os);
+
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (false);
+ traversal::namespace_ ns;
+ instance<class_> c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (false);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ instance<include> i;
+ i->generate ();
+
+ os << "namespace odb"
+ << "{";
+
+ unit.dispatch (ctx.unit);
+
+ os << "}";
+ }
+ }
+}
diff --git a/odb/odb/relational/source.hxx b/odb/odb/relational/source.hxx
new file mode 100644
index 0000000..ba6b2be
--- /dev/null
+++ b/odb/odb/relational/source.hxx
@@ -0,0 +1,7158 @@
+// file : odb/relational/source.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_SOURCE_HXX
+#define ODB_RELATIONAL_SOURCE_HXX
+
+#include <map>
+#include <set>
+#include <list>
+#include <vector>
+#include <sstream>
+
+#include <odb/diagnostics.hxx>
+
+#include <odb/relational/context.hxx>
+#include <odb/relational/common.hxx>
+#include <odb/relational/schema.hxx>
+
+namespace relational
+{
+ namespace source
+ {
+ // Column literal in a statement (e.g., in select-list, etc).
+ //
+ struct statement_column
+ {
+ statement_column (): member (0) {}
+ statement_column (std::string const& tbl,
+ std::string const& col,
+ std::string const& t,
+ semantics::data_member& m,
+ std::string const& kp = "")
+ : table (tbl), column (col), type (t), member (&m), key_prefix (kp)
+ {
+ }
+
+ std::string table; // Schema-qualifed and quoted table name.
+ std::string column; // Table-qualifed and quoted column expr.
+ std::string type; // Column SQL type.
+ semantics::data_member* member;
+ std::string key_prefix;
+ };
+
+ typedef std::list<statement_column> statement_columns;
+
+ // Query parameter generator. A new instance is created for each
+ // query, so the customized version can have a counter to implement,
+ // for example, numbered parameters (e.g., $1, $2, etc). The auto_id()
+ // function is called instead of next() for the automatically-assigned
+ // object id member when generating the persist statement. If empty
+ // string is returned, then parameter is ignored.
+ //
+ struct query_parameters: virtual context
+ {
+ typedef query_parameters base;
+
+ query_parameters (statement_kind sk, qname const& table)
+ : sk_ (sk), table_ (table) {}
+
+ virtual string
+ next (semantics::data_member&,
+ const std::string& /*column*/, // Table qualified and quoted.
+ const std::string& /*sqlt*/)
+ {
+ return "?";
+ }
+
+ virtual string
+ auto_id (semantics::data_member& m,
+ const std::string& column,
+ const std::string& sqlt)
+ {
+ return next (m, column, sqlt);
+ }
+
+ string
+ next (const object_columns_list::column& c)
+ {
+ return next (*c.member, quote_id (c.name), c.type);
+ }
+
+ string
+ next (const statement_column& c)
+ {
+ return next (*c.member, c.column, c.type);
+ }
+
+ protected:
+ statement_kind sk_;
+ qname table_;
+ };
+
+ struct object_columns: object_columns_base, virtual context
+ {
+ typedef object_columns base;
+
+ // If provided, used to resolve table/alias names for inverse
+ // pointed-to and base objects. Returns qualified name.
+ //
+ struct table_name_resolver
+ {
+ virtual string
+ resolve_pointer (semantics::data_member&) const = 0;
+
+ virtual string
+ resolve_base (semantics::class_&) const = 0;
+ };
+
+ object_columns (statement_kind sk,
+ statement_columns& sc,
+ query_parameters* param = 0,
+ object_section* section = 0)
+ : object_columns_base (true, true, section),
+ sk_ (sk),
+ ro_ (true),
+ sc_ (sc),
+ param_ (param),
+ table_name_resolver_ (0),
+ depth_ (1)
+ {
+ }
+
+ object_columns (statement_kind sk,
+ bool ignore_ro,
+ statement_columns& sc,
+ query_parameters* param)
+ : object_columns_base (true, true, 0),
+ sk_ (sk),
+ ro_ (ignore_ro),
+ sc_ (sc),
+ param_ (param),
+ table_name_resolver_ (0),
+ depth_ (1)
+ {
+ }
+
+ object_columns (std::string const& table_qname,
+ statement_kind sk,
+ statement_columns& sc,
+ size_t depth = 1,
+ object_section* section = 0,
+ table_name_resolver* tnr = 0)
+ : object_columns_base (true, true, section),
+ sk_ (sk),
+ ro_ (true),
+ sc_ (sc),
+ param_ (0),
+ table_name_ (table_qname),
+ table_name_resolver_ (tnr),
+ depth_ (depth)
+ {
+ }
+
+ virtual bool
+ section_test (data_member_path const& mp)
+ {
+ object_section& s (section (mp));
+
+ // Include eager loaded members into the main section for
+ // SELECT statements. Also include optimistic version into
+ // section's SELECT and UPDATE statements.
+ //
+ return section_ == 0 ||
+ *section_ == s ||
+ (sk_ == statement_select &&
+ *section_ == main_section &&
+ !s.separate_load ()) ||
+ (version (mp) &&
+ (sk_ == statement_update || sk_ == statement_select));
+ }
+
+ virtual void
+ traverse_object (semantics::class_& c)
+ {
+ // If we are generating a select statement and this is a derived
+ // type in a polymorphic hierarchy, then we need to include base
+ // columns, but do it in reverse order as well as switch the table
+ // name (base columns come from different tables).
+ //
+ semantics::class_* poly_root (polymorphic (c));
+ if (poly_root != 0 && poly_root != &c)
+ {
+ names (c);
+
+ if (sk_ == statement_select && --depth_ != 0)
+ {
+ semantics::class_& b (polymorphic_base (c));
+
+ table_name_ = table_name_resolver_ != 0
+ ? table_name_resolver_->resolve_base (b)
+ : table_qname (b);
+
+ inherits (c);
+ }
+ }
+ else
+ object_columns_base::traverse_object (c);
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ // Ignore polymorphic id references for select statements.
+ //
+ if (sk_ == statement_select && m.count ("polymorphic-ref"))
+ return;
+
+ data_member_path* imp (inverse (m, key_prefix_));
+
+ // Ignore certain columns depending on what kind of statement we are
+ // generating. Columns corresponding to the inverse members are
+ // only present in the select statements.
+ //
+ if (imp != 0 && sk_ != statement_select)
+ return;
+
+ // Inverse object pointers come from a joined table.
+ //
+ if (imp != 0)
+ {
+ bool poly (polymorphic (c) != 0);
+ semantics::data_member& imf (*imp->front ());
+ semantics::data_member& imb (*imp->back ());
+
+ // In a polymorphic hierarchy the inverse member can be in
+ // the base class, in which case we should use that table.
+ //
+ semantics::class_& imc (
+ poly ? dynamic_cast<semantics::class_&> (imf.scope ()) : c);
+
+ data_member_path& id (*id_member (imc));
+ semantics::type& idt (utype (id));
+
+ if (container (imb))
+ {
+ // This container is a direct member of the class so the table
+ // prefix is just the class table name. We don't assign join
+ // aliases for container tables so use the actual table name.
+ // Note that the if(!table_name_.empty ()) test may look wrong
+ // at first but it is not; if table_name_ is empty then we are
+ // generating a container table where we don't qualify columns
+ // with tables.
+ //
+ string table;
+
+ if (!table_name_.empty ())
+ {
+ if (table_name_resolver_ != 0)
+ table = table_name_resolver_->resolve_pointer (m);
+ else
+ table = table_qname (imc, *imp);
+ }
+
+ instance<object_columns> oc (table, sk_, sc_);
+ oc->traverse (imb, idt, "id", "object_id", &imc);
+ }
+ else
+ {
+ // Use the join alias instead of the actual table name unless we
+ // are handling a container. Generally, we want the join alias
+ // to be based on the column name. This is straightforward for
+ // single-column references. In case of a composite id, we will
+ // need to use the column prefix which is based on the data
+ // member name, unless overridden by the user. In the latter
+ // case the prefix can be empty, in which case we will just
+ // fall back on the member's public name. Note that the
+ // if(!table_name_.empty ()) test may look wrong at first but
+ // it is not; if table_name_ is empty then we are generating a
+ // container table where we don't qualify columns with tables.
+ //
+ string alias;
+
+ if (!table_name_.empty ())
+ {
+ if (table_name_resolver_ != 0)
+ alias = table_name_resolver_->resolve_pointer (m);
+ else
+ {
+ string n;
+
+ if (composite_wrapper (idt))
+ {
+ n = column_prefix (m, key_prefix_, default_name_).prefix;
+
+ if (n.empty ())
+ n = public_name_db (m);
+ else if (n[n.size () - 1] == '_')
+ n.resize (n.size () - 1); // Remove trailing underscore.
+ }
+ else
+ {
+ bool dummy;
+ n = column_name (m, key_prefix_, default_name_, dummy);
+ }
+
+ alias = column_prefix_.prefix + n;
+
+ if (poly)
+ {
+ qname const& table (table_name (imc));
+ alias = quote_id (alias + "_" + table.uname ());
+ }
+ else
+ alias = quote_id (alias);
+ }
+ }
+
+ instance<object_columns> oc (alias, sk_, sc_);
+ oc->traverse (id);
+ }
+ }
+ else
+ object_columns_base::traverse_pointer (m, c);
+ }
+
+ virtual bool
+ traverse_column (semantics::data_member& m, string const& name, bool)
+ {
+ // Ignore certain columns depending on what kind statement we are
+ // generating. Id and readonly columns are not present in the update
+ // statements.
+ //
+ if ((id () || readonly (member_path_, member_scope_)) &&
+ sk_ == statement_update && ro_)
+ return false;
+
+ return column (m, table_name_, quote_id (name));
+ }
+
+ virtual bool
+ column (semantics::data_member& m,
+ string const& table,
+ string const& column)
+ {
+ string r;
+
+ if (!table.empty ())
+ {
+ r += table; // Already quoted.
+ r += '.';
+ }
+
+ r += column; // Already quoted.
+
+ string const& sqlt (column_type ());
+
+ // Version column (optimistic concurrency) requires special
+ // handling in the UPDATE statement.
+ //
+ //
+ if (sk_ == statement_update && version (m))
+ {
+ r += "=" + r + "+1";
+ }
+ else if (param_ != 0)
+ {
+ r += '=';
+ r += convert_to (param_->next (m, column, sqlt), sqlt, m);
+ }
+ else if (sk_ == statement_select)
+ r = convert_from (r, sqlt, m);
+
+ sc_.push_back (statement_column (table, r, sqlt, m, key_prefix_));
+ return true;
+ }
+
+ protected:
+ statement_kind sk_;
+ bool ro_;
+ statement_columns& sc_;
+ query_parameters* param_;
+ string table_name_;
+ table_name_resolver* table_name_resolver_;
+ size_t depth_;
+ };
+
+ struct view_columns: object_columns_base,
+ object_columns::table_name_resolver,
+ virtual context
+ {
+ typedef view_columns base;
+
+ view_columns (statement_columns& sc,
+ strings& from,
+ const view_relationship_map& rm)
+ : sc_ (sc), from_ (from), rel_map_ (rm), in_composite_ (false) {}
+
+ // Implementation of table_name_resolver for object_columns.
+ //
+ virtual string
+ resolve_pointer (semantics::data_member& m) const
+ {
+ view_object& us (*ptr_->get<view_object*> ("view-object"));
+
+ data_member_path& imp (*inverse (m));
+ semantics::data_member& imf (*imp.front ());
+ semantics::data_member& imb (*imp.back ());
+
+ using semantics::class_;
+ typedef view_relationship_map::const_iterator iterator;
+
+ std::pair<iterator, iterator> r (rel_map_.equal_range (imp));
+
+ for (; r.first != r.second; ++r.first)
+ {
+ if (r.first->second.second != &us) // Not our associated.
+ continue;
+
+ view_object& vo (*r.first->second.first); // First because inverse.
+
+ // Derive the table name the same way as the JOIN code.
+ //
+ class_* c (vo.obj);
+ if (class_* root = polymorphic (*c))
+ {
+ // Can be in base.
+ //
+ c = &static_cast<class_&> (imf.scope ());
+
+ if (!polymorphic (*c))
+ c = root;
+ }
+
+ string const& a (vo.alias);
+
+ if (container (imb))
+ {
+ // If this is a container, then object_columns will use the
+ // column from the container table, not from the object table
+ // (which, strictly speaking, might not have been JOIN'ed).
+ //
+ qname t (table_name (*c, imp));
+ return a.empty ()
+ ? quote_id (t)
+ : quote_id (a + '_' + t.uname ());
+ }
+ else
+ {
+ qname t;
+ if (a.empty ())
+ t = table_name (*c);
+ else
+ {
+ if (polymorphic (*c))
+ t = qname (a + "_" + table_name (*c).uname ());
+ else
+ t = qname (a);
+ }
+ return quote_id (t);
+ }
+ }
+
+ // So there is no associated object for this column. The initial
+ // plan was to complain and ask the user to explicitly associate
+ // the object. This is not a bad plan except for one thing: if
+ // the direct side of the relationship is a container, then
+ // associating that object explicitly will result in both the
+ // container table and the object table being JOIN'ed. But we
+ // only need the container table (for the object id) So we will
+ // be joining a table for nothing, which is not very clean. So
+ // the alternative, and more difficult, plan is to go ahead and
+ // add the necessary JOIN's automatically.
+ //
+ // This code follows the normal JOIN generation code.
+ //
+ class_* o (object_pointer (utype (m)));
+ if (class_* root = polymorphic (*o))
+ {
+ o = &static_cast<class_&> (imf.scope ());
+
+ if (!polymorphic (*o))
+ o = root;
+ }
+
+ string const& a (us.alias);
+ string lt (a.empty () ? table_qname (*us.obj) : quote_id (a));
+ string rt;
+ qname ct (container (imb) ? table_name (*o, imp) : table_name (*o));
+
+ string l ("LEFT JOIN ");
+
+ if (a.empty ())
+ {
+ rt = quote_id (ct);
+ l += rt;
+ }
+ else
+ {
+ // The same relationship can be used by multiple associated
+ // objects. So if we have an alias, then also construct one
+ // for the table that we are joining.
+ //
+ rt = quote_id (a + '_' + ct.uname ());
+ l += quote_id (ct);
+ l += (need_alias_as ? " AS " : " ") + rt;
+ }
+
+ l += " ON";
+ from_.push_back (l);
+
+ instance<object_columns_list> l_cols; // Our id columns.
+ instance<object_columns_list> r_cols; // Other side id columns.
+
+ data_member_path& id (*id_member (*us.obj));
+
+ l_cols->traverse (id);
+
+ if (container (imb))
+ r_cols->traverse (imb, utype (id), "value", "value");
+ else
+ r_cols->traverse (imb, column_prefix (imp));
+
+ for (object_columns_list::iterator b (l_cols->begin ()), i (b),
+ j (r_cols->begin ()); i != l_cols->end (); ++i, ++j)
+ {
+ l.clear ();
+
+ if (i != b)
+ l += "AND ";
+
+ l += lt;
+ l += '.';
+ l += quote_id (i->name);
+ l += '=';
+ l += rt;
+ l += '.';
+ l += quote_id (j->name);
+
+ from_.push_back (strlit (l));
+ }
+
+ return rt;
+
+ /*
+ // The alternative implementation:
+ //
+ location const& l1 (m.location ());
+ location const& l2 (ptr_->location ());
+
+ string n1 (class_name (*object_pointer (utype (m))));
+ string n2 (class_name (*object_pointer (utype (*ptr_))));
+
+ error (l1) << "object '" << n1 << "' pointed-to by the inverse "
+ << "data member in object '" << n2 << "' must be "
+ << "explicitly associated with the view" << endl;
+
+ info (l2) << "view data member that loads '" << n2 << "' is "
+ << "defined here" << endl;
+
+ throw operation_failed ();
+ */
+ }
+
+ virtual string
+ resolve_base (semantics::class_& b) const
+ {
+ view_object& vo (*ptr_->get<view_object*> ("view-object"));
+
+ qname t (vo.alias.empty ()
+ ? table_name (b)
+ : qname (vo.alias + "_" + table_name (b).uname ()));
+
+ return quote_id (t);
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ type* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+ size_t poly_depth (poly_derived ? polymorphic_depth (c) : 1);
+
+ view_object& vo (*m.get<view_object*> ("view-object"));
+ string const& a (vo.alias);
+
+ qname t;
+ if (a.empty ())
+ t = table_name (c);
+ else
+ {
+ if (poly)
+ t = qname (a + "_" + table_name (c).uname ());
+ else
+ t = qname (a);
+ }
+ string qt (quote_id (t));
+
+ ptr_ = &m;
+
+ statement_kind sk (statement_select); // Imperfect forwarding.
+ object_section* s (&main_section); // Imperfect forwarding.
+ instance<object_columns> oc (qt, sk, sc_, poly_depth, s, this);
+ oc->traverse (c);
+ }
+
+ virtual void
+ traverse_composite (semantics::data_member* pm, semantics::class_& c)
+ {
+ if (in_composite_)
+ {
+ object_columns_base::traverse_composite (pm, c);
+ return;
+ }
+
+ // Override the column prerix.
+ //
+ semantics::data_member& m (*pm);
+
+ // If we have literal column specified, use that.
+ //
+ if (m.count ("column"))
+ {
+ table_column const& tc (m.get<table_column> ("column"));
+
+ if (!tc.table.empty ())
+ table_prefix_ = tc.table;
+
+ column_prefix_ = object_columns_base::column_prefix (m);
+ }
+ // Otherwise, see if there is a column expression. For composite
+ // members in a view, this should be a single reference.
+ //
+ else if (m.count ("column-expr"))
+ {
+ column_expr const& e (m.get<column_expr> ("column-expr"));
+
+ if (e.size () > 1)
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: column expression specified for a data member "
+ << "of a composite value type" << endl;
+
+ throw operation_failed ();
+ }
+
+ data_member_path const& mp (e.back ().member_path);
+
+ if (mp.size () > 1)
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: invalid data member in db pragma column"
+ << endl;
+
+ throw operation_failed ();
+ }
+
+ table_prefix_ = e.back ().table;
+ column_prefix_ = object_columns_base::column_prefix (*mp.back ());
+ }
+ else
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: no column prefix provided for a view data member"
+ << endl;
+
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": info: use db pragma column to specify the column prefix"
+ << endl;
+
+ throw operation_failed ();
+ }
+
+ in_composite_ = true;
+ object_columns_base::traverse_composite (pm, c);
+ in_composite_ = false;
+ }
+
+ virtual bool
+ traverse_column (semantics::data_member& m, string const& name, bool)
+ {
+ string tbl;
+ string col;
+
+ // If we are inside a composite value, use the standard
+ // column name machinery.
+ //
+ if (in_composite_)
+ {
+ if (!table_prefix_.empty ())
+ {
+ tbl = quote_id (table_prefix_);
+ col += tbl;
+ col += '.';
+ }
+
+ col += quote_id (name);
+ }
+ // If we have literal column specified, use that.
+ //
+ else if (m.count ("column"))
+ {
+ table_column const& tc (m.get<table_column> ("column"));
+
+ if (!tc.expr)
+ {
+ if (!tc.table.empty ())
+ {
+ tbl = quote_id (tc.table);
+ col += tbl;
+ col += '.';
+ }
+
+ col += quote_id (tc.column);
+ }
+ else
+ col += tc.column;
+ }
+ // Otherwise, see if there is a column expression.
+ //
+ else if (m.count ("column-expr"))
+ {
+ column_expr const& e (m.get<column_expr> ("column-expr"));
+
+ for (column_expr::const_iterator i (e.begin ()); i != e.end (); ++i)
+ {
+ switch (i->kind)
+ {
+ case column_expr_part::literal:
+ {
+ col += i->value;
+ break;
+ }
+ case column_expr_part::reference:
+ {
+ tbl = quote_id (i->table);
+ col += tbl;
+ col += '.';
+ col += column_qname (i->member_path);
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: no column name provided for a view data member"
+ << endl;
+
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": info: use db pragma column to specify the column name"
+ << endl;
+
+ throw operation_failed ();
+ }
+
+ return column (m, tbl, col);
+ }
+
+ // The column argument is a qualified and quoted column or
+ // expression.
+ //
+ virtual bool
+ column (semantics::data_member& m,
+ string const& table,
+ string const& column)
+ {
+ string const& sqlt (column_type ());
+ sc_.push_back (
+ statement_column (
+ table, convert_from (column, sqlt, m), sqlt, m));
+ return true;
+ }
+
+ protected:
+ statement_columns& sc_;
+ strings& from_;
+ const view_relationship_map& rel_map_;
+
+ bool in_composite_;
+ qname table_prefix_; // Table corresponding to column_prefix_;
+
+ // Set to the current pointer data member that we are traversing.
+ //
+ semantics::data_member* ptr_;
+ };
+
+ struct polymorphic_object_joins: object_columns_base, virtual context
+ {
+ typedef polymorphic_object_joins base;
+
+ polymorphic_object_joins (semantics::class_& obj,
+ bool query,
+ size_t depth,
+ string const& alias = "",
+ user_section* section = 0)
+ : object_columns_base (true, true),
+ obj_ (obj),
+ query_ (query),
+ depth_ (depth),
+ section_ (section),
+ alias_ (alias)
+ {
+ // Get the table and id columns.
+ //
+ table_ = alias_.empty ()
+ ? table_qname (obj_)
+ : quote_id (alias_ + "_" + table_name (obj_).uname ());
+
+ cols_->traverse (*id_member (obj_));
+ }
+
+ virtual void
+ traverse_object (semantics::class_& c)
+ {
+ // If section is specified, skip bases that don't add anything
+ // to load.
+ //
+ bool skip (false), stop (false);
+ if (section_ != 0)
+ {
+ skip = true;
+
+ if (section_->object == &c)
+ {
+ user_section& s (*section_);
+
+ if (s.total != 0 || s.optimistic ())
+ skip = false;
+
+ section_ = s.base; // Move to the next base.
+
+ if (section_ == 0)
+ stop = true; // Stop at this base if there are no more overrides.
+ }
+ }
+ // Skip intermediates that don't add any data members.
+ //
+ else if (!query_)
+ {
+ column_count_type const& cc (column_count (c));
+ if (cc.total == cc.id + cc.separate_load)
+ skip = true;
+ }
+
+ if (!skip)
+ {
+ std::ostringstream cond;
+
+ qname table (table_name (c));
+ string alias (alias_.empty ()
+ ? quote_id (table)
+ : quote_id (alias_ + "_" + table.uname ()));
+
+ for (object_columns_list::iterator b (cols_->begin ()), i (b);
+ i != cols_->end ();
+ ++i)
+ {
+ if (i != b)
+ cond << " AND ";
+
+ string qn (quote_id (i->name));
+ cond << alias << '.' << qn << '=' << table_ << '.' << qn;
+ }
+
+ string line ("LEFT JOIN " + quote_id (table));
+
+ if (!alias_.empty ())
+ line += (need_alias_as ? " AS " : " ") + alias;
+
+ line += " ON " + cond.str ();
+
+ joins.push_back (line);
+ }
+
+ if (!stop && --depth_ != 0)
+ inherits (c);
+ }
+
+ public:
+ strings joins;
+
+ strings::const_iterator
+ begin () const {return joins.begin ();}
+
+ strings::const_iterator
+ end () const {return joins.end ();}
+
+ private:
+ semantics::class_& obj_;
+ bool query_;
+ size_t depth_;
+ user_section* section_;
+ string alias_;
+ string table_;
+ instance<object_columns_list> cols_;
+ };
+
+ struct object_joins: object_columns_base, virtual context
+ {
+ typedef object_joins base;
+
+ //@@ context::{cur,top}_object; might have to be created every time.
+ //
+ object_joins (semantics::class_& scope,
+ bool query,
+ size_t depth,
+ object_section* section = 0)
+ : object_columns_base (true, true, section),
+ query_ (query),
+ depth_ (depth),
+ table_ (table_qname (scope)),
+ id_ (*id_member (scope))
+ {
+ id_cols_->traverse (id_);
+ }
+
+ virtual bool
+ section_test (data_member_path const& mp)
+ {
+ object_section& s (section (mp));
+
+ // Include eager loaded members into the main section.
+ //
+ return section_ == 0 ||
+ *section_ == s ||
+ (*section_ == main_section && !s.separate_load ());
+ }
+
+ virtual void
+ traverse_object (semantics::class_& c)
+ {
+ // If this is a derived type in a polymorphic hierarchy, then we
+ // need to include base joins, but do it in reverse order as well
+ // as switch the table name (base columns come from different
+ // tables).
+ //
+ semantics::class_* poly_root (polymorphic (c));
+ if (poly_root != 0 && poly_root != &c)
+ {
+ names (c);
+
+ if (query_ || --depth_ != 0)
+ {
+ table_ = table_qname (polymorphic_base (c));
+ inherits (c);
+ }
+ }
+ else
+ object_columns_base::traverse_object (c);
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ // Ignore polymorphic id references; they are joined by
+ // polymorphic_object_joins in a special way.
+ //
+ if (m.count ("polymorphic-ref"))
+ return;
+
+ string t, a, dt, da;
+ std::ostringstream cond, dcond; // @@ diversion?
+
+ // Derive table alias for this member. Generally, we want the
+ // alias to be based on the column name. This is straightforward
+ // for single-column references. In case of a composite id, we
+ // will need to use the column prefix which is based on the data
+ // member name, unless overridden by the user. In the latter
+ // case the prefix can be empty, in which case we will just
+ // fall back on the member's public name.
+ //
+ string alias;
+ {
+ string n;
+
+ if (composite_wrapper (utype (*id_member (c))))
+ {
+ n = column_prefix (m, key_prefix_, default_name_).prefix;
+
+ if (n.empty ())
+ n = public_name_db (m);
+ else if (n[n.size () - 1] == '_')
+ n.resize (n.size () - 1); // Remove trailing underscore.
+ }
+ else
+ {
+ bool dummy;
+ n = column_name (m, key_prefix_, default_name_, dummy);
+ }
+
+ alias = column_prefix_.prefix + n;
+ }
+
+ semantics::class_* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+
+ semantics::class_* joined_obj (0);
+
+ if (data_member_path* imp = inverse (m, key_prefix_))
+ {
+ semantics::data_member& imf (*imp->front ());
+ semantics::data_member& imb (*imp->back ());
+
+ // In a polymorphic hierarchy the inverse member can be in
+ // the base class, in which case we should use that table.
+ //
+ semantics::class_& imc (
+ poly ? dynamic_cast<semantics::class_&> (imf.scope ()) : c);
+
+ if (container (imb))
+ {
+ // This container is a direct member of the class so the table
+ // prefix is just the class table name.
+ //
+ t = table_qname (imc, *imp);
+
+ // Container's value is our id.
+ //
+ instance<object_columns_list> id_cols;
+ id_cols->traverse (imb, utype (id_), "value", "value");
+
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b),
+ j (id_cols_->begin ()); i != id_cols->end (); ++i, ++j)
+ {
+
+ if (i != b)
+ cond << " AND ";
+
+ cond << t << '.' << quote_id (i->name) << '=' <<
+ table_ << '.' << quote_id (j->name);
+ }
+
+ // Add the join for the object itself so that we are able to
+ // use it in the WHERE clause.
+ //
+ if (query_)
+ {
+ // Here we can use the most derived class instead of the
+ // one containing the inverse member.
+ //
+ qname const& table (table_name (c));
+
+ dt = quote_id (table);
+ da = quote_id (poly ? alias + "_" + table.uname () : alias);
+
+ data_member_path& id (*id_member (c));
+
+ instance<object_columns_list> oid_cols, cid_cols;
+ oid_cols->traverse (id);
+ cid_cols->traverse (imb, utype (id), "id", "object_id", &c);
+
+ for (object_columns_list::iterator b (cid_cols->begin ()), i (b),
+ j (oid_cols->begin ()); i != cid_cols->end (); ++i, ++j)
+ {
+
+ if (i != b)
+ dcond << " AND ";
+
+ dcond << da << '.' << quote_id (j->name) << '=' <<
+ t << '.' << quote_id (i->name);
+ }
+
+ joined_obj = &c;
+ }
+ }
+ else
+ {
+ qname const& table (table_name (imc));
+
+ t = quote_id (table);
+ a = quote_id (poly ? alias + "_" + table.uname () : alias);
+
+ instance<object_columns_list> id_cols;
+ id_cols->traverse (imb, column_prefix (*imp));
+
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b),
+ j (id_cols_->begin ()); i != id_cols->end (); ++i, ++j)
+ {
+ if (i != b)
+ cond << " AND ";
+
+ cond << a << '.' << quote_id (i->name) << '=' <<
+ table_ << '.' << quote_id (j->name);
+ }
+
+ // If we are generating query, JOIN base/derived classes so
+ // that we can use their data in the WHERE clause.
+ //
+ if (query_)
+ joined_obj = &imc;
+ }
+ }
+ else if (query_)
+ {
+ // We need the join to be able to use the referenced object
+ // in the WHERE clause.
+ //
+ qname const& table (table_name (c));
+
+ t = quote_id (table);
+ a = quote_id (poly ? alias + "_" + table.uname () : alias);
+
+ instance<object_columns_list> oid_cols (column_prefix_);
+ oid_cols->traverse (m);
+
+ instance<object_columns_list> pid_cols;
+ pid_cols->traverse (*id_member (c));
+
+ for (object_columns_list::iterator b (pid_cols->begin ()), i (b),
+ j (oid_cols->begin ()); i != pid_cols->end (); ++i, ++j)
+ {
+
+ if (i != b)
+ cond << " AND ";
+
+ cond << a << '.' << quote_id (i->name) << '=' <<
+ table_ << '.' << quote_id (j->name);
+ }
+
+ joined_obj = &c;
+ }
+
+ if (!t.empty ())
+ {
+ string line ("LEFT JOIN ");
+ line += t;
+
+ if (!a.empty ())
+ line += (need_alias_as ? " AS " : " ") + a;
+
+ line += " ON ";
+ line += cond.str ();
+
+ joins.push_back (line);
+ }
+
+ // Add dependent join (i.e., an object table join via the
+ // container table).
+ //
+ if (!dt.empty ())
+ {
+ string line ("LEFT JOIN ");
+ line += dt;
+
+ if (!da.empty ())
+ line += (need_alias_as ? " AS " : " ") + da;
+
+ line += " ON ";
+ line += dcond.str ();
+
+ joins.push_back (line);
+ }
+
+ // If we joined the object that is part of a polymorphic type
+ // hierarchy, then we may need join its bases as well as its
+ // derived types. This is only done for queries.
+ //
+ if (joined_obj != 0 && poly)
+ {
+ size_t depth (polymorphic_depth (*joined_obj));
+
+ // Join "up" (derived).
+ //
+ if (joined_obj != &c)
+ {
+ bool t (true); //@@ (im)perfect forward.
+ size_t d (polymorphic_depth (c) - depth); //@@ (im)perfect forward.
+ instance<polymorphic_object_joins> j (*joined_obj, t, d, alias);
+ j->traverse (c);
+ joins.insert (joins.end (), j->joins.begin (), j->joins.end ());
+ }
+
+ // Join "down" (base).
+ //
+ if (joined_obj != poly_root)
+ {
+ bool t (true); //@@ (im)perfect forward.
+ size_t d (depth - 1); //@@ (im)perfect forward.
+ instance<polymorphic_object_joins> j (*joined_obj, t, d, alias);
+ j->traverse (polymorphic_base (*joined_obj));
+ joins.insert (joins.end (), j->joins.begin (), j->joins.end ());
+ }
+ }
+ }
+
+ public:
+ strings joins;
+
+ strings::const_iterator
+ begin () const {return joins.begin ();}
+
+ strings::const_iterator
+ end () const {return joins.end ();}
+
+ private:
+ bool query_;
+ size_t depth_;
+ string table_;
+ data_member_path& id_;
+ instance<object_columns_list> id_cols_;
+ };
+
+ // Check that eager object pointers in the objects that a view loads
+ // can be loaded from the cache (i.e., they have session support
+ // enabled).
+ //
+ struct view_object_check: object_members_base
+ {
+ typedef view_object_check base;
+
+ view_object_check (view_object& vo, view_relationship_map& rm)
+ : object_members_base (false, &main_section),
+ session_ (false), vo_ (vo), rel_map_ (rm) {}
+
+ virtual bool
+ section_test (data_member_path const& mp)
+ {
+ // Include eager loaded members into the main section.
+ //
+ object_section& s (section (mp));
+ return *section_ == s || !s.separate_load ();
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ // Ignore polymorphic id references.
+ //
+ if (m.count ("polymorphic-ref"))
+ return;
+
+ check (m, inverse (m), utype (m), c);
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type&)
+ {
+ semantics::type& vt (container_vt (m));
+
+ if (semantics::class_* cvt = composite_wrapper (vt))
+ {
+ // Check this composite value for any pointers.
+ //
+ instance<view_object_check> t (vo_, rel_map_);
+ t->traverse (*cvt);
+
+ session_ = session_ || t->session_;
+ }
+ else if (semantics::class_* c = object_pointer (vt))
+ check (m, inverse (m, "value"), vt, *c);
+ }
+
+ void
+ check (semantics::data_member& m,
+ data_member_path* imp,
+ semantics::type& pt, // Pointer type.
+ semantics::class_& c)
+ {
+ // We don't care about lazy pointers.
+ //
+ if (lazy_pointer (pt))
+ return;
+
+ // First check the pointed-to object recursively.
+ //
+ if (!c.count ("view-object-check-seen"))
+ {
+ c.set ("view-object-check-seen", true);
+ instance<view_object_check> t (vo_, rel_map_);
+ t->traverse (c);
+
+ // We may come again for another view.
+ //
+ c.remove ("view-object-check-seen");
+
+ session_ = session_ || t->session_;
+ }
+
+ // See if the pointed-to object in this relationship is loaded
+ // by this view.
+ //
+ typedef view_relationship_map::const_iterator iterator;
+
+ std::pair<iterator, iterator> r (
+ rel_map_.equal_range (imp != 0 ? *imp : member_path_));
+
+ if (r.first == r.second)
+ return; // This relationship does not figure in the view.
+
+ view_object& vo (*(imp != 0
+ ? r.first->second.first
+ : r.first->second.second));
+
+ assert (vo.obj == &c); // Got the above right?
+
+ if (vo.ptr == 0)
+ return; // This object is not loaded by the view.
+
+ // The pointed-to object in this relationship is loaded
+ // by the view. The hard question, of course, is whether
+ // it has anything to do with us. We assume it does.
+ //
+ if (!session (c))
+ {
+ // Always direct data member.
+ //
+ semantics::class_& v (
+ dynamic_cast<semantics::class_&> (vo.ptr->scope ()));
+
+ location const& l1 (c.location ());
+ location const& l2 (m.location ());
+ location const& l3 (vo_.ptr->location ());
+ location const& l4 (vo.ptr->location ());
+
+ string on (class_name (c));
+ string vn (class_name (v));
+
+ error (l1) << "object '" << on << "' has session support disabled "
+ << "but may be loaded by view '" << vn << "' via "
+ << "several data members" << endl;
+
+ info (l2) << "indirectly via this data member..." << endl;
+ info (l3) << "...as a result of this object load" << endl;
+ info (l4) << "and directly as a result of this load" << endl;
+ info (l1) << "session support is required to only load one copy "
+ << "of the object" << endl;
+ info (l1) << "and don't forget to create a session instance when "
+ << "using this view" << endl;
+
+ throw operation_failed ();
+ }
+
+ session_ = true;
+ }
+
+ bool session_;
+
+ private:
+ view_object& vo_;
+ view_relationship_map& rel_map_;
+ };
+
+ //
+ // bind
+ //
+
+ struct bind_member: virtual member_base
+ {
+ typedef bind_member base;
+
+ // NULL section means we are generating object bind().
+ //
+ bind_member (string const& var = string (),
+ string const& arg = string (),
+ object_section* section = 0)
+ : member_base (var, 0, 0, string (), string (), section),
+ arg_override_ (arg) {}
+
+ bind_member (string const& var,
+ string const& arg,
+ semantics::type& t,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base (var, &t, ct, fq_type, key_prefix),
+ arg_override_ (arg) {}
+
+ protected:
+ string arg_override_;
+ };
+
+ template <typename T>
+ struct bind_member_impl: bind_member, virtual member_base_impl<T>
+ {
+ typedef bind_member_impl base_impl;
+
+ bind_member_impl (base const& x): base (x) {}
+
+ typedef typename member_base_impl<T>::member_info member_info;
+
+ using member_base_impl<T>::container;
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ if (container (mi))
+ return false;
+
+ // Treat version as present in every section.
+ //
+ if (section_ != 0 && !version (mi.m) && *section_ != section (mi.m))
+ return false;
+
+ // Ignore polymorphic id references; they are bound in a special
+ // way.
+ //
+ if (mi.ptr != 0 && mi.m.count ("polymorphic-ref"))
+ return false;
+
+ std::ostringstream ostr;
+ ostr << "b[n]";
+ b = ostr.str ();
+
+ arg = arg_override_.empty () ? string ("i") : arg_override_;
+
+ if (var_override_.empty ())
+ {
+ // Ignore inverse, separately-loaded members in the main
+ // section (nothing to persist).
+ //
+ if (section_ == 0 && separate_load (mi.m) && inverse (mi.m))
+ return false;
+
+ semantics::class_* comp (composite (mi.t));
+
+ os << "// " << mi.m.name () << endl
+ << "//" << endl;
+
+ // Order of these tests is important.
+ //
+ if (!insert_send_auto_id && auto_ (mi.m))
+ os << "if (sk != statement_insert && sk != statement_update)"
+ << "{";
+ else if (section_ == 0 && separate_load (mi.m))
+ os << "if (sk == statement_insert)"
+ << "{";
+ else if (inverse (mi.m, key_prefix_) || version (mi.m))
+ os << "if (sk == statement_select)"
+ << "{";
+ // If the whole class is readonly, then we will never be
+ // called with sk == statement_update.
+ //
+ else if (!readonly (*context::top_object))
+ {
+ if (id (mi.m) ||
+ readonly (mi.m) ||
+ (comp != 0 && readonly (*comp)) ||
+ (section_ == 0 && separate_update (mi.m)))
+ os << "if (sk != statement_update)"
+ << "{";
+ }
+
+ // If the member is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (mi.m));
+ unsigned long long dv (deleted (mi.m));
+
+ // If this is a composite member, see if it is summarily
+ // added/deleted.
+ //
+ if (comp != 0)
+ {
+ unsigned long long cav (added (*comp));
+ unsigned long long cdv (deleted (*comp));
+
+ if (cav != 0 && (av == 0 || av < cav))
+ av = cav;
+
+ if (cdv != 0 && (dv == 0 || dv > cdv))
+ dv = cdv;
+ }
+
+ // If the addition/deletion version is the same as the section's,
+ // then we don't need the test.
+ //
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")"
+ << "{";
+ }
+ }
+
+ return true;
+ }
+
+ virtual void
+ post (member_info& mi)
+ {
+ if (var_override_.empty ())
+ {
+ semantics::class_* comp (composite (mi.t));
+
+ // We need to increment the index even if we skipped this
+ // member due to the schema version.
+ //
+ unsigned long long av (added (mi.m));
+ unsigned long long dv (deleted (mi.m));
+
+ if (comp != 0)
+ {
+ unsigned long long cav (added (*comp));
+ unsigned long long cdv (deleted (*comp));
+
+ if (cav != 0 && (av == 0 || av < cav))
+ av = cav;
+
+ if (cdv != 0 && (dv == 0 || dv > cdv))
+ dv = cdv;
+ }
+
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if (av != 0 || dv != 0)
+ os << "}";
+
+ if (mi.ptr != 0 && view_member (mi.m))
+ {
+ // See column_count_impl for details on what's going on here.
+ //
+ column_count_type cc;
+ if (semantics::class_* root = polymorphic (*mi.ptr))
+ {
+ for (semantics::class_* b (mi.ptr);; b = &polymorphic_base (*b))
+ {
+ column_count_type const& ccb (column_count (*b));
+
+ cc.total += ccb.total - (b != root ? ccb.id : 0);
+ cc.separate_load += ccb.separate_load;
+
+ if (b == root)
+ break;
+ }
+ }
+ else
+ cc = column_count (*mi.ptr);
+
+ os << "n += " << cc.total - cc.separate_load << "UL;";
+ }
+ else if (comp != 0)
+ {
+ bool ro (readonly (*comp));
+ column_count_type const& cc (column_count (*comp));
+
+ os << "n += " << cc.total << "UL";
+
+ // select = total
+ // insert = total - inverse
+ // update = total - inverse - readonly
+ //
+ if (cc.inverse != 0 || (!ro && cc.readonly != 0))
+ {
+ os << " - (" << endl
+ << "sk == statement_select ? 0 : ";
+
+ if (cc.inverse != 0)
+ os << cc.inverse << "UL";
+
+ if (!ro && cc.readonly != 0)
+ {
+ if (cc.inverse != 0)
+ os << " + ";
+
+ os << "(" << endl
+ << "sk == statement_insert ? 0 : " <<
+ cc.readonly << "UL)";
+ }
+
+ os << ")";
+ }
+
+ os << ";";
+ }
+ else
+ os << "n++;";
+
+ bool block (false);
+
+ // The same logic as in pre().
+ //
+ if (!insert_send_auto_id && auto_ (mi.m))
+ block = true;
+ else if (section_ == 0 && separate_load (mi.m))
+ block = true;
+ else if (inverse (mi.m, key_prefix_) || version (mi.m))
+ block = true;
+ else if (!readonly (*context::top_object))
+ {
+ semantics::class_* c;
+
+ if (id (mi.m) ||
+ readonly (mi.m) ||
+ ((c = composite (mi.t)) && readonly (*c)) ||
+ (section_ == 0 && separate_update (mi.m)))
+ block = true;
+ }
+
+ if (block)
+ os << "}";
+ else
+ os << endl;
+ }
+ }
+
+ virtual void
+ traverse_pointer (member_info& mi)
+ {
+ // Object pointers in views require special treatment.
+ //
+ if (view_member (mi.m))
+ {
+ semantics::class_& c (*mi.ptr);
+ semantics::class_* poly_root (polymorphic (c));
+ bool poly_derived (poly_root != 0 && poly_root != &c);
+
+ os << "object_traits_impl< " << class_fq_name (c) << ", id_" <<
+ db << " >::bind (" << endl
+ << "b + n, " << (poly_derived ? "0, 0, " : "") << arg << "." <<
+ mi.var << "value, sk" << (versioned (c) ? ", svm" : "") << ");";
+ }
+ else
+ member_base_impl<T>::traverse_pointer (mi);
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ os << "composite_value_traits< " << mi.fq_type () << ", id_" <<
+ db << " >::bind (" << endl
+ << "b + n, " << arg << "." << mi.var << "value, sk" <<
+ (versioned (*composite (mi.t)) ? ", svm" : "") << ");";
+ }
+
+ protected:
+ string b;
+ string arg;
+ };
+
+ struct bind_base: traversal::class_, virtual context
+ {
+ typedef bind_base base;
+
+ virtual void
+ traverse (type& c)
+ {
+ bool obj (object (c));
+
+ // Ignore transient bases. Not used for views.
+ //
+ if (!(obj || composite (c)))
+ return;
+
+ os << "// " << class_name (c) << " base" << endl
+ << "//" << endl;
+
+ // If the derived class is readonly, then we will never be
+ // called with sk == statement_update.
+ //
+ bool ro (readonly (c));
+ bool check (ro && !readonly (*context::top_object));
+
+ if (check)
+ os << "if (sk != statement_update)"
+ << "{";
+
+ if (obj)
+ os << "object_traits_impl< ";
+ else
+ os << "composite_value_traits< ";
+
+ os << class_fq_name (c) << ", id_" << db << " >::bind (b + n, i, sk" <<
+ (versioned (c) ? ", svm" : "") << ");";
+
+ column_count_type const& cc (column_count (c));
+
+ os << "n += ";
+
+ // select = total - separate_load
+ // insert = total - inverse - optimistic_managed - id(auto & !sending)
+ // update = total - inverse - optimistic_managed - id - readonly -
+ // separate_update
+ //
+ size_t select (cc.total - cc.separate_load);
+ size_t insert (cc.total - cc.inverse - cc.optimistic_managed);
+ size_t update (insert - cc.id - cc.readonly - cc.separate_update);
+
+ data_member_path* id;
+ if (!insert_send_auto_id && (id = id_member (c)) != 0 && auto_ (*id))
+ insert -= cc.id;
+
+ if (select == insert && insert == update)
+ os << select << "UL;";
+ else if (select != insert && insert == update)
+ os << "sk == statement_select ? " << select << "UL : " <<
+ insert << "UL;";
+ else if (select == insert && insert != update)
+ os << "sk == statement_update ? " << update << "UL : " <<
+ select << "UL;";
+ else
+ os << "sk == statement_select ? " << select << "UL : " <<
+ "sk == statement_insert ? " << insert << "UL : " <<
+ update << "UL;";
+
+ if (check)
+ os << "}";
+ else
+ os << endl;
+ }
+ };
+
+ //
+ // grow
+ //
+
+ struct grow_member: virtual member_base
+ {
+ typedef grow_member base;
+
+ grow_member (size_t& index,
+ string const& var = string (),
+ user_section* section = 0)
+ : member_base (var, 0, 0, string (), string (), section),
+ index_ (index) {}
+
+ grow_member (size_t& index,
+ string const& var,
+ semantics::type& t,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base (var, &t, ct, fq_type, key_prefix), index_ (index) {}
+
+ protected:
+ size_t& index_;
+ };
+
+ template <typename T>
+ struct grow_member_impl: grow_member, virtual member_base_impl<T>
+ {
+ typedef grow_member_impl base_impl;
+
+ grow_member_impl (base const& x)
+ : member_base::base (x), // virtual base
+ base (x) {}
+
+ typedef typename member_base_impl<T>::member_info member_info;
+
+ using member_base_impl<T>::container;
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ if (container (mi))
+ return false;
+
+ // If we have a key prefix (container), then it can't be in a section
+ // (while mi.m can). The same for top-level -- if we got called, then
+ // we shouldn't ignore it. (Same logic as in has_grow_member).
+ //
+ if (!(!key_prefix_.empty () || top_level_ ||
+ (section_ == 0 && !separate_load (mi.m)) ||
+ (section_ != 0 && *section_ == section (mi.m))))
+ return false;
+
+ // Ignore polymorphic id references; they are not returned by
+ // the select statement.
+ //
+ if (mi.ptr != 0 && mi.m.count ("polymorphic-ref"))
+ return false;
+
+ std::ostringstream ostr;
+ ostr << "t[" << index_ << "UL]";
+ e = ostr.str ();
+
+ if (var_override_.empty ())
+ {
+ os << "// " << mi.m.name () << endl
+ << "//" << endl;
+
+ semantics::class_* comp (composite (mi.t));
+
+ // If the member is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (mi.m));
+ unsigned long long dv (deleted (mi.m));
+
+ // If this is a composite member, see if it is summarily
+ // added/deleted.
+ //
+ if (comp != 0)
+ {
+ unsigned long long cav (added (*comp));
+ unsigned long long cdv (deleted (*comp));
+
+ if (cav != 0 && (av == 0 || av < cav))
+ av = cav;
+
+ if (cdv != 0 && (dv == 0 || dv > cdv))
+ dv = cdv;
+ }
+
+ // If the addition/deletion version is the same as the section's,
+ // then we don't need the test.
+ //
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")"
+ << "{";
+ }
+ }
+
+ return true;
+ }
+
+ virtual void
+ post (member_info& mi)
+ {
+ semantics::class_* comp (composite (mi.t));
+
+ if (var_override_.empty ())
+ {
+ unsigned long long av (added (mi.m));
+ unsigned long long dv (deleted (mi.m));
+
+ if (comp != 0)
+ {
+ unsigned long long cav (added (*comp));
+ unsigned long long cdv (deleted (*comp));
+
+ if (cav != 0 && (av == 0 || av < cav))
+ av = cav;
+
+ if (cdv != 0 && (dv == 0 || dv > cdv))
+ dv = cdv;
+ }
+
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if (av != 0 || dv != 0)
+ os << "}";
+ }
+
+ if (mi.ptr != 0 && view_member (mi.m))
+ {
+ // See column_count_impl for details on what's going on here.
+ //
+ column_count_type cc;
+ if (semantics::class_* root = polymorphic (*mi.ptr))
+ {
+ for (semantics::class_* b (mi.ptr);; b = &polymorphic_base (*b))
+ {
+ column_count_type const& ccb (column_count (*b));
+
+ cc.total += ccb.total - (b != root ? ccb.id : 0);
+ cc.separate_load += ccb.separate_load;
+
+ if (b == root)
+ break;
+ }
+ }
+ else
+ cc = column_count (*mi.ptr);
+
+ index_ += cc.total - cc.separate_load;
+ }
+ else if (comp != 0)
+ index_ += column_count (*comp).total;
+ else
+ index_++;
+ }
+
+ virtual void
+ traverse_pointer (member_info& mi)
+ {
+ // Object pointers in views require special treatment. They
+ // can only be immediate members of the view class.
+ //
+ if (view_member (mi.m))
+ {
+ semantics::class_& c (*mi.ptr);
+
+ os << "if (object_traits_impl< " << class_fq_name (c) <<
+ ", id_" << db << " >::grow (" << endl
+ << "i." << mi.var << "value, t + " << index_ << "UL" <<
+ (versioned (c) ? ", svm" : "") << "))" << endl
+ << "grew = true;"
+ << endl;
+ }
+ else
+ member_base_impl<T>::traverse_pointer (mi);
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ semantics::class_& c (*composite (mi.t));
+
+ os << "if (composite_value_traits< " << mi.fq_type () <<
+ ", id_" << db << " >::grow (" << endl
+ << "i." << mi.var << "value, t + " << index_ << "UL" <<
+ (versioned (c) ? ", svm" : "") << "))" << endl
+ << "grew = true;"
+ << endl;
+ }
+
+ protected:
+ string e;
+ };
+
+ struct grow_base: traversal::class_, virtual context
+ {
+ typedef grow_base base;
+
+ grow_base (size_t& index): index_ (index) {}
+
+ virtual void
+ traverse (type& c)
+ {
+ bool obj (object (c));
+
+ // Ignore transient bases. Not used for views.
+ //
+ if (!(obj || composite (c)))
+ return;
+
+ os << "// " << class_name (c) << " base" << endl
+ << "//" << endl;
+
+ os << "if (";
+
+ if (obj)
+ os << "object_traits_impl< ";
+ else
+ os << "composite_value_traits< ";
+
+ os << class_fq_name (c) << ", id_" << db << " >::grow (" << endl
+ << "i, t + " << index_ << "UL" <<
+ (versioned (c) ? ", svm" : "") << "))" << endl
+ << "grew = true;"
+ << endl;
+
+ index_ += column_count (c).total;
+ }
+
+ protected:
+ size_t& index_;
+ };
+
+ //
+ // init image
+ //
+
+ struct init_image_member: virtual member_base
+ {
+ typedef init_image_member base;
+
+ init_image_member (string const& var = string (),
+ string const& member = string (),
+ user_section* section = 0)
+ : member_base (var, 0, 0, string (), string (), section),
+ member_override_ (member)
+ {
+ }
+
+ init_image_member (string const& var,
+ string const& member,
+ semantics::type& t,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base (var, &t, ct, fq_type, key_prefix),
+ member_override_ (member)
+ {
+ }
+
+ protected:
+ string member_override_;
+ };
+
+ template <typename T>
+ struct init_image_member_impl: init_image_member,
+ virtual member_base_impl<T>
+ {
+ typedef init_image_member_impl base_impl;
+
+ init_image_member_impl (base const& x)
+ : base (x),
+ member_database_type_id_ (base::type_override_,
+ base::custom_override_,
+ base::fq_type_override_,
+ base::key_prefix_)
+ {
+ }
+
+ typedef typename member_base_impl<T>::member_info member_info;
+
+ using member_base_impl<T>::container;
+
+ virtual void
+ set_null (member_info&) = 0;
+
+ virtual void
+ check_accessor (member_info&, member_access&) {}
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ // Ignore containers (they get their own table) and inverse
+ // object pointers (they are not present in this binding).
+ //
+ if (container (mi) || inverse (mi.m, key_prefix_))
+ return false;
+
+ if (section_ != 0 && *section_ != section (mi.m))
+ return false;
+
+ // Ignore polymorphic id references; they are initialized in a
+ // special way.
+ //
+ if (mi.ptr != 0 && mi.m.count ("polymorphic-ref"))
+ return false;
+
+ semantics::class_* comp (composite (mi.t));
+
+ if (!member_override_.empty ())
+ {
+ member = member_override_;
+ os << "{";
+ }
+ else
+ {
+ // If we are generating standard init() and this member
+ // contains version, ignore it.
+ //
+ if (version (mi.m))
+ return false;
+
+ // If we don't send auto id in INSERT statement, ignore this
+ // member altogether (we never send auto id in UPDATE).
+ //
+ if (!insert_send_auto_id && auto_ (mi.m))
+ return false;
+
+ os << "// " << mi.m.name () << endl
+ << "//" << endl;
+
+ // If the member is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (mi.m));
+ unsigned long long dv (deleted (mi.m));
+
+ // If this is a composite member, see if it is summarily
+ // added/deleted.
+ //
+ if (comp != 0)
+ {
+ unsigned long long cav (added (*comp));
+ unsigned long long cdv (deleted (*comp));
+
+ if (cav != 0 && (av == 0 || av < cav))
+ av = cav;
+
+ if (cdv != 0 && (dv == 0 || dv > cdv))
+ dv = cdv;
+ }
+
+ // If the addition/deletion version is the same as the section's,
+ // then we don't need the test.
+ //
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")"
+ << "{";
+ }
+
+ // If the whole class is readonly, then we will never be
+ // called with sk == statement_update.
+ //
+ if (!readonly (*context::top_object))
+ {
+ if (id (mi.m) ||
+ readonly (mi.m) ||
+ (section_ == 0 && separate_update (mi.m)) ||
+ (comp != 0 && readonly (*comp))) // Can't be id.
+ {
+ // If we are generating section init(), then sk can only be
+ // statement_update.
+ //
+ if (section_ == 0)
+ os << "if (sk == statement_insert)";
+ }
+ }
+
+ os << "{";
+
+ if (discriminator (mi.m))
+ member = "di.discriminator";
+ else
+ {
+ // Get the member using the accessor expression.
+ //
+ member_access& ma (mi.m.template get<member_access> ("get"));
+
+ // Make sure this kind of member can be accessed with this
+ // kind of accessor (database-specific, e.g., streaming).
+ //
+ if (comp == 0)
+ check_accessor (mi, ma);
+
+ // If this is not a synthesized expression, then output
+ // its location for easier error tracking.
+ //
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ // Use the original type to form the const reference. VC++
+ // cannot grok the constructor syntax.
+ //
+ os << member_ref_type (mi.m, true, "v") << " =" << endl
+ << " " << ma.translate ("o") << ";"
+ << endl;
+
+ member = "v";
+ }
+ }
+
+ // Translate.
+ //
+ if (mi.ct != 0)
+ {
+ os << "// From " << location_string (mi.ct->loc, true) << endl
+ << type_ref_type (*mi.ct->as, mi.ct->as_hint, true, "vt") <<
+ " =" << endl
+ << " " << mi.ct->translate_to (member) << ";"
+ << endl;
+
+ member = "vt";
+ }
+
+ // If this is a wrapped composite value, then we need to "unwrap"
+ // it. If this is a NULL wrapper, then we also need to handle that.
+ // For simple values this is taken care of by the value_traits
+ // specializations.
+ //
+ if (mi.wrapper != 0 && comp != 0)
+ {
+ // The wrapper type, not the wrapped type.
+ //
+ string const& wt (mi.fq_type (false));
+
+ // If this is a NULL wrapper and the member can be NULL, then
+ // we need to handle the NULL value.
+ //
+ if (null (mi.m, key_prefix_) &&
+ mi.wrapper->template get<bool> ("wrapper-null-handler"))
+ {
+ os << "if (wrapper_traits< " << wt << " >::get_null (" <<
+ member << "))" << endl
+ << "composite_value_traits< " << mi.fq_type () << ", id_" <<
+ db << " >::set_null (" << endl
+ << "i." << mi.var << "value, sk" <<
+ (versioned (*comp) ? ", svm" : "") << ");"
+ << "else"
+ << "{";
+ }
+
+ os << "const" << mi.fq_type () << "& vw = " << endl
+ << " wrapper_traits< " + wt + " >::get_ref (" + member + ");"
+ << endl;
+
+ member = "vw";
+ }
+
+ if (discriminator (mi.m))
+ os << "const info_type& di (map->find (typeid (o)));"
+ << endl;
+
+ if (mi.ptr != 0)
+ {
+ // When handling a pointer, mi.t is the id type of the referenced
+ // object.
+ //
+ semantics::type& pt (utype (mi.m, key_prefix_));
+
+ type = "obj_traits::id_type";
+
+ // Handle NULL pointers and extract the id.
+ //
+ os << "typedef object_traits< " << class_fq_name (*mi.ptr) <<
+ " > obj_traits;";
+
+ if (weak_pointer (pt))
+ {
+ os << "typedef odb::pointer_traits< " << mi.ptr_fq_type () <<
+ " > wptr_traits;"
+ << "typedef odb::pointer_traits< wptr_traits::" <<
+ "strong_pointer_type > ptr_traits;"
+ << endl
+ << "wptr_traits::strong_pointer_type sp (" <<
+ "wptr_traits::lock (" << member << "));";
+
+ member = "sp";
+ }
+ else
+ os << "typedef odb::pointer_traits< " << mi.ptr_fq_type () <<
+ " > ptr_traits;"
+ << endl;
+
+ os << "bool is_null (ptr_traits::null_ptr (" << member << "));"
+ << "if (!is_null)"
+ << "{"
+ << "const " << type << "& ptr_id (" << endl;
+
+ if (lazy_pointer (pt))
+ os << "ptr_traits::object_id< ptr_traits::element_type > (" <<
+ member << ")";
+ else
+ os << "obj_traits::id (ptr_traits::get_ref (" << member << "))";
+
+ os << ");"
+ << endl;
+
+ member = "ptr_id";
+ }
+ else if (comp != 0)
+ type = mi.fq_type ();
+ else
+ {
+ type = mi.fq_type ();
+
+ // Indicate to the value_traits whether this column can be NULL.
+ //
+ os << "bool is_null (" << null (mi.m, key_prefix_) << ");";
+ }
+
+ if (comp != 0)
+ traits = "composite_value_traits< " + type + ", id_" +
+ db.string () + " >";
+ else
+ {
+ db_type_id = member_database_type_id_->database_type_id (mi.m);
+ traits = db.string () + "::value_traits<\n "
+ + type + ",\n "
+ + db_type_id + " >";
+ }
+
+ return true;
+ }
+
+ virtual void
+ post (member_info& mi)
+ {
+ semantics::class_* comp (composite (mi.t));
+
+ if (mi.ptr != 0)
+ {
+ os << "}"
+ << "else" << endl;
+
+ if (!null (mi.m, key_prefix_))
+ os << "throw null_pointer ();";
+ else if (comp != 0)
+ os << traits << "::set_null (i." << mi.var << "value, sk" <<
+ (versioned (*comp) ? ", svm" : "") << ");";
+ else
+ set_null (mi);
+ }
+
+ if (mi.wrapper != 0 && comp != 0)
+ {
+ if (null (mi.m, key_prefix_) &&
+ mi.wrapper->template get<bool> ("wrapper-null-handler"))
+ os << "}";
+ }
+
+ os << "}";
+
+ if (member_override_.empty ())
+ {
+ unsigned long long av (added (mi.m));
+ unsigned long long dv (deleted (mi.m));
+
+ if (comp != 0)
+ {
+ unsigned long long cav (added (*comp));
+ unsigned long long cdv (deleted (*comp));
+
+ if (cav != 0 && (av == 0 || av < cav))
+ av = cav;
+
+ if (cdv != 0 && (dv == 0 || dv > cdv))
+ dv = cdv;
+ }
+
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if (av != 0 || dv != 0)
+ os << "}";
+ }
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ bool grow (generate_grow &&
+ context::grow (mi.m, mi.t, mi.ct, key_prefix_));
+
+ if (grow)
+ os << "if (";
+
+ os << traits << "::init (" << endl
+ << "i." << mi.var << "value," << endl
+ << member << "," << endl
+ << "sk";
+
+ if (versioned (*composite (mi.t)))
+ os << "," << endl
+ << "svm";
+
+ os << ")";
+
+ if (grow)
+ os << ")" << endl
+ << "grew = true";
+
+ os << ";";
+ }
+
+ protected:
+ string type;
+ string db_type_id;
+ string member;
+ string traits;
+
+ instance<member_database_type_id> member_database_type_id_;
+ };
+
+ struct init_image_base: traversal::class_, virtual context
+ {
+ typedef init_image_base base;
+
+ virtual void
+ traverse (type& c)
+ {
+ bool obj (object (c));
+
+ // Ignore transient bases. Not used for views.
+ //
+ if (!(obj || composite (c)))
+ return;
+
+ os << "// " << class_name (c) << " base" << endl
+ << "//" << endl;
+
+ // If the derived class is readonly, then we will never be
+ // called with sk == statement_update.
+ //
+ bool check (readonly (c) && !readonly (*context::top_object));
+
+ if (check)
+ os << "if (sk != statement_update)"
+ << "{";
+
+ if (generate_grow)
+ os << "if (";
+
+ if (obj)
+ os << "object_traits_impl< ";
+ else
+ os << "composite_value_traits< ";
+
+ os << class_fq_name (c) << ", id_" << db << " >::init (i, o, sk" <<
+ (versioned (c) ? ", svm" : "") << ")";
+
+ if (generate_grow)
+ os << ")" << endl
+ << "grew = true";
+
+ os << ";";
+
+ if (check)
+ os << "}";
+ else
+ os << endl;
+ }
+ };
+
+ //
+ // init value
+ //
+
+ struct init_value_member: virtual member_base
+ {
+ typedef init_value_member base;
+
+ init_value_member (string const& member = string (),
+ string const& var = string (),
+ bool ignore_implicit_discriminator = true,
+ user_section* section = 0)
+ : member_base (var, 0, 0, string (), string (), section),
+ member_override_ (member),
+ ignore_implicit_discriminator_ (ignore_implicit_discriminator)
+ {
+ }
+
+ init_value_member (string const& var,
+ string const& member,
+ semantics::type& t,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base (var, &t, ct, fq_type, key_prefix),
+ member_override_ (member),
+ ignore_implicit_discriminator_ (true)
+ {
+ }
+
+ virtual void
+ get_null (string const& /*var*/) const {};
+
+ protected:
+ string member_override_;
+ bool ignore_implicit_discriminator_;
+ };
+
+ template <typename T>
+ struct init_value_member_impl: init_value_member,
+ virtual member_base_impl<T>
+ {
+ typedef init_value_member_impl base_impl;
+
+ init_value_member_impl (base const& x)
+ : base (x),
+ member_database_type_id_ (base::type_override_,
+ base::custom_override_,
+ base::fq_type_override_,
+ base::key_prefix_)
+ {
+ }
+
+ typedef typename member_base_impl<T>::member_info member_info;
+
+ using member_base_impl<T>::container;
+
+ virtual void
+ get_null (string const& var) const = 0;
+
+ virtual void
+ check_modifier (member_info&, member_access&) {}
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ if (container (mi))
+ return false;
+
+ if (section_ != 0 && *section_ != section (mi.m))
+ return false;
+
+ // Ignore polymorphic id references; they are initialized in a
+ // special way.
+ //
+ if (mi.ptr != 0 && mi.m.count ("polymorphic-ref"))
+ return false;
+
+ // Ignore implicit discriminators.
+ //
+ if (ignore_implicit_discriminator_ && discriminator (mi.m))
+ return false;
+
+ semantics::class_* comp (composite (mi.t));
+
+ if (!member_override_.empty ())
+ {
+ os << "{";
+ member = member_override_;
+ }
+ else
+ {
+ // Ignore separately loaded members.
+ //
+ if (section_ == 0 && separate_load (mi.m))
+ return false;
+
+ os << "// " << mi.m.name () << endl
+ << "//" << endl;
+
+ // If the member is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (mi.m));
+ unsigned long long dv (deleted (mi.m));
+
+ // If this is a composite member, see if it is summarily
+ // added/deleted.
+ //
+ if (comp != 0)
+ {
+ unsigned long long cav (added (*comp));
+ unsigned long long cdv (deleted (*comp));
+
+ if (cav != 0 && (av == 0 || av < cav))
+ av = cav;
+
+ if (cdv != 0 && (dv == 0 || dv > cdv))
+ dv = cdv;
+ }
+
+ // If the addition/deletion version is the same as the section's,
+ // then we don't need the test.
+ //
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")";
+ }
+
+ os << "{";
+
+ if (mi.ptr != 0 && view_member (mi.m))
+ return true; // That's enough for the object pointer in view.
+
+ // Set the member using the modifier expression.
+ //
+ member_access& ma (mi.m.template get<member_access> ("set"));
+
+ // Make sure this kind of member can be modified with this
+ // kind of accessor (database-specific, e.g., streaming).
+ //
+ if (comp == 0)
+ check_modifier (mi, ma);
+
+ // If this is not a synthesized expression, then output
+ // its location for easier error tracking.
+ //
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ // See if we are modifying via a reference or proper modifier.
+ //
+ if (ma.placeholder ())
+ os << member_val_type (mi.m, false, "v") << ";"
+ << endl;
+ else
+ {
+ // Use the original type to form the reference. VC++ cannot
+ // grok the constructor syntax.
+ //
+ os << member_ref_type (mi.m, false, "v") << " =" << endl
+ << " ";
+
+ // If this member is const and we have a synthesized direct
+ // access, then cast away constness. Otherwise, we assume
+ // that the user-provided expression handles this.
+ //
+ bool cast (mi.cq && ma.direct ());
+ if (cast)
+ os << "const_cast< " << member_ref_type (mi.m, false) <<
+ " > (" << endl;
+
+ os << ma.translate ("o");
+
+ if (cast)
+ os << ")";
+
+ os << ";"
+ << endl;
+ }
+
+ member = "v";
+ }
+
+ // Translate.
+ //
+ if (mi.ct != 0)
+ {
+ os << type_val_type (*mi.ct->as, mi.ct->as_hint, false, "vt") << ";"
+ << endl;
+
+ translate_member = member;
+ member = "vt";
+ }
+
+ // If this is a wrapped composite value, then we need to "unwrap" it.
+ // If this is a NULL wrapper, then we also need to handle that. For
+ // simple values this is taken care of by the value_traits
+ // specializations.
+ //
+ if (mi.wrapper != 0 && comp != 0)
+ {
+ // The wrapper type, not the wrapped type.
+ //
+ string const& wt (mi.fq_type (false));
+
+ // If this is a NULL wrapper and the member can be NULL, then
+ // we need to handle the NULL value.
+ //
+ if (null (mi.m, key_prefix_) &&
+ mi.wrapper->template get<bool> ("wrapper-null-handler"))
+ {
+ os << "if (composite_value_traits< " << mi.fq_type () <<
+ ", id_" << db << " >::get_null (" << endl
+ << "i." << mi.var << "value" <<
+ (versioned (*comp) ? ", svm" : "") << "))" << endl
+ << "wrapper_traits< " << wt << " >::set_null (" << member + ");"
+ << "else"
+ << "{";
+ }
+
+ os << mi.fq_type () << "& vw =" << endl
+ << " wrapper_traits< " + wt + " >::set_ref (" + member + ");"
+ << endl;
+
+ wrap_member = member;
+ member = "vw";
+ }
+
+ if (mi.ptr != 0)
+ {
+ type = "obj_traits::id_type";
+
+ // Handle NULL pointers and extract the id.
+ //
+ os << "typedef object_traits< " << class_fq_name (*mi.ptr) <<
+ " > obj_traits;"
+ << "typedef odb::pointer_traits< " << mi.ptr_fq_type () <<
+ " > ptr_traits;"
+ << endl;
+
+ os << "if (";
+
+ if (comp != 0)
+ os << "composite_value_traits< " << type << ", id_" << db <<
+ " >::get_null (" << endl
+ << "i." << mi.var << "value" <<
+ (versioned (*comp) ? ", svm" : "") << ")";
+ else
+ get_null (mi.var);
+
+ os << ")" << endl;
+
+ // Don't throw null_pointer if we can't have NULLs and the pointer
+ // is NULL since this can be useful during migration. Instead, we
+ // rely on the database enforcing this.
+ //
+ os << member << " = ptr_traits::pointer_type ();";
+
+ os << "else"
+ << "{";
+
+ os << type << " ptr_id;";
+
+ member = "ptr_id";
+ }
+ else
+ type = mi.fq_type ();
+
+ if (comp != 0)
+ traits = "composite_value_traits< " + type + ", id_" +
+ db.string () + " >";
+ else
+ {
+ db_type_id = member_database_type_id_->database_type_id (mi.m);
+ traits = db.string () + "::value_traits<\n "
+ + type + ",\n "
+ + db_type_id + " >";
+ }
+
+ return true;
+ }
+
+ virtual void
+ post (member_info& mi)
+ {
+ if (mi.ptr != 0)
+ {
+ if (view_member (mi.m))
+ {
+ // The object pointer in view doesn't need any of this.
+ os << "}";
+ return;
+ }
+
+ // Restore the member variable name.
+ //
+ member = member_override_.empty () ? "v" : member_override_;
+
+ // When handling a pointer, mi.t is the id type of the referenced
+ // object.
+ //
+ semantics::type& pt (utype (mi.m, key_prefix_));
+
+ if (lazy_pointer (pt))
+ os << member << " = ptr_traits::pointer_type (" << endl
+ << "*static_cast<" << db << "::database*> (db), ptr_id);";
+ else
+ {
+ os << "// If a compiler error points to the line below, then" << endl
+ << "// it most likely means that a pointer used in a member" << endl
+ << "// cannot be initialized from an object pointer." << endl
+ << "//" << endl
+ << member << " = ptr_traits::pointer_type (" << endl
+ << "static_cast<" << db << "::database*> (db)->load<" << endl
+ << " obj_traits::object_type > (ptr_id));";
+
+ // If we are loading into an eager weak pointer, make sure there
+ // is someone else holding a strong pointer to it (normally a
+ // session). Otherwise, the object will be loaded and immediately
+ // deleted. Besides not making much sense, this also breaks the
+ // delayed loading machinery which expects the object to be around
+ // at least until the top-level load() returns.
+ //
+ if (weak_pointer (pt))
+ {
+ os << endl
+ << "if (odb::pointer_traits<" <<
+ "ptr_traits::strong_pointer_type>::null_ptr (" << endl
+ << "ptr_traits::lock (" << member << ")))" << endl
+ << "throw session_required ();";
+ }
+ }
+
+ os << "}";
+ }
+
+ // Wrap back (so to speak).
+ //
+ if (mi.wrapper != 0 && composite (mi.t) != 0)
+ {
+ if (null (mi.m, key_prefix_) &&
+ mi.wrapper->template get<bool> ("wrapper-null-handler"))
+ os << "}";
+
+ member = wrap_member;
+ }
+
+ // Untranslate.
+ //
+ if (mi.ct != 0)
+ {
+ //@@ Use move() in C++11? Or not.
+ //
+ os << "// From " << location_string (mi.ct->loc, true) << endl
+ << translate_member << " = " <<
+ mi.ct->translate_from (member) << ";";
+
+ member = translate_member;
+ }
+
+ // Call the modifier if we are using a proper one.
+ //
+ if (member_override_.empty ())
+ {
+ member_access& ma (mi.m.template get<member_access> ("set"));
+
+ if (ma.placeholder ())
+ {
+ // If this is not a synthesized expression, then output its
+ // location for easier error tracking.
+ //
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << ma.translate (
+ "o", "v", "*static_cast<" + db.string () + "::database*> (db)")
+ << ";";
+ }
+ }
+
+ os << "}";
+ }
+
+ virtual void
+ traverse_pointer (member_info& mi)
+ {
+ // Object pointers in views require special treatment.
+ //
+ if (view_member (mi.m))
+ {
+ // This is the middle part. The pre and post parts are generated
+ // by init_view_pointer_member below.
+ //
+ using semantics::class_;
+
+ class_& c (*mi.ptr);
+ class_* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+
+ string o_tp (mi.var + "object_type");
+ string o_tr (mi.var + "object_traits");
+ string r_tr (poly_derived ? mi.var + "root_traits" : o_tr);
+ string i_tp (mi.var + "info_type");
+
+ string id (mi.var + "id");
+ string o (mi.var + "o");
+ string pi (mi.var + "pi"); // Polymorphic type info.
+
+ // If load_() will be loading containers or the rest of the
+ // polymorphic object, then we need to perform several extra
+ // things. We need to initialize the id image in the object
+ // statements. We also have to lock the statements so that
+ // nobody messes up this id image.
+ //
+ bool init_id (
+ poly ||
+ has_a (c, test_container | include_eager_load, &main_section));
+
+ bool versioned (context::versioned (c));
+
+ os << "if (" << o << " != 0)"
+ << "{";
+
+ if (poly)
+ os << "callback_event ce (callback_event::pre_load);"
+ << pi << "->dispatch (" << i_tp << "::call_callback, " <<
+ "*db, " << o << ", &ce);";
+ else
+ os << o_tr << "::callback (*db, *" << o <<
+ ", callback_event::pre_load);";
+
+ os << o_tr << "::init (*" << o << ", i." << mi.var << "value, db" <<
+ (versioned ? ", svm" : "") << ");";
+
+ // Call load_() to load the rest of the object (containers, etc).
+ //
+ if (id_member (poly ? *poly_root : c) != 0)
+ {
+ const char* s (poly_derived ? "osts" : "sts");
+
+ os << o_tr << "::statements_type& " << s << " (" << endl
+ << "conn.statement_cache ().find_object<" << o_tp << "> ());";
+
+ if (poly_derived)
+ os << r_tr << "::statements_type& sts (osts.root_statements ());";
+
+ if (init_id)
+ {
+ // This can only be top-level call so lock must succeed.
+ //
+ os << r_tr << "::statements_type::auto_lock l (sts);"
+ << "assert (l.locked ()) /* Must be a top-level call. */;"
+ << endl
+ << r_tr << "::id_image_type& i (sts.id_image ());"
+ << r_tr << "::init (i, " << id << ");"
+ << db << "::binding& idb (sts.id_image_binding ());"
+ << "if (i.version != sts.id_image_version () || " <<
+ "idb.version == 0)"
+ << "{"
+ << r_tr << "::bind (idb.bind, i);"
+ << "sts.id_image_version (i.version);"
+ << "idb.version++;";
+ if (optimistic (poly ? *poly_root : c) != 0)
+ os << "sts.optimistic_id_image_binding ().version++;";
+ os << "}";
+ }
+
+ os << o_tr << "::load_ (" << s << ", *" << o << ", false" <<
+ (versioned ? ", svm" : "") << ");";
+
+ // Load the dynamic part of the object unless static and dynamic
+ // types are the same.
+ //
+ if (poly)
+ os << endl
+ << "if (" << pi << " != &" << o_tr << "::info)"
+ << "{"
+ << "std::size_t d (" << o_tr << "::depth);"
+ << pi << "->dispatch (" << i_tp << "::call_load, *db, " <<
+ o << ", &d);"
+ << "}";
+
+ if (init_id)
+ os << "sts.load_delayed (" << (versioned ? "&svm" : "0") << ");"
+ << "l.unlock ();";
+ }
+
+ os << "}";
+ }
+ else
+ member_base_impl<T>::traverse_pointer (mi);
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ os << traits << "::init (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "db";
+
+ if (versioned (*composite (mi.t)))
+ os << "," << endl
+ << "svm";
+
+ os << ");"
+ << endl;
+ }
+
+ protected:
+ string type;
+ string db_type_id;
+ string traits;
+ string member;
+ string translate_member; // Untranslated member.
+ string wrap_member; // Wrapped member.
+
+ instance<member_database_type_id> member_database_type_id_;
+ };
+
+ // This class generates the pre and post parts. The middle part is
+ // generated by init_value_member above.
+ //
+ struct init_view_pointer_member: virtual member_base,
+ member_base_impl<bool> // Dummy SQL type.
+ {
+ typedef init_view_pointer_member base;
+
+ init_view_pointer_member (bool pre, init_value_member const& ivm)
+ : member_base (0, 0, string (), string (), 0),
+ pre_ (pre), init_value_member_ (ivm) {}
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ // Only interested in object pointers inside views.
+ //
+ return mi.ptr != 0 && view_member (mi.m);
+ }
+
+ virtual void
+ traverse_pointer (member_info& mi)
+ {
+ using semantics::class_;
+
+ class_& c (*mi.ptr);
+ bool abst (abstract (c));
+ class_* poly_root (polymorphic (c));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c);
+ size_t poly_depth (poly_derived ? polymorphic_depth (c) : 1);
+
+ data_member_path* idm (id_member (poly ? *poly_root : c));
+
+ os << "// " << mi.m.name () << (pre_ ? " pre" : " post") << endl
+ << "//" << endl;
+
+ string o_tp (mi.var + "object_type");
+ string o_tr (mi.var + "object_traits");
+ string r_tr (poly_derived ? mi.var + "root_traits" : o_tr);
+ string i_tp (mi.var + "info_type");
+ string p_tp (mi.var + "pointer_type");
+ string p_tr (mi.var + "pointer_traits");
+ string c_tr (mi.var + "cache_traits");
+
+ string id (mi.var + "id"); // Object id.
+ string p (mi.var + "p"); // Object pointer.
+ string pg (mi.var + "pg"); // Pointer guard.
+ string ig (mi.var + "ig"); // Cache insert guard.
+ string o (mi.var + "o"); // Object.
+ string pi (mi.var + "pi"); // Polymorphic type info.
+
+ bool op_raw (c.get<bool> ("object-pointer-raw"));
+ bool mp_raw (utype (mi.m).is_a<semantics::pointer> ());
+
+ // Output aliases and variables before any schema version if-
+ // blocks since we need to be able to access them across all
+ // three parts.
+ //
+ if (pre_)
+ {
+ os << "typedef " << class_fq_name (c) << " " << o_tp << ";"
+ << "typedef object_traits_impl<" << o_tp << ", id_" << db <<
+ "> " << o_tr << ";";
+
+ if (poly_derived)
+ os << "typedef " << o_tr << "::root_traits " << r_tr << ";";
+
+ if (poly)
+ os << "typedef " << r_tr << "::info_type " << i_tp << ";";
+
+ os << "typedef " << r_tr << "::pointer_type " << p_tp << ";"
+ << "typedef " << r_tr << "::pointer_traits " << p_tr << ";";
+ if (idm != 0)
+ os << "typedef " << r_tr << "::pointer_cache_traits " <<
+ c_tr << ";";
+ os << endl;
+
+ if (idm != 0)
+ os << r_tr << "::id_type " << id << ";";
+ os << p_tp << " " << p << (op_raw ? " = 0" : "") << ";" // VC++
+ << p_tr << "::guard " << pg << ";";
+ if (idm != 0)
+ os << c_tr << "::insert_guard " << ig << ";";
+ os << o_tp << "* " << o << " (0);";
+
+ if (poly)
+ os << "const " << i_tp << "* " << pi << " = 0;"; // VC++
+
+ os << endl;
+ }
+
+ // If the member is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (mi.m));
+ unsigned long long dv (deleted (mi.m));
+
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")";
+ }
+
+ os << "{";
+
+ if (pre_)
+ {
+ string id_im;
+ if (idm != 0)
+ {
+ // Check for NULL.
+ //
+ string id_var;
+ {
+ id_im = mi.var + "value";
+
+ // In a polymorphic class, the id is in the root image.
+ //
+ for (size_t i (0); i < poly_depth - 1; ++i)
+ id_im += (i == 0 ? ".base" : "->base");
+
+ string n;
+ for (data_member_path::const_iterator i (idm->begin ());
+ i != idm->end ();
+ ++i)
+ {
+ // The same logic as in member_base.
+ //
+ if (!n.empty ())
+ n += "value."; // Composite.
+
+ string const& name ((*i)->name ());
+ n += name;
+
+ if (n[n.size () - 1] != '_')
+ n += '_';
+ }
+
+ id_var = id_im + (poly_derived ? "->" : ".") + n;
+ id_im = (poly_derived ? "*i." : "i.") + id_im;
+ }
+
+ os << "if (";
+
+ if (semantics::class_* comp = composite (mi.t))
+ os << "!composite_value_traits< " << o_tr << "::id_type, id_" <<
+ db << " >::get_null (" << endl
+ << "i." << id_var << "value" <<
+ (versioned (*comp) ? ", svm" : "") << ")";
+ else
+ {
+ os << "!(";
+ init_value_member_.get_null (id_var);
+ os << ")";
+ }
+
+ os << ")"
+ << "{";
+
+ // Check cache.
+ //
+ os << id << " = " << r_tr << "::id (" << id_im << ");"
+ << p << " = " << c_tr << "::find (*db, " << id << ");"
+ << endl;
+
+ os << "if (" << p_tr << "::null_ptr (" << p << "))"
+ << "{";
+ }
+
+ // To support by-value object loading, we are going to load
+ // into an existing instance if the pointer is already not
+ // NULL. To limit the potential misuse (especially when it
+ // comes to sessions), we are going to limit this support
+ // only to raw pointers. Furthermore, we will only insert
+ // such an object into the cache if its object pointer is
+ // also raw.
+ //
+ if (mp_raw && !poly)
+ {
+ // Get the member using the accessor expression.
+ //
+ member_access& ma (mi.m.get<member_access> ("get"));
+
+ // If this is not a synthesized expression, then output
+ // its location for easier error tracking.
+ //
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ // Use the original type to form the const reference. VC++
+ // cannot grok the constructor syntax.
+ //
+ os << member_ref_type (mi.m, true, "m") << " =" << endl
+ << " " << ma.translate ("o") << ";"
+ << endl;
+
+ os << "if (m != 0)"
+ << "{";
+
+ if (op_raw)
+ os << ig << ".reset (" << c_tr << "::insert (*db, " << id <<
+ ", m));";
+
+ os << o << " = m;"
+ << "}"
+ << "else"
+ << "{";
+ }
+
+ if (poly)
+ {
+ os << r_tr << "::discriminator_type d (" << endl
+ << r_tr << "::discriminator (" << id_im << "));";
+
+ if (abst)
+ os << pi << " = &" << r_tr << "::map->find (d);";
+ else
+ os << pi << " = (d == " << o_tr << "::info.discriminator" << endl
+ << "? &" << o_tr << "::info" << endl
+ << ": &" << r_tr << "::map->find (d));";
+
+ os << p << " = " << pi << "->create ();";
+ }
+ else
+ os << p << " = object_factory<" << o_tp << ", " << p_tp <<
+ ">::create ();";
+
+ os << pg << ".reset (" << p << ");";
+ if (idm != 0)
+ os << ig << ".reset (" << c_tr << "::insert (*db, " << id <<
+ ", " << p << "));";
+
+ if (poly_derived)
+ os << o << " = static_cast<" << o_tp << "*> (" << p_tr <<
+ "::get_ptr (" << p << "));";
+ else
+ os << o << " = " << p_tr << "::get_ptr (" << p << ");";
+
+ if (mp_raw && !poly)
+ os << "}";
+
+ if (idm != 0)
+ os << "}" // Cache.
+ << "}"; // NULL.
+ }
+ else
+ {
+ os << "if (" << o << " != 0)"
+ << "{";
+
+ if (poly)
+ os << "callback_event ce (callback_event::post_load);"
+ << pi << "->dispatch (" << i_tp << "::call_callback, " <<
+ "*db, " << o << ", &ce);";
+ else
+ os << o_tr << "::callback (*db, *" << o <<
+ ", callback_event::post_load);";
+
+ if (idm != 0)
+ {
+ if (mp_raw && !op_raw && !poly)
+ os << "if (!" << p_tr << "::null_ptr (" << p << "))"
+ << "{";
+
+ os << c_tr << "::load (" << ig << ".position ());"
+ << ig << ".release ();";
+
+ if (mp_raw && !op_raw && !poly)
+ os << "}";
+ }
+
+ os << pg << ".release ();";
+
+ os << "}";
+
+ // If member pointer is not raw, then result is in p.
+ // If both member and object are raw, then result is in o.
+ // If member is raw but object is not, then result is in
+ // p if it is not NULL, and in o (either NULL or the same
+ // as the member value) otherwise.
+ //
+ member_access& ma (mi.m.get<member_access> ("set"));
+
+ if (ma.empty () && !poly)
+ {
+ // It is ok to have empty modifier expression as long as
+ // the member pointer is raw. This is the by-value load
+ // and the user is not interested in learning whether the
+ // object is NULL.
+ //
+ if (!mp_raw)
+ {
+ error (ma.loc) << "non-empty modifier expression required " <<
+ "for loading an object via a smart pointer" << endl;
+ throw operation_failed ();
+ }
+
+ os << "// Empty modifier expression was specified for this\n"
+ << "// object so make sure we have actually loaded the\n"
+ << "// data into the existing instance rather than, say,\n"
+ << "// finding the object in the cache or creating a new one.\n"
+ << "//\n"
+ << "assert (" << p_tr << "::null_ptr (" << p << "));";
+ }
+ else
+ {
+ if (!(mp_raw && op_raw) || poly)
+ {
+ string r (options.std () >= cxx_version::cxx11
+ ? "std::move (" + p + ")"
+ : p);
+
+ if (poly_derived)
+ // This pointer could have come from cache, so use dynamic
+ // cast.
+ //
+ r = p_tr + "::dynamic_pointer_cast<" + o_tp + "> (\n" +
+ r + ")";
+
+ // Unless the pointer is raw, explicitly construct the
+ // smart pointer from the object pointer so that we get
+ // the behavior similar to calling database::load() (in
+ // both cases we are the "ownership end-points"; unless
+ // the object was already in the session before loading
+ // this view (in which case using raw pointers as object
+ // pointers is a really stupid idea), this logic will do
+ // the right thing and what the user most likely expects.
+ //
+ if (!mp_raw)
+ r = member_val_type (mi.m, false) + " (\n" + r + ")";
+
+ if (mp_raw && !op_raw)
+ os << "if (!" << p_tr << "::null_ptr (" << p << "))" << endl;
+
+ os << "// If a compiler error points to the line below, then\n"
+ << "// it most likely means that a pointer used in view\n"
+ << "// member cannot be initialized from an object pointer.\n"
+ << "//" << endl;
+
+ set_member (mi.m, "o", r, "db");
+ }
+
+ if (mp_raw && !poly)
+ {
+ if (!op_raw)
+ os << "else" << endl; // NULL p
+
+ set_member (mi.m, "o", o, "db");
+ }
+ }
+ }
+
+ os << "}";
+ }
+
+ virtual bool const&
+ member_sql_type (semantics::data_member&) {return pre_;};
+
+ protected:
+ bool pre_;
+ init_value_member const& init_value_member_;
+ };
+
+ struct init_value_base: traversal::class_, virtual context
+ {
+ typedef init_value_base base;
+
+ virtual void
+ traverse (type& c)
+ {
+ bool obj (object (c));
+
+ // Ignore transient bases. Not used for views.
+ //
+ if (!(obj || composite (c)))
+ return;
+
+ os << "// " << class_name (c) << " base" << endl
+ << "//" << endl;
+
+ if (obj)
+ os << "object_traits_impl< ";
+ else
+ os << "composite_value_traits< ";
+
+ os << class_fq_name (c) << ", id_" << db << " >::init (o, i, db" <<
+ (versioned (c) ? ", svm" : "") << ");"
+ << endl;
+ }
+ };
+
+ // Member-specific traits types for container members.
+ //
+ struct container_traits: object_members_base, virtual context
+ {
+ typedef container_traits base;
+
+ container_traits (semantics::class_& c)
+ : object_members_base (
+ true,
+ object (c), // Only build table prefix for objects.
+ false),
+ c_ (c)
+ {
+ scope_ = object (c)
+ ? "access::object_traits_impl< "
+ : "access::composite_value_traits< ";
+
+ scope_ += class_fq_name (c) + ", id_" + db.string () + " >";
+ }
+
+ // Unless the database system can execute several interleaving
+ // statements, cache the result set.
+ //
+ virtual void
+ cache_result (string const& statement)
+ {
+ os << statement << ".cache ();";
+ }
+
+ // Additional code that need to be executed following the call to
+ // init_value.
+ //
+ virtual void
+ init_value_extra ()
+ {
+ }
+
+ virtual void
+ process_statement_columns (statement_columns&,
+ statement_kind,
+ bool /*dynamic*/)
+ {
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&)
+ {
+ // We don't want to traverse composite id.
+ }
+
+ virtual void
+ traverse_composite (semantics::data_member* m, semantics::class_& c)
+ {
+ if (object (c_))
+ object_members_base::traverse_composite (m, c);
+ else
+ {
+ // If we are generating traits for a composite value type, then
+ // we don't want to go into its bases or it composite members.
+ //
+ if (m == 0 && &c == &c_)
+ names (c);
+ }
+ }
+
+ virtual void
+ container_extra (semantics::data_member&, semantics::type&)
+ {
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type& t)
+ {
+ using semantics::type;
+
+ // Figure out if this member is from a base object or composite
+ // value and if it's from an object, whether it is reuse-abstract.
+ //
+ bool base, reuse_abst;
+
+ if (object (c_))
+ {
+ base = cur_object != &c_ ||
+ !object (dynamic_cast<type&> (m.scope ()));
+ reuse_abst = abstract (c_) && !polymorphic (c_);
+ }
+ else
+ {
+ base = false; // We don't go into bases.
+ reuse_abst = true; // Always abstract.
+ }
+
+ container_kind_type ck (container_kind (t));
+
+ const custom_cxx_type* vct (0);
+ const custom_cxx_type* ict (0);
+ const custom_cxx_type* kct (0);
+
+ type& vt (container_vt (m, &vct));
+ type* it (0);
+ type* kt (0);
+
+ data_member_path* imp (context::inverse (m, "value"));
+
+ bool ordered (false);
+ bool inverse (imp != 0);
+ bool grow (false);
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (!unordered (m))
+ {
+ it = &container_it (m, &ict);
+ ordered = true;
+
+ if (generate_grow)
+ grow = grow || context::grow (m, *it, ict, "index");
+ }
+
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ kt = &container_kt (m, &kct);
+
+ if (generate_grow)
+ grow = grow || context::grow (m, *kt, kct, "key");
+
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ bool smart (!inverse &&
+ (ck != ck_ordered || ordered) &&
+ container_smart (t));
+
+ if (generate_grow)
+ grow = grow || context::grow (m, vt, vct, "value");
+
+ bool eager_ptr (is_a (member_path_,
+ member_scope_,
+ test_eager_pointer,
+ vt,
+ "value"));
+ if (!eager_ptr)
+ {
+ if (semantics::class_* cvt = composite_wrapper (vt))
+ eager_ptr = has_a (*cvt, test_eager_pointer);
+ }
+
+ bool versioned (context::versioned (m));
+
+ string name (flat_prefix_ + public_name (m) + "_traits");
+ string scope (scope_ + "::" + name);
+
+ os << "// " << m.name () << endl
+ << "//" << endl
+ << endl;
+
+ container_extra (m, t);
+
+ //
+ // Statements.
+ //
+ if (!reuse_abst)
+ {
+ string sep (versioned ? "\n" : " ");
+
+ semantics::type& idt (container_idt (m));
+
+ qname table (table_name (m, table_prefix_));
+ string qtable (quote_id (table));
+ instance<object_columns_list> id_cols;
+ instance<object_columns_list> ik_cols; // index/key columns
+
+ if (smart)
+ {
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ ik_cols->traverse (m, *it, "index", "index");
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+ }
+
+ // select_statement
+ //
+ os << "const char " << scope << "::" << endl
+ << "select_statement[] =" << endl;
+
+ if (inverse)
+ {
+ semantics::class_* c (object_pointer (vt));
+ semantics::data_member& imf (*imp->front ());
+ semantics::data_member& imb (*imp->back ());
+
+ // In a polymorphic hierarchy the inverse member can be in
+ // the base class, in which case we should use that class
+ // for the table name, etc.
+ //
+ if (polymorphic (*c))
+ c = &dynamic_cast<semantics::class_&> (imf.scope ());
+
+ data_member_path& inv_id (*id_member (*c));
+
+ qname inv_table; // Other table name.
+ string inv_qtable;
+ instance<object_columns_list> inv_id_cols; // Other id column.
+ instance<object_columns_list> inv_fid_cols; // Other foreign id
+ // column (ref to us).
+ statement_columns sc;
+
+ if (container (imb))
+ {
+ // many(i)-to-many
+ //
+
+ // This other container is a direct member of the class so the
+ // table prefix is just the class table name.
+ //
+ inv_table = table_name (*c, *imp);
+ inv_qtable = quote_id (inv_table);
+
+ inv_id_cols->traverse (imb, utype (inv_id), "id", "object_id", c);
+ inv_fid_cols->traverse (imb, idt, "value", "value");
+
+ for (object_columns_list::iterator i (inv_id_cols->begin ());
+ i != inv_id_cols->end (); ++i)
+ {
+ // If this is a simple id, then pass the "id" key prefix. If
+ // it is a composite id, then the members have no prefix.
+ //
+ sc.push_back (
+ statement_column (
+ inv_qtable,
+ convert_from (inv_qtable + "." + quote_id (i->name),
+ i->type,
+ *i->member),
+ i->type,
+ *i->member,
+ inv_id_cols->size () == 1 ? "id" : ""));
+ }
+ }
+ else
+ {
+ // many(i)-to-one
+ //
+ inv_table = table_name (*c);
+ inv_qtable = quote_id (inv_table);
+
+ inv_id_cols->traverse (inv_id);
+ inv_fid_cols->traverse (imb, column_prefix (*imp));
+
+ for (object_columns_list::iterator i (inv_id_cols->begin ());
+ i != inv_id_cols->end (); ++i)
+ {
+ sc.push_back (
+ statement_column (
+ inv_qtable,
+ convert_from (inv_qtable + "." + quote_id (i->name),
+ i->type,
+ *i->member),
+ i->type,
+ *i->member));
+ }
+ }
+
+ process_statement_columns (sc, statement_select, versioned);
+
+ os << strlit ("SELECT" + sep) << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "") + sep) << endl;
+ }
+
+ instance<query_parameters> qp (statement_select, inv_table);
+ os << strlit ("FROM " + inv_qtable + sep) << endl;
+
+ string where ("WHERE ");
+ for (object_columns_list::iterator b (inv_fid_cols->begin ()),
+ i (b); i != inv_fid_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += inv_qtable + "." + quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+ os << strlit (where);
+ }
+ else
+ {
+ id_cols->traverse (m, idt, "id", "object_id");
+
+ statement_columns sc;
+ statement_kind sk (statement_select); // Imperfect forwarding.
+ instance<object_columns> t (qtable, sk, sc);
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ t->traverse (m, *it, "index", "index");
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ t->traverse (m, *kt, "key", "key");
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ t->traverse (m, vt, "value", "value");
+
+ process_statement_columns (sc, statement_select, versioned);
+
+ os << strlit ("SELECT" + sep) << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "") + sep) << endl;
+ }
+
+ instance<query_parameters> qp (statement_select, table);
+ os << strlit ("FROM " + qtable + sep) << endl;
+
+ string where ("WHERE ");
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += qtable + "." + quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ if (ordered)
+ {
+ // Top-level column.
+ //
+ string const& col (
+ column_qname (m, "index", "index", column_prefix ()));
+
+ where += " ORDER BY " + qtable + "." + col;
+ }
+
+ os << strlit (where);
+ }
+
+ os << ";"
+ << endl;
+
+ // insert_statement
+ //
+ os << "const char " << scope << "::" << endl
+ << "insert_statement[] =" << endl;
+
+ if (inverse)
+ os << strlit ("") << ";"
+ << endl;
+ else
+ {
+ statement_columns sc;
+ statement_kind sk (statement_insert); // Imperfect forwarding.
+ instance<object_columns> t (sk, sc);
+
+ t->traverse (m, idt, "id", "object_id");
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ t->traverse (m, *it, "index", "index");
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ t->traverse (m, *kt, "key", "key");
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ t->traverse (m, vt, "value", "value");
+
+ process_statement_columns (sc, statement_insert, versioned);
+
+ os << strlit ("INSERT INTO " + qtable + sep) << endl;
+
+ for (statement_columns::const_iterator b (sc.begin ()), i (b),
+ e (sc.end ()); i != e;)
+ {
+ string s;
+
+ if (i == b)
+ s += '(';
+ s += i->column;
+ s += (++i != e ? ',' : ')');
+ s += sep;
+
+ os << strlit (s) << endl;
+ }
+
+ os << strlit ("VALUES" + sep) << endl;
+
+ string values ("(");
+ instance<query_parameters> qp (statement_insert, table);
+ for (statement_columns::const_iterator b (sc.begin ()), i (b),
+ e (sc.end ()); i != e; ++i)
+ {
+ if (i != b)
+ {
+ values += ',';
+ values += sep;
+ }
+
+ values += convert_to (qp->next (*i), i->type, *i->member);
+ }
+ values += ')';
+
+ os << strlit (values) << ";"
+ << endl;
+ }
+
+ // update_statement
+ //
+ if (smart)
+ {
+ os << "const char " << scope << "::" << endl
+ << "update_statement[] =" << endl
+ << strlit ("UPDATE " + qtable + sep) << endl
+ << strlit ("SET" + sep) << endl;
+
+ instance<query_parameters> qp (statement_update, table);
+ statement_columns sc;
+ {
+ bool f (false); // Imperfect forwarding.
+ query_parameters* p (qp.get ()); // Imperfect forwarding.
+ statement_kind sk (statement_update); // Imperfect forwarding.
+ instance<object_columns> t (sk, f, sc, p);
+ t->traverse (m, vt, "value", "value");
+ process_statement_columns (sc, statement_update, versioned);
+ }
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "") + sep) << endl;
+ }
+
+ string where ("WHERE ");
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ for (object_columns_list::iterator b (ik_cols->begin ()), i (b);
+ i != ik_cols->end (); ++i)
+ {
+ where += " AND " + quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ os << strlit (where) << ";"
+ << endl;
+ }
+
+ // delete_statement
+ //
+ os << "const char " << scope << "::" << endl
+ << "delete_statement[] =" << endl;
+
+ if (inverse)
+ os << strlit ("") << ";"
+ << endl;
+ else
+ {
+ instance<query_parameters> qp (statement_delete, table);
+
+ os << strlit ("DELETE FROM " + qtable + " ") << endl;
+
+ string where ("WHERE ");
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ if (smart)
+ {
+ for (object_columns_list::iterator b (ik_cols->begin ()), i (b);
+ i != ik_cols->end (); ++i)
+ {
+ where += " AND " + quote_id (i->name) +
+ (ck == ck_ordered ? ">=" : "=") +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+ }
+
+ os << strlit (where) << ";"
+ << endl;
+ }
+ }
+
+ if (base)
+ return;
+
+ //
+ // Functions.
+ //
+
+ // bind (cond_image_type)
+ //
+ if (smart)
+ {
+ os << "void " << scope << "::" << endl
+ << "bind (" << bind_vector << " b," << endl
+ << "const " << bind_vector << " id," << endl
+ << "std::size_t id_size," << endl
+ << "cond_image_type& c)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << "statement_kind sk (statement_select);"
+ << "ODB_POTENTIALLY_UNUSED (sk);"
+ << endl
+ << "std::size_t n (0);"
+ << endl;
+
+ os << "// object_id" << endl
+ << "//" << endl
+ << "if (id != 0)" << endl
+ << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
+ << "n += id_size;" // Not in if for "id unchanged" optimization.
+ << endl;
+
+ // We don't need to update the bind index since this is the
+ // last element.
+ //
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "index_", "c", *it, ict, "index_type", "index");
+ bm->traverse (m);
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "// key" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "key_", "c", *kt, kct, "key_type", "key");
+ bm->traverse (m);
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "// value" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "value_", "c", vt, vct, "value_type", "value");
+ bm->traverse (m);
+ break;
+ }
+ }
+ os << "}";
+ }
+
+ // bind (data_image_type)
+ //
+ {
+ os << "void " << scope << "::" << endl
+ << "bind (" << bind_vector << " b," << endl
+ << "const " << bind_vector << " id," << endl
+ << "std::size_t id_size," << endl
+ << "data_image_type& d";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ // In the case of containers, insert and select column sets are
+ // the same since we can't have inverse members as container
+ // elements.
+ //
+ << "statement_kind sk (statement_select);"
+ << "ODB_POTENTIALLY_UNUSED (sk);"
+ << endl
+ << "size_t n (0);"
+ << endl;
+
+ os << "// object_id" << endl
+ << "//" << endl
+ << "if (id != 0)" << endl
+ << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
+ << "n += id_size;" // Not in if for "id unchanged" optimization.
+ << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "index_", "d", *it, ict, "index_type", "index");
+ bm->traverse (m);
+ os << "n++;" // Simple value.
+ << endl;
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "// key" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "key_", "d", *kt, kct, "key_type", "key");
+ bm->traverse (m);
+
+ if (semantics::class_* c = composite_wrapper (*kt))
+ os << "n += " << column_count (*c).total << "UL;"
+ << endl;
+ else
+ os << "n++;"
+ << endl;
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ // We don't need to update the bind index since this is the
+ // last element.
+ //
+ os << "// value" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "value_", "d", vt, vct, "value_type", "value");
+ bm->traverse (m);
+
+ os << "}";
+ }
+
+ // bind (cond_image, data_image) (update)
+ //
+ if (smart)
+ {
+ os << "void " << scope << "::" << endl
+ << "bind (" << bind_vector << " b," << endl
+ << "const " << bind_vector << " id," << endl
+ << "std::size_t id_size," << endl
+ << "cond_image_type& c," << endl
+ << "data_image_type& d";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ // Use insert instead of update to include read-only members.
+ //
+ << "statement_kind sk (statement_insert);"
+ << "ODB_POTENTIALLY_UNUSED (sk);"
+ << endl
+ << "std::size_t n (0);"
+ << endl;
+
+ os << "// value" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "value_", "d", vt, vct, "value_type", "value");
+ bm->traverse (m);
+
+ if (semantics::class_* c = composite_wrapper (vt))
+ os << "n += " << column_count (*c).total << "UL;"
+ << endl;
+ else
+ os << "n++;"
+ << endl;
+
+ os << "// object_id" << endl
+ << "//" << endl
+ << "if (id != 0)" << endl
+ << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
+ << "n += id_size;" // Not in if for "id unchanged" optimization.
+ << endl;
+
+ // We don't need to update the bind index since this is the
+ // last element.
+ //
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "index_", "c", *it, ict, "index_type", "index");
+ bm->traverse (m);
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "// key" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "key_", "c", *kt, kct, "key_type", "key");
+ bm->traverse (m);
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "// value" << endl
+ << "//" << endl;
+ instance<bind_member> bm (
+ "value_", "c", vt, vct, "value_type", "value");
+ bm->traverse (m);
+ break;
+ }
+ }
+ os << "}";
+ }
+
+ // grow ()
+ //
+ if (generate_grow)
+ {
+ size_t index (0);
+
+ os << "void " << scope << "::" << endl
+ << "grow (data_image_type& i," << endl
+ << truncated_vector << " t";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "bool grew (false);"
+ << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl;
+ instance<grow_member> gm (
+ index, "index_", *it, ict, "index_type", "index");
+ gm->traverse (m);
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "// key" << endl
+ << "//" << endl;
+ instance<grow_member> gm (
+ index, "key_", *kt, kct, "key_type", "key");
+ gm->traverse (m);
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ os << "// value" << endl
+ << "//" << endl;
+ instance<grow_member> gm (
+ index, "value_", vt, vct, "value_type", "value");
+ gm->traverse (m);
+
+ os << "if (grew)" << endl
+ << "i.version++;"
+ << "}";
+ }
+
+ // init (data_image)
+ //
+ if (!inverse)
+ {
+ os << "void " << scope << "::" << endl
+ << "init (data_image_type& i," << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ os << "index_type* j," << endl;
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "const key_type* k," << endl;
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ break;
+ }
+
+ os << "const value_type& v";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << "statement_kind sk (statement_insert);"
+ << "ODB_POTENTIALLY_UNUSED (sk);"
+ << endl;
+
+ if (generate_grow)
+ os << "bool grew (false);"
+ << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl
+ << "if (j != 0)";
+
+ instance<init_image_member> im (
+ "index_", "*j", *it, ict, "index_type", "index");
+ im->traverse (m);
+ }
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "// key" << endl
+ << "//" << endl
+ << "if (k != 0)";
+
+ instance<init_image_member> im (
+ "key_", "*k", *kt, kct, "key_type", "key");
+ im->traverse (m);
+
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ os << "// value" << endl
+ << "//" << endl;
+ {
+ instance<init_image_member> im (
+ "value_", "v", vt, vct, "value_type", "value");
+ im->traverse (m);
+ }
+
+ if (generate_grow)
+ os << "if (grew)" << endl
+ << "i.version++;";
+
+ os << "}";
+ }
+
+ // init (cond_image)
+ //
+ if (smart)
+ {
+ os << "void " << scope << "::" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "init (cond_image_type& i, index_type j)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << "statement_kind sk (statement_select);"
+ << "ODB_POTENTIALLY_UNUSED (sk);"
+ << endl;
+
+ instance<init_image_member> im (
+ "index_", "j", *it, ict, "index_type", "index");
+ im->traverse (m);
+
+ os << "}";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ // Need to handle growth.
+ //
+ // os << "init (data_image_type&, const key_type&);";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ // Need to handle growth.
+ //
+ // os << "init (data_image_type&, const value_type&);";
+ break;
+ }
+ }
+
+ os << endl;
+ }
+
+ // init (data)
+ //
+ os << "void " << scope << "::" << endl
+ << "init (";
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ os << "index_type& j," << endl;
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "key_type& k," << endl;
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ break;
+ }
+
+ os << "value_type& v," << endl;
+ os << "const data_image_type& i," << endl
+ << "database* db";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);"
+ << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ {
+ os << "// index" << endl
+ << "//" << endl;
+
+ instance<init_value_member> im (
+ "index_", "j", *it, ict, "index_type", "index");
+ im->traverse (m);
+ }
+
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "// key" << endl
+ << "//" << endl;
+
+ instance<init_value_member> im (
+ "key_", "k", *kt, kct, "key_type", "key");
+ im->traverse (m);
+
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ break;
+ }
+
+ os << "// value" << endl
+ << "//" << endl;
+ {
+ // If the value is an object pointer, pass the id type as a
+ // type override.
+ //
+ instance<init_value_member> im (
+ "value_", "v", vt, vct, "value_type", "value");
+ im->traverse (m);
+ }
+ os << "}";
+
+ // insert
+ //
+ {
+ string ia, ka, va, da;
+
+ if (!inverse)
+ {
+ ia = ordered ? " i" : "";
+ ka = " k";
+ va = " v";
+ da = " d";
+ }
+
+ os << "void " << scope << "::" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "insert (index_type" << ia << ", " <<
+ "const value_type&" << va << ", " <<
+ "void*" << da << ")";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "insert (const key_type&" << ka << ", " <<
+ "const value_type&" << va << ", " <<
+ "void*" << da << ")";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "insert (const value_type&" << va << ", " <<
+ "void*" << da << ")";
+ break;
+ }
+ }
+
+ os << "{";
+
+ if (!inverse)
+ {
+ os << "using namespace " << db << ";"
+ << endl
+ << "statements_type& sts (*static_cast< statements_type* > (d));"
+ << "data_image_type& di (sts.data_image ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration ());";
+
+ os << endl
+ << "init (di, ";
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ os << "&i, ";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "&k, ";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ break;
+ }
+
+ os << "v" << (versioned ? ", svm" : "") << ");";
+
+ os << endl
+ << "if (sts.data_binding_test_version ())"
+ << "{"
+ << "const binding& id (sts.id_binding ());"
+ << "bind (sts.data_bind (), id.bind, id.count, di" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.data_binding_update_version ();"
+ << "}"
+ << "if (!sts.insert_statement ().execute ())" << endl
+ << "throw object_already_persistent ();";
+ }
+
+ os << "}";
+ }
+
+ // update
+ //
+ if (smart)
+ {
+ os << "void " << scope << "::" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "update (index_type i, const value_type& v, void* d)";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ os << "{";
+
+ os << "using namespace " << db << ";"
+ << endl
+ << "statements_type& sts (*static_cast< statements_type* > (d));"
+ << "cond_image_type& ci (sts.cond_image ());"
+ << "data_image_type& di (sts.data_image ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration ());";
+
+ os << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "init (ci, i);";
+ os << "init (di, 0, v" << (versioned ? ", svm" : "") << ");";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ //os << "init (di, 0, v);";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ //os << "init (di, v);";
+ break;
+ }
+ }
+
+ os << endl
+ << "if (sts.update_binding_test_version ())"
+ << "{"
+ << "const binding& id (sts.id_binding ());"
+ << "bind (sts.update_bind (), id.bind, id.count, ci, di" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.update_binding_update_version ();"
+ << "}";
+
+ os << "if (sts.update_statement ().execute () == 0)" << endl
+ << "throw object_not_persistent ();"
+ << "}";
+ }
+
+ // select
+ //
+ os << "bool " << scope << "::" << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "select (index_type&" << (ordered ? " i" : "") <<
+ ", value_type& v, void* d)";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "select (key_type& k, value_type& v, void* d)";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ os << "select (value_type& v, void* d)";
+ break;
+ }
+ }
+
+ os << "{"
+ << "using namespace " << db << ";"
+ << "using " << db << "::select_statement;" // Conflicts.
+ << endl
+ << "statements_type& sts (*static_cast< statements_type* > (d));"
+ << "data_image_type& di (sts.data_image ());";
+
+ if (versioned)
+ os << "const schema_version_migration& svm (" <<
+ "sts.version_migration ());";
+
+ os << endl
+ << "init (";
+
+ // Extract current element.
+ //
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ if (ordered)
+ os << "i, ";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ os << "k, ";
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ break;
+ }
+
+ os << "v, di, &sts.connection ().database ()" <<
+ (versioned ? ", svm" : "") << ");"
+ << endl;
+
+ init_value_extra ();
+
+ // If we are loading an eager pointer, then the call to init
+ // above executes other statements which potentially could
+ // change the image, including the id.
+ //
+ if (eager_ptr)
+ {
+ os << "if (sts.data_binding_test_version ())"
+ << "{"
+ << "const binding& id (sts.id_binding ());"
+ << "bind (sts.data_bind (), id.bind, id.count, di" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.data_binding_update_version ();"
+ << "}";
+ }
+
+ // Fetch next.
+ //
+ os << "select_statement& st (sts.select_statement ());"
+ << "select_statement::result r (st.fetch ());";
+
+ if (grow)
+ os << endl
+ << "if (r == select_statement::truncated)"
+ << "{"
+ << "grow (di, sts.select_image_truncated ()" <<
+ (versioned ? ", svm" : "") << ");"
+ << endl
+ << "if (sts.data_binding_test_version ())"
+ << "{"
+ // Id cannot change.
+ //
+ << "bind (sts.data_bind (), 0, sts.id_binding ().count, di" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.data_binding_update_version ();"
+ << "st.refetch ();"
+ << "}"
+ << "}";
+
+ os << "return r != select_statement::no_data;"
+ << "}";
+
+ // delete_
+ //
+ os << "void " << scope << "::" << endl
+ << "delete_ (";
+
+ if (smart)
+ {
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "index_type i, ";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+ }
+
+ os << "void*" << (inverse ? "" : " d") << ")"
+ << "{";
+
+ if (!inverse)
+ {
+ os << "using namespace " << db << ";"
+ << endl
+ << "statements_type& sts (*static_cast< statements_type* > (d));";
+
+ if (smart)
+ {
+ os << "cond_image_type& ci (sts.cond_image ());"
+ << endl;
+
+ switch (ck)
+ {
+ case ck_ordered:
+ {
+ os << "init (ci, i);";
+ break;
+ }
+ case ck_map:
+ case ck_multimap:
+ {
+ break;
+ }
+ case ck_set:
+ case ck_multiset:
+ {
+ break;
+ }
+ }
+
+ os << endl
+ << "if (sts.cond_binding_test_version ())"
+ << "{"
+ << "const binding& id (sts.id_binding ());"
+ << "bind (sts.cond_bind (), id.bind, id.count, ci);"
+ << "sts.cond_binding_update_version ();"
+ << "}";
+ }
+
+ os << "sts.delete_statement ().execute ();";
+ }
+
+ os << "}";
+
+ // persist
+ //
+ if (!inverse)
+ {
+ os << "void " << scope << "::" << endl
+ << "persist (const container_type& c," << endl
+ << "statements_type& sts";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << "functions_type& fs (sts.functions ());";
+
+ if (versioned)
+ os << "sts.version_migration (svm);";
+
+ if (!smart && ck == ck_ordered)
+ os << "fs.ordered_ = " << ordered << ";";
+
+ os << "container_traits_type::persist (c, fs);"
+ << "}";
+ }
+
+ // load
+ //
+ os << "void " << scope << "::" << endl
+ << "load (container_type& c," << endl
+ << "statements_type& sts";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << "using " << db << "::select_statement;" // Conflicts.
+ << endl
+ << "const binding& id (sts.id_binding ());"
+ << endl
+ << "if (sts.data_binding_test_version ())"
+ << "{"
+ << "bind (sts.data_bind (), id.bind, id.count, sts.data_image ()" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.data_binding_update_version ();"
+ << "}"
+ // We use the id binding directly so no need to check cond binding.
+ //
+ << "select_statement& st (sts.select_statement ());"
+ << "st.execute ();"
+ << "auto_result ar (st);";
+
+ // If we are loading eager object pointers, we may need to cache
+ // the result since we will be loading other objects.
+ //
+ if (eager_ptr)
+ cache_result ("st");
+
+ os << "select_statement::result r (st.fetch ());";
+
+ if (grow)
+ os << endl
+ << "if (r == select_statement::truncated)"
+ << "{"
+ << "data_image_type& di (sts.data_image ());"
+ << "grow (di, sts.select_image_truncated ()" <<
+ (versioned ? ", svm" : "") << ");"
+ << endl
+ << "if (sts.data_binding_test_version ())"
+ << "{"
+ // Id cannot change.
+ //
+ << "bind (sts.data_bind (), 0, id.count, di" <<
+ (versioned ? ", svm" : "") << ");"
+ << "sts.data_binding_update_version ();"
+ << "st.refetch ();"
+ << "}"
+ << "}";
+
+ os << "bool more (r != select_statement::no_data);"
+ << endl
+ << "functions_type& fs (sts.functions ());";
+
+ if (versioned)
+ os << "sts.version_migration (svm);";
+
+ if (!smart && ck == ck_ordered)
+ os << "fs.ordered_ = " << ordered << ";";
+
+ os << "container_traits_type::load (c, more, fs);"
+ << "}";
+
+ // update
+ //
+ if (!(inverse || readonly (member_path_, member_scope_)))
+ {
+ os << "void " << scope << "::" << endl
+ << "update (const container_type& c," << endl
+ << "statements_type& sts";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << "functions_type& fs (sts.functions ());";
+
+ if (versioned)
+ os << "sts.version_migration (svm);";
+
+ if (!smart && ck == ck_ordered)
+ os << "fs.ordered_ = " << ordered << ";";
+
+ os << "container_traits_type::update (c, fs);"
+ << "}";
+ }
+
+ // erase
+ //
+ if (!inverse)
+ {
+ os << "void " << scope << "::" << endl
+ << "erase (";
+
+ if (smart)
+ os << "const container_type* c, ";
+
+ os << "statements_type& sts)"
+ << "{"
+ << "using namespace " << db << ";"
+ << endl
+ << "functions_type& fs (sts.functions ());";
+
+ if (!smart && ck == ck_ordered)
+ os << "fs.ordered_ = " << ordered << ";";
+
+ os << "container_traits_type::erase (" << (smart ? "c, " : "") << "fs);"
+ << "}";
+ }
+ }
+
+ protected:
+ string scope_;
+ semantics::class_& c_;
+ };
+
+ // Extra statement cache members for containers.
+ //
+ struct container_cache_members: object_members_base, virtual context
+ {
+ typedef container_cache_members base;
+
+ container_cache_members ()
+ : object_members_base (true, false, false)
+ {
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type& c)
+ {
+ bool smart (!context::inverse (m, "value") &&
+ !unordered (m) &&
+ container_smart (c));
+
+ string traits (flat_prefix_ + public_name (m) + "_traits");
+
+ os << db << "::" << (smart ? "smart_" : "") <<
+ "container_statements_impl< " << traits << " > " <<
+ flat_prefix_ << m.name () << ";";
+ }
+ };
+
+ struct container_cache_init_members: object_members_base, virtual context
+ {
+ typedef container_cache_init_members base;
+
+ container_cache_init_members ()
+ : object_members_base (true, false, false), first_ (true)
+ {
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type&)
+ {
+ if (first_)
+ {
+ os << endl
+ << ": ";
+ first_ = false;
+ }
+ else
+ os << "," << endl
+ << " ";
+
+ os << flat_prefix_ << m.name () << " (c, id";
+ extra_members ();
+ os << ")";
+ }
+
+ virtual void
+ extra_members () {}
+
+ protected:
+ bool first_;
+ };
+
+ // Extra statement cache members for sections.
+ //
+ struct section_cache_members: virtual context
+ {
+ typedef section_cache_members base;
+
+ virtual void
+ traverse (user_section& s)
+ {
+ string traits (public_name (*s.member) + "_traits");
+
+ os << db << "::" << "section_statements< " <<
+ class_fq_name (*s.object) << ", " << traits << " > " <<
+ s.member->name () << ";";
+ }
+ };
+
+ struct section_cache_init_members: virtual context
+ {
+ typedef section_cache_init_members base;
+
+ section_cache_init_members (bool first): first_ (first) {}
+
+ virtual void
+ traverse (user_section& s)
+ {
+ if (first_)
+ {
+ os << endl
+ << ": ";
+ first_ = false;
+ }
+ else
+ os << "," << endl
+ << " ";
+
+ os << s.member->name () << " (c, im, idim, id, idv";
+ extra_members ();
+ os << ")";
+ }
+
+ virtual void
+ extra_members () {}
+
+ protected:
+ bool first_;
+ };
+
+ // Calls for container members.
+ //
+ struct container_calls: object_members_base, virtual context
+ {
+ typedef container_calls base;
+
+ enum call_type
+ {
+ persist_call,
+ load_call,
+ update_call,
+ erase_obj_call,
+ erase_id_call,
+ section_call
+ };
+
+ container_calls (call_type call, object_section* section = 0)
+ : object_members_base (true, false, true, false, section),
+ call_ (call),
+ obj_prefix_ ("obj"),
+ by_value_ (0)
+ {
+ }
+
+ virtual bool
+ section_test (data_member_path const& mp)
+ {
+ object_section& s (section (mp));
+
+ // Include eager loaded members into the main section for
+ // load calls.
+ //
+ return section_ == 0 ||
+ *section_ == s ||
+ (call_ == load_call &&
+ *section_ == main_section &&
+ !s.separate_load ());
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member&, semantics::class_&)
+ {
+ // We don't want to traverse composite id.
+ }
+
+ virtual void
+ traverse_composite_wrapper (semantics::data_member* m,
+ semantics::class_& c,
+ semantics::type* w)
+ {
+ if (m == 0 ||
+ call_ == erase_id_call ||
+ (call_ == load_call && by_value_ != 0))
+ {
+ object_members_base::traverse_composite (m, c);
+ return;
+ }
+
+ // Get this member using the accessor expression.
+ //
+ member_access& ma (
+ m->get<member_access> (call_ == load_call ? "set" : "get"));
+
+ // We don't support by-value modifiers for composite values
+ // with containers. However, at this point we don't know
+ // whether this composite value has any containers. So we
+ // are just going to set a flag that can be checked in
+ // traverse_container() below.
+ //
+ if (call_ == load_call && ma.placeholder ())
+ {
+ by_value_ = &ma;
+ object_members_base::traverse_composite (m, c);
+ by_value_ = 0;
+ return;
+ }
+
+ // We also don't support by-value accessors is there is a
+ // smart container inside (which, again, we don't know at
+ // this point). So keep track of such first instance.
+ //
+ member_access* old_by_value (by_value_);
+ if (call_ != load_call && ma.by_value && by_value_ == 0)
+ by_value_ = &ma;
+
+ string old_op (obj_prefix_);
+ string old_f (from_);
+ obj_prefix_.clear ();
+
+ // If this member is const and we have a synthesized direct
+ // access, then cast away constness. Otherwise, we assume
+ // that the user-provided expression handles this.
+ //
+ bool cast (call_ == load_call && ma.direct () && const_member (*m));
+ if (cast)
+ obj_prefix_ = "const_cast< " + member_ref_type (*m, false) +
+ " > (\n";
+
+ obj_prefix_ += ma.translate (old_op);
+
+ if (cast)
+ obj_prefix_ += ")";
+
+ // If this is not a synthesized expression, then store its
+ // location which we will output later for easier error
+ // tracking.
+ //
+ if (!ma.synthesized)
+ from_ += "// From " + location_string (ma.loc, true) + "\n";
+
+ // If this is a wrapped composite value, then we need to "unwrap" it.
+ //
+ if (w != 0)
+ {
+ semantics::names* hint;
+ semantics::type& t (utype (*m, hint));
+
+ // Because we cannot have nested containers, member type should
+ // be the same as w.
+ //
+ assert (&t == w);
+
+ obj_prefix_ = "wrapper_traits< " + t.fq_name (hint) + " >::" +
+ (call_ == load_call ? "set_ref" : "get_ref") +
+ " (\n" + obj_prefix_ + ")";
+ }
+
+ object_members_base::traverse_composite (m, c);
+ from_ = old_f;
+ obj_prefix_ = old_op;
+ by_value_ = old_by_value;
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type& c)
+ {
+ using semantics::type;
+
+ bool inverse (context::inverse (m, "value"));
+ bool smart (!inverse && !unordered (m) && container_smart (c));
+ bool versioned (context::versioned (m));
+
+ // In certain cases we don't need to do anything.
+ //
+ if ((call_ != load_call && inverse) ||
+ (call_ == section_call && !smart) ||
+ (call_ == update_call && readonly (member_path_, member_scope_)))
+ return;
+
+ string const& name (m.name ());
+ string sts_name (flat_prefix_ + name);
+ string traits (flat_prefix_ + public_name (m) + "_traits");
+
+ os << "// " << member_prefix_ << m.name () << endl
+ << "//" << endl;
+
+ // Get this member using the accessor expression.
+ //
+ string var;
+ member_access& ma (
+ m.get<member_access> (call_ == load_call ? "set" : "get"));
+
+ // We don't support by-value modifiers for composite values
+ // with containers.
+ //
+ if (call_ == load_call && by_value_ != 0)
+ {
+ error (by_value_->loc) << "by-value modification of a composite "
+ << "value with container is not supported"
+ << endl;
+ info (m.location ()) << "container member is defined here" << endl;
+ throw operation_failed ();
+ }
+
+ // We don't support by-value accessors for smart containers.
+ //
+ if (call_ != load_call && smart)
+ {
+ if (by_value_ != 0)
+ {
+ error (by_value_->loc) << "by-value access to a composite value "
+ << "with smart container is not supported"
+ << endl;
+ info (m.location ()) << "container member is defined here" << endl;
+ throw operation_failed ();
+ }
+
+ if (ma.by_value)
+ {
+ error (ma.loc) << "by-value access to a smart container is not "
+ << "supported" << endl;
+ info (m.location ()) << "container member is defined here" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ // If the member is soft- added or deleted, check the version.
+ //
+ unsigned long long av (added (member_path_));
+ unsigned long long dv (deleted (member_path_));
+
+ // If the addition/deletion version is the same as the section's,
+ // then we don't need the test.
+ //
+ if (user_section* s = dynamic_cast<user_section*> (section_))
+ {
+ if (av == added (*s->member))
+ av = 0;
+
+ if (dv == deleted (*s->member))
+ dv = 0;
+ }
+
+ if (av != 0 || dv != 0)
+ {
+ os << "if (";
+
+ if (av != 0)
+ os << "svm >= schema_version_migration (" << av << "ULL, true)";
+
+ if (av != 0 && dv != 0)
+ os << " &&" << endl;
+
+ if (dv != 0)
+ os << "svm <= schema_version_migration (" << dv << "ULL, true)";
+
+ os << ")" << endl;
+ }
+
+ os << "{";
+
+ if (call_ != erase_id_call && (call_ != erase_obj_call || smart))
+ {
+ // See if we are modifying via a reference or proper modifier.
+ //
+ if (call_ == load_call && ma.placeholder ())
+ os << member_val_type (m, false, "v") << ";"
+ << endl;
+ else
+ {
+ // Note: this case is for both access and modification.
+ //
+
+ // Output stored locations, if any.
+ //
+ os << from_;
+
+ // If this is not a synthesized expression, then output its
+ // location for easier error tracking.
+ //
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ // Note that here we don't decay arrays.
+ //
+ const string& ref_type (
+ member_ref_type (m, call_ != load_call, "v", false /* decay */));
+
+ // VC++ cannot grok the constructor syntax.
+ //
+ os << ref_type << " =" << endl
+ << " ";
+
+ // If this member is const and we have a synthesized direct
+ // access, then cast away constness. Otherwise, we assume
+ // that the user-provided expression handles this.
+ //
+ bool cast (call_ == load_call && ma.direct () && const_member (m));
+ if (cast)
+ os << "const_cast< " << member_ref_type (m, false, "", false) <<
+ " > (" << endl;
+
+ os << ma.translate (obj_prefix_);
+
+ if (cast)
+ os << ")";
+
+ os << ";"
+ << endl;
+ }
+
+ var = "v";
+
+ semantics::names* hint;
+ semantics::type& t (utype (m, hint));
+
+ // If this is a wrapped container, then we need to "unwrap" it.
+ //
+ if (wrapper (t))
+ {
+ var = "wrapper_traits< " + t.fq_name (hint) + " >::" +
+ (call_ == load_call ? "set_ref" : "get_ref") + " (" + var + ")";
+ }
+ }
+
+ switch (call_)
+ {
+ case persist_call:
+ {
+ os << traits << "::persist (" << endl
+ << var << "," << endl
+ << "esc." << sts_name;
+
+ if (versioned)
+ os << "," << endl
+ << "svm";
+
+ os << ");";
+ break;
+ }
+ case load_call:
+ {
+ os << traits << "::load (" << endl
+ << var << "," << endl
+ << "esc." << sts_name;
+
+ if (versioned)
+ os << "," << endl
+ << "svm";
+
+ os << ");";
+ break;
+ }
+ case update_call:
+ {
+ os << traits << "::update (" << endl
+ << var << "," << endl
+ << "esc." << sts_name;
+
+ if (versioned)
+ os << "," << endl
+ << "svm";
+
+ os << ");";
+ break;
+ }
+ case erase_obj_call:
+ {
+ os << traits << "::erase (" << endl;
+
+ if (smart)
+ os << "&" << var << "," << endl;
+
+ os << "esc." << sts_name << ");"
+ << endl;
+ break;
+ }
+ case erase_id_call:
+ {
+ os << traits << "::erase (" << endl;
+
+ if (smart)
+ os << "0," << endl;
+
+ os << "esc." << sts_name << ");"
+ << endl;
+ break;
+ }
+ case section_call:
+ {
+ os << "if (" << traits << "::container_traits_type::changed (" <<
+ var << "))" << endl
+ << "s.reset (true, true);"; // loaded, changed
+ break;
+ }
+ }
+
+ if (call_ == load_call)
+ {
+ // Call the modifier if we are using a proper one.
+ //
+ if (ma.placeholder ())
+ {
+ os << endl
+ << from_;
+
+ // If this is not a synthesized expression, then output its
+ // location for easier error tracking.
+ //
+ if (!ma.synthesized)
+ os << "// From " << location_string (ma.loc, true) << endl;
+
+ os << ma.translate (
+ obj_prefix_, "v", "static_cast<" + db.string () +
+ "::database&> (db)") << ";";
+ }
+ }
+
+ os << "}";
+ }
+
+ protected:
+ call_type call_;
+ string obj_prefix_;
+ string from_;
+ member_access* by_value_;
+ };
+
+ //
+ //
+ struct section_traits: traversal::class_, virtual context
+ {
+ typedef section_traits base;
+
+ section_traits (semantics::class_& c)
+ : c_ (c),
+ scope_ ("access::object_traits_impl< " + class_fq_name (c) +
+ ", id_" + db.string () + " >")
+ {
+ }
+
+ // Additional code that need to be executed following the call to
+ // init_value().
+ //
+ virtual void
+ init_value_extra ()
+ {
+ }
+
+ virtual void
+ process_statement_columns (statement_columns&,
+ statement_kind,
+ bool /*dynamic*/)
+ {
+ }
+
+ virtual void
+ section_extra (user_section&)
+ {
+ }
+
+ // Returning "1" means increment by one.
+ //
+ virtual string
+ optimistic_version_increment (semantics::data_member&)
+ {
+ return "1";
+ }
+
+ virtual string
+ update_statement_extra (user_section&)
+ {
+ return "";
+ }
+
+ virtual void
+ traverse (user_section& s)
+ {
+ using semantics::class_;
+ using semantics::data_member;
+
+ data_member& m (*s.member);
+
+ class_* poly_root (polymorphic (c_));
+ bool poly (poly_root != 0);
+ bool poly_derived (poly && poly_root != &c_);
+ class_* poly_base (poly_derived ? &polymorphic_base (c_) : 0);
+
+ data_member* opt (optimistic (c_));
+
+ // Treat the special version update sections as abstract in reuse
+ // inheritance.
+ //
+ bool reuse_abst (!poly &&
+ (abstract (c_) ||
+ s.special == user_section::special_version));
+
+ bool load (s.total != 0 && s.separate_load ());
+ bool load_con (s.containers && s.separate_load ());
+ bool load_opt (s.optimistic () && s.separate_load ());
+
+ bool update (s.total != s.inverse + s.readonly); // Always separate.
+ bool update_con (s.readwrite_containers);
+ bool update_opt (s.optimistic () && (s.readwrite_containers || poly));
+
+ // Don't generate anything for empty sections.
+ //
+ if (!(load || load_con || load_opt ||
+ update || update_con || update_opt))
+ return;
+
+ // If we are adding a new section to a derived class in an optimistic
+ // polymorphic hierarchy, then pretend it inherits from the special
+ // version update section.
+ //
+ user_section* rs (0);
+ if (opt != 0)
+ {
+ // Skip overrides and get to the new section if polymorphic.
+ //
+ for (rs = &s; poly && rs->base != 0; rs = rs->base) ;
+
+ if (rs != 0)
+ {
+ if (rs->object != &opt->scope ())
+ rs->base = &(poly ? poly_root : &opt->scope ())->
+ get<user_sections> ("user-sections").back ();
+ else
+ rs = 0;
+ }
+ }
+
+ string name (public_name (m) + "_traits");
+ string scope (scope_ + "::" + name);
+
+ os << "// " << m.name () << endl
+ << "//" << endl
+ << endl;
+
+ // bind (id, image_type)
+ //
+ if (load || load_opt || update || update_opt)
+ {
+ os << "std::size_t " << scope << "::" << endl
+ << "bind (" << bind_vector << " b," << endl
+ << "const " << bind_vector << (reuse_abst ? "," : " id,") << endl
+ << "std::size_t" << (reuse_abst ? "," : " id_size,") << endl
+ << "image_type& i," << endl
+ << db << "::statement_kind sk";
+
+ if (s.versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (sk);";
+
+ if (s.versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "using namespace " << db << ";"
+ << endl
+ << "std::size_t n (0);"
+ << endl;
+
+ // Bind reuse base. It is always first and we never ask it
+ // to bind id(+ver).
+ //
+ if (s.base != 0 && !poly_derived)
+ {
+ user_section& b (*s.base);
+
+ bool load (b.total != 0 && b.separate_load ());
+ bool load_opt (b.optimistic () && b.separate_load ());
+
+ bool update (b.total != b.inverse + b.readonly);
+
+ if (load || load_opt || update)
+ os << "// " << class_name (*b.object) << endl
+ << "//" << endl
+ << "n += object_traits_impl< " << class_fq_name (*b.object) <<
+ ", id_" << db << " >::" << public_name (*b.member) <<
+ "_traits::bind (" << endl
+ << "b, 0, 0, i, sk" << (b.versioned ? ", svm" : "") << ");"
+ << endl;
+ }
+
+ // Bind members.
+ //
+ {
+ instance<bind_member> bm ("", "", &s);
+ traversal::names n (*bm);
+ names (c_, n);
+ }
+
+ // Bind polymorphic image chain for the select statement.
+ //
+ if (s.base != 0 && poly_derived && s.separate_load ())
+ {
+ // Find the next base that has something to load, if any.
+ //
+ user_section* b (s.base);
+ string acc (".base");
+ for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
+ {
+ if (b->object == bo)
+ {
+ if (b->total != 0 || b->optimistic ())
+ break;
+
+ b = b->base;
+ if (b == 0 || !polymorphic (*b->object))
+ {
+ b = 0;
+ break;
+ }
+ }
+ acc += "->base";
+ }
+
+ if (b != 0)
+ os << "// " << class_name (*b->object) << endl
+ << "//" << endl
+ << "if (sk == statement_select)" << endl
+ << "n += object_traits_impl< " << class_fq_name (*b->object) <<
+ ", id_" << db << " >::" << public_name (*b->member) <<
+ "_traits::bind (" << endl
+ << "b + n, 0, 0, *i" << acc << ", sk" <<
+ (b->versioned ? ", svm" : "") << ");"
+ << endl;
+ }
+
+ if (!reuse_abst)
+ os << "// object_id" << endl
+ << "//" << endl
+ << "if (id != 0)" << endl
+ << "std::memcpy (&b[n], id, id_size * sizeof (id[0]));"
+ << "n += id_size;" // Not in if for "id unchanged" optimization.
+ << endl;
+
+ os << "return n;"
+ << "}";
+ }
+
+ // grow ()
+ //
+ if (generate_grow && (load || load_opt))
+ {
+ os << "bool " << scope << "::" << endl
+ << "grow (image_type& i," << endl
+ << truncated_vector << " t";
+
+ if (s.versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (t);";
+
+ if (s.versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "bool grew (false);"
+ << endl;
+
+ size_t index (0);
+
+ if (s.base != 0 && !poly_derived)
+ {
+ user_section& b (*s.base);
+
+ bool load (b.total != 0);
+ bool load_opt (b.optimistic ());
+
+ if (load || load_opt)
+ {
+ os << "// " << class_name (*b.object) << endl
+ << "//" << endl
+ << "grew = object_traits_impl< " << class_fq_name (*b.object) <<
+ ", id_" << db << " >::" << public_name (*b.member) <<
+ "_traits::grow (i, t" << (b.versioned ? ", svm" : "") << ");"
+ << endl;
+
+ index += b.total + (load_opt ? 1 : 0);
+ }
+ }
+
+ {
+ user_section* ps (&s);
+ instance<grow_member> gm (index, "", ps);
+ traversal::names n (*gm);
+ names (c_, n);
+ }
+
+ // Grow polymorphic image chain.
+ //
+ if (s.base != 0 && poly_derived)
+ {
+ // Find the next base that has something to load, if any.
+ //
+ user_section* b (s.base);
+ string acc (".base");
+ size_t cols;
+ for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
+ {
+ if (b->object == bo)
+ {
+ cols = b->total + (b->optimistic () ? 1 : 0);
+ if (cols != 0)
+ break;
+
+ b = b->base;
+ if (b == 0 || !polymorphic (*b->object))
+ {
+ b = 0;
+ break;
+ }
+ }
+ acc += "->base";
+ }
+
+ if (b != 0)
+ os << "// " << class_name (*b->object) << endl
+ << "//" << endl
+ << "if (object_traits_impl< " << class_fq_name (*b->object) <<
+ ", id_" << db << " >::" << public_name (*b->member) <<
+ "_traits::grow (" << endl
+ << "*i" << acc << ", t + " << cols << "UL" <<
+ (b->versioned ? ", svm" : "") << "))" << endl
+ << "i" << acc << "->version++;"
+ << endl;
+ }
+
+ os << "return grew;" << endl
+ << "}";
+ }
+
+ // init (object, image)
+ //
+ if (load)
+ {
+ os << "void " << scope << "::" << endl
+ << "init (object_type& o," << endl
+ << "const image_type& i," << endl
+ << "database* db";
+
+ if (s.versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (db);";
+
+ if (s.versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl;
+
+ if (s.base != 0)
+ {
+ if (!poly_derived)
+ {
+ user_section& b (*s.base);
+
+ bool load (b.total != 0);
+
+ if (load)
+ os << "// " << class_name (*b.object) << endl
+ << "//" << endl
+ << "object_traits_impl< " << class_fq_name (*b.object) <<
+ ", id_" << db << " >::" << public_name (*b.member) <<
+ "_traits::init (o, i, db" <<
+ (b.versioned ? ", svm" : "") << ");"
+ << endl;
+ }
+ else
+ {
+ // Find the next base that has something to load, if any.
+ //
+ user_section* b (s.base);
+ string acc (".base");
+ for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
+ {
+ if (b->object == bo)
+ {
+ if (b->total != 0)
+ break;
+
+ b = b->base;
+ if (b == 0 || !polymorphic (*b->object))
+ {
+ b = 0;
+ break;
+ }
+ }
+ acc += "->base";
+ }
+
+ if (b != 0)
+ os << "// " << class_name (*b->object) << endl
+ << "//" << endl
+ << "object_traits_impl< " << class_fq_name (*b->object) <<
+ ", id_" << db << " >::" << public_name (*b->member) <<
+ "_traits::init (" << endl
+ << "o, *i" << acc << ", db" <<
+ (b->versioned ? ", svm" : "") << ");"
+ << endl;
+ }
+ }
+
+ {
+ instance<init_value_member> iv ("", "", true, &s);
+ traversal::names n (*iv);
+ names (c_, n);
+ }
+
+ os << "}";
+ }
+
+ // init (image, object)
+ //
+ if (update)
+ {
+ os << (generate_grow ? "bool " : "void ") << scope << "::" << endl
+ << "init (image_type& i," << endl
+ << "const object_type& o";
+
+ if (s.versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{";
+
+ if (s.versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);"
+ << endl;
+
+ os << "using namespace " << db << ";"
+ << endl
+ << "statement_kind sk (statement_insert);"
+ << "ODB_POTENTIALLY_UNUSED (sk);"
+ << endl;
+
+ // There is no call to init_image_pre() here (which calls the
+ // copy callback for some databases) since we are not going to
+ // touch any of the members that were loaded by query.
+
+ if (generate_grow)
+ os << "bool grew (false);"
+ << endl;
+
+ if (s.base != 0 && !poly_derived)
+ {
+ user_section& b (*s.base);
+
+ bool update (b.total != b.inverse + b.readonly);
+
+ if (update)
+ os << "// " << class_name (*b.object) << endl
+ << "//" << endl
+ << (generate_grow ? "grew = " : "") <<
+ "object_traits_impl< " << class_fq_name (*b.object) <<
+ ", id_" << db << " >::" << public_name (*b.member) <<
+ "_traits::init (i, o" << (b.versioned ? ", svm" : "") << ");"
+ << endl;
+ }
+
+ {
+ instance<init_image_member> ii ("", "", &s);
+ traversal::names n (*ii);
+ names (c_, n);
+ }
+
+ if (generate_grow)
+ os << "return grew;";
+
+ os << "}";
+ }
+
+ // The rest does not apply to reuse-abstract sections.
+ //
+ if (reuse_abst)
+ {
+ section_extra (s);
+ return;
+ }
+
+ string sep (s.versioned ? "\n" : " ");
+
+ // Schema name as a string literal or empty.
+ //
+ string schema_name (options.schema_name ()[db]);
+ if (!schema_name.empty ())
+ schema_name = strlit (schema_name);
+
+ // Statements.
+ //
+ qname table (table_name (c_));
+ string qtable (quote_id (table));
+
+ instance<object_columns_list> id_cols;
+ id_cols->traverse (*id_member (c_));
+
+ // select_statement
+ //
+ if (load || load_opt)
+ {
+ size_t depth (poly_derived ? polymorphic_depth (c_) : 1);
+
+ statement_columns sc;
+ {
+ statement_kind sk (statement_select); // Imperfect forwarding.
+ object_section* ps (&s); // Imperfect forwarding.
+ instance<object_columns> t (qtable, sk, sc, depth, ps);
+ t->traverse (c_);
+ process_statement_columns (sc, statement_select, s.versioned);
+ }
+
+ os << "const char " << scope << "::" << endl
+ << "select_statement[] =" << endl
+ << strlit ("SELECT" + sep) << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "") + sep) << endl;
+ }
+
+ os << strlit ("FROM " + qtable + sep) << endl;
+
+ // Join polymorphic bases.
+ //
+ if (depth != 1 && s.base != 0)
+ {
+ bool f (false); //@@ (im)perfect forwarding
+ size_t d (depth - 1); //@@ (im)perfect forward.
+ instance<polymorphic_object_joins> j (c_, f, d, "", s.base);
+ j->traverse (*poly_base);
+
+ for (strings::const_iterator i (j->begin ()); i != j->end (); ++i)
+ os << strlit (*i + sep) << endl;
+ }
+
+ // Join tables of inverse members belonging to this section.
+ //
+ {
+ bool f (false); // @@ (im)perfect forwarding
+ object_section* ps (&s); // @@ (im)perfect forwarding
+ instance<object_joins> j (c_, f, depth, ps);
+ j->traverse (c_);
+
+ for (strings::const_iterator i (j->begin ()); i != j->end (); ++i)
+ os << strlit (*i + sep) << endl;
+ }
+
+ string where ("WHERE ");
+ instance<query_parameters> qp (statement_select, table);
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += qtable + "." + quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ os << strlit (where) << ";"
+ << endl;
+ }
+
+ // update_statement
+ //
+ if (update || update_opt)
+ {
+ instance<query_parameters> qp (statement_update, table);
+
+ statement_columns sc;
+ {
+ query_parameters* p (qp.get ()); // Imperfect forwarding.
+ statement_kind sk (statement_update); // Imperfect forwarding.
+ object_section* ps (&s); // Imperfect forwarding.
+ instance<object_columns> t (sk, sc, p, ps);
+ t->traverse (c_);
+ process_statement_columns (sc, statement_update, s.versioned);
+ }
+
+ os << "const char " << scope << "::" << endl
+ << "update_statement[] =" << endl
+ << strlit ("UPDATE " + qtable + sep) << endl
+ << strlit ("SET" + sep) << endl;
+
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "") + sep) << endl;
+ }
+
+ // This didn't work out: cannot change the identity column.
+ //
+ //if (sc.empty ())
+ //{
+ // // We can end up with nothing to set if we need to "touch" a row
+ // // in order to increment its optimistic concurrency version. In
+ // // this case just do a dummy assignment based on the id column.
+ // //
+ // string const& c (quote_id (id_cols->begin ()->name));
+ // os << strlit (c + "=" + c) << endl;
+ //}
+
+ string extra (update_statement_extra (s));
+
+ if (!extra.empty ())
+ os << strlit (extra + sep) << endl;
+
+ string where ("WHERE ");
+ for (object_columns_list::iterator b (id_cols->begin ()), i (b);
+ i != id_cols->end (); ++i)
+ {
+ if (i != b)
+ where += " AND ";
+
+ where += quote_id (i->name) + "=" +
+ convert_to (qp->next (*i), i->type, *i->member);
+ }
+
+ if (s.optimistic ()) // Note: not update_opt.
+ {
+ string name (column_qname (*opt, column_prefix ()));
+ string type (column_type (*opt));
+
+ where += " AND " + name + "=" +
+ convert_to (qp->next (*opt, name, type), type, *opt);
+ }
+
+ os << strlit (where) << ";"
+ << endl;
+ }
+
+ // load ()
+ //
+ if (load || load_opt || load_con)
+ {
+ os << "void " << scope << "::" << endl
+ << "load (extra_statement_cache_type& esc, object_type& obj" <<
+ (poly ? ", bool top" : "") << ")"
+ << "{";
+
+ if (poly)
+ os << "ODB_POTENTIALLY_UNUSED (top);"
+ << endl;
+
+ if (s.versioned || s.versioned_containers)
+ os << "const schema_version_migration& svm (" << endl
+ << "esc." << m.name () << ".version_migration (" <<
+ schema_name << "));"
+ << endl;
+
+ // Load values, if any.
+ //
+ if (load || load_opt)
+ {
+ // The SELECT statement for the top override loads all the
+ // values.
+ //
+ if (poly)
+ os << "if (top)"
+ << "{";
+
+ // Note that we don't use delayed load machinery here. While
+ // a section can definitely contain self-referencing pointers,
+ // loading such a pointer won't mess up the data members in the
+ // image that we care about. It also holds true for streaming
+ // result, since the bindings are different.
+
+ os << "using namespace " << db << ";"
+ << "using " << db << "::select_statement;" // Conflicts.
+ << endl
+ << "statements_type& sts (esc." << m.name () << ");"
+ << endl
+ << "image_type& im (sts.image ());"
+ << "binding& imb (sts.select_image_binding ());"
+ << endl;
+
+ // For the polymorphic case, instead of storing an array of
+ // versions as we do for objects, we will add all the versions
+ // up and use that as a cumulative image chain version. If you
+ // meditate a bit on that, you will realize that it will work
+ // (hint: versions can only increase).
+ //
+ string ver;
+ string ver_decl;
+
+ if (s.base != 0 && poly_derived)
+ {
+ ver = "imv";
+ ver_decl = "std::size_t imv (im.version";
+
+ user_section* b (s.base);
+ string acc ("im.base");
+ for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
+ {
+ if (b->object == bo)
+ {
+ if (b->total != 0 || b->optimistic ())
+ ver_decl += " +\n" + acc + "->version";
+
+ b = b->base;
+ if (b == 0 || !polymorphic (*b->object))
+ {
+ b = 0;
+ break;
+ }
+ }
+ acc += "->base";
+ }
+
+ ver_decl += ")";
+
+ os << ver_decl << ";"
+ << endl;
+ }
+ else
+ ver = "im.version";
+
+ os << "if (" << ver << " != sts.select_image_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, 0, 0, im, statement_select" <<
+ (s.versioned ? ", svm" : "") << ");"
+ << "sts.select_image_version (" << ver << ");"
+ << "imb.version++;"
+ << "}";
+
+ // Id binding is assumed initialized and bound.
+ //
+ os << "select_statement& st (sts.select_statement ());";
+
+ // The statement can be dynamically empty.
+ //
+ if (s.versioned)
+ os << "if (!st.empty ())"
+ << "{";
+
+ os << "st.execute ();"
+ << "auto_result ar (st);"
+ << "select_statement::result r (st.fetch ());"
+ << endl;
+
+ os << "if (r == select_statement::no_data)" << endl
+ << "throw object_not_persistent ();"
+ << endl;
+
+ if (grow (c_, &s))
+ {
+ os << "if (r == select_statement::truncated)"
+ << "{"
+ << "if (grow (im, sts.select_image_truncated ()" <<
+ (s.versioned ? ", svm" : "") << "))" << endl
+ << "im.version++;"
+ << endl;
+
+ // The same logic as above.
+ //
+ if (s.base != 0 && poly_derived)
+ os << ver_decl << ";"
+ << endl;
+
+ os << "if (" << ver << " != sts.select_image_version ())"
+ << "{"
+ << "bind (imb.bind, 0, 0, im, statement_select" <<
+ (s.versioned ? ", svm" : "") << ");"
+ << "sts.select_image_version (" << ver << ");"
+ << "imb.version++;"
+ << "st.refetch ();"
+ << "}"
+ << "}";
+ }
+
+ if (opt != 0) // Not load_opt, we do it in poly-derived as well.
+ {
+ os << "if (";
+
+ if (poly_derived)
+ {
+ os << "root_traits::version (*im.base";
+ for (class_* b (poly_base);
+ b != poly_root;
+ b = &polymorphic_base (*b))
+ os << "->base";
+ os << ")";
+ }
+ else
+ os << "version (im)";
+
+ os << " != " << (poly_derived ? "root_traits::" : "") <<
+ "version (obj))" << endl
+ << "throw object_changed ();"
+ << endl;
+ }
+
+ if (load)
+ {
+ os << "init (obj, im, &sts.connection ().database ()" <<
+ (s.versioned ? ", svm" : "") << ");";
+ init_value_extra (); // Stream results, etc.
+ os << endl;
+ }
+
+ if (s.versioned)
+ os << "}"; // if (!st.empty ())
+
+ if (poly)
+ os << "}"; // if (top)
+ }
+
+ // Call base to load its containers, if this is an override.
+ //
+ if (poly_derived && s.base != 0)
+ {
+ user_section* b (s.base);
+ for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
+ {
+ if (b->object == bo)
+ {
+ // If we don't have any values of our own but out base
+ // does, then allow it to load them.
+ //
+ if (b->containers ||
+ (!load && (b->total != 0 || b->optimistic ())))
+ break;
+
+ b = b->base;
+ if (b == 0 || !polymorphic (*b->object))
+ {
+ b = 0;
+ break;
+ }
+ }
+ }
+
+ // This one is tricky: ideally we would do a direct call to
+ // the base's load() (which may not be our immediate base,
+ // BTW) but there is no easy way to resolve base's extra
+ // statements from ours. So, instead, we are going to go
+ // via the dispatch machinery which requires a connection
+ // rather than statements. Not the most efficient way but
+ // simple.
+
+ // Find the "previous" override by starting the search from
+ // our base.
+ //
+ if (b != 0)
+ {
+ // Note that here we are using the base section index to
+ // handle the special version update base.
+ //
+ os << "info.base->find_section_load (" << b->index << "UL) (" <<
+ "esc." << m.name () << ".connection (), obj, " <<
+ // If we don't have any values of our own, then allow the
+ // base load its.
+ //
+ (load ? "false" : "top") << ");"
+ << endl;
+ }
+ }
+
+ // Load our containers, if any.
+ //
+ if (s.containers)
+ {
+ instance<container_calls> t (container_calls::load_call, &s);
+ t->traverse (c_);
+ }
+
+ os << "}";
+ }
+
+ // update ()
+ //
+ if (update || update_opt || update_con)
+ {
+ os << "void " << scope << "::" << endl
+ << "update (extra_statement_cache_type& esc, " <<
+ "const object_type& obj" <<
+ (poly_derived && s.base != 0 ? ", bool base" : "") << ")"
+ << "{";
+
+ // Call base if this is an override.
+ //
+ if (poly_derived && s.base != 0)
+ {
+ user_section* b (s.base);
+ for (class_* bo (poly_base);; bo = &polymorphic_base (*bo))
+ {
+ if (b->object == bo)
+ {
+ if (b->total != b->inverse + b->readonly ||
+ b->readwrite_containers ||
+ (poly && b->optimistic ()))
+ break;
+
+ b = b->base;
+ if (b == 0 || !polymorphic (*b->object))
+ {
+ b = 0;
+ break;
+ }
+ }
+ }
+
+ // The same (tricky) logic as in load(). Note that here we are
+ // using the base section index to handle the special version
+ // update base.
+ //
+ if (b != 0)
+ os << "if (base)" << endl
+ << "info.base->find_section_update (" << b->index <<
+ "UL) (esc." << m.name () << ".connection (), obj);"
+ << endl;
+ else
+ os << "ODB_POTENTIALLY_UNUSED (base);"
+ << endl;
+ }
+
+ if (s.versioned || s.readwrite_versioned_containers)
+ os << "const schema_version_migration& svm (" << endl
+ << "esc." << m.name () << ".version_migration (" <<
+ schema_name << "));"
+ << endl;
+
+ // Update values, if any.
+ //
+ if (update || update_opt)
+ {
+ os << "using namespace " << db << ";"
+ << "using " << db << "::update_statement;" // Conflicts.
+ << endl
+ << "statements_type& sts (esc." << m.name () << ");"
+ << endl
+ << "image_type& im (sts.image ());"
+ << "const binding& id (sts.idv_binding ());" // id+version
+ << "binding& imb (sts.update_image_binding ());"
+ << endl;
+
+ if (update)
+ {
+ if (generate_grow)
+ os << "if (";
+
+ os << "init (im, obj" << (s.versioned ? ", svm" : "") << ")";
+
+ if (generate_grow)
+ os << ")" << endl
+ << "im.version++";
+
+ os << ";"
+ << endl;
+ }
+
+ os << "if (im.version != sts.update_image_version () ||" << endl
+ << "id.version != sts.update_id_binding_version () ||" << endl
+ << "imb.version == 0)"
+ << "{"
+ << "bind (imb.bind, id.bind, id.count, im, statement_update" <<
+ (s.versioned ? ", svm" : "") << ");"
+ << "sts.update_image_version (im.version);"
+ << "sts.update_id_binding_version (id.version);"
+ << "imb.version++;"
+ << "}";
+
+ os << "update_statement& st (sts.update_statement ());"
+ << "if (";
+
+ if (s.versioned)
+ os << "!st.empty () && ";
+
+ os << "st.execute () == 0)" << endl;
+
+ if (opt == 0)
+ os << "throw object_not_persistent ();";
+ else
+ os << "throw object_changed ();";
+
+ os << endl;
+ }
+
+ // Update readwrite containers if any.
+ //
+ if (s.readwrite_containers)
+ {
+ instance<container_calls> t (container_calls::update_call, &s);
+ t->traverse (c_);
+ }
+
+ // Update the optimistic concurrency version in the object member.
+ // Very similar code to object.
+ //
+ if (s.optimistic ()) // Note: not update_opt.
+ {
+ // Object is passed as const reference so we need to cast away
+ // constness.
+ //
+ const char* obj ("const_cast<object_type&> (obj)");
+ string inc (optimistic_version_increment (*opt));
+
+ if (inc == "1")
+ inc_member (*opt, obj, "obj", "version_type");
+ else
+ set_member (*opt, obj, inc, "", "version_type");
+ }
+
+ os << "}";
+ }
+
+ section_extra (s);
+
+ if (rs != 0)
+ rs->base = 0;
+ }
+
+ using class_::traverse; // Unhide.
+
+ protected:
+ semantics::class_& c_;
+ string scope_;
+ };
+
+ // Output a list of parameters for the persist statement.
+ //
+ struct persist_statement_params: object_columns_base, virtual context
+ {
+ typedef persist_statement_params base;
+
+ persist_statement_params (string& params,
+ query_parameters& qp,
+ const string& sep)
+ : params_ (params), qp_ (qp), sep_ (sep)
+ {
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ if (!inverse (m, key_prefix_))
+ object_columns_base::traverse_pointer (m, c);
+ }
+
+ virtual bool
+ traverse_column (semantics::data_member& m,
+ string const& name,
+ bool first)
+ {
+ string p;
+
+ if (version (m))
+ p = version_value (m);
+ else
+ {
+ const string& qname (quote_id (name));
+ const string& type (column_type ());
+
+ p = auto_ (m) // Only simple, direct id can be auto.
+ ? qp_.auto_id (m, qname, type)
+ : qp_.next (m, qname, type);
+ }
+
+ if (!p.empty ())
+ {
+ if (!first)
+ {
+ params_ += ',';
+ params_ += sep_;
+ }
+
+ params_ += (p != "DEFAULT" ? convert_to (p, column_type (), m) : p);
+ }
+
+ return !p.empty ();
+ }
+
+ virtual string
+ version_value (semantics::data_member&)
+ {
+ return "1";
+ }
+
+ private:
+ string& params_;
+ query_parameters& qp_;
+ const string& sep_;
+ };
+
+ //
+ //
+ struct class_: traversal::class_, virtual context
+ {
+ typedef class_ base;
+
+ class_ ()
+ : typedefs_ (false),
+ query_columns_type_ (false, false, false),
+ view_query_columns_type_ (false),
+ index_ (0),
+ grow_base_ (index_),
+ grow_member_ (index_),
+ grow_version_member_ (index_, "version_"),
+ grow_discriminator_member_ (index_, "discriminator_"),
+ bind_id_member_ ("id_"),
+ bind_version_member_ ("version_"),
+ bind_discriminator_member_ ("discriminator_"),
+ init_id_image_member_ ("id_", "id"),
+ init_version_image_member_ ("version_", "(*v)"),
+ init_view_pointer_member_pre_ (true, *init_value_member_),
+ init_view_pointer_member_post_ (false, *init_value_member_),
+ init_id_value_member_ ("id"),
+ init_id_value_member_id_image_ ("id", "id_"),
+ init_version_value_member_ ("v"),
+ init_named_version_value_member_ ("v", "version_"),
+ init_discriminator_value_member_ ("d", "", false),
+ init_named_discriminator_value_member_ (
+ "d", "discriminator_", false)
+ {
+ init ();
+ }
+
+ class_ (class_ const&)
+ : root_context (), //@@ -Wextra
+ context (),
+ typedefs_ (false),
+ query_columns_type_ (false, false, false),
+ view_query_columns_type_ (false),
+ index_ (0),
+ grow_base_ (index_),
+ grow_member_ (index_),
+ grow_version_member_ (index_, "version_"),
+ grow_discriminator_member_ (index_, "discriminator_"),
+ bind_id_member_ ("id_"),
+ bind_version_member_ ("version_"),
+ bind_discriminator_member_ ("discriminator_"),
+ init_id_image_member_ ("id_", "id"),
+ init_version_image_member_ ("version_", "(*v)"),
+ init_view_pointer_member_pre_ (true, *init_value_member_),
+ init_view_pointer_member_post_ (false, *init_value_member_),
+ init_id_value_member_ ("id"),
+ init_id_value_member_id_image_ ("id", "id_"),
+ init_version_value_member_ ("v"),
+ init_named_version_value_member_ ("v", "version_"),
+ init_discriminator_value_member_ ("d", "", false),
+ init_named_discriminator_value_member_ (
+ "d", "discriminator_", false)
+ {
+ init ();
+ }
+
+ void
+ init ()
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+
+ if (generate_grow)
+ {
+ grow_base_inherits_ >> grow_base_;
+ grow_member_names_ >> grow_member_;
+ }
+
+ bind_base_inherits_ >> bind_base_;
+ bind_member_names_ >> bind_member_;
+
+ init_image_base_inherits_ >> init_image_base_;
+ init_image_member_names_ >> init_image_member_;
+
+ init_value_base_inherits_ >> init_value_base_;
+ init_value_member_names_ >> init_value_member_;
+
+ init_view_pointer_member_pre_names_ >> init_view_pointer_member_pre_;
+ init_view_pointer_member_post_names_ >> init_view_pointer_member_post_;
+ }
+
+ virtual void
+ init_auto_id (semantics::data_member&, // id member
+ string const&) // image variable prefix
+ {
+ if (insert_send_auto_id)
+ assert (false);
+ }
+
+ virtual void
+ init_image_pre (type&)
+ {
+ }
+
+ virtual void
+ init_value_extra ()
+ {
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck == class_other ||
+ (!options.at_once () && class_file (c) != unit.file ()))
+ return;
+
+ names (c);
+
+ context::top_object = context::cur_object = &c;
+
+ switch (ck)
+ {
+ case class_object: traverse_object (c); break;
+ case class_view: traverse_view (c); break;
+ case class_composite: traverse_composite (c); break;
+ default: break;
+ }
+
+ context::top_object = context::cur_object = 0;
+ }
+
+ //
+ // statements
+ //
+
+ enum persist_position
+ {
+ persist_after_columns,
+ persist_after_values
+ };
+
+ virtual string
+ persist_statement_extra (type&, query_parameters&, persist_position)
+ {
+ return "";
+ }
+
+ virtual string
+ update_statement_extra (type&)
+ {
+ return "";
+ }
+
+ //
+ // common
+ //
+
+ virtual void
+ post_query_ (type&, bool /*once_off*/)
+ {
+ }
+
+ virtual void
+ process_statement_columns (statement_columns&,
+ statement_kind,
+ bool /*dynamic*/)
+ {
+ }
+
+ //
+ // object
+ //
+
+ virtual void
+ object_extra (type&) {}
+
+ virtual void
+ extra_statement_cache_extra_args (bool /*containers*/,
+ bool /*sections*/) {}
+
+ virtual void
+ object_query_statement_ctor_args (type&,
+ std::string const& q,
+ bool process,
+ bool /*prepared*/)
+ {
+ os << "conn," << endl
+ << "text," << endl
+ << process << "," << endl // Process.
+ << "true," << endl // Optimize.
+ << q << ".parameters_binding ()," << endl
+ << "imb";
+ }
+
+ virtual void
+ object_erase_query_statement_ctor_args (type&)
+ {
+ os << "conn," << endl
+ << "text," << endl
+ << "q.parameters_binding ()";
+ }
+
+ virtual string
+ optimistic_version_init (semantics::data_member&, bool /*index*/ = false)
+ {
+ return "1";
+ }
+
+ // Returning "1" means increment by one.
+ //
+ virtual string
+ optimistic_version_increment (semantics::data_member&,
+ bool /*index*/ = false)
+ {
+ return "1";
+ }
+
+ virtual bool
+ optimistic_insert_bind_version (semantics::data_member&)
+ {
+ return false;
+ }
+
+ virtual void
+ traverse_object (type& c);
+
+ //
+ // view
+ //
+
+ virtual void
+ view_extra (type&)
+ {
+ }
+
+ virtual void
+ view_query_statement_ctor_args (type&,
+ string const& q,
+ bool process,
+ bool /*prepared*/)
+ {
+ os << "conn," << endl
+ << q << ".clause ()," << endl
+ << process << "," << endl // Process.
+ << "true," << endl // Optimize.
+ << q << ".parameters_binding ()," << endl
+ << "imb";
+ }
+
+ virtual string
+ from_trailer (type&) { return "";}
+
+ virtual string
+ select_trailer (type& c)
+ {
+ return c.get<view_query> ("query").for_update ? "FOR UPDATE" : "";
+ }
+
+ virtual string
+ join_syntax (view_object const& vo)
+ {
+ const char* r (0);
+
+ switch (vo.join)
+ {
+ case view_object::left: r = "LEFT JOIN"; break;
+ case view_object::right: r = "RIGHT JOIN"; break;
+ case view_object::full: r = "FULL JOIN"; break;
+ case view_object::inner: r = "INNER JOIN"; break;
+ case view_object::cross: r = "CROSS JOIN"; break;
+ }
+
+ return r;
+ }
+
+ virtual void
+ traverse_view (type& c);
+
+ struct expression
+ {
+ explicit
+ expression (std::string const& v): kind (literal), value (v) {}
+ expression (view_object* vo): kind (pointer), vo (vo) {}
+
+ enum kind_type {literal, pointer};
+
+ kind_type kind;
+ std::string value;
+ data_member_path member_path;
+ view_object* vo;
+ };
+
+ expression
+ translate_expression (type& c,
+ cxx_tokens const&,
+ semantics::scope& start_scope,
+ location_t loc,
+ string const& prag,
+ bool* placeholder = 0,
+ bool predicate = true);
+ //
+ // composite
+ //
+
+ virtual void
+ traverse_composite (type& c)
+ {
+ bool versioned (context::versioned (c));
+
+ string const& type (class_fq_name (c));
+ string traits ("access::composite_value_traits< " + type + ", id_" +
+ db.string () + " >");
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl
+ << endl;
+
+ // Containers.
+ //
+ {
+ instance<container_traits> t (c);
+ t->traverse (c);
+ }
+
+ // grow ()
+ //
+ if (generate_grow)
+ {
+ os << "bool " << traits << "::" << endl
+ << "grow (image_type& i," << endl
+ << truncated_vector << " t";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (t);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "bool grew (false);"
+ << endl;
+
+ index_ = 0;
+ inherits (c, grow_base_inherits_);
+ names (c, grow_member_names_);
+
+ os << "return grew;"
+ << "}";
+ }
+
+ // bind (image_type)
+ //
+ os << "void " << traits << "::" << endl
+ << "bind (" << bind_vector << " b," << endl
+ << "image_type& i," << endl
+ << db << "::statement_kind sk";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (b);"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (sk);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "using namespace " << db << ";"
+ << endl;
+
+ if (readonly (c))
+ os << "assert (sk != statement_update);"
+ << endl;
+
+ os << "std::size_t n (0);"
+ << "ODB_POTENTIALLY_UNUSED (n);"
+ << endl;
+
+ inherits (c, bind_base_inherits_);
+ names (c, bind_member_names_);
+
+ os << "}";
+
+ // init (image, value)
+ //
+ os << (generate_grow ? "bool " : "void ") << traits << "::" << endl
+ << "init (image_type& i," << endl
+ << "const value_type& o," << endl
+ << db << "::statement_kind sk";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (o);"
+ << "ODB_POTENTIALLY_UNUSED (sk);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl
+ << "using namespace " << db << ";"
+ << endl;
+
+ if (readonly (c))
+ os << "assert (sk != statement_update);"
+ << endl;
+
+ if (generate_grow)
+ os << "bool grew (false);"
+ << endl;
+
+ inherits (c, init_image_base_inherits_);
+ names (c, init_image_member_names_);
+
+ if (generate_grow)
+ os << "return grew;";
+
+ os << "}";
+
+ // init (value, image)
+ //
+ os << "void " << traits << "::" << endl
+ << "init (value_type& o," << endl
+ << "const image_type& i," << endl
+ << "database* db";
+
+ if (versioned)
+ os << "," << endl
+ << "const schema_version_migration& svm";
+
+ os << ")"
+ << "{"
+ << "ODB_POTENTIALLY_UNUSED (o);"
+ << "ODB_POTENTIALLY_UNUSED (i);"
+ << "ODB_POTENTIALLY_UNUSED (db);";
+
+ if (versioned)
+ os << "ODB_POTENTIALLY_UNUSED (svm);";
+
+ os << endl;
+
+ inherits (c, init_value_base_inherits_);
+ names (c, init_value_member_names_);
+
+ os << "}";
+ }
+
+ private:
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ instance<query_columns_type> query_columns_type_;
+ instance<view_query_columns_type> view_query_columns_type_;
+
+ size_t index_;
+ instance<grow_base> grow_base_;
+ traversal::inherits grow_base_inherits_;
+ instance<grow_member> grow_member_;
+ traversal::names grow_member_names_;
+ instance<grow_member> grow_version_member_;
+ instance<grow_member> grow_discriminator_member_;
+
+
+ instance<bind_base> bind_base_;
+ traversal::inherits bind_base_inherits_;
+ instance<bind_member> bind_member_;
+ traversal::names bind_member_names_;
+ instance<bind_member> bind_id_member_;
+ instance<bind_member> bind_version_member_;
+ instance<bind_member> bind_discriminator_member_;
+
+ instance<init_image_base> init_image_base_;
+ traversal::inherits init_image_base_inherits_;
+ instance<init_image_member> init_image_member_;
+ traversal::names init_image_member_names_;
+
+ instance<init_image_member> init_id_image_member_;
+ instance<init_image_member> init_version_image_member_;
+
+ instance<init_value_base> init_value_base_;
+ traversal::inherits init_value_base_inherits_;
+ instance<init_value_member> init_value_member_;
+ traversal::names init_value_member_names_;
+
+ instance<init_view_pointer_member> init_view_pointer_member_pre_;
+ instance<init_view_pointer_member> init_view_pointer_member_post_;
+ traversal::names init_view_pointer_member_pre_names_;
+ traversal::names init_view_pointer_member_post_names_;
+
+ instance<init_value_member> init_id_value_member_;
+ instance<init_value_member> init_id_value_member_id_image_;
+ instance<init_value_member> init_version_value_member_;
+ instance<init_value_member> init_named_version_value_member_;
+ instance<init_value_member> init_discriminator_value_member_;
+ instance<init_value_member> init_named_discriminator_value_member_;
+ };
+
+ struct include: virtual context
+ {
+ typedef include base;
+
+ virtual void
+ generate ()
+ {
+ extra_pre ();
+
+ os << "#include <cassert>" << endl
+ << "#include <cstring> // std::memcpy" << endl;
+
+ if (features.polymorphic_object)
+ os << "#include <typeinfo>" << endl;
+
+ os << endl;
+
+ if (features.polymorphic_object)
+ os << "#include <odb/polymorphic-map.hxx>" << endl;
+
+ if (embedded_schema)
+ os << "#include <odb/schema-catalog-impl.hxx>" << endl;
+
+ if (multi_dynamic)
+ os << "#include <odb/function-table.hxx>" << endl;
+
+ os << endl;
+
+ os << "#include <odb/" << db << "/traits.hxx>" << endl
+ << "#include <odb/" << db << "/database.hxx>" << endl
+ << "#include <odb/" << db << "/transaction.hxx>" << endl
+ << "#include <odb/" << db << "/connection.hxx>" << endl
+ << "#include <odb/" << db << "/statement.hxx>" << endl
+ << "#include <odb/" << db << "/statement-cache.hxx>" << endl;
+
+ if (features.simple_object)
+ os << "#include <odb/" << db << "/simple-object-statements.hxx>" << endl;
+
+ if (features.polymorphic_object)
+ os << "#include <odb/" << db << "/polymorphic-object-statements.hxx>" << endl;
+
+ if (features.no_id_object)
+ os << "#include <odb/" << db << "/no-id-object-statements.hxx>" << endl;
+
+ if (features.view)
+ os << "#include <odb/" << db << "/view-statements.hxx>" << endl;
+
+ if (features.section)
+ os << "#include <odb/" << db << "/section-statements.hxx>" << endl;
+
+ os << "#include <odb/" << db << "/container-statements.hxx>" << endl
+ << "#include <odb/" << db << "/exceptions.hxx>" << endl;
+
+ if (options.generate_query ())
+ {
+ if (options.generate_prepared ())
+ os << "#include <odb/" << db << "/prepared-query.hxx>" << endl;
+
+ if (features.simple_object)
+ os << "#include <odb/" << db << "/simple-object-result.hxx>" << endl;
+
+ if (features.polymorphic_object)
+ os << "#include <odb/" << db << "/polymorphic-object-result.hxx>" << endl;
+
+ if (features.no_id_object)
+ os << "#include <odb/" << db << "/no-id-object-result.hxx>" << endl;
+
+ if (features.view)
+ os << "#include <odb/" << db << "/view-result.hxx>" << endl;
+ }
+
+ extra_post ();
+
+ os << endl;
+ }
+
+ virtual void
+ extra_pre ()
+ {
+ }
+
+ virtual void
+ extra_post ()
+ {
+ }
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_SOURCE_HXX
diff --git a/odb/odb/relational/sqlite/common.cxx b/odb/odb/relational/sqlite/common.cxx
new file mode 100644
index 0000000..03a3599
--- /dev/null
+++ b/odb/odb/relational/sqlite/common.cxx
@@ -0,0 +1,217 @@
+// file : odb/relational/sqlite/common.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cassert>
+
+#include <odb/relational/sqlite/common.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace sqlite
+ {
+ //
+ // member_base
+ //
+
+ sql_type const& member_base::
+ member_sql_type (semantics::data_member& m)
+ {
+ return parse_sql_type (column_type (m, key_prefix_), m);
+ }
+
+ void member_base::
+ traverse_simple (member_info& mi)
+ {
+ switch (mi.st->type)
+ {
+ case sql_type::INTEGER:
+ {
+ traverse_integer (mi);
+ break;
+ }
+ case sql_type::REAL:
+ {
+ traverse_real (mi);
+ break;
+ }
+ case sql_type::TEXT:
+ {
+ if (mi.st->stream)
+ traverse_text_stream (mi);
+ else
+ traverse_text (mi);
+ break;
+ }
+ case sql_type::BLOB:
+ {
+ if (mi.st->stream)
+ traverse_blob_stream (mi);
+ else
+ traverse_blob (mi);
+ break;
+ }
+ case sql_type::invalid:
+ {
+ assert (false);
+ break;
+ }
+ }
+ }
+
+ //
+ // member_image_type
+ //
+
+ member_image_type::
+ member_image_type (base const& x)
+ : member_base::base (x), // virtual base
+ base (x) {}
+
+ member_image_type::
+ member_image_type ()
+ : relational::member_base (0, 0, string (), string ()) {}
+
+ member_image_type::
+ member_image_type (semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : relational::member_base (type, ct, fq_type, key_prefix) {}
+
+ string member_image_type::
+ image_type (semantics::data_member& m)
+ {
+ type_.clear ();
+ member_base::traverse (m, true);
+ return type_;
+ }
+
+ void member_image_type::
+ traverse_composite (member_info& mi)
+ {
+ type_ = "composite_value_traits< " + mi.fq_type () +
+ ", id_sqlite >::image_type";
+ }
+
+ void member_image_type::
+ traverse_integer (member_info&)
+ {
+ type_ = "long long";
+ }
+
+ void member_image_type::
+ traverse_real (member_info&)
+ {
+ type_ = "double";
+ }
+
+ void member_image_type::
+ traverse_string (member_info&)
+ {
+ type_ = "details::buffer";
+ }
+
+ void member_image_type::
+ traverse_stream (member_info&)
+ {
+ type_ = "sqlite::stream_buffers";
+ }
+
+ entry<member_image_type> member_image_type_;
+
+ //
+ // member_database_type
+ //
+
+ member_database_type_id::
+ member_database_type_id (base const& x)
+ : member_base::base (x), // virtual base
+ base (x) {}
+
+ member_database_type_id::
+ member_database_type_id ()
+ : member_base::base (0, 0, string (), string ()), // virtual base
+ base (0, 0, string (), string ()) {}
+
+ member_database_type_id::
+ member_database_type_id (semantics::type* type,
+ const custom_cxx_type* ct,
+ string const& fq_type,
+ string const& key_prefix)
+ : member_base::base (type, ct, fq_type, key_prefix), // virtual base
+ base (type, ct, fq_type, key_prefix) {}
+
+ string member_database_type_id::
+ database_type_id (type& m)
+ {
+ type_id_.clear ();
+ member_base::traverse (m, true);
+ return type_id_;
+ }
+
+ void member_database_type_id::
+ traverse_composite (member_info&)
+ {
+ assert (false);
+ }
+
+ void member_database_type_id::
+ traverse_integer (member_info&)
+ {
+ type_id_ = "sqlite::id_integer";
+ }
+
+ void member_database_type_id::
+ traverse_real (member_info&)
+ {
+ type_id_ = "sqlite::id_real";
+ }
+
+ void member_database_type_id::
+ traverse_text (member_info&)
+ {
+ type_id_ = "sqlite::id_text";
+ }
+
+ void member_database_type_id::
+ traverse_blob (member_info&)
+ {
+ type_id_ = "sqlite::id_blob";
+ }
+
+ void member_database_type_id::
+ traverse_text_stream (member_info&)
+ {
+ type_id_ = "sqlite::id_text_stream";
+ }
+
+ void member_database_type_id::
+ traverse_blob_stream (member_info&)
+ {
+ type_id_ = "sqlite::id_blob_stream";
+ }
+
+ entry<member_database_type_id> member_database_type_id_;
+
+ //
+ // query_columns
+ //
+
+ struct query_columns: relational::query_columns, context
+ {
+ query_columns (base const& x): base_impl (x) {}
+
+ virtual string
+ database_type_id (semantics::data_member& m)
+ {
+ return member_database_type_id_.database_type_id (m);
+ }
+
+ private:
+ member_database_type_id member_database_type_id_;
+ };
+ entry<query_columns> query_columns_;
+ }
+}
diff --git a/odb/odb/relational/sqlite/common.hxx b/odb/odb/relational/sqlite/common.hxx
new file mode 100644
index 0000000..4d6089e
--- /dev/null
+++ b/odb/odb/relational/sqlite/common.hxx
@@ -0,0 +1,147 @@
+// file : odb/relational/sqlite/common.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_SQLITE_COMMON_HXX
+#define ODB_RELATIONAL_SQLITE_COMMON_HXX
+
+#include <odb/relational/common.hxx>
+#include <odb/relational/sqlite/context.hxx>
+
+namespace relational
+{
+ namespace sqlite
+ {
+ struct member_base: virtual relational::member_base_impl<sql_type>, context
+ {
+ member_base (base const& x): base (x), base_impl (x) {}
+
+ // This c-tor is for the direct use inside the sqlite namespace.
+ // If you do use this c-tor, you should also explicitly call
+ // relational::member_base (aka base).
+ //
+ member_base () {}
+
+ virtual sql_type const&
+ member_sql_type (semantics::data_member&);
+
+ virtual void
+ traverse_simple (member_info&);
+
+ virtual void
+ traverse_integer (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_real (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_text (member_info& m)
+ {
+ traverse_string (m);
+ }
+
+ virtual void
+ traverse_blob (member_info& m)
+ {
+ traverse_string (m);
+ }
+
+ // String covers both text and blob.
+ //
+ virtual void
+ traverse_string (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_text_stream (member_info& m)
+ {
+ traverse_stream (m);
+ }
+
+ virtual void
+ traverse_blob_stream (member_info& m)
+ {
+ traverse_stream (m);
+ }
+
+ virtual void
+ traverse_stream (member_info&)
+ {
+ }
+ };
+
+ struct member_image_type: relational::member_image_type,
+ member_base
+ {
+ member_image_type (base const&);
+ member_image_type ();
+ member_image_type (semantics::type* type,
+ const custom_cxx_type*,
+ string const& fq_type = string (),
+ string const& key_prefix = string ());
+ virtual string
+ image_type (semantics::data_member&);
+
+ virtual void
+ traverse_composite (member_info&);
+
+ virtual void
+ traverse_integer (member_info&);
+
+ virtual void
+ traverse_real (member_info&);
+
+ virtual void
+ traverse_string (member_info&);
+
+ virtual void
+ traverse_stream (member_info&);
+
+ private:
+ string type_;
+ };
+
+ struct member_database_type_id: relational::member_database_type_id,
+ member_base
+ {
+ member_database_type_id (base const&);
+ member_database_type_id ();
+ member_database_type_id (semantics::type* type,
+ const custom_cxx_type*,
+ string const& fq_type = string (),
+ string const& key_prefix = string ());
+
+ virtual string
+ database_type_id (type&);
+
+ virtual void
+ traverse_composite (member_info&);
+
+ virtual void
+ traverse_integer (member_info&);
+
+ virtual void
+ traverse_real (member_info&);
+
+ virtual void
+ traverse_text (member_info&);
+
+ virtual void
+ traverse_blob (member_info&);
+
+ virtual void
+ traverse_text_stream (member_info&);
+
+ virtual void
+ traverse_blob_stream (member_info&);
+
+ private:
+ string type_id_;
+ };
+ }
+}
+#endif // ODB_RELATIONAL_SQLITE_COMMON_HXX
diff --git a/odb/odb/relational/sqlite/context.cxx b/odb/odb/relational/sqlite/context.cxx
new file mode 100644
index 0000000..9a4369f
--- /dev/null
+++ b/odb/odb/relational/sqlite/context.cxx
@@ -0,0 +1,490 @@
+// file : odb/relational/sqlite/context.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <vector>
+#include <cassert>
+#include <sstream>
+
+#include <odb/sql-token.hxx>
+#include <odb/sql-lexer.hxx>
+
+#include <odb/relational/sqlite/context.hxx>
+#include <odb/relational/sqlite/common.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace sqlite
+ {
+ namespace
+ {
+ struct type_map_entry
+ {
+ char const* const cxx_type;
+ char const* const db_type;
+ char const* const db_id_type;
+ bool const null;
+ };
+
+ type_map_entry type_map[] =
+ {
+ {"bool", "INTEGER", 0, false},
+
+ {"char", "TEXT", 0, false},
+ {"wchar_t", "TEXT", 0, false},
+ {"signed char", "INTEGER", 0, false},
+ {"unsigned char", "INTEGER", 0, false},
+
+ {"short int", "INTEGER", 0, false},
+ {"short unsigned int", "INTEGER", 0, false},
+
+ {"int", "INTEGER", 0, false},
+ {"unsigned int", "INTEGER", 0, false},
+
+ {"long int", "INTEGER", 0, false},
+ {"long unsigned int", "INTEGER", 0, false},
+
+ {"long long int", "INTEGER", 0, false},
+ {"long long unsigned int", "INTEGER", 0, false},
+
+ // SQLite stores NaN as NULL.
+ //
+ {"float", "REAL", 0, true},
+ {"double", "REAL", 0, true},
+
+ {"::std::string", "TEXT", 0, false},
+ {"::std::wstring", "TEXT", 0, false}
+ };
+ }
+
+ context* context::current_;
+
+ context::
+ ~context ()
+ {
+ if (current_ == this)
+ current_ = 0;
+ }
+
+ context::
+ context (ostream& os,
+ semantics::unit& u,
+ options_type const& ops,
+ features_type& f,
+ sema_rel::model* m)
+ : root_context (os, u, ops, f, data_ptr (new (shared) data (os))),
+ base_context (static_cast<data*> (root_context::data_.get ()), m),
+ data_ (static_cast<data*> (base_context::data_))
+ {
+ assert (current_ == 0);
+ current_ = this;
+
+ generate_grow = true;
+ need_alias_as = true;
+ insert_send_auto_id = true;
+ delay_freeing_statement_result = false;
+ need_image_clone = false;
+ generate_bulk = false;
+ global_index = true;
+ global_fkey = false;
+ data_->bind_vector_ = "sqlite::bind*";
+ data_->truncated_vector_ = "bool*";
+
+ // Populate the C++ type to DB type map.
+ //
+ for (size_t i (0); i < sizeof (type_map) / sizeof (type_map_entry); ++i)
+ {
+ type_map_entry const& e (type_map[i]);
+
+ type_map_type::value_type v (
+ e.cxx_type,
+ db_type_type (
+ e.db_type, e.db_id_type ? e.db_id_type : e.db_type, e.null));
+
+ data_->type_map_.insert (v);
+ }
+ }
+
+ context::
+ context ()
+ : data_ (current ().data_)
+ {
+ }
+
+ string const& context::
+ convert_expr (string const& sqlt, semantics::data_member& m, bool to)
+ {
+ sql_type const& t (parse_sql_type (sqlt, m));
+ return to ? t.to : t.from;
+ }
+
+ namespace
+ {
+ struct has_grow: traversal::class_
+ {
+ has_grow (bool& r, user_section* s)
+ : r_ (r), section_ (s)
+ {
+ *this >> inherits_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ // Ignore transient bases.
+ //
+ if (!(context::object (c) || context::composite (c)))
+ return;
+
+ if (section_ == 0 && c.count ("sqlite-grow"))
+ r_ = c.get<bool> ("sqlite-grow");
+ else
+ {
+ // r_ should be false.
+ //
+ inherits (c);
+
+ if (!r_)
+ names (c);
+
+ if (section_ == 0)
+ c.set ("sqlite-grow", r_);
+ }
+ }
+
+ private:
+ bool& r_;
+ user_section* section_;
+ traversal::inherits inherits_;
+ };
+
+ struct has_grow_member: member_base
+ {
+ has_grow_member (bool& r, user_section* section = 0)
+ : relational::member_base (0, 0, string (), string (), section),
+ r_ (r) {}
+
+ has_grow_member (bool& r,
+ user_section* section,
+ semantics::type* t,
+ const custom_cxx_type* ct,
+ string const& key_prefix = string ())
+ : relational::member_base (t, ct, string (), key_prefix, section),
+ r_ (r) {}
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ // If we have a key prefix (container), then it can't be in a
+ // section (while mi.m can). The same for top-level -- if we got
+ // called, then we shouldn't ignore it.
+ //
+ return !key_prefix_.empty () || top_level_ ||
+ (section_ == 0 && !separate_load (mi.m)) ||
+ (section_ != 0 && *section_ == section (mi.m));
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ // By calling grow() instead of recursing, we reset any overrides.
+ // We also don't pass section since they don't apply inside
+ // composites.
+ //
+ r_ = r_ || context::grow (dynamic_cast<semantics::class_&> (mi.t));
+ }
+
+ virtual void
+ traverse_string (member_info&)
+ {
+ r_ = true;
+ }
+
+ private:
+ bool& r_;
+ };
+ }
+
+ bool context::
+ grow_impl (semantics::class_& c, user_section* section)
+ {
+ if (section == 0 && c.count ("sqlite-grow"))
+ return c.get<bool> ("sqlite-grow");
+
+ bool r (false);
+ has_grow ct (r, section);
+ has_grow_member mt (r, section);
+ traversal::names names;
+ ct >> names >> mt;
+ ct.traverse (c);
+ return r;
+ }
+
+ bool context::
+ grow_impl (semantics::data_member& m)
+ {
+ bool r (false);
+ has_grow_member mt (r);
+ mt.traverse (m, true);
+ return r;
+ }
+
+ bool context::
+ grow_impl (semantics::data_member& m,
+ semantics::type& t,
+ const custom_cxx_type* ct,
+ string const& kp)
+ {
+ bool r (false);
+ has_grow_member mt (r, 0, &t, ct, kp);
+ mt.traverse (m, true);
+ return r;
+ }
+
+ string context::
+ database_type_impl (semantics::type& t,
+ semantics::names* hint,
+ bool id,
+ bool* null)
+ {
+ string r (base_context::database_type_impl (t, hint, id, null));
+
+ if (!r.empty ())
+ return r;
+
+ using semantics::array;
+
+ // char[N] mapping.
+ //
+ if (array* a = dynamic_cast<array*> (&t))
+ {
+ semantics::type& bt (a->base_type ());
+ if (bt.is_a<semantics::fund_char> () ||
+ bt.is_a<semantics::fund_wchar> ())
+ {
+ if (a->size () != 0)
+ r = "TEXT";
+ }
+ }
+
+ return r;
+ }
+
+ //
+ // SQL type parsing.
+ //
+
+ namespace
+ {
+ struct sql_parser
+ {
+ typedef context::invalid_sql_type invalid_sql_type;
+
+ sql_parser (custom_db_types const* ct): ct_ (ct) {}
+
+ sql_type
+ parse (string sql)
+ {
+ sql_type r;
+
+ // First run the type through the custom mapping, if requested.
+ //
+ if (ct_ != 0)
+ {
+ for (custom_db_types::const_iterator i (ct_->begin ());
+ i != ct_->end (); ++i)
+ {
+ custom_db_type const& t (*i);
+
+ if (t.type.match (sql))
+ {
+ r.to = t.type.replace (sql, t.to);
+ r.from = t.type.replace (sql, t.from);
+ sql = t.type.replace (sql, t.as);
+ break;
+ }
+ }
+ }
+
+ // Parse the type into a sequence of identifiers.
+ //
+ try
+ {
+ l_.lex (sql);
+
+ for (sql_token t (l_.next ()); t.type () != sql_token::t_eos;)
+ {
+ sql_token::token_type tt (t.type ());
+
+ if (tt == sql_token::t_identifier)
+ {
+ ids_.push_back (context::upcase (t.identifier ()));
+ t = l_.next ();
+
+ if (t.punctuation () == sql_token::p_lparen)
+ {
+ if (!parse_range ())
+ return error (m_);
+
+ t = l_.next ();
+ }
+ }
+ else
+ return error ("expected SQLite type name instead of '" +
+ t.string () + "'");
+ }
+ }
+ catch (sql_lexer::invalid_input const& e)
+ {
+ return error ("invalid SQLite type declaration: " + e.message);
+ }
+
+ if (ids_.empty ())
+ return error ("expected SQLite type name");
+
+ // First check our own types.
+ //
+ if (ids_.size () == 2 && ids_[0] == "TEXT" && ids_[1] == "STREAM")
+ {
+ r.type = sql_type::TEXT;
+ r.stream = true;
+ }
+ if (ids_.size () == 2 && ids_[0] == "BLOB" && ids_[1] == "STREAM")
+ {
+ r.type = sql_type::BLOB;
+ r.stream = true;
+ }
+ //
+ // Apply the first four rules of the SQLite type to affinity
+ // conversion algorithm.
+ //
+ else if (find ("INT"))
+ r.type = sql_type::INTEGER;
+ else if (find ("TEXT") || find ("CHAR") || find ("CLOB"))
+ r.type = sql_type::TEXT;
+ else if (find ("BLOB"))
+ r.type = sql_type::BLOB;
+ else if (find ("REAL") || find ("FLOA") || find ("DOUB"))
+ r.type = sql_type::REAL;
+ else
+ {
+ // Instead of the fifth rule which maps everything else
+ // to NUMERICAL (which we don't have), map some commonly
+ // used type names to one of the above types.
+ //
+ string const& id (ids_[0]);
+
+ if (id == "NUMERIC")
+ r.type = sql_type::REAL;
+ else if (id == "DECIMAL")
+ r.type = sql_type::TEXT;
+ else if (id == "BOOLEAN" || id == "BOOL")
+ r.type = sql_type::INTEGER;
+ else if (id == "DATE" || id == "TIME" || id == "DATETIME")
+ r.type = sql_type::TEXT;
+ else
+ return error ("unknown SQLite type '" + id + "'");
+ }
+
+ return r;
+ }
+
+ bool
+ parse_range ()
+ {
+ // Skip tokens until we get the closing paren.
+ //
+ for (sql_token t (l_.next ());; t = l_.next ())
+ {
+ if (t.punctuation () == sql_token::p_rparen)
+ break;
+
+ if (t.type () == sql_token::t_eos)
+ {
+ m_ = "missing ')' in SQLite type declaration";
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private:
+ sql_type
+ error (string const& m)
+ {
+ if (ct_ == 0)
+ return sql_type ();
+ else
+ throw invalid_sql_type (m);
+ }
+
+ bool
+ find (string const& str) const
+ {
+ for (identifiers::const_iterator i (ids_.begin ());
+ i != ids_.end (); ++i)
+ {
+ if (i->find (str) != string::npos)
+ return true;
+ }
+
+ return false;
+ }
+
+ private:
+ custom_db_types const* ct_;
+ sql_lexer l_;
+ string m_; // Error message.
+
+ typedef vector<string> identifiers;
+ identifiers ids_;
+ };
+ }
+
+ sql_type const& context::
+ parse_sql_type (string const& t, semantics::data_member& m, bool custom)
+ {
+ // If this proves to be too expensive, we can maintain a cache of
+ // parsed types across contexts.
+ //
+ data::sql_type_cache::iterator i (data_->sql_type_cache_.find (t));
+
+ if (i != data_->sql_type_cache_.end ()
+ && (custom ? i->second.custom_cached : i->second.straight_cached))
+ {
+ return (custom ? i->second.custom : i->second.straight);
+ }
+ else
+ {
+ try
+ {
+ sql_type st (
+ parse_sql_type (
+ t,
+ custom ? &unit.get<custom_db_types> ("custom-db-types") : 0));
+
+ if (custom)
+ return data_->sql_type_cache_[t].cache_custom (st);
+ else
+ return data_->sql_type_cache_[t].cache_straight (st);
+ }
+ catch (invalid_sql_type const& e)
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: " << e.message () << endl;
+
+ throw operation_failed ();
+ }
+ }
+ }
+
+ sql_type context::
+ parse_sql_type (string const& sqlt, custom_db_types const* ct)
+ {
+ sql_parser p (ct);
+ return p.parse (sqlt);
+ }
+ }
+}
diff --git a/odb/odb/relational/sqlite/context.hxx b/odb/odb/relational/sqlite/context.hxx
new file mode 100644
index 0000000..777998b
--- /dev/null
+++ b/odb/odb/relational/sqlite/context.hxx
@@ -0,0 +1,146 @@
+// file : odb/relational/sqlite/context.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_SQLITE_CONTEXT_HXX
+#define ODB_RELATIONAL_SQLITE_CONTEXT_HXX
+
+#include <map>
+
+#include <odb/relational/context.hxx>
+
+namespace relational
+{
+ namespace sqlite
+ {
+ struct sql_type
+ {
+ // Keep the order in each block of types.
+ //
+ enum core_type
+ {
+ INTEGER,
+ REAL,
+ TEXT,
+ BLOB,
+ invalid
+ };
+
+ sql_type (): type (invalid), stream (false) {}
+
+ core_type type;
+ bool stream; // TEXT or BLOB via sqlite3_blob_open().
+
+ // Conversion expressions for custom database types.
+ //
+ std::string to;
+ std::string from;
+ };
+
+ class context: public virtual relational::context
+ {
+ public:
+ sql_type const&
+ parse_sql_type (string const&,
+ semantics::data_member&,
+ bool custom = true);
+ public:
+ struct invalid_sql_type
+ {
+ invalid_sql_type (string const& message): message_ (message) {}
+
+ string const&
+ message () const {return message_;}
+
+ private:
+ string message_;
+ };
+
+ // If custom_db_types is NULL, then this function returns
+ // invalid type instead of throwing in case an unknown type
+ // is encountered.
+ //
+ static sql_type
+ parse_sql_type (string const&, custom_db_types const* = 0);
+
+ protected:
+ virtual string const&
+ convert_expr (string const&, semantics::data_member&, bool);
+
+ virtual bool
+ grow_impl (semantics::class_&, user_section*);
+
+ virtual bool
+ grow_impl (semantics::data_member&);
+
+ virtual bool
+ grow_impl (semantics::data_member&,
+ semantics::type&,
+ const custom_cxx_type*,
+ string const&);
+
+ protected:
+ virtual string
+ database_type_impl (semantics::type&, semantics::names*, bool, bool*);
+
+ public:
+ virtual
+ ~context ();
+ context ();
+ context (std::ostream&,
+ semantics::unit&,
+ options_type const&,
+ features_type& f,
+ sema_rel::model*);
+
+ static context&
+ current ()
+ {
+ return *current_;
+ }
+
+ private:
+ static context* current_;
+
+ private:
+ struct data: base_context::data
+ {
+ data (std::ostream& os): base_context::data (os) {}
+
+ struct sql_type_cache_entry
+ {
+ sql_type_cache_entry ()
+ : custom_cached (false), straight_cached (false) {}
+
+ sql_type const&
+ cache_custom (sql_type const& t)
+ {
+ custom = t;
+ custom_cached = true;
+ return custom;
+ }
+
+ sql_type const&
+ cache_straight (sql_type const& t)
+ {
+ straight = t;
+ straight_cached = true;
+ return straight;
+ }
+
+ sql_type custom; // With custom mapping.
+ sql_type straight; // Without custom mapping.
+
+ bool custom_cached;
+ bool straight_cached;
+ };
+
+ typedef std::map<string, sql_type_cache_entry> sql_type_cache;
+ sql_type_cache sql_type_cache_;
+ };
+
+ data* data_;
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_SQLITE_CONTEXT_HXX
diff --git a/odb/odb/relational/sqlite/header.cxx b/odb/odb/relational/sqlite/header.cxx
new file mode 100644
index 0000000..1aafe7a
--- /dev/null
+++ b/odb/odb/relational/sqlite/header.cxx
@@ -0,0 +1,63 @@
+// file : odb/relational/sqlite/header.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/header.hxx>
+
+#include <odb/relational/sqlite/common.hxx>
+#include <odb/relational/sqlite/context.hxx>
+
+namespace relational
+{
+ namespace sqlite
+ {
+ namespace header
+ {
+ namespace relational = relational::header;
+
+ struct image_member: relational::image_member_impl<sql_type>,
+ member_base
+ {
+ image_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x) {}
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_real (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "std::size_t " << mi.var << "size;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+
+ virtual void
+ traverse_stream (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << "std::size_t " << mi.var << "size;"
+ << "bool " << mi.var << "null;"
+ << endl;
+ }
+ };
+ entry<image_member> image_member_;
+ }
+ }
+}
diff --git a/odb/odb/relational/sqlite/inline.cxx b/odb/odb/relational/sqlite/inline.cxx
new file mode 100644
index 0000000..dd3274f
--- /dev/null
+++ b/odb/odb/relational/sqlite/inline.cxx
@@ -0,0 +1,42 @@
+// file : odb/relational/sqlite/inline.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/inline.hxx>
+
+#include <odb/relational/sqlite/common.hxx>
+#include <odb/relational/sqlite/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace sqlite
+ {
+ namespace inline_
+ {
+ namespace relational = relational::inline_;
+
+ struct null_member: relational::null_member_impl<sql_type>,
+ member_base
+ {
+ null_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ traverse_simple (member_info& mi)
+ {
+ if (get_)
+ os << "r = r && i." << mi.var << "null;";
+ else
+ os << "i." << mi.var << "null = true;";
+ }
+ };
+ entry<null_member> null_member_;
+ }
+ }
+}
diff --git a/odb/odb/relational/sqlite/model.cxx b/odb/odb/relational/sqlite/model.cxx
new file mode 100644
index 0000000..da16ded
--- /dev/null
+++ b/odb/odb/relational/sqlite/model.cxx
@@ -0,0 +1,91 @@
+// file : odb/relational/sqlite/model.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <odb/relational/model.hxx>
+
+#include <odb/relational/sqlite/common.hxx>
+#include <odb/relational/sqlite/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace sqlite
+ {
+ namespace model
+ {
+ namespace relational = relational::model;
+
+ struct object_columns: relational::object_columns, context
+ {
+ object_columns (base const& x): base (x) {}
+
+ virtual string
+ type (semantics::data_member& m)
+ {
+ // Translate BLOB|TEXT STREAM to just BLOB|TEXT.
+ //
+ string r (relational::object_columns::type (m));
+
+ sql_type const& t (parse_sql_type (r, m, false));
+ if (t.stream)
+ {
+ switch (t.type)
+ {
+ case sql_type::BLOB: r = "BLOB"; break;
+ case sql_type::TEXT: r = "TEXT"; break;
+ default: break;
+ }
+ }
+
+ return r;
+ }
+
+ virtual bool
+ null (semantics::data_member& m)
+ {
+ return options.sqlite_override_null () || base::null (m);
+ }
+
+ virtual string
+ default_enum (semantics::data_member& m, tree en, string const&)
+ {
+ // Make sure the column is mapped to INTEGER.
+ //
+ sql_type const& t (parse_sql_type (column_type (), m, false));
+ if (t.type != sql_type::INTEGER)
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: column with default value specified as C++ "
+ << "enumerator must map to SQLite INTEGER" << endl;
+
+ throw operation_failed ();
+ }
+
+ using semantics::enumerator;
+
+ enumerator& e (dynamic_cast<enumerator&> (*unit.find (en)));
+
+ ostringstream ostr;
+
+ if (e.enum_ ().unsigned_ ())
+ ostr << e.value ();
+ else
+ ostr << static_cast<long long> (e.value ());
+
+ return ostr.str ();
+ }
+
+ virtual void
+ primary_key (sema_rel::primary_key& pk)
+ {
+ if (pk.auto_ () && options.sqlite_lax_auto_id ())
+ pk.extra ()["lax"] = "true";
+ }
+ };
+ entry<object_columns> object_columns_;
+ }
+ }
+}
diff --git a/odb/odb/relational/sqlite/schema.cxx b/odb/odb/relational/sqlite/schema.cxx
new file mode 100644
index 0000000..f5549b4
--- /dev/null
+++ b/odb/odb/relational/sqlite/schema.cxx
@@ -0,0 +1,455 @@
+// file : odb/relational/sqlite/schema.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/schema.hxx>
+
+#include <odb/relational/sqlite/common.hxx>
+#include <odb/relational/sqlite/context.hxx>
+
+namespace relational
+{
+ namespace sqlite
+ {
+ namespace schema
+ {
+ namespace relational = relational::schema;
+
+ //
+ // Drop.
+ //
+
+ struct drop_column: trav_rel::drop_column, relational::common
+ {
+ drop_column (relational::common const& c)
+ : relational::common (c), first_ (true) {}
+
+ virtual void
+ traverse (sema_rel::drop_column& dc)
+ {
+ // SQLite does not support dropping columns. If this column is
+ // not NULLable, then there is nothing we can do. Otherwise, do
+ // a logical DROP by setting all the values to NULL.
+ //
+ sema_rel::column& c (find<sema_rel::column> (dc));
+
+ if (!c.null ())
+ {
+ cerr << "error: SQLite does not support dropping of columns" <<
+ endl;
+ cerr << "info: first dropped column is '" << dc.name () <<
+ "' in table '" << dc.table ().name () << "'" << endl;
+ cerr << "info: could have performed logical drop if the column " <<
+ "allowed NULL values" << endl;
+ throw operation_failed ();
+ }
+
+ if (first_)
+ first_ = false;
+ else
+ os << "," << endl
+ << " ";
+
+ os << quote_id (dc.name ()) << " = NULL";
+ }
+
+ private:
+ bool first_;
+ };
+ // Not registered as an override.
+
+ struct drop_index: relational::drop_index, context
+ {
+ drop_index (base const& x): base (x) {}
+
+ virtual string
+ name (sema_rel::index& in)
+ {
+ // In SQLite, index names can be qualified with the database.
+ //
+ sema_rel::table& t (static_cast<sema_rel::table&> (in.scope ()));
+ sema_rel::qname n (t.name ().qualifier ());
+ n.append (in.name ());
+ return quote_id (n);
+ }
+ };
+ entry<drop_index> drop_index_;
+
+ struct drop_table: relational::drop_table, context
+ {
+ drop_table (base const& x): base (x) {}
+
+ virtual void
+ traverse (sema_rel::table& t, bool migration)
+ {
+ // In SQLite there is no way to drop foreign keys except as part
+ // of the table.
+ //
+ if (pass_ != 2)
+ return;
+
+ // Polymorphic base cleanup code. Because we cannot drop foreign
+ // keys, we will trigger cascade deletion. The only way to work
+ // around this problem is to delete from the root table and rely
+ // on the cascade to clean up the rest.
+ //
+ if (migration && t.extra ()["kind"] == "polymorphic derived object")
+ {
+ using sema_rel::model;
+ using sema_rel::table;
+ using sema_rel::primary_key;
+ using sema_rel::foreign_key;
+
+ model& m (dynamic_cast<model&> (t.scope ()));
+
+ table* p (&t);
+ do
+ {
+ // The polymorphic link is the first primary key.
+ //
+ for (table::names_iterator i (p->names_begin ());
+ i != p->names_end (); ++i)
+ {
+ if (foreign_key* fk = dynamic_cast<foreign_key*> (
+ &i->nameable ()))
+ {
+ p = m.find<table> (fk->referenced_table ());
+ assert (p != 0); // Base table should be there.
+ break;
+ }
+ }
+ }
+ while (p->extra ()["kind"] != "polymorphic root object");
+
+ primary_key& rkey (*p->find<primary_key> (""));
+ primary_key& dkey (*t.find<primary_key> (""));
+ assert (rkey.contains_size () == dkey.contains_size ());
+ delete_ (p->name (), t.name (), rkey, dkey);
+ }
+
+ drop (t, migration);
+ }
+ };
+ entry<drop_table> drop_table_;
+
+ //
+ // Create.
+ //
+
+ struct create_column: relational::create_column, context
+ {
+ create_column (base const& x): base (x) {}
+
+ virtual void
+ traverse (sema_rel::add_column& ac)
+ {
+ using sema_rel::alter_table;
+ using sema_rel::add_column;
+ using sema_rel::add_foreign_key;
+
+ alter_table& at (static_cast<alter_table&> (ac.scope ()));
+
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (at.name ()) << endl
+ << " ADD COLUMN ";
+
+ // In SQLite it is impossible to alter a column later, so unless
+ // it has a default value, we add it as NULL. Without this, it
+ // will be impossible to add a column to a table that contains
+ // some rows.
+ //
+ create (ac);
+
+ // SQLite doesn't support adding foreign keys other than inline
+ // via a column definition. See if we can handle any.
+ //
+ add_foreign_key* afk (0);
+
+ for (add_column::contained_iterator i (ac.contained_begin ());
+ i != ac.contained_end ();
+ ++i)
+ {
+ if ((afk = dynamic_cast<add_foreign_key*> (&i->key ())))
+ {
+ // Check that it is a single-column foreign key. Also make
+ // sure the column and foreign key are from the same changeset.
+ //
+ if (afk->contains_size () != 1 || &ac.scope () != &afk->scope ())
+ afk = 0;
+ else
+ break;
+ }
+ }
+
+ if (afk != 0)
+ {
+ os << " CONSTRAINT " << quote_id (afk->name ()) << " REFERENCES " <<
+ quote_id (afk->referenced_table ().uname ()) << " (" <<
+ quote_id (afk->referenced_columns ()[0]) << ")";
+
+ bool del (afk->on_delete () != sema_rel::foreign_key::no_action);
+ bool def (!afk->not_deferrable ());
+
+ if (del || def)
+ {
+ instance<relational::create_foreign_key> cfk (*this);
+
+ if (del)
+ cfk->on_delete (afk->on_delete ());
+
+ if (def)
+ cfk->deferrable (afk->deferrable ());
+ }
+
+ afk->set ("sqlite-fk-defined", true); // Mark it as defined.
+ }
+
+ os << endl;
+ post_statement ();
+ }
+
+ virtual void
+ auto_ (sema_rel::primary_key& pk)
+ {
+ if (pk.extra ().count ("lax"))
+ os << " /*AUTOINCREMENT*/";
+ else
+ os << " AUTOINCREMENT";
+ }
+ };
+ entry<create_column> create_column_;
+
+ struct create_foreign_key: relational::create_foreign_key, context
+ {
+ create_foreign_key (base const& x): base (x) {}
+
+ virtual void
+ traverse (sema_rel::foreign_key& fk)
+ {
+ // In SQLite, all constraints are defined as part of a table.
+ //
+ os << "," << endl
+ << " CONSTRAINT ";
+
+ create (fk);
+ }
+
+ virtual string
+ table_name (sema_rel::foreign_key& fk)
+ {
+ // In SQLite, the referenced table cannot be qualified with the
+ // database name (it has to be in the same database anyway).
+ //
+ return quote_id (fk.referenced_table ().uname ());
+ }
+ };
+ entry<create_foreign_key> create_foreign_key_;
+
+ struct create_index: relational::create_index, context
+ {
+ create_index (base const& x): base (x) {}
+
+ virtual string
+ name (sema_rel::index& in)
+ {
+ // In SQLite, index names can be qualified with the database.
+ //
+ sema_rel::table& t (static_cast<sema_rel::table&> (in.scope ()));
+ sema_rel::qname n (t.name ().qualifier ());
+ n.append (in.name ());
+ return quote_id (n);
+ }
+
+ virtual string
+ table_name (sema_rel::index& in)
+ {
+ // In SQLite, the index table cannot be qualified with the
+ // database name (it has to be in the same database).
+ //
+ return quote_id (
+ static_cast<sema_rel::table&> (in.scope ()).name ().uname ());
+ }
+ };
+ entry<create_index> create_index_;
+
+ struct create_table: relational::create_table, context
+ {
+ create_table (base const& x): base (x) {}
+
+ void
+ traverse (sema_rel::table& t)
+ {
+ // For SQLite we do everything in a single pass since there
+ // is no way to add constraints later.
+ //
+ if (pass_ == 1)
+ create (t);
+ }
+ };
+ entry<create_table> create_table_;
+
+ //
+ // Alter.
+ //
+
+ struct alter_table_pre: relational::alter_table_pre, context
+ {
+ alter_table_pre (base const& x): base (x) {}
+
+ virtual void
+ alter (sema_rel::alter_table& at)
+ {
+ // SQLite can only add a single column per ALTER TABLE statement.
+ //
+ instance<create_column> cc (*this);
+ trav_rel::unames n (*cc);
+ names (at, n);
+
+ // SQLite does not support altering columns.
+ //
+ if (sema_rel::alter_column* ac = check<sema_rel::alter_column> (at))
+ {
+ cerr << "error: SQLite does not support altering of columns"
+ << endl;
+ cerr << "info: first altered column is '" << ac->name () <<
+ "' in table '" << at.name () << "'" << endl;
+ throw operation_failed ();
+ }
+
+ // SQLite does not support dropping constraints. We are going to
+ // ignore this if the column is NULL'able since in most cases
+ // the constraint is going to be dropped as a result of the
+ // column drop (e.g., an object pointer member got deleted).
+ // If we were not to allow this, then it would be impossible
+ // to do logical drop for pointer columns.
+ //
+ for (sema_rel::alter_table::names_iterator i (at.names_begin ());
+ i != at.names_end (); ++i)
+ {
+ using sema_rel::foreign_key;
+ using sema_rel::drop_foreign_key;
+
+ drop_foreign_key* dfk (
+ dynamic_cast<drop_foreign_key*> (&i->nameable ()));
+
+ if (dfk == 0)
+ continue;
+
+ foreign_key& fk (find<foreign_key> (*dfk));
+
+ for (foreign_key::contains_iterator j (fk.contains_begin ());
+ j != fk.contains_end (); ++j)
+ {
+ if (j->column ().null ())
+ continue;
+
+ cerr << "error: SQLite does not support dropping of foreign " <<
+ "keys" << endl;
+ cerr << "info: first dropped foreign key is '" << dfk->name () <<
+ "' in table '" << at.name () << "'" << endl;
+ cerr << "info: could have ignored it if the contained " <<
+ "column(s) allowed NULL values" << endl;
+ throw operation_failed ();
+ }
+ }
+ }
+ };
+ entry<alter_table_pre> alter_table_pre_;
+
+ struct alter_table_post: relational::alter_table_post, context
+ {
+ alter_table_post (base const& x): base (x) {}
+
+ virtual void
+ alter (sema_rel::alter_table& at)
+ {
+ // SQLite does not support altering columns (we have to do this
+ // in both alter_table_pre/post because of the
+ // check_alter_column_null() test in the common code).
+ //
+ if (sema_rel::alter_column* ac = check<sema_rel::alter_column> (at))
+ {
+ cerr << "error: SQLite does not support altering of columns"
+ << endl;
+ cerr << "info: first altered column is '" << ac->name () <<
+ "' in table '" << at.name () << "'" << endl;
+ throw operation_failed ();
+ }
+
+ // Try to do logical column drop.
+ //
+ if (check<sema_rel::drop_column> (at))
+ {
+ pre_statement ();
+
+ os << "UPDATE " << quote_id (at.name ()) << endl
+ << " SET ";
+
+ drop_column dc (*this);
+ trav_rel::unames n (dc);
+ names (at, n);
+ os << endl;
+
+ post_statement ();
+ }
+
+ // SQLite doesn't support adding foreign keys other than inline
+ // via a column definition. See if there are any that we couldn't
+ // handle that way.
+ //
+ for (sema_rel::alter_table::names_iterator i (at.names_begin ());
+ i != at.names_end (); ++i)
+ {
+ sema_rel::add_foreign_key* afk (
+ dynamic_cast<sema_rel::add_foreign_key*> (&i->nameable ()));
+
+ if (afk == 0 || afk->count ("sqlite-fk-defined"))
+ continue;
+
+ cerr << "error: SQLite does not support adding foreign keys"
+ << endl;
+ cerr << "info: first added foreign key is '" << afk->name () <<
+ "' in table '" << at.name () << "'" << endl;
+ throw operation_failed ();
+ }
+ }
+ };
+ entry<alter_table_post> alter_table_post_;
+
+ //
+ // Schema version table.
+ //
+
+ struct version_table: relational::version_table, context
+ {
+ version_table (base const& x): base (x) {}
+
+ virtual void
+ create_table ()
+ {
+ pre_statement ();
+
+ os << "CREATE TABLE IF NOT EXISTS " << qt_ << " (" << endl
+ << " " << qn_ << " TEXT NOT NULL PRIMARY KEY," << endl
+ << " " << qv_ << " INTEGER NOT NULL," << endl
+ << " " << qm_ << " INTEGER NOT NULL)" << endl;
+
+ post_statement ();
+ }
+
+ virtual void
+ create (sema_rel::version v)
+ {
+ pre_statement ();
+
+ os << "INSERT OR IGNORE INTO " << qt_ << " (" << endl
+ << " " << qn_ << ", " << qv_ << ", " << qm_ << ")" << endl
+ << " VALUES (" << qs_ << ", " << v << ", 0)" << endl;
+
+ post_statement ();
+ }
+ };
+ entry<version_table> version_table_;
+ }
+ }
+}
diff --git a/odb/odb/relational/sqlite/source.cxx b/odb/odb/relational/sqlite/source.cxx
new file mode 100644
index 0000000..5a4b9d3
--- /dev/null
+++ b/odb/odb/relational/sqlite/source.cxx
@@ -0,0 +1,471 @@
+// file : odb/relational/sqlite/source.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/relational/source.hxx>
+
+#include <odb/relational/sqlite/common.hxx>
+#include <odb/relational/sqlite/context.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace sqlite
+ {
+ namespace source
+ {
+ namespace relational = relational::source;
+
+ struct query_parameters: relational::query_parameters, context
+ {
+ query_parameters (base const& x): base (x) {}
+
+ virtual string
+ next (semantics::data_member& m,
+ const string& column,
+ const string& sqlt)
+ {
+ // Handle stream columns. Specifically, we somehow need to
+ // pass the column name to the code that runs in the
+ // statement. So what we are going to do is encode it
+ // in the parameter name.
+ //
+ if (sk_ == statement_insert || sk_ == statement_update)
+ {
+ const sql_type& t (parse_sql_type (sqlt, m, false));
+ if (t.stream)
+ {
+ // The column name is quoted.
+ //
+ string r (column);
+ r[0] = '$'; // Replace leading '"'.
+ r.resize (r.size () - 1); // Remove trailing '"'.
+
+ // Verify it only contains allowed characters.
+ //
+ for (size_t i (1); i != r.size (); ++i)
+ {
+ char c (r[i]);
+ if (c != '_' &&
+ (c < '0' || c > '9') &&
+ (c < 'a' || c > 'z') &&
+ (c < 'A' || c > 'Z'))
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: unsupported character '" << c << "' in "
+ << sqlt << " column name " << column << endl;
+
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": info: STREAM column can contain alpha-numeric "
+ << "characters plus '_'" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ // For TEXT columns, since we use the *_bind_zeroblob()
+ // function (there is no *_bind_zerotext()), the value
+ // that will be stored is BLOB, not TEXT, unless we
+ // explicitly CAST it. The user better make sure the
+ // encoding of raw TEXT data they are going to write
+ // matches the database encoding.
+ //
+ if (t.type == sql_type::TEXT)
+ r = "CAST(" + r + " AS TEXT)";
+
+ return r;
+ }
+ }
+
+ return "?";
+ }
+ };
+ entry<query_parameters> query_parameters_;
+
+ //
+ // bind
+ //
+
+ struct bind_member: relational::bind_member_impl<sql_type>,
+ member_base
+ {
+ bind_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << b << ".type = sqlite::bind::integer;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_real (member_info& mi)
+ {
+ os << b << ".type = sqlite::bind::real;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_text (member_info& mi)
+ {
+ os << b << ".type = sqlite::image_traits<" << endl
+ << " " << mi.fq_type () << "," << endl
+ << " sqlite::id_text>::bind_value;"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data ();"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".capacity = " << arg << "." << mi.var <<
+ "value.capacity ();"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_blob (member_info& mi)
+ {
+ os << b << ".type = sqlite::bind::blob;"
+ << b << ".buffer = " << arg << "." << mi.var << "value.data ();"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".capacity = " << arg << "." << mi.var <<
+ "value.capacity ();"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+
+ virtual void
+ traverse_stream (member_info& mi)
+ {
+ os << b << ".type = sqlite::bind::stream;"
+ << b << ".buffer = &" << arg << "." << mi.var << "value;"
+ << b << ".size = &" << arg << "." << mi.var << "size;"
+ << b << ".is_null = &" << arg << "." << mi.var << "null;";
+ }
+ };
+ entry<bind_member> bind_member_;
+
+ //
+ // grow
+ //
+
+ struct grow_member: relational::grow_member_impl<sql_type>,
+ member_base
+ {
+ grow_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x) {}
+
+ virtual void
+ traverse_integer (member_info&)
+ {
+ os << e << " = false;"
+ << endl;
+ }
+
+ virtual void
+ traverse_real (member_info&)
+ {
+ os << e << " = false;"
+ << endl;
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << "if (" << e << ")" << endl
+ << "{"
+ << "i." << mi.var << "value.capacity (i." << mi.var << "size);"
+ << "grew = true;"
+ << "}";
+ }
+
+ virtual void
+ traverse_stream (member_info&)
+ {
+ os << e << " = false;"
+ << endl;
+ }
+ };
+ entry<grow_member> grow_member_;
+
+ //
+ // init image
+ //
+
+ struct init_image_member: relational::init_image_member_impl<sql_type>,
+ member_base
+ {
+ init_image_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ set_null (member_info& mi)
+ {
+ os << "i." << mi.var << "null = true;";
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+
+ virtual void
+ traverse_real (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << "std::size_t cap (i." << mi.var << "value.capacity ());"
+ << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;"
+ << "grew = grew || (cap != i." << mi.var << "value.capacity ());";
+ }
+
+ virtual void
+ traverse_stream (member_info& mi)
+ {
+ os << traits << "::set_image (" << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "is_null," << endl
+ << member << ");"
+ << "i." << mi.var << "null = is_null;";
+ }
+ };
+ entry<init_image_member> init_image_member_;
+
+ //
+ // init value
+ //
+
+ struct init_value_member: relational::init_value_member_impl<sql_type>,
+ member_base
+ {
+ init_value_member (base const& x)
+ : member_base::base (x), // virtual base
+ member_base::base_impl (x), // virtual base
+ base_impl (x),
+ member_base (x)
+ {
+ }
+
+ virtual void
+ get_null (string const& var) const
+ {
+ os << "i." << var << "null";
+ }
+
+ virtual void
+ traverse_integer (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_real (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_string (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+
+ virtual void
+ traverse_stream (member_info& mi)
+ {
+ os << traits << "::set_value (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "i." << mi.var << "size," << endl
+ << "i." << mi.var << "null);"
+ << endl;
+ }
+ };
+ entry<init_value_member> init_value_member_;
+
+ struct statement_columns_common: context
+ {
+ void
+ process (relational::statement_columns& cs, statement_kind sk)
+ {
+ using relational::statement_columns;
+
+ // For SELECT statements, add _ROWID_ "follow-up" column to
+ // each stream column. The reason we need both, and not just
+ // ROWID is the NULL value. Let's hope that SELECT'ing a BLOB
+ // but not actually reading it with sqlite3_result_blob() is
+ // as fast as not SELECT'ing it.
+ //
+ if (sk != statement_select)
+ return;
+
+ for (statement_columns::iterator i (cs.begin ());
+ i != cs.end (); ++i)
+ {
+ if (parse_sql_type (i->type, *i->member).stream)
+ {
+ // Column is already table-qualified and quoted. Do some
+ // surgery to replace it with _ROWID_. That is, we want to
+ // transform "table"."column" to "table"."_ROWID_".
+ //
+ string c (i->column);
+ string::size_type n (c.size ()), p (c.rfind ('"', n - 2));
+ assert (p != string::npos);
+ string as (c, p + 1, n - p - 2);
+ c.resize (p);
+ c += "\"_ROWID_\"";
+
+ // We are going to pack this "tightly", without any newlines,
+ // so that the statement processing code treats them as a
+ // single column.
+ //
+ i->column += ',';
+ i->column += c;
+ }
+ }
+ }
+ };
+
+ struct container_traits: relational::container_traits,
+ statement_columns_common
+ {
+ container_traits (base const& x): base (x) {}
+
+ virtual void
+ cache_result (string const&)
+ {
+ // Caching is not necessary since SQLite can execute several
+ // interleaving statements.
+ //
+ }
+
+ virtual void
+ process_statement_columns (relational::statement_columns& cols,
+ statement_kind sk,
+ bool)
+ {
+ statement_columns_common::process (cols, sk);
+ }
+ };
+ entry<container_traits> container_traits_;
+
+ struct section_traits: relational::section_traits,
+ statement_columns_common
+ {
+ section_traits (base const& x): base (x) {}
+
+ virtual void
+ process_statement_columns (relational::statement_columns& cols,
+ statement_kind sk,
+ bool)
+ {
+ statement_columns_common::process (cols, sk);
+ }
+ };
+ entry<section_traits> section_traits_;
+
+ struct class_: relational::class_, statement_columns_common
+ {
+ class_ (base const& x): base (x) {}
+
+ virtual void
+ init_auto_id (semantics::data_member& m, string const& im)
+ {
+ // Don't set the id value to NULL if this is a nullable wrapper.
+ // This will allow the user to control whether the value is auto or
+ // manually assigned by using something like this:
+ //
+ // #pragma db auto
+ // odb::nullable<int64_t> id;
+ //
+ semantics::type& t (utype (m));
+ if (wrapper (t) && t.template get<bool> ("wrapper-null-handler"))
+ return;
+
+ os << im << "null = true;"
+ << endl;
+ }
+
+ virtual string
+ select_trailer (type&)
+ {
+ // SQLite has not support for FOR UPDATE and since this is an
+ // optimization, we simply ignore it.
+ //
+ return "";
+ }
+
+ virtual string
+ join_syntax (view_object const& vo)
+ {
+ const char* n (0);
+
+ if (vo.join == view_object::full)
+ n = "FULL OUTER JOIN";
+ else if (vo.join == view_object::right)
+ n = "RIGHT OUTER JOIN";
+
+ if (n != 0)
+ {
+ error (vo.loc) << n << " is not supported by SQLite" << endl;
+ throw operation_failed ();
+ }
+
+ return base::join_syntax (vo);
+ }
+
+ virtual void
+ process_statement_columns (relational::statement_columns& cols,
+ statement_kind sk,
+ bool)
+ {
+ statement_columns_common::process (cols, sk);
+ }
+ };
+ entry<class_> class_entry_;
+ }
+ }
+}
diff --git a/odb/odb/relational/validator.cxx b/odb/odb/relational/validator.cxx
new file mode 100644
index 0000000..50c887e
--- /dev/null
+++ b/odb/odb/relational/validator.cxx
@@ -0,0 +1,638 @@
+// file : odb/relational/validator.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <map>
+#include <iostream>
+
+#include <odb/diagnostics.hxx>
+#include <odb/traversal.hxx>
+#include <odb/relational/common.hxx>
+#include <odb/relational/context.hxx>
+#include <odb/relational/validator.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace
+ {
+ //
+ // Pass 2.
+ //
+
+ struct data_member2: traversal::data_member, context
+ {
+ data_member2 (bool& valid)
+ : valid_ (valid)
+ {
+ }
+
+ virtual void
+ traverse (type& m)
+ {
+ if (transient (m))
+ return;
+
+ if (null (m))
+ {
+ if (semantics::class_* c = composite_wrapper (utype (m)))
+ {
+ if (has_a (*c, test_container))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: composite member containing containers cannot "
+ << "be null" << endl;
+
+ os << c->file () << ":" << c->line () << ":" << c->column ()
+ << ": info: composite value type is defined here" << endl;
+
+ valid_ = false;
+ }
+ }
+ }
+
+ // Check on-delete.
+ //
+ if (m.count ("on-delete"))
+ {
+ const char* kp (container (m) ? "value" : "");
+ location l (m.location ());
+
+ // Make sure it is a pointer or a member with points_to pragma.
+ //
+ if (!object_pointer (utype (m, kp)) && !points_to (m))
+ {
+ error (l) << "on_delete specified for non-object pointer" << endl;
+ valid_ = false;
+ }
+
+ // Make sure it is not inverse.
+ //
+ if (inverse (m, kp))
+ {
+ error (l) << "on_delete specified for inverse object " <<
+ "pointer" << endl;
+ valid_ = false;
+ }
+
+ // Make sure the pointer is nullable if asked to set it to NULL.
+ //
+ using sema_rel::foreign_key;
+
+ if (m.get<foreign_key::action_type> ("on-delete") ==
+ foreign_key::set_null &&
+ !null (m, kp))
+ {
+ error (l) << "set_null specified for non-nullable object "
+ "pointer" << endl;
+ valid_ = false;
+ }
+ }
+ }
+
+ bool& valid_;
+ };
+
+ struct object_no_id_members: object_members_base
+ {
+ object_no_id_members (bool& valid)
+ : object_members_base (false, false, true), valid_ (valid), dm_ (0)
+ {
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_&)
+ {
+ if (inverse (m))
+ {
+ semantics::data_member& dm (dm_ != 0 ? *dm_ : m);
+
+ os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":"
+ << " error: inverse object pointer member '" << member_prefix_
+ << m.name () << "' in an object without an object id" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type&)
+ {
+ semantics::data_member& dm (dm_ != 0 ? *dm_ : m);
+
+ os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":"
+ << " error: container member '" << member_prefix_ << m.name ()
+ << "' in an object without an object id" << endl;
+
+ valid_ = false;
+ }
+
+ virtual void
+ traverse_composite (semantics::data_member* m, semantics::class_& c)
+ {
+ semantics::data_member* old_dm (dm_);
+
+ if (dm_ == 0)
+ dm_ = m;
+
+ object_members_base::traverse_composite (m, c);
+
+ dm_ = old_dm;
+ }
+
+ private:
+ bool& valid_;
+ semantics::data_member* dm_; // Direct object data member.
+ };
+
+ struct composite_id_members: object_members_base
+ {
+ composite_id_members (bool& valid)
+ : object_members_base (false, false, true), valid_ (valid), dm_ (0)
+ {
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_&)
+ {
+ semantics::data_member& dm (dm_ != 0 ? *dm_ : m);
+
+ os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":"
+ << " error: object pointer member '" << member_prefix_ << m.name ()
+ << "' in a composite value type that is used as an object id"
+ << endl;
+
+ valid_ = false;
+ }
+
+ virtual void
+ traverse_simple (semantics::data_member& m)
+ {
+ if (readonly (member_path_, member_scope_))
+ {
+ semantics::data_member& dm (dm_ != 0 ? *dm_ : m);
+
+ os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":"
+ << " error: readonly member '" << member_prefix_ << m.name ()
+ << "' in a composite value type that is used as an object id"
+ << endl;
+
+ valid_ = false;
+ }
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type&)
+ {
+ semantics::data_member& dm (dm_ != 0 ? *dm_ : m);
+
+ os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":"
+ << " error: container member '" << member_prefix_ << m.name ()
+ << "' in a composite value type that is used as an object id"
+ << endl;
+
+ valid_ = false;
+ }
+
+ virtual void
+ traverse_composite (semantics::data_member* m, semantics::class_& c)
+ {
+ semantics::data_member* old_dm (dm_);
+
+ if (dm_ == 0)
+ dm_ = m;
+
+ object_members_base::traverse_composite (m, c);
+
+ dm_ = old_dm;
+ }
+
+ private:
+ bool& valid_;
+ semantics::data_member* dm_; // Direct composite member.
+ };
+
+ struct view_members: object_members_base
+ {
+ view_members (bool& valid)
+ : object_members_base (false, false, true), valid_ (valid), dm_ (0)
+ {
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_&)
+ {
+ if (dm_ != 0 && object_pointer (utype (m)))
+ {
+ location const& l (dm_->location ());
+
+ error (l) << "nested view data member '" << member_prefix_
+ << m.name () << "' is an object pointer" << endl;
+ info (l) << "views can only contain direct object pointer members"
+ << endl;
+
+ valid_ = false;
+ }
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type&)
+ {
+ semantics::data_member& dm (dm_ != 0 ? *dm_ : m);
+
+ os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":"
+ << " error: view data member '" << member_prefix_ << m.name ()
+ << "' is a container" << endl;
+
+ os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":"
+ << ": info: views cannot contain containers" << endl;
+
+ valid_ = false;
+ }
+
+ virtual void
+ traverse_composite (semantics::data_member* m, semantics::class_& c)
+ {
+ semantics::data_member* old_dm (dm_);
+
+ if (dm_ == 0)
+ dm_ = m;
+
+ object_members_base::traverse_composite (m, c);
+
+ dm_ = old_dm;
+ }
+
+ private:
+ bool& valid_;
+ semantics::data_member* dm_; // Direct view data member.
+ };
+
+ struct class2: traversal::class_, context
+ {
+ class2 (bool& valid)
+ : valid_ (valid),
+ typedefs_ (true),
+ data_member_ (valid),
+ object_no_id_members_ (valid),
+ composite_id_members_ (valid),
+ view_members_ (valid)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+
+ data_member_names_ >> data_member_;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+ switch (ck)
+ {
+ case class_object:
+ names (c);
+ traverse_object (c);
+ break;
+ case class_view:
+ names (c);
+ traverse_view (c);
+ break;
+ case class_composite:
+ names (c);
+ traverse_composite (c);
+ break;
+ case class_other:
+ break;
+ }
+
+ // Make sure indexes are not defined for anything other than objects.
+ //
+ if (c.count ("index") && ck != class_object)
+ {
+ indexes& ins (c.get<indexes> ("index"));
+
+ for (indexes::iterator i (ins.begin ()); i != ins.end (); ++i)
+ {
+ error (i->loc) << "index definition on a non-persistent class"
+ << endl;
+ valid_ = false;
+ }
+ }
+ }
+
+ virtual void
+ traverse_object (type& c)
+ {
+ data_member_path* id (id_member (c));
+
+ if (id != 0)
+ {
+ if (semantics::class_* cm = composite_wrapper (utype (*id)))
+ {
+ location idl (id->front ()->location ());
+
+ // Composite id cannot be auto.
+ //
+ if (auto_ (*id))
+ {
+ error (idl) << "composite id cannot be automatically assigned"
+ << endl;
+ valid_ = false;
+ }
+
+ // Make sure we don't have any containers or pointers in this
+ // composite value type.
+ //
+ if (valid_)
+ {
+ composite_id_members_.traverse (*cm);
+
+ if (!valid_)
+ info (idl) << "composite id is defined here" << endl;
+ }
+
+ // Check that the composite value type is default-constructible.
+ //
+ if (!cm->default_ctor ())
+ {
+ os << cm->file () << ":" << cm->line () << ":" << cm->column ()
+ << ": error: composite value type that is used as object id "
+ << "is not default-constructible" << endl;
+
+ os << cm->file () << ":" << cm->line () << ":" << cm->column ()
+ << ": info: provide default constructor for this value type"
+ << endl;
+
+ info (idl) << "composite id is defined here" << endl;
+
+ valid_ = false;
+ }
+ }
+ }
+ else
+ {
+ if (!abstract (c))
+ {
+ // Make sure we don't have any containers or inverse pointers.
+ //
+ object_no_id_members_.traverse (c);
+ }
+ }
+
+ names (c, data_member_names_);
+
+ // Validate bulk operation support.
+ //
+ for (bool i (true); i && c.count ("bulk"); i = false)
+ {
+ location_t l (c.get<location_t> ("bulk-location"));
+
+ if (polymorphic (c))
+ {
+ error (l) << "bulk operations on polymorphic objects are "
+ "not supported" << endl;
+ valid_ = false;
+ break;
+ }
+
+ if (has_a (c, test_straight_container))
+ {
+ error (l) << "bulk operations on objects with containers are "
+ "not supported" << endl;
+ valid_ = false;
+ break;
+ }
+
+ bool update (true);
+
+ // Unless we only have manually-updated sections, we cannot generate
+ // the bulk update operation.
+ //
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ for (user_sections::iterator i (uss.begin ());
+ update && i != uss.end ();
+ ++i)
+ {
+ const user_section& s (*i);
+
+ // Skip special sections.
+ //
+ if (s.special != user_section::special_ordinary)
+ continue;
+
+ // Always-updated section still needs a separate statement
+ // (since it may not be loaded).
+ //
+ if (!s.update_empty () && s.update != user_section::update_manual)
+ update = false;
+ }
+
+ c.set ("bulk-persist", true);
+ if (update) c.set ("bulk-update", true);
+ c.set ("bulk-erase", true);
+ }
+
+ // Validate indexes.
+ //
+ {
+ indexes& ins (c.get<indexes> ("index"));
+
+ // Make sure index members are not transient, inverse, or
+ // containers.
+ //
+ for (indexes::iterator i (ins.begin ()); i != ins.end (); ++i)
+ {
+ index& in (*i);
+
+ for (index::members_type::iterator i (in.members.begin ());
+ i != in.members.end (); ++i)
+ {
+ index::member& im (*i);
+ semantics::data_member& m (*im.path.back ());
+
+ if (transient (m))
+ {
+ error (im.loc) << "index member is transient" << endl;
+ valid_ = false;
+ }
+
+ if (inverse (m))
+ {
+ error (im.loc) << "index member is an inverse object " <<
+ "pointer" << endl;
+ valid_ = false;
+ }
+
+ if (container (m))
+ {
+ error (im.loc) << "index member is a container" << endl;
+ valid_ = false;
+ }
+ }
+ }
+ }
+ }
+
+ virtual void
+ traverse_view (type& c)
+ {
+ const view_query& vq (c.get<view_query> ("query"));
+
+ // Make sure we don't have any containers or object pointers.
+ //
+ view_members_.traverse (c);
+
+ names (c, data_member_names_);
+
+ // Allow certain kinds of empty views.
+ //
+ if (vq.kind != view_query::runtime &&
+ vq.kind != view_query::complete_execute)
+ {
+ // Allow all the members to be deleted.
+ //
+ column_count_type const& cc (column_count (c));
+
+ if (cc.total == 0)
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: no persistent data members in the class" << endl;
+ valid_ = false;
+ }
+ }
+ }
+
+ virtual void
+ traverse_composite (type& c)
+ {
+ names (c, data_member_names_);
+ }
+
+ public:
+ bool& valid_;
+
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ data_member2 data_member_;
+ traversal::names data_member_names_;
+
+ object_no_id_members object_no_id_members_;
+ composite_id_members composite_id_members_;
+ view_members view_members_;
+ };
+ }
+
+ void
+ validate (options const&,
+ features&,
+ semantics::unit& u,
+ semantics::path const&,
+ unsigned short pass)
+ {
+ bool valid (true);
+
+ // Validate custom type mapping.
+ //
+ if (pass == 1)
+ {
+ // Create an empty list if we don't have one. This makes the
+ // rest of the code simpler.
+ //
+ if (!u.count ("custom-db-types"))
+ u.set ("custom-db-types", custom_db_types ());
+
+ custom_db_types & cts (u.get<custom_db_types> ("custom-db-types"));
+
+ for (custom_db_types::iterator i (cts.begin ()); i != cts.end (); ++i)
+ {
+ custom_db_type& ct (*i);
+
+ if (ct.type.empty ())
+ {
+ error (ct.loc) << "'type' clause expected in db pragma map" << endl;
+ valid = false;
+ }
+
+ if (ct.as.empty ())
+ {
+ error (ct.loc) << "'as' clause expected in db pragma map" << endl;
+ valid = false;
+ }
+
+ if (ct.to.empty ())
+ ct.to = "(?)";
+ else
+ {
+ size_t p (ct.to.find ("(?)"));
+
+ if (p == string::npos)
+ {
+ error (ct.loc) << "no '(?)' expression in the 'to' clause "
+ << "of db pragma map" << endl;
+ valid = false;
+ }
+ else if (ct.to.find ("(?)", p + 3) != string::npos)
+ {
+ error (ct.loc) << "multiple '(?)' expressions in the 'to' "
+ << "clause of db pragma map" << endl;
+ valid = false;
+ }
+ }
+
+ if (ct.from.empty ())
+ ct.from = "(?)";
+ else
+ {
+ size_t p (ct.from.find ("(?)"));
+
+ if (p == string::npos)
+ {
+ error (ct.loc) << "no '(?)' expression in the 'from' clause "
+ << "of db pragma map" << endl;
+ valid = false;
+ }
+ else if (ct.from.find ("(?)", p + 3) != string::npos)
+ {
+ error (ct.loc) << "multiple '(?)' expressions in the 'from' "
+ << "clause of db pragma map" << endl;
+ valid = false;
+ }
+ }
+ }
+ }
+
+ if (!valid)
+ throw operation_failed ();
+
+ if (pass == 1)
+ {
+ }
+ else
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (true);
+ traversal::namespace_ ns;
+ class2 c (valid);
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (true);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (u);
+ }
+
+ if (!valid)
+ throw operation_failed ();
+ }
+}
diff --git a/odb/odb/relational/validator.hxx b/odb/odb/relational/validator.hxx
new file mode 100644
index 0000000..d6602f7
--- /dev/null
+++ b/odb/odb/relational/validator.hxx
@@ -0,0 +1,24 @@
+// file : odb/relational/validator.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_VALIDATOR_HXX
+#define ODB_RELATIONAL_VALIDATOR_HXX
+
+#include <odb/options.hxx>
+#include <odb/features.hxx>
+#include <odb/semantics/unit.hxx>
+
+namespace relational
+{
+ // The first pass is performed before processing. The second -- after.
+ // Throws operation_failed to signal a failure.
+ //
+ void
+ validate (options const&,
+ features&,
+ semantics::unit&,
+ semantics::path const&,
+ unsigned short pass);
+}
+
+#endif // ODB_RELATIONAL_VALIDATOR_HXX
diff --git a/odb/odb/semantics.hxx b/odb/odb/semantics.hxx
new file mode 100644
index 0000000..83416a6
--- /dev/null
+++ b/odb/odb/semantics.hxx
@@ -0,0 +1,19 @@
+// file : odb/semantics.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_HXX
+#define ODB_SEMANTICS_HXX
+
+#include <odb/semantics/class.hxx>
+#include <odb/semantics/class-template.hxx>
+#include <odb/semantics/derived.hxx>
+#include <odb/semantics/elements.hxx>
+#include <odb/semantics/enum.hxx>
+#include <odb/semantics/fundamental.hxx>
+#include <odb/semantics/namespace.hxx>
+#include <odb/semantics/template.hxx>
+#include <odb/semantics/union.hxx>
+#include <odb/semantics/union-template.hxx>
+#include <odb/semantics/unit.hxx>
+
+#endif // ODB_SEMANTICS_HXX
diff --git a/odb/odb/semantics/class-template.cxx b/odb/odb/semantics/class-template.cxx
new file mode 100644
index 0000000..f8bbca4
--- /dev/null
+++ b/odb/odb/semantics/class-template.cxx
@@ -0,0 +1,54 @@
+// file : odb/semantics/class-template.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/class-template.hxx>
+
+namespace semantics
+{
+ class_template::
+ class_template (path const& file, size_t line, size_t column, tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ class_instantiation::
+ class_instantiation (path const& file,
+ size_t line,
+ size_t column,
+ tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // class_template
+ //
+ {
+ type_info ti (typeid (class_template));
+ ti.add_base (typeid (type_template));
+ ti.add_base (typeid (scope));
+ insert (ti);
+ }
+
+ // class_instantiation
+ //
+ {
+ type_info ti (typeid (class_instantiation));
+ ti.add_base (typeid (class_));
+ ti.add_base (typeid (type_instantiation));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/odb/odb/semantics/class-template.hxx b/odb/odb/semantics/class-template.hxx
new file mode 100644
index 0000000..bffb3f2
--- /dev/null
+++ b/odb/odb/semantics/class-template.hxx
@@ -0,0 +1,68 @@
+// file : odb/semantics/class-template.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_CLASS_TEMPLATE_HXX
+#define ODB_SEMANTICS_CLASS_TEMPLATE_HXX
+
+#include <odb/semantics/elements.hxx>
+#include <odb/semantics/class.hxx>
+#include <odb/semantics/template.hxx>
+
+namespace semantics
+{
+ class class_template: public type_template, public scope
+ {
+ private:
+ typedef std::vector<inherits*> inherits_list;
+
+ public:
+ typedef pointer_iterator<inherits_list::const_iterator> inherits_iterator;
+
+ inherits_iterator
+ inherits_begin () const
+ {
+ return inherits_.begin ();
+ }
+
+ inherits_iterator
+ inherits_end () const
+ {
+ return inherits_.end ();
+ }
+
+ public:
+ class_template (path const&, size_t line, size_t column, tree);
+
+ void
+ add_edge_left (inherits& e)
+ {
+ inherits_.push_back (&e);
+ }
+
+ void
+ add_edge_right (inherits&)
+ {
+ }
+
+ using scope::add_edge_left;
+ using scope::add_edge_right;
+
+ // Resolve conflict between scope::scope and nameable::scope.
+ //
+ using nameable::scope;
+
+ private:
+ inherits_list inherits_;
+ };
+
+ class class_instantiation: public class_, public type_instantiation
+ {
+ public:
+ class_instantiation (path const&, size_t line, size_t column, tree);
+
+ using class_::add_edge_left;
+ using type_instantiation::add_edge_left;
+ };
+}
+
+#endif // ODB_SEMANTICS_CLASS_TEMPLATE_HXX
diff --git a/odb/odb/semantics/class.cxx b/odb/odb/semantics/class.cxx
new file mode 100644
index 0000000..97cf088
--- /dev/null
+++ b/odb/odb/semantics/class.cxx
@@ -0,0 +1,175 @@
+// file : odb/semantics/class.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx> // TYPE_HAS_DEFAULT_CONSTRUCTOR
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/class.hxx>
+
+namespace semantics
+{
+ inherits::
+ inherits (access_type access, bool virt)
+ : virt_ (virt), access_ (access)
+ {
+ }
+
+ class_::
+ class_ (path const& file, size_t line, size_t column, tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ bool class_::
+ default_ctor () const
+ {
+ tree t (tree_node ());
+
+ // TYPE_HAS_DEFAULT_CONSTRUCTOR() returns true if we have a deleted
+ // default ctor. locate_ctor(), on the other hand, returns NULL_TREE in
+ // this case.
+ //
+ if (TYPE_HAS_DEFAULT_CONSTRUCTOR (t))
+ {
+#if BUILDING_GCC_MAJOR >= 8
+
+ // Work around GCC bug 86441. Essentially, we should not trigger an
+ // instantiation or completion of the default ctor. As a result, we will
+ // assume that if we have a lazy default ctor, it is not implicitly
+ // deleted.
+ //
+ if (CLASSTYPE_LAZY_DEFAULT_CTOR (t))
+ return true;
+
+ for (ovl_iterator i (CLASSTYPE_CONSTRUCTORS (t)); i; ++i)
+ {
+ tree f (*i);
+
+ if (TREE_CODE (f) == FUNCTION_DECL && DECL_DELETED_FN (f))
+ continue;
+
+ if (default_ctor_p (f))
+ return true;
+ }
+#else
+ return locate_ctor (t) != NULL_TREE;
+#endif
+
+ }
+
+ return false;
+ }
+
+ bool class_::
+ complete () const
+ {
+ return COMPLETE_TYPE_P (tree_node ());
+ }
+
+ bool class_::
+ abstract () const
+ {
+ return CLASSTYPE_PURE_VIRTUALS (tree_node ());
+ }
+
+ names* class_::
+ lookup (string const& name,
+ type_id const& ti,
+ unsigned int flags,
+ bool* ph) const
+ {
+ bool h (false);
+ bool& rh (ph != 0 ? *ph : h);
+
+ names* r (scope::lookup (name, ti, flags | exclude_outer, &rh));
+
+ if (r != 0)
+ return r;
+
+ // If we found a name but the types didn't match, then bail out
+ // unless we want hidden names.
+ //
+ if (rh && (flags & include_hidden) == 0)
+ return 0;
+
+ // Look in the base classes unless requested not to. For the name
+ // lookup purposes, bases can be viewed as a parallel set of outer
+ // scopes that are searched after the class scope and before any
+ // real outer scope. Interestingly, outer scopes of bases are not
+ // considered during this lookup, only their bases.
+ //
+ if ((flags & exclude_base) == 0)
+ {
+ // Being hidden in one base doesn't mean it is also hidden in the
+ // other. Normally that would be an ambiguous lookup, but we use
+ // relaxed rules.
+ //
+ bool any_h (false); // Indicates whether any base hides the name.
+
+ for (inherits_iterator i (inherits_begin ()); i != inherits_end (); ++i)
+ {
+ bool h (false); // Indicates whether this base hides the name.
+ names* br (i->base ().lookup (name, ti, flags | exclude_outer, &h));
+ any_h = any_h || h;
+
+ if (br != 0)
+ {
+ if (r != 0)
+ throw ambiguous (*r, *br);
+
+ r = br;
+
+ if (h)
+ rh = true;
+ }
+ }
+
+ if (r != 0)
+ return r;
+
+ if (any_h)
+ {
+ rh = true;
+ if ((flags & include_hidden) == 0)
+ return 0;
+ }
+ }
+
+ // Look in the outer scope unless requested not to.
+ //
+ if ((flags & exclude_outer) == 0)
+ return scope ().lookup (name, ti, flags, &rh);
+
+ return 0;
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // inherits
+ //
+ {
+ type_info ti (typeid (inherits));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // class_
+ //
+ {
+ type_info ti (typeid (class_));
+ ti.add_base (typeid (type));
+ ti.add_base (typeid (scope));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/odb/odb/semantics/class.hxx b/odb/odb/semantics/class.hxx
new file mode 100644
index 0000000..e02337a
--- /dev/null
+++ b/odb/odb/semantics/class.hxx
@@ -0,0 +1,142 @@
+// file : odb/semantics/class.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_CLASS_HXX
+#define ODB_SEMANTICS_CLASS_HXX
+
+#include <vector>
+#include <odb/semantics/elements.hxx>
+
+namespace semantics
+{
+ class class_;
+
+ class inherits: public edge
+ {
+ public:
+ typedef semantics::access access_type;
+
+ class_&
+ base () const
+ {
+ return *base_;
+ }
+
+ class_&
+ derived () const
+ {
+ return *derived_;
+ }
+
+ bool
+ virtual_ () const
+ {
+ return virt_;
+ }
+
+ access_type
+ access () const
+ {
+ return access_;
+ }
+
+ public:
+ inherits (access_type, bool virt);
+
+ void
+ set_left_node (class_& n)
+ {
+ derived_ = &n;
+ }
+
+ void
+ set_right_node (class_& n)
+ {
+ base_ = &n;
+ }
+
+ protected:
+ bool virt_;
+ access_type access_;
+
+ class_* base_;
+ class_* derived_;
+ };
+
+ //
+ //
+ class class_: public virtual type, public scope
+ {
+ private:
+ typedef std::vector<inherits*> inherits_list;
+
+ public:
+ typedef pointer_iterator<inherits_list::const_iterator> inherits_iterator;
+
+ inherits_iterator
+ inherits_begin () const
+ {
+ return inherits_.begin ();
+ }
+
+ inherits_iterator
+ inherits_end () const
+ {
+ return inherits_.end ();
+ }
+
+ public:
+ bool
+ default_ctor () const;
+
+ bool
+ complete () const;
+
+ bool
+ abstract () const;
+
+ public:
+ // When doing lookup in class scope, take into account bases.
+ //
+ static unsigned int const exclude_base = 0x04; // Exclude base classes.
+
+ virtual names*
+ lookup (string const& name,
+ type_id const&,
+ unsigned int flags = 0,
+ bool* hidden = 0) const;
+
+ using scope::lookup;
+
+ public:
+ class_ (path const&, size_t line, size_t column, tree);
+
+ void
+ add_edge_left (inherits& e)
+ {
+ inherits_.push_back (&e);
+ }
+
+ void
+ add_edge_right (inherits&)
+ {
+ }
+
+ using scope::add_edge_left;
+ using type::add_edge_right;
+
+ // Resolve conflict between scope::scope and nameable::scope.
+ //
+ using nameable::scope;
+
+ protected:
+ class_ ()
+ {
+ }
+
+ private:
+ inherits_list inherits_;
+ };
+}
+
+#endif // ODB_SEMANTICS_CLASS_HXX
diff --git a/odb/odb/semantics/derived.cxx b/odb/odb/semantics/derived.cxx
new file mode 100644
index 0000000..771ad21
--- /dev/null
+++ b/odb/odb/semantics/derived.cxx
@@ -0,0 +1,254 @@
+// file : odb/semantics/derived.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <sstream>
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/derived.hxx>
+
+using namespace std;
+
+namespace semantics
+{
+ qualifies::
+ qualifies ()
+ : hint_ (0)
+ {
+ }
+
+ qualifier::
+ qualifier (path const& file,
+ size_t line,
+ size_t column,
+ tree tn,
+ bool c,
+ bool v,
+ bool r)
+ : node (file, line, column, tn), c_ (c), v_ (v), r_ (r)
+ {
+ }
+
+ string qualifier::
+ fq_name (names* hint) const
+ {
+ if (hint != 0 || defined_ != 0)
+ return nameable::fq_name (hint);
+
+ // GCC type_as_string() for some reason cannot correctly print names
+ // like 'const std::string'. Instead it prints 'const string'.
+ //
+ type& bt (base_type ());
+
+ // Use the trailing qualifier syntax so that we don't get bogged down
+ // in stuff like 'const const foo*'. We also have to handle arrays in
+ // a special way since char[16] const is not a legal syntax.
+ //
+ string q;
+ if (c_)
+ q += " const";
+
+ if (v_)
+ q += " volatile";
+
+ if (r_)
+ q += " __restrict";
+
+ hint = qualifies ().hint ();
+
+ if (array* a = dynamic_cast<array*> (&bt))
+ return a->fq_name (hint, q);
+ else
+ return bt.fq_name (hint) + q;
+ }
+
+ points::
+ points ()
+ : hint_ (0)
+ {
+ }
+
+ pointer::
+ pointer (path const& file, size_t line, size_t column, tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ string pointer::
+ fq_name (names* hint) const
+ {
+ if (hint != 0 || defined_ != 0)
+ return nameable::fq_name (hint);
+
+ // GCC type_as_string() for some reason cannot correctly print names
+ // like 'const std::string*'. Instead it prints 'const string*'.
+ //
+ string r (base_type ().fq_name (points ().hint ()));
+ r += '*';
+ return r;
+ }
+
+ references::
+ references ()
+ : hint_ (0)
+ {
+ }
+
+ reference::
+ reference (path const& file, size_t line, size_t column, tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ string reference::
+ fq_name (names* hint) const
+ {
+ if (hint != 0 || defined_ != 0)
+ return nameable::fq_name (hint);
+
+ // GCC type_as_string() for some reason cannot correctly print names
+ // like 'const std::string&'. Instead it prints 'const string&'.
+ //
+ string r (base_type ().fq_name (points ().hint ()));
+ r += '&';
+ return r;
+ }
+
+ contains::
+ contains ()
+ : hint_ (0)
+ {
+ }
+
+ array::
+ array (path const& file,
+ size_t line,
+ size_t column,
+ tree tn,
+ unsigned long long size)
+ : node (file, line, column, tn), size_ (size)
+ {
+ }
+
+ string array::
+ fq_name (names* hint) const
+ {
+ // GCC type_as_string() for some reason cannot correctly print names
+ // like 'const std::string[123]'. Instead it prints 'const string[123]'.
+ //
+ string t;
+ return fq_name (hint, t);
+ }
+
+ string array::
+ fq_name (names* hint, string& t) const
+ {
+ if (hint != 0 || defined_ != 0)
+ return nameable::fq_name (hint) + t;
+
+ t += '[';
+ ostringstream ostr;
+ ostr << size ();
+ t += ostr.str ();
+
+ if (size () > 0xFFFFFFFF)
+ t += "ULL";
+ else if (size () > 2147483647)
+ t += "U";
+
+ t += ']';
+
+ type& bt (base_type ());
+ hint = contains ().hint ();
+ array* a;
+
+ if (hint != 0 || (a = dynamic_cast<array*> (&bt)) == 0)
+ return bt.fq_name (hint) + t;
+ else
+ return a->fq_name (0, t);
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // derived_type
+ //
+ {
+ type_info ti (typeid (derived_type));
+ ti.add_base (typeid (type));
+ insert (ti);
+ }
+
+ // qualifies
+ //
+ {
+ type_info ti (typeid (qualifies));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // qualifier
+ //
+ {
+ type_info ti (typeid (qualifier));
+ ti.add_base (typeid (derived_type));
+ insert (ti);
+ }
+
+ // points
+ //
+ {
+ type_info ti (typeid (points));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // pointer
+ //
+ {
+ type_info ti (typeid (pointer));
+ ti.add_base (typeid (derived_type));
+ insert (ti);
+ }
+
+ // references
+ //
+ {
+ type_info ti (typeid (references));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // reference
+ //
+ {
+ type_info ti (typeid (reference));
+ ti.add_base (typeid (derived_type));
+ insert (ti);
+ }
+
+ // contains
+ //
+ {
+ type_info ti (typeid (contains));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // array
+ //
+ {
+ type_info ti (typeid (array));
+ ti.add_base (typeid (derived_type));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/odb/odb/semantics/derived.hxx b/odb/odb/semantics/derived.hxx
new file mode 100644
index 0000000..e58ec9f
--- /dev/null
+++ b/odb/odb/semantics/derived.hxx
@@ -0,0 +1,440 @@
+// file : odb/semantics/derived.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_DERIVED_HXX
+#define ODB_SEMANTICS_DERIVED_HXX
+
+#include <odb/semantics/elements.hxx>
+
+namespace semantics
+{
+ //
+ // Derived types (cvr-qualifiers, pointer, reference, and array).
+ //
+
+ class derived_type: public type
+ {
+ public:
+ virtual type&
+ base_type () const = 0;
+ };
+
+ //
+ //
+ class qualifier;
+
+ class qualifies: public edge
+ {
+ public:
+ typedef semantics::type type_type;
+ typedef semantics::qualifier qualifier_type;
+
+ type_type&
+ type () const
+ {
+ return *type_;
+ }
+
+ qualifier_type&
+ qualifier () const
+ {
+ return *qualifier_;
+ }
+
+ // Name hint of the base type.
+ //
+ public:
+ void
+ hint (names& hint)
+ {
+ hint_ = &hint;
+ }
+
+ names*
+ hint () const
+ {
+ return hint_;
+ }
+
+ public:
+ qualifies ();
+
+ void
+ set_left_node (qualifier_type& n)
+ {
+ qualifier_ = &n;
+ }
+
+ void
+ set_right_node (type_type& n)
+ {
+ type_ = &n;
+ }
+
+ protected:
+ type_type* type_;
+ qualifier_type* qualifier_;
+ names* hint_;
+ };
+
+ class qualifier: public derived_type
+ {
+ public:
+ typedef semantics::qualifies qualifies_type;
+
+ bool
+ const_ () const
+ {
+ return c_;
+ }
+
+ bool
+ volatile_ () const
+ {
+ return v_;
+ }
+
+ bool
+ restrict_ () const
+ {
+ return r_;
+ }
+
+ virtual type&
+ base_type () const
+ {
+ return qualifies_->type ();
+ }
+
+ qualifies_type&
+ qualifies () const
+ {
+ return *qualifies_;
+ }
+
+ public:
+ virtual string
+ fq_name (names*) const;
+
+ public:
+ qualifier (path const&,
+ size_t line,
+ size_t column,
+ tree,
+ bool c,
+ bool v,
+ bool r);
+
+ void
+ add_edge_left (qualifies_type& e)
+ {
+ qualifies_ = &e;
+ }
+
+ private:
+ bool c_, v_, r_;
+ qualifies_type* qualifies_;
+ };
+
+ //
+ //
+ class pointer;
+
+ class points: public edge
+ {
+ public:
+ typedef semantics::type type_type;
+ typedef semantics::pointer pointer_type;
+
+ type_type&
+ type () const
+ {
+ return *type_;
+ }
+
+ pointer_type&
+ pointer () const
+ {
+ return *pointer_;
+ }
+
+ // Name hint of the base type.
+ //
+ public:
+ void
+ hint (names& hint)
+ {
+ hint_ = &hint;
+ }
+
+ names*
+ hint () const
+ {
+ return hint_;
+ }
+
+ public:
+ points ();
+
+ void
+ set_left_node (pointer_type& n)
+ {
+ pointer_ = &n;
+ }
+
+ void
+ set_right_node (type_type& n)
+ {
+ type_ = &n;
+ }
+
+ protected:
+ type_type* type_;
+ pointer_type* pointer_;
+ names* hint_;
+ };
+
+ class pointer: public derived_type
+ {
+ public:
+ typedef semantics::points points_type;
+
+ virtual type&
+ base_type () const
+ {
+ return points_->type ();
+ }
+
+ points_type&
+ points () const
+ {
+ return *points_;
+ }
+
+ public:
+ virtual string
+ fq_name (names*) const;
+
+ public:
+ pointer (path const&, size_t line, size_t column, tree);
+
+ void
+ add_edge_left (points_type& e)
+ {
+ points_ = &e;
+ }
+
+ private:
+ points_type* points_;
+ };
+
+
+ //
+ //
+ class reference;
+
+ class references: public edge
+ {
+ public:
+ typedef semantics::type type_type;
+ typedef semantics::reference reference_type;
+
+ type_type&
+ type () const
+ {
+ return *type_;
+ }
+
+ reference_type&
+ reference () const
+ {
+ return *reference_;
+ }
+
+ // Name hint of the base type.
+ //
+ public:
+ void
+ hint (names& hint)
+ {
+ hint_ = &hint;
+ }
+
+ names*
+ hint () const
+ {
+ return hint_;
+ }
+
+ public:
+ references ();
+
+ void
+ set_left_node (reference_type& n)
+ {
+ reference_ = &n;
+ }
+
+ void
+ set_right_node (type_type& n)
+ {
+ type_ = &n;
+ }
+
+ protected:
+ type_type* type_;
+ reference_type* reference_;
+ names* hint_;
+ };
+
+ class reference: public derived_type
+ {
+ public:
+ typedef semantics::references references_type;
+
+ virtual type&
+ base_type () const
+ {
+ return references_->type ();
+ }
+
+ references_type&
+ references () const
+ {
+ return *references_;
+ }
+
+ public:
+ virtual string
+ fq_name (names*) const;
+
+ public:
+ reference (path const&, size_t line, size_t column, tree);
+
+ void
+ add_edge_left (references_type& e)
+ {
+ references_ = &e;
+ }
+
+ private:
+ references_type* references_;
+ };
+
+
+ //
+ //
+ class array;
+
+ class contains: public edge
+ {
+ public:
+ typedef semantics::type type_type;
+ typedef semantics::array array_type;
+
+ type_type&
+ type () const
+ {
+ return *type_;
+ }
+
+ array_type&
+ array () const
+ {
+ return *array_;
+ }
+
+ // Name hint of the base type.
+ //
+ public:
+ void
+ hint (names& hint)
+ {
+ hint_ = &hint;
+ }
+
+ names*
+ hint () const
+ {
+ return hint_;
+ }
+
+ public:
+ contains ();
+
+ void
+ set_left_node (array_type& n)
+ {
+ array_ = &n;
+ }
+
+ void
+ set_right_node (type_type& n)
+ {
+ type_ = &n;
+ }
+
+ protected:
+ type_type* type_;
+ array_type* array_;
+ names* hint_;
+ };
+
+ class array: public derived_type
+ {
+ public:
+ typedef semantics::contains contains_type;
+
+ // Return the number of elements in the array or 0 if it is not
+ // specified (e.g., int[]).
+ //
+ unsigned long long
+ size () const
+ {
+ return size_;
+ }
+
+ virtual type&
+ base_type () const
+ {
+ return contains_->type ();
+ }
+
+ contains_type&
+ contains () const
+ {
+ return *contains_;
+ }
+
+ public:
+ virtual string
+ fq_name (names*) const;
+
+ private:
+ friend class qualifier;
+
+ string
+ fq_name (names*, string& trailer) const;
+
+ using derived_type::fq_name; // Unhide.
+
+ public:
+ array (path const&,
+ size_t line,
+ size_t column,
+ tree,
+ unsigned long long size);
+
+ void
+ add_edge_left (contains_type& e)
+ {
+ contains_ = &e;
+ }
+
+ private:
+ contains_type* contains_;
+ unsigned long long size_;
+ };
+}
+
+#endif // ODB_SEMANTICS_DERIVED_HXX
diff --git a/odb/odb/semantics/elements.cxx b/odb/odb/semantics/elements.cxx
new file mode 100644
index 0000000..b5793d0
--- /dev/null
+++ b/odb/odb/semantics/elements.cxx
@@ -0,0 +1,619 @@
+// file : odb/semantics/elements.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/cxx-lexer.hxx>
+
+#include <odb/semantics/elements.hxx>
+#include <odb/semantics/namespace.hxx>
+#include <odb/semantics/unit.hxx>
+
+using namespace std;
+
+namespace semantics
+{
+ // access
+ //
+ static char const* access_str[] = {"public", "protected", "private"};
+
+ char const* access::
+ string () const
+ {
+ return access_str[value_];
+ }
+
+ //
+ //
+ node::
+ node (path const& file, size_t line, size_t column, tree tn)
+ : tree_node_ (tn), loc_ (file, line, column)
+ {
+ }
+
+ node::
+ node ()
+ : loc_ (0)
+ {
+ // GCC plugin machinery #define's abort as a macro.
+ //
+ // std::abort ();
+ abort ();
+ }
+
+ // nameable
+ //
+
+ bool nameable::
+ in_scope (scope_type& s)
+ {
+ for (scope_type* p (&scope ());; p = &p->scope_ ())
+ {
+ //@@ Need to handle namespace extensions.
+ //
+ if (p == &s)
+ return true;
+
+ if (!p->named_p () || p->global_scope ())
+ break;
+ }
+
+ return false;
+ }
+
+ bool nameable::
+ anonymous_ () const
+ {
+ tree n (tree_node ());
+
+ if (TYPE_P (n))
+ {
+ tree name (0);
+
+ if (tree decl = TYPE_NAME (n))
+ name = DECL_NAME (decl);
+
+ return name != 0 && IDENTIFIER_ANON_P (name);
+ }
+
+ return true;
+ }
+
+ bool nameable::
+ fq_anonymous_ (scope_entry const* prev) const
+ {
+ scope_entry scope (this, prev);
+
+ // Nameable is fq-anonymous if all the paths to the global scope
+ // have at least one anonymous link.
+ //
+ if (defined_ != 0 || !named_.empty ())
+ {
+ if (named ().global_scope ())
+ return false;
+
+ if (defined_ != 0)
+ {
+ nameable const& s (defined_->scope ());
+
+ if (!scope.find (&s) && !s.fq_anonymous_ (&scope))
+ return false;
+ }
+
+ for (names_list::const_iterator i (named_.begin ()), e (named_.end ());
+ i != e; ++i)
+ {
+ nameable const& s ((*i)->scope ());
+
+ if (!scope.find (&s) && !s.fq_anonymous_ (&scope))
+ return false;
+ }
+ }
+
+ // If we can get a literal name for this type node, then it is not
+ // anonymous as long as its scope is not anonymous.
+ //
+ tree type (tree_node ());
+
+ if (TYPE_P (type))
+ {
+ tree name (0);
+
+ if (tree decl = TYPE_NAME (type))
+ {
+ name = DECL_NAME (decl);
+ if (name != 0 && IDENTIFIER_ANON_P (name))
+ return true;
+
+ tree s (CP_DECL_CONTEXT (decl));
+
+ gcc_tree_code_type tc (TREE_CODE (s));
+
+ if (tc == TYPE_DECL)
+ s = TREE_TYPE (s);
+ else if (tc == NAMESPACE_DECL)
+ {
+ // "Unwind" any inline namespaces since they are not in
+ // semantic grapth.
+ //
+ while (s != global_namespace)
+ {
+ tree prev (CP_DECL_CONTEXT (s));
+
+#if BUILDING_GCC_MAJOR >= 8
+ if (!is_nested_namespace (prev, s, true))
+#else
+ if (!is_associated_namespace (prev, s))
+#endif
+ break;
+
+ s = prev;
+ }
+ }
+
+ if (nameable* n = dynamic_cast<nameable*> (unit ().find (s)))
+ return scope.find (n) || n->fq_anonymous_ (&scope);
+ }
+ else
+ return false; // Assume this is a derived type (e.g., pointer).
+ }
+
+ return true;
+ }
+
+ bool nameable::
+ fq_anonymous (names* hint) const
+ {
+ if (hint != 0 || defined_ != 0)
+ {
+ names& n (hint ? *hint : *defined_);
+
+ if (n.global_scope ())
+ return false;
+
+ return n.scope ().fq_anonymous ();
+ }
+ else
+ return fq_anonymous ();
+ }
+
+ static string
+ qualify_names (string const& n, bool qualify_first)
+ {
+ // @@ Creating a lexer for each call is a bad idea. Need
+ // to cache it somewhere.
+ //
+ cxx_string_lexer l;
+ l.start (n);
+
+ string r, t;
+ bool punc (false);
+ bool scoped (false);
+
+ // Names returned by GCC's type_as_string() (on which this function
+ // is called) include inline namespaces (e.g., std::__cxx11::string).
+ // So, besides fully-qualifying names, this function also needs to get
+ // rid of those. The idea is to resolve names as we lex them, skipping
+ // inline namespaces and stopping once we reach something other than a
+ // namespace.
+ //
+ tree ns (global_namespace);
+ tree id;
+
+ for (cpp_ttype tt = l.next (t, &id); tt != CPP_EOF; tt = l.next (t, &id))
+ {
+ if (punc && tt > CPP_LAST_PUNCTUATOR)
+ r += ' ';
+
+ punc = false;
+ tree new_ns (global_namespace); // By default, revert to global.
+
+ switch (static_cast<unsigned> (tt))
+ {
+ case CPP_LESS:
+ {
+ r += "< ";
+ break;
+ }
+ case CPP_GREATER:
+ {
+ r += " >";
+ break;
+ }
+ case CPP_COMMA:
+ {
+ r += ", ";
+ break;
+ }
+ case CPP_NAME:
+ {
+ // Check if this is a namespace and, if so, whether it is
+ // inline.
+ //
+ if (ns != 0)
+ {
+ new_ns = lookup_qualified_name (ns, id, false, false);
+
+ if (new_ns == error_mark_node ||
+ TREE_CODE (new_ns) != NAMESPACE_DECL)
+ new_ns = 0; // Not a namespace, stop resolving.
+ else
+ {
+ // Check if this is an inline namespace and skip it if so.
+ //
+#if BUILDING_GCC_MAJOR >= 8
+ if (is_nested_namespace (ns, new_ns, true))
+#else
+ if (is_associated_namespace (ns, new_ns))
+#endif
+ {
+ // Skip also the following scope operator. Strictly speaking
+ // there could be none (i.e., this is a name of an inline
+ // namespace) but we only use this function to print names
+ // of anonymous types.
+ //
+ assert (l.next (t) == CPP_SCOPE);
+ continue;
+ }
+ }
+ }
+ else
+ new_ns = 0; // Keep it disabled until we hit a new name.
+
+ // If the name was not preceeded with '::', qualify it.
+ //
+ if (!scoped)
+ {
+ if (!qualify_first)
+ qualify_first = true;
+ else
+ r += "::";
+ }
+
+ r += t;
+ punc = true;
+ break;
+ }
+ case CPP_KEYWORD:
+ case CPP_NUMBER:
+ {
+ r += t;
+ punc = true;
+ break;
+ }
+ case CPP_SCOPE:
+ {
+ new_ns = ns; // Don't change the namespace.
+ }
+ // Fall through.
+ default:
+ {
+ r += t;
+ break;
+ }
+ }
+
+ scoped = (tt == CPP_SCOPE);
+ ns = new_ns;
+ }
+
+ return r;
+ }
+
+ string nameable::
+ name_ () const
+ {
+ tree n (tree_node ());
+
+ if (!TYPE_P (n))
+ return "<anonymous>";
+
+ // @@ Doing this once and caching the result is probably a
+ // good idea.
+ //
+ return qualify_names (
+ type_as_string (n, TFF_PLAIN_IDENTIFIER | TFF_UNQUALIFIED_NAME), false);
+ }
+
+ string nameable::
+ fq_name () const
+ {
+ return fq_name_ (0);
+ }
+
+ string nameable::
+ fq_name_ (scope_entry const* prev) const
+ {
+ // @@ Doing this once and caching the result is probably a
+ // good idea.
+ //
+ scope_entry scope (this, prev);
+
+ if (named_p () && named ().global_scope ())
+ return "";
+
+ if (defined_ != 0)
+ {
+ nameable const& s (defined_->scope ());
+
+ if (!scope.find (&s) && !s.fq_anonymous_ (&scope))
+ return s.fq_name_ (&scope) + "::" + name ();
+ }
+
+ for (names_list::const_iterator i (named_.begin ()), e (named_.end ());
+ i != e; ++i)
+ {
+ nameable const& s ((*i)->scope ());
+
+ if (!scope.find (&s) && !s.fq_anonymous_ (&scope))
+ return s.fq_name_ (&scope) + "::" + name ();
+ }
+
+ tree n (tree_node ());
+
+ if (!TYPE_P (n))
+ return "<anonymous>";
+
+ return qualify_names (type_as_string (n, TFF_PLAIN_IDENTIFIER), true);
+ }
+
+ string nameable::
+ fq_name (names* hint) const
+ {
+ if (hint != 0 || defined_ != 0)
+ {
+ names& n (hint ? *hint : *defined_);
+
+ if (n.global_scope ())
+ return "";
+
+ return n.scope ().fq_name () + "::" + n.name ();
+ }
+ else
+ {
+ // Since there was no hint, prefer the literal name over the names
+ // edges.
+ //
+ tree n (tree_node ());
+
+ if (TYPE_P (n))
+ return qualify_names (type_as_string (n, TFF_PLAIN_IDENTIFIER), true);
+
+ // Last resort is to call the other version of fq_name which will
+ // check the names edges.
+ //
+ return fq_name ();
+ }
+ }
+
+ // scope
+ //
+
+ scope::names_iterator_pair scope::
+ find (string const& name) const
+ {
+ names_map::const_iterator i (names_map_.find (name));
+
+ if (i == names_map_.end ())
+ return names_iterator_pair (names_.end (), names_.end ());
+ else
+ return names_iterator_pair (i->second.begin (), i->second.end ());
+ }
+
+ scope::names_iterator scope::
+ find (names& e)
+ {
+ list_iterator_map::iterator i (iterator_map_.find (&e));
+ return i != iterator_map_.end () ? i->second : names_.end ();
+ }
+
+ static bool
+ is_base (type_id const& b, compiler::type_info const& d)
+ {
+ using compiler::type_info;
+
+ for (type_info::base_iterator i (d.begin_base ());
+ i != d.end_base (); ++i)
+ {
+ type_info const& ti (i->type_info ());
+
+ if (b == ti.type_id () || is_base (b, ti))
+ return true;
+ }
+
+ return false;
+ }
+
+ names* scope::
+ lookup (string const& name,
+ type_id const& ti,
+ unsigned int flags,
+ bool* hidden) const
+ {
+ names_iterator_pair p (find (name));
+ names* r (0);
+
+ for (names_const_iterator i (p.first); i != p.second; ++i)
+ {
+ type_id const& xti (typeid (i->named ()));
+
+ // If types are equal, then we found a match. Also check if ti is
+ // a base type of xti.
+ //
+ if (xti == ti || is_base (ti, compiler::lookup (xti)))
+ {
+ if (r != 0)
+ {
+ // If both are namespaces, then the one is just an extension
+ // of the other.
+ //
+ if (!(r->named ().is_a<namespace_> () &&
+ i->named ().is_a<namespace_> ()))
+ throw ambiguous (*r, *i);
+ }
+ else
+ r = &*i;
+ }
+ }
+
+ if (r != 0)
+ return r;
+
+ // If we found a name but the types didn't match, then bail out
+ // unless we want hidden names.
+ //
+ if (p.first != p.second)
+ {
+ if (hidden != 0)
+ *hidden = true;
+
+ if ((flags & include_hidden) == 0)
+ return 0;
+ }
+
+ // Look in the outer scope unless requested not to or if this is
+ // the global scope.
+ //
+ if ((flags & exclude_outer) == 0 && named_p () && !global_scope ())
+ return scope ().lookup (name, ti, flags, hidden);
+
+ return 0;
+ }
+
+ void scope::
+ add_edge_left (names& e)
+ {
+ names_list::iterator i (names_.insert (names_.end (), &e));
+ iterator_map_[&e] = i;
+ names_map_[e.name ()].push_back (&e);
+ }
+
+ void scope::
+ add_edge_left (names& e, names_iterator after)
+ {
+ names_list::iterator i;
+
+ if (after.base () == names_.end ())
+ i = names_.insert (names_.begin (), &e);
+ else
+ {
+ names_list::iterator j (after.base ());
+ i = names_.insert (++j, &e);
+ }
+
+ iterator_map_[&e] = i;
+ names_map_[e.name ()].push_back (&e);
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // node
+ //
+ insert (type_info (typeid (node)));
+
+ // edge
+ //
+ insert (type_info (typeid (edge)));
+
+ // names
+ //
+ {
+ type_info ti (typeid (names));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // declares
+ //
+ {
+ type_info ti (typeid (declares));
+ ti.add_base (typeid (names));
+ insert (ti);
+ }
+
+ // defines
+ //
+ {
+ type_info ti (typeid (defines));
+ ti.add_base (typeid (declares));
+ insert (ti);
+ }
+
+ // typedefs
+ //
+ {
+ type_info ti (typeid (typedefs));
+ ti.add_base (typeid (declares));
+ insert (ti);
+ }
+
+ // nameable
+ //
+ {
+ type_info ti (typeid (nameable));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+
+ // scope
+ //
+ {
+ type_info ti (typeid (scope));
+ ti.add_base (typeid (nameable));
+ insert (ti);
+ }
+
+ // type
+ //
+ {
+ type_info ti (typeid (type));
+ ti.add_base (typeid (nameable));
+ insert (ti);
+ }
+
+ // belongs
+ //
+ {
+ type_info ti (typeid (belongs));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // instance
+ //
+ {
+ type_info ti (typeid (instance));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+
+ // data_member
+ //
+ {
+ type_info ti (typeid (data_member));
+ ti.add_base (typeid (nameable));
+ ti.add_base (typeid (instance));
+ insert (ti);
+ }
+
+ // unsupported_type
+ //
+ {
+ type_info ti (typeid (unsupported_type));
+ ti.add_base (typeid (type));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/odb/odb/semantics/elements.hxx b/odb/odb/semantics/elements.hxx
new file mode 100644
index 0000000..699a1be
--- /dev/null
+++ b/odb/odb/semantics/elements.hxx
@@ -0,0 +1,841 @@
+// file : odb/semantics/elements.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_ELEMENTS_HXX
+#define ODB_SEMANTICS_ELEMENTS_HXX
+
+#include <map>
+#include <list>
+#include <vector>
+#include <string>
+#include <cstdlib> // std::abort
+#include <cstddef> // std::size_t
+#include <utility> // std::pair
+#include <cassert>
+
+#include <libcutl/fs/path.hxx>
+#include <libcutl/container/graph.hxx>
+#include <libcutl/container/pointer-iterator.hxx>
+#include <libcutl/compiler/type-id.hxx>
+#include <libcutl/compiler/context.hxx>
+
+#include <odb/gcc-fwd.hxx>
+#include <odb/location.hxx>
+
+namespace semantics
+{
+ using namespace cutl;
+
+ using std::size_t;
+ using std::string;
+
+ using container::graph;
+ using container::pointer_iterator;
+
+ using compiler::type_id;
+ using compiler::context;
+
+ //
+ //
+ using fs::path;
+ using fs::invalid_path;
+
+ //
+ //
+ class access
+ {
+ public:
+ enum value { public_, protected_, private_ };
+
+ access (value v)
+ : value_ (v)
+ {
+ }
+
+ operator value () const
+ {
+ return value_;
+ }
+
+ char const* string () const;
+
+ private:
+ value value_;
+ };
+
+ //
+ //
+ class node;
+ class edge;
+ class unit;
+
+ // Support for inserting edges at specified positions.
+ //
+ template <typename N, typename I>
+ struct node_position
+ {
+ node_position (N& node, I pos)
+ : node_ (node), pos_ (pos)
+ {
+ }
+
+ operator N& () const
+ {
+ return node_;
+ }
+
+ template <typename E>
+ void
+ add_edge_left (E& e)
+ {
+ node_.add_edge_left (e, pos_);
+ }
+
+ template <typename E>
+ void
+ add_edge_right (E& e)
+ {
+ node_.add_edge_right (e, pos_);
+ }
+
+ private:
+ N& node_;
+ I pos_;
+ };
+
+ //
+ //
+ class edge: public context
+ {
+ public:
+ virtual
+ ~edge () {}
+
+ public:
+ template <typename X>
+ X*
+ is_a () {return dynamic_cast<X*> (this);}
+
+ template <typename X>
+ const X*
+ is_a () const {return dynamic_cast<const X*> (this);}
+ };
+
+ //
+ //
+ class node: public context
+ {
+ public:
+ virtual
+ ~node () {}
+
+ public:
+ tree
+ tree_node () const
+ {
+ return tree_node_;
+ }
+
+ public:
+ typedef ::location location_type;
+
+ path const&
+ file () const
+ {
+ return loc_.file;
+ }
+
+ size_t
+ line () const
+ {
+ return loc_.line;
+ }
+
+ size_t
+ column () const
+ {
+ return loc_.column;
+ }
+
+ location_type const&
+ location () const
+ {
+ return loc_;
+ }
+
+ public:
+ template <typename X>
+ X*
+ is_a () {return dynamic_cast<X*> (this);}
+
+ template <typename X>
+ const X*
+ is_a () const {return dynamic_cast<const X*> (this);}
+
+ public:
+ node (path const& file, size_t line, size_t column, tree);
+
+ // Sink functions that allow extensions in the form of one-way
+ // edges.
+ //
+ void
+ add_edge_left (edge&) {}
+
+ void
+ add_edge_right (edge&) {}
+
+ protected:
+ // For virtual inheritance. Should never be actually called.
+ //
+ node ();
+
+ protected:
+ typedef semantics::unit unit_type;
+
+ unit_type const&
+ unit () const
+ {
+ return *unit_;
+ }
+
+ unit_type&
+ unit ()
+ {
+ return *unit_;
+ }
+
+ private:
+ friend class semantics::unit;
+
+ void
+ unit (unit_type& u)
+ {
+ unit_ = &u;
+ }
+
+ private:
+ tree tree_node_;
+ unit_type* unit_;
+
+ location_type loc_;
+ };
+
+ //
+ //
+ class scope;
+ class nameable;
+
+
+ //
+ //
+ class names: public edge
+ {
+ public:
+ typedef semantics::scope scope_type;
+ typedef semantics::access access_type;
+
+ string const&
+ name () const
+ {
+ return name_;
+ }
+
+ scope_type&
+ scope () const
+ {
+ return *scope_;
+ }
+
+ // Return true if the entity that this edge names is a global scope.
+ // In this case calling scope() is undefined behavior.
+ //
+ bool
+ global_scope () const
+ {
+ return scope_ == 0;
+ }
+
+ nameable&
+ named () const
+ {
+ return *named_;
+ }
+
+ access_type
+ access () const
+ {
+ return access_;
+ }
+
+ // Names edge in terms of which this edge was defined. Can be NULL.
+ //
+ public:
+ void
+ hint (names& hint)
+ {
+ hint_ = &hint;
+ }
+
+ names*
+ hint () const
+ {
+ return hint_;
+ }
+
+ public:
+ names (string const& name, access_type access = access_type::public_)
+ : name_ (name), access_ (access), hint_ (0)
+ {
+ }
+
+ void
+ set_left_node (scope_type& n)
+ {
+ scope_ = &n;
+ }
+
+ void
+ set_right_node (nameable& n)
+ {
+ named_ = &n;
+ }
+
+ protected:
+ scope_type* scope_;
+ nameable* named_;
+ string name_;
+ access_type access_;
+ names* hint_;
+ };
+
+ //
+ // Declarations and definitions.
+ //
+
+ class declares: public names
+ {
+ public:
+ declares (string const& name, access_type access = access_type::public_)
+ : names (name, access)
+ {
+ }
+ };
+
+ class defines: public declares
+ {
+ public:
+ defines (string const& name, access_type access = access_type::public_)
+ : declares (name, access)
+ {
+ }
+ };
+
+ class type;
+ class typedefs: public declares
+ {
+ public:
+ typedef semantics::type type_type;
+
+ type_type&
+ type () const;
+
+ public:
+ typedefs (string const& name, access_type access = access_type::public_)
+ : declares (name, access)
+ {
+ }
+ };
+
+ //
+ //
+ class nameable: public virtual node
+ {
+ typedef std::vector<names*> names_list;
+
+ public:
+ typedef semantics::scope scope_type;
+
+ // Return true if this type is unnamed and no literal name, such as
+ // template-id or derived type declarator, can be used instead.
+ //
+ bool
+ anonymous () const
+ {
+ if (defined_ != 0 || !named_.empty ())
+ return false;
+
+ return anonymous_ ();
+ }
+
+ // Return true if the node itself or any of the scopes up to the
+ // global scope is anonymous. For a named class nested in an unnamed
+ // class, anonymous() will return false and fq_anonymous() will
+ // return true.
+ //
+ bool
+ fq_anonymous () const
+ {
+ return fq_anonymous_ (0);
+ }
+
+ // As above but use the hint to select the first outer scope. If
+ // hint is 0, use the defines edge.
+ //
+ bool
+ fq_anonymous (names* hint) const;
+
+ // Return the node's unqualifed name. If the node has a name, then
+ // return it, preferring the defines edge. Otherwise, return a
+ // literal name, e.g., template-id or a derived type declarator.
+ // Finally, if the type is anonymous, return <anonymous> string.
+ //
+ string
+ name () const
+ {
+ if (defined_ != 0)
+ return defined_->name ();
+
+ if (!named_.empty ())
+ return named_[0]->name ();
+
+ return name_ ();
+ }
+
+ // Return the node's fully-qualifed name.
+ //
+ virtual string
+ fq_name () const;
+
+ // As above but use the hint to select the first outer scope. If hint
+ // is 0, use the defines edge.
+ //
+ virtual string
+ fq_name (names* hint) const;
+
+ // Return true if the type is named.
+ //
+ bool
+ named_p () const
+ {
+ return defined_ != 0 || !named_.empty ();
+ }
+
+ scope_type&
+ scope () const
+ {
+ return named ().scope ();
+ }
+
+ names&
+ named () const
+ {
+ return defined_ != 0 ? *defined_ : *named_[0];
+ }
+
+ bool
+ in_scope (scope_type&);
+
+ public:
+ nameable ()
+ : defined_ (0)
+ {
+ }
+
+ void
+ add_edge_right (defines& e)
+ {
+ assert (defined_ == 0);
+ defined_ = &e;
+ }
+
+ void
+ add_edge_right (names& e)
+ {
+ named_.push_back (&e);
+ }
+
+ using node::add_edge_right;
+
+ protected:
+ // We need to keep the scope we have seen in the fq_* function
+ // family in order to detect names that are inside the node
+ // and which would otherwise lead to infinite recursion. Here
+ // is the canonical example:
+ //
+ // template <typename X>
+ // class c
+ // {
+ // typedef c this_type;
+ // };
+ //
+ struct scope_entry
+ {
+ scope_entry (nameable const* e, scope_entry const* p)
+ : entry_ (e), prev_ (p)
+ {
+ }
+
+ bool
+ find (nameable const* n) const
+ {
+ for (scope_entry const* i (this); i != 0; i = i->prev_)
+ if (i->entry_ == n)
+ return true;
+
+ return false;
+ }
+
+ private:
+ nameable const* entry_;
+ scope_entry const* prev_;
+ };
+
+ bool
+ anonymous_ () const;
+
+ bool
+ fq_anonymous_ (scope_entry const*) const;
+
+ string
+ name_ () const;
+
+ string
+ fq_name_ (scope_entry const*) const;
+
+ protected:
+ defines* defined_;
+ names_list named_;
+ };
+
+
+ // Ambiguous name lookup exception.
+ //
+ struct ambiguous
+ {
+ ambiguous (names& f, names& s): first (f), second (s) {}
+ names& first;
+ names& second;
+ };
+
+ // Unresolved name lookup exception.
+ //
+ struct unresolved
+ {
+ unresolved (string const& n, bool tm): name (n), type_mismatch (tm) {}
+ string name;
+ bool type_mismatch; // True if the name resolved but types didn't match.
+ };
+
+ //
+ //
+ class scope: public virtual nameable
+ {
+ protected:
+ typedef std::list<names*> names_list;
+ typedef std::map<names*, names_list::iterator> list_iterator_map;
+ typedef std::map<string, names_list> names_map;
+
+ public:
+ typedef pointer_iterator<names_list::iterator> names_iterator;
+ typedef pointer_iterator<names_list::const_iterator> names_const_iterator;
+
+ typedef
+ std::pair<names_const_iterator, names_const_iterator>
+ names_iterator_pair;
+
+ public:
+ bool
+ global_scope () const
+ {
+ return named ().global_scope ();
+ }
+
+ scope&
+ scope_ () const
+ {
+ return nameable::scope ();
+ }
+
+ names_iterator
+ names_begin ()
+ {
+ return names_.begin ();
+ }
+
+ names_iterator
+ names_end ()
+ {
+ return names_.end ();
+ }
+
+ names_const_iterator
+ names_begin () const
+ {
+ return names_.begin ();
+ }
+
+ names_const_iterator
+ names_end () const
+ {
+ return names_.end ();
+ }
+
+ // Find a name in this scope.
+ //
+ public:
+ virtual names_iterator_pair
+ find (string const& name) const;
+
+ names_iterator
+ find (names&);
+
+ // Lookup a name of the specified type in this scope and, if not
+ // found, in outer scopes.
+ //
+ public:
+ static unsigned int const exclude_outer = 0x01; // Exclude outer scopes.
+ static unsigned int const include_hidden = 0x02; // Include hidden names.
+
+ virtual names*
+ lookup (string const& name,
+ type_id const&,
+ unsigned int flags = 0,
+ bool* hidden = 0) const;
+
+ template <typename T>
+ T&
+ lookup (string const& name, unsigned int flags = 0) const
+ {
+ bool hidden (false);
+
+ if (names* n = lookup (name, typeid (T), flags, &hidden))
+ return dynamic_cast<T&> (n->named ());
+
+ throw unresolved (name, hidden);
+ }
+
+ public:
+ scope (path const& file, size_t line, size_t column, tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ void
+ add_edge_left (names&);
+
+ void
+ add_edge_left (names&, names_iterator after);
+
+ using nameable::add_edge_right;
+
+ protected:
+ scope ()
+ {
+ }
+
+ private:
+ names_list names_;
+ list_iterator_map iterator_map_;
+ names_map names_map_;
+ };
+
+ //
+ //
+ class points;
+
+ class belongs;
+ class qualifies;
+
+ class type: public virtual nameable
+ {
+ typedef std::vector<qualifies*> qualified;
+
+ public:
+ typedef pointer_iterator<qualified::const_iterator> qualified_iterator;
+
+ qualified_iterator
+ qualified_begin () const
+ {
+ return qualified_.begin ();
+ }
+
+ qualified_iterator
+ qualified_end () const
+ {
+ return qualified_.end ();
+ }
+
+ public:
+ bool
+ pointed_p () const {return pointed_ != 0;}
+
+ points&
+ pointed () const {return *pointed_;}
+
+ public:
+ type (): pointed_ (0) {}
+
+ void
+ add_edge_right (belongs&)
+ {
+ }
+
+ void
+ add_edge_right (qualifies& e)
+ {
+ qualified_.push_back (&e);
+ }
+
+ void
+ add_edge_right (points& e)
+ {
+ pointed_ = &e;
+ }
+
+ using nameable::add_edge_right;
+
+ private:
+ qualified qualified_;
+ points* pointed_;
+ };
+
+ //
+ //
+ class instance;
+
+ class belongs: public edge
+ {
+ public:
+ typedef semantics::type type_type;
+ typedef semantics::instance instance_type;
+
+ type_type&
+ type () const
+ {
+ return *type_;
+ }
+
+ instance_type&
+ instance () const
+ {
+ return *instance_;
+ }
+
+ public:
+ void
+ hint (names& hint)
+ {
+ hint_ = &hint;
+ }
+
+ names*
+ hint () const
+ {
+ return hint_;
+ }
+
+ public:
+ belongs ()
+ : hint_ (0)
+ {
+ }
+
+ void
+ set_left_node (instance_type& n)
+ {
+ instance_ = &n;
+ }
+
+ void
+ set_right_node (type_type& n)
+ {
+ type_ = &n;
+ }
+
+ private:
+ type_type* type_;
+ instance_type* instance_;
+ names* hint_;
+ };
+
+ //
+ //
+ class instance: public virtual node
+ {
+ public:
+ typedef semantics::type type_type;
+ typedef semantics::belongs belongs_type;
+
+ type_type&
+ type () const
+ {
+ return belongs_->type ();
+ }
+
+ belongs_type&
+ belongs () const
+ {
+ return *belongs_;
+ }
+
+ public:
+ void
+ add_edge_left (belongs_type& e)
+ {
+ belongs_ = &e;
+ }
+
+ protected:
+ instance ()
+ {
+ }
+
+ private:
+ belongs_type* belongs_;
+ };
+
+ // Data member for class and union types.
+ //
+ class data_member: public nameable, public instance
+ {
+ public:
+ data_member (path const& file, size_t line, size_t column, tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ protected:
+ data_member ()
+ {
+ }
+ };
+
+ // Unsupported type.
+ //
+ class unsupported_type: public type
+ {
+ public:
+ string const&
+ type_name () const
+ {
+ return type_name_;
+ }
+
+ public:
+ unsupported_type (path const& file,
+ size_t line,
+ size_t column,
+ tree tn,
+ string const& type_name)
+ : node (file, line, column, tn), type_name_ (type_name)
+ {
+ }
+
+ private:
+ string const type_name_;
+ };
+}
+
+#include <odb/semantics/elements.ixx>
+
+#endif // ODB_SEMANTICS_ELEMENTS_HXX
diff --git a/odb/odb/semantics/elements.ixx b/odb/odb/semantics/elements.ixx
new file mode 100644
index 0000000..32f9ced
--- /dev/null
+++ b/odb/odb/semantics/elements.ixx
@@ -0,0 +1,13 @@
+// file : odb/semantics/elements.ixx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+namespace semantics
+{
+ // typedefs
+ //
+ inline typedefs::type_type& typedefs::
+ type () const
+ {
+ return dynamic_cast<type_type&> (named ());
+ }
+}
diff --git a/odb/odb/semantics/enum.cxx b/odb/odb/semantics/enum.cxx
new file mode 100644
index 0000000..6432986
--- /dev/null
+++ b/odb/odb/semantics/enum.cxx
@@ -0,0 +1,85 @@
+// file : odb/semantics/enum.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/enum.hxx>
+
+namespace semantics
+{
+ enumerates::
+ enumerates ()
+ {
+ }
+
+ enumerator::
+ enumerator (path const& file,
+ size_t line,
+ size_t column,
+ tree tn,
+ unsigned long long value)
+ : node (file, line, column, tn), value_ (value)
+ {
+ }
+
+ underlies::
+ underlies ()
+ : type_ (0), enum__ (0), hint_ (0)
+ {
+ }
+
+ enum_::
+ enum_ (path const& file,
+ size_t line,
+ size_t column,
+ tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // enumerates
+ //
+ {
+ type_info ti (typeid (enumerates));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // enumerator
+ //
+ {
+ type_info ti (typeid (enumerator));
+ ti.add_base (typeid (nameable));
+ ti.add_base (typeid (instance));
+ insert (ti);
+ }
+
+ // underlies
+ //
+ {
+ type_info ti (typeid (underlies));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // enum_
+ //
+ {
+ type_info ti (typeid (enum_));
+ ti.add_base (typeid (type));
+ ti.add_base (typeid (scope));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/odb/odb/semantics/enum.hxx b/odb/odb/semantics/enum.hxx
new file mode 100644
index 0000000..bfcce53
--- /dev/null
+++ b/odb/odb/semantics/enum.hxx
@@ -0,0 +1,228 @@
+// file : odb/semantics/enum.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_ENUM_HXX
+#define ODB_SEMANTICS_ENUM_HXX
+
+#include <vector>
+#include <odb/semantics/elements.hxx>
+#include <odb/semantics/fundamental.hxx>
+
+namespace semantics
+{
+ class enum_;
+ class enumerator;
+
+ class enumerates: public edge
+ {
+ public:
+ typedef semantics::enum_ enum_type;
+ typedef semantics::enumerator enumerator_type;
+
+ enum_type&
+ enum_ () const
+ {
+ return *enum__;
+ }
+
+ enumerator_type&
+ enumerator () const
+ {
+ return *enumerator_;
+ }
+
+ public:
+ enumerates ();
+
+ void
+ set_left_node (enum_type& n)
+ {
+ enum__ = &n;
+ }
+
+ void
+ set_right_node (enumerator_type& n)
+ {
+ enumerator_ = &n;
+ }
+
+ protected:
+ enum_type* enum__;
+ enumerator_type* enumerator_;
+ };
+
+ //
+ //
+ class enumerator: public nameable, public instance
+ {
+ public:
+ typedef semantics::enum_ enum_type;
+
+ enum_type&
+ enum_ () const
+ {
+ return enumerated_->enum_ ();
+ }
+
+ enumerates&
+ enumerated () const
+ {
+ return *enumerated_;
+ }
+
+ // If the enumeration is signed, then this value should be re-
+ // interpreted as signed.
+ //
+ unsigned long long
+ value () const
+ {
+ return value_;
+ }
+
+ public:
+ enumerator (path const&,
+ size_t line,
+ size_t column,
+ tree,
+ unsigned long long value);
+
+ void
+ add_edge_right (enumerates& e)
+ {
+ enumerated_ = &e;
+ }
+
+ using nameable::add_edge_right;
+
+ private:
+ unsigned long long value_;
+ enumerates* enumerated_;
+ };
+
+ //
+ //
+ class underlies: public edge
+ {
+ public:
+ typedef semantics::enum_ enum_type;
+
+ integral_type&
+ type () const
+ {
+ return *type_;
+ }
+
+ enum_type&
+ enum_ () const
+ {
+ return *enum__;
+ }
+
+ // Names edge in terms of which this edge was defined. Can be NULL.
+ //
+ public:
+ void
+ hint (names& hint)
+ {
+ hint_ = &hint;
+ }
+
+ names*
+ hint () const
+ {
+ return hint_;
+ }
+
+ public:
+ underlies ();
+
+ void
+ set_left_node (integral_type& n)
+ {
+ type_ = &n;
+ }
+
+ void
+ set_right_node (enum_type& n)
+ {
+ enum__ = &n;
+ }
+
+ protected:
+ integral_type* type_;
+ enum_type* enum__;
+ names* hint_;
+ };
+
+ //
+ //
+ class enum_: public type, public scope
+ {
+ private:
+ typedef std::vector<enumerates*> enumerates_list;
+
+ public:
+ typedef
+ pointer_iterator<enumerates_list::const_iterator>
+ enumerates_iterator;
+
+ enumerates_iterator
+ enumerates_begin () const
+ {
+ return enumerates_.begin ();
+ }
+
+ enumerates_iterator
+ enumerates_end () const
+ {
+ return enumerates_.end ();
+ }
+
+ underlies&
+ underlied () const
+ {
+ return *underlied_;
+ }
+
+ integral_type&
+ underlying_type () const
+ {
+ return underlied_->type ();
+ }
+
+ names*
+ underlying_type_hint () const
+ {
+ return underlied_->hint ();
+ }
+
+ bool
+ unsigned_ () const
+ {
+ return underlying_type ().unsigned_ ();
+ }
+
+ public:
+ enum_ (path const&, size_t line, size_t column, tree);
+
+ void
+ add_edge_right (underlies& e)
+ {
+ underlied_ = &e;
+ }
+
+ void
+ add_edge_left (enumerates& e)
+ {
+ enumerates_.push_back (&e);
+ }
+
+ using scope::add_edge_left;
+
+ private:
+ enumerates_list enumerates_;
+ underlies* underlied_;
+ };
+}
+
+#endif // ODB_SEMANTICS_ENUM_HXX
diff --git a/odb/odb/semantics/fundamental.cxx b/odb/odb/semantics/fundamental.cxx
new file mode 100644
index 0000000..ed4a67f
--- /dev/null
+++ b/odb/odb/semantics/fundamental.cxx
@@ -0,0 +1,222 @@
+// file : odb/semantics/fundamental.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/fundamental.hxx>
+
+namespace semantics
+{
+ string fund_type::
+ fq_name () const
+ {
+ return name ();
+ }
+
+ string fund_type::
+ fq_name (names* hint) const
+ {
+ if (hint == 0)
+ return name ();
+
+ return type::fq_name (hint);
+ }
+
+ // char
+ //
+ bool fund_char::
+ unsigned_ () const
+ {
+ return TYPE_UNSIGNED (tree_node ()) != 0;
+ }
+
+ // wchar_t
+ //
+ bool fund_wchar::
+ unsigned_ () const
+ {
+ return TYPE_UNSIGNED (tree_node ()) != 0;
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // fund_type
+ //
+ {
+ type_info ti (typeid (fund_type));
+ ti.add_base (typeid (type));
+ insert (ti);
+ }
+
+ // fund_void
+ //
+ {
+ type_info ti (typeid (fund_void));
+ ti.add_base (typeid (fund_type));
+ insert (ti);
+ }
+
+ // integral_type
+ //
+ {
+ type_info ti (typeid (integral_type));
+ ti.add_base (typeid (fund_type));
+ insert (ti);
+ }
+
+ // fund_bool
+ //
+ {
+ type_info ti (typeid (fund_bool));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_char
+ //
+ {
+ type_info ti (typeid (fund_char));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_wchar
+ //
+ {
+ type_info ti (typeid (fund_wchar));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_char16
+ //
+ {
+ type_info ti (typeid (fund_char16));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_char32
+ //
+ {
+ type_info ti (typeid (fund_char32));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_signed_char
+ //
+ {
+ type_info ti (typeid (fund_signed_char));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_unsigned_char
+ //
+ {
+ type_info ti (typeid (fund_unsigned_char));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_short
+ //
+ {
+ type_info ti (typeid (fund_short));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_unsigned_short
+ //
+ {
+ type_info ti (typeid (fund_unsigned_short));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_int
+ //
+ {
+ type_info ti (typeid (fund_int));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_unsigned_int
+ //
+ {
+ type_info ti (typeid (fund_unsigned_int));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_long
+ //
+ {
+ type_info ti (typeid (fund_long));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_unsigned_long
+ //
+ {
+ type_info ti (typeid (fund_unsigned_long));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_long_long
+ //
+ {
+ type_info ti (typeid (fund_long_long));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_unsigned_long_long
+ //
+ {
+ type_info ti (typeid (fund_unsigned_long_long));
+ ti.add_base (typeid (integral_type));
+ insert (ti);
+ }
+
+ // fund_float
+ //
+ {
+ type_info ti (typeid (fund_float));
+ ti.add_base (typeid (fund_type));
+ insert (ti);
+ }
+
+ // fund_double
+ //
+ {
+ type_info ti (typeid (fund_double));
+ ti.add_base (typeid (fund_type));
+ insert (ti);
+ }
+
+ // fund_long_double
+ //
+ {
+ type_info ti (typeid (fund_long_double));
+ ti.add_base (typeid (fund_type));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/odb/odb/semantics/fundamental.hxx b/odb/odb/semantics/fundamental.hxx
new file mode 100644
index 0000000..15b5cbb
--- /dev/null
+++ b/odb/odb/semantics/fundamental.hxx
@@ -0,0 +1,150 @@
+// file : odb/semantics/fundamental.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_FUNDAMENTAL_HXX
+#define ODB_SEMANTICS_FUNDAMENTAL_HXX
+
+#include <odb/semantics/elements.hxx>
+
+namespace semantics
+{
+ //
+ // Fundamental C++ types.
+ //
+
+ struct fund_type: type
+ {
+ virtual string
+ fq_name () const;
+
+ virtual string
+ fq_name (names*) const;
+ };
+
+ struct fund_void: fund_type
+ {
+ fund_void (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ };
+
+ //
+ // Integral.
+ //
+
+ struct integral_type: fund_type
+ {
+ virtual bool
+ unsigned_ () const = 0;
+ };
+
+ struct fund_bool: integral_type
+ {
+ fund_bool (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const {return true;}
+ };
+
+ struct fund_char: integral_type
+ {
+ fund_char (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const;
+ };
+
+ struct fund_wchar: integral_type
+ {
+ fund_wchar (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const;
+ };
+
+ struct fund_char16: integral_type
+ {
+ fund_char16 (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const {return true;}
+ };
+
+ struct fund_char32: integral_type
+ {
+ fund_char32 (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const {return true;}
+ };
+
+ struct fund_signed_char: integral_type
+ {
+ fund_signed_char (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const {return false;}
+ };
+
+ struct fund_unsigned_char: integral_type
+ {
+ fund_unsigned_char (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const {return true;}
+ };
+
+ struct fund_short: integral_type
+ {
+ fund_short (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const {return false;}
+ };
+
+ struct fund_unsigned_short: integral_type
+ {
+ fund_unsigned_short (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const {return true;}
+ };
+
+ struct fund_int: integral_type
+ {
+ fund_int (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const {return false;}
+ };
+
+ struct fund_unsigned_int: integral_type
+ {
+ fund_unsigned_int (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const {return true;}
+ };
+
+ struct fund_long: integral_type
+ {
+ fund_long (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const {return false;}
+ };
+
+ struct fund_unsigned_long: integral_type
+ {
+ fund_unsigned_long (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const {return true;}
+ };
+
+ struct fund_long_long: integral_type
+ {
+ fund_long_long (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const {return false;}
+ };
+
+ struct fund_unsigned_long_long: integral_type
+ {
+ fund_unsigned_long_long (tree tn)
+ : node (path ("<fundamental>"), 0, 0, tn) {}
+ virtual bool unsigned_ () const {return true;}
+ };
+
+ //
+ // Real.
+ //
+
+ struct fund_float: fund_type
+ {
+ fund_float (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ };
+
+ struct fund_double: fund_type
+ {
+ fund_double (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ };
+
+ struct fund_long_double: fund_type
+ {
+ fund_long_double (tree tn): node (path ("<fundamental>"), 0, 0, tn) {}
+ };
+}
+
+#endif // ODB_SEMANTICS_FUNDAMENTAL_HXX
diff --git a/odb/odb/semantics/namespace.cxx b/odb/odb/semantics/namespace.cxx
new file mode 100644
index 0000000..d9be903
--- /dev/null
+++ b/odb/odb/semantics/namespace.cxx
@@ -0,0 +1,107 @@
+// file : odb/semantics/namespace.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/namespace.hxx>
+
+namespace semantics
+{
+ namespace_::
+ namespace_ (path const& file, size_t line, size_t column, tree tn)
+ : node (file, line, column, tn), original_ (0)
+ {
+ }
+
+ namespace_::
+ namespace_ ()
+ : original_ (0)
+ {
+ }
+
+ names* namespace_::
+ lookup (string const& name,
+ type_id const& ti,
+ unsigned int flags,
+ bool* hidden) const
+ {
+ if (original_ != 0)
+ return original_->lookup (name, ti, flags, hidden);
+
+ // Being hidden in one namespace doesn't mean it is also hidden in
+ // the other. Normally that would be an ambiguous lookup, but we use
+ // relaxed rules.
+ //
+ bool h (false); // Indicates whether this namespace hides the name.
+ bool any_h (false); // Indicates whether any namespace hides the name.
+
+ names* r (scope::lookup (name, ti, flags | exclude_outer, &h));
+ any_h = any_h || h;
+
+ if (r != 0 && h && hidden != 0)
+ *hidden = true;
+
+ for (extensions_iterator i (extensions_begin ());
+ i != extensions_end ();
+ ++i)
+ {
+ h = false;
+ names* er ((*i)->scope::lookup (name, ti, flags | exclude_outer, &h));
+ any_h = any_h || h;
+
+ if (er != 0)
+ {
+ if (r != 0)
+ {
+ // If both are namespaces, then the one is just an extension
+ // of the other.
+ //
+ if (!(r->named ().is_a<namespace_> () &&
+ er->named ().is_a<namespace_> ()))
+ throw ambiguous (*r, *er);
+ }
+ else
+ r = er;
+
+ if (h && hidden != 0)
+ *hidden = true;
+ }
+ }
+
+ if (r != 0)
+ return r;
+
+ if (any_h)
+ {
+ if (hidden != 0)
+ *hidden = true;
+
+ if ((flags & include_hidden) == 0)
+ return 0;
+ }
+
+ // Look in the outer scope unless requested not to or if this is
+ // the global scope.
+ //
+ if ((flags & exclude_outer) == 0 && !global_scope ())
+ return scope ().lookup (name, ti, flags, hidden);
+
+ return 0;
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ type_info ti (typeid (namespace_));
+ ti.add_base (typeid (scope));
+ insert (ti);
+ }
+ } init_;
+ }
+}
diff --git a/odb/odb/semantics/namespace.hxx b/odb/odb/semantics/namespace.hxx
new file mode 100644
index 0000000..b025c2e
--- /dev/null
+++ b/odb/odb/semantics/namespace.hxx
@@ -0,0 +1,71 @@
+// file : odb/semantics/namespace.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_NAMESPACE_HXX
+#define ODB_SEMANTICS_NAMESPACE_HXX
+
+#include <vector>
+
+#include <odb/semantics/elements.hxx>
+
+namespace semantics
+{
+ class namespace_: public scope
+ {
+ typedef std::vector<namespace_*> extensions_type;
+
+ public:
+ bool
+ extension () const
+ {
+ return original_ != 0;
+ }
+
+ namespace_&
+ original ()
+ {
+ return *original_;
+ }
+
+ void
+ original (namespace_& ns)
+ {
+ original_ = &ns;
+ ns.extensions_.push_back (this);
+ }
+
+ public:
+ typedef extensions_type::const_iterator extensions_iterator;
+
+ extensions_iterator
+ extensions_begin () const {return extensions_.begin ();}
+
+ extensions_iterator
+ extensions_end () const {return extensions_.end ();}
+
+ public:
+ virtual names*
+ lookup (string const& name,
+ type_id const&,
+ unsigned int flags = 0,
+ bool* hidden = 0) const;
+
+ using scope::lookup;
+
+ public:
+ namespace_ (path const&, size_t line, size_t column, tree);
+
+ // Resolve conflict between scope::scope and nameable::scope.
+ //
+ using nameable::scope;
+
+ protected:
+ namespace_ ();
+
+ private:
+ namespace_* original_;
+ extensions_type extensions_;
+ };
+}
+
+#endif // ODB_SEMANTICS_NAMESPACE_HXX
diff --git a/odb/odb/semantics/relational.hxx b/odb/odb/semantics/relational.hxx
new file mode 100644
index 0000000..db08a61
--- /dev/null
+++ b/odb/odb/semantics/relational.hxx
@@ -0,0 +1,18 @@
+// file : odb/semantics/relational.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_RELATIONAL_HXX
+#define ODB_SEMANTICS_RELATIONAL_HXX
+
+#include <odb/semantics/relational/changelog.hxx>
+#include <odb/semantics/relational/changeset.hxx>
+#include <odb/semantics/relational/column.hxx>
+#include <odb/semantics/relational/elements.hxx>
+#include <odb/semantics/relational/foreign-key.hxx>
+#include <odb/semantics/relational/index.hxx>
+#include <odb/semantics/relational/key.hxx>
+#include <odb/semantics/relational/model.hxx>
+#include <odb/semantics/relational/primary-key.hxx>
+#include <odb/semantics/relational/table.hxx>
+
+#endif // ODB_SEMANTICS_RELATIONAL_HXX
diff --git a/odb/odb/semantics/relational/changelog.cxx b/odb/odb/semantics/relational/changelog.cxx
new file mode 100644
index 0000000..353497f
--- /dev/null
+++ b/odb/odb/semantics/relational/changelog.cxx
@@ -0,0 +1,187 @@
+// file : odb/semantics/relational/changelog.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <vector>
+#include <sstream>
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/changelog.hxx>
+#include <odb/semantics/relational/model.hxx>
+#include <odb/semantics/relational/changeset.hxx>
+
+using namespace std;
+
+namespace semantics
+{
+ namespace relational
+ {
+ changelog::
+ changelog (xml::parser& p)
+ : contains_model_ (0)
+ {
+ using namespace xml;
+
+ p.next_expect (parser::start_element, xmlns, "changelog");
+ p.content (content::complex);
+
+ if (p.attribute<unsigned int> ("version") != 1)
+ throw parsing (p, "unsupported changelog format version");
+
+ database_ = p.attribute ("database");
+ schema_name_ = p.attribute ("schema-name", "");
+
+ // Because things are stored in the reverse order, first save the
+ // changesets as XML chunks and then re-parse them in the reverse
+ // order. We have to do it this way so that we can do lookups along
+ // the alters edges.
+ //
+ typedef vector<string> changesets;
+ changesets cs;
+
+ for (parser::event_type e (p.peek ());
+ e == parser::start_element;
+ e = p.peek ())
+ {
+ if (p.qname () != xml::qname (xmlns, "changeset"))
+ break; // Not our elements.
+
+ ostringstream os;
+ os.exceptions (ios_base::badbit | ios_base::failbit);
+ serializer s (os, "changeset", 0); // No pretty-printing.
+ size_t depth (0);
+
+ do
+ {
+ switch (p.next ())
+ {
+ case parser::start_element:
+ {
+ s.start_element (p.qname ());
+
+ if (depth == 0)
+ s.namespace_decl (xmlns, "");
+
+ typedef parser::attribute_map_type attr_map;
+ attr_map const& am (p.attribute_map ());
+
+ for (attr_map::const_iterator i (am.begin ());
+ i != am.end (); ++i)
+ s.attribute (i->first, i->second.value);
+
+ depth++;
+ break;
+ }
+ case parser::end_element:
+ {
+ depth--;
+ s.end_element ();
+ break;
+ }
+ case parser::characters:
+ {
+ s.characters (p.value ());
+ break;
+ }
+ default:
+ {
+ depth = 0;
+ break;
+ }
+ }
+ } while (depth != 0);
+
+ cs.push_back (os.str ());
+ }
+
+ // Get the model.
+ //
+ p.next_expect (parser::start_element, xmlns, "model");
+ model_type& m (new_node<model_type> (p, *this));
+ new_edge<contains_model_type> (*this, m);
+ p.next_expect (parser::end_element);
+
+ // Re-parse the changesets in reverse order.
+ //
+ qscope* base (&m);
+ for (changesets::reverse_iterator i (cs.rbegin ()); i != cs.rend (); ++i)
+ {
+ istringstream is (*i);
+ is.exceptions (ios_base::badbit | ios_base::failbit);
+ parser ip (is, p.input_name ());
+
+ ip.next_expect (parser::start_element, xmlns, "changeset");
+
+ changeset& c (new_node<changeset> (ip, *base, *this));
+ new_edge<contains_changeset> (*this, c);
+ base = &c;
+
+ ip.next_expect (parser::end_element);
+ }
+
+ p.next_expect (parser::end_element);
+ }
+
+ void changelog::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "changelog");
+ s.namespace_decl (xmlns, "");
+ s.attribute ("database", database_);
+ if (!schema_name_.empty ())
+ s.attribute ("schema-name", schema_name_);
+ s.attribute ("version", 1); // Format version.
+
+ // For better readability serialize things in reverse order so that
+ // the most recent changeset appears first.
+ //
+ for (contains_changeset_list::const_reverse_iterator i (
+ contains_changeset_.rbegin ());
+ i != contains_changeset_.rend (); ++i)
+ {
+ (*i)->changeset ().serialize (s);
+ s.characters ("\n");
+ }
+
+ model ().serialize (s);
+ s.end_element ();
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // contains_model
+ //
+ {
+ type_info ti (typeid (contains_model));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // contains_changeset
+ //
+ {
+ type_info ti (typeid (contains_changeset));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // changelog
+ //
+ {
+ type_info ti (typeid (changelog));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/odb/semantics/relational/changelog.hxx b/odb/odb/semantics/relational/changelog.hxx
new file mode 100644
index 0000000..2398cf6
--- /dev/null
+++ b/odb/odb/semantics/relational/changelog.hxx
@@ -0,0 +1,164 @@
+// file : odb/semantics/relational/changelog.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_RELATIONAL_CHANGELOG_HXX
+#define ODB_SEMANTICS_RELATIONAL_CHANGELOG_HXX
+
+#include <odb/semantics/relational/elements.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ class model;
+ class changeset;
+ class changelog;
+
+ class contains_model: public edge
+ {
+ public:
+ typedef relational::changelog changelog_type;
+ typedef relational::model model_type;
+
+ changelog_type&
+ changelog () const {return *changelog_;}
+
+ model_type&
+ model () const {return *model_;}
+
+ public:
+ void
+ set_left_node (changelog_type& n) {changelog_ = &n;}
+
+ void
+ set_right_node (model_type& n) {model_ = &n;}
+
+ protected:
+ changelog_type* changelog_;
+ model_type* model_;
+ };
+
+ class contains_changeset: public edge
+ {
+ public:
+ typedef relational::changelog changelog_type;
+ typedef relational::changeset changeset_type;
+
+ changelog_type&
+ changelog () const {return *changelog_;}
+
+ changeset_type&
+ changeset () const {return *changeset_;}
+
+ public:
+ void
+ set_left_node (changelog_type& n) {changelog_ = &n;}
+
+ void
+ set_right_node (changeset_type& n) {changeset_ = &n;}
+
+ protected:
+ changelog_type* changelog_;
+ changeset_type* changeset_;
+ };
+
+ class changelog: public graph, public node
+ {
+ typedef std::vector<contains_changeset*> contains_changeset_list;
+
+ public:
+ typedef relational::model model_type;
+ typedef relational::contains_model contains_model_type;
+
+ model_type&
+ model () const {return contains_model_->model ();}
+
+ contains_model_type&
+ contains_model () const {return *contains_model_;}
+
+ public:
+ typedef
+ pointer_iterator<contains_changeset_list::const_iterator>
+ contains_changeset_iterator;
+
+ contains_changeset const&
+ contains_changeset_back () const
+ {
+ return *contains_changeset_.back ();
+ }
+
+ contains_changeset const&
+ contains_changeset_at (contains_changeset_list::size_type i) const
+ {
+ return *contains_changeset_[i];
+ }
+
+ contains_changeset_iterator
+ contains_changeset_begin () const
+ {
+ return contains_changeset_.begin ();
+ }
+
+ contains_changeset_iterator
+ contains_changeset_end () const
+ {
+ return contains_changeset_.end ();
+ }
+
+ contains_changeset_list::size_type
+ contains_changeset_size () const
+ {
+ return contains_changeset_.size ();
+ }
+
+ bool
+ contains_changeset_empty () const
+ {
+ return contains_changeset_.empty ();
+ }
+
+ public:
+ string const&
+ database () const {return database_;}
+
+ string const&
+ schema_name () const {return schema_name_;}
+
+ public:
+ changelog (string const& db, string const& sn)
+ : database_ (db), schema_name_ (sn), contains_model_ (0) {}
+ changelog (xml::parser&);
+
+ void
+ add_edge_left (contains_model_type& e)
+ {
+ assert (contains_model_ == 0);
+ contains_model_ = &e;
+ }
+
+ void
+ add_edge_left (contains_changeset& e)
+ {
+ contains_changeset_.push_back (&e);
+ }
+
+ virtual string
+ kind () const {return "changelog";}
+
+ virtual void
+ serialize (xml::serializer&) const;
+
+ private:
+ changelog (changelog const&);
+ changelog& operator= (changelog const&);
+
+ protected:
+ string database_;
+ string schema_name_;
+ contains_model_type* contains_model_;
+ contains_changeset_list contains_changeset_;
+ };
+ }
+}
+
+#endif // ODB_SEMANTICS_RELATIONAL_CHANGELOG_HXX
diff --git a/odb/odb/semantics/relational/changeset.cxx b/odb/odb/semantics/relational/changeset.cxx
new file mode 100644
index 0000000..b044a0c
--- /dev/null
+++ b/odb/odb/semantics/relational/changeset.cxx
@@ -0,0 +1,56 @@
+// file : odb/semantics/relational/changeset.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/changeset.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ changeset::
+ changeset (changeset const& c, qscope& b, graph& g)
+ : qscope (c, &b, g),
+ version_ (c.version_),
+ alters_model_ (0)
+ {
+ }
+
+ changeset::
+ changeset (xml::parser& p, qscope& b, graph& g)
+ : qscope (p, &b, g),
+ version_ (p.attribute<version_type> ("version")),
+ alters_model_ (0)
+ {
+ }
+
+ void changeset::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "changeset");
+ s.attribute ("version", version_);
+ qscope::serialize_content (s);
+ s.end_element ();
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ {
+ type_info ti (typeid (changeset));
+ ti.add_base (typeid (qscope));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/odb/semantics/relational/changeset.hxx b/odb/odb/semantics/relational/changeset.hxx
new file mode 100644
index 0000000..efe2c61
--- /dev/null
+++ b/odb/odb/semantics/relational/changeset.hxx
@@ -0,0 +1,93 @@
+// file : odb/semantics/relational/changeset.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_RELATIONAL_CHANGESET_HXX
+#define ODB_SEMANTICS_RELATIONAL_CHANGESET_HXX
+
+#include <odb/semantics/relational/elements.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ class model;
+ class changeset;
+
+ class alters_model: public edge
+ {
+ public:
+ typedef relational::model model_type;
+ typedef relational::changeset changeset_type;
+
+ model_type&
+ model () const {return *model_;}
+
+ changeset_type&
+ changeset () const {return *changeset_;}
+
+ public:
+ alters_model () : model_ (0), changeset_ (0) {}
+
+ void
+ set_left_node (changeset_type& c)
+ {
+ assert (changeset_ == 0);
+ changeset_ = &c;
+ }
+
+ void
+ set_right_node (model_type& m)
+ {
+ assert (model_ == 0);
+ model_ = &m;
+ }
+
+ protected:
+ model_type* model_;
+ changeset_type* changeset_;
+ };
+
+ class changeset: public qscope
+ {
+ public:
+ typedef relational::version version_type;
+
+ version_type
+ version () const {return version_;}
+
+ // Returns model to which this changeset applies (as opposed to the
+ // ultimate base model). Note that it may not be set.
+ //
+ model&
+ base_model () const {return alters_model_->model ();}
+
+ public:
+ changeset (version_type v): version_ (v), alters_model_ (0) {}
+ changeset (changeset const&, qscope& base, graph&);
+ changeset (xml::parser&, qscope& base, graph&);
+
+ virtual string
+ kind () const {return "changeset";}
+
+ virtual void
+ serialize (xml::serializer&) const;
+
+ public:
+ virtual void
+ add_edge_left (alters_model& am)
+ {
+ assert (alters_model_ == 0);
+ alters_model_ = &am;
+ }
+
+ using qscope::add_edge_left;
+ using qscope::add_edge_right;
+
+ private:
+ version_type version_;
+ alters_model* alters_model_;
+ };
+ }
+}
+
+#endif // ODB_SEMANTICS_RELATIONAL_CHANGESET_HXX
diff --git a/odb/odb/semantics/relational/column.cxx b/odb/odb/semantics/relational/column.cxx
new file mode 100644
index 0000000..9d4d6e5
--- /dev/null
+++ b/odb/odb/semantics/relational/column.cxx
@@ -0,0 +1,201 @@
+// file : odb/semantics/relational/column.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/column.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ // column
+ //
+ column::
+ column (column const& c, uscope&, graph& g)
+ : unameable (c, g),
+ type_ (c.type_),
+ null_ (c.null_),
+ default__ (c.default__),
+ options_ (c.options_)
+ {
+ }
+
+ column::
+ column (xml::parser& p, uscope&, graph& g)
+ : unameable (p, g),
+ type_ (p.attribute ("type", string ())),
+ null_ (p.attribute<bool> ("null")),
+ default__ (p.attribute ("default", string ())),
+ options_ (p.attribute ("options", string ()))
+ {
+ p.content (xml::content::empty);
+ }
+
+ column& column::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<column> (*this, s, g);
+ }
+
+ void column::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "column");
+ serialize_attributes (s);
+ s.end_element ();
+ }
+
+ void column::
+ serialize_attributes (xml::serializer& s) const
+ {
+ unameable::serialize_attributes (s);
+
+ s.attribute ("type", type ());
+ s.attribute ("null", null ()); // Output even if false.
+
+ if (!default_ ().empty ())
+ s.attribute ("default", default_ ());
+
+ if (!options ().empty ())
+ s.attribute ("options", options ());
+ }
+
+ // add_column
+ //
+ add_column& add_column::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<add_column> (*this, s, g);
+ }
+
+ void add_column::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "add-column");
+ column::serialize_attributes (s);
+ s.end_element ();
+ }
+
+ // drop_column
+ //
+ drop_column::
+ drop_column (xml::parser& p, uscope&, graph& g)
+ : unameable (p, g)
+ {
+ p.content (xml::content::empty);
+ }
+
+ drop_column& drop_column::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<drop_column> (*this, s, g);
+ }
+
+ void drop_column::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "drop-column");
+ unameable::serialize_attributes (s);
+ s.end_element ();
+ }
+
+ // alter_column
+ //
+ alter_column::
+ alter_column (alter_column const& ac, uscope& s, graph& g)
+ : column (ac, s, g),
+ alters_ (0),
+ null_altered_ (ac.null_altered_)
+ {
+ column* b (s.lookup<column, drop_column> (ac.name ()));
+ assert (b != 0);
+ g.new_edge<alters> (*this, *b);
+ }
+
+ alter_column::
+ alter_column (xml::parser& p, uscope& s, graph& g)
+ : column (p, s, g),
+ alters_ (0),
+ null_altered_ (p.attribute_present ("null"))
+ {
+ name_type n (p.attribute<name_type> ("name"));
+ column* b (s.lookup<column, drop_column> (n));
+ assert (b != 0);
+ g.new_edge<alters> (*this, *b);
+ }
+
+ alter_column& alter_column::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<alter_column> (*this, s, g);
+ }
+
+ void alter_column::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "alter-column");
+
+ // Here we override the standard column logic.
+ //
+ unameable::serialize_attributes (s);
+
+ if (null_altered_)
+ s.attribute ("null", null_);
+
+ s.end_element ();
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ unameable::parser_map& m (unameable::parser_map_);
+
+ m["column"] = &unameable::parser_impl<column>;
+ m["add-column"] = &unameable::parser_impl<add_column>;
+ m["drop-column"] = &unameable::parser_impl<drop_column>;
+ m["alter-column"] = &unameable::parser_impl<alter_column>;
+
+ using compiler::type_info;
+
+ // column
+ //
+ {
+ type_info ti (typeid (column));
+ ti.add_base (typeid (unameable));
+ insert (ti);
+ }
+
+ // add_column
+ //
+ {
+ type_info ti (typeid (add_column));
+ ti.add_base (typeid (column));
+ insert (ti);
+ }
+
+ // drop_column
+ //
+ {
+ type_info ti (typeid (drop_column));
+ ti.add_base (typeid (unameable));
+ insert (ti);
+ }
+
+ // alter_column
+ //
+ {
+ type_info ti (typeid (alter_column));
+ ti.add_base (typeid (column));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/odb/semantics/relational/column.hxx b/odb/odb/semantics/relational/column.hxx
new file mode 100644
index 0000000..b7a2c31
--- /dev/null
+++ b/odb/odb/semantics/relational/column.hxx
@@ -0,0 +1,205 @@
+// file : odb/semantics/relational/column.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_RELATIONAL_COLUMN_HXX
+#define ODB_SEMANTICS_RELATIONAL_COLUMN_HXX
+
+#include <odb/semantics/relational/elements.hxx>
+#include <odb/semantics/relational/table.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ class contains;
+
+ class column: public unameable
+ {
+ typedef std::vector<contains*> contained_list;
+
+ public:
+ virtual string const&
+ type () const {return type_;}
+
+ virtual bool
+ null () const {return null_;}
+
+ virtual void
+ null (bool n) {null_ = n;}
+
+ virtual string const&
+ default_ () const {return default__;}
+
+ virtual void
+ default_ (string const& d) {default__ = d;}
+
+ virtual string const&
+ options () const {return options_;}
+
+ virtual void
+ options (string const& o) {options_ = o;}
+
+ public:
+ typedef relational::table table_type;
+
+ table_type&
+ table () const {return dynamic_cast<table_type&> (scope ());}
+
+ // Key containment.
+ //
+ public:
+ typedef
+ pointer_iterator<contained_list::const_iterator>
+ contained_iterator;
+
+ contained_iterator
+ contained_begin () const {return contained_.begin ();}
+
+ contained_iterator
+ contained_end () const {return contained_.end ();}
+
+ public:
+ column (string const& id, string const& type, bool null)
+ : unameable (id), type_ (type), null_ (null)
+ {
+ }
+
+ column (column const&, uscope&, graph&);
+ column (xml::parser&, uscope&, graph&);
+
+ virtual column&
+ clone (uscope&, graph&) const;
+
+ void
+ add_edge_right (contains& e)
+ {
+ contained_.push_back (&e);
+ }
+
+ using unameable::add_edge_right;
+
+ virtual string
+ kind () const
+ {
+ return "column";
+ }
+
+ virtual void
+ serialize (xml::serializer&) const;
+
+ protected:
+ void
+ serialize_attributes (xml::serializer&) const;
+
+ protected:
+ string type_;
+ bool null_;
+ string default__;
+ string options_;
+
+ private:
+ contained_list contained_;
+ };
+
+ class add_column: public column
+ {
+ public:
+ add_column (string const& id, string const& type, bool null)
+ : column (id, type, null) {}
+ add_column (column const& c, uscope& s, graph& g): column (c, s, g) {}
+ add_column (xml::parser& p, uscope& s, graph& g): column (p, s, g) {}
+
+ virtual add_column&
+ clone (uscope&, graph&) const;
+
+ virtual string
+ kind () const {return "add column";}
+
+ virtual void
+ serialize (xml::serializer&) const;
+ };
+
+ class drop_column: public unameable
+ {
+ public:
+ drop_column (string const& id): unameable (id) {}
+ drop_column (drop_column const& c, uscope&, graph& g)
+ : unameable (c, g) {}
+ drop_column (xml::parser&, uscope&, graph&);
+
+ public:
+ typedef relational::table table_type;
+
+ table_type&
+ table () const {return dynamic_cast<table_type&> (scope ());}
+
+ public:
+ virtual drop_column&
+ clone (uscope&, graph&) const;
+
+ virtual string
+ kind () const {return "drop column";}
+
+ virtual void
+ serialize (xml::serializer&) const;
+ };
+
+ class alter_column: public column
+ {
+ public:
+ virtual string const&
+ type () const {return base ().type ();}
+
+ bool
+ null_altered () const {return null_altered_;}
+
+ virtual bool
+ null () const {return null_altered_ ? null_ : base ().null ();}
+
+ virtual void
+ null (bool n) {null_ = n; null_altered_ = true;}
+
+ virtual string const&
+ default_ () const {return base ().default_ ();}
+
+ virtual string const&
+ options () const {return base ().options ();}
+
+ public:
+ column&
+ base () const {return dynamic_cast<column&> (alters_->base ());}
+
+ public:
+ alter_column (string const& id)
+ : column (id, "", false), alters_ (0), null_altered_ (false) {}
+ alter_column (alter_column const&, uscope&, graph&);
+ alter_column (xml::parser&, uscope&, graph&);
+
+ virtual alter_column&
+ clone (uscope&, graph&) const;
+
+ virtual string
+ kind () const
+ {
+ return "alter column";
+ }
+
+ virtual void
+ serialize (xml::serializer&) const;
+
+ virtual void
+ add_edge_left (alters& a)
+ {
+ assert (alters_ == 0);
+ alters_ = &a;
+ }
+
+ private:
+ alters* alters_;
+
+ bool null_altered_;
+ };
+ }
+}
+
+#endif // ODB_SEMANTICS_RELATIONAL_COLUMN_HXX
diff --git a/odb/odb/semantics/relational/deferrable.cxx b/odb/odb/semantics/relational/deferrable.cxx
new file mode 100644
index 0000000..076ff69
--- /dev/null
+++ b/odb/odb/semantics/relational/deferrable.cxx
@@ -0,0 +1,55 @@
+// file : odb/semantics/relational/deferrable.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <ostream>
+#include <istream>
+
+#include <odb/semantics/relational/deferrable.hxx>
+
+using namespace std;
+
+namespace semantics
+{
+ namespace relational
+ {
+ static const char* deferrable_[] =
+ {
+ "NOT DEFERRABLE",
+ "IMMEDIATE",
+ "DEFERRED"
+ };
+
+ string deferrable::
+ string () const
+ {
+ return deferrable_[v_];
+ }
+
+ ostream&
+ operator<< (ostream& os, deferrable const& v)
+ {
+ return os << v.string ();
+ }
+
+ istream&
+ operator>> (istream& is, deferrable& v)
+ {
+ string s;
+ is >> s;
+
+ if (!is.fail ())
+ {
+ if (s == "not_deferrable" || s == "NOT DEFERRABLE")
+ v = deferrable::not_deferrable;
+ else if (s == "immediate" || s == "IMMEDIATE")
+ v = deferrable::immediate;
+ else if (s == "deferred" || s == "DEFERRED")
+ v = deferrable::deferred;
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+ }
+ }
+}
diff --git a/odb/odb/semantics/relational/deferrable.hxx b/odb/odb/semantics/relational/deferrable.hxx
new file mode 100644
index 0000000..b2f888d
--- /dev/null
+++ b/odb/odb/semantics/relational/deferrable.hxx
@@ -0,0 +1,41 @@
+// file : odb/semantics/relational/deferrable.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_RELATIONAL_DEFERRABLE_HXX
+#define ODB_SEMANTICS_RELATIONAL_DEFERRABLE_HXX
+
+#include <string>
+#include <iosfwd>
+
+namespace semantics
+{
+ namespace relational
+ {
+ struct deferrable
+ {
+ enum value
+ {
+ not_deferrable,
+ immediate,
+ deferred
+ };
+
+ deferrable (value v = value (0)) : v_ (v) {}
+ operator value () const {return v_;}
+
+ std::string
+ string () const;
+
+ private:
+ value v_;
+ };
+
+ std::ostream&
+ operator<< (std::ostream&, deferrable const&);
+
+ std::istream&
+ operator>> (std::istream&, deferrable&);
+ }
+}
+
+#endif // ODB_SEMANTICS_RELATIONAL_DEFERRABLE_HXX
diff --git a/odb/odb/semantics/relational/elements.cxx b/odb/odb/semantics/relational/elements.cxx
new file mode 100644
index 0000000..de1878a
--- /dev/null
+++ b/odb/odb/semantics/relational/elements.cxx
@@ -0,0 +1,179 @@
+// file : odb/semantics/relational/elements.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/elements.hxx>
+#include <odb/semantics/relational/column.hxx>
+#include <odb/semantics/relational/primary-key.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ string const xmlns = "http://www.codesynthesis.com/xmlns/odb/changelog";
+
+ // duplicate_name
+ //
+ template <>
+ duplicate_name::
+ duplicate_name (uscope& s, unameable& o, unameable& d)
+ : scope (s), orig (o), dup (d), name (o.name ())
+ {
+ }
+
+ template <>
+ duplicate_name::
+ duplicate_name (qscope& s, qnameable& o, qnameable& d)
+ : scope (s), orig (o), dup (d), name (o.name ().string ())
+ {
+ }
+
+ // scope<uname>
+ //
+ template <>
+ void scope<uname>::
+ add_edge_left (names_type& e)
+ {
+ nameable_type& n (e.nameable ());
+ name_type const& name (e.name ());
+
+ typename names_map::iterator i (names_map_.find (name));
+
+ if (i == names_map_.end ())
+ {
+ typename names_list::iterator i;
+
+ // We want the order to be add/alter columns first, then the
+ // primary key, then other keys, and finnally drop columns.
+ //
+ if (n.is_a<column> () ||
+ n.is_a<add_column> () ||
+ n.is_a<alter_column> ())
+ {
+ i = names_.insert (first_key_, &e);
+ }
+ else if (!n.is_a<drop_column> ())
+ {
+ if (n.is_a<primary_key> ())
+ first_key_ = i = names_.insert (
+ first_key_ != names_.end () ? first_key_ : first_drop_column_,
+ &e);
+ else
+ {
+ i = names_.insert (first_drop_column_, &e);
+
+ if (first_key_ == names_.end ())
+ first_key_ = i;
+ }
+ }
+ else
+ {
+ i = names_.insert (names_.end (), &e);
+
+ if (first_drop_column_ == names_.end ())
+ first_drop_column_ = i;
+ }
+
+ names_map_[name] = i;
+ iterator_map_[&e] = i;
+ }
+ else
+ throw duplicate_name (*this, (*i->second)->nameable (), n);
+ }
+
+ template <>
+ void scope<uname>::
+ remove_edge_left (names_type& e)
+ {
+ typename names_iterator_map::iterator i (iterator_map_.find (&e));
+ assert (i != iterator_map_.end ());
+
+ // If we are removing the first key, then move to the next key (or
+ // the end which means there are no keys).
+ //
+ if (first_key_ == i->second)
+ first_key_++;
+
+ // The same for the first drop column.
+ //
+ if (first_drop_column_ == i->second)
+ first_drop_column_++;
+
+ names_.erase (i->second);
+ names_map_.erase (e.name ());
+ iterator_map_.erase (i);
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // node
+ //
+ insert (type_info (typeid (node)));
+
+ // edge
+ //
+ insert (type_info (typeid (edge)));
+
+ // alters
+ //
+ {
+ type_info ti (typeid (alters));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // names
+ //
+ {
+ type_info ti (typeid (unames));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ {
+ type_info ti (typeid (qnames));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // nameable
+ //
+ {
+ type_info ti (typeid (unameable));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+
+ {
+ type_info ti (typeid (qnameable));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+
+ // scope
+ //
+ {
+ type_info ti (typeid (uscope));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+
+ {
+ type_info ti (typeid (qscope));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/odb/semantics/relational/elements.hxx b/odb/odb/semantics/relational/elements.hxx
new file mode 100644
index 0000000..4036942
--- /dev/null
+++ b/odb/odb/semantics/relational/elements.hxx
@@ -0,0 +1,462 @@
+// file : odb/semantics/relational/elements.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_RELATIONAL_ELEMENTS_HXX
+#define ODB_SEMANTICS_RELATIONAL_ELEMENTS_HXX
+
+#include <map>
+#include <list>
+#include <vector>
+#include <string>
+#include <cassert>
+
+#include <libcutl/container/graph.hxx>
+#include <libcutl/container/pointer-iterator.hxx>
+#include <libcutl/compiler/context.hxx>
+
+#include <libstudxml/parser.hxx>
+#include <libstudxml/serializer.hxx>
+
+#include <odb/semantics/relational/name.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ using namespace cutl;
+
+ using std::string;
+
+ using container::pointer_iterator;
+ using compiler::context;
+
+ typedef unsigned long long version;
+
+ //
+ //
+ extern string const xmlns;
+
+ //
+ //
+ class node;
+ class edge;
+
+ typedef container::graph<node, edge> graph;
+
+ //
+ //
+ class edge: public context
+ {
+ public:
+ template <typename X>
+ bool
+ is_a () const
+ {
+ return dynamic_cast<X const*> (this) != 0;
+ }
+
+ public:
+ virtual
+ ~edge () {}
+ };
+
+ //
+ //
+ class node: public context
+ {
+ // Return name of the node.
+ //
+ public:
+ virtual string
+ kind () const = 0;
+
+ public:
+ template <typename X>
+ bool
+ is_a () const
+ {
+ return dynamic_cast<X const*> (this) != 0;
+ }
+
+ public:
+ virtual
+ ~node () {}
+
+ // XML serialization.
+ //
+ virtual void
+ serialize (xml::serializer&) const = 0;
+
+ // Sink functions that allow extensions in the form of one-way
+ // edges.
+ //
+ void
+ add_edge_right (edge&) {}
+
+ void
+ remove_edge_right (edge&) {}
+ };
+
+ //
+ //
+ class alters: public edge
+ {
+ public:
+ node&
+ base () const {return *base_;}
+
+ node&
+ modifier () const {return *modifier_;}
+
+ public:
+ alters () : base_ (0), modifier_ (0) {}
+
+ void
+ set_left_node (node& m)
+ {
+ assert (modifier_ == 0);
+ modifier_ = &m;
+ }
+
+ void
+ set_right_node (node& b)
+ {
+ assert (base_ == 0);
+ base_ = &b;
+ }
+
+ void
+ clear_left_node (node& m)
+ {
+ assert (modifier_ == &m);
+ modifier_ = 0;
+ }
+
+ void
+ clear_right_node (node& b)
+ {
+ assert (base_ == &b);
+ base_ = 0;
+ }
+
+ protected:
+ node* base_;
+ node* modifier_;
+ };
+
+ //
+ //
+ template <typename N>
+ class scope;
+
+ template <typename N>
+ class nameable;
+
+ //
+ //
+ template <typename N>
+ class names: public edge
+ {
+ public:
+ typedef N name_type;
+ typedef relational::scope<N> scope_type;
+ typedef relational::nameable<N> nameable_type;
+
+ name_type const&
+ name () const
+ {
+ return name_;
+ }
+
+ scope_type&
+ scope () const
+ {
+ return *scope_;
+ }
+
+ nameable_type&
+ nameable () const
+ {
+ return *nameable_;
+ }
+
+ public:
+ names (name_type const& name): name_ (name) {}
+
+ void
+ set_left_node (scope_type& n)
+ {
+ scope_ = &n;
+ }
+
+ void
+ set_right_node (nameable_type& n)
+ {
+ nameable_ = &n;
+ }
+
+ void
+ clear_left_node (scope_type& n)
+ {
+ assert (scope_ == &n);
+ scope_ = 0;
+ }
+
+ void
+ clear_right_node (nameable_type& n)
+ {
+ assert (nameable_ == &n);
+ nameable_ = 0;
+ }
+
+ protected:
+ name_type name_;
+ scope_type* scope_;
+ nameable_type* nameable_;
+ };
+
+ typedef names<uname> unames;
+ typedef names<qname> qnames;
+
+ //
+ //
+ template <typename N>
+ class nameable: public virtual node
+ {
+ public:
+ typedef N name_type;
+ typedef relational::names<N> names_type;
+ typedef relational::scope<N> scope_type;
+
+ name_type const&
+ name () const {return named_->name ();}
+
+ scope_type&
+ scope () const {return named ().scope ();}
+
+ names_type&
+ named () const {return *named_;}
+
+ string const&
+ id () const {return id_;}
+
+ public:
+ // Id identifies the C++ node (e.g., a class or a data member) that
+ // this model node corresponds to. The ids are not necessarily unique
+ // (e.g., there can be a foreign key and an index with the same id that
+ // correspond to a container member). However, in any given scope, the
+ // {id,typeid} must be unique. This becomes important when we try to
+ // find correspondance between nodes during model diff'ing.
+ //
+ nameable (string const& id): id_ (id), named_ (0) {}
+
+ virtual nameable&
+ clone (scope_type&, graph&) const = 0;
+
+ // Virtual because we call it via nameable interface (e.g., in copy).
+ //
+ virtual void
+ add_edge_right (names_type& e)
+ {
+ assert (named_ == 0);
+ named_ = &e;
+ }
+
+ virtual void
+ remove_edge_right (names_type& e)
+ {
+ assert (named_ == &e);
+ named_ = 0;
+ }
+
+ using node::add_edge_right;
+ using node::remove_edge_right;
+
+ protected:
+ nameable (nameable const&, graph& g);
+ nameable (xml::parser&, graph& g);
+
+ void
+ serialize_attributes (xml::serializer&) const;
+
+ public:
+ typedef void (*parser_func) (xml::parser&, scope_type&, graph&);
+ typedef std::map<std::string, parser_func> parser_map;
+ static parser_map parser_map_;
+
+ template <typename T>
+ static void
+ parser_impl (xml::parser&, scope_type&, graph&);
+
+ private:
+ string id_;
+ names_type* named_;
+ };
+
+ typedef nameable<uname> unameable;
+ typedef nameable<qname> qnameable;
+
+
+ //
+ //
+ struct duplicate_name
+ {
+ template <typename N>
+ duplicate_name (relational::scope<N>&,
+ relational::nameable<N>& orig,
+ relational::nameable<N>& dup);
+
+ node& scope;
+ node& orig;
+ node& dup;
+
+ string name;
+ };
+
+ template <typename N>
+ class scope: public virtual node
+ {
+ protected:
+ typedef N name_type;
+ typedef relational::names<N> names_type;
+ typedef relational::nameable<N> nameable_type;
+
+ typedef std::list<names_type*> names_list;
+ typedef std::map<name_type, typename names_list::iterator> names_map;
+ typedef
+ std::map<names_type const*, typename names_list::iterator>
+ names_iterator_map;
+
+ public:
+ typedef pointer_iterator<typename names_list::iterator> names_iterator;
+ typedef
+ pointer_iterator<typename names_list::const_iterator>
+ names_const_iterator;
+
+ public:
+ // Iteration.
+ //
+ names_iterator
+ names_begin ()
+ {
+ return names_.begin ();
+ }
+
+ names_iterator
+ names_end ()
+ {
+ return names_.end ();
+ }
+
+ names_const_iterator
+ names_begin () const
+ {
+ return names_.begin ();
+ }
+
+ names_const_iterator
+ names_end () const
+ {
+ return names_.end ();
+ }
+
+ bool
+ names_empty () const
+ {
+ return names_.empty ();
+ }
+
+ // Find (this scope only).
+ //
+ template <typename T>
+ T*
+ find (name_type const&);
+
+ names_iterator
+ find (name_type const&);
+
+ names_const_iterator
+ find (name_type const&) const;
+
+ names_iterator
+ find (names_type const&);
+
+ names_const_iterator
+ find (names_type const&) const;
+
+ // Lookup in this and all altered scopes until we find what we are
+ // looking for or hit a stop node of type S (e.g., drop_*).
+ //
+ template <typename T, typename S>
+ T*
+ lookup (name_type const&);
+
+ public:
+ scope*
+ base () const
+ {
+ return alters_ != 0 ? &dynamic_cast<scope&> (alters_->base ()) : 0;
+ }
+
+ public:
+ scope ()
+ : first_key_ (names_.end ()),
+ first_drop_column_ (names_.end ()),
+ alters_ (0) {}
+
+ // Virtual because we call it via scope interface (e.g., in copy).
+ //
+ virtual void
+ add_edge_left (alters& a)
+ {
+ assert (alters_ == 0);
+ alters_ = &a;
+ }
+
+ virtual void
+ remove_edge_left (alters& a)
+ {
+ assert (alters_ == &a);
+ alters_ = 0;
+ }
+
+ virtual void
+ add_edge_left (names_type&);
+
+ virtual void
+ remove_edge_left (names_type&);
+
+ protected:
+ scope (scope const&, scope* base, graph&);
+ scope (xml::parser&, scope* base, graph&);
+
+ void
+ serialize_content (xml::serializer&) const;
+
+ protected:
+ names_list names_;
+ names_map names_map_;
+ names_iterator_map iterator_map_;
+
+ typename names_list::iterator first_key_;
+ typename names_list::iterator first_drop_column_;
+
+ alters* alters_;
+ };
+
+ template <>
+ void scope<uname>::
+ add_edge_left (names_type&);
+
+ template <>
+ void scope<uname>::
+ remove_edge_left (names_type&);
+
+ typedef scope<uname> uscope;
+ typedef scope<qname> qscope;
+ }
+}
+
+#include <odb/semantics/relational/elements.txx>
+
+#endif // ODB_SEMANTICS_RELATIONAL_ELEMENTS_HXX
diff --git a/odb/odb/semantics/relational/elements.txx b/odb/odb/semantics/relational/elements.txx
new file mode 100644
index 0000000..2362d48
--- /dev/null
+++ b/odb/odb/semantics/relational/elements.txx
@@ -0,0 +1,215 @@
+// file : odb/semantics/relational/elements.txx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+namespace semantics
+{
+ namespace relational
+ {
+ // nameable
+ //
+ template <typename N>
+ typename nameable<N>::parser_map nameable<N>::parser_map_;
+
+ template <typename N>
+ template <typename T>
+ void nameable<N>::
+ parser_impl (xml::parser& p, scope_type& s, graph& g)
+ {
+ name_type n (p.attribute ("name", name_type ()));
+ T& x (g.new_node<T> (p, s, g));
+ g.new_edge<names_type> (s, x, n);
+ }
+
+ template <typename N>
+ nameable<N>::
+ nameable (nameable const& n, graph&)
+ : id_ (n.id_), named_ (0)
+ {
+ }
+
+ template <typename N>
+ nameable<N>::
+ nameable (xml::parser&, graph&)
+ // : id_ (p.attribute<string> ("id"))
+ : named_ (0)
+ {
+ // The name attribute is handled in parser_impl().
+ }
+
+ template <typename N>
+ void nameable<N>::
+ serialize_attributes (xml::serializer& s) const
+ {
+ // Omit empty names (e.g., a primary key).
+ //
+ name_type const& n (name ());
+ if (!n.empty ())
+ s.attribute ("name", n);
+
+ //s.attribute ("id", id_);
+ }
+
+ // scope
+ //
+
+ template <typename N>
+ template <typename T, typename S>
+ T* scope<N>::
+ lookup (name_type const& name)
+ {
+ if (T* r = find<T> (name))
+ return r;
+
+ if (scope* b = base ())
+ {
+ if (find<S> (name) == 0)
+ return b->lookup<T, S> (name);
+ }
+
+ return 0;
+ }
+
+ template <typename N>
+ template <typename T>
+ T* scope<N>::
+ find (name_type const& name)
+ {
+ typename names_map::iterator i (names_map_.find (name));
+ return i != names_map_.end ()
+ ? dynamic_cast<T*> (&(*i->second)->nameable ())
+ : 0;
+ }
+
+ template <typename N>
+ typename scope<N>::names_iterator scope<N>::
+ find (name_type const& name)
+ {
+ typename names_map::iterator i (names_map_.find (name));
+
+ if (i == names_map_.end ())
+ return names_.end ();
+ else
+ return i->second;
+ }
+
+ template <typename N>
+ typename scope<N>::names_const_iterator scope<N>::
+ find (name_type const& name) const
+ {
+ typename names_map::const_iterator i (names_map_.find (name));
+
+ if (i == names_map_.end ())
+ return names_.end ();
+ else
+ return names_const_iterator (i->second);
+ }
+
+ template <typename N>
+ typename scope<N>::names_iterator scope<N>::
+ find (names_type const& e)
+ {
+ typename names_iterator_map::iterator i (iterator_map_.find (&e));
+ return i != iterator_map_.end () ? i->second : names_.end ();
+ }
+
+ template <typename N>
+ typename scope<N>::names_const_iterator scope<N>::
+ find (names_type const& e) const
+ {
+ typename names_iterator_map::const_iterator i (iterator_map_.find (&e));
+ return i != iterator_map_.end () ? i->second : names_.end ();
+ }
+
+ template <typename N>
+ scope<N>::
+ scope (scope const& s, scope* base, graph& g)
+ : first_key_ (names_.end ()),
+ first_drop_column_ (names_.end ()),
+ alters_ (0)
+ {
+ // Set the alters edge for lookup.
+ //
+ if (base != 0)
+ g.new_edge<alters> (*this, *base);
+
+ for (names_const_iterator i (s.names_begin ());
+ i != s.names_end (); ++i)
+ {
+ nameable_type& n (i->nameable ().clone (*this, g));
+ g.new_edge<names_type> (*this, n, i->name ());
+ }
+ }
+
+ template <typename N>
+ scope<N>::
+ scope (xml::parser& p, scope* base, graph& g)
+ : first_key_ (names_.end ()),
+ first_drop_column_ (names_.end ()),
+ alters_ (0)
+ {
+ // Set the alters edge for lookup.
+ //
+ if (base != 0)
+ g.new_edge<alters> (*this, *base);
+
+ using namespace xml;
+ p.content (content::complex);
+
+ for (parser::event_type e (p.peek ());
+ e == parser::start_element;
+ e = p.peek ())
+ {
+ typename nameable_type::parser_map::iterator i (
+ nameable_type::parser_map_.find (p.name ()));
+
+ if (p.namespace_ () != xmlns || i == nameable_type::parser_map_.end ())
+ break; // Not one of our elements.
+
+ p.next ();
+ i->second (p, *this, g);
+ p.next_expect (parser::end_element);
+ }
+ }
+
+ template <typename N>
+ void scope<N>::
+ serialize_content (xml::serializer& s) const
+ {
+ for (names_const_iterator i (names_begin ()); i != names_end (); ++i)
+ i->nameable ().serialize (s);
+ }
+
+ class column;
+ class primary_key;
+
+ template <typename N>
+ void scope<N>::
+ add_edge_left (names_type& e)
+ {
+ name_type const& name (e.name ());
+
+ typename names_map::iterator i (names_map_.find (name));
+
+ if (i == names_map_.end ())
+ {
+ typename names_list::iterator i (names_.insert (names_.end (), &e));
+ names_map_[name] = i;
+ iterator_map_[&e] = i;
+ }
+ else
+ throw duplicate_name (*this, (*i->second)->nameable (), e.nameable ());
+ }
+
+ template <typename N>
+ void scope<N>::
+ remove_edge_left (names_type& e)
+ {
+ typename names_iterator_map::iterator i (iterator_map_.find (&e));
+ assert (i != iterator_map_.end ());
+
+ names_.erase (i->second);
+ names_map_.erase (e.name ());
+ iterator_map_.erase (i);
+ }
+ }
+}
diff --git a/odb/odb/semantics/relational/foreign-key.cxx b/odb/odb/semantics/relational/foreign-key.cxx
new file mode 100644
index 0000000..0357d95
--- /dev/null
+++ b/odb/odb/semantics/relational/foreign-key.cxx
@@ -0,0 +1,218 @@
+// file : odb/semantics/relational/foreign-key.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <ostream>
+#include <istream>
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/foreign-key.hxx>
+
+using namespace std;
+
+namespace semantics
+{
+ namespace relational
+ {
+ static const char* action_str[] = {"NO ACTION", "CASCADE", "SET NULL"};
+
+ ostream&
+ operator<< (ostream& os, foreign_key::action_type v)
+ {
+ return os << action_str[v];
+ }
+
+ istream&
+ operator>> (istream& is, foreign_key::action_type& v)
+ {
+ string s;
+ getline (is, s);
+
+ if (!is.eof ())
+ is.setstate (istream::failbit);
+
+ if (!is.fail ())
+ {
+ if (s == "NO ACTION")
+ v = foreign_key::no_action;
+ else if (s == "CASCADE")
+ v = foreign_key::cascade;
+ else if (s == "SET NULL")
+ v = foreign_key::set_null;
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+ }
+
+ foreign_key::
+ foreign_key (foreign_key const& k, uscope& s, graph& g)
+ : key (k, s, g),
+ referenced_table_ (k.referenced_table_),
+ referenced_columns_ (k.referenced_columns_),
+ deferrable_ (k.deferrable_),
+ on_delete_ (k.on_delete_)
+ {
+ }
+
+ foreign_key::
+ foreign_key (xml::parser& p, uscope& s, graph& g)
+ : key (p, s, g),
+ deferrable_ (p.attribute ("deferrable", deferrable_type ())),
+ on_delete_ (p.attribute ("on-delete", no_action))
+ {
+ using namespace xml;
+
+ p.next_expect (parser::start_element, xmlns, "references");
+ referenced_table_ = p.attribute<qname> ("table");
+ p.content (content::complex);
+
+ for (parser::event_type e (p.peek ());
+ e == parser::start_element;
+ e = p.peek ())
+ {
+ if (p.qname () != xml::qname (xmlns, "column"))
+ break; // Not our elements.
+
+ p.next ();
+ referenced_columns_.push_back (p.attribute<uname> ("name"));
+ p.content (content::empty);
+ p.next_expect (parser::end_element);
+ }
+
+ p.next_expect (parser::end_element);
+ }
+
+ foreign_key& foreign_key::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<foreign_key> (*this, s, g);
+ }
+
+ void foreign_key::
+ serialize_attributes (xml::serializer& s) const
+ {
+ key::serialize_attributes (s);
+
+ if (deferrable () != deferrable_type::not_deferrable)
+ s.attribute ("deferrable", deferrable ());
+
+ if (on_delete () != no_action)
+ s.attribute ("on-delete", on_delete ());
+ }
+
+ void foreign_key::
+ serialize_content (xml::serializer& s) const
+ {
+ key::serialize_content (s);
+
+ // Referenced columns.
+ //
+ s.start_element (xmlns, "references");
+ s.attribute ("table", referenced_table ());
+
+ for (columns::const_iterator i (referenced_columns_.begin ());
+ i != referenced_columns_.end (); ++i)
+ {
+ s.start_element (xmlns, "column");
+ s.attribute ("name", *i);
+ s.end_element ();
+ }
+
+ s.end_element (); // references
+ }
+
+ void foreign_key::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "foreign-key");
+ serialize_attributes (s);
+ serialize_content (s);
+ s.end_element (); // foreign-key
+ }
+
+ // add_foreign_key
+ //
+ add_foreign_key& add_foreign_key::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<add_foreign_key> (*this, s, g);
+ }
+
+ void add_foreign_key::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "add-foreign-key");
+ foreign_key::serialize_attributes (s);
+ foreign_key::serialize_content (s);
+ s.end_element ();
+ }
+
+ // drop_foreign_key
+ //
+ drop_foreign_key::
+ drop_foreign_key (xml::parser& p, uscope&, graph& g)
+ : unameable (p, g)
+ {
+ p.content (xml::content::empty);
+ }
+
+ drop_foreign_key& drop_foreign_key::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<drop_foreign_key> (*this, s, g);
+ }
+
+ void drop_foreign_key::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "drop-foreign-key");
+ unameable::serialize_attributes (s);
+ s.end_element ();
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ unameable::parser_map& m (unameable::parser_map_);
+
+ m["foreign-key"] = &unameable::parser_impl<foreign_key>;
+ m["add-foreign-key"] = &unameable::parser_impl<add_foreign_key>;
+ m["drop-foreign-key"] = &unameable::parser_impl<drop_foreign_key>;
+
+ using compiler::type_info;
+
+ // foreign_key
+ //
+ {
+ type_info ti (typeid (foreign_key));
+ ti.add_base (typeid (key));
+ insert (ti);
+ }
+
+ // add_foreign_key
+ //
+ {
+ type_info ti (typeid (add_foreign_key));
+ ti.add_base (typeid (foreign_key));
+ insert (ti);
+ }
+
+ // drop_foreign_key
+ //
+ {
+ type_info ti (typeid (drop_foreign_key));
+ ti.add_base (typeid (unameable));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/odb/semantics/relational/foreign-key.hxx b/odb/odb/semantics/relational/foreign-key.hxx
new file mode 100644
index 0000000..32179fa
--- /dev/null
+++ b/odb/odb/semantics/relational/foreign-key.hxx
@@ -0,0 +1,152 @@
+// file : odb/semantics/relational/foreign-key.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_RELATIONAL_FOREIGN_KEY_HXX
+#define ODB_SEMANTICS_RELATIONAL_FOREIGN_KEY_HXX
+
+#include <iosfwd>
+
+#include <odb/semantics/relational/deferrable.hxx>
+#include <odb/semantics/relational/elements.hxx>
+#include <odb/semantics/relational/key.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ class foreign_key: public key
+ {
+ public:
+ qname const&
+ referenced_table () const
+ {
+ return referenced_table_;
+ }
+
+ typedef std::vector<string> columns;
+
+ columns const&
+ referenced_columns () const
+ {
+ return referenced_columns_;
+ }
+
+ columns&
+ referenced_columns ()
+ {
+ return referenced_columns_;
+ }
+
+ public:
+ typedef relational::deferrable deferrable_type;
+
+ deferrable_type
+ deferrable () const {return deferrable_;}
+
+ bool
+ not_deferrable () const
+ {
+ return deferrable_ == deferrable_type::not_deferrable;
+ }
+
+ enum action_type
+ {
+ no_action,
+ cascade,
+ set_null
+ };
+
+ action_type
+ on_delete () const {return on_delete_;}
+
+ public:
+ foreign_key (string const& id,
+ qname const& referenced_table,
+ deferrable_type deferrable,
+ action_type on_delete = no_action)
+ : key (id),
+ referenced_table_ (referenced_table),
+ deferrable_ (deferrable),
+ on_delete_ (on_delete)
+ {
+ }
+
+ foreign_key (foreign_key const&, uscope&, graph&);
+ foreign_key (xml::parser&, uscope&, graph&);
+
+ virtual foreign_key&
+ clone (uscope&, graph&) const;
+
+ virtual string
+ kind () const
+ {
+ return "foreign key";
+ }
+
+ virtual void
+ serialize (xml::serializer&) const;
+
+ protected:
+ void
+ serialize_attributes (xml::serializer&) const;
+
+ void
+ serialize_content (xml::serializer&) const;
+
+ private:
+ qname referenced_table_;
+ columns referenced_columns_;
+ deferrable_type deferrable_;
+ action_type on_delete_;
+ };
+
+ std::ostream&
+ operator<< (std::ostream&, foreign_key::action_type);
+
+ std::istream&
+ operator>> (std::istream&, foreign_key::action_type&);
+
+ class add_foreign_key: public foreign_key
+ {
+ public:
+ add_foreign_key (string const& id,
+ qname const& rt,
+ deferrable_type d,
+ action_type od = no_action)
+ : foreign_key (id, rt, d, od) {}
+ add_foreign_key (foreign_key const& fk, uscope& s, graph& g)
+ : foreign_key (fk, s, g) {}
+ add_foreign_key (xml::parser& p, uscope& s, graph& g)
+ : foreign_key (p, s, g) {}
+
+ virtual add_foreign_key&
+ clone (uscope&, graph&) const;
+
+ virtual string
+ kind () const {return "add foreign key";}
+
+ virtual void
+ serialize (xml::serializer&) const;
+ };
+
+ class drop_foreign_key: public unameable
+ {
+ public:
+ drop_foreign_key (string const& id): unameable (id) {}
+ drop_foreign_key (drop_foreign_key const& dfk, uscope&, graph& g)
+ : unameable (dfk, g) {}
+ drop_foreign_key (xml::parser&, uscope&, graph&);
+
+ virtual drop_foreign_key&
+ clone (uscope&, graph&) const;
+
+ virtual string
+ kind () const {return "drop foreign key";}
+
+ virtual void
+ serialize (xml::serializer&) const;
+ };
+ }
+}
+
+#endif // ODB_SEMANTICS_RELATIONAL_FOREIGN_KEY_HXX
diff --git a/odb/odb/semantics/relational/index.cxx b/odb/odb/semantics/relational/index.cxx
new file mode 100644
index 0000000..2329f3a
--- /dev/null
+++ b/odb/odb/semantics/relational/index.cxx
@@ -0,0 +1,145 @@
+// file : odb/semantics/relational/index.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/index.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ // index
+ //
+ index::
+ index (index const& i, uscope& s, graph& g)
+ : key (i, s, g),
+ type_ (i.type_),
+ method_ (i.method_),
+ options_ (i.options_)
+ {
+ }
+
+ index::
+ index (xml::parser& p, uscope& s, graph& g)
+ : key (p, s, g),
+ type_ (p.attribute ("type", string ())),
+ method_ (p.attribute ("method", string ())),
+ options_ (p.attribute ("options", string ()))
+ {
+ }
+
+ index& index::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<index> (*this, s, g);
+ }
+
+ void index::
+ serialize_attributes (xml::serializer& s) const
+ {
+ key::serialize_attributes (s);
+
+ if (!type ().empty ())
+ s.attribute ("type", type ());
+
+ if (!method ().empty ())
+ s.attribute ("method", method ());
+
+ if (!options ().empty ())
+ s.attribute ("options", options ());
+ }
+
+ void index::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "index");
+ serialize_attributes (s);
+ key::serialize_content (s);
+ s.end_element ();
+ }
+
+ // add_index
+ //
+ add_index& add_index::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<add_index> (*this, s, g);
+ }
+
+ void add_index::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "add-index");
+ index::serialize_attributes (s);
+ index::serialize_content (s);
+ s.end_element ();
+ }
+
+ // drop_index
+ //
+ drop_index::
+ drop_index (xml::parser& p, uscope&, graph& g)
+ : unameable (p, g)
+ {
+ p.content (xml::content::empty);
+ }
+
+ drop_index& drop_index::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<drop_index> (*this, s, g);
+ }
+
+ void drop_index::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "drop-index");
+ unameable::serialize_attributes (s);
+ s.end_element ();
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ unameable::parser_map& m (unameable::parser_map_);
+
+ m["index"] = &unameable::parser_impl<index>;
+ m["add-index"] = &unameable::parser_impl<add_index>;
+ m["drop-index"] = &unameable::parser_impl<drop_index>;
+
+ using compiler::type_info;
+
+ // index
+ //
+ {
+ type_info ti (typeid (index));
+ ti.add_base (typeid (key));
+ insert (ti);
+ }
+
+ // add_index
+ //
+ {
+ type_info ti (typeid (add_index));
+ ti.add_base (typeid (index));
+ insert (ti);
+ }
+
+ // drop_index
+ //
+ {
+ type_info ti (typeid (drop_index));
+ ti.add_base (typeid (unameable));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/odb/semantics/relational/index.hxx b/odb/odb/semantics/relational/index.hxx
new file mode 100644
index 0000000..68648cb
--- /dev/null
+++ b/odb/odb/semantics/relational/index.hxx
@@ -0,0 +1,100 @@
+// file : odb/semantics/relational/index.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_RELATIONAL_INDEX_HXX
+#define ODB_SEMANTICS_RELATIONAL_INDEX_HXX
+
+#include <odb/semantics/relational/elements.hxx>
+#include <odb/semantics/relational/key.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ // Note that in our model indexes are defined in the table scope.
+ //
+ class index: public key
+ {
+ public:
+ string const&
+ type () const {return type_;}
+
+ string const&
+ method () const {return method_;}
+
+ string const&
+ options () const {return options_;}
+
+ public:
+ index (string const& id,
+ string const& t = string (),
+ string const& m = string (),
+ string const& o = string ())
+ : key (id), type_ (t), method_ (m), options_ (o) {}
+ index (index const&, uscope&, graph&);
+ index (xml::parser&, uscope&, graph&);
+
+ virtual index&
+ clone (uscope&, graph&) const;
+
+ virtual string
+ kind () const
+ {
+ return "index";
+ }
+
+ virtual void
+ serialize (xml::serializer&) const;
+
+ protected:
+ void
+ serialize_attributes (xml::serializer&) const;
+
+ private:
+ string type_; // E.g., "UNIQUE", etc.
+ string method_; // E.g., "BTREE", etc.
+ string options_; // Database-specific index options.
+ };
+
+ class add_index: public index
+ {
+ public:
+ add_index (string const& id,
+ string const& t = string (),
+ string const& m = string (),
+ string const& o = string ())
+ : index (id, t, m, o) {}
+ add_index (index const& i, uscope& s, graph& g): index (i, s, g) {}
+ add_index (xml::parser& p, uscope& s, graph& g): index (p, s, g) {}
+
+ virtual add_index&
+ clone (uscope&, graph&) const;
+
+ virtual string
+ kind () const {return "add index";}
+
+ virtual void
+ serialize (xml::serializer&) const;
+ };
+
+ class drop_index: public unameable
+ {
+ public:
+ drop_index (string const& id): unameable (id) {}
+ drop_index (drop_index const& di, uscope&, graph& g)
+ : unameable (di, g) {}
+ drop_index (xml::parser&, uscope&, graph&);
+
+ virtual drop_index&
+ clone (uscope&, graph&) const;
+
+ virtual string
+ kind () const {return "drop index";}
+
+ virtual void
+ serialize (xml::serializer&) const;
+ };
+ }
+}
+
+#endif // ODB_SEMANTICS_RELATIONAL_INDEX_HXX
diff --git a/odb/odb/semantics/relational/key.cxx b/odb/odb/semantics/relational/key.cxx
new file mode 100644
index 0000000..3511618
--- /dev/null
+++ b/odb/odb/semantics/relational/key.cxx
@@ -0,0 +1,97 @@
+// file : odb/semantics/relational/key.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/key.hxx>
+#include <odb/semantics/relational/column.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ key::
+ key (key const& k, uscope& s, graph& g)
+ : unameable (k, g)
+ {
+ for (contains_iterator i (k.contains_begin ());
+ i != k.contains_end (); ++i)
+ {
+ column* c (s.lookup<column, drop_column> (i->column ().name ()));
+ assert (c != 0);
+ g.new_edge<contains> (*this, *c, i->options ());
+ }
+ }
+
+ key::
+ key (xml::parser& p, uscope& s, graph& g)
+ : unameable (p, g)
+ {
+ using namespace xml;
+ p.content (content::complex);
+
+ for (parser::event_type e (p.peek ());
+ e == parser::start_element;
+ e = p.peek ())
+ {
+ if (p.qname () != xml::qname (xmlns, "column"))
+ break; // Not our elements.
+
+ p.next ();
+ p.content (content::empty);
+
+ uname n (p.attribute<uname> ("name"));
+ column* c (s.lookup<column, drop_column> (n));
+ if (c == 0)
+ throw parsing (p, "invalid column name in the 'name' attribute");
+
+ string o (p.attribute ("options", string ()));
+ g.new_edge<contains> (*this, *c, o);
+
+ p.next_expect (parser::end_element);
+ }
+ }
+
+ void key::
+ serialize_content (xml::serializer& s) const
+ {
+ for (contains_iterator i (contains_begin ()); i != contains_end (); ++i)
+ {
+ s.start_element (xmlns, "column");
+ s.attribute ("name", i->column ().name ());
+ if (!i->options ().empty ())
+ s.attribute ("options", i->options ());
+ s.end_element ();
+ }
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // contains
+ //
+ {
+ type_info ti (typeid (contains));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // key
+ //
+ {
+ type_info ti (typeid (key));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/odb/semantics/relational/key.hxx b/odb/odb/semantics/relational/key.hxx
new file mode 100644
index 0000000..814d2ec
--- /dev/null
+++ b/odb/odb/semantics/relational/key.hxx
@@ -0,0 +1,104 @@
+// file : odb/semantics/relational/key.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_RELATIONAL_KEY_HXX
+#define ODB_SEMANTICS_RELATIONAL_KEY_HXX
+
+#include <odb/semantics/relational/elements.hxx>
+#include <odb/semantics/relational/table.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ class key;
+ class column;
+
+ class contains: public edge
+ {
+ public:
+ typedef relational::key key_type;
+ typedef relational::column column_type;
+
+ key_type&
+ key () const {return *key_;}
+
+ column_type&
+ column () const {return *column_;}
+
+ string const&
+ options () const {return options_;}
+
+ public:
+ contains (string const& o = string ()) : options_ (o) {}
+
+ void
+ set_left_node (key_type& n)
+ {
+ key_ = &n;
+ }
+
+ void
+ set_right_node (column_type& n)
+ {
+ column_ = &n;
+ }
+
+ protected:
+ key_type* key_;
+ column_type* column_;
+ string options_;
+ };
+
+ class key: public unameable
+ {
+ typedef std::vector<contains*> contains_list;
+
+ public:
+ typedef contains_list::size_type contains_size_type;
+
+ typedef
+ pointer_iterator<contains_list::const_iterator>
+ contains_iterator;
+
+ contains_iterator
+ contains_begin () const {return contains_.begin ();}
+
+ contains_iterator
+ contains_end () const {return contains_.end ();}
+
+ contains_size_type
+ contains_size () const {return contains_.size ();}
+
+ contains&
+ contains_at (contains_size_type i) const {return *contains_[i];}
+
+ public:
+ typedef relational::table table_type;
+
+ table_type&
+ table () const {return dynamic_cast<table_type&> (scope ());}
+
+ public:
+ key (std::string const& id): unameable (id) {}
+
+ void
+ add_edge_left (contains& e)
+ {
+ contains_.push_back (&e);
+ }
+
+ protected:
+ key (key const&, uscope&, graph&);
+ key (xml::parser&, uscope&, graph&);
+
+ void
+ serialize_content (xml::serializer&) const;
+
+ private:
+ contains_list contains_;
+ };
+ }
+}
+
+#endif // ODB_SEMANTICS_RELATIONAL_KEY_HXX
diff --git a/odb/odb/semantics/relational/model.cxx b/odb/odb/semantics/relational/model.cxx
new file mode 100644
index 0000000..8763045
--- /dev/null
+++ b/odb/odb/semantics/relational/model.cxx
@@ -0,0 +1,54 @@
+// file : odb/semantics/relational/model.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/model.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ model::
+ model (model const& m, graph& g)
+ : qscope (m, 0, g),
+ version_ (m.version_)
+ {
+ }
+
+ model::
+ model (xml::parser& p, graph& g)
+ : qscope (p, 0, g),
+ version_ (p.attribute<version_type> ("version"))
+ {
+ }
+
+ void model::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "model");
+ s.attribute ("version", version_);
+ qscope::serialize_content (s);
+ s.end_element ();
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ {
+ type_info ti (typeid (model));
+ ti.add_base (typeid (qscope));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/odb/semantics/relational/model.hxx b/odb/odb/semantics/relational/model.hxx
new file mode 100644
index 0000000..02d1863
--- /dev/null
+++ b/odb/odb/semantics/relational/model.hxx
@@ -0,0 +1,49 @@
+// file : odb/semantics/relational/model.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_RELATIONAL_MODEL_HXX
+#define ODB_SEMANTICS_RELATIONAL_MODEL_HXX
+
+#include <odb/semantics/relational/elements.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ class model: public graph, public qscope
+ {
+ public:
+ typedef relational::version version_type;
+
+ version_type
+ version () const {return version_;}
+
+ void
+ version (version_type v) {version_ = v;}
+
+ public:
+ model (version_type v): version_ (v) {}
+ model (model const&, graph&);
+ model (xml::parser&, graph&);
+
+ virtual string
+ kind () const {return "model";}
+
+ virtual void
+ serialize (xml::serializer&) const;
+
+ public:
+ using qscope::add_edge_left;
+ using qscope::add_edge_right;
+
+ private:
+ model (model const&);
+ model& operator= (model const&);
+
+ private:
+ version_type version_;
+ };
+ }
+}
+
+#endif // ODB_SEMANTICS_RELATIONAL_MODEL_HXX
diff --git a/odb/odb/semantics/relational/name.cxx b/odb/odb/semantics/relational/name.cxx
new file mode 100644
index 0000000..6eb2e16
--- /dev/null
+++ b/odb/odb/semantics/relational/name.cxx
@@ -0,0 +1,89 @@
+// file : odb/semantics/relational/name.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <ostream>
+#include <istream>
+
+#include <odb/semantics/relational/name.hxx>
+
+using namespace std;
+
+namespace semantics
+{
+ namespace relational
+ {
+ string qname::
+ string () const
+ {
+ std::string r;
+
+ bool f (true);
+ for (iterator i (begin ()); i < end (); ++i)
+ {
+ if (i->empty ())
+ continue;
+
+ if (f)
+ f = false;
+ else
+ r += '.';
+
+ r += *i;
+ }
+
+ return r;
+ }
+
+ qname qname::
+ from_string (std::string const& s)
+ {
+ using std::string;
+
+ qname n;
+
+ string::size_type p (string::npos);
+
+ for (size_t i (0); i < s.size (); ++i)
+ {
+ char c (s[i]);
+
+ if (c == '.')
+ {
+ if (p == string::npos)
+ n.append (string (s, 0, i));
+ else
+ n.append (string (s, p + 1, i - p - 1));
+
+ p = i;
+ }
+ }
+
+ if (p == string::npos)
+ n.append (s);
+ else
+ n.append (string (s, p + 1, string::npos));
+
+ return n;
+ }
+
+ ostream&
+ operator<< (ostream& os, qname const& n)
+ {
+ return os << n.string ();
+ }
+
+ istream&
+ operator>> (istream& is, qname& n)
+ {
+ string s;
+ is >> s;
+
+ if (!is.fail ())
+ n = qname::from_string (s);
+ else
+ n.clear ();
+
+ return is;
+ }
+ }
+}
diff --git a/odb/odb/semantics/relational/name.hxx b/odb/odb/semantics/relational/name.hxx
new file mode 100644
index 0000000..5268b4a
--- /dev/null
+++ b/odb/odb/semantics/relational/name.hxx
@@ -0,0 +1,159 @@
+// file : odb/semantics/relational/name.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_RELATIONAL_NAME_HXX
+#define ODB_SEMANTICS_RELATIONAL_NAME_HXX
+
+#include <string>
+#include <vector>
+#include <iosfwd>
+
+namespace semantics
+{
+ namespace relational
+ {
+ typedef std::string uname;
+
+ class qname
+ {
+ public:
+ typedef relational::uname uname_type;
+
+ qname () {}
+
+ explicit
+ qname (uname_type const& n) {append (n);}
+
+ template <typename I>
+ qname (I begin, I end)
+ {
+ for (; begin != end; ++begin)
+ append (*begin);
+ }
+
+ qname&
+ operator= (uname_type const& n)
+ {
+ components_.resize (1);
+ components_[0] = n;
+ return *this;
+ }
+
+ void
+ append (uname_type const& n) {components_.push_back (n);}
+
+ void
+ append (qname const& n)
+ {
+ components_.insert (components_.end (),
+ n.components_.begin (),
+ n.components_.end ());
+ }
+
+ void
+ clear () {components_.clear ();}
+
+ // Append a string to the last component.
+ //
+ qname&
+ operator+= (std::string const& s)
+ {
+ if (empty ())
+ append (s);
+ else
+ uname () += s;
+
+ return *this;
+ }
+
+ friend qname
+ operator+ (qname const& n, std::string const& s)
+ {
+ qname r (n);
+
+ if (r.empty ())
+ r.append (s);
+ else
+ r.uname () += s;
+
+ return r;
+ }
+
+ void
+ swap (qname& n) {components_.swap (n.components_);}
+
+ public:
+ bool
+ empty () const {return components_.empty ();}
+
+ bool
+ qualified () const {return components_.size () > 1;}
+
+ bool
+ fully_qualified () const
+ {
+ return qualified () && components_.front ().empty ();
+ }
+
+ public:
+ typedef std::vector<uname_type> components;
+ typedef components::const_iterator iterator;
+
+ iterator
+ begin () const {return components_.begin ();}
+
+ iterator
+ end () const {return components_.end ();}
+
+ uname_type&
+ uname () {return components_.back ();}
+
+ uname_type const&
+ uname () const {return components_.back ();}
+
+ qname
+ qualifier () const
+ {
+ return empty ()
+ ? qname ()
+ : qname (components_.begin (), components_.end () - 1);
+ }
+
+ std::string
+ string () const;
+
+ static qname
+ from_string (std::string const&);
+
+ public:
+ friend bool
+ operator== (qname const& x, qname const& y)
+ {
+ return x.components_ == y.components_;
+ }
+
+ friend bool
+ operator!= (qname const& x, qname const& y)
+ {
+ return x.components_ != y.components_;
+ }
+
+ friend bool
+ operator< (qname const& x, qname const& y)
+ {
+ return x.components_ < y.components_;
+ }
+
+ private:
+ components components_;
+ };
+
+ std::ostream&
+ operator<< (std::ostream&, qname const&);
+
+ std::istream&
+ operator>> (std::istream&, qname&);
+ }
+}
+
+#endif // ODB_SEMANTICS_RELATIONAL_NAME_HXX
diff --git a/odb/odb/semantics/relational/primary-key.cxx b/odb/odb/semantics/relational/primary-key.cxx
new file mode 100644
index 0000000..235340f
--- /dev/null
+++ b/odb/odb/semantics/relational/primary-key.cxx
@@ -0,0 +1,80 @@
+// file : odb/semantics/relational/primary-key.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/primary-key.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ primary_key::
+ primary_key (primary_key const& k, uscope& s, graph& g)
+ : key (k, s, g), auto__ (k.auto__), extra_map_ (k.extra_map_)
+ {
+ }
+
+ primary_key::
+ primary_key (xml::parser& p, uscope& s, graph& g)
+ : key (p, s, g),
+ auto__ (p.attribute ("auto", false))
+ {
+ // All unhandled attributes go into the extra map.
+ //
+ typedef xml::parser::attribute_map_type attr_map;
+ attr_map const& am (p.attribute_map ());
+
+ for (attr_map::const_iterator i (am.begin ()); i != am.end (); ++i)
+ {
+ if (!i->second.handled)
+ extra_map_[i->first.name ()] = i->second.value;
+ }
+ }
+
+ primary_key& primary_key::
+ clone (uscope& s, graph& g) const
+ {
+ return g.new_node<primary_key> (*this, s, g);
+ }
+
+ void primary_key::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "primary-key");
+ key::serialize_attributes (s);
+
+ if (auto_ ())
+ s.attribute ("auto", true);
+
+ for (extra_map::const_iterator i (extra_map_.begin ());
+ i != extra_map_.end (); ++i)
+ s.attribute (i->first, i->second);
+
+ key::serialize_content (s);
+ s.end_element ();
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ unameable::parser_map_["primary-key"] =
+ &unameable::parser_impl<primary_key>;
+
+ using compiler::type_info;
+
+ {
+ type_info ti (typeid (primary_key));
+ ti.add_base (typeid (key));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/odb/semantics/relational/primary-key.hxx b/odb/odb/semantics/relational/primary-key.hxx
new file mode 100644
index 0000000..114f682
--- /dev/null
+++ b/odb/odb/semantics/relational/primary-key.hxx
@@ -0,0 +1,62 @@
+// file : odb/semantics/relational/primary-key.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_RELATIONAL_PRIMARY_KEY_HXX
+#define ODB_SEMANTICS_RELATIONAL_PRIMARY_KEY_HXX
+
+#include <map>
+
+#include <odb/semantics/relational/elements.hxx>
+#include <odb/semantics/relational/key.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ class primary_key: public key
+ {
+ public:
+ bool
+ auto_ () const {return auto__;}
+
+ // Extra information.
+ //
+ public:
+ typedef std::map<string, string> extra_map;
+
+ extra_map&
+ extra () {return extra_map_;}
+
+ extra_map const&
+ extra () const {return extra_map_;}
+
+ public:
+ primary_key (bool auto_)
+ : key (""), // Primary key has the implicit empty id.
+ auto__ (auto_)
+ {
+ }
+
+ primary_key (primary_key const&, uscope&, graph&);
+ primary_key (xml::parser&, uscope&, graph&);
+
+ virtual primary_key&
+ clone (uscope&, graph&) const;
+
+ virtual string
+ kind () const
+ {
+ return "primary key";
+ }
+
+ virtual void
+ serialize (xml::serializer&) const;
+
+ private:
+ bool auto__;
+ extra_map extra_map_;
+ };
+ }
+}
+
+#endif // ODB_SEMANTICS_RELATIONAL_PRIMARY_KEY_HXX
diff --git a/odb/odb/semantics/relational/table.cxx b/odb/odb/semantics/relational/table.cxx
new file mode 100644
index 0000000..3bf763d
--- /dev/null
+++ b/odb/odb/semantics/relational/table.cxx
@@ -0,0 +1,183 @@
+// file : odb/semantics/relational/table.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+
+#include <odb/semantics/relational/table.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ // table
+ //
+ table::
+ table (table const& t, qscope& s, graph& g, bool b)
+ : qnameable (t, g),
+ uscope (t, (b ? s.lookup<table, drop_table> (t.name ()) : 0), g),
+ options_ (t.options_),
+ extra_map_ (t.extra_map_)
+ {
+ }
+
+ table::
+ table (xml::parser& p, qscope& s, graph& g, bool b)
+ : qnameable (p, g),
+ uscope (
+ p,
+ (b ? s.lookup<table, drop_table> (
+ p.attribute<qnameable::name_type> ("name")) : 0),
+ g),
+ options_ (p.attribute ("options", string ()))
+ {
+ // All unhandled attributes go into the extra map.
+ //
+ typedef xml::parser::attribute_map_type attr_map;
+ attr_map const& am (p.attribute_map ());
+
+ for (attr_map::const_iterator i (am.begin ()); i != am.end (); ++i)
+ {
+ if (!i->second.handled)
+ extra_map_[i->first.name ()] = i->second.value;
+ }
+ }
+
+ table& table::
+ clone (qscope& s, graph& g) const
+ {
+ return g.new_node<table> (*this, s, g);
+ }
+
+ void table::
+ serialize_attributes (xml::serializer& s) const
+ {
+ qnameable::serialize_attributes (s);
+
+ if (!options_.empty ())
+ s.attribute ("options", options_);
+
+ for (extra_map::const_iterator i (extra_map_.begin ());
+ i != extra_map_.end (); ++i)
+ s.attribute (i->first, i->second);
+ }
+
+ void table::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "table");
+ serialize_attributes (s);
+ uscope::serialize_content (s);
+ s.end_element ();
+ }
+
+ // add_table
+ //
+ add_table& add_table::
+ clone (qscope& s, graph& g) const
+ {
+ return g.new_node<add_table> (*this, s, g);
+ }
+
+ void add_table::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "add-table");
+ table::serialize_attributes (s);
+ table::serialize_content (s);
+ s.end_element ();
+ }
+
+ // drop_table
+ //
+ drop_table::
+ drop_table (xml::parser& p, qscope&, graph& g)
+ : qnameable (p, g)
+ {
+ p.content (xml::content::empty);
+ }
+
+ drop_table& drop_table::
+ clone (qscope& s, graph& g) const
+ {
+ return g.new_node<drop_table> (*this, s, g);
+ }
+
+ void drop_table::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "drop-table");
+ qnameable::serialize_attributes (s);
+ s.end_element ();
+ }
+
+ // alter_table
+ //
+ alter_table& alter_table::
+ clone (qscope& s, graph& g) const
+ {
+ return g.new_node<alter_table> (*this, s, g);
+ }
+
+ void alter_table::
+ serialize (xml::serializer& s) const
+ {
+ s.start_element (xmlns, "alter-table");
+ table::serialize_attributes (s);
+ table::serialize_content (s);
+ s.end_element ();
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ qnameable::parser_map& m (qnameable::parser_map_);
+
+ m["table"] = &qnameable::parser_impl<table>;
+ m["add-table"] = &qnameable::parser_impl<add_table>;
+ m["drop-table"] = &qnameable::parser_impl<drop_table>;
+ m["alter-table"] = &qnameable::parser_impl<alter_table>;
+
+ using compiler::type_info;
+
+ // table
+ //
+ {
+ type_info ti (typeid (table));
+ ti.add_base (typeid (qnameable));
+ ti.add_base (typeid (uscope));
+ insert (ti);
+ }
+
+ // add_table
+ //
+ {
+ type_info ti (typeid (add_table));
+ ti.add_base (typeid (table));
+ insert (ti);
+ }
+
+ // drop_table
+ //
+ {
+ type_info ti (typeid (drop_table));
+ ti.add_base (typeid (qnameable));
+ insert (ti);
+ }
+
+ // alter_table
+ //
+ {
+ type_info ti (typeid (alter_table));
+ ti.add_base (typeid (table));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+ }
+}
diff --git a/odb/odb/semantics/relational/table.hxx b/odb/odb/semantics/relational/table.hxx
new file mode 100644
index 0000000..1c4efcf
--- /dev/null
+++ b/odb/odb/semantics/relational/table.hxx
@@ -0,0 +1,115 @@
+// file : odb/semantics/relational/table.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_RELATIONAL_TABLE_HXX
+#define ODB_SEMANTICS_RELATIONAL_TABLE_HXX
+
+#include <odb/semantics/relational/elements.hxx>
+
+namespace semantics
+{
+ namespace relational
+ {
+ class table: public qnameable, public uscope
+ {
+ public:
+ virtual string const&
+ options () const {return options_;}
+
+ virtual void
+ options (string const& o) {options_ = o;}
+
+ // Extra information.
+ //
+ public:
+ typedef std::map<string, string> extra_map;
+
+ extra_map&
+ extra () {return extra_map_;}
+
+ extra_map const&
+ extra () const {return extra_map_;}
+
+ public:
+ table (string const& id): qnameable (id) {}
+ table (table const&, qscope&, graph&, bool base = false);
+ table (xml::parser&, qscope&, graph&, bool base = false);
+
+ virtual table&
+ clone (qscope&, graph&) const;
+
+ virtual string
+ kind () const {return "table";}
+
+ virtual void
+ serialize (xml::serializer&) const;
+
+ // Resolve ambiguity.
+ //
+ using qnameable::scope;
+
+ protected:
+ void
+ serialize_attributes (xml::serializer&) const;
+
+ protected:
+ string options_;
+ extra_map extra_map_;
+ };
+
+ class add_table: public table
+ {
+ public:
+ add_table (string const& id): table (id) {}
+ add_table (table const& t, qscope& s, graph& g): table (t, s, g) {}
+ add_table (xml::parser& p, qscope& s, graph& g): table (p, s, g) {}
+
+ virtual add_table&
+ clone (qscope&, graph&) const;
+
+ virtual string
+ kind () const {return "add table";}
+
+ virtual void
+ serialize (xml::serializer&) const;
+ };
+
+ class drop_table: public qnameable
+ {
+ public:
+ drop_table (string const& id): qnameable (id) {}
+ drop_table (drop_table const& t, qscope&, graph& g): qnameable (t, g) {}
+ drop_table (xml::parser&, qscope&, graph&);
+
+ virtual drop_table&
+ clone (qscope&, graph&) const;
+
+ virtual string
+ kind () const {return "drop table";}
+
+ virtual void
+ serialize (xml::serializer&) const;
+ };
+
+ class alter_table: public table
+ {
+ public:
+ alter_table (string const& id): table (id) {}
+ alter_table (alter_table const& at, qscope& s, graph& g)
+ : table (at, s, g, true) {}
+ alter_table (xml::parser& p, qscope& s, graph& g)
+ : table (p, s, g, true) {}
+
+ virtual alter_table&
+ clone (qscope&, graph&) const;
+
+ virtual string
+ kind () const {return "alter table";}
+
+ virtual void
+ serialize (xml::serializer&) const;
+ };
+ }
+}
+
+#endif // ODB_SEMANTICS_RELATIONAL_TABLE_HXX
diff --git a/odb/odb/semantics/template.cxx b/odb/odb/semantics/template.cxx
new file mode 100644
index 0000000..f492be0
--- /dev/null
+++ b/odb/odb/semantics/template.cxx
@@ -0,0 +1,88 @@
+// file : odb/semantics/template.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/template.hxx>
+
+namespace semantics
+{
+ template_::
+ template_ ()
+ {
+ }
+
+ instantiates::
+ instantiates ()
+ {
+ }
+
+ instantiation::
+ instantiation ()
+ {
+ }
+
+ type_template::
+ type_template ()
+ {
+ }
+
+ type_instantiation::
+ type_instantiation ()
+ {
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // template_
+ //
+ {
+ type_info ti (typeid (template_));
+ ti.add_base (typeid (nameable));
+ insert (ti);
+ }
+
+ // instantiates
+ //
+ {
+ type_info ti (typeid (instantiates));
+ ti.add_base (typeid (edge));
+ insert (ti);
+ }
+
+ // instantiation
+ //
+ {
+ type_info ti (typeid (instantiation));
+ ti.add_base (typeid (node));
+ insert (ti);
+ }
+
+ // type_template
+ //
+ {
+ type_info ti (typeid (type_template));
+ ti.add_base (typeid (template_));
+ insert (ti);
+ }
+
+ // type_instantiation
+ //
+ {
+ type_info ti (typeid (type_instantiation));
+ ti.add_base (typeid (type));
+ ti.add_base (typeid (instantiation));
+ insert (ti);
+ }
+
+ }
+ } init_;
+ }
+}
diff --git a/odb/odb/semantics/template.hxx b/odb/odb/semantics/template.hxx
new file mode 100644
index 0000000..11fe340
--- /dev/null
+++ b/odb/odb/semantics/template.hxx
@@ -0,0 +1,146 @@
+// file : odb/semantics/template.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_TEMPLATE_HXX
+#define ODB_SEMANTICS_TEMPLATE_HXX
+
+#include <vector>
+#include <odb/semantics/elements.hxx>
+
+namespace semantics
+{
+ //
+ //
+ class instantiates;
+
+ class template_: public virtual nameable
+ {
+ typedef std::vector<instantiates*> instantiated;
+
+ public:
+ typedef
+ pointer_iterator<instantiated::const_iterator>
+ instantiated_iterator;
+
+ instantiated_iterator
+ instantiated_begin () const
+ {
+ return instantiated_.begin ();
+ }
+
+ instantiated_iterator
+ instantiated_end () const
+ {
+ return instantiated_.end ();
+ }
+
+ public:
+ void
+ add_edge_right (instantiates& e)
+ {
+ instantiated_.push_back (&e);
+ }
+
+ using nameable::add_edge_right;
+
+ protected:
+ template_ ();
+
+ private:
+ instantiated instantiated_;
+ };
+
+ //
+ //
+ class instantiation;
+
+ class instantiates: public edge
+ {
+ public:
+ typedef semantics::template_ template_type;
+ typedef semantics::instantiation instantiation_type;
+
+ template_type&
+ template_ () const
+ {
+ return *template__;
+ }
+
+ instantiation_type&
+ instantiation () const
+ {
+ return *instantiation_;
+ }
+
+ public:
+ instantiates ();
+
+ void
+ set_left_node (instantiation_type& n)
+ {
+ instantiation_ = &n;
+ }
+
+ void
+ set_right_node (template_type& n)
+ {
+ template__ = &n;
+ }
+
+ private:
+ template_type* template__;
+ instantiation_type* instantiation_;
+ };
+
+ //
+ //
+ class instantiation: public virtual node
+ {
+ public:
+ typedef semantics::template_ template_type;
+ typedef semantics::instantiates instantiates_type;
+
+ template_type&
+ template_ () const
+ {
+ return instantiates_->template_ ();
+ }
+
+ instantiates_type&
+ instantiates () const
+ {
+ return *instantiates_;
+ }
+
+ public:
+ void
+ add_edge_left (instantiates_type& e)
+ {
+ instantiates_ = &e;
+ }
+
+ protected:
+ instantiation ();
+
+ private:
+ instantiates_type* instantiates_;
+ };
+
+ //
+ // Type template and instantiation.
+ //
+
+ class type_template: public template_
+ {
+ protected:
+ type_template ();
+ };
+
+ class type_instantiation: public virtual type, public instantiation
+ {
+ protected:
+ type_instantiation ();
+ };
+}
+
+#endif // ODB_SEMANTICS_TEMPLATE_HXX
diff --git a/odb/odb/semantics/union-template.cxx b/odb/odb/semantics/union-template.cxx
new file mode 100644
index 0000000..21fc9c0
--- /dev/null
+++ b/odb/odb/semantics/union-template.cxx
@@ -0,0 +1,54 @@
+// file : odb/semantics/union-template.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/union-template.hxx>
+
+namespace semantics
+{
+ union_template::
+ union_template (path const& file, size_t line, size_t column, tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ union_instantiation::
+ union_instantiation (path const& file,
+ size_t line,
+ size_t column,
+ tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // union_template
+ //
+ {
+ type_info ti (typeid (union_template));
+ ti.add_base (typeid (type_template));
+ ti.add_base (typeid (scope));
+ insert (ti);
+ }
+
+ // union_instantiation
+ //
+ {
+ type_info ti (typeid (union_instantiation));
+ ti.add_base (typeid (union_));
+ ti.add_base (typeid (type_instantiation));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/odb/odb/semantics/union-template.hxx b/odb/odb/semantics/union-template.hxx
new file mode 100644
index 0000000..3e719b7
--- /dev/null
+++ b/odb/odb/semantics/union-template.hxx
@@ -0,0 +1,33 @@
+// file : odb/semantics/union-template.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_UNION_TEMPLATE_HXX
+#define ODB_SEMANTICS_UNION_TEMPLATE_HXX
+
+#include <odb/semantics/elements.hxx>
+#include <odb/semantics/union.hxx>
+#include <odb/semantics/template.hxx>
+
+namespace semantics
+{
+ class union_template: public type_template, public scope
+ {
+ public:
+ union_template (path const&, size_t line, size_t column, tree);
+
+ // Resolve conflict between scope::scope and nameable::scope.
+ //
+ using nameable::scope;
+ };
+
+ class union_instantiation: public union_, public type_instantiation
+ {
+ public:
+ union_instantiation (path const&, size_t line, size_t column, tree);
+
+ using union_::add_edge_left;
+ using type_instantiation::add_edge_left;
+ };
+}
+
+#endif // ODB_SEMANTICS_UNION_TEMPLATE_HXX
diff --git a/odb/odb/semantics/union.cxx b/odb/odb/semantics/union.cxx
new file mode 100644
index 0000000..007ef57
--- /dev/null
+++ b/odb/odb/semantics/union.cxx
@@ -0,0 +1,36 @@
+// file : odb/semantics/union.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/union.hxx>
+
+namespace semantics
+{
+ union_::
+ union_ (path const& file, size_t line, size_t column, tree tn)
+ : node (file, line, column, tn)
+ {
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // union_
+ //
+ {
+ type_info ti (typeid (union_));
+ ti.add_base (typeid (type));
+ ti.add_base (typeid (scope));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/odb/odb/semantics/union.hxx b/odb/odb/semantics/union.hxx
new file mode 100644
index 0000000..79adc42
--- /dev/null
+++ b/odb/odb/semantics/union.hxx
@@ -0,0 +1,27 @@
+// file : odb/semantics/union.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_UNION_HXX
+#define ODB_SEMANTICS_UNION_HXX
+
+#include <odb/semantics/elements.hxx>
+
+namespace semantics
+{
+ class union_: public virtual type, public scope
+ {
+ public:
+ union_ (path const&, size_t line, size_t column, tree);
+
+ // Resolve conflict between scope::scope and nameable::scope.
+ //
+ using nameable::scope;
+
+ protected:
+ union_ ()
+ {
+ }
+ };
+}
+
+#endif // ODB_SEMANTICS_UNION_HXX
diff --git a/odb/odb/semantics/unit.cxx b/odb/odb/semantics/unit.cxx
new file mode 100644
index 0000000..4f92aed
--- /dev/null
+++ b/odb/odb/semantics/unit.cxx
@@ -0,0 +1,42 @@
+// file : odb/semantics/unit.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <libcutl/compiler/type-info.hxx>
+#include <odb/semantics/unit.hxx>
+
+namespace semantics
+{
+ unit::
+ unit (path const& file)
+ : node (file, 1, 1, global_namespace), graph_ (*this)
+ {
+ // Use a special edge to get this->name() return the global
+ // namespace name ("").
+ //
+ new_edge<global_names> (*this, *this);
+ node::unit (*this);
+ }
+
+ // type info
+ //
+ namespace
+ {
+ struct init
+ {
+ init ()
+ {
+ using compiler::type_info;
+
+ // unit
+ //
+ {
+ type_info ti (typeid (unit));
+ ti.add_base (typeid (namespace_));
+ insert (ti);
+ }
+ }
+ } init_;
+ }
+}
diff --git a/odb/odb/semantics/unit.hxx b/odb/odb/semantics/unit.hxx
new file mode 100644
index 0000000..cfccbff
--- /dev/null
+++ b/odb/odb/semantics/unit.hxx
@@ -0,0 +1,183 @@
+// file : odb/semantics/unit.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SEMANTICS_UNIT_HXX
+#define ODB_SEMANTICS_UNIT_HXX
+
+#include <map>
+
+#include <odb/semantics/elements.hxx>
+#include <odb/semantics/namespace.hxx>
+
+namespace semantics
+{
+ class unit: public graph<node, edge>, public namespace_
+ {
+ public:
+ unit (path const&);
+
+ private:
+ unit (unit const&);
+ unit& operator= (unit const&);
+
+ // Mapping from tree nodes to semantic graph nodes.
+ //
+ public:
+ node*
+ find (tree key) const
+ {
+ tree_node_map::const_iterator i (tree_node_map_.find (key));
+ return i != tree_node_map_.end () ? i->second : 0;
+ }
+
+ void
+ insert (tree key, node& value)
+ {
+ tree_node_map_[key] = &value;
+ }
+
+ using namespace_::find;
+
+ // Mapping from tree nodes to name hints.
+ //
+ public:
+ names*
+ find_hint (tree key) const
+ {
+ name_hint_map::const_iterator i (name_hint_map_.find (key));
+ return i != name_hint_map_.end () ? i->second : 0;
+ }
+
+ void
+ insert_hint (tree key, names& name)
+ {
+ name_hint_map_[key] = &name;
+ }
+
+ public:
+ template <class T>
+ T&
+ new_node (path const& file, size_t line, size_t column)
+ {
+ T& r (graph_.new_node<T> (file, line, column));
+ r.unit (*this);
+ return r;
+ }
+
+ template <class T, class A0>
+ T&
+ new_node (path const& file, size_t line, size_t column, A0 const& a0)
+ {
+ T& r (graph_.new_node<T> (file, line, column, a0));
+ r.unit (*this);
+ return r;
+ }
+
+ template <class T, class A0, class A1>
+ T&
+ new_node (path const& file, size_t line, size_t column,
+ A0 const& a0, A1 const& a1)
+ {
+ T& r (graph_.new_node<T> (file, line, column, a0, a1));
+ r.unit (*this);
+ return r;
+ }
+
+ template <class T, class A0, class A1, class A2>
+ T&
+ new_node (path const& file, size_t line, size_t column,
+ A0 const& a0, A1 const& a1, A2 const& a2)
+ {
+ T& r (graph_.new_node<T> (file, line, column, a0, a1, a2));
+ r.unit (*this);
+ return r;
+ }
+
+ template <class T, class A0, class A1, class A2, class A3>
+ T&
+ new_node (path const& file, size_t line, size_t column,
+ A0 const& a0, A1 const& a1, A2 const& a2, A3 const& a3)
+ {
+ T& r (graph_.new_node<T> (file, line, column, a0, a1, a2, a3));
+ r.unit (*this);
+ return r;
+ }
+
+ template <class T, class A0, class A1, class A2, class A3, class A4>
+ T&
+ new_node (path const& file, size_t line, size_t column,
+ A0 const& a0, A1 const& a1, A2 const& a2, A3 const& a3,
+ A4 const& a4)
+ {
+ T& r (graph_.new_node<T> (file, line, column, a0, a1, a2, a3, a4));
+ r.unit (*this);
+ return r;
+ }
+
+ // For fundamental types.
+ //
+ template <class T>
+ T&
+ new_fund_node (tree tn)
+ {
+ T& r (graph_.new_node<T> (tn));
+ r.unit (*this);
+ return r;
+ }
+
+ protected:
+ // Special names edge for the global namespace.
+ //
+ class global_names: public names
+ {
+ public:
+ global_names ()
+ : names ("")
+ {
+ scope_ = 0;
+ }
+
+ void
+ set_left_node (unit&)
+ {
+ }
+
+ void
+ set_right_node (nameable& n)
+ {
+ named_ = &n;
+ }
+
+ void
+ clear_left_node (unit&)
+ {
+ }
+
+ void
+ clear_right_node (nameable& n)
+ {
+ assert (named_ == &n);
+ named_ = 0;
+ }
+ };
+
+ public:
+ void
+ add_edge_left (global_names&)
+ {
+ }
+
+ using namespace_::add_edge_right;
+
+ private:
+ graph<node, edge>& graph_;
+
+ typedef std::map<tree, node*> tree_node_map;
+ tree_node_map tree_node_map_;
+
+ typedef std::map<tree, names*> name_hint_map;
+ name_hint_map name_hint_map_;
+ };
+}
+
+#endif // ODB_SEMANTICS_UNIT_HXX
diff --git a/odb/odb/source.cxx b/odb/odb/source.cxx
new file mode 100644
index 0000000..b2a39be
--- /dev/null
+++ b/odb/odb/source.cxx
@@ -0,0 +1,151 @@
+// file : odb/source.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/common.hxx>
+#include <odb/context.hxx>
+#include <odb/generate.hxx>
+#include <odb/diagnostics.hxx>
+
+using namespace std;
+
+namespace source
+{
+ struct class_: traversal::class_, virtual context
+ {
+ class_ ()
+ : typedefs_ (false),
+ query_columns_type_ (false, false, false),
+ view_query_columns_type_ (false)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck == class_other ||
+ (!options.at_once () && class_file (c) != unit.file ()))
+ return;
+
+ names (c);
+
+ switch (ck)
+ {
+ case class_object: traverse_object (c); break;
+ case class_view: traverse_view (c); break;
+ default: break;
+ }
+ }
+
+ void
+ traverse_object (type&);
+
+ void
+ traverse_view (type&);
+
+ private:
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ instance<query_columns_type> query_columns_type_;
+ instance<view_query_columns_type> view_query_columns_type_;
+ };
+}
+
+void source::class_::
+traverse_object (type& c)
+{
+ bool poly (polymorphic (c));
+ bool abst (abstract (c));
+ bool reuse_abst (abst && !poly);
+
+ // The rest only applies to dynamic milti-database support.
+ //
+ if (!multi_dynamic)
+ return;
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl
+ << endl;
+
+ // query_columns
+ //
+ if (options.generate_query ())
+ query_columns_type_->traverse (c);
+
+ // The rest does not apply to reuse-abstract objects.
+ //
+ if (reuse_abst)
+ return;
+
+ string const& type (class_fq_name (c));
+ string traits ("access::object_traits_impl< " + type + ", id_common >");
+
+ os << "const " << traits << "::" << endl
+ << "function_table_type*" << endl
+ << traits << "::" << endl
+ << "function_table[database_count];"
+ << endl;
+}
+
+void source::class_::
+traverse_view (type& c)
+{
+ // The rest only applies to dynamic milti-database support.
+ //
+ if (!multi_dynamic)
+ return;
+
+ os << "// " << class_name (c) << endl
+ << "//" << endl
+ << endl;
+
+ if (c.get<size_t> ("object-count") != 0)
+ view_query_columns_type_->traverse (c);
+
+ string const& type (class_fq_name (c));
+ string traits ("access::view_traits_impl< " + type + ", id_common >");
+
+ os << "const " << traits << "::" << endl
+ << "function_table_type*" << endl
+ << traits << "::" << endl
+ << "function_table[database_count];"
+ << endl;
+}
+
+namespace source
+{
+ void
+ generate ()
+ {
+ context ctx;
+ ostream& os (ctx.os);
+
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (false);
+ traversal::namespace_ ns;
+ class_ c;
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (false);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ os << "namespace odb"
+ << "{";
+
+ unit.dispatch (ctx.unit);
+
+ os << "}";
+ }
+}
diff --git a/odb/odb/sql-lexer.cxx b/odb/odb/sql-lexer.cxx
new file mode 100644
index 0000000..ab6f549
--- /dev/null
+++ b/odb/odb/sql-lexer.cxx
@@ -0,0 +1,250 @@
+// file : odb/sql-lexer.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <iostream>
+
+#include <odb/sql-lexer.hxx>
+
+using namespace std;
+
+sql_lexer::
+sql_lexer ()
+ : loc_ ("C"), buf_ (0, 0, 0)
+{
+}
+
+sql_lexer::
+sql_lexer (std::string const& sql)
+ : loc_ ("C"), buf_ (0, 0, 0)
+{
+ lex (sql);
+}
+
+void sql_lexer::
+lex (std::string const& sql)
+{
+ is_.str (sql);
+ is_.clear ();
+
+ l_ = c_ = 1;
+ buf_ = xchar (0, 0, 0);
+ eos_ = false;
+ unget_ = false;
+}
+
+sql_lexer::xchar sql_lexer::
+peek ()
+{
+ if (unget_)
+ return buf_;
+ else
+ {
+ if (eos_)
+ return xchar (xchar::traits_type::eof (), l_, c_);
+ else
+ {
+ xchar::int_type i (is_.peek ());
+
+ if (i == xchar::traits_type::eof ())
+ eos_ = true;
+
+ return xchar (i, l_, c_);
+ }
+ }
+}
+
+sql_lexer::xchar sql_lexer::
+get ()
+{
+ if (unget_)
+ {
+ unget_ = false;
+ return buf_;
+ }
+ else
+ {
+ // When is_.get () returns eof, the failbit is also set (stupid,
+ // isn't?) which may trigger an exception. To work around this
+ // we will call peek() first and only call get() if it is not
+ // eof. But we can only call peek() on eof once; any subsequent
+ // calls will spoil the failbit (even more stupid).
+ //
+ xchar c (peek ());
+
+ if (!is_eos (c))
+ {
+ is_.get ();
+
+ if (c == '\n')
+ {
+ l_++;
+ c_ = 1;
+ }
+ else
+ c_++;
+ }
+
+ return c;
+ }
+}
+
+void sql_lexer::
+unget (xchar c)
+{
+ // Because iostream::unget cannot work once eos is reached,
+ // we have to provide our own implementation.
+ //
+ buf_ = c;
+ unget_ = true;
+}
+
+sql_token sql_lexer::
+next ()
+{
+ skip_spaces ();
+
+ xchar c (get ());
+
+ if (is_eos (c))
+ return sql_token ();
+
+ switch (c)
+ {
+ case '\'':
+ {
+ return string_literal (c);
+ }
+ case '\"':
+ {
+ return string_literal (c);
+ }
+ case '`':
+ {
+ return string_literal (c);
+ }
+ case ';':
+ {
+ return sql_token (sql_token::p_semi);
+ }
+ case ',':
+ {
+ return sql_token (sql_token::p_comma);
+ }
+ case '(':
+ {
+ return sql_token (sql_token::p_lparen);
+ }
+ case ')':
+ {
+ return sql_token (sql_token::p_rparen);
+ }
+ case '=':
+ {
+ return sql_token (sql_token::p_eq);
+ }
+ case '-':
+ {
+ return int_literal (get (), true);
+ }
+ case '+':
+ {
+ return int_literal (get (), false);
+ }
+ }
+
+ if (is_alpha (c) || c == '_')
+ {
+ return identifier (c);
+ }
+
+ if (is_dec_digit (c))
+ {
+ return int_literal (c);
+ }
+
+ ostringstream msg;
+ msg << "unexpected character '" << c << "'";
+ throw invalid_input (c.line (), c.column (), msg.str ());
+}
+
+void sql_lexer::
+skip_spaces ()
+{
+ for (xchar c (peek ());; c = peek ())
+ {
+ if (is_eos (c) || !is_space (c))
+ break;
+
+ get ();
+ }
+}
+
+sql_token sql_lexer::
+identifier (xchar c)
+{
+ string lexeme;
+ lexeme += c;
+
+ for (c = peek ();
+ !is_eos (c) && (is_alnum (c) || c == '_');
+ c = peek ())
+ {
+ get ();
+ lexeme += c;
+ }
+
+ return sql_token (sql_token::t_identifier, lexeme);
+}
+
+sql_token sql_lexer::
+int_literal (xchar c, bool neg, size_t /*ml*/, size_t /*mc*/)
+{
+ //size_t ln (neg ? ml : c.line ()), cl (neg ? mc : c.column ());
+ string lexeme;
+
+ if (neg)
+ lexeme += '-';
+
+ lexeme += c;
+
+ for (c = peek (); !is_eos (c) && is_dec_digit (c); c = peek ())
+ {
+ get ();
+ lexeme += c;
+ }
+
+ return sql_token (sql_token::t_int_lit, lexeme);
+}
+
+sql_token sql_lexer::
+string_literal (xchar c)
+{
+ //size_t ln (c.line ()), cl (c.column ());
+ char q (c);
+ string lexeme;
+ lexeme += c;
+
+ while (true)
+ {
+ xchar c = get ();
+
+ if (is_eos (c))
+ throw invalid_input (
+ c.line (), c.column (), "unterminated quoted string");
+
+ lexeme += c;
+
+ // In SQL, double-quote is used to encode a single quote inside
+ // a string.
+ //
+ if (c == q)
+ {
+ if (peek () == q)
+ get ();
+ else
+ break;
+ }
+ }
+
+ return sql_token (sql_token::t_string_lit, lexeme);
+}
diff --git a/odb/odb/sql-lexer.hxx b/odb/odb/sql-lexer.hxx
new file mode 100644
index 0000000..9d24233
--- /dev/null
+++ b/odb/odb/sql-lexer.hxx
@@ -0,0 +1,129 @@
+// file : odb/sql-lexer.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SQL_LEXER_HXX
+#define ODB_SQL_LEXER_HXX
+
+#include <string>
+#include <locale>
+#include <cstddef> // std::size_t
+#include <sstream>
+
+#include <odb/sql-token.hxx>
+
+class sql_lexer
+{
+public:
+ sql_lexer ();
+ sql_lexer (std::string const& sql);
+
+ void lex (std::string const& sql);
+
+ struct invalid_input
+ {
+ invalid_input (std::size_t l, std::size_t c, std::string const& m)
+ : line (l), column (c), message (m)
+ {
+ }
+
+ std::size_t line;
+ std::size_t column;
+ std::string const message;
+ };
+
+ sql_token
+ next ();
+
+protected:
+ class xchar
+ {
+ public:
+ typedef std::char_traits<char> traits_type;
+ typedef traits_type::int_type int_type;
+ typedef traits_type::char_type char_type;
+
+ xchar (int_type v, std::size_t l, std::size_t c);
+
+ operator char_type () const;
+
+ int_type
+ value () const;
+
+ std::size_t
+ line () const;
+
+ std::size_t
+ column () const;
+
+ private:
+ int_type v_;
+ std::size_t l_;
+ std::size_t c_;
+ };
+
+ xchar
+ peek ();
+
+ xchar
+ get ();
+
+ void
+ unget (xchar);
+
+protected:
+ void
+ skip_spaces ();
+
+ sql_token
+ identifier (xchar);
+
+ sql_token
+ int_literal (xchar,
+ bool neg = false,
+ std::size_t ml = 0,
+ std::size_t mc = 0);
+
+ sql_token
+ string_literal (xchar);
+
+protected:
+ bool
+ is_alpha (char c) const;
+
+ bool
+ is_oct_digit (char c) const;
+
+ bool
+ is_dec_digit (char c) const;
+
+ bool
+ is_hex_digit (char c) const;
+
+ bool
+ is_alnum (char c) const;
+
+ bool
+ is_space (char c) const;
+
+ bool
+ is_eos (xchar const& c) const;
+
+ char
+ to_upper (char c) const;
+
+private:
+ std::locale loc_;
+ std::istringstream is_;
+ std::size_t l_;
+ std::size_t c_;
+
+ bool eos_;
+ bool include_;
+
+ xchar buf_;
+ bool unget_;
+};
+
+#include <odb/sql-lexer.ixx>
+
+#endif // ODB_SQL_LEXER_HXX
diff --git a/odb/odb/sql-lexer.ixx b/odb/odb/sql-lexer.ixx
new file mode 100644
index 0000000..9179804
--- /dev/null
+++ b/odb/odb/sql-lexer.ixx
@@ -0,0 +1,84 @@
+// file : odb/sql-lexer.ixx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+// sql_lexer::xchar
+//
+inline sql_lexer::xchar::
+xchar (int_type v, std::size_t l, std::size_t c)
+ : v_ (v), l_ (l), c_ (c)
+{
+}
+
+inline sql_lexer::xchar::
+operator char_type () const
+{
+ return traits_type::to_char_type (v_);
+}
+
+inline sql_lexer::xchar::int_type sql_lexer::xchar::
+value () const
+{
+ return v_;
+}
+
+inline std::size_t sql_lexer::xchar::
+line () const
+{
+ return l_;
+}
+
+inline std::size_t sql_lexer::xchar::
+column () const
+{
+ return c_;
+}
+
+// lexer
+//
+inline bool sql_lexer::
+is_alpha (char c) const
+{
+ return std::isalpha (c, loc_);
+}
+
+inline bool sql_lexer::
+is_oct_digit (char c) const
+{
+ return std::isdigit (c, loc_) && c != '8' && c != '9';
+}
+
+inline bool sql_lexer::
+is_dec_digit (char c) const
+{
+ return std::isdigit (c, loc_);
+}
+
+inline bool sql_lexer::
+is_hex_digit (char c) const
+{
+ return std::isxdigit (c, loc_);
+}
+
+inline bool sql_lexer::
+is_alnum (char c) const
+{
+ return std::isalnum (c, loc_);
+}
+
+inline bool sql_lexer::
+is_space (char c) const
+{
+ return std::isspace (c, loc_);
+}
+
+inline bool sql_lexer::
+is_eos (xchar const& c) const
+{
+ return c.value () == xchar::traits_type::eof ();
+}
+
+inline char sql_lexer::
+to_upper (char c) const
+{
+ return std::toupper (c, loc_);
+}
diff --git a/odb/odb/sql-token.cxx b/odb/odb/sql-token.cxx
new file mode 100644
index 0000000..da9ecb2
--- /dev/null
+++ b/odb/odb/sql-token.cxx
@@ -0,0 +1,44 @@
+// file : odb/sql-token.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <ostream>
+
+#include <odb/sql-token.hxx>
+
+using namespace std;
+
+static char punctuation_literals[] = {';', ',', '(', ')', '='};
+
+string sql_token::
+string () const
+{
+ switch (type ())
+ {
+ case sql_token::t_eos:
+ {
+ return "<end-of-stream>";
+ }
+ case sql_token::t_identifier:
+ {
+ return identifier ();
+ }
+ case sql_token::t_punctuation:
+ {
+ return std::string (1, punctuation_literals[punctuation ()]);
+ }
+ case sql_token::t_string_lit:
+ case sql_token::t_int_lit:
+ case sql_token::t_float_lit:
+ {
+ return literal ();
+ }
+ }
+
+ return "";
+}
+
+ostream&
+operator<< (ostream& os, sql_token const& t)
+{
+ return os << t.string ();
+}
diff --git a/odb/odb/sql-token.hxx b/odb/odb/sql-token.hxx
new file mode 100644
index 0000000..c34de21
--- /dev/null
+++ b/odb/odb/sql-token.hxx
@@ -0,0 +1,89 @@
+// file : odb/sql-token.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_SQL_TOKEN_HXX
+#define ODB_SQL_TOKEN_HXX
+
+#include <string>
+#include <iosfwd>
+#include <cstddef> // std::size_t
+
+class sql_token
+{
+public:
+ enum token_type
+ {
+ t_eos,
+ t_identifier,
+ t_punctuation,
+ t_string_lit,
+ t_int_lit,
+ t_float_lit,
+ };
+
+ token_type
+ type () const;
+
+ // Identifier
+ //
+public:
+ std::string const&
+ identifier () const;
+
+ // Punctuation
+ //
+public:
+ enum punctuation_type
+ {
+ // Keep synched with punctuation_literals in source file.
+ //
+ p_semi,
+ p_comma,
+ p_lparen,
+ p_rparen,
+ p_eq,
+ p_invalid
+ };
+
+ // Return the punctuation id if type is t_punctuation and p_invalid
+ // otherwise.
+ //
+ punctuation_type
+ punctuation () const;
+
+ // Literals.
+ //
+public:
+ std::string const&
+ literal () const;
+
+ // Human-readable string representation.
+ //
+public:
+ std::string
+ string () const;
+
+ // C-tors.
+ //
+public:
+ // EOS and punctuations.
+ //
+ sql_token ();
+ sql_token (punctuation_type p);
+
+ // Identifier and literals.
+ //
+ sql_token (token_type t, std::string const& s);
+
+private:
+ token_type type_;
+ punctuation_type punctuation_;
+ std::string str_;
+};
+
+std::ostream&
+operator<< (std::ostream&, sql_token const&);
+
+#include <odb/sql-token.ixx>
+
+#endif // ODB_SQL_TOKEN_HXX
diff --git a/odb/odb/sql-token.ixx b/odb/odb/sql-token.ixx
new file mode 100644
index 0000000..5118c9f
--- /dev/null
+++ b/odb/odb/sql-token.ixx
@@ -0,0 +1,44 @@
+// file : odb/sql-token.ixx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+inline sql_token::token_type sql_token::
+type () const
+{
+ return type_;
+}
+
+inline std::string const& sql_token::
+identifier () const
+{
+ return str_;
+}
+
+inline sql_token::punctuation_type sql_token::
+punctuation () const
+{
+ return type_ == t_punctuation ? punctuation_ : p_invalid;
+}
+
+inline std::string const& sql_token::
+literal () const
+{
+ return str_;
+}
+
+inline sql_token::
+sql_token ()
+ : type_ (t_eos)
+{
+}
+
+inline sql_token::
+sql_token (punctuation_type p)
+ : type_ (t_punctuation), punctuation_ (p)
+{
+}
+
+inline sql_token::
+sql_token (token_type t, std::string const& s)
+ : type_ (t), str_ (s)
+{
+}
diff --git a/odb/odb/traversal.hxx b/odb/odb/traversal.hxx
new file mode 100644
index 0000000..7f421b1
--- /dev/null
+++ b/odb/odb/traversal.hxx
@@ -0,0 +1,19 @@
+// file : odb/traversal.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_HXX
+#define ODB_TRAVERSAL_HXX
+
+#include <odb/traversal/class.hxx>
+#include <odb/traversal/class-template.hxx>
+#include <odb/traversal/derived.hxx>
+#include <odb/traversal/elements.hxx>
+#include <odb/traversal/enum.hxx>
+#include <odb/traversal/fundamental.hxx>
+#include <odb/traversal/namespace.hxx>
+#include <odb/traversal/template.hxx>
+#include <odb/traversal/union.hxx>
+#include <odb/traversal/union-template.hxx>
+#include <odb/traversal/unit.hxx>
+
+#endif // ODB_TRAVERSAL_HXX
diff --git a/odb/odb/traversal/class-template.cxx b/odb/odb/traversal/class-template.cxx
new file mode 100644
index 0000000..b04b625
--- /dev/null
+++ b/odb/odb/traversal/class-template.cxx
@@ -0,0 +1,62 @@
+// file : odb/traversal/class-template.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/traversal/class-template.hxx>
+
+namespace traversal
+{
+ //
+ //
+ void class_template::
+ traverse (type& c)
+ {
+ inherits (c);
+ names (c);
+ }
+
+ void class_template::
+ inherits (type& c)
+ {
+ inherits (c, *this);
+ }
+
+ void class_template::
+ inherits (type& c, edge_dispatcher& d)
+ {
+ iterate_and_dispatch (c.inherits_begin (), c.inherits_end (), d);
+ }
+
+ //
+ //
+ void class_instantiation::
+ traverse (type& c)
+ {
+ instantiates (c);
+ inherits (c);
+ names (c);
+ }
+
+ void class_instantiation::
+ instantiates (type& c)
+ {
+ instantiates (c, *this);
+ }
+
+ void class_instantiation::
+ instantiates (type& c, edge_dispatcher& d)
+ {
+ d.dispatch (c.instantiates ());
+ }
+
+ void class_instantiation::
+ inherits (type& c)
+ {
+ inherits (c, *this);
+ }
+
+ void class_instantiation::
+ inherits (type& c, edge_dispatcher& d)
+ {
+ iterate_and_dispatch (c.inherits_begin (), c.inherits_end (), d);
+ }
+}
diff --git a/odb/odb/traversal/class-template.hxx b/odb/odb/traversal/class-template.hxx
new file mode 100644
index 0000000..18e1e5b
--- /dev/null
+++ b/odb/odb/traversal/class-template.hxx
@@ -0,0 +1,45 @@
+// file : odb/traversal/class-template.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_CLASS_TEMPLATE_HXX
+#define ODB_TRAVERSAL_CLASS_TEMPLATE_HXX
+
+#include <odb/semantics/class-template.hxx>
+
+#include <odb/traversal/elements.hxx>
+#include <odb/traversal/class.hxx>
+
+namespace traversal
+{
+ struct class_template: scope_template<semantics::class_template>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ inherits (type&);
+
+ virtual void
+ inherits (type&, edge_dispatcher&);
+ };
+
+ struct class_instantiation: scope_template<semantics::class_instantiation>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ instantiates (type&);
+
+ virtual void
+ instantiates (type&, edge_dispatcher&);
+
+ virtual void
+ inherits (type&);
+
+ virtual void
+ inherits (type&, edge_dispatcher&);
+ };
+}
+
+#endif // ODB_TRAVERSAL_CLASS_TEMPLATE_HXX
diff --git a/odb/odb/traversal/class.cxx b/odb/odb/traversal/class.cxx
new file mode 100644
index 0000000..80c8b80
--- /dev/null
+++ b/odb/odb/traversal/class.cxx
@@ -0,0 +1,34 @@
+// file : odb/traversal/class.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/traversal/class.hxx>
+
+namespace traversal
+{
+ void inherits::
+ traverse (type& i)
+ {
+ dispatch (i.base ());
+ }
+
+ //
+ //
+ void class_::
+ traverse (type& c)
+ {
+ inherits (c);
+ names (c);
+ }
+
+ void class_::
+ inherits (type& c)
+ {
+ inherits (c, *this);
+ }
+
+ void class_::
+ inherits (type& c, edge_dispatcher& d)
+ {
+ iterate_and_dispatch (c.inherits_begin (), c.inherits_end (), d);
+ }
+}
diff --git a/odb/odb/traversal/class.hxx b/odb/odb/traversal/class.hxx
new file mode 100644
index 0000000..de86cc0
--- /dev/null
+++ b/odb/odb/traversal/class.hxx
@@ -0,0 +1,40 @@
+// file : odb/traversal/class.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_CLASS_HXX
+#define ODB_TRAVERSAL_CLASS_HXX
+
+#include <odb/semantics/class.hxx>
+#include <odb/traversal/elements.hxx>
+
+namespace traversal
+{
+ struct inherits: edge<semantics::inherits>
+ {
+ inherits ()
+ {
+ }
+
+ inherits (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct class_: scope_template<semantics::class_>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ inherits (type&);
+
+ virtual void
+ inherits (type&, edge_dispatcher&);
+ };
+}
+
+#endif // ODB_TRAVERSAL_CLASS_HXX
diff --git a/odb/odb/traversal/derived.cxx b/odb/odb/traversal/derived.cxx
new file mode 100644
index 0000000..a0acab8
--- /dev/null
+++ b/odb/odb/traversal/derived.cxx
@@ -0,0 +1,111 @@
+// file : odb/traversal/derived.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/traversal/derived.hxx>
+
+namespace traversal
+{
+ void qualifies::
+ traverse (type& e)
+ {
+ dispatch (e.type ());
+ }
+
+ void points::
+ traverse (type& e)
+ {
+ dispatch (e.type ());
+ }
+
+ void references::
+ traverse (type& e)
+ {
+ dispatch (e.type ());
+ }
+
+ void contains::
+ traverse (type& e)
+ {
+ dispatch (e.type ());
+ }
+
+ //
+ //
+ void qualifier::
+ traverse (type& q)
+ {
+ qualifies (q);
+ }
+
+ void qualifier::
+ qualifies (type& q)
+ {
+ qualifies (q, *this);
+ }
+
+ void qualifier::
+ qualifies (type& q, edge_dispatcher& d)
+ {
+ d.dispatch (q.qualifies ());
+ }
+
+ //
+ //
+ void pointer::
+ traverse (type& p)
+ {
+ points (p);
+ }
+
+ void pointer::
+ points (type& p)
+ {
+ points (p, *this);
+ }
+
+ void pointer::
+ points (type& p, edge_dispatcher& d)
+ {
+ d.dispatch (p.points ());
+ }
+
+ //
+ //
+ void reference::
+ traverse (type& r)
+ {
+ references (r);
+ }
+
+ void reference::
+ references (type& r)
+ {
+ references (r, *this);
+ }
+
+ void reference::
+ references (type& r, edge_dispatcher& d)
+ {
+ d.dispatch (r.references ());
+ }
+
+ //
+ //
+ void array::
+ traverse (type& a)
+ {
+ contains (a);
+ }
+
+ void array::
+ contains (type& a)
+ {
+ contains (a, *this);
+ }
+
+ void array::
+ contains (type& a, edge_dispatcher& d)
+ {
+ d.dispatch (a.contains ());
+ }
+}
diff --git a/odb/odb/traversal/derived.hxx b/odb/odb/traversal/derived.hxx
new file mode 100644
index 0000000..b7648aa
--- /dev/null
+++ b/odb/odb/traversal/derived.hxx
@@ -0,0 +1,130 @@
+// file : odb/traversal/derived.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_DERIVED_HXX
+#define ODB_TRAVERSAL_DERIVED_HXX
+
+#include <odb/semantics/derived.hxx>
+#include <odb/traversal/elements.hxx>
+
+namespace traversal
+{
+ //
+ // Edges.
+ //
+ struct qualifies: edge<semantics::qualifies>
+ {
+ qualifies ()
+ {
+ }
+
+ qualifies (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct points: edge<semantics::points>
+ {
+ points ()
+ {
+ }
+
+ points (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct references: edge<semantics::references>
+ {
+ references ()
+ {
+ }
+
+ references (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct contains: edge<semantics::contains>
+ {
+ contains ()
+ {
+ }
+
+ contains (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+
+ //
+ // Nodes.
+ //
+ struct derived_type: node<semantics::derived_type> {};
+
+ struct qualifier: node<semantics::qualifier>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ qualifies (type&);
+
+ virtual void
+ qualifies (type&, edge_dispatcher&);
+ };
+
+ struct pointer: node<semantics::pointer>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ points (type&);
+
+ virtual void
+ points (type&, edge_dispatcher&);
+ };
+
+ struct reference: node<semantics::reference>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ references (type&);
+
+ virtual void
+ references (type&, edge_dispatcher&);
+ };
+
+ struct array: node<semantics::array>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ contains (type&);
+
+ virtual void
+ contains (type&, edge_dispatcher&);
+ };
+}
+
+#endif // ODB_TRAVERSAL_DERIVED_HXX
diff --git a/odb/odb/traversal/elements.cxx b/odb/odb/traversal/elements.cxx
new file mode 100644
index 0000000..f95917a
--- /dev/null
+++ b/odb/odb/traversal/elements.cxx
@@ -0,0 +1,77 @@
+// file : odb/traversal/elements.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/traversal/elements.hxx>
+
+namespace traversal
+{
+ void names::
+ traverse (type& e)
+ {
+ dispatch (e.named ());
+ }
+
+ void declares::
+ traverse (type& e)
+ {
+ dispatch (e.named ());
+ }
+
+ void defines::
+ traverse (type& e)
+ {
+ dispatch (e.named ());
+ }
+
+ void typedefs::
+ traverse (type& e)
+ {
+ dispatch (e.named ());
+ }
+
+ void belongs::
+ traverse (type& e)
+ {
+ dispatch (e.type ());
+ }
+
+ // instance
+ //
+ void instance::
+ traverse (type& i)
+ {
+ belongs (i);
+ }
+
+ void instance::
+ belongs (type& i)
+ {
+ belongs (i, *this);
+ }
+
+ void instance::
+ belongs (type& i, edge_dispatcher& d)
+ {
+ d.dispatch (i.belongs ());
+ }
+
+ // data_member
+ //
+ void data_member::
+ traverse (type& m)
+ {
+ belongs (m);
+ }
+
+ void data_member::
+ belongs (type& m)
+ {
+ belongs (m, *this);
+ }
+
+ void data_member::
+ belongs (type& m, edge_dispatcher& d)
+ {
+ d.dispatch (m.belongs ());
+ }
+}
diff --git a/odb/odb/traversal/elements.hxx b/odb/odb/traversal/elements.hxx
new file mode 100644
index 0000000..d67a6d8
--- /dev/null
+++ b/odb/odb/traversal/elements.hxx
@@ -0,0 +1,238 @@
+// file : odb/traversal/elements.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_ELEMENTS_HXX
+#define ODB_TRAVERSAL_ELEMENTS_HXX
+
+#include <libcutl/compiler/traversal.hxx>
+#include <odb/semantics/elements.hxx>
+
+namespace traversal
+{
+ using namespace cutl;
+
+ //
+ //
+ typedef compiler::dispatcher<semantics::node> node_dispatcher;
+ typedef compiler::dispatcher<semantics::edge> edge_dispatcher;
+
+ //
+ //
+ struct node_base: node_dispatcher, edge_dispatcher
+ {
+ void
+ edge_traverser (edge_dispatcher& d)
+ {
+ edge_dispatcher::traverser (d);
+ }
+
+ edge_dispatcher&
+ edge_traverser ()
+ {
+ return *this;
+ }
+
+ using node_dispatcher::dispatch;
+ using edge_dispatcher::dispatch;
+
+ using edge_dispatcher::iterate_and_dispatch;
+ };
+
+ struct edge_base: edge_dispatcher, node_dispatcher
+ {
+ void
+ node_traverser (node_dispatcher& d)
+ {
+ node_dispatcher::traverser (d);
+ }
+
+ node_dispatcher&
+ node_traverser ()
+ {
+ return *this;
+ }
+
+ using edge_dispatcher::dispatch;
+ using node_dispatcher::dispatch;
+
+ using node_dispatcher::iterate_and_dispatch;
+ };
+
+ inline edge_base&
+ operator>> (node_base& n, edge_base& e)
+ {
+ n.edge_traverser (e);
+ return e;
+ }
+
+ inline node_base&
+ operator>> (edge_base& e, node_base& n)
+ {
+ e.node_traverser (n);
+ return n;
+ }
+
+ //
+ //
+ template <typename X>
+ struct node: compiler::traverser_impl<X, semantics::node>,
+ virtual node_base
+ {
+ };
+
+ template <typename X>
+ struct edge: compiler::traverser_impl<X, semantics::edge>,
+ virtual edge_base
+ {
+ };
+
+ //
+ // Edges
+ //
+
+ struct names: edge<semantics::names>
+ {
+ names ()
+ {
+ }
+
+ names (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct declares: edge<semantics::declares>
+ {
+ declares ()
+ {
+ }
+
+ declares (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct defines: edge<semantics::defines>
+ {
+ defines ()
+ {
+ }
+
+ defines (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct typedefs: edge<semantics::typedefs>
+ {
+ typedefs ()
+ {
+ }
+
+ typedefs (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct belongs: edge<semantics::belongs>
+ {
+ belongs ()
+ {
+ }
+
+ belongs (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ //
+ // Nodes
+ //
+
+ struct nameable: node<semantics::nameable> {};
+
+ //
+ //
+ template <typename T>
+ struct scope_template: node<T>
+ {
+ public:
+ virtual void
+ traverse (T& s)
+ {
+ names (s);
+ }
+
+ virtual void
+ names (T& s)
+ {
+ names (s, *this);
+ }
+
+ virtual void
+ names (T& s, edge_dispatcher& d)
+ {
+ this->iterate_and_dispatch (s.names_begin (), s.names_end (), d);
+ }
+ };
+
+ struct scope: scope_template<semantics::scope> {};
+
+ //
+ //
+ struct type: node<semantics::type> {};
+
+ //
+ //
+ struct instance: node<semantics::instance>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ belongs (type&);
+
+ virtual void
+ belongs (type&, edge_dispatcher&);
+ };
+
+ //
+ //
+ struct data_member: node<semantics::data_member>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ belongs (type&);
+
+ virtual void
+ belongs (type&, edge_dispatcher&);
+ };
+
+ //
+ //
+ struct unsupported_type: node<semantics::unsupported_type> {};
+}
+
+#endif // ODB_TRAVERSAL_ELEMENTS_HXX
diff --git a/odb/odb/traversal/enum.cxx b/odb/odb/traversal/enum.cxx
new file mode 100644
index 0000000..fa33f92
--- /dev/null
+++ b/odb/odb/traversal/enum.cxx
@@ -0,0 +1,54 @@
+// file : odb/traversal/enum.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/traversal/enum.hxx>
+
+namespace traversal
+{
+ void enumerates::
+ traverse (type& e)
+ {
+ dispatch (e.enumerator ());
+ }
+
+ //
+ //
+ void underlies::
+ traverse (type& u)
+ {
+ dispatch (u.type ());
+ }
+
+ //
+ //
+ void enum_::
+ traverse (type& e)
+ {
+ underlied (e);
+ enumerates (e);
+ }
+
+ void enum_::
+ underlied (type& e)
+ {
+ underlied (e, *this);
+ }
+
+ void enum_::
+ underlied (type& e, edge_dispatcher& d)
+ {
+ d.dispatch (e.underlied ());
+ }
+
+ void enum_::
+ enumerates (type& e)
+ {
+ enumerates (e, *this);
+ }
+
+ void enum_::
+ enumerates (type& e, edge_dispatcher& d)
+ {
+ iterate_and_dispatch (e.enumerates_begin (), e.enumerates_end (), d);
+ }
+}
diff --git a/odb/odb/traversal/enum.hxx b/odb/odb/traversal/enum.hxx
new file mode 100644
index 0000000..cd141f2
--- /dev/null
+++ b/odb/odb/traversal/enum.hxx
@@ -0,0 +1,57 @@
+// file : odb/traversal/enum.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_ENUM_HXX
+#define ODB_TRAVERSAL_ENUM_HXX
+
+#include <odb/semantics/enum.hxx>
+#include <odb/traversal/elements.hxx>
+
+namespace traversal
+{
+ struct enumerates: edge<semantics::enumerates>
+ {
+ enumerates () {}
+ enumerates (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct enumerator: node<semantics::enumerator> {};
+
+ struct underlies: edge<semantics::underlies>
+ {
+ underlies () {}
+ underlies (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct enum_: node<semantics::enum_>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ underlied (type&);
+
+ virtual void
+ underlied (type&, edge_dispatcher&);
+
+ virtual void
+ enumerates (type&);
+
+ virtual void
+ enumerates (type&, edge_dispatcher&);
+ };
+}
+
+#endif // ODB_TRAVERSAL_ENUM_HXX
diff --git a/odb/odb/traversal/fundamental.hxx b/odb/odb/traversal/fundamental.hxx
new file mode 100644
index 0000000..974e74f
--- /dev/null
+++ b/odb/odb/traversal/fundamental.hxx
@@ -0,0 +1,39 @@
+// file : odb/traversal/fundamental.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_FUNDAMENTAL_HXX
+#define ODB_TRAVERSAL_FUNDAMENTAL_HXX
+
+#include <odb/semantics/fundamental.hxx>
+#include <odb/traversal/elements.hxx>
+
+namespace traversal
+{
+ struct fund_type: node<semantics::fund_type> {};
+
+ struct fund_void: node<semantics::fund_void> {};
+ struct fund_bool: node<semantics::fund_bool> {};
+
+ // Integral.
+ //
+ struct fund_char: node<semantics::fund_char> {};
+ struct fund_wchar: node<semantics::fund_wchar> {};
+ struct fund_signed_char: node<semantics::fund_signed_char> {};
+ struct fund_unsigned_char: node<semantics::fund_unsigned_char> {};
+ struct fund_short: node<semantics::fund_short> {};
+ struct fund_unsigned_short: node<semantics::fund_unsigned_short> {};
+ struct fund_int: node<semantics::fund_int> {};
+ struct fund_unsigned_int: node<semantics::fund_unsigned_int> {};
+ struct fund_long: node<semantics::fund_long> {};
+ struct fund_unsigned_long: node<semantics::fund_unsigned_long> {};
+ struct fund_long_long: node<semantics::fund_long_long> {};
+ struct fund_unsigned_long_long: node<semantics::fund_unsigned_long_long> {};
+
+ // Real.
+ //
+ struct fund_float: node<semantics::fund_float> {};
+ struct fund_double: node<semantics::fund_double> {};
+ struct fund_long_double: node<semantics::fund_long_double> {};
+}
+
+#endif // ODB_TRAVERSAL_FUNDAMENTAL_HXX
diff --git a/odb/odb/traversal/namespace.hxx b/odb/odb/traversal/namespace.hxx
new file mode 100644
index 0000000..223322b
--- /dev/null
+++ b/odb/odb/traversal/namespace.hxx
@@ -0,0 +1,15 @@
+// file : odb/traversal/namespace.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_NAMESPACE_HXX
+#define ODB_TRAVERSAL_NAMESPACE_HXX
+
+#include <odb/semantics/namespace.hxx>
+#include <odb/traversal/elements.hxx>
+
+namespace traversal
+{
+ struct namespace_: scope_template<semantics::namespace_> {};
+}
+
+#endif // ODB_TRAVERSAL_NAMESPACE_HXX
diff --git a/odb/odb/traversal/relational.hxx b/odb/odb/traversal/relational.hxx
new file mode 100644
index 0000000..a78e26b
--- /dev/null
+++ b/odb/odb/traversal/relational.hxx
@@ -0,0 +1,18 @@
+// file : odb/traversal/relational.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_RELATIONAL_HXX
+#define ODB_TRAVERSAL_RELATIONAL_HXX
+
+#include <odb/traversal/relational/changelog.hxx>
+#include <odb/traversal/relational/changeset.hxx>
+#include <odb/traversal/relational/column.hxx>
+#include <odb/traversal/relational/elements.hxx>
+#include <odb/traversal/relational/foreign-key.hxx>
+#include <odb/traversal/relational/index.hxx>
+#include <odb/traversal/relational/key.hxx>
+#include <odb/traversal/relational/model.hxx>
+#include <odb/traversal/relational/primary-key.hxx>
+#include <odb/traversal/relational/table.hxx>
+
+#endif // ODB_TRAVERSAL_RELATIONAL_HXX
diff --git a/odb/odb/traversal/relational/changelog.cxx b/odb/odb/traversal/relational/changelog.cxx
new file mode 100644
index 0000000..149d93d
--- /dev/null
+++ b/odb/odb/traversal/relational/changelog.cxx
@@ -0,0 +1,63 @@
+// file : odb/traversal/relational/changelog.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/traversal/relational/changelog.hxx>
+#include <odb/traversal/relational/model.hxx>
+#include <odb/traversal/relational/changeset.hxx>
+
+namespace traversal
+{
+ namespace relational
+ {
+ // contains_model
+ //
+ void contains_model::
+ traverse (type& c)
+ {
+ dispatch (c.model ());
+ }
+
+ // contains_changeset
+ //
+ void contains_changeset::
+ traverse (type& c)
+ {
+ dispatch (c.changeset ());
+ }
+
+ // changelog
+ //
+ void changelog::
+ traverse (type& cl)
+ {
+ contains_model (cl);
+ contains_changeset (cl);
+ }
+
+ void changelog::
+ contains_model (type& cl)
+ {
+ contains_model (cl, *this);
+ }
+
+ void changelog::
+ contains_model (type& cl, edge_dispatcher& d)
+ {
+ d.dispatch (cl.contains_model ());
+ }
+
+ void changelog::
+ contains_changeset (type& cl)
+ {
+ contains_changeset (cl, *this);
+ }
+
+ void changelog::
+ contains_changeset (type& cl, edge_dispatcher& d)
+ {
+ iterate_and_dispatch (cl.contains_changeset_begin (),
+ cl.contains_changeset_end (),
+ d);
+ }
+ }
+}
diff --git a/odb/odb/traversal/relational/changelog.hxx b/odb/odb/traversal/relational/changelog.hxx
new file mode 100644
index 0000000..4b7f18f
--- /dev/null
+++ b/odb/odb/traversal/relational/changelog.hxx
@@ -0,0 +1,53 @@
+// file : odb/traversal/relational/changelog.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_RELATIONAL_CHANGELOG_HXX
+#define ODB_TRAVERSAL_RELATIONAL_CHANGELOG_HXX
+
+#include <odb/semantics/relational/changelog.hxx>
+#include <odb/traversal/relational/elements.hxx>
+
+namespace traversal
+{
+ namespace relational
+ {
+ struct contains_model: edge<semantics::relational::contains_model>
+ {
+ contains_model () {}
+ contains_model (node_dispatcher& n) {node_traverser (n);}
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct contains_changeset: edge<semantics::relational::contains_changeset>
+ {
+ contains_changeset () {}
+ contains_changeset (node_dispatcher& n) {node_traverser (n);}
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct changelog: node<semantics::relational::changelog>
+ {
+ public:
+ virtual void
+ traverse (type&);
+
+ virtual void
+ contains_model (type&);
+
+ virtual void
+ contains_model (type&, edge_dispatcher&);
+
+ virtual void
+ contains_changeset (type&);
+
+ virtual void
+ contains_changeset (type&, edge_dispatcher&);
+ };
+ }
+}
+
+#endif // ODB_TRAVERSAL_RELATIONAL_CHANGELOG_HXX
diff --git a/odb/odb/traversal/relational/changeset.hxx b/odb/odb/traversal/relational/changeset.hxx
new file mode 100644
index 0000000..3cc522a
--- /dev/null
+++ b/odb/odb/traversal/relational/changeset.hxx
@@ -0,0 +1,18 @@
+// file : odb/traversal/relational/changeset.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_RELATIONAL_CHANGESET_HXX
+#define ODB_TRAVERSAL_RELATIONAL_CHANGESET_HXX
+
+#include <odb/semantics/relational/changeset.hxx>
+#include <odb/traversal/relational/elements.hxx>
+
+namespace traversal
+{
+ namespace relational
+ {
+ struct changeset: scope_template<semantics::relational::changeset> {};
+ }
+}
+
+#endif // ODB_TRAVERSAL_RELATIONAL_CHANGESET_HXX
diff --git a/odb/odb/traversal/relational/column.hxx b/odb/odb/traversal/relational/column.hxx
new file mode 100644
index 0000000..b9c586d
--- /dev/null
+++ b/odb/odb/traversal/relational/column.hxx
@@ -0,0 +1,21 @@
+// file : odb/traversal/relational/column.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_RELATIONAL_COLUMN_HXX
+#define ODB_TRAVERSAL_RELATIONAL_COLUMN_HXX
+
+#include <odb/semantics/relational/column.hxx>
+#include <odb/traversal/relational/elements.hxx>
+
+namespace traversal
+{
+ namespace relational
+ {
+ struct column: node<semantics::relational::column> {};
+ struct add_column: node<semantics::relational::add_column> {};
+ struct drop_column: node<semantics::relational::drop_column> {};
+ struct alter_column: node<semantics::relational::alter_column> {};
+ }
+}
+
+#endif // ODB_TRAVERSAL_RELATIONAL_COLUMN_HXX
diff --git a/odb/odb/traversal/relational/elements.hxx b/odb/odb/traversal/relational/elements.hxx
new file mode 100644
index 0000000..2b43ab0
--- /dev/null
+++ b/odb/odb/traversal/relational/elements.hxx
@@ -0,0 +1,162 @@
+// file : odb/traversal/relational/elements.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_RELATIONAL_ELEMENTS_HXX
+#define ODB_TRAVERSAL_RELATIONAL_ELEMENTS_HXX
+
+#include <libcutl/compiler/traversal.hxx>
+#include <odb/semantics/relational/elements.hxx>
+
+namespace traversal
+{
+ namespace relational
+ {
+ using namespace cutl;
+
+ //
+ //
+ typedef compiler::dispatcher<semantics::relational::node> node_dispatcher;
+ typedef compiler::dispatcher<semantics::relational::edge> edge_dispatcher;
+
+ //
+ //
+ struct node_base: node_dispatcher, edge_dispatcher
+ {
+ void
+ edge_traverser (edge_dispatcher& d)
+ {
+ edge_dispatcher::traverser (d);
+ }
+
+ edge_dispatcher&
+ edge_traverser ()
+ {
+ return *this;
+ }
+
+ using node_dispatcher::dispatch;
+ using edge_dispatcher::dispatch;
+
+ using edge_dispatcher::iterate_and_dispatch;
+ };
+
+ struct edge_base: edge_dispatcher, node_dispatcher
+ {
+ void
+ node_traverser (node_dispatcher& d)
+ {
+ node_dispatcher::traverser (d);
+ }
+
+ node_dispatcher&
+ node_traverser ()
+ {
+ return *this;
+ }
+
+ using edge_dispatcher::dispatch;
+ using node_dispatcher::dispatch;
+
+ using node_dispatcher::iterate_and_dispatch;
+ };
+
+ inline edge_base&
+ operator>> (node_base& n, edge_base& e)
+ {
+ n.edge_traverser (e);
+ return e;
+ }
+
+ inline node_base&
+ operator>> (edge_base& e, node_base& n)
+ {
+ e.node_traverser (n);
+ return n;
+ }
+
+ //
+ //
+ template <typename X>
+ struct node: compiler::traverser_impl<X, semantics::relational::node>,
+ virtual node_base
+ {
+ virtual void
+ traverse (X&) {}
+ };
+
+ template <typename X>
+ struct edge: compiler::traverser_impl<X, semantics::relational::edge>,
+ virtual edge_base
+ {
+ };
+
+ //
+ // Edges
+ //
+
+ template <typename N>
+ struct names: edge<semantics::relational::names<N> >
+ {
+ names ()
+ {
+ }
+
+ names (node_dispatcher& n)
+ {
+ this->node_traverser (n);
+ }
+
+ virtual void
+ traverse (semantics::relational::names<N>& e)
+ {
+ this->dispatch (e.nameable ());
+ }
+ };
+
+ typedef names<semantics::relational::uname> unames;
+ typedef names<semantics::relational::qname> qnames;
+
+ //
+ // Nodes
+ //
+
+ template <typename N>
+ struct nameable: node<semantics::relational::nameable<N> > {};
+
+ typedef nameable<semantics::relational::uname> unameable;
+ typedef nameable<semantics::relational::qname> qnameable;
+
+ //
+ //
+ template <typename T>
+ struct scope_template: node<T>
+ {
+ public:
+ virtual void
+ traverse (T& s)
+ {
+ names (s);
+ }
+
+ virtual void
+ names (T& s)
+ {
+ names (s, *this);
+ }
+
+ virtual void
+ names (T& s, edge_dispatcher& d)
+ {
+ this->iterate_and_dispatch (s.names_begin (), s.names_end (), d);
+ }
+ };
+
+ template <typename N>
+ struct scope: scope_template<semantics::relational::scope<N> > {};
+
+ typedef scope<semantics::relational::uname> uscope;
+ typedef scope<semantics::relational::qname> qscope;
+ }
+}
+
+#endif // ODB_TRAVERSAL_RELATIONAL_ELEMENTS_HXX
diff --git a/odb/odb/traversal/relational/foreign-key.hxx b/odb/odb/traversal/relational/foreign-key.hxx
new file mode 100644
index 0000000..b8ccba3
--- /dev/null
+++ b/odb/odb/traversal/relational/foreign-key.hxx
@@ -0,0 +1,21 @@
+// file : odb/traversal/relational/foreign-key.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_RELATIONAL_FOREIGN_KEY_HXX
+#define ODB_TRAVERSAL_RELATIONAL_FOREIGN_KEY_HXX
+
+#include <odb/semantics/relational/foreign-key.hxx>
+#include <odb/traversal/relational/key.hxx>
+
+namespace traversal
+{
+ namespace relational
+ {
+ struct foreign_key: key_template<semantics::relational::foreign_key> {};
+ struct add_foreign_key:
+ key_template<semantics::relational::add_foreign_key> {};
+ struct drop_foreign_key: node<semantics::relational::drop_foreign_key> {};
+ }
+}
+
+#endif // ODB_TRAVERSAL_RELATIONAL_FOREIGN_KEY_HXX
diff --git a/odb/odb/traversal/relational/index.hxx b/odb/odb/traversal/relational/index.hxx
new file mode 100644
index 0000000..2277fee
--- /dev/null
+++ b/odb/odb/traversal/relational/index.hxx
@@ -0,0 +1,20 @@
+// file : odb/traversal/relational/index.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_RELATIONAL_INDEX_HXX
+#define ODB_TRAVERSAL_RELATIONAL_INDEX_HXX
+
+#include <odb/semantics/relational/index.hxx>
+#include <odb/traversal/relational/key.hxx>
+
+namespace traversal
+{
+ namespace relational
+ {
+ struct index: key_template<semantics::relational::index> {};
+ struct add_index: key_template<semantics::relational::add_index> {};
+ struct drop_index: node<semantics::relational::drop_index> {};
+ }
+}
+
+#endif // ODB_TRAVERSAL_RELATIONAL_INDEX_HXX
diff --git a/odb/odb/traversal/relational/key.cxx b/odb/odb/traversal/relational/key.cxx
new file mode 100644
index 0000000..15a131c
--- /dev/null
+++ b/odb/odb/traversal/relational/key.cxx
@@ -0,0 +1,17 @@
+// file : odb/traversal/relational/key.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/traversal/relational/key.hxx>
+#include <odb/traversal/relational/column.hxx>
+
+namespace traversal
+{
+ namespace relational
+ {
+ void contains::
+ traverse (type& c)
+ {
+ dispatch (c.column ());
+ }
+ }
+}
diff --git a/odb/odb/traversal/relational/key.hxx b/odb/odb/traversal/relational/key.hxx
new file mode 100644
index 0000000..d0ba2d7
--- /dev/null
+++ b/odb/odb/traversal/relational/key.hxx
@@ -0,0 +1,50 @@
+// file : odb/traversal/relational/key.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_RELATIONAL_KEY_HXX
+#define ODB_TRAVERSAL_RELATIONAL_KEY_HXX
+
+#include <odb/semantics/relational/key.hxx>
+#include <odb/traversal/relational/elements.hxx>
+
+namespace traversal
+{
+ namespace relational
+ {
+ struct contains: edge<semantics::relational::contains>
+ {
+ contains () {}
+ contains (node_dispatcher& n) {node_traverser (n);}
+
+ virtual void
+ traverse (type&);
+ };
+
+ template <typename T>
+ struct key_template: node<T>
+ {
+ public:
+ virtual void
+ traverse (T& k)
+ {
+ contains (k);
+ }
+
+ virtual void
+ contains (T& k)
+ {
+ contains (k, *this);
+ }
+
+ virtual void
+ contains (T& k, edge_dispatcher& d)
+ {
+ this->iterate_and_dispatch (k.contains_begin (), k.contains_end (), d);
+ }
+ };
+
+ struct key: key_template<semantics::relational::key> {};
+ }
+}
+
+#endif // ODB_TRAVERSAL_RELATIONAL_KEY_HXX
diff --git a/odb/odb/traversal/relational/model.hxx b/odb/odb/traversal/relational/model.hxx
new file mode 100644
index 0000000..03d70b1
--- /dev/null
+++ b/odb/odb/traversal/relational/model.hxx
@@ -0,0 +1,18 @@
+// file : odb/traversal/relational/model.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_RELATIONAL_MODEL_HXX
+#define ODB_TRAVERSAL_RELATIONAL_MODEL_HXX
+
+#include <odb/semantics/relational/model.hxx>
+#include <odb/traversal/relational/elements.hxx>
+
+namespace traversal
+{
+ namespace relational
+ {
+ struct model: scope_template<semantics::relational::model> {};
+ }
+}
+
+#endif // ODB_TRAVERSAL_RELATIONAL_MODEL_HXX
diff --git a/odb/odb/traversal/relational/primary-key.hxx b/odb/odb/traversal/relational/primary-key.hxx
new file mode 100644
index 0000000..dcefab6
--- /dev/null
+++ b/odb/odb/traversal/relational/primary-key.hxx
@@ -0,0 +1,18 @@
+// file : odb/traversal/relational/primary-key.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_RELATIONAL_PRIMARY_KEY_HXX
+#define ODB_TRAVERSAL_RELATIONAL_PRIMARY_KEY_HXX
+
+#include <odb/semantics/relational/primary-key.hxx>
+#include <odb/traversal/relational/key.hxx>
+
+namespace traversal
+{
+ namespace relational
+ {
+ struct primary_key: key_template<semantics::relational::primary_key> {};
+ }
+}
+
+#endif // ODB_TRAVERSAL_RELATIONAL_PRIMARY_KEY_HXX
diff --git a/odb/odb/traversal/relational/table.hxx b/odb/odb/traversal/relational/table.hxx
new file mode 100644
index 0000000..d80e37e
--- /dev/null
+++ b/odb/odb/traversal/relational/table.hxx
@@ -0,0 +1,21 @@
+// file : odb/traversal/relational/table.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_RELATIONAL_TABLE_HXX
+#define ODB_TRAVERSAL_RELATIONAL_TABLE_HXX
+
+#include <odb/semantics/relational/table.hxx>
+#include <odb/traversal/relational/elements.hxx>
+
+namespace traversal
+{
+ namespace relational
+ {
+ struct table: scope_template<semantics::relational::table> {};
+ struct add_table: scope_template<semantics::relational::add_table> {};
+ struct drop_table: node<semantics::relational::drop_table> {};
+ struct alter_table: scope_template<semantics::relational::alter_table> {};
+ }
+}
+
+#endif // ODB_TRAVERSAL_RELATIONAL_TABLE_HXX
diff --git a/odb/odb/traversal/template.cxx b/odb/odb/traversal/template.cxx
new file mode 100644
index 0000000..21a846d
--- /dev/null
+++ b/odb/odb/traversal/template.cxx
@@ -0,0 +1,53 @@
+// file : odb/traversal/template.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/traversal/template.hxx>
+
+namespace traversal
+{
+ void instantiates::
+ traverse (type& i)
+ {
+ dispatch (i.template_ ());
+ }
+
+ //
+ //
+ void instantiation::
+ traverse (type& i)
+ {
+ instantiates (i);
+ }
+
+ void instantiation::
+ instantiates (type& i)
+ {
+ instantiates (i, *this);
+ }
+
+ void instantiation::
+ instantiates (type& i, edge_dispatcher& d)
+ {
+ d.dispatch (i.instantiates ());
+ }
+
+ //
+ //
+ void type_instantiation::
+ traverse (type& i)
+ {
+ instantiates (i);
+ }
+
+ void type_instantiation::
+ instantiates (type& i)
+ {
+ instantiates (i, *this);
+ }
+
+ void type_instantiation::
+ instantiates (type& i, edge_dispatcher& d)
+ {
+ d.dispatch (i.instantiates ());
+ }
+}
diff --git a/odb/odb/traversal/template.hxx b/odb/odb/traversal/template.hxx
new file mode 100644
index 0000000..28a64d5
--- /dev/null
+++ b/odb/odb/traversal/template.hxx
@@ -0,0 +1,56 @@
+// file : odb/traversal/template.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_TEMPLATE_HXX
+#define ODB_TRAVERSAL_TEMPLATE_HXX
+
+#include <odb/semantics/template.hxx>
+#include <odb/traversal/elements.hxx>
+
+namespace traversal
+{
+ struct instantiates: edge<semantics::instantiates>
+ {
+ instantiates ()
+ {
+ }
+
+ instantiates (node_dispatcher& n)
+ {
+ node_traverser (n);
+ }
+
+ virtual void
+ traverse (type&);
+ };
+
+ struct template_: node<semantics::template_> {};
+
+ struct instantiation: node<semantics::instantiation>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ instantiates (type&);
+
+ virtual void
+ instantiates (type&, edge_dispatcher&);
+ };
+
+ struct type_template: node<semantics::type_template> {};
+
+ struct type_instantiation: node<semantics::type_instantiation>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ instantiates (type&);
+
+ virtual void
+ instantiates (type&, edge_dispatcher&);
+ };
+}
+
+#endif // ODB_TRAVERSAL_TEMPLATE_HXX
diff --git a/odb/odb/traversal/union-template.cxx b/odb/odb/traversal/union-template.cxx
new file mode 100644
index 0000000..9d72fcd
--- /dev/null
+++ b/odb/odb/traversal/union-template.cxx
@@ -0,0 +1,26 @@
+// file : odb/traversal/union-template.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/traversal/union-template.hxx>
+
+namespace traversal
+{
+ void union_instantiation::
+ traverse (type& u)
+ {
+ instantiates (u);
+ names (u);
+ }
+
+ void union_instantiation::
+ instantiates (type& u)
+ {
+ instantiates (u, *this);
+ }
+
+ void union_instantiation::
+ instantiates (type& u, edge_dispatcher& d)
+ {
+ d.dispatch (u.instantiates ());
+ }
+}
diff --git a/odb/odb/traversal/union-template.hxx b/odb/odb/traversal/union-template.hxx
new file mode 100644
index 0000000..10d1d49
--- /dev/null
+++ b/odb/odb/traversal/union-template.hxx
@@ -0,0 +1,29 @@
+// file : odb/traversal/union-template.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_UNION_TEMPLATE_HXX
+#define ODB_TRAVERSAL_UNION_TEMPLATE_HXX
+
+#include <odb/semantics/union-template.hxx>
+
+#include <odb/traversal/elements.hxx>
+#include <odb/traversal/union.hxx>
+
+namespace traversal
+{
+ struct union_template: scope_template<semantics::union_template> {};
+
+ struct union_instantiation: scope_template<semantics::union_instantiation>
+ {
+ virtual void
+ traverse (type&);
+
+ virtual void
+ instantiates (type&);
+
+ virtual void
+ instantiates (type&, edge_dispatcher&);
+ };
+}
+
+#endif // ODB_TRAVERSAL_UNION_TEMPLATE_HXX
diff --git a/odb/odb/traversal/union.hxx b/odb/odb/traversal/union.hxx
new file mode 100644
index 0000000..2d1a7af
--- /dev/null
+++ b/odb/odb/traversal/union.hxx
@@ -0,0 +1,15 @@
+// file : odb/traversal/union.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_UNION_HXX
+#define ODB_TRAVERSAL_UNION_HXX
+
+#include <odb/semantics/union.hxx>
+#include <odb/traversal/elements.hxx>
+
+namespace traversal
+{
+ struct union_: scope_template<semantics::union_> {};
+}
+
+#endif // ODB_TRAVERSAL_UNION_HXX
diff --git a/odb/odb/traversal/unit.hxx b/odb/odb/traversal/unit.hxx
new file mode 100644
index 0000000..a90418a
--- /dev/null
+++ b/odb/odb/traversal/unit.hxx
@@ -0,0 +1,15 @@
+// file : odb/traversal/unit.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_TRAVERSAL_UNIT_HXX
+#define ODB_TRAVERSAL_UNIT_HXX
+
+#include <odb/semantics/unit.hxx>
+#include <odb/traversal/elements.hxx>
+
+namespace traversal
+{
+ struct unit: scope_template<semantics::unit> {};
+}
+
+#endif // ODB_TRAVERSAL_UNIT_HXX
diff --git a/odb/odb/validator.cxx b/odb/odb/validator.cxx
new file mode 100644
index 0000000..aa45294
--- /dev/null
+++ b/odb/odb/validator.cxx
@@ -0,0 +1,1917 @@
+// file : odb/validator.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <set>
+#include <iostream>
+
+#include <odb/traversal.hxx>
+#include <odb/common.hxx>
+#include <odb/context.hxx>
+#include <odb/diagnostics.hxx>
+#include <odb/validator.hxx>
+#include <odb/cxx-lexer.hxx>
+
+#include <odb/relational/validator.hxx>
+
+using namespace std;
+
+namespace
+{
+ // Resolve null overrides.
+ //
+ static void
+ override_null (semantics::node& n, string const& prefix = "")
+ {
+ string p (prefix.empty () ? prefix : prefix + '-');
+
+ if (n.count (p + "null") && n.count (p + "not-null"))
+ {
+ if (n.get<location_t> (p + "null-location") <
+ n.get<location_t> (p + "not-null-location"))
+ {
+ n.remove (p + "null");
+ n.remove (p + "null-location");
+ }
+ else
+ {
+ n.remove (p + "not-null");
+ n.remove (p + "not-null-location");
+ }
+ }
+ }
+
+ //
+ // Pass 1.
+ //
+
+ struct data_member1: traversal::data_member, context
+ {
+ data_member1 (bool& valid)
+ : valid_ (valid)
+ {
+ }
+
+ virtual void
+ traverse (type& m)
+ {
+ semantics::class_& c (dynamic_cast<semantics::class_&> (m.scope ()));
+ bool obj (object (c));
+
+ // If the class is marked transient, then mark each non-virtual
+ // data member as transient.
+ //
+ {
+ bool t (transient (m));
+
+ if (!t && c.count ("transient") && !m.count ("virtual"))
+ {
+ m.set ("transient", true);
+ t = true;
+ }
+
+ if (t)
+ return;
+ }
+
+ semantics::names* hint;
+ semantics::type& t (utype (m, hint));
+
+ if (t.fq_anonymous (hint))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: unnamed type in data member declaration" << endl;
+
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " info: use 'typedef' to name this type" << endl;
+
+ valid_ = false;
+ }
+
+ // Make sure id or inverse member is not marked readonly since we
+ // depend on these three sets not having overlaps.
+ //
+ if (m.count ("readonly")) // context::readonly() also checks the class.
+ {
+ if (id (m))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: object id should not be declared readonly" << endl;
+
+ valid_ = false;
+ }
+
+ if (inverse (m))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: inverse object pointer should not be declared "
+ << "readonly" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ // Make sure a member of a section is an immediate member of an object.
+ // The same for the section member itself.
+ //
+ bool section (t.fq_name () == "::odb::section");
+
+ if (!obj)
+ {
+ if (m.count ("section-member"))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ": " <<
+ "error: data member belonging to a section can only be a " <<
+ "direct member of a persistent class" << endl;
+ valid_ = false;
+ }
+
+ if (section)
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ": " <<
+ "error: section data member can only be a direct member of a " <<
+ "persistent class" << endl;
+ valid_ = false;
+ }
+ }
+
+ // Make sure the load and update pragmas are only specified on
+ // section members.
+ //
+ if (!section)
+ {
+ if (m.count ("section-load"))
+ {
+ location_t loc (m.get<location_t> ("section-load-location"));
+ error (loc) << "'#pragma db load' can only be specified for "
+ "a section data member" << endl;
+ valid_ = false;
+ }
+
+ if (m.count ("section-update"))
+ {
+ location_t loc (m.get<location_t> ("section-update-location"));
+ error (loc) << "'#pragma db update' can only be specified for "
+ "a section data member" << endl;
+ valid_ = false;
+ }
+ }
+
+ // Check that the addition version makes sense.
+ //
+ unsigned long long av (m.get<unsigned long long> ("added", 0));
+ if (av != 0)
+ {
+ location_t l (m.get<location_t> ("added-location"));
+
+ if (id (m))
+ {
+ error (l) << "object id cannod be soft-added" << endl;
+ valid_ = false;
+ }
+
+ if (version (m))
+ {
+ error (l) << "optimistic concurrency version cannod be "
+ "soft-added" << endl;
+ valid_ = false;
+ }
+
+ if (!versioned ())
+ {
+ error (l) << "added data member in a non-versioned object " <<
+ "model" << endl;
+ valid_ = false;
+ }
+ else
+ {
+ model_version const& mv (version ());
+
+ if (av > mv.current)
+ {
+ error (l) << "addition version is greater than the current " <<
+ "model version" << endl;
+ valid_ = false;
+ }
+ else if (av <= mv.base)
+ {
+ error (l) << "addition version is less than or equal to the " <<
+ "base model version" << endl;
+ info (l) << "delete this pragma since migration to version " <<
+ av << " is no longer possible" << endl;
+ valid_ = false;
+ }
+ }
+ }
+
+ // Check that the deletion version makes sense.
+ //
+ unsigned long long dv (m.get<unsigned long long> ("deleted", 0));
+ if (dv != 0)
+ {
+ location_t l (m.get<location_t> ("deleted-location"));
+
+ if (id (m))
+ {
+ error (l) << "object id cannod be soft-deleted" << endl;
+ valid_ = false;
+ }
+
+ if (version (m))
+ {
+ error (l) << "optimistic concurrency version cannod be "
+ "soft-deleted" << endl;
+ valid_ = false;
+ }
+
+ if (!versioned ())
+ {
+ error (l) << "deleted data member in a non-versioned object " <<
+ "model" << endl;
+ valid_ = false;
+ }
+ else
+ {
+ model_version const& mv (version ());
+
+ if (dv > mv.current)
+ {
+ error (l) << "deletion version is greater than the current " <<
+ "model version" << endl;
+ valid_ = false;
+ }
+ else if (dv <= mv.base)
+ {
+ error (l) << "deletion version is less than or equal to the " <<
+ "base model version" << endl;
+ info (c.location ()) << "delete this data member since " <<
+ "migration to version " << dv << " is no longer possible" <<
+ endl;
+ valid_ = false;
+ }
+ }
+ }
+
+ // Make sure that addition and deletion versions are properly ordered.
+ // We can have both the [av, dv] (added then deleted) and [dv, av]
+ // (deleted then re-added) intervals.
+ //
+ if (av != 0 && dv != 0 && av == dv)
+ {
+ location_t al (m.get<location_t> ("added-location"));
+ location_t dl (m.get<location_t> ("deleted-location"));
+
+ error (al) << "addition and deletion versions are the same" << endl;
+ info (dl) << "deletion version is specified here" << endl;
+ valid_ = false;
+ }
+
+ if (section)
+ return; // Section data member is transient.
+
+ // Resolve null overrides.
+ //
+ override_null (m);
+ override_null (m, "value");
+ }
+
+ bool& valid_;
+ };
+
+ // Find special members (id, version).
+ //
+ struct special_members: traversal::class_, context
+ {
+ special_members (class_kind_type kind,
+ bool& valid,
+ semantics::data_member*& id,
+ semantics::data_member*& optimistic)
+ : kind_ (kind), member_ (valid, id, optimistic)
+ {
+ if (kind != class_view)
+ *this >> inherits_ >> *this;
+
+ *this >> names_ >> member_;
+ }
+
+ virtual void
+ traverse (semantics::class_& c)
+ {
+ // Skip transient bases.
+ //
+ switch (kind_)
+ {
+ case class_object:
+ {
+ if (!object (c))
+ return;
+ break;
+ }
+ case class_view:
+ {
+ break;
+ }
+ case class_composite:
+ {
+ if (!composite (c))
+ return;
+ break;
+ }
+ case class_other:
+ {
+ assert (false);
+ break;
+ }
+ }
+
+ // Views don't have bases.
+ //
+ if (kind_ != class_view)
+ inherits (c);
+
+ names (c);
+ }
+
+ private:
+ struct member: traversal::data_member, context
+ {
+ member (bool& valid,
+ semantics::data_member*& id,
+ semantics::data_member*& optimistic)
+ : valid_ (valid), id_ (id), optimistic_ (optimistic)
+ {
+ }
+
+ virtual void
+ traverse (semantics::data_member& m)
+ {
+ if (m.count ("id"))
+ {
+ if (id_ == 0)
+ id_ = &m;
+ else
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: multiple object id members" << endl;
+
+ os << id_->file () << ":" << id_->line () << ":" << id_->column ()
+ << ": info: previous id member is declared here" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ if (version (m))
+ {
+ if (optimistic_ == 0)
+ optimistic_ = &m;
+ else
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: multiple version members" << endl;
+
+ semantics::data_member& o (*optimistic_);
+
+ os << o.file () << ":" << o.line () << ":" << o.column ()
+ << ": info: previous version member is declared here" << endl;
+
+ valid_ = false;
+ }
+ }
+ }
+
+ bool& valid_;
+ semantics::data_member*& id_;
+ semantics::data_member*& optimistic_;
+ };
+
+ class_kind_type kind_;
+ member member_;
+ traversal::names names_;
+ traversal::inherits inherits_;
+ };
+
+ //
+ //
+ struct value_type: traversal::type, context
+ {
+ virtual void
+ traverse (semantics::type& t)
+ {
+ // Resolve null overrides.
+ //
+ override_null (t);
+ override_null (t, "value");
+ }
+ };
+
+ struct typedefs1: typedefs
+ {
+ typedefs1 (traversal::declares& d)
+ : typedefs (true), declares_ (d)
+ {
+ }
+
+ void
+ traverse (semantics::typedefs& t)
+ {
+ if (check (t))
+ traversal::typedefs::traverse (t);
+ else
+ declares_.traverse (t);
+ }
+
+ private:
+ traversal::declares& declares_;
+ };
+
+ //
+ //
+ struct class1: traversal::class_, context
+ {
+ class1 (bool& valid)
+ : valid_ (valid), typedefs_ (declares_), member_ (valid)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+ *this >> declares_ >> vt_;
+
+ names_member_ >> member_;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck != class_other)
+ {
+ for (type::inherits_iterator i (c.inherits_begin ());
+ i != c.inherits_end (); ++i)
+ {
+ type& b (i->base ());
+
+ if (class_kind (b) == class_other)
+ continue;
+
+ location_t cl;
+ location_t bl;
+
+ // If we are in the same file, then use real locations (as
+ // opposed to definition locations) since that's the order
+ // in which we will be generating the code.
+ //
+ if (class_file (c) == class_file (b))
+ {
+ cl = class_real_location (c);
+ bl = class_real_location (b);
+ }
+ else
+ {
+ cl = class_location (c);
+ bl = class_location (b);
+ }
+
+ if (cl < bl)
+ {
+ // We cannot use class_name() for b since it might not have
+ // yet been processed (tree-hint).
+ //
+ error (cl) << "base class " << b.name () << " must "
+ << "be defined before derived class " << class_name (c)
+ << endl;
+
+ info (bl) << "class " << b.name () << " is define here"
+ << endl;
+
+ valid_ = false;
+ }
+ }
+ }
+
+ switch (ck)
+ {
+ case class_object:
+ names (c);
+ traverse_object (c);
+ break;
+ case class_view:
+ names (c);
+ traverse_view (c);
+ break;
+ case class_composite:
+ names (c);
+ traverse_composite (c);
+ // Fall through.
+ case class_other:
+ vt_.dispatch (c);
+ break;
+ }
+ }
+
+ virtual void
+ traverse_object (type& c)
+ {
+ // Check that the deletion version makes sense.
+ //
+ if (unsigned long long v = c.get<unsigned long long> ("deleted", 0))
+ {
+ location_t l (c.get<location_t> ("deleted-location"));
+
+ if (!versioned ())
+ {
+ error (l) << "deleted class in a non-versioned object model" << endl;
+ valid_ = false;
+ }
+ else
+ {
+ model_version const& mv (version ());
+
+ if (v > mv.current)
+ {
+ error (l) << "deletion version is greater than the current " <<
+ "model version" << endl;
+ valid_ = false;
+ }
+ else if (v <= mv.base)
+ {
+ error (l) << "deletion version is less than or equal to the " <<
+ "base model version" << endl;
+ info (c.location ()) << "delete this class since migration to " <<
+ "version " << v << " is no longer possible" << endl;
+ valid_ = false;
+ }
+ }
+ }
+
+ // Check that the callback function exist.
+ //
+ if (c.count ("callback"))
+ {
+ string name (c.get<string> ("callback"));
+ tree decl (
+ lookup_qualified_name (
+ c.tree_node (), get_identifier (name.c_str ()), false, false));
+
+ if (decl != error_mark_node && TREE_CODE (decl) == BASELINK)
+ {
+ // Figure out if we have a const version of the callback. OVL_*
+ // macros work for both FUNCTION_DECL and OVERLOAD.
+ //
+#if BUILDING_GCC_MAJOR >= 8
+ for (ovl_iterator i (BASELINK_FUNCTIONS (decl)); i; ++i)
+#else
+ for (tree o (BASELINK_FUNCTIONS (decl)); o != 0; o = OVL_NEXT (o))
+#endif
+ {
+#if BUILDING_GCC_MAJOR >= 8
+ tree f (*i);
+#else
+ tree f (OVL_CURRENT (o));
+#endif
+ if (DECL_CONST_MEMFUNC_P (f))
+ {
+ c.set ("callback-const", true);
+ break;
+ }
+ }
+
+ //@@ Would be nice to check the signature of the function(s)
+ // instead of postponing it until the C++ compilation. Though
+ // we may still get C++ compilation errors because of const
+ // mismatch.
+ //
+ }
+ else
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ": "
+ << "error: unable to resolve member function '" << name << "' "
+ << "specified with '#pragma db callback' for class '"
+ << class_name (c) << "'" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ // Check bases.
+ //
+ type* poly_root (0);
+
+ for (type::inherits_iterator i (c.inherits_begin ());
+ i != c.inherits_end ();
+ ++i)
+ {
+ type& b (i->base ());
+
+ if (object (b))
+ {
+ if (type* r = polymorphic (b))
+ {
+ if (poly_root == 0)
+ {
+ poly_root = r;
+ c.set ("polymorphic-base", &static_cast<semantics::class_&> (b));
+ }
+ // If poly_root and r are the same, then we have virtual
+ // inheritance. Though we don't support it at the moment.
+ //
+ else //if (poly_root != r)
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: persistent class '" << class_name (c) << "' "
+ << "derives from multiple polymorphic bases" << endl;
+
+ type& a (*poly_root);
+ os << a.file () << ":" << a.line () << ":" << a.column () << ":"
+ << " info: first polymorphic base defined here" << endl;
+
+ type& b (*r);
+ os << b.file () << ":" << b.line () << ":" << b.column () << ":"
+ << " info: second polymorphic base defined here" << endl;
+
+ valid_ = false;
+ }
+ }
+ }
+ else if (view (b) || composite (b))
+ {
+ // @@ Should we use hint here?
+ //
+ string name (class_fq_name (b));
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: base class '" << name << "' is a view or value type"
+ << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: object types cannot derive from view or value "
+ << "types"
+ << endl;
+
+ os << b.file () << ":" << b.line () << ":" << b.column () << ":"
+ << " info: class '" << name << "' is defined here" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ // Check members.
+ //
+ names (c, names_member_);
+
+ // Check special members.
+ //
+ using semantics::data_member;
+
+ data_member* id (0);
+ data_member* optimistic (0);
+ {
+ special_members t (class_object, valid_, id, optimistic);
+ t.traverse (c);
+ }
+
+ if (id == 0)
+ {
+ // An object without an id should either be reuse-abstract
+ // or explicitly marked as such. We check that it is not
+ // polymorphic below.
+ //
+ if (!(c.count ("id") || abstract (c)))
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: no data member designated as an object id" << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: use '#pragma db id' to specify an object id member"
+ << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: or explicitly declare that this persistent class "
+ << "has no object id with '#pragma db object no_id'" << endl;
+
+ valid_ = false;
+ }
+ }
+ else
+ {
+ // Convert id to a member path. This has to happen early since
+ // a lot of code that runs next (e.g., processor, pass 1) depends
+ // on this information being available.
+ //
+ data_member_path& idp (c.set ("id-member", data_member_path ()));
+ idp.push_back (id);
+
+ // See if we have a member path that we need to resolve.
+ //
+ const string& name (id->get<string> ("id"));
+ location_t l (id->get<location_t> ("id-location"));
+
+ if (!name.empty ())
+ {
+ if (id->count ("auto"))
+ {
+ error (l) << "nested id cannot be automatically assigned" << endl;
+ valid_ = false;
+ }
+
+ if (semantics::class_* comp = utype (*id).is_a<semantics::class_> ())
+ {
+ try
+ {
+ resolve_data_members (idp, *comp, name, l, lex_);
+ }
+ catch (const operation_failed&) {valid_ = false;}
+ }
+ else
+ {
+ error (l) << "nested id requires composite member" << endl;
+ valid_ = false;
+ }
+
+ // Mark the whole member as readonly.
+ //
+ id->set ("readonly", true);
+ }
+
+ data_member* idf (idp.front ());
+ data_member* idb (idp.back ());
+
+ // Complain if an id member has a default value (default value
+ // for the id's type is ok -- we will ignore it).
+ //
+ if (idb->count ("default"))
+ {
+ error (l) << "object id member cannot have default value" << endl;
+ valid_ = false;
+ }
+
+ // Complain if an id member is in a section.
+ //
+ if (idf->count ("section-member"))
+ {
+ error (l) << "object id member cannot be in a section" << endl;
+ valid_ = false;
+ }
+
+ // Automatically mark the id member as not null. If we already have
+ // an explicit null pragma for this member, issue an error.
+ //
+ if (idb->count ("null"))
+ {
+ error (l) << "object id member cannot be null" << endl;
+ valid_ = false;
+ }
+ else
+ idf->set ("not-null", true);
+ }
+
+ if (optimistic != 0)
+ {
+ semantics::data_member& m (*optimistic);
+
+ // Make sure we have the class declared optimistic.
+ //
+ if (&m.scope () == &c && !c.count ("optimistic"))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: version data member in a class not declared "
+ << "optimistic" << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: use '#pragma db optimistic' to declare this "
+ << "class optimistic" << endl;
+
+ valid_ = false;
+ }
+
+ // Make sure we have object id.
+ //
+ if (id == 0)
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: optimistic class without an object id" << endl;
+
+ valid_ = false;
+ }
+
+ // Make sure id and version members are in the same class. The
+ // current architecture relies on that.
+ //
+ if (id != 0 && &id->scope () != &m.scope ())
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: object id and version members are in different "
+ << "classes" << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: object id and version members must be in the same "
+ << "class" << endl;
+
+ os << id->file () << ":" << id->line () << ":" << id->column ()
+ << ": info: object id member is declared here" << endl;
+
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: version member is declared here" << endl;
+
+ valid_ = false;
+ }
+
+ // Make sure this class is not readonly.
+ //
+ if (readonly (c))
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: optimistic class cannot be readonly" << endl;
+
+ valid_ = false;
+ }
+
+ // Complain if the version member is in a section.
+ //
+ if (m.count ("section-member"))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: version member cannot be in a section" << endl;
+ valid_ = false;
+ }
+
+ // This takes care of also marking derived classes as optimistic.
+ //
+ c.set ("optimistic-member", optimistic);
+ }
+ else
+ {
+ // Make sure there is a version member if the class declared
+ // optimistic.
+ //
+ if (c.count ("optimistic"))
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: optimistic class without a version member" << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: use '#pragma db version' to declare on of the "
+ << "data members as a version" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ // Polymorphic inheritance.
+ //
+ if (c.count ("polymorphic") && poly_root == 0)
+ {
+ // Root of the hierarchy.
+ //
+
+ if (id == 0)
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: polymorphic class without an object id" << endl;
+
+ valid_ = false;
+ }
+
+ if (!TYPE_POLYMORPHIC_P (c.tree_node ()))
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: non-polymorphic class (class without virtual "
+ << "functions) cannot be declared polymorphic" << endl;
+
+ valid_ = false;
+ }
+
+ poly_root = &c;
+ }
+
+ if (poly_root != 0)
+ c.set ("polymorphic-root", poly_root);
+
+ // Sectionable objects.
+ //
+ if (c.count ("sectionable"))
+ {
+ if (optimistic == 0)
+ {
+ location_t l (c.get<location_t> ("sectionable-location"));
+ error (l) << "only optimistic class can be sectionable" << endl;
+ valid_ = false;
+ }
+ else if (&optimistic->scope () != &c && poly_root != &c)
+ {
+ location l (c.get<location_t> ("sectionable-location"));
+ error (l) << "only optimistic class that declares the version " <<
+ "data member or that is a root of a polymorphic hierarchy can " <<
+ "be sectionable" << endl;
+ info (optimistic->location ()) << "version member is declared " <<
+ "here" << endl;
+ valid_ = false;
+ }
+ }
+
+ // Update features set based on this object.
+ //
+ if (options.at_once () || class_file (c) == unit.file ())
+ {
+ if (poly_root != 0)
+ features.polymorphic_object = true;
+ else if (id == 0 && !abstract (c))
+ features.no_id_object = true;
+ else
+ features.simple_object = true;
+ }
+ }
+
+ virtual void
+ traverse_view (type& c)
+ {
+ // Views require query support.
+ //
+ if (!options.generate_query ())
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: query support is required when using views"
+ << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: use the --generate-query option to enable query "
+ << "support"
+ << endl;
+
+ valid_ = false;
+ }
+
+ // Check that the callback function exist.
+ //
+ if (c.count ("callback"))
+ {
+ string name (c.get<string> ("callback"));
+ tree decl (
+ lookup_qualified_name (
+ c.tree_node (), get_identifier (name.c_str ()), false, false));
+
+ if (decl == error_mark_node || TREE_CODE (decl) != BASELINK)
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ": "
+ << "error: unable to resolve member function '" << name << "' "
+ << "specified with '#pragma db callback' for class '"
+ << class_name (c) << "'" << endl;
+
+ valid_ = false;
+ }
+
+ // No const version for views.
+
+ //@@ Would be nice to check the signature of the function(s)
+ // instead of postponing it until the C++ compilation. Though
+ // we may still get C++ compilation errors because of const
+ // mismatch.
+ //
+ }
+
+ // Check bases.
+ //
+ for (type::inherits_iterator i (c.inherits_begin ());
+ i != c.inherits_end ();
+ ++i)
+ {
+ type& b (i->base ());
+
+ if (object (b) || view (b) || composite (b))
+ {
+ // @@ Should we use hint here?
+ //
+ string name (class_fq_name (b));
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: base class '" << name << "' is an object, "
+ << "view, or value type"
+ << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: view types cannot derive from view, object or "
+ << "value types"
+ << endl;
+
+ os << b.file () << ":" << b.line () << ":" << b.column () << ":"
+ << " info: class '" << name << "' is defined here" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ // Check members.
+ //
+ names (c, names_member_);
+
+ // Check id.
+ //
+ semantics::data_member* id (0);
+ semantics::data_member* optimistic (0);
+ {
+ special_members t (class_view, valid_, id, optimistic);
+ t.traverse (c);
+ }
+
+ if (id != 0)
+ {
+ os << id->file () << ":" << id->line () << ":" << id->column ()
+ << ": error: view type data member cannot be designated as an "
+ << "object id" << endl;
+
+ valid_ = false;
+ }
+
+ if (optimistic != 0)
+ {
+ semantics::data_member& o (*optimistic);
+
+ os << o.file () << ":" << o.line () << ":" << o.column ()
+ << ": error: view type data member cannot be designated as a "
+ << "version" << endl;
+
+ valid_ = false;
+ }
+
+ // Update features set based on this view.
+ //
+ if (options.at_once () || class_file (c) == unit.file ())
+ {
+ features.view = true;
+ }
+ }
+
+ virtual void
+ traverse_composite (type& c)
+ {
+ for (type::inherits_iterator i (c.inherits_begin ());
+ i != c.inherits_end ();
+ ++i)
+ {
+ type& b (i->base ());
+
+ if (object (b) || view (b))
+ {
+ // @@ Should we use hint here?
+ //
+ string name (class_fq_name (b));
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: base class '" << name << "' is a view or object "
+ << "type"
+ << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " info: composite value types cannot derive from object "
+ << "or view types" << endl;
+
+ os << b.file () << ":" << b.line () << ":" << b.column () << ":"
+ << " info: class '" << name << "' is defined here" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ // Check members.
+ //
+ names (c, names_member_);
+
+ // Check id.
+ //
+ semantics::data_member* id (0);
+ semantics::data_member* optimistic (0);
+ {
+ special_members t (class_composite, valid_, id, optimistic);
+ t.traverse (c);
+ }
+
+ if (id != 0)
+ {
+ os << id->file () << ":" << id->line () << ":" << id->column ()
+ << ": error: value type data member cannot be designated as an "
+ << "object id" << endl;
+
+ valid_ = false;
+ }
+
+ if (optimistic != 0)
+ {
+ semantics::data_member& o (*optimistic);
+
+ os << o.file () << ":" << o.line () << ":" << o.column ()
+ << ": error: value type data member cannot be designated as a "
+ << "version" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ bool& valid_;
+
+ traversal::defines defines_;
+ traversal::declares declares_;
+ typedefs1 typedefs_;
+
+ value_type vt_;
+ data_member1 member_;
+ traversal::names names_member_;
+
+ cxx_string_lexer lex_;
+ };
+
+ //
+ // Pass 2.
+ //
+
+ struct data_member2: traversal::data_member, context
+ {
+ data_member2 (bool& valid): valid_ (valid) {}
+
+ // Verify that composite value 'c' that is used by data member
+ // 'm' is defined before (or inside) class 's'.
+ //
+ void
+ verify_composite_location (semantics::class_& c,
+ semantics::class_& s,
+ semantics::data_member& m)
+ {
+ if (c.in_scope (s))
+ return;
+
+ location_t cl;
+ location_t sl;
+
+ // If we are in the same file, then use real locations (as
+ // opposed to definition locations) since that's the order
+ // in which we will be generating the code.
+ //
+ if (class_file (c) == class_file (s))
+ {
+ cl = class_real_location (c);
+ sl = class_real_location (s);
+ }
+ else
+ {
+ cl = class_location (c);
+ sl = class_location (s);
+ }
+
+ if (sl < cl)
+ {
+ const string& cn (class_name (c));
+
+ error (sl)
+ << "composite value type " << class_fq_name (c) << " must "
+ << "be defined before being used in class " << class_fq_name (s)
+ << endl;
+
+ info (m.location ())
+ << "data member " << m.name () << " uses " << cn << endl;
+
+ info (cl) << "class " << cn << " is define here" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ virtual void
+ traverse (type& m)
+ {
+ using semantics::class_;
+
+ semantics::type& t (utype (m));
+ class_& s (dynamic_cast<class_&> (m.scope ()));
+
+ // Validate pointed-to objects.
+ //
+ class_* c;
+ if ((c = object_pointer (t)) || (c = points_to (m)))
+ {
+ bool poly (polymorphic (*c));
+ bool abst (abstract (*c));
+
+ // Make sure the pointed-to class is complete.
+ //
+ if (!c->complete ())
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ": "
+ << "error: pointed-to class '" << class_fq_name (*c) << "' "
+ << "is incomplete" << endl;
+
+ os << c->file () << ":" << c->line () << ":" << c->column () << ": "
+ << "info: class '" << class_name (*c) << "' is declared here"
+ << endl;
+
+ os << c->file () << ":" << c->line () << ":" << c->column () << ": "
+ << "info: consider including its definition with the "
+ << "--odb-epilogue option" << endl;
+
+ valid_ = false;
+ return;
+ }
+
+ // Make sure the pointed-to class is not reuse-abstract.
+ //
+ if (abst && !poly)
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ": "
+ << "error: pointed-to class '" << class_fq_name (*c) << "' "
+ << "is abstract" << endl;
+
+ os << c->file () << ":" << c->line () << ":" << c->column () << ": "
+ << "info: class '" << class_name (*c) << "' is defined here"
+ << endl;
+
+ throw operation_failed ();
+ }
+
+ // Make sure the pointed-to class has object id unless it is in a
+ // view where we can load no-id objects.
+ //
+ if (data_member_path* id = id_member (*c))
+ {
+ semantics::type& idt (utype (*id));
+
+ // Make sure composite id is defined before this pointer. Failed
+ // that we get non-obvious C++ compiler errors in generated code.
+ //
+ if (class_* comp = composite_wrapper (idt))
+ verify_composite_location (*comp, s, m);
+ }
+ else if (!view_member (m))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ": "
+ << "error: pointed-to class '" << class_fq_name (*c) << "' "
+ << "has no object id" << endl;
+
+ os << c->file () << ":" << c->line () << ":" << c->column () << ": "
+ << "info: class '" << class_name (*c) << "' is defined here"
+ << endl;
+
+ valid_ = false;
+ return;
+ }
+
+ // Make sure the pointed-to class has a default ctor. Since we will
+ // use database::load() in the generated code, lack of a default ctor
+ // will lead to uncompilable generated code. Poly-abstract is Ok.
+ //
+ if (!c->default_ctor () && !(abst && poly))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ": "
+ << "error: pointed-to class '" << class_fq_name (*c) << "' "
+ << "has no default constructor" << endl;
+
+ os << c->file () << ":" << c->line () << ":" << c->column () << ": "
+ << "info: class '" << class_name (*c) << "' is defined here"
+ << endl;
+
+ valid_ = false;
+ return;
+ }
+ }
+
+ // Make sure composite type is defined before (or inside)
+ // this class. Failed that we get non-obvious C++ compiler
+ // errors in generated code.
+ //
+ if (class_* comp = composite_wrapper (t))
+ verify_composite_location (*comp, s, m);
+
+ // Check that containers with composite value element types don't have
+ // any nested containers.
+ //
+ if (semantics::type* c = container (m))
+ {
+ class_* vt (0);
+ class_* kt (0);
+
+ switch (container_kind (*c))
+ {
+ case ck_map:
+ case ck_multimap:
+ {
+ kt = composite_wrapper (container_kt (m));
+ }
+ // Fall through.
+ default:
+ {
+ vt = composite_wrapper (container_vt (m));
+ }
+ }
+
+ if (vt != 0 && has_a (*vt, test_container))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ": "
+ << "error: containers of containers not supported" << endl;
+
+ os << vt->file () << ":" << vt->line () << ":" << vt->column ()
+ << ": info: composite element value " << class_fq_name (*vt)
+ << " contains container(s)" << endl;
+
+ valid_ = false;
+ }
+
+ if (kt != 0 && has_a (*kt, test_container))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ": "
+ << "error: containers of containers not supported" << endl;
+
+ os << kt->file () << ":" << kt->line () << ":" << kt->column ()
+ << ": info: composite element key " << class_fq_name (*kt)
+ << " contains container(s)" << endl;
+
+ valid_ = false;
+ }
+ }
+ }
+
+ bool& valid_;
+ };
+
+ // Make sure soft-delete versions make sense for dependent entities.
+ // We don't seem to need anything for soft-add since if an entity is
+ // not added (e.g., an object), then we cannot reference it in the
+ // C++ sense (e.g., a pointer).
+ //
+ struct version_dependencies: object_members_base
+ {
+ version_dependencies (bool& valid): valid_ (valid) {}
+
+ virtual void
+ traverse_object (semantics::class_& c)
+ {
+ // For reuse inheritance we allow the base to be soft-deleted. In
+ // a sense, it becomes like an abstract class with the added
+ // functionality of being able to load old data.
+ //
+ // For polymorphism inheritance things are a lot more complicated
+ // and we don't allow a base to be soft-deleted since there is a
+ // link (foreign key) from the derived table.
+ //
+ semantics::class_* poly (polymorphic (c));
+ if (poly != 0 && poly != &c)
+ {
+ semantics::class_& base (polymorphic_base (c));
+ check_strict (
+ c, base, "polymorphic derived object", "polymorphic base");
+ }
+
+ // Don't go into bases.
+ //
+ names (c);
+ }
+
+ virtual void
+ traverse_simple (semantics::data_member& m)
+ {
+ semantics::class_& c (dynamic_cast<semantics::class_&> (m.scope ()));
+
+ switch (class_kind (c))
+ {
+ case class_object:
+ {
+ // Member shouldn't be deleted after the object that contains it.
+ //
+ check_lax (m, c, "data member", "object");
+ break;
+ }
+ // We leave it up to the user to make sure the view is consistent
+ // with the database schema it is being run on.
+ //
+ default:
+ break;
+ }
+ }
+
+ virtual void
+ traverse_composite (semantics::data_member* m, semantics::class_& c)
+ {
+ if (m != 0)
+ traverse_simple (*m); // Do simple value tests.
+ else
+ names (c); // Don't go into bases.
+ }
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ traverse_simple (m); // Do simple value tests.
+
+ // Pointer must be deleted before the pointed-to object.
+ //
+ check_strict (m, c, "object pointer", "pointed-to object");
+
+ // Inverse pointer must be deleted before the direct side.
+ //
+ if (data_member_path* imp = inverse (m))
+ check_strict (m, *imp, "inverse object pointer", "direct pointer");
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type&)
+ {
+ traverse_simple (m); // Do simple value tests.
+
+ if (semantics::class_* c = object_pointer (container_vt (m)))
+ {
+ // Pointer must be deleted before the pointed-to object.
+ //
+ check_strict (m, *c, "object pointer", "pointed-to object");
+
+ // Inverse pointer must be deleted before the direct side.
+ //
+ if (data_member_path* imp = inverse (m, "value"))
+ check_strict (m, *imp, "inverse object pointer", "direct pointer");
+ }
+ }
+
+ private:
+ // Check for harmless things like a data member deleted after the
+ // object.
+ //
+ template <typename D, typename P>
+ void
+ check_lax (D& dep, P& pre, char const* dname, char const* pname)
+ {
+ unsigned long long dv (deleted (dep));
+ unsigned long long pv (deleted (pre));
+
+ if (pv == 0 || // Prerequisite never deleted.
+ dv == 0 || // Dependent never deleted.
+ dv <= pv) // Dependent deleted before prerequisite.
+ return;
+
+ location_t dl (dep.template get<location_t> ("deleted-location"));
+ location_t pl (pre.template get<location_t> ("deleted-location"));
+
+ error (dl) << dname << " is deleted after " << dname << endl;
+ info (pl) << pname << " deletion version is specified here" << endl;
+
+ valid_ = false;
+ }
+
+ // Check for harmful things that will result in an invalid database
+ // schema, like a pointer pointing to a deleted object.
+ //
+ template <typename D, typename P>
+ void
+ check_strict (D& dep, P& pre, char const* dname, char const* pname)
+ {
+ location_t dl (0), pl (0);
+ unsigned long long dv (deleted (dep, &dl));
+ unsigned long long pv (deleted (pre, &pl));
+
+ if (pv == 0) // Prerequisite never deleted.
+ return;
+
+ if (dv == 0) // Dependent never deleted.
+ {
+ location dl (dep.location ());
+
+ error (dl) << dname << " is not deleted" << endl;
+ info (pl) << pname << " is deleted here" << endl;
+
+ valid_ = false;
+ }
+ else if (pv > dv) // Prerequisite deleted before dependent.
+ {
+ error (dl) << dname << " is deleted after " << pname << endl;
+ info (pl) << pname << " deletion version is specified here" << endl;
+
+ valid_ = false;
+ }
+ }
+
+ private:
+ bool& valid_;
+ };
+
+ struct class2: traversal::class_, context
+ {
+ class2 (bool& valid)
+ : valid_ (valid), has_lt_operator_ (0), typedefs_ (true), member_ (valid)
+ {
+ // Find the has_lt_operator function template.
+ //
+ tree odb (
+ lookup_qualified_name (
+ global_namespace, get_identifier ("odb"), false, false));
+
+ if (odb != error_mark_node)
+ {
+ tree compiler (
+ lookup_qualified_name (
+ odb, get_identifier ("compiler"), false, false));
+
+ if (compiler != error_mark_node)
+ {
+ has_lt_operator_ = lookup_qualified_name (
+ compiler, get_identifier ("has_lt_operator"), false, false);
+
+ if (has_lt_operator_ != error_mark_node)
+ {
+#if BUILDING_GCC_MAJOR >= 8
+ has_lt_operator_ = OVL_FIRST (has_lt_operator_);
+#else
+ has_lt_operator_ = OVL_CURRENT (has_lt_operator_);
+#endif
+ }
+ else
+ {
+ os << unit.file () << ": error: unable to resolve has_lt_operator "
+ << "function template inside odb::compiler" << endl;
+ has_lt_operator_ = 0;
+ }
+ }
+ else
+ os << unit.file () << ": error: unable to resolve compiler "
+ << "namespace inside odb" << endl;
+ }
+ else
+ os << unit.file () << ": error: unable to resolve odb namespace"
+ << endl;
+
+ if (has_lt_operator_ == 0)
+ valid_ = false;
+
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+
+ names_member_ >> member_;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck == class_other)
+ return;
+
+ names (c);
+
+ switch (ck)
+ {
+ case class_object: traverse_object (c); break;
+ case class_view: traverse_view (c); break;
+ case class_composite: traverse_composite (c); break;
+ default: return;
+ }
+
+ // Check members.
+ //
+ names (c, names_member_);
+
+ // Check version dependencies.
+ //
+ {
+ version_dependencies vd (valid_);
+ vd.traverse (c);
+ }
+ }
+
+ virtual void
+ traverse_object (type& c)
+ {
+ bool abst (abstract (c));
+ bool poly (polymorphic (c));
+
+ // Make sure we have no empty or pointless sections unless we
+ // are reuse-abstract or polymorphic.
+ //
+ if (!poly && !abst)
+ {
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
+ {
+ user_section& s (*i);
+
+ // Skip the special version update section (we always treat it
+ // as abstract in reuse inheritance).
+ //
+ if (s.special == user_section::special_version)
+ continue;
+
+ semantics::data_member& m (*s.member);
+ location const& l (m.location ());
+
+ if (s.total == 0 && !s.containers)
+ {
+ error (l) << "empty section" << endl;
+
+ if (&m.scope () != &c)
+ info (c.location ()) << "as seen in this non-abstract " <<
+ "persistent class" << endl;
+
+ valid_ = false;
+ continue;
+ }
+
+ // Eager-loaded section with readonly members.
+ //
+ if (s.load == user_section::load_eager && s.update_empty ())
+ {
+ error (l) << "eager-loaded section with readonly members is " <<
+ "pointless" << endl;
+
+ if (&m.scope () != &c)
+ info (c.location ()) << "as seen in this non-abstract " <<
+ "persistent class" << endl;
+
+ valid_ = false;
+ }
+ }
+ }
+
+ if (data_member_path* id = id_member (c))
+ {
+ semantics::type& t (utype (*id));
+
+ // If this is a session object, make sure that the id type can
+ // be compared.
+ //
+ if (session (c) && has_lt_operator_ != 0)
+ {
+ tree args (make_tree_vec (1));
+ TREE_VEC_ELT (args, 0) = t.tree_node ();
+
+ tree inst (
+ instantiate_template (
+ has_lt_operator_, args, tf_none));
+
+ bool v (inst != error_mark_node);
+
+ // Old version of has_lt_operator() relied on full instantiation
+ // while the new one is based on SFINAE.
+ //
+#if 0
+ if (v &&
+ DECL_TEMPLATE_INSTANTIATION (inst) &&
+ !DECL_TEMPLATE_INSTANTIATED (inst))
+ {
+ // Instantiate this function template to see if the value type
+ // provides operator<. Unfortunately, GCC instantiate_decl()
+ // does not provide any control over the diagnostics it issues
+ // in case of an error. To work around this, we are going to
+ // temporarily redirect diagnostics to /dev/null, which is
+ // where asm_out_file points to (see plugin.cxx).
+ //
+ // Needless to say, this is very hacky and we should quickly fail
+ // (as we do below) if there were errors.
+ //
+ int ec (errorcount);
+ FILE* s (global_dc->printer->buffer->stream);
+ global_dc->printer->buffer->stream = asm_out_file;
+
+ instantiate_decl (inst, false, false);
+
+ global_dc->printer->buffer->stream = s;
+ v = (ec == errorcount);
+ }
+#endif
+
+ if (!v)
+ {
+ semantics::data_member& idm (*id->front ());
+
+ os << t.file () << ":" << t.line () << ":" << t.column ()
+ << ": error: value type that is used as object id in "
+ << "persistent class with session support does not define "
+ << "the less than (<) comparison" << endl;
+
+ os << t.file () << ":" << t.line () << ":" << t.column ()
+ << ": info: provide operator< for this value type" << endl;
+
+ os << idm.file () << ":" << idm.line () << ":" << idm.column ()
+ << ": info: id member is defined here" << endl;
+
+ os << c.file () << ":" << c.line () << ":" << c.column ()
+ << ": info: persistent class is defined here" << endl;
+
+ valid_ = false;
+ }
+ }
+ }
+ else
+ {
+ // Make sure an object without id has no sections.
+ //
+ user_sections& uss (c.get<user_sections> ("user-sections"));
+
+ if (!uss.empty ())
+ {
+ semantics::data_member& m (*uss.front ().member);
+ os << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: object without id cannot have sections" << endl;
+ valid_ = false;
+ }
+ }
+
+ // Allow all the members to be deleted as long as there is no
+ // schema for this class.
+ //
+ column_count_type const& cc (column_count (c));
+ size_t cont (has_a (c, test_container));
+ size_t dcont (cont - has_a (c, test_container | exclude_deleted));
+
+ if ((cc.total == 0 && cont == 0) ||
+ (cc.total == cc.deleted && cont == dcont && !(abst || deleted (c))))
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: no persistent data members in the class" << endl;
+ valid_ = false;
+ }
+ }
+
+ virtual void
+ traverse_view (type& c)
+ {
+ // We don't check for the column count here since we may want to
+ // allow certain kinds of empty views. Instead, this is handled
+ // in relational::validation.
+
+ // See if any of the associated objects are polymorphic. If so,
+ // we need to include polymorphism-specific headers. Also, if the
+ // view is loading the object, then we will need the corresponding
+ // statements.
+ //
+ if (c.count ("objects"))
+ {
+ view_objects& objs (c.get<view_objects> ("objects"));
+
+ for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
+ {
+ if (i->kind == view_object::object)
+ {
+ if (polymorphic (*i->obj))
+ features.polymorphic_object = true;
+ else if (i->ptr != 0)
+ {
+ (id_member (*i->obj) != 0
+ ? features.simple_object
+ : features.no_id_object) = true;
+ }
+ }
+ }
+ }
+ }
+
+ virtual void
+ traverse_composite (type& c)
+ {
+ // Allow all the members to be deleted.
+ //
+ column_count_type const& cc (column_count (c));
+ size_t cont (has_a (c, test_container));
+
+ if (cc.total == 0 && cont == 0)
+ {
+ os << c.file () << ":" << c.line () << ":" << c.column () << ":"
+ << " error: no persistent data members in the class" << endl;
+ valid_ = false;
+ }
+ }
+
+ bool& valid_;
+ tree has_lt_operator_;
+
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ data_member2 member_;
+ traversal::names names_member_;
+ };
+}
+
+void
+validate (options const& ops,
+ features& f,
+ semantics::unit& u,
+ semantics::path const& p,
+ unsigned short pass)
+{
+ bool valid (true);
+ database db (ops.database ()[0]);
+
+ // Validate options.
+ //
+ if (ops.generate_schema_only () &&
+ ops.schema_format ()[db].count (schema_format::embedded))
+ {
+ cerr << "error: --generate-schema-only is only valid when generating " <<
+ "schema as a standalone SQL or separate C++ file" << endl;
+ valid = false;
+ }
+
+ // Multi-database support options.
+ //
+ if (ops.multi_database () == multi_database::dynamic &&
+ ops.default_database_specified () &&
+ ops.default_database () != database::common)
+ {
+ cerr << "error: when dynamic multi-database support is used, the " <<
+ "default database can only be 'common'" << endl;
+ valid = false;
+ }
+
+ if (db == database::common &&
+ ops.multi_database () == multi_database::disabled)
+ {
+ cerr << "error: 'common' database is only valid with multi-database " <<
+ "support enabled" << endl;
+ valid = false;
+ }
+
+ // Changelog options.
+ //
+ if (ops.changelog_in ().count (db) != ops.changelog_out ().count (db))
+ {
+ cerr << "error: both --changelog-in and --changelog-out must be " <<
+ "specified" << endl;
+ valid = false;
+ }
+
+ if (!valid)
+ throw validator_failed ();
+
+ unique_ptr<context> ctx (create_context (cerr, u, ops, f, 0));
+
+ if (pass == 1)
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ traversal::declares unit_declares;
+ typedefs1 unit_typedefs (unit_declares);
+ traversal::namespace_ ns;
+ value_type vt;
+ class1 c (valid);
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_declares >> vt;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ traversal::declares ns_declares;
+ typedefs1 ns_typedefs (ns_declares);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_declares >> vt;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (u);
+ }
+ else
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (true);
+ traversal::namespace_ ns;
+ class2 c (valid);
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (true);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (u);
+ }
+
+ if (!valid)
+ throw validator_failed ();
+
+ switch (db)
+ {
+ case database::common:
+ {
+ break;
+ }
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ try
+ {
+ relational::validate (ops, f, u, p, pass);
+ }
+ catch (operation_failed const&)
+ {
+ throw validator_failed ();
+ }
+
+ break;
+ }
+ }
+}
diff --git a/odb/odb/validator.hxx b/odb/odb/validator.hxx
new file mode 100644
index 0000000..d0aeac9
--- /dev/null
+++ b/odb/odb/validator.hxx
@@ -0,0 +1,23 @@
+// file : odb/validator.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_VALIDATOR_HXX
+#define ODB_VALIDATOR_HXX
+
+#include <odb/options.hxx>
+#include <odb/features.hxx>
+#include <odb/semantics/unit.hxx>
+
+class validator_failed {};
+
+// The first pass is performed after processing pass 1 but before
+// processing pass 2. The second -- after pass 2.
+//
+void
+validate (options const&,
+ features&,
+ semantics::unit&,
+ semantics::path const&,
+ unsigned short pass);
+
+#endif // ODB_VALIDATOR_HXX
diff --git a/odb/odb/version.hxx b/odb/odb/version.hxx
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/odb/odb/version.hxx
diff --git a/odb/odb/version.hxx.in b/odb/odb/version.hxx.in
new file mode 100644
index 0000000..2bf2ae7
--- /dev/null
+++ b/odb/odb/version.hxx.in
@@ -0,0 +1,54 @@
+// file : odb/version.hxx.in
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_COMPILER_VERSION // Note: using the version macro itself.
+
+// New numeric version format is AAAAABBBBBCCCCCDDDE where:
+//
+// AAAAA - major version number
+// BBBBB - minor version number
+// CCCCC - bugfix version number
+// DDD - alpha / beta (DDD + 500) version number
+// E - final (0) / snapshot (1)
+//
+// When DDDE is not 0, 1 is subtracted from AAAAABBBBBCCCCC. For example:
+//
+// Version AAAAABBBBBCCCCCDDDE
+//
+// 0.1.0 0000000001000000000
+// 0.1.2 0000000001000020000
+// 1.2.3 0000100002000030000
+// 2.2.0-a.1 0000200001999990010
+// 3.0.0-b.2 0000299999999995020
+// 2.2.0-a.1.z 0000200001999990011
+//
+#define ODB_COMPILER_VERSION $odb.version.project_number$ULL
+#define ODB_COMPILER_VERSION_STR "$odb.version.project$"
+#define ODB_COMPILER_VERSION_ID "$odb.version.project_id$"
+#define ODB_COMPILER_VERSION_FULL "$odb.version$"
+
+#define ODB_COMPILER_SNAPSHOT $odb.version.snapshot_sn$ULL
+
+// Old/deprecated numeric version format is AABBCCDD where:
+//
+// AA - major version number
+// BB - minor version number
+// CC - bugfix version number
+// DD - alpha / beta (DD + 50) version number
+//
+// When DD is not 00, 1 is subtracted from AABBCC. For example:
+//
+// Version AABBCCDD
+// 2.0.0 02000000
+// 2.1.0 02010000
+// 2.1.1 02010100
+// 2.2.0.a1 02019901
+// 3.0.0.b2 02999952
+//
+#define ODB_COMPILER_VERSION_OLD 2049976
+
+// ODB interface version: minor, major, and alpha/beta versions.
+//
+#define ODB_VERSION 20476
+
+#endif // ODB_COMPILER_VERSION