File indexing completed on 2025-02-23 05:15:18

0001 //
0002 // Copyright (C) 2004-2006 Maciej Sobczak, Stephen Hutton, David Courtney
0003 // Distributed under the Boost Software License, Version 1.0.
0004 // (See accompanying file LICENSE_1_0.txt or copy at
0005 // http://www.boost.org/LICENSE_1_0.txt)
0006 //
0007 
0008 #define SOCI_ODBC_SOURCE
0009 #include "soci/soci-platform.h"
0010 #include "soci/odbc/soci-odbc.h"
0011 #include "soci/session.h"
0012 
0013 #include "soci-autostatement.h"
0014 
0015 #include <cstdio>
0016 
0017 using namespace soci;
0018 using namespace soci::details;
0019 
0020 char const * soci::odbc_option_driver_complete = "odbc.driver_complete";
0021 
0022 odbc_session_backend::odbc_session_backend(
0023     connection_parameters const & parameters)
0024     : henv_(0), hdbc_(0), product_(prod_uninitialized)
0025 {
0026     SQLRETURN rc;
0027 
0028     // Allocate environment handle
0029     rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv_);
0030     if (is_odbc_error(rc))
0031     {
0032         throw soci_error("Unable to get environment handle");
0033     }
0034 
0035     // Set the ODBC version environment attribute
0036     rc = SQLSetEnvAttr(henv_, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
0037     if (is_odbc_error(rc))
0038     {
0039         throw odbc_soci_error(SQL_HANDLE_ENV, henv_, "setting ODBC version 3");
0040     }
0041 
0042     // Allocate connection handle
0043     rc = SQLAllocHandle(SQL_HANDLE_DBC, henv_, &hdbc_);
0044     if (is_odbc_error(rc))
0045     {
0046         throw odbc_soci_error(SQL_HANDLE_DBC, hdbc_,
0047                               "allocating connection handle");
0048     }
0049 
0050     SQLCHAR outConnString[1024];
0051     SQLSMALLINT strLength = 0;
0052 
0053     // Prompt the user for any missing information (typically UID/PWD) in the
0054     // connection string by default but allow overriding this using "prompt"
0055     // option and also suppress prompts when reconnecting, see the comment in
0056     // soci::session::reconnect().
0057     SQLHWND hwnd_for_prompt = NULL;
0058     unsigned completion = SQL_DRIVER_COMPLETE;
0059 
0060     if (parameters.is_option_on(option_reconnect))
0061     {
0062       completion = SQL_DRIVER_NOPROMPT;
0063     }
0064     else
0065     {
0066       std::string completionString;
0067       if (parameters.get_option(odbc_option_driver_complete, completionString))
0068       {
0069         // The value of the option is supposed to be just the integer value of
0070         // one of SQL_DRIVER_XXX constants but don't check for the exact value in
0071         // case more of them are added in the future, the ODBC driver will return
0072         // an error if we pass it an invalid value anyhow.
0073         if (std::sscanf(completionString.c_str(), "%u", &completion) != 1)
0074         {
0075           throw soci_error("Invalid non-numeric driver completion option value \"" +
0076                             completionString + "\".");
0077         }
0078       }
0079     }
0080 
0081 #ifdef _WIN32
0082     if (completion != SQL_DRIVER_NOPROMPT)
0083       hwnd_for_prompt = ::GetDesktopWindow();
0084 #endif // _WIN32
0085 
0086     std::string const & connectString = parameters.get_connect_string();
0087 
0088     // This "infinite" loop can be executed at most twice.
0089     std::string errContext;
0090     for (;;)
0091     {
0092         rc = SQLDriverConnect(hdbc_, hwnd_for_prompt,
0093                               sqlchar_cast(connectString),
0094                               (SQLSMALLINT)connectString.size(),
0095                               outConnString, 1024, &strLength,
0096                               static_cast<SQLUSMALLINT>(completion));
0097 
0098         // Don't use is_odbc_error() here as it doesn't consider SQL_NO_DATA to be
0099         // an error -- but it is one here, as it's returned if a message box shown
0100         // by SQLDriverConnect() was cancelled and this means we failed to connect.
0101         switch (rc)
0102         {
0103           case SQL_SUCCESS:
0104           case SQL_SUCCESS_WITH_INFO:
0105             break;
0106 
0107           case SQL_NO_DATA:
0108             throw soci_error("Connecting to the database cancelled by user.");
0109 
0110           default:
0111             odbc_soci_error err(SQL_HANDLE_DBC, hdbc_, "connecting to database");
0112 
0113             // If connection pooling had been enabled by the application, we
0114             // would get HY110 ODBC error for any connection attempt not using
0115             // SQL_DRIVER_NOPROMPT, so it's worth retrying with it in this
0116             // case: in the worst case, we'll hit 28000 ODBC error (login
0117             // failed), which we'll report together with the context helping to
0118             // understand where it came from.
0119             if (memcmp(err.odbc_error_code(), "HY110", 6) == 0 &&
0120                     completion != SQL_DRIVER_NOPROMPT)
0121             {
0122                 errContext = "while retrying to connect without prompting, as "
0123                              "prompting the user is not supported when using "
0124                              "pooled connections";
0125                 completion = SQL_DRIVER_NOPROMPT;
0126                 continue;
0127             }
0128 
0129             if (!errContext.empty())
0130                 err.add_context(errContext);
0131 
0132             throw err;
0133         }
0134 
0135         // This loop only runs once unless we retry in case of HY110 above.
0136         break;
0137     }
0138 
0139     connection_string_.assign((const char*)outConnString, strLength);
0140 
0141     reset_transaction();
0142 
0143     configure_connection();
0144 }
0145 
0146 void odbc_session_backend::configure_connection()
0147 {
0148     if ( get_database_product() == prod_postgresql )
0149     {
0150         // Increase the number of digits used for floating point values to
0151         // ensure that the conversions to/from text round trip correctly, which
0152         // is not the case with the default value of 0. Use the maximal
0153         // supported value, which was 2 until 9.x and is 3 since it.
0154 
0155         char product_ver[1024];
0156         SQLSMALLINT len = sizeof(product_ver);
0157         SQLRETURN rc = SQLGetInfo(hdbc_, SQL_DBMS_VER, product_ver, len, &len);
0158         if (is_odbc_error(rc))
0159         {
0160             throw odbc_soci_error(SQL_HANDLE_DBC, henv_,
0161                                   "getting PostgreSQL ODBC driver version");
0162         }
0163 
0164         // The returned string is of the form "##.##.#### ...", but we don't
0165         // need to parse it fully, we just need the major version which,
0166         // conveniently, comes first.
0167         unsigned major_ver = 0;
0168         if (std::sscanf(product_ver, "%u", &major_ver) != 1)
0169         {
0170             throw soci_error("DBMS version \"" + std::string(product_ver) +
0171                              "\" in unrecognizable format.");
0172         }
0173 
0174         details::auto_statement<odbc_statement_backend> st(*this);
0175 
0176         std::string const q(major_ver >= 9 ? "SET extra_float_digits = 3"
0177                                            : "SET extra_float_digits = 2");
0178         rc = SQLExecDirect(st.hstmt_, sqlchar_cast(q), static_cast<SQLINTEGER>(q.size()));
0179 
0180         if (is_odbc_error(rc))
0181         {
0182             throw odbc_soci_error(SQL_HANDLE_DBC, henv_,
0183                                   "setting extra_float_digits for PostgreSQL");
0184         }
0185 
0186         // This is extracted from pgapifunc.h header from psqlODBC driver.
0187         enum
0188         {
0189             SQL_ATTR_PGOPT_UNKNOWNSASLONGVARCHAR = 65544
0190         };
0191 
0192         // Also configure the driver to handle unknown types, such as "xml",
0193         // that we use for x_xmltype, as long varchar instead of limiting them
0194         // to 256 characters (by default).
0195         rc = SQLSetConnectAttr(hdbc_, SQL_ATTR_PGOPT_UNKNOWNSASLONGVARCHAR, (SQLPOINTER)1, 0);
0196 
0197         // Ignore the error from this one, failure to set it is not fatal and
0198         // the attribute is only supported in very recent version of the driver
0199         // (>= 9.6.300). Using "UnknownsAsLongVarchar=1" in odbc.ini (or
0200         // setting the corresponding option in the driver dialog box) should
0201         // work with all versions however.
0202     }
0203 }
0204 
0205 odbc_session_backend::~odbc_session_backend()
0206 {
0207     clean_up();
0208 }
0209 
0210 bool odbc_session_backend::is_connected()
0211 {
0212     details::auto_statement<odbc_statement_backend> st(*this);
0213 
0214     // The name of the table we check for is irrelevant, as long as we have a
0215     // working connection, it should still find (or, hopefully, not) something.
0216     return !is_odbc_error(SQLTables(st.hstmt_,
0217                                     NULL, SQL_NTS,
0218                                     NULL, SQL_NTS,
0219                                     sqlchar_cast("bloordyblop"), SQL_NTS,
0220                                     NULL, SQL_NTS));
0221 }
0222 
0223 void odbc_session_backend::begin()
0224 {
0225     SQLRETURN rc = SQLSetConnectAttr( hdbc_, SQL_ATTR_AUTOCOMMIT,
0226                     (SQLPOINTER)SQL_AUTOCOMMIT_OFF, 0 );
0227     if (is_odbc_error(rc))
0228     {
0229         throw odbc_soci_error(SQL_HANDLE_DBC, hdbc_, "beginning transaction");
0230     }
0231 }
0232 
0233 void odbc_session_backend::commit()
0234 {
0235     SQLRETURN rc = SQLEndTran(SQL_HANDLE_DBC, hdbc_, SQL_COMMIT);
0236     if (is_odbc_error(rc))
0237     {
0238         throw odbc_soci_error(SQL_HANDLE_DBC, hdbc_, "committing transaction");
0239     }
0240     reset_transaction();
0241 }
0242 
0243 void odbc_session_backend::rollback()
0244 {
0245     SQLRETURN rc = SQLEndTran(SQL_HANDLE_DBC, hdbc_, SQL_ROLLBACK);
0246     if (is_odbc_error(rc))
0247     {
0248         throw odbc_soci_error(SQL_HANDLE_DBC, hdbc_, "rolling back transaction");
0249     }
0250     reset_transaction();
0251 }
0252 
0253 bool odbc_session_backend::get_next_sequence_value(
0254     session & s, std::string const & sequence, long long & value)
0255 {
0256     std::string query;
0257 
0258     switch ( get_database_product() )
0259     {
0260         case prod_db2:
0261             query = "select next value for " + sequence + " from SYSIBM.SYSDUMMY1";
0262             break;
0263 
0264         case prod_firebird:
0265             query = "select next value for " + sequence + " from rdb$database";
0266             break;
0267 
0268         case prod_oracle:
0269             query = "select " + sequence + ".nextval from dual";
0270             break;
0271 
0272         case prod_postgresql:
0273             query = "select nextval('" + sequence + "')";
0274             break;
0275 
0276         case prod_mssql:
0277         case prod_mysql:
0278         case prod_sqlite:
0279             // These RDBMS implement get_last_insert_id() instead.
0280             return false;
0281 
0282         case prod_unknown:
0283             // For this one we can't do anything at all.
0284             return false;
0285 
0286         case prod_uninitialized:
0287             // This is not supposed to happen at all but still cover this case
0288             // here to avoid gcc warnings about unhandled enum values in a
0289             // switch.
0290             return false;
0291     }
0292 
0293     s << query, into(value);
0294 
0295     return true;
0296 }
0297 
0298 bool odbc_session_backend::get_last_insert_id(
0299     session & s, std::string const & table, long long & value)
0300 {
0301     std::string query;
0302 
0303     switch ( get_database_product() )
0304     {
0305         case prod_db2:
0306             query = "SELECT IDENTITY_VAL_LOCAL() AS LASTID FROM SYSIBM.SYSDUMMY1";
0307             break;
0308 
0309         case prod_mssql:
0310             {
0311                 // We can't use `ident_current()` because it doesn't
0312                 // distinguish between the empty table and a table with one
0313                 // row inserted and returns `seed_value` in both cases, while
0314                 // we need previous to the initial value in the former
0315                 // (i.e. `seed_value` - `increment_value`).
0316                 long long last, seed, inc;
0317                 indicator ind;
0318 
0319                 s << "select last_value, seed_value, increment_value "
0320                      "from sys.identity_columns where "
0321                      "object_id = object_id('" << table << "')"
0322                      , into(last, ind), into(seed), into(inc);
0323 
0324                 if (ind == i_null)
0325                 {
0326                     value = seed - inc;
0327                 }
0328                 else
0329                 {
0330                     value = last;
0331                 }
0332             }
0333             return true;
0334 
0335         case prod_mysql:
0336             query = "select last_insert_id()";
0337             break;
0338 
0339         case prod_sqlite:
0340             query = "select last_insert_rowid()";
0341             break;
0342 
0343         case prod_firebird:
0344         case prod_oracle:
0345         case prod_postgresql:
0346             // For these RDBMS get_next_sequence_value() should have been used.
0347             return false;
0348 
0349 
0350         case prod_unknown:
0351             // For this one we can't do anything at all.
0352             return false;
0353 
0354         case prod_uninitialized:
0355             // As above, this is not supposed to happen but put it here to
0356             // mollify gcc.
0357             return false;
0358     }
0359 
0360     s << query, into(value);
0361 
0362     return true;
0363 }
0364 
0365 std::string odbc_session_backend::get_dummy_from_table() const
0366 {
0367     std::string table;
0368 
0369     switch ( get_database_product() )
0370     {
0371         case prod_db2:
0372             table = "SYSIBM.SYSDUMMY1";
0373             break;
0374 
0375         case prod_firebird:
0376             table = "rdb$database";
0377             break;
0378 
0379         case prod_oracle:
0380             table = "dual";
0381             break;
0382 
0383         case prod_mssql:
0384         case prod_mysql:
0385         case prod_sqlite:
0386         case prod_postgresql:
0387             // No special dummy table needed.
0388             break;
0389 
0390             // These cases are here just to make the switch exhaustive, we
0391             // can't really do anything about them anyhow.
0392         case prod_unknown:
0393         case prod_uninitialized:
0394             break;
0395     }
0396 
0397     return table;
0398 }
0399 
0400 void odbc_session_backend::reset_transaction()
0401 {
0402     SQLRETURN rc = SQLSetConnectAttr( hdbc_, SQL_ATTR_AUTOCOMMIT,
0403                     (SQLPOINTER)SQL_AUTOCOMMIT_ON, 0 );
0404     if (is_odbc_error(rc))
0405     {
0406         throw odbc_soci_error(SQL_HANDLE_DBC, hdbc_, "enabling auto commit");
0407     }
0408 }
0409 
0410 
0411 void odbc_session_backend::clean_up()
0412 {
0413     SQLRETURN rc = SQLDisconnect(hdbc_);
0414     if (is_odbc_error(rc))
0415     {
0416         throw odbc_soci_error(SQL_HANDLE_DBC, hdbc_, "disconnecting");
0417     }
0418 
0419     rc = SQLFreeHandle(SQL_HANDLE_DBC, hdbc_);
0420     if (is_odbc_error(rc))
0421     {
0422         throw odbc_soci_error(SQL_HANDLE_DBC, hdbc_, "freeing connection");
0423     }
0424 
0425     rc = SQLFreeHandle(SQL_HANDLE_ENV, henv_);
0426     if (is_odbc_error(rc))
0427     {
0428         throw odbc_soci_error(SQL_HANDLE_ENV, henv_, "freeing environment");
0429     }
0430 }
0431 
0432 odbc_statement_backend * odbc_session_backend::make_statement_backend()
0433 {
0434     return new odbc_statement_backend(*this);
0435 }
0436 
0437 odbc_rowid_backend * odbc_session_backend::make_rowid_backend()
0438 {
0439     return new odbc_rowid_backend(*this);
0440 }
0441 
0442 odbc_blob_backend * odbc_session_backend::make_blob_backend()
0443 {
0444     return new odbc_blob_backend(*this);
0445 }
0446 
0447 odbc_session_backend::database_product
0448 odbc_session_backend::get_database_product() const
0449 {
0450     // Cache the product type, it's not going to change during our life time.
0451     if (product_ != prod_uninitialized)
0452         return product_;
0453 
0454     char product_name[1024];
0455     SQLSMALLINT len = sizeof(product_name);
0456     SQLRETURN rc = SQLGetInfo(hdbc_, SQL_DBMS_NAME, product_name, len, &len);
0457     if (is_odbc_error(rc))
0458     {
0459         throw odbc_soci_error(SQL_HANDLE_DBC, henv_,
0460                               "getting ODBC driver name");
0461     }
0462 
0463     if (strcmp(product_name, "Firebird") == 0)
0464         product_ = prod_firebird;
0465     else if (strcmp(product_name, "Microsoft SQL Server") == 0)
0466         product_ = prod_mssql;
0467     else if (strcmp(product_name, "MySQL") == 0)
0468         product_ = prod_mysql;
0469     else if (strcmp(product_name, "Oracle") == 0)
0470         product_ = prod_oracle;
0471     else if (strcmp(product_name, "PostgreSQL") == 0)
0472         product_ = prod_postgresql;
0473     else if (strcmp(product_name, "SQLite") == 0)
0474         product_ = prod_sqlite;
0475     else if (strstr(product_name, "DB2") == product_name) // "DB2/LINUXX8664"
0476         product_ = prod_db2;
0477     else
0478         product_ = prod_unknown;
0479 
0480     return product_;
0481 }