aboutsummaryrefslogtreecommitdiff
path: root/odb
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2012-01-20 10:30:22 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2012-01-20 15:43:46 +0200
commit64cc9e9f0ed1ac6742ce9d5b370bf7de7b1cb461 (patch)
treeade429bd15d070efc66386d455745cb64ce8af22 /odb
parent89c06fb9ce3470c7a1b55cc906f2165791917957 (diff)
Work around SQL Server 2005 bug with long data and OUTPUT clause
Diffstat (limited to 'odb')
-rw-r--r--odb/option-types.cxx48
-rw-r--r--odb/option-types.hxx74
-rw-r--r--odb/options.cli26
-rw-r--r--odb/relational/mssql/common.hxx63
-rw-r--r--odb/relational/mssql/source.cxx35
5 files changed, 235 insertions, 11 deletions
diff --git a/odb/option-types.cxx b/odb/option-types.cxx
index 715577d..fcd7b88 100644
--- a/odb/option-types.cxx
+++ b/odb/option-types.cxx
@@ -12,8 +12,10 @@
using namespace std;
+//
// database
//
+
static const char* database_[] =
{
"mssql",
@@ -55,8 +57,10 @@ operator<< (ostream& os, database db)
return os << db.string ();
}
+//
// schema_format
//
+
static const char* schema_format_[] =
{
"embedded",
@@ -95,8 +99,10 @@ operator<< (ostream& os, schema_format sf)
return os << sf.string ();
}
+//
// oracle_version
//
+
istream&
operator>> (istream& is, oracle_version& v)
{
@@ -134,3 +140,45 @@ operator<< (ostream& os, oracle_version v)
{
return os << v.ver_major () << '.' << v.ver_minor ();
}
+
+//
+// mssql_version
+//
+
+istream&
+operator>> (istream& is, mssql_version& v)
+{
+ unsigned short major, minor;
+
+ // Extract the major version.
+ //
+ is >> major;
+
+ if (!is.fail ())
+ {
+ // Extract the decimal point.
+ //
+ char p;
+ is >> p;
+
+ if (!is.fail () && p == '.')
+ {
+ // Extract the minor version.
+ //
+ is >> minor;
+
+ if (!is.fail ())
+ v = mssql_version (major, minor);
+ }
+ else
+ is.setstate (istream::failbit);
+ }
+
+ return is;
+}
+
+ostream&
+operator<< (ostream& os, mssql_version v)
+{
+ return os << v.ver_major () << '.' << v.ver_minor ();
+}
diff --git a/odb/option-types.hxx b/odb/option-types.hxx
index 2b96941..b674eae 100644
--- a/odb/option-types.hxx
+++ b/odb/option-types.hxx
@@ -69,8 +69,6 @@ operator<< (std::ostream&, schema_format);
//
struct oracle_version
{
- oracle_version (): major_ (0), minor_ (0) {}
-
oracle_version (unsigned short major, unsigned short minor)
: major_ (major), minor_ (minor)
{
@@ -139,4 +137,76 @@ operator>> (std::istream&, oracle_version&);
std::ostream&
operator<< (std::ostream&, oracle_version);
+//
+//
+struct mssql_version
+{
+ mssql_version (unsigned short major, unsigned short minor)
+ : major_ (major), minor_ (minor)
+ {
+ }
+
+ unsigned short
+ ver_major () const
+ {
+ return major_;
+ }
+
+ unsigned short
+ ver_minor () const
+ {
+ return minor_;
+ }
+
+private:
+ unsigned short major_;
+ unsigned short minor_;
+};
+
+inline bool
+operator== (const mssql_version& x, const mssql_version& y)
+{
+ return x.ver_major () == y.ver_major ();
+}
+
+inline bool
+operator!= (const mssql_version& x, const mssql_version& y)
+{
+ return !(x == y);
+}
+
+inline bool
+operator< (const mssql_version& x, const mssql_version& y)
+{
+ return x.ver_major () < y.ver_major () ||
+ (x.ver_major () == y.ver_major () &&
+ x.ver_minor () < y.ver_minor ());
+}
+
+inline bool
+operator> (const mssql_version& x, const mssql_version& y)
+{
+ return x.ver_major () > y.ver_major () ||
+ (x.ver_major () == y.ver_major () &&
+ x.ver_minor () > y.ver_minor ());
+}
+
+inline bool
+operator<= (const mssql_version& x, const mssql_version& y)
+{
+ return !(x > y);
+}
+
+inline bool
+operator>= (const mssql_version& x, const mssql_version& y)
+{
+ return !(x < y);
+}
+
+std::istream&
+operator>> (std::istream&, mssql_version&);
+
+std::ostream&
+operator<< (std::ostream&, mssql_version);
+
#endif // ODB_OPTION_TYPES_HXX
diff --git a/odb/options.cli b/odb/options.cli
index 4287231..fedc00a 100644
--- a/odb/options.cli
+++ b/odb/options.cli
@@ -430,12 +430,24 @@ class options
// SQL Server-specific options.
//
+ ::mssql_version --mssql-server-version (10, 0)
+ {
+ "<v>",
+ "Specify the minimum SQL Server server version with which the generated
+ C++ code will be used. This information is used to enable
+ version-specific optimizations and workarounds in the generated C++
+ code. The version must be in the \c{\i{major}\b{.}\i{minor}} form, for
+ example, \cb{9.0} (SQL Server 2005), \cb{10.5} (2008R2), or \cb{11.0}
+ (2012). If this options is not specified, then 10.0 (SQL Server 2008)
+ or later is assumed."
+ };
+
unsigned int --mssql-short-limit = 1024
{
"<size>",
- "Specify the short data size limit. If character, national character,
- or binary data type has a maximum length (in bytes) less than this
- limit, then it is treated as \i{short data}, otherwise it is \i{long
+ "Specify the short data size limit. If character, national character, or
+ binary data type has a maximum length (in bytes) less than or equal to
+ this limit, then it is treated as \i{short data}, otherwise it is \i{long
data}. For short data ODB pre-allocates an intermediate buffer of
the maximum size and binds it directly to a parameter or result
column. This way the underlying API (ODBC) can read/write directly
@@ -466,13 +478,15 @@ class options
// Oracle-specific options.
//
- ::oracle_version --oracle-client-version
+ ::oracle_version --oracle-client-version (10, 1)
{
"<v>",
"Specify the minimum Oracle client library (OCI) version with which the
generated C++ code will be linked. This information is used to enable
- version-specific optimizations in the generated C++ code. The version
- must be in the \c{\i{major}\b{.}\i{minor}} form, for example, \cb{11.2}."
+ version-specific optimizations and workarounds in the generated C++
+ code. The version must be in the \c{\i{major}\b{.}\i{minor}} form,
+ for example, \cb{11.2}. If this options is not specified, then 10.1
+ or later is assumed."
};
//
diff --git a/odb/relational/mssql/common.hxx b/odb/relational/mssql/common.hxx
index 5bd37f5..eacc435 100644
--- a/odb/relational/mssql/common.hxx
+++ b/odb/relational/mssql/common.hxx
@@ -357,6 +357,69 @@ namespace relational
private:
string type_id_;
};
+
+ struct has_long_data: object_columns_base, context
+ {
+ has_long_data (bool& r): r_ (r) {}
+
+ virtual bool
+ traverse_column (semantics::data_member& m, string const&, bool)
+ {
+ if (inverse (m))
+ return false;
+
+ sql_type const& st (column_sql_type (m));
+
+ switch (st.type)
+ {
+ 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 ())
+ r_ = true;
+
+ break;
+ }
+ case sql_type::NCHAR:
+ case sql_type::NVARCHAR:
+ {
+ // Zero precision means max in NVARCHAR(max). Note that
+ // the precision is in 2-byte UCS-2 characters, not bytes.
+ //
+ if (st.prec == 0 || st.prec * 2 > options.mssql_short_limit ())
+ r_ = true;
+
+ break;
+ }
+ case sql_type::BINARY:
+ case sql_type::VARBINARY:
+ {
+ // Zero precision means max in VARCHAR(max).
+ //
+ if (st.prec == 0 || st.prec > options.mssql_short_limit ())
+ r_ = true;
+
+ break;
+ }
+ case sql_type::TEXT:
+ case sql_type::NTEXT:
+ case sql_type::IMAGE:
+ {
+ r_ = true;
+ break;
+ }
+ default:
+ break;
+ }
+
+ return true;
+ }
+
+ private:
+ bool& r_;
+ };
}
}
#endif // ODB_RELATIONAL_MSSQL_COMMON_HXX
diff --git a/odb/relational/mssql/source.cxx b/odb/relational/mssql/source.cxx
index 0e449dd..afb5887 100644
--- a/odb/relational/mssql/source.cxx
+++ b/odb/relational/mssql/source.cxx
@@ -1297,12 +1297,41 @@ namespace relational
relational::query_parameters&,
persist_position p)
{
- if (p != persist_after_columns)
+ semantics::data_member* id (id_member (c));
+
+ if (id == 0 || !auto_ (*id))
return;
- semantics::data_member* id (id_member (c));
+ // SQL Server 2005 has a bug that causes it to fail on an
+ // INSERT statement with the OUTPUT clause if data for one
+ // of the inserted columns is supplied at execution (long
+ // data). To work around this problem we use the less
+ // efficient batch of INSERT and SELECT statements.
+ //
+ if (options.mssql_server_version () <= mssql_version (9, 0))
+ {
+ bool ld (false);
+
+ if (c.count ("mssql-has-long-data"))
+ ld = c.get<bool> ("mssql-has-long-data");
+ else
+ {
+ has_long_data t (ld);
+ t.traverse (c);
+ c.set ("mssql-has-long-data", ld);
+ }
+
+ if (ld)
+ {
+ if (p == persist_after_values)
+ os << endl
+ << strlit ("; SELECT SCOPE_IDENTITY()");
+
+ return;
+ }
+ }
- if (id != 0 && id->count ("auto"))
+ if (p == persist_after_columns)
os << strlit (" OUTPUT INSERTED." + column_qname (*id)) << endl;
}