1 //
2 // Copyright (C) 2004-2007 Maciej Sobczak, Stephen Hutton
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
6 //
7
8 #define soci_ORACLE_SOURCE
9
10 #include "soci/oracle/soci-oracle.h"
11 #include "error.h"
12 #include "soci/soci-backend.h"
13 #include <cctype>
14 #include <cstdio>
15 #include <cstring>
16 #include <ctime>
17 #include <limits>
18 #include <sstream>
19
20 #ifdef _MSC_VER
21 #pragma warning(disable:4355)
22 #endif
23
24 using namespace soci;
25 using namespace soci::details;
26 using namespace soci::details::oracle;
27
oracle_statement_backend(oracle_session_backend & session)28 oracle_statement_backend::oracle_statement_backend(oracle_session_backend &session)
29 : session_(session), stmtp_(NULL), boundByName_(false), boundByPos_(false),
30 noData_(false)
31 {
32 }
33
alloc()34 void oracle_statement_backend::alloc()
35 {
36 sword res = OCIHandleAlloc(session_.envhp_,
37 reinterpret_cast<dvoid**>(&stmtp_),
38 OCI_HTYPE_STMT, 0, 0);
39 if (res != OCI_SUCCESS)
40 {
41 throw soci_error("Cannot allocate statement handle");
42 }
43 }
44
clean_up()45 void oracle_statement_backend::clean_up()
46 {
47 // deallocate statement handle
48 if (stmtp_ != NULL)
49 {
50 OCIHandleFree(stmtp_, OCI_HTYPE_STMT);
51 stmtp_ = NULL;
52 }
53
54 boundByName_ = false;
55 boundByPos_ = false;
56 }
57
prepare(std::string const & query,statement_type)58 void oracle_statement_backend::prepare(std::string const &query,
59 statement_type /* eType */)
60 {
61 sb4 stmtLen = static_cast<sb4>(query.size());
62 sword res = OCIStmtPrepare(stmtp_,
63 session_.errhp_,
64 reinterpret_cast<text*>(const_cast<char*>(query.c_str())),
65 stmtLen, OCI_V7_SYNTAX, OCI_DEFAULT);
66 if (res != OCI_SUCCESS)
67 {
68 throw_oracle_soci_error(res, session_.errhp_);
69 }
70 }
71
execute(int number)72 statement_backend::exec_fetch_result oracle_statement_backend::execute(int number)
73 {
74 sword res = OCIStmtExecute(session_.svchp_, stmtp_, session_.errhp_,
75 static_cast<ub4>(number), 0, 0, 0, OCI_DEFAULT);
76
77 if (res == OCI_SUCCESS || res == OCI_SUCCESS_WITH_INFO)
78 {
79 noData_ = false;
80 return ef_success;
81 }
82 else if (res == OCI_NO_DATA)
83 {
84 noData_ = true;
85 return ef_no_data;
86 }
87 else
88 {
89 throw_oracle_soci_error(res, session_.errhp_);
90 return ef_no_data; // unreachable dummy return to please the compiler
91 }
92 }
93
fetch(int number)94 statement_backend::exec_fetch_result oracle_statement_backend::fetch(int number)
95 {
96 if (noData_)
97 {
98 return ef_no_data;
99 }
100
101 sword res = OCIStmtFetch(stmtp_, session_.errhp_,
102 static_cast<ub4>(number), OCI_FETCH_NEXT, OCI_DEFAULT);
103
104 if (res == OCI_SUCCESS || res == OCI_SUCCESS_WITH_INFO)
105 {
106 return ef_success;
107 }
108 else if (res == OCI_NO_DATA)
109 {
110 noData_ = true;
111 return ef_no_data;
112 }
113 else
114 {
115 throw_oracle_soci_error(res, session_.errhp_);
116 return ef_no_data; // unreachable dummy return to please the compiler
117 }
118 }
119
get_affected_rows()120 long long oracle_statement_backend::get_affected_rows()
121 {
122 ub4 row_count;
123 sword res = OCIAttrGet(static_cast<dvoid*>(stmtp_),
124 OCI_HTYPE_STMT, &row_count,
125 0, OCI_ATTR_ROW_COUNT, session_.errhp_);
126
127 if (res != OCI_SUCCESS)
128 {
129 throw_oracle_soci_error(res, session_.errhp_);
130 }
131
132 return row_count;
133 }
134
get_number_of_rows()135 int oracle_statement_backend::get_number_of_rows()
136 {
137 int rows;
138 sword res = OCIAttrGet(static_cast<dvoid*>(stmtp_),
139 OCI_HTYPE_STMT, static_cast<dvoid*>(&rows),
140 0, OCI_ATTR_ROWS_FETCHED, session_.errhp_);
141
142 if (res != OCI_SUCCESS)
143 {
144 throw_oracle_soci_error(res, session_.errhp_);
145 }
146
147 return rows;
148 }
149
get_parameter_name(int) const150 std::string oracle_statement_backend::get_parameter_name(int /* index */) const
151 {
152 // TODO: How to get the parameter names from the query we prepared?
153 return std::string();
154 }
155
rewrite_for_procedure_call(std::string const & query)156 std::string oracle_statement_backend::rewrite_for_procedure_call(
157 std::string const &query)
158 {
159 std::string newQuery("begin ");
160 newQuery += query;
161 newQuery += "; end;";
162 return newQuery;
163 }
164
prepare_for_describe()165 int oracle_statement_backend::prepare_for_describe()
166 {
167 sword res = OCIStmtExecute(session_.svchp_, stmtp_, session_.errhp_,
168 1, 0, 0, 0, OCI_DESCRIBE_ONLY);
169 if (res != OCI_SUCCESS)
170 {
171 throw_oracle_soci_error(res, session_.errhp_);
172 }
173
174 int cols;
175 res = OCIAttrGet(static_cast<dvoid*>(stmtp_),
176 static_cast<ub4>(OCI_HTYPE_STMT), static_cast<dvoid*>(&cols),
177 0, static_cast<ub4>(OCI_ATTR_PARAM_COUNT), session_.errhp_);
178
179 if (res != OCI_SUCCESS)
180 {
181 throw_oracle_soci_error(res, session_.errhp_);
182 }
183
184 return cols;
185 }
186
describe_column(int colNum,data_type & type,std::string & columnName)187 void oracle_statement_backend::describe_column(int colNum, data_type &type,
188 std::string &columnName)
189 {
190 ub2 dbtype;
191 text* dbname;
192 ub4 nameLength;
193
194 ub2 dbsize;
195 sb2 dbprec;
196 ub1 dbscale; //sb2 in some versions of Oracle?
197
198 // Get the column handle
199 OCIParam* colhd;
200 sword res = OCIParamGet(reinterpret_cast<dvoid*>(stmtp_),
201 static_cast<ub4>(OCI_HTYPE_STMT),
202 reinterpret_cast<OCIError*>(session_.errhp_),
203 reinterpret_cast<dvoid**>(&colhd),
204 static_cast<ub4>(colNum));
205 if (res != OCI_SUCCESS)
206 {
207 throw_oracle_soci_error(res, session_.errhp_);
208 }
209
210 // Get the column name
211 res = OCIAttrGet(reinterpret_cast<dvoid*>(colhd),
212 static_cast<ub4>(OCI_DTYPE_PARAM),
213 reinterpret_cast<dvoid**>(&dbname),
214 reinterpret_cast<ub4*>(&nameLength),
215 static_cast<ub4>(OCI_ATTR_NAME),
216 reinterpret_cast<OCIError*>(session_.errhp_));
217 if (res != OCI_SUCCESS)
218 {
219 throw_oracle_soci_error(res, session_.errhp_);
220 }
221
222 // Get the column type
223 res = OCIAttrGet(reinterpret_cast<dvoid*>(colhd),
224 static_cast<ub4>(OCI_DTYPE_PARAM),
225 reinterpret_cast<dvoid*>(&dbtype),
226 0,
227 static_cast<ub4>(OCI_ATTR_DATA_TYPE),
228 reinterpret_cast<OCIError*>(session_.errhp_));
229 if (res != OCI_SUCCESS)
230 {
231 throw_oracle_soci_error(res, session_.errhp_);
232 }
233
234 // get the data size
235 res = OCIAttrGet(reinterpret_cast<dvoid*>(colhd),
236 static_cast<ub4>(OCI_DTYPE_PARAM),
237 reinterpret_cast<dvoid*>(&dbsize),
238 0,
239 static_cast<ub4>(OCI_ATTR_DATA_SIZE),
240 reinterpret_cast<OCIError*>(session_.errhp_));
241 if (res != OCI_SUCCESS)
242 {
243 throw_oracle_soci_error(res, session_.errhp_);
244 }
245
246 // get the precision
247 res = OCIAttrGet(reinterpret_cast<dvoid*>(colhd),
248 static_cast<ub4>(OCI_DTYPE_PARAM),
249 reinterpret_cast<dvoid*>(&dbprec),
250 0,
251 static_cast<ub4>(OCI_ATTR_PRECISION),
252 reinterpret_cast<OCIError*>(session_.errhp_));
253 if (res != OCI_SUCCESS)
254 {
255 throw_oracle_soci_error(res, session_.errhp_);
256 }
257
258 // get the scale
259 res = OCIAttrGet(reinterpret_cast<dvoid*>(colhd),
260 static_cast<ub4>(OCI_DTYPE_PARAM),
261 reinterpret_cast<dvoid*>(&dbscale),
262 0,
263 static_cast<ub4>(OCI_ATTR_SCALE),
264 reinterpret_cast<OCIError*>(session_.errhp_));
265 if (res != OCI_SUCCESS)
266 {
267 throw_oracle_soci_error(res, session_.errhp_);
268 }
269
270 columnName.assign(dbname, dbname + nameLength);
271
272 switch (dbtype)
273 {
274 case SQLT_CHR:
275 case SQLT_AFC:
276 type = dt_string;
277 break;
278 case SQLT_NUM:
279 if (dbscale > 0)
280 {
281 if (session_.get_option_decimals_as_strings())
282 {
283 type = dt_string;
284 }
285 else
286 {
287 type = dt_double;
288 }
289 }
290 else if (dbprec <= std::numeric_limits<int>::digits10)
291 {
292 type = dt_integer;
293 }
294 else
295 {
296 type = dt_long_long;
297 }
298 break;
299 case OCI_TYPECODE_BDOUBLE:
300 type = dt_double;
301 break;
302 case SQLT_DAT:
303 type = dt_date;
304 break;
305 default:
306 // Unknown oracle types will just be represented by a string
307 type = dt_string;
308 }
309 }
310
column_size(int position)311 std::size_t oracle_statement_backend::column_size(int position)
312 {
313 // Note: we may want to optimize so that the OCI_DESCRIBE_ONLY call
314 // happens only once per statement.
315 // Possibly use existing statement::describe() / make column prop
316 // access lazy at same time
317
318 int colSize(0);
319
320 sword res = OCIStmtExecute(session_.svchp_, stmtp_,
321 session_.errhp_, 1, 0, 0, 0, OCI_DESCRIBE_ONLY);
322 if (res != OCI_SUCCESS)
323 {
324 throw_oracle_soci_error(res, session_.errhp_);
325 }
326
327 // Get The Column Handle
328 OCIParam* colhd;
329 res = OCIParamGet(reinterpret_cast<dvoid*>(stmtp_),
330 static_cast<ub4>(OCI_HTYPE_STMT),
331 reinterpret_cast<OCIError*>(session_.errhp_),
332 reinterpret_cast<dvoid**>(&colhd),
333 static_cast<ub4>(position));
334 if (res != OCI_SUCCESS)
335 {
336 throw_oracle_soci_error(res, session_.errhp_);
337 }
338
339 // Get The Data Size
340 res = OCIAttrGet(reinterpret_cast<dvoid*>(colhd),
341 static_cast<ub4>(OCI_DTYPE_PARAM),
342 reinterpret_cast<dvoid*>(&colSize),
343 0,
344 static_cast<ub4>(OCI_ATTR_DATA_SIZE),
345 reinterpret_cast<OCIError*>(session_.errhp_));
346 if (res != OCI_SUCCESS)
347 {
348 throw_oracle_soci_error(res, session_.errhp_);
349 }
350
351 return static_cast<std::size_t>(colSize);
352 }
353