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