aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2011-11-18 14:54:27 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2011-11-18 14:54:27 +0200
commite075f77af3a09c22a8bec660c69c9fa7a9808d8e (patch)
treef6aded862398269756ecdb96ac1198943ade8a4d
parenta46dd03b3003b5e3bf87051ea49cea3d2f6848a8 (diff)
Implement remaining database constructors, update options
-rw-r--r--odb/mssql/database.cxx494
-rw-r--r--odb/mssql/database.hxx126
-rw-r--r--odb/mssql/details/options.cli33
3 files changed, 479 insertions, 174 deletions
diff --git a/odb/mssql/database.cxx b/odb/mssql/database.cxx
index 0eb0ed9..bafe176 100644
--- a/odb/mssql/database.cxx
+++ b/odb/mssql/database.cxx
@@ -3,8 +3,7 @@
// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
// license : ODB NCUEL; see accompanying LICENSE file
-//@@ disabled functionality
-
+#include <cstring> // std::strcmp, std::strncmp
#include <sstream>
#include <odb/mssql/mssql.hxx>
@@ -21,195 +20,426 @@ namespace odb
namespace mssql
{
database::
- database (const string& c, SQLHENV e, auto_ptr<connection_factory> f)
- : connect_string_ (c), environment_ (e), factory_ (f)
+ database (const std::string& user,
+ const std::string& password,
+ const std::string& db,
+ const std::string& server,
+ const std::string& driver,
+ const std::string& extra_connect_string,
+ SQLHENV environment,
+ std::auto_ptr<connection_factory> factory)
+ : user_ (user),
+ password_ (password),
+ db_ (db),
+ protocol_ (protocol_auto),
+ port_ (0),
+ server_ (server),
+ driver_ (driver),
+ extra_connect_string_ (extra_connect_string),
+ environment_ (environment),
+ factory_ (factory)
{
- if (environment_ == 0)
- {
- SQLRETURN r (
- SQLAllocHandle (SQL_HANDLE_ENV,
- SQL_NULL_HANDLE,
- &environment_));
-
- if (!SQL_SUCCEEDED (r))
- throw database_exception (
- 0, "?????", "unable to allocate environment handle");
-
- auto_environment_.reset (environment_);
-
- // Set ODBC version.
- //
- r = SQLSetEnvAttr (environment_,
- SQL_ATTR_ODBC_VERSION,
- (SQLPOINTER) SQL_OV_ODBC3,
- 0);
-
- if (!SQL_SUCCEEDED (r))
- translate_error (environment_, SQL_HANDLE_ENV);
- }
-
- if (factory_.get () == 0)
- factory_.reset (new connection_pool_factory ());
+ init ();
+ }
- factory_->database (*this);
+ database::
+ database (const std::string& user,
+ const std::string& password,
+ const std::string& db,
+ protocol_type protocol,
+ const std::string& host,
+ const std::string& instance,
+ const std::string& driver,
+ const std::string& extra_connect_string,
+ SQLHENV environment,
+ std::auto_ptr<connection_factory> factory)
+ : user_ (user),
+ password_ (password),
+ db_ (db),
+ protocol_ (protocol),
+ host_ (host),
+ instance_ (instance),
+ port_ (0),
+ driver_ (driver),
+ extra_connect_string_ (extra_connect_string),
+ environment_ (environment),
+ factory_ (factory)
+ {
+ init ();
}
- /*
database::
- database (const string& user,
- const string& password,
- const string& service,
- const string& host,
+ database (const std::string& user,
+ const std::string& password,
+ const std::string& db,
+ const std::string& host,
unsigned int port,
- ub2 charset,
- ub2 ncharset,
- OCIEnv* environment,
- auto_ptr<connection_factory> factory)
+ const std::string& driver,
+ const std::string& extra_connect_string,
+ SQLHENV environment,
+ std::auto_ptr<connection_factory> factory)
: user_ (user),
password_ (password),
- service_ (service),
+ db_ (db),
+ protocol_ (protocol_tcp),
host_ (host),
port_ (port),
- charset_ (charset),
- ncharset_ (ncharset),
+ driver_ (driver),
+ extra_connect_string_ (extra_connect_string),
environment_ (environment),
factory_ (factory)
{
- if (environment_ == 0)
- {
- sword s (OCIEnvNlsCreate (&environment_,
- OCI_THREADED,
- 0, 0, 0, 0, 0, 0,
- charset,
- ncharset));
+ init ();
+ }
+
+ database::
+ database (const string& connect_string,
+ SQLHENV environment,
+ auto_ptr<connection_factory> factory)
+ : protocol_ (protocol_auto),
+ port_ (0),
+ connect_string_ (connect_string),
+ environment_ (environment),
+ factory_ (factory)
+ {
+ init ();
+ }
- if (s == OCI_ERROR)
- translate_error (environment_);
+ database::
+ database (int& argc,
+ char* argv[],
+ bool erase,
+ const std::string& extra_connect_string,
+ SQLHENV environment,
+ auto_ptr<connection_factory> factory)
+ : protocol_ (protocol_auto),
+ port_ (0),
+ extra_connect_string_ (extra_connect_string),
+ environment_ (environment),
+ factory_ (factory)
+ {
+ using namespace details;
- auto_environment_.reset (environment_);
+ try
+ {
+ cli::argv_file_scanner scan (argc, argv, "--options-file", erase);
+ options ops (scan, cli::unknown_mode::skip, cli::unknown_mode::skip);
+
+ user_ = ops.user ();
+ password_ = ops.password ();
+ db_ = ops.database ();
+ server_ = ops.server ();
+ driver_ = ops.driver ();
+ }
+ catch (const cli::exception& e)
+ {
+ ostringstream oss;
+ oss << e;
+ throw cli_exception (oss.str ());
}
- ostringstream ss;
+ init ();
+ }
+
+ /*
+
+ NOTE: This code hasn't been tested.
- if (!host.empty ())
+ void database::
+ parse ()
+ {
+ // Parse the server string and extract individual parts (protocol,
+ // host, instance, and port).
+ //
+ string port;
+
+ if (server_.compare (0, 4, "lpc:") == 0)
{
- ss << "//" << host_;
+ // lpc:<host>[\<instance>]
+ //
+ protocol_ = protocol_shm;
+ string::size_type p (server_.find (4, '\\'));
- if (port != 0)
- ss << ":" << port;
+ if (p == string::npos)
+ host_.assign (server_, 4, string::npos);
+ else
+ {
+ host_.assign (server_, 4, p - 4);
+ instance_.assign (server_, p + 1, string::npos);
+ }
}
+ else if (server_.compare (0, 3, "np:") == 0)
+ {
+ // np:<host>\pipe\[MSSQL$<instance>\]sql\query
+ //
+ protocol_ = protocol_pipe;
+
+ string::size_type p (server_.find (3, '\\'));
+
+ if (p != string::npos)
+ {
+ host_.assign (server_, 3, p - 3);
+
+ p = server_.find (p + 1, '$');
- if (!service_.empty ())
+ if (p != string::npos)
+ {
+ p++;
+ instance_.assign (server_, p, server_.find (p, '\\') - p);
+ }
+ }
+ }
+ else
{
- if (!host.empty ())
- ss << "/" << service_;
+ // <host>[\<instance>][,<port>]
+ // tcp:<host>[\<instance>][,<port>]
+ //
+ string::size_type p1 (0), p2;
+
+ if (server_.compare (0, 4, "tcp:") == 0)
+ {
+ protocol_ = protocol_tcp;
+ p1 = 4;
+ }
+
+ p2 = server_.find (p1, '\\');
+
+ if (p2 == string::npos)
+ {
+ p2 = server_.find (p1, ',');
+
+ if (p2 == string::npos)
+ host_.assign (server_, p1, string::npos);
+ else
+ {
+ host_.assign (server_, 4, p2 - p1);
+ port.assign (server_, p2 + 1, string::npos);
+ }
+ }
else
- ss << service_;
- }
+ {
+ host_.assign (server_, 4, p2 - p1);
- // @@ Quote FQ connect identifier.
- //
- db_ = ss.str ();
+ p1 = server_.find (p2 + 1, ',');
- if (factory_.get () == 0)
- factory_.reset (new connection_pool_factory ());
+ if (p1 == string::npos)
+ instance_.assign (server_, p2 + 1, string::npos);
+ else
+ {
+ instance_.assign (server_, p2 + 1, p1 - p2 - 1);
+ port.assign (server_, p1 + 1, string::npos);
+ }
+ }
+ }
- factory_->database (*this);
+ if (!port.empty ())
+ {
+ istringstream is (port);
+ is >> port;
+ protocol_ = protocol_tcp;
+ }
}
*/
- database::
- database (int& argc,
- char* argv[],
- bool erase,
- SQLHENV environment,
- auto_ptr<connection_factory> factory)
- : environment_ (environment),
- factory_ (factory)
+ void database::
+ init ()
{
- /*
+ SQLRETURN r;
+
if (environment_ == 0)
{
- sword s (OCIEnvNlsCreate (&environment_,
- OCI_THREADED,
- 0, 0, 0, 0, 0, 0,
- charset,
- ncharset));
+ r = SQLAllocHandle (SQL_HANDLE_ENV, SQL_NULL_HANDLE, &environment_);
- if (s == OCI_ERROR)
- translate_error (environment_);
+ if (!SQL_SUCCEEDED (r))
+ throw database_exception (
+ 0, "?????", "unable to allocate environment handle");
auto_environment_.reset (environment_);
- }
- using namespace details;
+ // Set ODBC version.
+ //
+ r = SQLSetEnvAttr (environment_,
+ SQL_ATTR_ODBC_VERSION,
+ (SQLPOINTER) SQL_OV_ODBC3,
+ 0);
- try
+ if (!SQL_SUCCEEDED (r))
+ translate_error (environment_, SQL_HANDLE_ENV);
+ }
+
+ // Build the connection string.
+ //
+ if (connect_string_.empty ())
{
- cli::argv_file_scanner scan (argc, argv, "--options-file", erase);
- options ops (scan, cli::unknown_mode::skip, cli::unknown_mode::skip);
+ // Find the driver.
+ //
+ if (driver_.empty ())
+ {
+ for (bool first (true);; )
+ {
+ char desc[256];
+ SQLSMALLINT desc_size, attr_size;
+
+ r = SQLDrivers (environment_,
+ first ? SQL_FETCH_FIRST : SQL_FETCH_NEXT,
+ (SQLCHAR*) desc,
+ sizeof (desc),
+ &desc_size,
+ 0,
+ 0,
+ &attr_size);
+
+ if (r == SQL_NO_DATA)
+ break;
+ else if (!SQL_SUCCEEDED (r))
+ translate_error (environment_, SQL_HANDLE_ENV);
+
+ // Native Client 9.0 (first version).
+ //
+ if (strcmp (desc, "SQL Native Client") == 0 ||
+ strncmp (desc, "SQL Server Native Client", 24) == 0)
+ {
+ // Compare driver strings lexicographically. Provided that
+ // Microsoft keeps its naming consistent, we will get the
+ // correct result. For example, "SQL Server Native Client
+ // 10.0" (SQL Server 2008) will be greater than "SQL Native
+ // Client" (SQL Server 2005). Similarly, "SQL Server Native
+ // Client 11.0" (SQL Server 2012) will be preferred over
+ // "SQL Server Native Client 10.0" (SQL Server 2008).
+ //
+ if (desc > driver_)
+ driver_ = desc;
+ }
- if (ops.user_specified ())
- user_ = ops.user ();
+ if (first)
+ first = false;
+ }
+ }
- if (ops.password_specified ())
- password_ = ops.password ();
+ connect_string_ += "DRIVER={";
+ connect_string_ += driver_;
+ connect_string_ += "};";
- if (ops.database_specified ())
+ // If necessary, assemble the server address string, depending
+ // on which protocol we are using.
+ if (server_.empty ())
{
- if (ops.host_specified () ||
- ops.port_specified () ||
- ops.service_specified ())
-
- throw cli_exception ("--host, --port, or --service options "
- "cannot be specified together with "
- "--database option");
- db_ = ops.database ();
- }
- else
- {
- bool host_present (false);
- ostringstream oss;
-
- if (ops.host_specified () && !ops.host ().empty ())
+ switch (protocol_)
{
- host_present = true;
+ case protocol_auto:
+ {
+ server_ = (host_.empty () ? "localhost" : host_.c_str ());
- host_ = ops.host ();
- oss << "//" << host_;
+ if (!instance_.empty ())
+ {
+ server_ += '\\';
+ server_ += instance_;
+ }
- if (ops.port_specified ())
+ break;
+ }
+ case protocol_tcp:
{
- port_ = ops.port ();
+ server_ = "tcp:";
+ server_ += (host_.empty () ? "localhost" : host_.c_str ());
+ // Port seems to take precedence over instance. For example,
+ // if you specify both, and the instance name is invalid, the
+ // Native Client driver still connects without any problems.
+ //
if (port_ != 0)
- oss << ":" << port_;
+ {
+ ostringstream os;
+ os << port_;
+ server_ += ',';
+ server_ += os.str ();
+ }
+ else if (!instance_.empty ())
+ {
+ server_ += '\\';
+ server_ += instance_;
+ }
+
+ break;
+ }
+ case protocol_lpc:
+ {
+ server_ = "lpc:";
+ server_ += (host_.empty () ? "localhost" : host_.c_str ());
+
+ if (!instance_.empty ())
+ {
+ server_ += '\\';
+ server_ += instance_;
+ }
+
+ break;
+ }
+ case protocol_np:
+ {
+ server_ = "np:\\\\";
+ server_ += (host_.empty () ? "." : host_.c_str ());
+ server_ += "\\pipe\\";
+
+ if (!instance_.empty ())
+ {
+ server_ += "MSSQL$";
+ server_ += instance_;
+ server_ += '\\';
+ }
+
+ server_ += "sql\\query";
+ break;
}
}
+ }
- if (ops.service_specified () && !ops.service ().empty ())
- {
- service_ = ops.service ();
+ // The Address attribute seems to be preferred over SERVER. However,
+ // SQL Server 2005 Native Client only seem to support Address since
+ // SP1. Since we don't know the exact driver version, for now always
+ // use SERVER with SQL Server 2005 driver.
+ //
+ connect_string_ += (driver_ == "SQL Native Client"
+ ? "SERVER={"
+ : "Address={");
+
+ connect_string_ += server_;
+ connect_string_ += "};";
- if (host_present)
- oss << "/" << service_;
- else
- oss << service_;
+ // Add login information.
+ //
+ if (user_.empty ())
+ // Windows authentication.
+ //
+ connect_string_ += "Trusted_Connection=yes;";
+ else
+ {
+ connect_string_ += "UID={";
+ connect_string_ += user_;
+ connect_string_ += "};";
+
+ if (!password_.empty ())
+ {
+ connect_string_ += "PWD={";
+ connect_string_ += password_;
+ connect_string_ += "};";
}
+ }
- db_ = oss.str ();
+ // Add database.
+ //
+ if (!db_.empty ())
+ {
+ connect_string_ += "Database={";
+ connect_string_ += db_;
+ connect_string_ += "};";
}
- // @@ Quote FQ connect identifier.
+ // Add any extra connection attributes.
//
+ if (!extra_connect_string_.empty ())
+ connect_string_ += extra_connect_string_;
}
- catch (const cli::exception& e)
- {
- ostringstream oss;
- oss << e;
- throw cli_exception (oss.str ());
- }
- */
if (factory_.get () == 0)
factory_.reset (new connection_pool_factory ());
diff --git a/odb/mssql/database.hxx b/odb/mssql/database.hxx
index d20d860..cabdbb4 100644
--- a/odb/mssql/database.hxx
+++ b/odb/mssql/database.hxx
@@ -32,35 +32,82 @@ namespace odb
{
class transaction_impl;
+ enum protocol
+ {
+ protocol_auto,
+ protocol_tcp, // TCP/IP.
+ protocol_lpc, // Shared memory (local procedure call).
+ protocol_np // Named pipes.
+ };
+
class LIBODB_MSSQL_EXPORT database: public odb::database
{
public:
- database (const std::string& connect_string,
+ typedef mssql::protocol protocol_type;
+
+ // Connect to the specified server using the latest available SQL
+ // Server Native Client ODBC driver by default. If user is empty,
+ // then use Windows authentication. If database is empty, then
+ // use the default database for this user.
+ //
+ database (const std::string& user,
+ const std::string& password,
+ const std::string& db,
+ const std::string& server,
+ const std::string& driver = "",
+ const std::string& extra_connect_string = "",
SQLHENV environment = 0,
std::auto_ptr<connection_factory> factory =
std::auto_ptr<connection_factory> (0));
- /*
+ // By default connect to the default instance on localhost using
+ // default protocol and the latest available SQL Server Native
+ // Client ODBC driver. If user is empty, then use Windows
+ // authentication. If database is empty, then use the default
+ // database for this user.
+ //
database (const std::string& user,
const std::string& password,
- const std::string& service,
+ const std::string& db,
+ protocol_type protocol = protocol_auto,
const std::string& host = "",
- unsigned int port = 0,
- ub2 charset = 0,
- ub2 ncharset = 0,
- OCIEnv* environment = 0,
+ const std::string& instance = "",
+ const std::string& driver = "",
+ const std::string& extra_connect_string = "",
+ SQLHENV environment = 0,
+ std::auto_ptr<connection_factory> factory =
+ std::auto_ptr<connection_factory> (0));
+
+ // Connect using TCP/IP to the specified host and port. If port is
+ // 0, use the default port (1433).
+ //
+ database (const std::string& user,
+ const std::string& password,
+ const std::string& db,
+ const std::string& host,
+ unsigned int port,
+ const std::string& driver = "",
+ const std::string& extra_connect_string = "",
+ SQLHENV environment = 0,
+ std::auto_ptr<connection_factory> factory =
+ std::auto_ptr<connection_factory> (0));
+
+ // Connect using a custom SQL Server Native Client ODBC driver
+ // conection string.
+ //
+ database (const std::string& connect_string,
+ SQLHENV environment = 0,
std::auto_ptr<connection_factory> factory =
std::auto_ptr<connection_factory> (0));
// Extract the database parameters from the command line. The
// following options are recognized:
//
- // --user
- // --password
- // --database
- // --service
- // --host
- // --port
+ // --user | -U
+ // --password | -P
+ // --database | -d
+ // --server | -S
+ // --driver
// --options-file
//
// For more information, see the output of the print_usage() function
@@ -68,11 +115,10 @@ namespace odb
// argv array and the argc count is updated accordingly. This
// constructor may throw the cli_exception exception.
//
- */
-
database (int& argc,
char* argv[],
bool erase = false,
+ const std::string& extra_connect_string = "",
SQLHENV environment = 0,
std::auto_ptr<connection_factory> =
std::auto_ptr<connection_factory> (0));
@@ -90,7 +136,6 @@ namespace odb
connection ();
public:
- /*
const std::string&
user () const
{
@@ -109,10 +154,10 @@ namespace odb
return db_;
}
- const std::string&
- service () const
+ protocol_type
+ protocol () const
{
- return service_;
+ return protocol_;
}
const std::string&
@@ -121,12 +166,35 @@ namespace odb
return host_;
}
+ const std::string&
+ instance () const
+ {
+ return instance_;
+ }
+
unsigned int
port () const
{
return port_;
}
- */
+
+ const std::string&
+ server () const
+ {
+ return server_;
+ }
+
+ const std::string&
+ driver () const
+ {
+ return driver_;
+ }
+
+ const std::string&
+ extra_connect_string () const
+ {
+ return extra_connect_string_;
+ }
const std::string&
connect_string () const
@@ -170,20 +238,20 @@ namespace odb
connection_ ();
private:
- /*
+ void
+ init ();
+
+ private:
std::string user_;
std::string password_;
-
std::string db_;
-
- std::string service_;
+ protocol_type protocol_;
std::string host_;
+ std::string instance_;
unsigned int port_;
-
- ub2 charset_;
- ub2 ncharset_;
- */
-
+ std::string server_;
+ std::string driver_;
+ std::string extra_connect_string_;
std::string connect_string_;
auto_handle<SQL_HANDLE_ENV> auto_environment_;
diff --git a/odb/mssql/details/options.cli b/odb/mssql/details/options.cli
index 693f644..2158bea 100644
--- a/odb/mssql/details/options.cli
+++ b/odb/mssql/details/options.cli
@@ -13,35 +13,42 @@ namespace odb
{
class options
{
- std::string --user | --username
+ std::string --user | -U
{
"<name>",
- "PostgreSQL database user."
+ "SQL Server database user. If not specified, then Windows
+ authentication is used."
};
- std::string --password
+ std::string --password | -P
{
"<str>",
- "PostgreSQL database password."
+ "SQL Server database password. Omit this option if the user
+ password is blank or Windows authentication is used."
};
- std::string --database | --dbname
+ std::string --database | -d
{
"<name>",
- "PostgreSQL database name."
+ "SQL Server database name. If not specified, then the default
+ database for this user is used."
};
- std::string --host
+ std::string --server | -S
{
- "<str>",
- "PostgreSQL database host name or address (localhost by default)."
+ "<addr>",
+ "SQL Server instance address in the
+ \c{[\i{protocol}\b{:}]\i{host}[\b{\\}\i{instance}][\b{,}\i{port}]}
+ format, where \ci{protocol} can be \cb{tcp} (TCP/IP),
+ \cb{lpc} (shared memory), or \cb{np} (named pipe). If not specifid,
+ then \cb{localhost} is used."
};
- std::string --port
+ std::string --driver
{
- "<str>",
- "PostgreSQL database port number or socket file name extension for
- Unix-domain connections."
+ "<name>",
+ "SQL Server Native Client ODBC driver name. If not specified, then
+ the latest available driver is used."
};
std::string --options-file