Commit 8c6b4797 authored by Maciej Sobczak's avatar Maciej Sobczak

Added NLS support for connection parameters in Oracle.

parent 912649c2
......@@ -61,7 +61,8 @@ The set of parameters used in the connection string for Oracle is:
* `service`
* `user`
* `password`
* `mode` (optional)
* `mode` (optional; valid values are `sysdba`, `sysoper` and `default`)
* `charset` and `ncharset` (optional; valid values are `utf8`, `utf16`, `we8mswin1252` and `win1252`)
Once you have created a `session` object as shown above, you can use it to access the database, for example:
......
......@@ -247,7 +247,9 @@ struct oracle_session_backend : details::session_backend
std::string const & userName,
std::string const & password,
int mode,
bool decimals_as_strings = false);
bool decimals_as_strings = false,
int charset = 0,
int ncharset = 0);
~oracle_session_backend();
......
......@@ -69,17 +69,56 @@ std::string::const_iterator get_key_value(std::string::const_iterator & i,
return i;
}
// decode charset and ncharset names
int charset_code(const std::string & name)
{
// Note: unofficial reference for charset ids is:
// http://www.mydul.net/charsets.html
int code;
if (name == "utf8")
{
code = 871;
}
else if (name == "utf16")
{
code = OCI_UTF16ID;
}
else if (name == "we8mswin1252" || name == "win1252")
{
code = 178;
}
else
{
// allow explicit number value
std::istringstream ss(name);
ss >> code;
if (!ss)
{
throw soci_error("Invalid character set name.");
}
}
return code;
}
// retrieves service name, user name and password from the
// uniform connect string
void chop_connect_string(std::string const & connectString,
std::string & serviceName, std::string & userName,
std::string & password, int & mode, bool & decimals_as_strings)
std::string & password, int & mode, bool & decimals_as_strings,
int & charset, int & ncharset)
{
serviceName.clear();
userName.clear();
password.clear();
mode = OCI_DEFAULT;
decimals_as_strings = false;
charset = 0;
ncharset = 0;
std::string key, value;
std::string::const_iterator i = connectString.begin();
......@@ -121,6 +160,14 @@ void chop_connect_string(std::string const & connectString,
{
decimals_as_strings = value == "1" || value == "Y" || value == "y";
}
else if (key == "charset")
{
charset = charset_code(value);
}
else if (key == "ncharset")
{
ncharset = charset_code(value);
}
}
}
......@@ -131,12 +178,14 @@ oracle_session_backend * oracle_backend_factory::make_session(
std::string serviceName, userName, password;
int mode;
bool decimals_as_strings;
int charset;
int ncharset;
chop_connect_string(parameters.get_connect_string(), serviceName, userName, password,
mode, decimals_as_strings);
mode, decimals_as_strings, charset, ncharset);
return new oracle_session_backend(serviceName, userName, password,
mode, decimals_as_strings);
mode, decimals_as_strings, charset, ncharset);
}
oracle_backend_factory const soci::oracle;
......
......@@ -24,15 +24,28 @@ using namespace soci::details::oracle;
oracle_session_backend::oracle_session_backend(std::string const & serviceName,
std::string const & userName, std::string const & password, int mode,
bool decimals_as_strings)
bool decimals_as_strings, int charset, int ncharset)
: envhp_(NULL), srvhp_(NULL), errhp_(NULL), svchp_(NULL), usrhp_(NULL)
, decimals_as_strings_(decimals_as_strings)
{
// assume service/user/password are utf8-compatible already
const int defaultSourceCharSetId = 871;
// arbitrary length for charset conversion buffer
const size_t nlsBufLen = 100;
char nlsService[nlsBufLen];
size_t nlsServiceLen;
char nlsUserName[nlsBufLen];
size_t nlsUserNameLen;
char nlsPassword[nlsBufLen];
size_t nlsPasswordLen;
sword res;
// create the environment
res = OCIEnvCreate(&envhp_, OCI_THREADED | OCI_ENV_NO_MUTEX,
0, 0, 0, 0, 0, 0);
res = OCIEnvNlsCreate(&envhp_, OCI_THREADED | OCI_ENV_NO_MUTEX,
0, 0, 0, 0, 0, 0, charset, ncharset);
if (res != OCI_SUCCESS)
{
throw soci_error("Cannot create environment");
......@@ -56,11 +69,84 @@ oracle_session_backend::oracle_session_backend(std::string const & serviceName,
throw soci_error("Cannot create error handle");
}
if (charset != 0)
{
// convert service/user/password to the expected charset
res = OCINlsCharSetConvert(envhp_, errhp_,
charset, nlsService, nlsBufLen,
defaultSourceCharSetId, serviceName.c_str(), serviceName.size(), &nlsServiceLen);
if (res != OCI_SUCCESS)
{
std::string msg;
int errNum;
get_error_details(res, errhp_, msg, errNum);
clean_up();
throw oracle_soci_error(msg, errNum);
}
res = OCINlsCharSetConvert(envhp_, errhp_,
charset, nlsUserName, nlsBufLen,
defaultSourceCharSetId, userName.c_str(), userName.size(), &nlsUserNameLen);
if (res != OCI_SUCCESS)
{
std::string msg;
int errNum;
get_error_details(res, errhp_, msg, errNum);
clean_up();
throw oracle_soci_error(msg, errNum);
}
res = OCINlsCharSetConvert(envhp_, errhp_,
charset, nlsPassword, nlsBufLen,
defaultSourceCharSetId, password.c_str(), password.size(), &nlsPasswordLen);
if (res != OCI_SUCCESS)
{
std::string msg;
int errNum;
get_error_details(res, errhp_, msg, errNum);
clean_up();
throw oracle_soci_error(msg, errNum);
}
}
else
{
// do not perform any charset conversions
nlsServiceLen = serviceName.size();
if (nlsServiceLen < nlsBufLen)
{
std::strcpy(nlsService, serviceName.c_str());
}
else
{
throw soci_error("Service name is too long.");
}
nlsUserNameLen = userName.size();
if (nlsUserNameLen < nlsBufLen)
{
std::strcpy(nlsUserName, userName.c_str());
}
else
{
throw soci_error("User name is too long.");
}
nlsPasswordLen = password.size();
if (nlsPasswordLen < nlsBufLen)
{
std::strcpy(nlsPassword, password.c_str());
}
else
{
throw soci_error("Password is too long.");
}
}
// create the server context
sb4 serviceNameLen = static_cast<sb4>(serviceName.size());
res = OCIServerAttach(srvhp_, errhp_,
reinterpret_cast<text*>(const_cast<char*>(serviceName.c_str())),
serviceNameLen, OCI_DEFAULT);
reinterpret_cast<text*>(nlsService), nlsServiceLen, OCI_DEFAULT);
if (res != OCI_SUCCESS)
{
std::string msg;
......@@ -101,10 +187,9 @@ oracle_session_backend::oracle_session_backend(std::string const & serviceName,
}
// set username attribute in the user session handle
sb4 userNameLen = static_cast<sb4>(userName.size());
res = OCIAttrSet(usrhp_, OCI_HTYPE_SESSION,
reinterpret_cast<dvoid*>(const_cast<char*>(userName.c_str())),
userNameLen, OCI_ATTR_USERNAME, errhp_);
reinterpret_cast<dvoid*>(nlsUserName),
nlsUserNameLen, OCI_ATTR_USERNAME, errhp_);
if (res != OCI_SUCCESS)
{
clean_up();
......@@ -112,10 +197,9 @@ oracle_session_backend::oracle_session_backend(std::string const & serviceName,
}
// set password attribute
sb4 passwordLen = static_cast<sb4>(password.size());
res = OCIAttrSet(usrhp_, OCI_HTYPE_SESSION,
reinterpret_cast<dvoid*>(const_cast<char*>(password.c_str())),
passwordLen, OCI_ATTR_PASSWORD, errhp_);
reinterpret_cast<dvoid*>(nlsPassword),
nlsPasswordLen, OCI_ATTR_PASSWORD, errhp_);
if (res != OCI_SUCCESS)
{
clean_up();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment