London C++ Meetup
Slides licensed under the Creative Commons Attribution License
C++ hacker, geospatial developer
Open Source developer
Boost.Geometry, GDAL, libLAS, PostGIS, SOCI, ...
Charter Member @OSGeo
Senior Programmer @Cadcorp
Sochi
(IPA: [ˈsotɕɪ])
Photo by Mikhail Mordasov | Copyright (C) Mikhail Mordasov (AFP - Getty Images)
Simple Oracle Call Interface
class statement {public:statement(session& s);void exchange(/*...*/);void define_and_bind();void prepare(string q);bool execute();bool fetch();};
class session {public:// open connectionsession(string c);void begin();void commit();void rollback();};
* bind , define terminology from Oracle Call Interface heritage; SOCI documentation use binding for directions.
std::string name; // define variableint id = 101; // bind variablesession db("dbname=test");statement st(db);st.exchange(id); // bind inputst.exchange(name); // define outputst.define_and_bind();st.prepare("SELECT name FROM person ""WHERE id = ?");st.execute();while (st.fetch()) {std::cout << name << std::endl;}
I'm expressin' with my full capabilities
Now I'm livin' in correctional facilities
...I get straight, meditate, like a Buddhist
...But my technique is very necessary
N.W.A - Express Youself, 1988
int v = 7;std::cout << v;
person p;session sql(...);sql << "SELECT name,age FROM person",into(p);
using namespace soci;session sql([BACKEND], [CONNECTION STRING]);
session sql;sql.open([BACKEND], [CONNECTION STRING]);
Database-specific connection session
string connstr("dbname=test user=skippy");// uses factory object providedsession sql(postgresql, connstr);// loads backend by name at run-timesession sql("postgresql", connstr);session sql("postgresql://dbname=test");
connection_parameters p("odbc","DSN=test");// Force SQL_DRIVER_NOPROMPT p.set_option(odbc_option_driver_complete,"0");session sql(p);
Executed only once, immediately
session sql;sql << "DROP TABLE employee";
string table_name("employee");sql << "DROP TABLE " << table_name;
int id = 7;sql << "DELETE FROM "<< table_name<< " WHERE id="<< id;
sql << "DROP TABLE " << table_name;
sql.once << "DROP TABLE " << table_name;
class session {public:once_type once;template <typename T>once_temp_type operator<<(T t) {return once << t;}};
sql.once << "DROP TABLE " << table_name;
class once_type { session* session_; public: template <typename T> once_temp_type operator<<(T t) {once_temp_type once_temp(*session_); return once_temp << t;} };
sql.once << "DROP TABLE " ? << table_name ?;
class once_temp_type {ref_counted_statement* st_;public:template <typename T>once_temp_type& operator<<(T t) {// push into buffer, stringstreamst_->accumulate(t);return *this;}~once_temp_type() { st_->dec_ref(); }};
sql.once << "DROP TABLE " << table_name; ?
class ref_counted_statement {statement st_;public:void def_ref() {if (--counter == 0) final_action();}void final_action() {st_.prepare(get_query());st_.define_and_bind();st_.execute();}};
noexcept(false)
Thanks to Roger Orr and Anthony Williams
for reporting this issue in SOCI.
string name; // output variablesql << "SELECT name FROM person",into(name);
int id = 7; // input variablesql << "SELECT name FROM personWHERE id=:i", into(name), use(id);
indicator ind;sql << "SELECT name FROM person",into(name, ind);if (ind == soci::i_null) { ... }
class once_temp_type {ref_counted_statement* st_;public:once_temp_type& operator,(into_type_ptr i){// push onto statement intosst_->exchange(i);}once_temp_type& operator,(use_type_ptr u){// push onto statement usesst_->exchange(u);}};
sql << "SELECT name FROM p", into(name);
template <typename T>into_type_ptr into(T& t) {return do_into(t, typenameexchange_traits<T>::type_family());}
template <typename T>into_type_ptr do_into(T& t,basic_type_tag) {return into_type_ptr(new into_type<T>(t));}
class into_type {// statement backend define_by_posvoid define(statement* s, int& position);// convert data to exchange typevoid post_fetch();void* data_;indicator* ind_;exchange_type type_;};
template <typename T>into_type_ptr do_into(T& t,basic_type_tag);
template <typename T>into_type_ptr do_into(T& t,indicator& ind,basic_type_tag);
template <typename T>into_type_ptr do_into(T& t,user_type_tag);
template <typename T>into_type_ptr do_into(T& t,indicator& ind,user_type_tag);
sql<<"SELECT n FROM p WHERE id=:i", use(id);
template <typename T>use_type_ptr use(T& t, string name) {return do_use(t, name, typenameexchange_traits<T>::type_family());}
template <typename T>use_type_ptr do_use(T& t, string name, basic_type_tag) {return use_type_ptr(new use_type<T>(t, name));}
class use_type {// statement backend bind_by_posvoid bind(statement* s, int& position);// convert data to exchange typevoid pre_use();void* data_;indicator* ind_;exchange_type type_;string name_;};
template <typename T>use_type_ptr do_use(T& t, string name, basic_type_tag);
template <typename T>use_type_ptr do_use(T& t,indicator& ind, string name, basic_type_tag);
template <typename T>use_type_ptr do_use(T& t,string name, user_type_tag);
template <typename T>use_type_ptr do_use(T& t,indicator& ind, string name, user_type_tag);
row r;sql << "SELECT name,age FROM p", into(r);
for (size_t i=0; i<r.size(); ++i) { column_properties& c = r.get_properties();if (c.get_data_type() == dt_string) string name = r.get<string>(i); else if (c.get_data_type() == dt_integer) int age = r.get<int>(i); }
string name; int age;r >> name >> age;
template<> struct type_conversion<MyX> {typedef string base_type;void from_base(string s,indicator i,MyX& x){if (i == i_null) { ... }x.from_string(s); // convert}void to_base(MyX x,string& s,indicator& i){s = x.to_string();// converti = i_ok;}};
CREATE TABLE person (name VARCHAR(60),age INT)
struct Person {string name;int age;};
template<> struct type_conversion<Person>{typedef values base_type;from_base(values v,indicator i,Person& p){p.name = v.get<string>();p.age = v.get<int>();}to_base(Person p,values& v,indicator& i){v.set("name", p.name);v.set("age", p.age);i = i_ok;}};
template<> struct type_conversion<MyX> {typedef string base_type;void from_base(string s,indicator i,MyX& x){if (i == i_null) { ... }x.from_string(s); // convert}void to_base(MyX x,string& s,indicator& i){s = x.to_string();// converti = i_ok;}};
Prepared statements and repeated execution
int i;statement st = (sql.prepare<< "INSERT INTO n(v) VALUES(:v)",use(i));for (i = 0 i < 100; ++i){st.execute(true);}
statement wrapper with STL-like iterator interface
rowset<row> rs = (sql.prepare<< "SELECT name,age FROM person");for (auto it=rs.begin(); it!=rs.end(); ++it){// dynamic data extraction from each rowcout << "name: " << it->get<string>(0)<< ",age: " << it->get<int>(1)<< endl;}