From c6d92f2f979eae471f49d9af1768b7b05f3a6f6f Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 21 Dec 2011 11:19:25 +0200 Subject: ODB compiler implementation, traits, and types test for SQL Server --- odb/context.cxx | 6 + odb/generator.cxx | 5 + odb/makefile | 10 + odb/odb.cxx | 4 +- odb/option-functions.cxx | 1 + odb/option-types.cxx | 1 + odb/option-types.hxx | 1 + odb/options.cli | 56 +- odb/relational/mssql/common.cxx | 558 ++++++++++++++++ odb/relational/mssql/common.hxx | 362 +++++++++++ odb/relational/mssql/context.cxx | 577 +++++++++++++++++ odb/relational/mssql/context.hxx | 142 ++++ odb/relational/mssql/header.cxx | 244 +++++++ odb/relational/mssql/model.cxx | 74 +++ odb/relational/mssql/schema.cxx | 224 +++++++ odb/relational/mssql/source.cxx | 1320 ++++++++++++++++++++++++++++++++++++++ odb/relational/mysql/source.cxx | 36 +- odb/relational/oracle/source.cxx | 7 +- odb/relational/pgsql/source.cxx | 7 +- odb/relational/source.hxx | 389 ++++++----- 20 files changed, 3845 insertions(+), 179 deletions(-) create mode 100644 odb/relational/mssql/common.cxx create mode 100644 odb/relational/mssql/common.hxx create mode 100644 odb/relational/mssql/context.cxx create mode 100644 odb/relational/mssql/context.hxx create mode 100644 odb/relational/mssql/header.cxx create mode 100644 odb/relational/mssql/model.cxx create mode 100644 odb/relational/mssql/schema.cxx create mode 100644 odb/relational/mssql/source.cxx 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 #include +#include #include #include #include @@ -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 { "", - "Generate code for the database. Valid values are \cb{mysql}, - \cb{oracle}, \cb{pgsql}, and \cb{sqlite}." + "Generate code for the 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 + { + "", + "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 +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +#include + +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_; + } +} 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 +// 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 +#include + +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 ("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 +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include +#include + +#include +#include + +#include + +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 (root_context::data_.get ()), m), + data_ (static_cast (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 ()) + 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 (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 +// 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 + +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 +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +#include +#include + +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_; + + 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_; + } + } +} 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 +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +#include + +#include +#include + +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 (*unit.find (en))); + + ostringstream ostr; + + if (e.enum_ ().unsigned_ ()) + ostr << e.value (); + else + ostr << static_cast (e.value ()); + + return ostr.str (); + } + }; + entry 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 +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +#include + +#include +#include + +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_; + + // + // 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_; + + // + // 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_; + + 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 tables_; // Set of tables we have already defined. + }; + entry 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_)); + + 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 (fk.scope ()).name () + + '_' + fk.name (); + } + + virtual void + deferred () + { + // SQL Server doesn't support deferred. + } + }; + entry 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 (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 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 +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +#include + +#include +#include + +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_; + + // + // + 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_; + + // + // 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 (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 (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 (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_; + + // + // 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 (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 (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 (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 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 (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 (" << + "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 (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_; + + 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_; + + 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_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_; @@ -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_; 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 #include +#include #include #include @@ -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_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 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 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 t (table, sk, sc); switch (ck) { @@ -991,25 +998,20 @@ namespace relational { if (ordered) { - instance 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 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 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 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 t (sk, sc); switch (ck) { case ck_ordered: { if (ordered) - { - instance 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 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 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 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 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 qp; - instance 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 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 t (table, sk, sc); + t->traverse (c); + process_statement_columns (sc, statement_select); + } + os << "const char " << traits << "::find_statement[] =" << endl << strlit ("SELECT ") << endl; - instance 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 qp; + + statement_columns sc; + { + query_parameters* p (qp.get ()); // Imperfect forwarding. + statement_kind sk (statement_update); // Imperfect forwarding. + instance 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 qp; - instance 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 oc (table, sk, sc); + oc->traverse (c); + process_statement_columns (sc, statement_select); + } + bool t (true); instance 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 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 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 t; - t->traverse (c); + string const& c (i->column); + os << strlit (c + (++i != e ? "," : "")) << endl; } os << ");" -- cgit v1.1