1 //
2 // Copyright (C) 2004-2016 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 #ifndef SOCI_BACKEND_H_INCLUDED
9 #define SOCI_BACKEND_H_INCLUDED
10 
11 #include "soci/soci-platform.h"
12 #include "soci/error.h"
13 // std
14 #include <cstddef>
15 #include <map>
16 #include <string>
17 #include <sstream>
18 
19 namespace soci
20 {
21 
22 // data types, as seen by the user
23 enum data_type
24 {
25     dt_string, dt_date, dt_double, dt_integer, dt_long_long, dt_unsigned_long_long,
26     dt_blob, dt_xml
27 };
28 
29 // the enum type for indicator variables
30 enum indicator { i_ok, i_null, i_truncated };
31 
32 class session;
33 class failover_callback;
34 
35 namespace details
36 {
37 
38 // data types, as used to describe exchange format
39 enum exchange_type
40 {
41     x_char,
42     x_stdstring,
43     x_short,
44     x_integer,
45     x_long_long,
46     x_unsigned_long_long,
47     x_double,
48     x_stdtm,
49     x_statement,
50     x_rowid,
51     x_blob,
52 
53     x_xmltype,
54     x_longstring
55 };
56 
57 // type of statement (used for optimizing statement preparation)
58 enum statement_type
59 {
60     st_one_time_query,
61     st_repeatable_query
62 };
63 
64 // polymorphic into type backend
65 
66 class standard_into_type_backend
67 {
68 public:
standard_into_type_backend()69     standard_into_type_backend() {}
~standard_into_type_backend()70     virtual ~standard_into_type_backend() {}
71 
72     virtual void define_by_pos(int& position, void* data, exchange_type type) = 0;
73 
pre_exec(int)74     virtual void pre_exec(int /* num */) {}
75     virtual void pre_fetch() = 0;
76     virtual void post_fetch(bool gotData, bool calledFromFetch, indicator* ind) = 0;
77 
78     virtual void clean_up() = 0;
79 
80 private:
81     SOCI_NOT_COPYABLE(standard_into_type_backend)
82 };
83 
84 class vector_into_type_backend
85 {
86 public:
87 
vector_into_type_backend()88     vector_into_type_backend() {}
~vector_into_type_backend()89     virtual ~vector_into_type_backend() {}
90 
define_by_pos_bulk(int &,void *,exchange_type,std::size_t,std::size_t *)91     virtual void define_by_pos_bulk(
92         int & /* position */, void * /* data */, exchange_type /* type */,
93         std::size_t /* begin */, std::size_t * /* end */)
94     {
95         throw soci_error("into bulk iterators are not supported with this backend");
96     }
97 
98     virtual void define_by_pos(int& position, void* data, exchange_type type) = 0;
99 
pre_exec(int)100     virtual void pre_exec(int /* num */) {}
101     virtual void pre_fetch() = 0;
102     virtual void post_fetch(bool gotData, indicator* ind) = 0;
103 
104     virtual void resize(std::size_t sz) = 0;
105     virtual std::size_t size() = 0;
106 
107     virtual void clean_up() = 0;
108 
109 private:
110     SOCI_NOT_COPYABLE(vector_into_type_backend)
111 };
112 
113 // polymorphic use type backend
114 
115 class standard_use_type_backend
116 {
117 public:
standard_use_type_backend()118     standard_use_type_backend() {}
~standard_use_type_backend()119     virtual ~standard_use_type_backend() {}
120 
121     virtual void bind_by_pos(int& position, void* data,
122         exchange_type type, bool readOnly) = 0;
123     virtual void bind_by_name(std::string const& name,
124         void* data, exchange_type type, bool readOnly) = 0;
125 
pre_exec(int)126     virtual void pre_exec(int /* num */) {}
127     virtual void pre_use(indicator const* ind) = 0;
128     virtual void post_use(bool gotData, indicator * ind) = 0;
129 
130     virtual void clean_up() = 0;
131 
132 private:
133     SOCI_NOT_COPYABLE(standard_use_type_backend)
134 };
135 
136 class vector_use_type_backend
137 {
138 public:
vector_use_type_backend()139     vector_use_type_backend() {}
~vector_use_type_backend()140     virtual ~vector_use_type_backend() {}
141 
142     virtual void bind_by_pos(int& position, void* data, exchange_type type) = 0;
bind_by_pos_bulk(int &,void *,exchange_type,std::size_t,std::size_t *)143     virtual void bind_by_pos_bulk(int& /* position */, void* /* data */, exchange_type /* type */,
144         std::size_t /* begin */, std::size_t * /* end */)
145     {
146         throw soci_error("use bulk iterators are not supported with this backend");
147     }
148 
149     virtual void bind_by_name(std::string const& name,
150         void* data, exchange_type type) = 0;
151 
bind_by_name_bulk(std::string const &,void *,exchange_type,std::size_t,std::size_t *)152     virtual void bind_by_name_bulk(std::string const& /* name */,
153         void* /* data */, exchange_type /* type */,
154         std::size_t /* begin */, std::size_t * /* end */)
155     {
156         throw soci_error("use bulk iterators are not supported with this backend");
157     }
158 
pre_exec(int)159     virtual void pre_exec(int /* num */) {}
160     virtual void pre_use(indicator const* ind) = 0;
161 
162     virtual std::size_t size() = 0;
163 
164     virtual void clean_up() = 0;
165 
166 private:
167     SOCI_NOT_COPYABLE(vector_use_type_backend)
168 };
169 
170 // polymorphic statement backend
171 
172 class statement_backend
173 {
174 public:
statement_backend()175     statement_backend() {}
~statement_backend()176     virtual ~statement_backend() {}
177 
178     virtual void alloc() = 0;
179     virtual void clean_up() = 0;
180 
181     virtual void prepare(std::string const& query, statement_type eType) = 0;
182 
183     enum exec_fetch_result
184     {
185         ef_success,
186         ef_no_data
187     };
188 
189     virtual exec_fetch_result execute(int number) = 0;
190     virtual exec_fetch_result fetch(int number) = 0;
191 
192     virtual long long get_affected_rows() = 0;
193     virtual int get_number_of_rows() = 0;
194 
195     virtual std::string get_parameter_name(int index) const = 0;
196 
197     virtual std::string rewrite_for_procedure_call(std::string const& query) = 0;
198 
199     virtual int prepare_for_describe() = 0;
200     virtual void describe_column(int colNum, data_type& dtype,
201         std::string& column_name) = 0;
202 
203     virtual standard_into_type_backend* make_into_type_backend() = 0;
204     virtual standard_use_type_backend* make_use_type_backend() = 0;
205     virtual vector_into_type_backend* make_vector_into_type_backend() = 0;
206     virtual vector_use_type_backend* make_vector_use_type_backend() = 0;
207 
208 private:
209     SOCI_NOT_COPYABLE(statement_backend)
210 };
211 
212 // polymorphic RowID backend
213 
214 class rowid_backend
215 {
216 public:
~rowid_backend()217     virtual ~rowid_backend() {}
218 };
219 
220 // polymorphic blob backend
221 
222 class blob_backend
223 {
224 public:
blob_backend()225     blob_backend() {}
~blob_backend()226     virtual ~blob_backend() {}
227 
228     virtual std::size_t get_len() = 0;
229 
230     virtual std::size_t read(std::size_t offset, char* buf,
231         std::size_t toRead) = 0;
232 
read_from_start(char *,std::size_t,std::size_t)233     virtual std::size_t read_from_start(char * /* buf */, std::size_t /* toRead */,
234         std::size_t /* offset */)
235     {
236         throw soci_error("read_from_start is not implemented for this backend");
237     }
238 
239     virtual std::size_t write(std::size_t offset, char const* buf,
240         std::size_t toWrite) = 0;
241 
write_from_start(const char *,std::size_t,std::size_t)242     virtual std::size_t write_from_start(const char * /* buf */, std::size_t /* toWrite */,
243         std::size_t /* offset */)
244     {
245         throw soci_error("write_from_start is not implemented for this backend");
246     }
247 
248     virtual std::size_t append(char const* buf, std::size_t toWrite) = 0;
249 
250     virtual void trim(std::size_t newLen) = 0;
251 
252 private:
253     SOCI_NOT_COPYABLE(blob_backend)
254 };
255 
256 // polymorphic session backend
257 
258 class session_backend
259 {
260 public:
session_backend()261     session_backend() : failoverCallback_(NULL), session_(NULL) {}
~session_backend()262     virtual ~session_backend() {}
263 
264     virtual bool is_connected() = 0;
265 
266     virtual void begin() = 0;
267     virtual void commit() = 0;
268     virtual void rollback() = 0;
269 
270     // At least one of these functions is usually not implemented for any given
271     // backend as RDBMS support either sequences or auto-generated values, so
272     // we don't declare them as pure virtuals to avoid having to define trivial
273     // versions of them in the derived classes. However every backend should
274     // define at least one of them to allow the code using auto-generated values
275     // to work.
get_next_sequence_value(session &,std::string const &,long long &)276     virtual bool get_next_sequence_value(session&, std::string const&, long long&)
277     {
278         return false;
279     }
get_last_insert_id(session &,std::string const &,long long &)280     virtual bool get_last_insert_id(session&, std::string const&, long long&)
281     {
282         return false;
283     }
284 
285     // There is a set of standard SQL metadata structures that can be
286     // queried in a portable way - backends that are standard compliant
287     // do not need to override the following methods, which are intended
288     // to return a proper query for basic metadata statements.
289 
290     // Returns a parameterless query for the list of table names in the current schema.
get_table_names_query()291     virtual std::string get_table_names_query() const
292     {
293         return "select table_name as \"TABLE_NAME\""
294             " from information_schema.tables"
295             " where table_schema = 'public'";
296     }
297 
298     // Returns a query with a single parameter (table name) for the list
299     // of columns and their properties.
get_column_descriptions_query()300     virtual std::string get_column_descriptions_query() const
301     {
302         return "select column_name as \"COLUMN_NAME\","
303             " data_type as \"DATA_TYPE\","
304             " character_maximum_length as \"CHARACTER_MAXIMUM_LENGTH\","
305             " numeric_precision as \"NUMERIC_PRECISION\","
306             " numeric_scale as \"NUMERIC_SCALE\","
307             " is_nullable as \"IS_NULLABLE\""
308             " from information_schema.columns"
309             " where table_schema = 'public' and table_name = :t";
310     }
311 
create_table(const std::string & tableName)312     virtual std::string create_table(const std::string & tableName)
313     {
314         return "create table " + tableName + " (";
315     }
drop_table(const std::string & tableName)316     virtual std::string drop_table(const std::string & tableName)
317     {
318         return "drop table " + tableName;
319     }
truncate_table(const std::string & tableName)320     virtual std::string truncate_table(const std::string & tableName)
321     {
322         return "truncate table " + tableName;
323     }
create_column_type(data_type dt,int precision,int scale)324     virtual std::string create_column_type(data_type dt,
325         int precision, int scale)
326     {
327         // PostgreSQL was selected as a baseline for the syntax:
328 
329         std::string res;
330         switch (dt)
331         {
332         case dt_string:
333             {
334                 std::ostringstream oss;
335 
336                 if (precision == 0)
337                 {
338                     oss << "text";
339                 }
340                 else
341                 {
342                     oss << "varchar(" << precision << ")";
343                 }
344 
345                 res += oss.str();
346             }
347             break;
348 
349         case dt_date:
350             res += "timestamp";
351             break;
352 
353         case dt_double:
354             {
355                 std::ostringstream oss;
356                 if (precision == 0)
357                 {
358                     oss << "numeric";
359                 }
360                 else
361                 {
362                     oss << "numeric(" << precision << ", " << scale << ")";
363                 }
364 
365                 res += oss.str();
366             }
367             break;
368 
369         case dt_integer:
370             res += "integer";
371             break;
372 
373         case dt_long_long:
374             res += "bigint";
375             break;
376 
377         case dt_unsigned_long_long:
378             res += "bigint";
379             break;
380 
381         case dt_blob:
382             res += "oid";
383             break;
384 
385         case dt_xml:
386             res += "xml";
387             break;
388 
389         default:
390             throw soci_error("this data_type is not supported in create_column");
391         }
392 
393         return res;
394     }
add_column(const std::string & tableName,const std::string & columnName,data_type dt,int precision,int scale)395     virtual std::string add_column(const std::string & tableName,
396         const std::string & columnName, data_type dt,
397         int precision, int scale)
398     {
399         return "alter table " + tableName + " add column " + columnName +
400             " " + create_column_type(dt, precision, scale);
401     }
alter_column(const std::string & tableName,const std::string & columnName,data_type dt,int precision,int scale)402     virtual std::string alter_column(const std::string & tableName,
403         const std::string & columnName, data_type dt,
404         int precision, int scale)
405     {
406         return "alter table " + tableName + " alter column " +
407             columnName + " type " +
408             create_column_type(dt, precision, scale);
409     }
drop_column(const std::string & tableName,const std::string & columnName)410     virtual std::string drop_column(const std::string & tableName,
411         const std::string & columnName)
412     {
413         return "alter table " + tableName +
414             " drop column " + columnName;
415     }
constraint_unique(const std::string & name,const std::string & columnNames)416     virtual std::string constraint_unique(const std::string & name,
417         const std::string & columnNames)
418     {
419         return "constraint " + name +
420             " unique (" + columnNames + ")";
421     }
constraint_primary_key(const std::string & name,const std::string & columnNames)422     virtual std::string constraint_primary_key(const std::string & name,
423         const std::string & columnNames)
424     {
425         return "constraint " + name +
426             " primary key (" + columnNames + ")";
427     }
constraint_foreign_key(const std::string & name,const std::string & columnNames,const std::string & refTableName,const std::string & refColumnNames)428     virtual std::string constraint_foreign_key(const std::string & name,
429         const std::string & columnNames,
430         const std::string & refTableName,
431         const std::string & refColumnNames)
432     {
433         return "constraint " + name +
434             " foreign key (" + columnNames + ")" +
435             " references " + refTableName + " (" + refColumnNames + ")";
436     }
empty_blob()437     virtual std::string empty_blob()
438     {
439         return "lo_creat(-1)";
440     }
nvl()441     virtual std::string nvl()
442     {
443         return "coalesce";
444     }
445 
446     virtual std::string get_dummy_from_table() const = 0;
447 
set_failover_callback(failover_callback & callback,session & sql)448     void set_failover_callback(failover_callback & callback, session & sql)
449     {
450         failoverCallback_ = &callback;
451         session_ = &sql;
452     }
453 
454     virtual std::string get_backend_name() const = 0;
455 
456     virtual statement_backend* make_statement_backend() = 0;
457     virtual rowid_backend* make_rowid_backend() = 0;
458     virtual blob_backend* make_blob_backend() = 0;
459 
460     failover_callback * failoverCallback_;
461     session * session_;
462 
463 private:
464     SOCI_NOT_COPYABLE(session_backend)
465 };
466 
467 } // namespace details
468 
469 // simple base class for the session back-end factory
470 
471 class connection_parameters;
472 
473 class SOCI_DECL backend_factory
474 {
475 public:
backend_factory()476     backend_factory() {}
~backend_factory()477     virtual ~backend_factory() {}
478 
479     virtual details::session_backend* make_session(
480         connection_parameters const& parameters) const = 0;
481 };
482 
483 } // namespace soci
484 
485 #endif // SOCI_BACKEND_H_INCLUDED
486