aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2011-12-21 11:19:25 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2012-01-20 15:43:45 +0200
commitc6d92f2f979eae471f49d9af1768b7b05f3a6f6f (patch)
treeb4360890f32380a635b80bee88b755b09dbec1d4
parent0c3091b5071951c05c03486f01f3eaee98922524 (diff)
ODB compiler implementation, traits, and types test for SQL Server
-rw-r--r--odb/context.cxx6
-rw-r--r--odb/generator.cxx5
-rw-r--r--odb/makefile10
-rw-r--r--odb/odb.cxx4
-rw-r--r--odb/option-functions.cxx1
-rw-r--r--odb/option-types.cxx1
-rw-r--r--odb/option-types.hxx1
-rw-r--r--odb/options.cli56
-rw-r--r--odb/relational/mssql/common.cxx558
-rw-r--r--odb/relational/mssql/common.hxx362
-rw-r--r--odb/relational/mssql/context.cxx577
-rw-r--r--odb/relational/mssql/context.hxx142
-rw-r--r--odb/relational/mssql/header.cxx244
-rw-r--r--odb/relational/mssql/model.cxx74
-rw-r--r--odb/relational/mssql/schema.cxx224
-rw-r--r--odb/relational/mssql/source.cxx1320
-rw-r--r--odb/relational/mysql/source.cxx36
-rw-r--r--odb/relational/oracle/source.cxx7
-rw-r--r--odb/relational/pgsql/source.cxx7
-rw-r--r--odb/relational/source.hxx389
20 files changed, 3845 insertions, 179 deletions
diff --git a/odb/context.cxx b/odb/context.cxx
index 267b233..95b05aa 100644
--- a/odb/context.cxx
+++ b/odb/context.cxx
@@ -12,6 +12,7 @@
#include <odb/common.hxx>
#include <odb/pragma.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>
@@ -128,6 +129,11 @@ create_context (ostream& os,
switch (ops.database ())
{
+ case database::mssql:
+ {
+ r.reset (new relational::mssql::context (os, unit, ops, m));
+ break;
+ }
case database::mysql:
{
r.reset (new relational::mysql::context (os, unit, ops, m));
diff --git a/odb/generator.cxx b/odb/generator.cxx
index d7ceb86..d3dedef 100644
--- a/odb/generator.cxx
+++ b/odb/generator.cxx
@@ -102,6 +102,7 @@ generate (options const& ops, semantics::unit& unit, path const& p)
switch (ops.database ())
{
+ case database::mssql:
case database::mysql:
case database::oracle:
case database::pgsql:
@@ -253,6 +254,7 @@ generate (options const& ops, semantics::unit& unit, path const& p)
switch (ops.database ())
{
+ case database::mssql:
case database::mysql:
case database::oracle:
case database::pgsql:
@@ -300,6 +302,7 @@ generate (options const& ops, semantics::unit& unit, path const& p)
switch (ops.database ())
{
+ case database::mssql:
case database::mysql:
case database::oracle:
case database::pgsql:
@@ -342,6 +345,7 @@ generate (options const& ops, semantics::unit& unit, path const& p)
switch (ops.database ())
{
+ case database::mssql:
case database::mysql:
case database::oracle:
case database::pgsql:
@@ -376,6 +380,7 @@ generate (options const& ops, semantics::unit& unit, path const& p)
switch (ops.database ())
{
+ case database::mssql:
case database::mysql:
case database::oracle:
case database::pgsql:
diff --git a/odb/makefile b/odb/makefile
index 3550c50..321b532 100644
--- a/odb/makefile
+++ b/odb/makefile
@@ -38,6 +38,16 @@ relational/source.cxx \
relational/model.cxx \
relational/schema.cxx
+# Relational/MSSQL.
+#
+cxx_ptun += \
+relational/mssql/common.cxx \
+relational/mssql/context.cxx \
+relational/mssql/header.cxx \
+relational/mssql/source.cxx \
+relational/mssql/model.cxx \
+relational/mssql/schema.cxx
+
# Relational/MySQL.
#
cxx_ptun += \
diff --git a/odb/odb.cxx b/odb/odb.cxx
index fd61e95..e55328d 100644
--- a/odb/odb.cxx
+++ b/odb/odb.cxx
@@ -120,11 +120,11 @@ profile_paths (strings const& args, char const* name);
static char const* const db_macro[] =
{
+ "-DODB_DATABASE_MSSQL",
"-DODB_DATABASE_MYSQL",
"-DODB_DATABASE_ORACLE",
"-DODB_DATABASE_PGSQL",
- "-DODB_DATABASE_SQLITE",
- "-DODB_DATABASE_TRACER"
+ "-DODB_DATABASE_SQLITE"
};
int
diff --git a/odb/option-functions.cxx b/odb/option-functions.cxx
index 4410817..8cd736d 100644
--- a/odb/option-functions.cxx
+++ b/odb/option-functions.cxx
@@ -20,6 +20,7 @@ process_options (options& o)
switch (o.database ())
{
+ case database::mssql:
case database::mysql:
case database::oracle:
case database::pgsql:
diff --git a/odb/option-types.cxx b/odb/option-types.cxx
index c3c02fb..715577d 100644
--- a/odb/option-types.cxx
+++ b/odb/option-types.cxx
@@ -16,6 +16,7 @@ using namespace std;
//
static const char* database_[] =
{
+ "mssql",
"mysql",
"oracle",
"pgsql",
diff --git a/odb/option-types.hxx b/odb/option-types.hxx
index 60d602a..2b96941 100644
--- a/odb/option-types.hxx
+++ b/odb/option-types.hxx
@@ -14,6 +14,7 @@ struct database
{
// Keep in alphabetic order.
//
+ mssql,
mysql,
oracle,
pgsql,
diff --git a/odb/options.cli b/odb/options.cli
index 97bc48a..a8ff86d 100644
--- a/odb/options.cli
+++ b/odb/options.cli
@@ -48,8 +48,8 @@ class options
::database --database | -d
{
"<db>",
- "Generate code for the <db> database. Valid values are \cb{mysql},
- \cb{oracle}, \cb{pgsql}, and \cb{sqlite}."
+ "Generate code for the <db> database. Valid values are \cb{mssql},
+ \cb{mysql}, \cb{oracle}, \cb{pgsql}, and \cb{sqlite}."
};
bool --generate-query | -q
@@ -69,9 +69,9 @@ class options
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, and Oracle 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."
+ 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."
};
std::set< ::schema_format> --schema-format
@@ -427,6 +427,26 @@ class options
bool --trace {"Trace the compilation process."};
//
+ // SQL Server-specific options.
+ //
+
+ unsigned int --mssql-short-limit = 256
+ {
+ "<size>",
+ "Specify the short data size limit. If character, national character,
+ or binary data type has a maximum length (in bytes) less than 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 256 bytes."
+ };
+
+ //
// MySQL-specific options.
//
@@ -441,19 +461,6 @@ class options
};
//
- // SQLite-specific options.
- //
-
- 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."
- };
-
- //
// Oracle-specific options.
//
@@ -465,4 +472,17 @@ class options
version-specific optimizations in the generated C++ code. The version
must be in the \c{\i{major}\b{.}\i{minor}} form, for example, \cb{11.2}."
};
+
+ //
+ // SQLite-specific options.
+ //
+
+ 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."
+ };
};
diff --git a/odb/relational/mssql/common.cxx b/odb/relational/mssql/common.cxx
new file mode 100644
index 0000000..f9c89e0
--- /dev/null
+++ b/odb/relational/mssql/common.cxx
@@ -0,0 +1,558 @@
+// file : odb/relational/mssql/common.cxx
+// author : Constantin Michael <constantin@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// 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
+ //
+
+ void member_base::
+ 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_type (m.type ()));
+ semantics::type& t (type_override_ != 0 ? *type_override_ : utype (m));
+
+ semantics::type* cont;
+ 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,
+ (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,
+ (wrapper (t) ? &t : 0),
+ cq,
+ var,
+ fq_type_override_);
+ if (pre (mi))
+ {
+ traverse_container (mi);
+ post (mi);
+ }
+ }
+ else
+ {
+ sql_type const& st (column_sql_type (m, key_prefix_));
+
+ if (semantics::class_* c = object_pointer (t))
+ {
+ member_info mi (m,
+ utype (*id_member (*c)),
+ 0,
+ cq,
+ var,
+ fq_type_override_);
+ mi.st = &st;
+ if (pre (mi))
+ {
+ traverse_object_pointer (mi);
+ post (mi);
+ }
+ }
+ else
+ {
+ member_info mi (m, t, 0, cq, var, fq_type_override_);
+ mi.st = &st;
+ if (pre (mi))
+ {
+ traverse_simple (mi);
+ post (mi);
+ }
+ }
+ }
+ }
+
+ void member_base::
+ traverse_simple (member_info& mi)
+ {
+ const sql_type& st (*mi.st);
+
+ 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 (semantics::type* type,
+ string const& fq_type,
+ string const& key_prefix)
+ : relational::member_base (type, fq_type, key_prefix)
+ {
+ }
+
+ string member_image_type::
+ image_type (semantics::data_member& m)
+ {
+ type_.clear ();
+ member_base::traverse (m);
+ return type_;
+ }
+
+ void member_image_type::
+ traverse_composite (member_info& mi)
+ {
+ type_ = "composite_value_traits< " + mi.fq_type () + " >::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*";
+ }
+
+ //
+ // 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 (semantics::type* type,
+ string const& fq_type,
+ string const& key_prefix)
+ : relational::member_base (type, fq_type, key_prefix)
+ {
+ }
+
+ string member_database_type_id::
+ database_type_id (type& m)
+ {
+ type_id_.clear ();
+ member_base::traverse (m);
+ 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";
+ }
+
+ //
+ // query_columns
+ //
+
+ struct query_columns: relational::query_columns, context
+ {
+ query_columns (base const& x): base (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/relational/mssql/common.hxx b/odb/relational/mssql/common.hxx
new file mode 100644
index 0000000..5bd37f5
--- /dev/null
+++ b/odb/relational/mssql/common.hxx
@@ -0,0 +1,362 @@
+// file : odb/relational/mssql/common.hxx
+// author : Constantin Michael <constantin@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// 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, context
+ {
+ member_base (base const& x): base (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.
+ //
+ member_base () {}
+
+ virtual void
+ traverse (semantics::data_member& m);
+
+ 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.
+ 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.
+ sql_type 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 (fq_type_.empty ())
+ {
+ 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_,
+ semantics::type* wrapper_,
+ bool cq_,
+ string& var_,
+ string const& fq_type)
+ : m (m_),
+ t (t_),
+ 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&)
+ {
+ }
+
+ virtual void
+ traverse_composite (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_container (member_info&)
+ {
+ }
+
+ virtual void
+ traverse_object_pointer (member_info& mi)
+ {
+ traverse_simple (mi);
+ }
+
+ 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: member_base
+ {
+ member_image_type (semantics::type* type = 0,
+ string const& fq_type = string (),
+ string const& key_prefix = string ());
+ 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: member_base
+ {
+ member_database_type_id (semantics::type* type = 0,
+ string const& fq_type = string (),
+ string const& key_prefix = string ());
+ string
+ database_type_id (type&);
+
+ 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_;
+ };
+ }
+}
+#endif // ODB_RELATIONAL_MSSQL_COMMON_HXX
diff --git a/odb/relational/mssql/context.cxx b/odb/relational/mssql/context.cxx
new file mode 100644
index 0000000..bda8df9
--- /dev/null
+++ b/odb/relational/mssql/context.cxx
@@ -0,0 +1,577 @@
+// file : odb/relational/mssql/context.cxx
+// author : Constantin Michael <constantin@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// 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
+ {
+ const char* const cxx_type;
+ const char* const db_type;
+ const char* const db_id_type;
+ };
+
+ type_map_entry type_map[] =
+ {
+ {"bool", "BIT", 0},
+
+ {"char", "TINYINT", 0},
+ {"signed char", "TINYINT", 0},
+ {"unsigned char", "TINYINT", 0},
+
+ {"short int", "SMALLINT", 0},
+ {"short unsigned int", "SMALLINT", 0},
+
+ {"int", "INT", 0},
+ {"unsigned int", "INT", 0},
+
+ {"long int", "BIGINT", 0},
+ {"long unsigned int", "BIGINT", 0},
+
+ {"long long int", "BIGINT", 0},
+ {"long long unsigned int", "BIGINT", 0},
+
+ {"float", "REAL", 0},
+ {"double", "FLOAT", 0},
+
+ {"::std::string", "VARCHAR(8000)", "VARCHAR(900)"},
+
+ {"::size_t", "BIGINT", 0},
+ {"::std::size_t", "BIGINT", 0},
+
+ // Windows GUID/UUID (typedef struct _GUID {...} GUID, UUID;).
+ //
+ {"::_GUID", "UNIQUEIDENTIFIER", 0}
+ };
+ }
+
+ context* context::current_;
+
+ context::
+ ~context ()
+ {
+ if (current_ == this)
+ current_ = 0;
+ }
+
+ context::
+ context (ostream& os,
+ semantics::unit& u,
+ options_type const& ops,
+ sema_rel::model* m)
+ : root_context (os, u, ops, 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;
+ 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));
+
+ data_->type_map_.insert (v);
+ }
+ }
+
+ context::
+ context ()
+ : data_ (current ().data_)
+ {
+ }
+
+ string context::
+ quote_id_impl (string const& id) const
+ {
+ string r;
+ r.reserve (130); // Max MSSQL identifier length is 128.
+ r += '[';
+ r.append (id, 0, 128);
+ r += ']';
+ return r;
+ }
+
+ string context::
+ database_type_impl (semantics::type& t, semantics::names* hint, bool id)
+ {
+ string r (base_context::database_type_impl (t, hint, id));
+
+ if (!r.empty ())
+ return r;
+
+ using semantics::enum_;
+
+ if (t.is_a<semantics::enum_> ())
+ r = "INT";
+
+ return r;
+ }
+
+ //
+ // SQL type parsing.
+ //
+
+ namespace
+ {
+ struct sql_parser
+ {
+ typedef context::invalid_sql_type invalid_sql_type;
+
+ sql_parser (std::string const& sql)
+ : l_ (sql)
+ {
+ }
+
+ sql_type
+ parse ()
+ {
+ r_ = sql_type ();
+
+ try
+ {
+ parse_name ();
+ }
+ catch (sql_lexer::invalid_input const& e)
+ {
+ throw invalid_sql_type ("invalid SQL Server type declaration: " +
+ e.message);
+ }
+
+ return r_;
+ }
+
+ void
+ parse_name ()
+ {
+ sql_token t (l_.next ());
+
+ if (t.type () != sql_token::t_identifier)
+ {
+ throw invalid_sql_type ("expected SQL Server type name "
+ "instead of '" + t.string () + "'");
+ }
+
+ 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;
+
+ parse_precision (l_.next ());
+ }
+ 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;
+
+ parse_precision (l_.next ());
+ }
+ else if (id == "DOUBLE")
+ {
+ t = l_.next ();
+
+ if (t.type () != sql_token::t_identifier ||
+ upcase (t.identifier ()) != "PRECISION")
+ {
+ throw invalid_sql_type ("expected 'PRECISION' instead of '"
+ + t.string () + "'");
+ }
+
+ r_.type = sql_type::FLOAT;
+
+ r_.has_prec = true;
+ r_.prec = 53;
+
+ // It appears that DOUBLE PRECISION can be follows by the
+ // precision specification.
+ //
+ parse_precision (l_.next ());
+ }
+ else if (id == "CHAR" ||
+ id == "CHARACTER")
+ {
+ parse_char_trailer (false);
+ }
+ else if (id == "VARCHAR")
+ {
+ r_.type = sql_type::VARCHAR;
+
+ r_.has_prec = true;
+ r_.prec = 1;
+
+ parse_precision (l_.next ());
+ }
+ else if (id == "TEXT")
+ {
+ r_.type = sql_type::TEXT;
+ }
+ else if (id == "NCHAR")
+ {
+ r_.type = sql_type::NCHAR;
+
+ r_.has_prec = true;
+ r_.prec = 1;
+
+ parse_precision (l_.next ());
+ }
+ else if (id == "NVARCHAR")
+ {
+ r_.type = sql_type::NVARCHAR;
+
+ r_.has_prec = true;
+ r_.prec = 1;
+
+ parse_precision (l_.next ());
+ }
+ else if (id == "NTEXT")
+ {
+ r_.type = sql_type::NTEXT;
+ }
+ 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;
+ }
+ else if (id == "CHAR" ||
+ id == "CHARACTER")
+ {
+ parse_char_trailer (true);
+ }
+ else
+ {
+ throw invalid_sql_type (
+ "expected 'CHAR', 'CHARACTER', or 'TEXT' instead of '"
+ + t.string () + "'");
+ }
+ }
+ 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;
+
+ parse_precision (t);
+ }
+ else if (id == "VARBINARY")
+ {
+ r_.type = sql_type::VARBINARY;
+
+ r_.has_prec = true;
+ r_.prec = 1;
+
+ parse_precision (l_.next ());
+ }
+ else if (id == "IMAGE")
+ {
+ r_.type = sql_type::IMAGE;
+ }
+ 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;
+
+ parse_precision (l_.next ());
+ }
+ 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;
+
+ parse_precision (l_.next ());
+ }
+ 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;
+
+ parse_precision (l_.next ());
+ }
+ else if (id == "UNIQUEIDENTIFIER")
+ {
+ r_.type = sql_type::UNIQUEIDENTIFIER;
+ }
+ else if (id == "ROWVERSION" ||
+ id == "TIMESTAMP")
+ {
+ r_.type = sql_type::ROWVERSION;
+ }
+ else
+ {
+ throw invalid_sql_type ("unexpected SQL Server type name '" +
+ t.identifier () + "'");
+ }
+ }
+
+ void
+ 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 ()))
+ {
+ throw invalid_sql_type (
+ "invalid precision value '" + t.literal () + "' in SQL "
+ "Server type declaration");
+ }
+
+ 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
+ {
+ throw invalid_sql_type (
+ "integer precision expected in SQL Server type declaration");
+ }
+
+ // 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)
+ {
+ throw invalid_sql_type (
+ "unexpected scale in SQL Server type declaration");
+ }
+
+ t = l_.next ();
+
+ if (t.type () != sql_token::t_int_lit)
+ {
+ throw invalid_sql_type (
+ "integer scale expected in SQL Server type declaration");
+ }
+
+ istringstream is (t.literal ());
+
+ if (!(is >> r_.scale && is.eof ()))
+ {
+ throw invalid_sql_type (
+ "invalid scale value '" + t.literal () + "' in SQL Server "
+ "type declaration");
+ }
+
+ r_.has_scale = true;
+ t = l_.next ();
+ }
+
+ if (t.punctuation () != sql_token::p_rparen)
+ {
+ throw invalid_sql_type (
+ "expected ')' in SQL Server type declaration");
+ }
+ }
+ }
+
+ void
+ 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;
+
+ parse_precision (t);
+ }
+
+ private:
+ string
+ upcase (string const& s)
+ {
+ return context::upcase (s);
+ }
+
+ private:
+ sql_lexer l_;
+ sql_type r_;
+ };
+ }
+
+ sql_type const& context::
+ column_sql_type (semantics::data_member& m, string const& kp)
+ {
+ string key (kp.empty ()
+ ? string ("mssql-column-sql-type")
+ : "mssql-" + kp + "-column-sql-type");
+
+ if (!m.count (key))
+ {
+ try
+ {
+ m.set (key, parse_sql_type (column_type (m, kp)));
+ }
+ catch (invalid_sql_type const& e)
+ {
+ cerr << m.file () << ":" << m.line () << ":" << m.column ()
+ << ": error: " << e.message () << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ return m.get<sql_type> (key);
+ }
+
+ sql_type context::
+ parse_sql_type (string const& t)
+ {
+ sql_parser p (t);
+ return p.parse ();
+ }
+ }
+}
diff --git a/odb/relational/mssql/context.hxx b/odb/relational/mssql/context.hxx
new file mode 100644
index 0000000..8f250ea
--- /dev/null
+++ b/odb/relational/mssql/context.hxx
@@ -0,0 +1,142 @@
+// file : odb/relational/mssql/context.hxx
+// author : Constantin Michael <constantin@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_RELATIONAL_MSSQL_CONTEXT_HXX
+#define ODB_RELATIONAL_MSSQL_CONTEXT_HXX
+
+#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), has_scale (false)
+ {
+ }
+
+ 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.
+ };
+
+ class context: public virtual relational::context
+ {
+ public:
+ sql_type const&
+ column_sql_type (semantics::data_member&,
+ string const& key_prefix = string ());
+
+ public:
+ struct invalid_sql_type
+ {
+ invalid_sql_type (string const& message): message_ (message) {}
+
+ string const&
+ message () const {return message_;}
+
+ private:
+ string message_;
+ };
+
+ static sql_type
+ parse_sql_type (string const&);
+
+ protected:
+ virtual string
+ quote_id_impl (string const&) const;
+
+ protected:
+ virtual string
+ database_type_impl (semantics::type&, semantics::names*, bool);
+
+ public:
+ virtual
+ ~context ();
+
+ context ();
+ context (std::ostream&,
+ semantics::unit&,
+ options_type const&,
+ 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) {}
+ };
+ data* data_;
+ };
+ }
+}
+
+#endif // ODB_RELATIONAL_MSSQL_CONTEXT_HXX
diff --git a/odb/relational/mssql/header.cxx b/odb/relational/mssql/header.cxx
new file mode 100644
index 0000000..07f9521
--- /dev/null
+++ b/odb/relational/mssql/header.cxx
@@ -0,0 +1,244 @@
+// file : odb/relational/mssql/header.cxx
+// author : Constantin Michael <constantin@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// 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 image_type: relational::image_type, context
+ {
+ image_type (base const& x): base (x) {};
+
+ virtual void
+ image_extra (type& c)
+ {
+ if (!(composite (c) || abstract (c)))
+ {
+ /*
+ @@ TODO
+
+ 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 << "}"
+ << endl;
+ */
+ }
+ }
+ };
+ entry<image_type> image_type_;
+
+ struct image_member: relational::image_member, member_base
+ {
+ image_member (base const& x)
+ : member_base::base (x), // virtual base
+ base (x),
+ member_base (x),
+ member_image_type_ (base::type_override_,
+ base::fq_type_override_,
+ base::key_prefix_)
+ {
+ }
+
+ 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_composite (member_info& mi)
+ {
+ os << image_type << " " << mi.var << "value;"
+ << endl;
+ }
+
+ 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;
+ }
+
+ private:
+ string image_type;
+ member_image_type member_image_type_;
+ };
+ entry<image_member> image_member_;
+ }
+ }
+}
diff --git a/odb/relational/mssql/model.cxx b/odb/relational/mssql/model.cxx
new file mode 100644
index 0000000..344eef1
--- /dev/null
+++ b/odb/relational/mssql/model.cxx
@@ -0,0 +1,74 @@
+// file : odb/relational/mssql/model.cxx
+// author : Constantin Michael <constantin@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// 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_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 or DECIMAL type.
+ //
+ switch (column_sql_type (m).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/relational/mssql/schema.cxx b/odb/relational/mssql/schema.cxx
new file mode 100644
index 0000000..2cbeb5f
--- /dev/null
+++ b/odb/relational/mssql/schema.cxx
@@ -0,0 +1,224 @@
+// file : odb/relational/mssql/schema.cxx
+// author : Constantin Michael <constantin@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <set>
+
+#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;
+
+ struct schema_emitter: relational::schema_emitter
+ {
+ schema_emitter (const base& x): base (x) {}
+
+ virtual void
+ post ()
+ {
+ if (!first_) // Ignore empty statements.
+ {
+ os << ';' << endl
+ << "GO" << endl
+ << endl;
+ }
+ }
+ };
+ entry<schema_emitter> schema_emitter_;
+
+ //
+ // Drop.
+ //
+
+ struct drop_table: relational::drop_table, context
+ {
+ drop_table (base const& x): base (x) {}
+
+ virtual void
+ drop (string const& table)
+ {
+ // SQL Server has no IF EXISTS conditional for dropping table.
+ // The following approach appears to be the recommended way to
+ // drop a table if it exists.
+ //
+ string const& qt ();
+
+ os << "IF OBJECT_ID(" << quote_string (table) <<
+ ", " << quote_string ("U") << ") IS NOT NULL" << endl
+ << " DROP TABLE " << quote_id (table) << endl;
+ }
+ };
+ entry<drop_table> drop_table_;
+
+ //
+ // Create.
+ //
+
+ struct create_column: relational::create_column, context
+ {
+ create_column (base const& x): base (x) {}
+
+ virtual void
+ auto_ (sema_rel::column&)
+ {
+ os << " IDENTITY";
+ }
+ };
+ entry<create_column> create_column_;
+
+ struct create_foreign_key;
+
+ struct create_table: relational::create_table, context
+ {
+ create_table (base const& x): base (x) {}
+
+ void
+ traverse (sema_rel::table&);
+
+ private:
+ friend class create_foreign_key;
+ set<string> tables_; // Set of tables we have already defined.
+ };
+ entry<create_table> create_table_;
+
+ struct create_foreign_key: relational::create_foreign_key, context
+ {
+ create_foreign_key (schema_format f, relational::create_table& ct)
+ : base (f, ct)
+ {
+ }
+
+ create_foreign_key (base const& x): base (x) {}
+
+ virtual void
+ traverse (sema_rel::foreign_key& fk)
+ {
+ // 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
+ // (see add_foreign_key below).
+ //
+ create_table& ct (static_cast<create_table&> (create_table_));
+
+ if (ct.tables_.find (fk.referenced_table ()) != ct.tables_.end ())
+ {
+ // SQL Server does not support deferred constraint checking.
+ // Output such foreign keys as comments, for documentation,
+ // unless we are generating embedded schema.
+ //
+ if (fk.deferred ())
+ {
+ // Don't bloat C++ code with comment strings if we are
+ // generating embedded schema.
+ //
+ if (format_ != schema_format::embedded)
+ {
+ os << endl
+ << endl
+ << " /*" << endl;
+
+ base::create (fk);
+
+ os << endl
+ << " */";
+ }
+ }
+ else
+ base::traverse (fk);
+
+ fk.set ("mssql-fk-defined", true); // Mark it as defined.
+ }
+ }
+
+ virtual string
+ name (sema_rel::foreign_key& fk)
+ {
+ // In SQL Server, foreign key names are schema-global. Make them
+ // unique by prefixing the key name with table name.
+ //
+ return static_cast<sema_rel::table&> (fk.scope ()).name () +
+ '_' + fk.name ();
+ }
+
+ virtual void
+ deferred ()
+ {
+ // SQL Server doesn't support deferred.
+ }
+ };
+ entry<create_foreign_key> create_foreign_key_;
+
+ struct add_foreign_key: create_foreign_key, relational::common
+ {
+ add_foreign_key (schema_format f, relational::create_table& ct)
+ : create_foreign_key (f, ct), common (ct.emitter (), ct.stream ())
+ {
+ }
+
+ virtual void
+ traverse (sema_rel::foreign_key& fk)
+ {
+ if (!fk.count ("mssql-fk-defined"))
+ {
+ sema_rel::table& t (dynamic_cast<sema_rel::table&> (fk.scope ()));
+
+ // SQL Server has no deferred constraints.
+ //
+ if (fk.deferred ())
+ {
+ if (format_ != schema_format::embedded)
+ {
+ os << "/*" << endl;
+
+ os << "ALTER TABLE " << quote_id (t.name ()) << " ADD" << endl;
+ base::create (fk);
+
+ os << endl
+ << "*/" << endl
+ << endl;
+ }
+ }
+ else
+ {
+ pre_statement ();
+
+ os << "ALTER TABLE " << quote_id (t.name ()) << " ADD" << endl;
+ base::create (fk);
+ os << endl;
+
+ post_statement ();
+ }
+ }
+ }
+ };
+
+ void create_table::
+ traverse (sema_rel::table& t)
+ {
+ if (pass_ == 1)
+ {
+ tables_.insert (t.name ()); // Add it before to cover self-refs.
+ base::traverse (t);
+ return;
+ }
+
+ // Add foreign keys.
+ //
+ instance<add_foreign_key> fk (format_, *this);
+ trav_rel::names n (*fk);
+ names (t, n);
+ }
+ }
+ }
+}
diff --git a/odb/relational/mssql/source.cxx b/odb/relational/mssql/source.cxx
new file mode 100644
index 0000000..eddd782
--- /dev/null
+++ b/odb/relational/mssql/source.cxx
@@ -0,0 +1,1320 @@
+// file : odb/relational/mssql/source.cxx
+// author : Constantin Michael <constantin@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <sstream>
+
+#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 ()
+ {
+ return "";
+ }
+ };
+ entry<query_parameters> query_parameters_;
+
+ //
+ //
+ struct object_columns: relational::object_columns, context
+ {
+ object_columns (base const& x): base (x) {}
+
+ virtual void
+ column (semantics::data_member& m,
+ string const& key_prefix,
+ string const& table,
+ string const& column)
+ {
+ // Don't add a column for auto id in the INSERT statement.
+ //
+ if (!(sk_ == statement_insert &&
+ key_prefix.empty () &&
+ id (m) && auto_(m)))
+ {
+ base::column (m, key_prefix, table, column);
+ }
+ }
+ };
+ entry<object_columns> object_columns_;
+
+ //
+ // 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, member_base
+ {
+ bind_member (base const& x)
+ : member_base::base (x), // virtual base
+ base (x),
+ member_base (x)
+ {
+ }
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ if (container (mi))
+ return false;
+
+ ostringstream ostr;
+ ostr << "b[n]";
+ b = ostr.str ();
+
+ arg = arg_override_.empty () ? string ("i") : arg_override_;
+
+ if (var_override_.empty ())
+ {
+ os << "// " << mi.m.name () << endl
+ << "//" << endl;
+
+ if (id (mi.m) && auto_ (mi.m))
+ // For SQL Server we don't send auto id in INSERT.
+ //
+ os << "if (sk != statement_insert && sk != statement_update)"
+ << "{";
+ 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))
+ {
+ semantics::class_* c;
+
+ if (id (mi.m) ||
+ readonly (mi.m) ||
+ ((c = composite (mi.t)) && readonly (*c)))
+ os << "if (sk != statement_update)"
+ << "{";
+ }
+ }
+
+ return true;
+ }
+
+ virtual void
+ post (member_info& mi)
+ {
+ if (var_override_.empty ())
+ {
+ semantics::class_* c;
+
+ if ((c = composite (mi.t)))
+ {
+ bool ro (readonly (*c));
+ column_count_type const& cc (column_count (*c));
+
+ 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 (id (mi.m) && auto_ (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)))
+ block = true;
+ }
+
+ if (block)
+ os << "}";
+ else
+ os << endl;
+ }
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ os << "composite_value_traits< " << mi.fq_type () <<
+ " >::bind (b + n, " << arg << "." << mi.var << "value, sk);";
+ }
+
+ 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;";
+ }
+
+ private:
+ string b;
+ string arg;
+ };
+ entry<bind_member> bind_member_;
+
+ //
+ // init image
+ //
+
+ struct init_image_member: relational::init_image_member, member_base
+ {
+ init_image_member (base const& x)
+ : member_base::base (x), // virtual base
+ base (x),
+ member_base (x),
+ member_database_type_id_ (base::type_override_,
+ base::fq_type_override_,
+ base::key_prefix_)
+ {
+ }
+
+ 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 (!member_override_.empty ())
+ member = member_override_;
+ else
+ {
+ // If we are generating standard init() and this member
+ // contains version, ignore it.
+ //
+ if (version (mi.m))
+ return false;
+
+ // For SQL Server we don't send auto id in INSERT statement
+ // (nor in UPDATE, as for other databases). So ignore it
+ // altogether.
+ //
+ if (id (mi.m) && auto_ (mi.m))
+ return false;
+
+ string const& name (mi.m.name ());
+ member = "o." + name;
+
+ os << "// " << name << endl
+ << "//" << endl;
+
+ // If the whole class is readonly, then we will never be
+ // called with sk == statement_update.
+ //
+ if (!readonly (*context::top_object))
+ {
+ semantics::class_* c;
+
+ if (id (mi.m) ||
+ readonly (mi.m) ||
+ ((c = composite (mi.t)) && readonly (*c)))
+ os << "if (sk == statement_insert)";
+ }
+ }
+
+ // If this is a wrapped composite value, then we need to
+ // "unwrap" it. For simple values this is taken care of
+ // by the value_traits specializations.
+ //
+ if (mi.wrapper != 0 && composite (mi.t))
+ {
+ // Here we need the wrapper type, not the wrapped type.
+ //
+ member = "wrapper_traits< " + mi.fq_type (false) + " >::" +
+ "get_ref (" + member + ")";
+ }
+
+ if (composite (mi.t))
+ {
+ os << "{";
+ traits = "composite_value_traits< " + mi.fq_type () + " >";
+ }
+ else
+ {
+ // When handling a pointer, mi.t is the id type of the referenced
+ // object.
+ //
+ semantics::type& mt (member_utype (mi.m, key_prefix_));
+
+ if (semantics::class_* c = object_pointer (mt))
+ {
+ type = "obj_traits::id_type";
+ db_type_id = member_database_type_id_.database_type_id (mi.m);
+
+ // Handle NULL pointers and extract the id.
+ //
+ os << "{"
+ << "typedef object_traits< " << c->fq_name () <<
+ " > obj_traits;";
+
+ if (weak_pointer (mt))
+ {
+ os << "typedef pointer_traits< " << mi.fq_type () <<
+ " > wptr_traits;"
+ << "typedef 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 pointer_traits< " << mi.fq_type () <<
+ " > ptr_traits;"
+ << endl;
+
+ os << "bool is_null (ptr_traits::null_ptr (" << member << "));"
+ << "if (!is_null)"
+ << "{"
+ << "const " << type << "& id (" << endl;
+
+ if (lazy_pointer (mt))
+ os << "ptr_traits::object_id< ptr_traits::element_type > (" <<
+ member << ")";
+ else
+ os << "obj_traits::id (ptr_traits::get_ref (" << member << "))";
+
+ os << ");"
+ << endl;
+
+ member = "id";
+ }
+ else
+ {
+ type = mi.fq_type ();
+ db_type_id = member_database_type_id_.database_type_id (mi.m);
+
+ os << "{"
+ << "bool is_null;";
+ }
+
+ traits = "mssql::value_traits<\n "
+ + type + ",\n "
+ + db_type_id + " >";
+ }
+
+ return true;
+ }
+
+ virtual void
+ post (member_info& mi)
+ {
+ if (composite (mi.t))
+ os << "}";
+ else
+ {
+ // When handling a pointer, mi.t is the id type of the referenced
+ // object.
+ //
+ if (object_pointer (member_utype (mi.m, key_prefix_)))
+ {
+ os << "}";
+
+ if (!null (mi.m, key_prefix_))
+ os << "else" << endl
+ << "throw null_pointer ();";
+ }
+
+ os << "}";
+ }
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ os << traits << "::init (" << endl
+ << "i." << mi.var << "value," << endl
+ << member << "," << endl
+ << "sk);";
+ }
+
+ 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 ? " <<
+ "SQL_NULL_DATA : 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 ? " <<
+ "SQL_NULL_DATA : 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;";
+ }
+
+ private:
+ string type;
+ string db_type_id;
+ string member;
+ string traits;
+
+ member_database_type_id member_database_type_id_;
+ };
+ entry<init_image_member> init_image_member_;
+
+ //
+ // init value
+ //
+
+ struct init_value_member: relational::init_value_member, member_base
+ {
+ init_value_member (base const& x)
+ : member_base::base (x), // virtual base
+ base (x),
+ member_base (x),
+ member_database_type_id_ (base::type_override_,
+ base::fq_type_override_,
+ base::key_prefix_)
+ {
+ }
+
+ virtual bool
+ pre (member_info& mi)
+ {
+ if (container (mi))
+ return false;
+
+ if (!member_override_.empty ())
+ member = member_override_;
+ else
+ {
+ string const& name (mi.m.name ());
+ member = "o." + name;
+
+ if (mi.cq)
+ member = "const_cast< " + mi.fq_type (false) + "& > (" +
+ member + ")";
+
+ os << "// " << name << endl
+ << "//" << endl;
+ }
+
+ // If this is a wrapped composite value, then we need to
+ // "unwrap" it. For simple values this is taken care of
+ // by the value_traits specializations.
+ //
+ if (mi.wrapper != 0 && composite (mi.t))
+ {
+ // Here we need the wrapper type, not the wrapped type.
+ //
+ member = "wrapper_traits< " + mi.fq_type (false) + " >::" +
+ "set_ref (\n" + member + ")";
+ }
+
+ if (composite (mi.t))
+ traits = "composite_value_traits< " + mi.fq_type () + " >";
+ else
+ {
+ // When handling a pointer, mi.t is the id type of the referenced
+ // object.
+ //
+ semantics::type& mt (member_utype (mi.m, key_prefix_));
+
+ if (semantics::class_* c = object_pointer (mt))
+ {
+ type = "obj_traits::id_type";
+ db_type_id = member_database_type_id_.database_type_id (mi.m);
+
+ // Handle NULL pointers and extract the id.
+ //
+ os << "{"
+ << "typedef object_traits< " << c->fq_name () <<
+ " > obj_traits;"
+ << "typedef pointer_traits< " << mi.fq_type () <<
+ " > ptr_traits;"
+ << endl
+ << "if (i." << mi.var << "indicator == -1)" << endl;
+
+ if (null (mi.m, key_prefix_))
+ os << member << " = ptr_traits::pointer_type ();";
+ else
+ os << "throw null_pointer ();";
+
+ os << "else"
+ << "{"
+ << type << " id;";
+
+ member = "id";
+ }
+ else
+ {
+ type = mi.fq_type ();
+ db_type_id = member_database_type_id_.database_type_id (mi.m);
+ }
+
+ traits = "mssql::value_traits<\n "
+ + type + ",\n "
+ + db_type_id + " >";
+ }
+
+ return true;
+ }
+
+ virtual void
+ post (member_info& mi)
+ {
+ if (composite (mi.t))
+ return;
+
+ // When handling a pointer, mi.t is the id type of the referenced
+ // object.
+ //
+ semantics::type& mt (member_utype (mi.m, key_prefix_));
+
+ if (object_pointer (mt))
+ {
+ if (!member_override_.empty ())
+ member = member_override_;
+ else
+ {
+ member = "o." + mi.m.name ();
+
+ if (mi.cq)
+ member = "const_cast< " + mi.fq_type (false) + "& > (" +
+ member + ")";
+ }
+
+ if (lazy_pointer (mt))
+ os << member << " = ptr_traits::pointer_type (db, 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
+ << "db.load< obj_traits::object_type > (id));";
+
+ os << "}"
+ << "}";
+ }
+ }
+
+ virtual void
+ traverse_composite (member_info& mi)
+ {
+ os << traits << "::init (" << endl
+ << member << "," << endl
+ << "i." << mi.var << "value," << endl
+ << "db);"
+ << endl;
+ }
+
+ 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;
+ }
+
+ private:
+ string type;
+ string db_type_id;
+ string traits;
+ string member;
+
+ member_database_type_id member_database_type_id_;
+ };
+ entry<init_value_member> init_value_member_;
+
+ struct statement_columns_common: context
+ {
+ void
+ process (relational::statement_columns& cols, statement_kind sk)
+ {
+ using relational::statement_columns;
+
+ // Long data columns must come last in the SELECT statement.
+ //
+ if (sk != statement_select)
+ 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)
+ {
+ sql_type const& st (column_sql_type (*i->member, i->key_prefix));
+
+ bool l (false);
+
+ // The same "short/long data" tests as in common.cxx.
+ //
+ 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 ())
+ l = 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 ())
+ l = true;
+
+ break;
+ }
+ case sql_type::TEXT:
+ case sql_type::NTEXT:
+ case sql_type::IMAGE:
+ {
+ l = true;
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (l)
+ {
+ 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_all_statement ().stream_result ();"
+ << endl;
+ }
+
+ virtual void
+ process_statement_columns (relational::statement_columns& cols,
+ statement_kind sk)
+ {
+ statement_columns_common::process (cols, sk);
+ }
+ };
+ entry<container_traits> container_traits_;
+
+ struct class_: relational::class_, statement_columns_common
+ {
+ class_ (base const& x): base (x) {}
+
+ virtual void
+ init_auto_id (semantics::data_member&, string const&)
+ {
+ // We are not sending anything for IDENTITY columns.
+ }
+
+ virtual void
+ init_image_pre (type& /*c*/)
+ {
+ /*
+ @@ TODO
+
+ if (options.generate_query () && !(composite (c) || abstract (c)))
+ os << "if (i.change_callback_.callback != 0)" << endl
+ << "(i.change_callback_.callback) (i.change_callback_.context);"
+ << endl;
+ */
+ }
+
+ virtual void
+ init_value_extra ()
+ {
+ os << "sts.find_statement ().stream_result ();"
+ << "sts.find_statement ().free_result ();";
+ }
+
+ virtual void
+ free_statement_result ()
+ {
+ // Only free the result if there are no rows. Otherwise we
+ // need to keep the result alive until after we are done
+ // streaming long data.
+ //
+ os << "if (r == select_statement::no_data)" << endl
+ << "st.free_result ();"
+ << endl;
+ }
+
+ virtual void
+ persist_statement_extra (type& c,
+ relational::query_parameters&,
+ persist_position p)
+ {
+ if (p != persist_after_columns)
+ return;
+
+ semantics::data_member* id (id_member (c));
+
+ if (id != 0 && id->count ("auto"))
+ os << strlit (" OUTPUT INSERTED." + column_qname (*id)) << endl;
+ }
+
+ virtual void
+ process_statement_columns (relational::statement_columns& cols,
+ statement_kind sk)
+ {
+ statement_columns_common::process (cols, sk);
+ }
+ };
+ entry<class_> class_entry_;
+ }
+ }
+}
diff --git a/odb/relational/mysql/source.cxx b/odb/relational/mysql/source.cxx
index b709648..5d03ec7 100644
--- a/odb/relational/mysql/source.cxx
+++ b/odb/relational/mysql/source.cxx
@@ -118,26 +118,30 @@ namespace relational
return;
}
- line_ += "CONCAT(";
+ string r;
+
+ r += "CONCAT(";
if (!table.empty ())
{
- line_ += table;
- line_ += '.';
+ r += table;
+ r += '.';
}
- line_ += column;
- line_ += "+0,' ',";
+ r += column;
+ r += "+0,' ',";
if (!table.empty ())
{
- line_ += table;
- line_ += '.';
+ r += table;
+ r += '.';
}
- line_ += column;
+ r += column;
+
+ r += ")";
- line_ += ")";
+ sc_.push_back (relational::statement_column (r, m, key_prefix));
}
};
entry<object_columns> object_columns_;
@@ -157,11 +161,15 @@ namespace relational
return;
}
- line_ += "CONCAT(";
- line_ += column;
- line_ += "+0,' ',";
- line_ += column;
- line_ += ")";
+ string r;
+
+ r += "CONCAT(";
+ r += column;
+ r += "+0,' ',";
+ r += column;
+ r += ")";
+
+ sc_.push_back (relational::statement_column (r, m));
}
};
entry<view_columns> view_columns_;
diff --git a/odb/relational/oracle/source.cxx b/odb/relational/oracle/source.cxx
index af738c4..d47e203 100644
--- a/odb/relational/oracle/source.cxx
+++ b/odb/relational/oracle/source.cxx
@@ -928,8 +928,13 @@ namespace relational
}
virtual void
- persist_stmt_extra (type& c, relational::query_parameters& qp)
+ persist_statement_extra (type& c,
+ relational::query_parameters& qp,
+ persist_position p)
{
+ if (p != persist_after_values)
+ return;
+
semantics::data_member* id (id_member (c));
if (id != 0 && id->count ("auto"))
diff --git a/odb/relational/pgsql/source.cxx b/odb/relational/pgsql/source.cxx
index 00ee777..7469047 100644
--- a/odb/relational/pgsql/source.cxx
+++ b/odb/relational/pgsql/source.cxx
@@ -993,8 +993,13 @@ namespace relational
}
virtual void
- persist_stmt_extra (type& c, relational::query_parameters&)
+ persist_statement_extra (type& c,
+ relational::query_parameters&,
+ persist_position p)
{
+ if (p != persist_after_values)
+ return;
+
semantics::data_member* id (id_member (c));
if (id != 0 && id->count ("auto"))
diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx
index 6e92607..ecdd5d1 100644
--- a/odb/relational/source.hxx
+++ b/odb/relational/source.hxx
@@ -8,6 +8,7 @@
#include <map>
#include <set>
+#include <list>
#include <vector>
#include <sstream>
@@ -22,11 +23,31 @@ 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& c,
+ semantics::data_member& m,
+ std::string const& kp = "")
+ : column (c), member (&m), key_prefix (kp)
+ {
+ }
+
+ std::string column;
+ 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.
+ // object id member when generating the persist statement. If empty
+ // string is returned, then parameter is ignored.
//
struct query_parameters: virtual context
{
@@ -50,23 +71,21 @@ namespace relational
typedef object_columns base;
object_columns (statement_kind sk,
- bool last = true,
+ statement_columns& sc,
query_parameters* param = 0)
- : sk_ (sk), param_ (param), last_ (last)
+ : sk_ (sk), sc_ (sc), param_ (param)
{
}
object_columns (std::string const& table_qname,
statement_kind sk,
- bool last = true)
- : sk_ (sk), param_ (0), table_name_ (table_qname), last_ (last)
+ statement_columns& sc)
+ : sk_ (sk), sc_ (sc), param_ (0), table_name_ (table_qname)
{
}
virtual bool
- traverse_column (semantics::data_member& m,
- string const& name,
- bool first)
+ traverse_column (semantics::data_member& m, string const& name, bool)
{
semantics::data_member* im (inverse (m));
@@ -82,25 +101,6 @@ namespace relational
sk_ == statement_update)
return false;
- if (!first)
- {
- line_ += ',';
- os << strlit (line_) << endl;
- }
-
- line_.clear ();
-
- // Version column (optimistic concurrency) requires special
- // handling in the UPDATE statement.
- //
- //
- if (sk_ == statement_update && version (m))
- {
- string const& qname (quote_id (name));
- line_ = qname + "=" + qname + "+1";
- return true;
- }
-
// Inverse object pointers come from a joined table.
//
if (im != 0)
@@ -114,12 +114,12 @@ namespace relational
// aliases for container tables so use the actual table name.
//
column (
- *im, "id",
+ *im,
+ "id",
table_name_.empty ()
? table_name_
: table_qname (*im, table_prefix (table_name (*c) + "_", 1)),
column_qname (*im, "id", "object_id"));
-
}
else
{
@@ -128,65 +128,64 @@ namespace relational
// Use the join alias (column name) instead of the actual
// table name unless we are handling a container.
//
- column (id, "",
- table_name_.empty () ? table_name_ : quote_id (name),
- column_qname (id));
+ column (
+ id,
+ "",
+ table_name_.empty () ? table_name_ : quote_id (name),
+ column_qname (id));
}
}
else
column (m, "", table_name_, quote_id (name));
- if (param_ != 0)
- {
- line_ += '=';
- line_ += param_->next ();
- }
-
return true;
}
virtual void
- column (semantics::data_member&,
- string const& /*key_prefix*/,
+ column (semantics::data_member& m,
+ string const& key_prefix,
string const& table,
string const& column)
{
+ string r;
+
if (!table.empty ())
{
- line_ += table;
- line_ += '.';
+ r += table; // Already quoted.
+ r += '.';
}
- line_ += column; // Already quoted.
- }
+ r += column; // Already quoted.
- virtual void
- flush ()
- {
- if (!last_)
- line_ += ',';
-
- if (!line_.empty ())
- os << strlit (line_);
+ // 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 += param_->next ();
+ }
- os << endl;
+ sc_.push_back (statement_column (r, m, key_prefix));
}
protected:
statement_kind sk_;
+ statement_columns& sc_;
query_parameters* param_;
- string line_;
string table_name_;
-
- private:
- bool last_;
};
struct view_columns: object_columns_base, virtual context
{
typedef view_columns base;
- view_columns (): in_composite_ (false) {}
+ view_columns (statement_columns& sc): sc_ (sc), in_composite_ (false) {}
virtual void
traverse_composite (semantics::data_member* pm, semantics::class_& c)
@@ -261,18 +260,8 @@ namespace relational
}
virtual bool
- traverse_column (semantics::data_member& m,
- string const& name,
- bool first)
+ traverse_column (semantics::data_member& m, string const& name, bool)
{
- if (!first)
- {
- line_ += ',';
- os << strlit (line_) << endl;
- }
-
- line_.clear ();
-
string col;
// If we are inside a composite value, use the standard
@@ -346,7 +335,6 @@ namespace relational
}
column (m, col);
-
return true;
}
@@ -354,20 +342,13 @@ namespace relational
// expression.
//
virtual void
- column (semantics::data_member&, string const& column)
+ column (semantics::data_member& m, string const& column)
{
- line_ += column;
- }
-
- virtual void
- flush ()
- {
- if (!line_.empty ())
- os << strlit (line_) << endl;
+ sc_.push_back (statement_column (column, m));
}
protected:
- string line_;
+ statement_columns& sc_;
bool in_composite_;
string table_prefix_; // Table corresponding to column_prefix_;
};
@@ -810,8 +791,8 @@ namespace relational
os << statement << ".cache ();";
}
- // Additional statements that need to be executed follow the call to init
- // that initializes the query result image can be made here.
+ // Additional code that need to be executed following the call to
+ // init_value.
//
virtual void
init_value_extra ()
@@ -819,6 +800,11 @@ namespace relational
}
virtual void
+ process_statement_columns (statement_columns&, statement_kind)
+ {
+ }
+
+ virtual void
traverse_composite (semantics::data_member* m, semantics::class_& c)
{
if (object (c_))
@@ -946,6 +932,7 @@ namespace relational
string inv_table; // Other table name.
string inv_id; // Other id column.
string inv_fid; // Other foreign id column (ref to us).
+ statement_columns sc;
if (container (*im))
{
@@ -959,22 +946,39 @@ namespace relational
inv_table = table_qname (*im, tp);
inv_id = column_qname (*im, "id", "object_id");
inv_fid = column_qname (*im, "value", "value");
+
+ sc.push_back (statement_column (
+ inv_table + "." + inv_fid, *im, "value"));
+ sc.push_back (statement_column (
+ inv_table + "." + inv_id, *im, "id"));
}
else
{
// many(i)-to-one
//
+ semantics::data_member& id (*id_member (*c));
+
inv_table = table_qname (*c);
- inv_id = column_qname (*id_member (*c));
+ inv_id = column_qname (id);
inv_fid = column_qname (*im);
+
+ sc.push_back (statement_column (inv_table + "." + inv_fid, *im));
+ sc.push_back (statement_column (inv_table + "." + inv_id, id));
}
- instance<query_parameters> qp;
+ process_statement_columns (sc, statement_select);
+
+ os << strlit ("SELECT ") << endl;
- os << strlit ("SELECT ") << endl
- << strlit (inv_table + "." + inv_fid + ',') << endl
- << strlit (inv_table + "." + inv_id) << endl
- << strlit (" FROM " + inv_table +
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "")) << endl;
+ }
+
+ instance<query_parameters> qp;
+ os << strlit (" FROM " + inv_table +
" WHERE " + inv_table + "." + inv_fid + "=" +
qp->next ());
}
@@ -982,8 +986,11 @@ namespace relational
{
string const& id_col (column_qname (m, "id", "object_id"));
- os << strlit ("SELECT ") << endl
- << strlit (table + "." + id_col + ',') << endl;
+ statement_columns sc;
+ sc.push_back (statement_column (table + "." + id_col, m, "id"));
+
+ statement_kind sk (statement_select); // Imperfect forwarding.
+ instance<object_columns> t (table, sk, sc);
switch (ck)
{
@@ -991,25 +998,20 @@ namespace relational
{
if (ordered)
{
- instance<object_columns> t (table, statement_select, false);
string const& col (column_qname (m, "index", "index"));
t->column (m, "index", table, col);
- t->flush ();
}
break;
}
case ck_map:
case ck_multimap:
{
- instance<object_columns> t (table, statement_select, false);
-
if (semantics::class_* ckt = composite_wrapper (*kt))
t->traverse (m, *ckt, "key", "key");
else
{
string const& col (column_qname (m, "key", "key"));
t->column (m, "key", table, col);
- t->flush ();
}
break;
}
@@ -1020,15 +1022,23 @@ namespace relational
}
}
- instance<object_columns> t (table, statement_select);
-
if (semantics::class_* cvt = composite_wrapper (vt))
t->traverse (m, *cvt, "value", "value");
else
{
string const& col (column_qname (m, "value", "value"));
t->column (m, "value", table, col);
- t->flush ();
+ }
+
+ process_statement_columns (sc, statement_select);
+
+ os << 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;
}
instance<query_parameters> qp;
@@ -1059,33 +1069,30 @@ namespace relational
<< endl;
else
{
- os << strlit ("INSERT INTO " + table + " (") << endl
- << strlit (column_qname (m, "id", "object_id") + ',') << endl;
+ statement_columns sc;
+ sc.push_back (
+ statement_column (
+ column_qname (m, "id", "object_id"), m, "id"));
+
+ statement_kind sk (statement_insert); // Imperfect forwarding.
+ instance<object_columns> t (sk, sc);
switch (ck)
{
case ck_ordered:
{
if (ordered)
- {
- instance<object_columns> t (statement_insert, false);
- t->column (m, "index", "", column_qname (m, "index", "index"));
- t->flush ();
- }
+ t->column (
+ m, "index", "", column_qname (m, "index", "index"));
break;
}
case ck_map:
case ck_multimap:
{
- instance<object_columns> t (statement_insert, false);
-
if (semantics::class_* ckt = composite_wrapper (*kt))
t->traverse (m, *ckt, "key", "key");
else
- {
t->column (m, "key", "", column_qname (m, "key", "key"));
- t->flush ();
- }
break;
}
case ck_set:
@@ -1095,14 +1102,20 @@ namespace relational
}
}
- instance<object_columns> t (statement_insert);
-
if (semantics::class_* cvt = composite_wrapper (vt))
t->traverse (m, *cvt, "value", "value");
else
- {
t->column (m, "value", "", column_qname (m, "value", "value"));
- t->flush ();
+
+ process_statement_columns (sc, statement_insert);
+
+ os << strlit ("INSERT INTO " + table + " (") << 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;
}
string values;
@@ -1116,7 +1129,7 @@ namespace relational
values += qp->next ();
}
- os << strlit (") VALUES (" + values + ")") << ";"
+ os << strlit (" VALUES (" + values + ")") << ";"
<< endl;
}
@@ -2108,15 +2121,22 @@ namespace relational
{
if (!inverse (m))
{
- if (count_++ != 0)
- params_ += ',';
+ string p;
if (version (m))
- params_ += "1";
- else if (m.count ("id") && m.count ("auto"))
- params_ += qp_.auto_id ();
+ p = "1";
+ else if (id (m) && auto_ (m))
+ p = qp_.auto_id ();
else
- params_ += qp_.next ();
+ p = qp_.next ();
+
+ if (!p.empty ())
+ {
+ if (count_++ != 0)
+ params_ += ',';
+
+ params_ += p;
+ }
}
}
@@ -2247,8 +2267,14 @@ namespace relational
// statements
//
+ enum persist_position
+ {
+ persist_after_columns,
+ persist_after_values
+ };
+
virtual void
- persist_stmt_extra (type&, query_parameters&)
+ persist_statement_extra (type&, query_parameters&, persist_position)
{
}
@@ -2261,6 +2287,11 @@ namespace relational
{
}
+ virtual void
+ process_statement_columns (statement_columns&, statement_kind)
+ {
+ }
+
//
// object
//
@@ -2271,6 +2302,12 @@ namespace relational
}
virtual void
+ free_statement_result ()
+ {
+ os << "st.free_result ();";
+ }
+
+ virtual void
object_query_statement_ctor_args (type&)
{
os << "sts.connection ()," << endl
@@ -2575,21 +2612,43 @@ namespace relational
// persist_statement
//
{
+ 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);
+ }
+
+ bool dv (sc.empty ()); // The DEFAULT VALUES syntax.
+
os << "const char " << traits << "::persist_statement[] " <<
"=" << endl
- << strlit ("INSERT INTO " + table_qname(c) + " (") << endl;
+ << strlit ("INSERT INTO " + table_qname(c) +
+ (dv ? "" : " (")) << endl;
- instance<object_columns> ct (statement_insert);
- ct->traverse (c);
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : ")")) << endl;
+ }
- string values;
instance<query_parameters> qp;
- instance<persist_statement_params> pt (values, *qp);
- pt->traverse (c);
- os << strlit (") VALUES (" + values + ")");
+ persist_statement_extra (c, *qp, persist_after_columns);
- persist_stmt_extra (c, *qp);
+ if (!dv)
+ {
+ string values;
+ instance<persist_statement_params> pt (values, *qp);
+ pt->traverse (c);
+ os << strlit (" VALUES (" + values + ")");
+ }
+ else
+ os << strlit (" DEFAULT VALUES");
+
+ persist_statement_extra (c, *qp, persist_after_values);
os << ";"
<< endl;
@@ -2602,11 +2661,23 @@ namespace relational
// find_statement
//
{
+ statement_columns sc;
+ {
+ statement_kind sk (statement_select); // Imperfect forwarding.
+ instance<object_columns> t (table, sk, sc);
+ t->traverse (c);
+ process_statement_columns (sc, statement_select);
+ }
+
os << "const char " << traits << "::find_statement[] =" << endl
<< strlit ("SELECT ") << endl;
- instance<object_columns> t (table, statement_select);
- t->traverse (c);
+ 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 " + table) << endl;
@@ -2625,13 +2696,27 @@ namespace relational
//
if (cc.total != cc.id + cc.inverse + cc.readonly)
{
+ instance<query_parameters> qp;
+
+ statement_columns sc;
+ {
+ query_parameters* p (qp.get ()); // Imperfect forwarding.
+ statement_kind sk (statement_update); // Imperfect forwarding.
+ instance<object_columns> t (sk, sc, p);
+ t->traverse (c);
+ process_statement_columns (sc, statement_update);
+ }
+
os << "const char " << traits << "::update_statement[] " <<
"=" << endl
<< strlit ("UPDATE " + table + " SET ") << endl;
- instance<query_parameters> qp;
- instance<object_columns> t (statement_update, true, qp.get ());
- t->traverse (c);
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
+ {
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "")) << endl;
+ }
string where (" WHERE " + id_col + "=" + qp->next ());
@@ -2672,6 +2757,14 @@ namespace relational
{
// query_statement
//
+ statement_columns sc;
+ {
+ statement_kind sk (statement_select); // Imperfect forwarding.
+ instance<object_columns> oc (table, sk, sc);
+ oc->traverse (c);
+ process_statement_columns (sc, statement_select);
+ }
+
bool t (true);
instance<object_joins> oj (c, t); //@@ (im)perfect forwarding
oj->traverse (c);
@@ -2679,9 +2772,11 @@ namespace relational
os << "const char " << traits << "::query_statement[] =" << endl
<< strlit ("SELECT ") << endl;
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
{
- instance<object_columns> oc (table, statement_select);
- oc->traverse (c);
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "")) << endl;
}
os << strlit (" FROM " + table) << endl;
@@ -3235,8 +3330,9 @@ namespace relational
<< "}"
<< "}";
- os << "st.free_result ();"
- << "return r != select_statement::no_data;"
+ free_statement_result ();
+
+ os << "return r != select_statement::no_data;"
<< "}";
}
@@ -3695,14 +3791,21 @@ namespace relational
}
else // vq.kind == view_query::condition
{
+ statement_columns sc;
+ {
+ instance<view_columns> t (sc);
+ t->traverse (c);
+ process_statement_columns (sc, statement_select);
+ }
+
os << "query_base_type r (" << endl
<< strlit ("SELECT ") << endl;
- // Generate select-list.
- //
+ for (statement_columns::const_iterator i (sc.begin ()),
+ e (sc.end ()); i != e;)
{
- instance<view_columns> t;
- t->traverse (c);
+ string const& c (i->column);
+ os << strlit (c + (++i != e ? "," : "")) << endl;
}
os << ");"