1 #include "dbixx.h"
2 #include <stdio.h>
3 #include <limits>
4 #include <iomanip>
5 #include <sstream>
6
7 namespace dbixx {
8
9 using namespace std;
10
11
12 class loader {
13 public:
loader()14 loader() { dbi_initialize(NULL); };
~loader()15 ~loader() { dbi_shutdown(); };
16 };
17
18 static loader backend_loader;
19
session()20 session::session()
21 {
22 conn=NULL;
23 }
24
connect(std::string const & connection_string)25 void session::connect(std::string const &connection_string)
26 {
27 size_t p = connection_string.find(':');
28 if( p == std::string::npos )
29 throw dbixx_error("Invalid connection string - no driver given");
30 driver(connection_string.substr(0,p));
31 p++;
32 while(p<connection_string.size()) {
33 size_t n=connection_string.find('=',p);
34 if(n==std::string::npos)
35 throw dbixx_error("Invalid connection string - invalid property");
36 std::string key = connection_string.substr(p,n-p);
37 p=n+1;
38 std::string value;
39 bool is_string = true;
40 if(p>=connection_string.size()) {
41 /// Nothing - empty property
42 }
43 else if(connection_string[p]=='\'') {
44 p++;
45 while(true) {
46 if(p>=connection_string.size()) {
47 throw dbixx_error("Invalid connection string unterminated string");
48 }
49 if(connection_string[p]=='\'') {
50 if(p+1 < connection_string.size() && connection_string[p+1]=='\'') {
51 value+='\'';
52 p+=2;
53 }
54 else {
55 p++;
56 break;
57 }
58 }
59 else {
60 value+=connection_string[p];
61 p++;
62 }
63 }
64 }
65 else {
66 size_t n=connection_string.find(';',p);
67 if(n==std::string::npos) {
68 value=connection_string.substr(p);
69 p=connection_string.size();
70 }
71 else {
72 value=connection_string.substr(p,n-p);
73 p=n;
74 }
75 if(!value.empty()) {
76 size_t pos = 0;
77 if(value[0]=='-') {
78 pos = 1;
79 }
80 bool digit_detected = false;
81 bool only_digits = true;
82 while(pos < value.size() && only_digits) {
83 if('0'<=value[pos] && value[pos]<='9')
84 digit_detected = true;
85 else
86 only_digits = false;
87 pos++;
88 }
89 if(only_digits && digit_detected) {
90 is_string = false;
91 }
92 }
93 }
94 if(is_string) {
95 param(key,value);
96 }
97 else {
98 param(key,atoi(value.c_str()));
99 }
100 if(p < connection_string.size() && connection_string[p]==';')
101 ++p;
102
103 }
104 connect();
105 }
106
connect()107 void session::connect()
108 {
109 check_open();
110 map<string,string>::const_iterator sp;
111 for(sp=string_params.begin();sp!=string_params.end();sp++){
112 if(dbi_conn_set_option(conn,sp->first.c_str(),sp->second.c_str())) {
113 error();
114 }
115 }
116
117 map<string,int>::const_iterator ip;
118 for(ip=numeric_params.begin();ip!=numeric_params.end();ip++){
119 if(dbi_conn_set_option_numeric(conn,ip->first.c_str(),ip->second)) {
120 error();
121 }
122 }
123
124 if(dbi_conn_connect(conn)<0) {
125 error();
126 }
127 }
128
reconnect()129 void session::reconnect()
130 {
131 close();
132 driver(this->backend);
133 connect();
134 }
135
session(string const & backend_or_conn_str)136 session::session(string const &backend_or_conn_str)
137 {
138 conn=NULL;
139
140 if(backend_or_conn_str.find(':')==std::string::npos)
141 driver(backend_or_conn_str);
142 else
143 connect(backend_or_conn_str);
144 }
145
close()146 void session::close()
147 {
148 if(conn) {
149 dbi_conn_close(conn);
150 conn=NULL;
151 }
152 }
153
~session()154 session::~session()
155 {
156 close();
157 }
158
driver(string const & backend)159 void session::driver(string const &backend)
160 {
161 close();
162 this->backend=backend;
163 conn=dbi_conn_new(backend.c_str());
164 if(!conn) {
165 throw dbixx_error("Failed to load backend");
166 }
167 }
168
driver()169 std::string session::driver()
170 {
171 return backend;
172 }
173
check_open(void)174 void session::check_open(void)
175 {
176 if(!conn) throw dbixx_error("Backend is not open");
177 }
178
rowid(char const * name)179 unsigned long long session::rowid(char const *name)
180 {
181 check_open();
182 return dbi_conn_sequence_last(conn,name);
183 }
184
error()185 void session::error()
186 {
187 char const *e;
188 dbi_conn_error(conn,&e);
189 throw dbixx_error(e,escaped_query);
190 }
191
param(string const & par,string const & val)192 void session::param(string const &par,string const &val)
193 {
194 string_params[par]=val;
195 }
196
param(string const & par,int val)197 void session::param(string const &par,int val)
198 {
199 numeric_params[par]=val;
200 }
201
escape()202 void session::escape()
203 {
204 while(pos_read<query_in.size()) {
205 if(query_in[pos_read]=='\'') {
206 escaped_query+='\'';
207 pos_read++;
208 while(query_in[pos_read]!='\'' && pos_read!=query_in.size()){
209 escaped_query+=query_in[pos_read];
210 pos_read++;
211 }
212 if(pos_read==query_in.size())
213 throw dbixx_error("Unexpected end of query after \"'\"");
214 escaped_query+='\'';
215 pos_read++;
216 continue;
217 }
218 if(query_in[pos_read]=='?') {
219 ready_for_input=true;
220 pos_read++;
221 break;
222 }
223 escaped_query+=query_in[pos_read];
224 pos_read++;
225 }
226 if(ready_for_input)
227 return;
228 if(pos_read==query_in.size()) {
229 complete=true;
230 return;
231 }
232 throw dbixx_error("Internal dbixx error");
233 }
234
check_input()235 void session::check_input()
236 {
237 if(!ready_for_input) {
238 throw dbixx_error("More parameters given then inputs in query");
239 }
240 }
241
242 template<typename T>
do_bind(T v,bool is_null)243 void session::do_bind(T v,bool is_null)
244 {
245 check_input();
246 // The representation of a number in decimal form
247 // is more compact then in binary so it should be enough
248 if(is_null) {
249 escaped_query+="NULL";
250 }
251 else {
252 std::ostringstream ss;
253 ss.imbue(std::locale::classic());
254
255 if(!std::numeric_limits<T>::is_integer) {
256 ss<<std::setprecision(std::numeric_limits<T>::digits10+1);
257 }
258
259 ss << v;
260 escaped_query+=ss.str();
261 }
262 ready_for_input=false;
263 escape();
264 }
265
bind(int v,bool isnull)266 void session::bind(int v,bool isnull) { do_bind(v,isnull); }
bind(unsigned v,bool isnull)267 void session::bind(unsigned v,bool isnull) { do_bind(v,isnull); }
bind(long v,bool isnull)268 void session::bind(long v,bool isnull) { do_bind(v,isnull); }
bind(unsigned long v,bool isnull)269 void session::bind(unsigned long v,bool isnull) { do_bind(v,isnull); }
bind(long long v,bool isnull)270 void session::bind(long long v,bool isnull) { do_bind(v,isnull); }
bind(unsigned long long v,bool isnull)271 void session::bind(unsigned long long v,bool isnull) { do_bind(v,isnull); }
bind(double v,bool isnull)272 void session::bind(double v,bool isnull) { do_bind(v,isnull); }
bind(long double v,bool isnull)273 void session::bind(long double v,bool isnull) { do_bind(v,isnull); }
274
bind(std::tm const & v,bool isnull)275 void session::bind(std::tm const &v,bool isnull)
276 {
277 check_input();
278 // The representation of a number in decimal form
279 // is more compact then in binary so it should be enough
280 if(isnull) {
281 escaped_query+="NULL";
282 }
283 else {
284 std::ostringstream ss;
285 ss.imbue(std::locale::classic());
286 ss<<std::setfill('0');
287 ss <<"'";
288 ss << std::setw(4) << v.tm_year+1900 <<'-';
289 ss << std::setw(2) << v.tm_mon+1 <<'-';
290 ss << std::setw(2) << v.tm_mday <<' ';
291 ss << std::setw(2) << v.tm_hour <<':';
292 ss << std::setw(2) << v.tm_min <<':';
293 ss << std::setw(2) << v.tm_sec;
294 ss <<"'";
295
296 escaped_query+=ss.str();
297 }
298 ready_for_input=false;
299 escape();
300 }
301
bind(null const & m,bool isnull)302 void session::bind(null const &m,bool isnull)
303 {
304 check_input();
305 escaped_query+="NULL";
306 ready_for_input=false;
307 escape();
308 }
309
310
bind(string const & s,bool isnull)311 void session::bind(string const &s,bool isnull)
312 {
313 check_input();
314 check_open();
315 if(isnull) {
316 escaped_query+="NULL";
317 }
318 else {
319 if(s.size()!=0){
320 char *new_str=NULL;
321 size_t sz=dbi_conn_quote_string_copy(conn,s.c_str(),&new_str);
322 if(sz==0) {
323 error();
324 }
325 try {
326 escaped_query+=new_str;
327 }
328 catch(...) {
329 free(new_str);
330 throw;
331 };
332 free(new_str);
333 }
334 else {
335 escaped_query+="\'\'";
336 }
337 }
338 ready_for_input=false;
339 escape();
340 }
341
342
343
query(std::string const & q)344 void session::query(std::string const &q)
345 {
346 complete=false;
347 ready_for_input=false;
348 query_in=q;
349 pos_read=0;
350 escaped_query="";
351 escaped_query.reserve(q.size()*3/2);
352 pos_write=0;
353 escape();
354 }
355
exec()356 void session::exec()
357 {
358 check_open();
359 if(!complete)
360 throw dbixx_error("Not all parameters are bind");
361 dbi_result res=dbi_conn_query(conn,escaped_query.c_str());
362 if(!res) error();
363 if(dbi_result_get_numrows(res)!=0) {
364 dbi_result_free(res);
365 throw dbixx_error("exec() query may not return results");
366 }
367 affected_rows=dbi_result_get_numrows_affected(res);
368 dbi_result_free(res);
369 }
370
fetch(result & r)371 void session::fetch(result &r)
372 {
373 check_open();
374 if(!complete)
375 throw dbixx_error("Not all parameters are bind");
376 dbi_result res=dbi_conn_query(conn,escaped_query.c_str());
377 if(!res) error();
378 r.assign(res);
379 }
380
single(row & r)381 bool session::single(row &r)
382 {
383 check_open();
384 if(!complete)
385 throw dbixx_error("Not all parameters are bind");
386 dbi_result res=dbi_conn_query(conn,escaped_query.c_str());
387 if(!res) error();
388 int n;
389 if((n=dbi_result_get_numrows(res))!=0 && n!=1) {
390 dbi_result_free(res);
391 throw dbixx_error("signle() must return 1 or 0 rows");
392 }
393 if(n==1) {
394 r.assign(res);
395 return true;
396 }
397 else {
398 r.reset();
399 }
400 return false;
401 }
402
~transaction()403 transaction::~transaction()
404 {
405 if(!commited){
406 try {
407 sql<<"ROLLBACK",exec();
408 }
409 catch(...){}
410 }
411 }
412
begin()413 void transaction::begin()
414 {
415 sql<<"BEGIN",exec();
416 }
417
commit()418 void transaction::commit()
419 {
420 sql<<"COMMIT",exec();
421 commited=true;
422 }
423
rollback()424 void transaction::rollback()
425 {
426 sql<<"ROLLBACK",exec();
427 commited=true;
428 }
429
430 } // END OF NAMESPACE DBIXX
431