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 connection
session(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 variable
int id = 101; // bind variable
session db("dbname=test");
statement st(db);
st.exchange(id); // bind input
st.exchange(name); // define output
st.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 provided
session sql(postgresql, connstr);
// loads backend by name at run-time
session 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, stringstream
st_->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 variable
sql << "SELECT name FROM person",into(name);
int id = 7; // input variable
sql << "SELECT name FROM person
WHERE 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 intos
st_->exchange(i);
}
once_temp_type& operator,(use_type_ptr u){
// push onto statement uses
st_->exchange(u);
}
};
sql << "SELECT name FROM p", into(name);
template <typename T>
into_type_ptr into(T& t) {
return do_into(t, typename
exchange_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_pos
void define(statement* s, int& position);
// convert data to exchange type
void 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, typename
exchange_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_pos
void bind(statement* s, int& position);
// convert data to exchange type
void 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();// convert
i = 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();// convert
i = 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 row
cout << "name: " << it->get<string>(0)
<< ",age: " << it->get<int>(1)
<< endl;
}