db_conn  v0.2.1-alpha
Database Connection API
db_conn

Header files based C++ database connection API based on KISS principle

The why?

I had a hard time finding a free open source C++ Sybase driver that would be lightweight with no dependencies, easy to add and use, having a generic extensible interface in case database would be switched later on. Since then I also added SQLite driver.

Thread safety:

The driver functions are thread-safe, all other objects (connection, statement, result_set) are not.

It's up to the users to decide whether to use (and what kind) any synchronization means or use a different design, eg:

  • use a single connection per thread
  • create a connection pool
  • create a separate database connection thread with its own connection

Currently supported databases:

Development state:

The code has not been extensively tested, thus there could be some bugs.

Especial concerns would be the large data type (text, blob, unitext) and utf support.

Usage example (same for all drivers for supported features - see sample files)

1 #include "sybase_driver.hpp"
2 
3 using namespace std;
4 using namespace vgi::dbconn::dbi;
5 using namespace vgi::dbconn::dbd;
6 
7 int main(int argc, char** argv)
8 {
9  try
10  {
11  connection conn = driver<sybase::driver>::load().get_connection("DBSYB1", "sa", "");
12  if (conn.connect())
13  {
14  statement stmt = conn.get_statement();
15 
16  // change database
17  stmt.execute("use tempdb");
18 
19  stmt.execute("if object_id('tempdb..test') is not null drop table test");
20 
21  // create
22  stmt.execute("create table test (id int, txt varchar(10) null, num numeric(18, 8) null, primary key(id))");
23 
24  // insert
25  stmt.execute("insert into test (id, txt) values (1, 'txt1') \
26  insert into test (id, txt) values (2, 'txt2')");
27 
28  // update
29  stmt.execute("update test set txt = 'test1' where id = 1 \
30  update test set txt = 'test2' where id = 2");
31 
32  // select
33  result_set rs = stmt.execute("select * from test");
34  while (rs.next())
35  {
36  cout << rs.column_name(0) << ": " << rs.get_int(0) << endl;
37  cout << rs.column_name(1) << ": " << rs.get_string(1) << endl;
38  cout << "column id: " << rs.get_int("id") << endl;
39  cout << "column txt1: " << rs.get_string("txt") << endl;
40  }
41 
42  // delete
43  stmt.prepare("delete from test where id = 2");
44  rs = stmt.execute();
45  cout << "rows affected = " << rs.rows_affected() << endl;
46 
47  // prepared statement
48  stmt.prepare("insert into test values (?, ?)");
49  stmt.set_int(0, 2);
50  stmt.set_string(1, "test2");
51  stmt.execute();
52 
53  // cursor
54  rs = stmt.execute("select id, txt from test", true);
55  while (rs.next())
56  {
57  cout << rs.column_name(0) << ": " << (rs.is_null(0) ? -1 : rs.get_int(0)) << endl;
58  cout << rs.column_name(1) << ": " << (rs.is_null(1) ? "NULL" : rs.get_string(1)) << endl;
59  }
60 
61  // scrollable cursor
62  rs = stmt.execute("select id, txt from test", true, true);
63  while (rs.next())
64  {
65  cout << rs.column_name(0) << ": " << (rs.is_null(0) ? -1 : rs.get_int(0)) << endl;
66  cout << rs.column_name(1) << ": " << (rs.is_null(1) ? "NULL" : rs.get_string(1)) << endl;
67  }
68  rs.first();
69  do
70  {
71  cout << rs.column_name(0) << ": " << (rs.is_null(0) ? -1 : rs.get_int(0)) << endl;
72  cout << rs.column_name(1) << ": " << (rs.is_null(1) ? "NULL" : rs.get_string(1)) << endl;
73  }
74  while (rs.next());
75  while (rs.prev())
76  {
77  cout << rs.column_name(0) << ": " << (rs.is_null(0) ? -1 : rs.get_int(0)) << endl;
78  cout << rs.column_name(1) << ": " << (rs.is_null(1) ? "NULL" : rs.get_string(1)) << endl;
79  }
80 
81  // stored procedure
82  stmt.execute("create procedure test_proc @id int, @error varchar(128) output AS \
83  BEGIN \
84  DECLARE @ret int \
85  SET @error = NULL \
86  SET @ret = 0 \
87  IF @id = 0 \
88  BEGIN \
89  SET @error = 'id must be > 0' \
90  SET @ret = 1 \
91  END \
92  SELECT txt FROM test WHERE id = @id \
93  RETURN @ret \
94  END");
95  stmt.call("test_proc");
96  stmt.set_int(0, 3);
97  rs = stmt.execute();
98  while (rs.next())
99  cout << rs.column_name(0) << ": " << (rs.is_null(0) ? "NULL" : rs.get_string(0)) << endl;
100  cout << "rows affected = " << rs.rows_affected() << endl;
101  cout << "stored proc return = " << stmt.proc_retval() << endl;
102  cout << "@error: >" << (rs.is_null(0) ? "NULL" : rs.get_string("@error")) << endl;
103 
104  // truncate
105  stmt.execute("truncate table test");
106 
107  // transaction
108  conn.autocommit(false);
109  stmt.execute("insert into test values (1, 'test1')");
110  conn.commit();
111  stmt.execute("insert into test values (2, 'test2')");
112  stmt.execute("insert into test values (3, 'test3')");
113  conn.rollback();
114  conn.autocommit(true);
115  rs = stmt.execute("select count(*) from test");
116  rs.next();
117  cout << "Table has " << rs.get_int(0) << " rows\n";
118 
119  // drop
120  stmt.execute("drop table test");
121  }
122  else
123  cout << "failed to connect!\n";
124  }
125  catch (const exception& e)
126  {
127  cout << "exception: " << e.what() << endl;
128  }
129  return 0;
130 }

Limited amount of testing was done.

If someone would like to contribute please ping me.