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 }