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

0001 //
0002 // Copyright (C) 2004-2007 Maciej Sobczak, Stephen Hutton
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_ORACLE_SOURCE
0009 
0010 #include "soci/oracle/soci-oracle.h"
0011 #include "error.h"
0012 #include "soci/soci-backend.h"
0013 #include <cctype>
0014 #include <cstdio>
0015 #include <cstring>
0016 #include <ctime>
0017 #include <limits>
0018 #include <sstream>
0019 
0020 #ifdef _MSC_VER
0021 #pragma warning(disable:4355)
0022 #endif
0023 
0024 using namespace soci;
0025 using namespace soci::details;
0026 using namespace soci::details::oracle;
0027 
0028 oracle_statement_backend::oracle_statement_backend(oracle_session_backend &session)
0029     : session_(session), stmtp_(NULL), boundByName_(false), boundByPos_(false),
0030       noData_(false)
0031 {
0032 }
0033 
0034 void oracle_statement_backend::alloc()
0035 {
0036     sword res = OCIHandleAlloc(session_.envhp_,
0037         reinterpret_cast<dvoid**>(&stmtp_),
0038         OCI_HTYPE_STMT, 0, 0);
0039     if (res != OCI_SUCCESS)
0040     {
0041         throw soci_error("Cannot allocate statement handle");
0042     }
0043 }
0044 
0045 void oracle_statement_backend::clean_up()
0046 {
0047     // deallocate statement handle
0048     if (stmtp_ != NULL)
0049     {
0050         OCIHandleFree(stmtp_, OCI_HTYPE_STMT);
0051         stmtp_ = NULL;
0052     }
0053 
0054     boundByName_ = false;
0055     boundByPos_ = false;
0056 }
0057 
0058 void oracle_statement_backend::prepare(std::string const &query,
0059     statement_type /* eType */)
0060 {
0061     sb4 stmtLen = static_cast<sb4>(query.size());
0062     sword res = OCIStmtPrepare(stmtp_,
0063         session_.errhp_,
0064         reinterpret_cast<text*>(const_cast<char*>(query.c_str())),
0065         stmtLen, OCI_V7_SYNTAX, OCI_DEFAULT);
0066     if (res != OCI_SUCCESS)
0067     {
0068         throw_oracle_soci_error(res, session_.errhp_);
0069     }
0070 }
0071 
0072 statement_backend::exec_fetch_result oracle_statement_backend::execute(int number)
0073 {
0074     sword res = OCIStmtExecute(session_.svchp_, stmtp_, session_.errhp_,
0075         static_cast<ub4>(number), 0, 0, 0, OCI_DEFAULT);
0076 
0077     if (res == OCI_SUCCESS || res == OCI_SUCCESS_WITH_INFO)
0078     {
0079         noData_ = false;
0080         return ef_success;
0081     }
0082     else if (res == OCI_NO_DATA)
0083     {
0084         noData_ = true;
0085         return ef_no_data;
0086     }
0087     else
0088     {
0089         throw_oracle_soci_error(res, session_.errhp_);
0090         return ef_no_data; // unreachable dummy return to please the compiler
0091     }
0092 }
0093 
0094 statement_backend::exec_fetch_result oracle_statement_backend::fetch(int number)
0095 {
0096     if (noData_)
0097     {
0098         return ef_no_data;
0099     }
0100 
0101     sword res = OCIStmtFetch(stmtp_, session_.errhp_,
0102         static_cast<ub4>(number), OCI_FETCH_NEXT, OCI_DEFAULT);
0103 
0104     if (res == OCI_SUCCESS || res == OCI_SUCCESS_WITH_INFO)
0105     {
0106         return ef_success;
0107     }
0108     else if (res == OCI_NO_DATA)
0109     {
0110         noData_ = true;
0111         return ef_no_data;
0112     }
0113     else
0114     {
0115         throw_oracle_soci_error(res, session_.errhp_);
0116         return ef_no_data; // unreachable dummy return to please the compiler
0117     }
0118 }
0119 
0120 long long oracle_statement_backend::get_affected_rows()
0121 {
0122     ub4 row_count;
0123     sword res = OCIAttrGet(static_cast<dvoid*>(stmtp_),
0124         OCI_HTYPE_STMT, &row_count,
0125         0, OCI_ATTR_ROW_COUNT, session_.errhp_);
0126 
0127     if (res != OCI_SUCCESS)
0128     {
0129         throw_oracle_soci_error(res, session_.errhp_);
0130     }
0131 
0132     return row_count;
0133 }
0134 
0135 int oracle_statement_backend::get_number_of_rows()
0136 {
0137     int rows;
0138     sword res = OCIAttrGet(static_cast<dvoid*>(stmtp_),
0139         OCI_HTYPE_STMT, static_cast<dvoid*>(&rows),
0140         0, OCI_ATTR_ROWS_FETCHED, session_.errhp_);
0141 
0142     if (res != OCI_SUCCESS)
0143     {
0144         throw_oracle_soci_error(res, session_.errhp_);
0145     }
0146 
0147     return rows;
0148 }
0149 
0150 std::string oracle_statement_backend::get_parameter_name(int /* index */) const
0151 {
0152     // TODO: How to get the parameter names from the query we prepared?
0153     return std::string();
0154 }
0155 
0156 std::string oracle_statement_backend::rewrite_for_procedure_call(
0157     std::string const &query)
0158 {
0159     std::string newQuery("begin ");
0160     newQuery += query;
0161     newQuery += "; end;";
0162     return newQuery;
0163 }
0164 
0165 int oracle_statement_backend::prepare_for_describe()
0166 {
0167     sword res = OCIStmtExecute(session_.svchp_, stmtp_, session_.errhp_,
0168         1, 0, 0, 0, OCI_DESCRIBE_ONLY);
0169     if (res != OCI_SUCCESS)
0170     {
0171         throw_oracle_soci_error(res, session_.errhp_);
0172     }
0173 
0174     int cols;
0175     res = OCIAttrGet(static_cast<dvoid*>(stmtp_),
0176         static_cast<ub4>(OCI_HTYPE_STMT), static_cast<dvoid*>(&cols),
0177         0, static_cast<ub4>(OCI_ATTR_PARAM_COUNT), session_.errhp_);
0178 
0179     if (res != OCI_SUCCESS)
0180     {
0181         throw_oracle_soci_error(res, session_.errhp_);
0182     }
0183 
0184     return cols;
0185 }
0186 
0187 void oracle_statement_backend::describe_column(int colNum, data_type &type,
0188     std::string &columnName)
0189 {
0190     ub2 dbtype;
0191     text* dbname;
0192     ub4 nameLength;
0193 
0194     ub2 dbsize;
0195     sb2 dbprec;
0196     ub1 dbscale; //sb2 in some versions of Oracle?
0197 
0198     // Get the column handle
0199     OCIParam* colhd;
0200     sword res = OCIParamGet(reinterpret_cast<dvoid*>(stmtp_),
0201         static_cast<ub4>(OCI_HTYPE_STMT),
0202         reinterpret_cast<OCIError*>(session_.errhp_),
0203         reinterpret_cast<dvoid**>(&colhd),
0204         static_cast<ub4>(colNum));
0205     if (res != OCI_SUCCESS)
0206     {
0207         throw_oracle_soci_error(res, session_.errhp_);
0208     }
0209 
0210     // Get the column name
0211     res = OCIAttrGet(reinterpret_cast<dvoid*>(colhd),
0212         static_cast<ub4>(OCI_DTYPE_PARAM),
0213         reinterpret_cast<dvoid**>(&dbname),
0214         reinterpret_cast<ub4*>(&nameLength),
0215         static_cast<ub4>(OCI_ATTR_NAME),
0216         reinterpret_cast<OCIError*>(session_.errhp_));
0217     if (res != OCI_SUCCESS)
0218     {
0219         throw_oracle_soci_error(res, session_.errhp_);
0220     }
0221 
0222     // Get the column type
0223     res = OCIAttrGet(reinterpret_cast<dvoid*>(colhd),
0224         static_cast<ub4>(OCI_DTYPE_PARAM),
0225         reinterpret_cast<dvoid*>(&dbtype),
0226         0,
0227         static_cast<ub4>(OCI_ATTR_DATA_TYPE),
0228         reinterpret_cast<OCIError*>(session_.errhp_));
0229     if (res != OCI_SUCCESS)
0230     {
0231         throw_oracle_soci_error(res, session_.errhp_);
0232     }
0233 
0234     // get the data size
0235     res = OCIAttrGet(reinterpret_cast<dvoid*>(colhd),
0236         static_cast<ub4>(OCI_DTYPE_PARAM),
0237         reinterpret_cast<dvoid*>(&dbsize),
0238         0,
0239         static_cast<ub4>(OCI_ATTR_DATA_SIZE),
0240         reinterpret_cast<OCIError*>(session_.errhp_));
0241     if (res != OCI_SUCCESS)
0242     {
0243         throw_oracle_soci_error(res, session_.errhp_);
0244     }
0245 
0246     // get the precision
0247     res = OCIAttrGet(reinterpret_cast<dvoid*>(colhd),
0248         static_cast<ub4>(OCI_DTYPE_PARAM),
0249         reinterpret_cast<dvoid*>(&dbprec),
0250         0,
0251         static_cast<ub4>(OCI_ATTR_PRECISION),
0252         reinterpret_cast<OCIError*>(session_.errhp_));
0253     if (res != OCI_SUCCESS)
0254     {
0255         throw_oracle_soci_error(res, session_.errhp_);
0256     }
0257 
0258     // get the scale if necessary, i.e. if not using just NUMBER, for which
0259     // both precision and scale are 0 anyhow
0260     if (dbprec)
0261     {
0262         res = OCIAttrGet(reinterpret_cast<dvoid*>(colhd),
0263             static_cast<ub4>(OCI_DTYPE_PARAM),
0264             reinterpret_cast<dvoid*>(&dbscale),
0265             0,
0266             static_cast<ub4>(OCI_ATTR_SCALE),
0267             reinterpret_cast<OCIError*>(session_.errhp_));
0268         if (res != OCI_SUCCESS)
0269         {
0270             throw_oracle_soci_error(res, session_.errhp_);
0271         }
0272     }
0273     else // precision is 0, meaning that this is the default number type
0274     {
0275         // don't bother retrieving the scale using OCI, we know its default
0276         // value, as NUMBER is the same as NUMBER(38,10), and we also don't
0277         // really care about it, all that matters is for it to be > 0 so that
0278         // the right type is selected below
0279         dbscale = 10;
0280     }
0281 
0282     columnName.assign(dbname, dbname + nameLength);
0283 
0284     switch (dbtype)
0285     {
0286     case SQLT_CHR:
0287     case SQLT_AFC:
0288         type = dt_string;
0289         break;
0290     case SQLT_NUM:
0291         if (dbscale > 0)
0292         {
0293             if (session_.get_option_decimals_as_strings())
0294             {
0295                 type = dt_string;
0296             }
0297             else
0298             {
0299                 type = dt_double;
0300             }
0301         }
0302         else if (dbprec <= std::numeric_limits<int>::digits10)
0303         {
0304             type = dt_integer;
0305         }
0306         else
0307         {
0308             type = dt_long_long;
0309         }
0310         break;
0311     case OCI_TYPECODE_BDOUBLE:
0312         type = dt_double;
0313         break;
0314     case SQLT_DAT:
0315         type = dt_date;
0316         break;
0317     default:
0318         // Unknown oracle types will just be represented by a string
0319         type = dt_string;
0320     }
0321 }
0322 
0323 std::size_t oracle_statement_backend::column_size(int position)
0324 {
0325     // Note: we may want to optimize so that the OCI_DESCRIBE_ONLY call
0326     // happens only once per statement.
0327     // Possibly use existing statement::describe() / make column prop
0328     // access lazy at same time
0329 
0330     int colSize(0);
0331 
0332     sword res = OCIStmtExecute(session_.svchp_, stmtp_,
0333          session_.errhp_, 1, 0, 0, 0, OCI_DESCRIBE_ONLY);
0334     if (res != OCI_SUCCESS)
0335     {
0336         throw_oracle_soci_error(res, session_.errhp_);
0337     }
0338 
0339     // Get The Column Handle
0340     OCIParam* colhd;
0341     res = OCIParamGet(reinterpret_cast<dvoid*>(stmtp_),
0342          static_cast<ub4>(OCI_HTYPE_STMT),
0343          reinterpret_cast<OCIError*>(session_.errhp_),
0344          reinterpret_cast<dvoid**>(&colhd),
0345          static_cast<ub4>(position));
0346     if (res != OCI_SUCCESS)
0347     {
0348         throw_oracle_soci_error(res, session_.errhp_);
0349     }
0350 
0351      // Get The Data Size
0352     res = OCIAttrGet(reinterpret_cast<dvoid*>(colhd),
0353          static_cast<ub4>(OCI_DTYPE_PARAM),
0354          reinterpret_cast<dvoid*>(&colSize),
0355          0,
0356          static_cast<ub4>(OCI_ATTR_DATA_SIZE),
0357          reinterpret_cast<OCIError*>(session_.errhp_));
0358     if (res != OCI_SUCCESS)
0359     {
0360         throw_oracle_soci_error(res, session_.errhp_);
0361     }
0362 
0363     return static_cast<std::size_t>(colSize);
0364 }