r20378 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r20377‎ | r20378 | r20379 >
Date:23:45, 12 March 2007
Author:river
Status:old
Tags:
Comment:
don't depend on orapp
prepared statements for db layer
\lt, \dt commands to list/describe tables
Modified paths:
  • /trunk/skirmish/Makefile (modified) (history)
  • /trunk/skirmish/db.cc (modified) (history)
  • /trunk/skirmish/db.h (modified) (history)
  • /trunk/skirmish/mysql.cc (modified) (history)
  • /trunk/skirmish/mysqldb.h (modified) (history)
  • /trunk/skirmish/ora.cc (modified) (history)
  • /trunk/skirmish/ora.h (modified) (history)
  • /trunk/skirmish/orapp (deleted) (history)
  • /trunk/skirmish/pgsql.cc (modified) (history)
  • /trunk/skirmish/pgsql.h (modified) (history)
  • /trunk/skirmish/rules.mk (modified) (history)
  • /trunk/skirmish/skirmish.cc (modified) (history)

Diff [purge]

Index: trunk/skirmish/rules.mk
@@ -1,5 +1,3 @@
2 -include config.mk
3 -
42 ifeq ($(BUILD_MYSQL),YES)
53 INCLUDES += $(shell mysql_config --include)
64 LIBS += $(shell mysql_config --libs)
@@ -8,8 +6,8 @@
97 endif
108
119 ifeq ($(BUILD_ORACLE),YES)
12 -INCLUDES += -I$(ORACLE_HOME)/rdbms/public -Iorapp
13 -LIBS += -L$(ORACLE_HOME)/lib -lclntsh -Lorapp -lorapp
 10+INCLUDES += -I$(ORACLE_HOME)/rdbms/public
 11+LIBS += -L$(ORACLE_HOME)/lib -lclntsh
1412 SUBDIRS += orapp
1513 CPPFLAGS += -DSKIRMISH_ORACLE
1614 DB_SRCS += ora.cc
Index: trunk/skirmish/mysqldb.h
@@ -10,22 +10,25 @@
1111
1212 namespace mysql {
1313
14 -struct execution_result;
 14+struct result;
1515
1616 struct result_row : db::result_row {
17 - result_row(execution_result *er, MYSQL_ROW row);
 17+ result_row(result *er, MYSQL_ROW row);
1818
1919 std::string string_value(int col);
2020
2121 MYSQL_ROW row;
22 - execution_result *er;
 22+ result *er;
2323 };
2424
25 -struct execution_result : db::execution_result {
26 - execution_result(MYSQL *);
27 - ~execution_result();
 25+struct result : db::result {
 26+ result(MYSQL *, std::string const &sql);
 27+ ~result();
2828
29 - bool has_data(void);
 29+ void bind(std::string const &, std::string const &);
 30+ void execute();
 31+
 32+ bool empty(void);
3033 int num_fields(void);
3134 int affected_rows(void);
3235 std::string field_name(int col);
@@ -35,6 +38,7 @@
3639 MYSQL *conn;
3740 MYSQL_RES *res;
3841 std::vector<std::string> names;
 42+ std::string sql;
3943 };
4044
4145 struct connection : db::connection {
@@ -46,7 +50,8 @@
4751
4852 std::string error(void);
4953
50 - execution_result *execute_sql(std::string const &);
 54+ db::resultptr execute_sql(std::string const &);
 55+ db::resultptr prepare_sql(std::string const &);
5156 std::vector<db::table> describe_tables(std::string const &);
5257 db::table describe_table(std::string const &, std::string const &);
5358
Index: trunk/skirmish/ora.cc
@@ -7,6 +7,16 @@
88
99 #include "ora.h"
1010
 11+namespace {
 12+
 13+bool
 14+ora_success(int e)
 15+{
 16+ return e == OCI_SUCCESS || e == OCI_SUCCESS_WITH_INFO;
 17+}
 18+
 19+}
 20+
1121 namespace oracle {
1222
1323 connection::connection(std::string const &desc)
@@ -43,127 +53,204 @@
4454 void
4555 connection::open(void)
4656 {
47 - if (!db.connect(sid, user, password))
48 - throw db::error(db.error());
 57+ if (!ora_success(OCIEnvCreate(&env, OCI_DEFAULT,
 58+ NULL, NULL, NULL, NULL, 0, NULL)))
 59+ throw db::error(error());
 60+
 61+ if (!ora_success(OCIHandleAlloc(env, (void **)&err, OCI_HTYPE_ERROR, 0, NULL)))
 62+ throw db::error(error());
 63+
 64+ if (!ora_success(OCILogon(env, err, &svc,
 65+ (text *)user.c_str(), user.size(),
 66+ (text *)password.c_str(), password.size(),
 67+ (text *)sid.c_str(), sid.size())))
 68+ throw db::error(error());
4969 }
5070
5171 void
5272 connection::close(void)
5373 {
54 - db.disconnect();
 74+ OCILogoff(svc, err);
 75+ svc = NULL;
 76+ OCIHandleFree(err, OCI_HTYPE_ERROR);
 77+ err = NULL;
 78+ OCIHandleFree(env, OCI_HTYPE_ENV);
 79+ env = NULL;
5580 }
5681
5782 std::string
5883 connection::error(void)
5984 {
60 - return err;
 85+ if (last_error == OCI_SUCCESS || last_error == OCI_SUCCESS_WITH_INFO)
 86+ return "no error";
 87+
 88+ char buf[1024];
 89+ sb4 errno;
 90+ OCIErrorGet(err, 1, NULL, &errno, (text *)buf, sizeof(buf), OCI_HTYPE_ERROR);
 91+ std::string ret = buf;
 92+ ret.resize(ret.size() - 1);
 93+ return ret;
6194 }
6295
63 -execution_result *
 96+db::resultptr
6497 connection::execute_sql(std::string const &sql)
6598 {
66 - ORAPP::Query *q = db.query(sql.c_str());
67 - if (!q->execute()) {
68 - throw db::error(db.error());
69 - }
 99+ db::resultptr r = prepare_sql(sql);
 100+ r->execute();
 101+ return r;
 102+}
70103
71 - return new execution_result(db, q);
 104+db::resultptr
 105+connection::prepare_sql(std::string const &sql)
 106+{
 107+ return db::resultptr(new result(this, sql));
72108 }
73109
74 -execution_result::execution_result(ORAPP::Connection &conn, ORAPP::Query *q)
75 - : conn(conn)
76 - , q(q)
77 - , row(0)
 110+result::result(connection *conn, std::string const &q)
 111+ : stmt(0)
 112+ , conn(conn)
78113 {
79 - ORAPP::Row *r;
80 - while (r = q->fetch()) {
81 - orarow nr;
82 - for (int i = 0; i < r->width(); ++i) {
83 - if (rows.empty())
84 - names.push_back(r->name(i));
85 - nr.content.push_back((std::string) (*r)[i]);
86 - }
 114+ if (!ora_success(OCIHandleAlloc(conn->env, (void **)&stmt, OCI_HTYPE_STMT, 0, NULL)))
 115+ throw db::error(conn->error());
87116
88 - rows.push_back(nr);
 117+ if (!ora_success(OCIStmtPrepare(stmt, conn->err, (text *)q.c_str(), q.size(), OCI_NTV_SYNTAX, OCI_DEFAULT)))
 118+ throw db::error(conn->error());
 119+
 120+ ub4 sz;
 121+
 122+ sz = sizeof(type);
 123+ if (!ora_success(OCIAttrGet(stmt, OCI_HTYPE_STMT, &type, &sz, OCI_ATTR_STMT_TYPE, conn->err)))
 124+ throw db::error(conn->error());
 125+}
 126+
 127+void
 128+result::execute(void)
 129+{
 130+ conn->last_error = OCIStmtExecute(conn->svc, stmt, conn->err,
 131+ (type == OCI_STMT_SELECT) ? 0 : 1,
 132+ 0, NULL, NULL, OCI_COMMIT_ON_SUCCESS);
 133+ if (!ora_success(conn->last_error))
 134+ throw db::error(conn->error());
 135+
 136+ if (type != OCI_STMT_SELECT)
 137+ return;
 138+
 139+ ub4 sz;
 140+
 141+ sz = sizeof(ncols);
 142+ if (!ora_success(OCIAttrGet(stmt, OCI_HTYPE_STMT, &ncols, &sz, OCI_ATTR_PARAM_COUNT, conn->err)))
 143+ throw db::error(conn->error());
 144+
 145+ OCIParam *p;
 146+ fields.resize(ncols);
 147+ for (int i = 0; i < ncols; ++i) {
 148+ if (!ora_success(OCIParamGet(stmt, OCI_HTYPE_STMT, conn->err, (void **)&p, i + 1)))
 149+ throw db::error(conn->error());
 150+
 151+ char *colname;
 152+ unsigned namelen;
 153+ if (!ora_success(OCIAttrGet(p, OCI_DTYPE_PARAM, &colname, &namelen, OCI_ATTR_NAME, conn->err)))
 154+ throw db::error(conn->error());
 155+
 156+ fields[i].name = colname;
 157+
 158+ ub2 width;
 159+ if (!ora_success(OCIAttrGet(p, OCI_DTYPE_PARAM, &width, 0, OCI_ATTR_DATA_SIZE, conn->err)))
 160+ throw db::error(conn->error());
 161+
 162+ fields[i].width = width;
 163+
 164+ fields[i].data.resize(width);
 165+ ub2 rcode, len;
 166+ if (!ora_success(OCIDefineByPos(stmt, &fields[i].define, conn->err, i + 1, &fields[i].data[0], width,
 167+ SQLT_STR, &fields[i].isnull, &len, &rcode, OCI_DEFAULT)))
 168+ throw db::error(conn->error());
89169 }
90170 }
91171
92 -execution_result::~execution_result()
 172+void
 173+result::bind(std::string const &key, std::string const &value)
93174 {
 175+ OCIBind *bind;
 176+ if (!ora_success(OCIBindByName(stmt, &bind, conn->err,
 177+ (text *)key.c_str(), -1, (void *)value.c_str(), value.size() + 1, SQLT_STR,
 178+ NULL, NULL, NULL, 0, NULL, OCI_DEFAULT)))
 179+ throw db::error(conn->error());
94180 }
95181
 182+result::~result()
 183+{
 184+}
 185+
96186 bool
97 -execution_result::has_data(void)
 187+result::empty(void)
98188 {
99 - return !rows.empty();
 189+ return type != OCI_STMT_SELECT;
100190 }
101191
102192 int
103 -execution_result::num_fields(void)
 193+result::num_fields(void)
104194 {
105 - return names.size();
 195+ return fields.size();
106196 }
107197
108198 int
109 -execution_result::affected_rows(void)
 199+result::affected_rows(void)
110200 {
111201 return 0; /* XXX */
112202 }
113203
114204 result_row *
115 -execution_result::next_row(void)
 205+result::next_row(void)
116206 {
117 - if (row == rows.size())
 207+ conn->last_error = OCIStmtFetch(stmt, conn->err, 1, OCI_FETCH_NEXT, OCI_DEFAULT);
 208+ if (conn->last_error == OCI_NO_DATA)
118209 return NULL;
 210+ if (!ora_success(conn->last_error))
 211+ throw db::error(conn->error());
119212
120 - result_row *ret = new result_row(this, row);
121 - ++row;
122 - return ret;
 213+ return new result_row(this);
123214 }
124215
125 -result_row::result_row(execution_result *er, int row)
126 - : row(row)
127 - , er(er)
 216+result_row::result_row(result *er)
 217+ : er(er)
128218 {
129219 }
130220
131221 std::string
132 -result_row::string_value(int col)
 222+result_row::string_value(int col)
133223 {
134 - return er->rows[row].content[col];
 224+ return std::string(&er->fields[col].data[0]);
135225
136226 }
137227
138228 std::string
139 -execution_result::field_name(int col)
 229+result::field_name(int col)
140230 {
141 - return names[col];
 231+ return fields[col].name;
142232 }
143233
144234 std::vector<db::table>
145235 connection::describe_tables(std::string const &schema)
146236 {
147 - ORAPP::Query *q;
 237+ std::string n = boost::algorithm::to_upper_copy(schema);
148238
 239+ db::resultptr r;
149240 if (schema.empty())
150 - q = db.query("SELECT owner, table_name FROM all_tables");
 241+ r = prepare_sql("SELECT owner, table_name FROM all_tables");
151242 else {
152 - q = db.query("SELECT owner, table_name FROM all_tables WHERE owner = :towner");
153 - std::string n = boost::algorithm::to_upper_copy(schema);
154 - q->bind(":towner", n.c_str());
 243+ r = prepare_sql("SELECT owner, table_name FROM all_tables WHERE owner = :towner");
 244+ r->bind(":towner", n);
155245 }
 246+ r->execute();
156247
157248 std::vector<std::pair<std::string, std::string> > names;
158249
159 - if (!q->execute()) {
160 - throw db::error(db.error());
161 - }
162 -
163 - ORAPP::Row *r;
164250 std::vector<db::table> ret;
165 - while (r = q->fetch()) {
 251+ result::iterator it = r->begin(), end = r->end();
 252+ for (; it != end; ++it) {
166253 names.push_back(std::pair<std::string, std::string>(
167 - (std::string) (*r)[0], (std::string) (*r)[1]));
 254+ it->string_value(0), it->string_value(1)));
168255 }
169256
170257 for (int i = 0; i < names.size(); ++i) {
@@ -180,28 +267,25 @@
181268 ret.name = name;
182269 ret.schema = schema;
183270
184 - ORAPP::Query *q = db.query(
 271+ db::resultptr r = prepare_sql(
185272 "SELECT column_name, data_type, nullable FROM all_tab_columns WHERE owner = :tabowner AND table_name = :name");
186273 std::string n = boost::algorithm::to_upper_copy(name);
187274 std::string o = boost::algorithm::to_upper_copy(schema);
188275
189 - q->bind(":name", n.c_str());
190 - q->bind(":tabowner", o.c_str());
 276+ r->bind(":name", n);
 277+ r->bind(":tabowner", o);
 278+ r->execute();
191279
192 - if (!q->execute()) {
193 - throw db::error(db.error());
194 - }
195 -
196 - ORAPP::Row *r;
197 - while (r = q->fetch()) {
 280+ result::iterator it = r->begin(), end = r->end();
 281+ for (; it != end; ++it) {
198282 db::column c;
199 - c.name = (std::string) (*r)[0];
200 - c.type = (std::string) (*r)[1];
201 - c.nullable = ((char) (*r)[2]) == 'Y';
 283+ c.name = it->string_value(0);
 284+ c.type = it->string_value(1);
 285+ c.nullable = it->string_value(2) == "Y";
202286 ret.columns.push_back(c);
203287 }
204288
205289 return ret;
206290 }
207291
208 -} // namespace pgsql
 292+} // namespace oracle
Index: trunk/skirmish/ora.h
@@ -4,44 +4,54 @@
55 #include <string>
66 #include <vector>
77
 8+#include <oci.h>
 9+
810 #include "db.h"
9 -#include "row.hh"
10 -#include "query.hh"
11 -#include "conn.hh"
1211
1312 namespace oracle {
1413
15 -struct execution_result;
 14+struct result;
 15+struct connection;
1616
1717 struct orarow {
1818 std::vector<std::string> content;
1919 };
2020
 21+struct orafield {
 22+ std::string name;
 23+ ub2 width;
 24+ unsigned isnull;
 25+ std::vector<char> data;
 26+ OCIDefine *define;
 27+};
 28+
2129 struct result_row : db::result_row {
22 - result_row(execution_result *er, int);
 30+ result_row(result *er);
2331
2432 std::string string_value(int col);
2533
26 - int row;
27 - execution_result *er;
 34+ result *er;
2835 };
2936
30 -struct execution_result : db::execution_result {
31 - execution_result(ORAPP::Connection &, ORAPP::Query *);
32 - ~execution_result();
 37+struct result : db::result {
 38+ result(connection *, std::string const &);
 39+ ~result();
3340
34 - bool has_data(void);
 41+ void bind(std::string const &, std::string const &);
 42+ void execute(void);
 43+
 44+ bool empty(void);
3545 int num_fields(void);
3646 int affected_rows(void);
3747 std::string field_name(int col);
3848
3949 result_row *next_row(void);
4050
41 - ORAPP::Connection &conn;
42 - ORAPP::Query *q;
43 - std::vector<std::string> names;
44 - std::vector<orarow> rows;
45 - int row;
 51+ std::vector<orafield> fields;
 52+ connection *conn;
 53+ OCIStmt *stmt;
 54+ ub2 type;
 55+ ub4 ncols;
4656 };
4757
4858 struct connection : db::connection {
@@ -53,15 +63,21 @@
5464
5565 std::string error(void);
5666
57 - execution_result *execute_sql(std::string const &);
 67+ db::resultptr execute_sql(std::string const &);
 68+ db::resultptr prepare_sql(std::string const &);
 69+
5870 std::vector<db::table> describe_tables(std::string const &);
5971 db::table describe_table(std::string const &, std::string const &);
6072
6173 private:
62 - ORAPP::Connection db;
 74+ friend class result;
6375
64 - std::string err;
 76+ OCIEnv *env;
 77+ OCIError *err;
 78+ OCISvcCtx *svc;
 79+
6580 std::string sid, user, password;
 81+ unsigned last_error;
6682 };
6783
6884 } // namespace oracle
Index: trunk/skirmish/db.cc
@@ -1,5 +1,8 @@
22 #include <string>
 3+#include <map>
34 #include <boost/format.hpp>
 5+#include <boost/function.hpp>
 6+#include <boost/assign/list_of.hpp>
47
58 #include "db.h"
69
@@ -25,7 +28,16 @@
2629 {
2730 }
2831
 32+namespace {
 33+template<typename T>
2934 connectionptr
 35+construct(std::string const &desc)
 36+{
 37+ return connectionptr(new T(desc));
 38+}
 39+}
 40+
 41+connectionptr
3042 connection::create(std::string const &desc)
3143 {
3244 std::string type;
@@ -35,25 +47,22 @@
3648 throw db::error("invalid scheme in description");
3749
3850 type = desc.substr(0, i);
39 -#ifdef SKIRMISH_MYSQL
40 - if (type == "mysql")
41 - return connectionptr(new mysql::connection(desc));
42 - else
43 -#endif
44 -#ifdef SKIRMISH_POSTGRES
45 - if (type == "postgres")
46 - return connectionptr(new postgres::connection(desc));
47 - else
48 -#endif
49 -#ifdef SKIRMISH_ORACLE
50 - if (type == "oracle")
51 - return connectionptr(new oracle::connection(desc));
52 - else
53 -#endif
 51+
 52+ typedef std::map<std::string, boost::function<connectionptr (std::string const &)> > schemelist_t;
 53+
 54+ static schemelist_t schemes = boost::assign::map_list_of
 55+ ("mysql", construct<mysql::connection>)
 56+ ("postgres", construct<postgres::connection>)
 57+ ("oracle", construct<oracle::connection>)
 58+ ;
 59+
 60+ schemelist_t::iterator it = schemes.find(type);
 61+ if (it == schemes.end())
5462 throw db::error(str(boost::format("unknown scheme \"%s\" in description") % desc));
 63+ return it->second(desc);
5564 }
5665
57 -execution_result::~execution_result()
 66+result::~result()
5867 {
5968 }
6069
@@ -61,4 +70,79 @@
6271 {
6372 }
6473
 74+resultset_iterator::resultset_iterator()
 75+ : isend(true)
 76+{
 77+}
 78+
 79+resultset_iterator::resultset_iterator(result *er)
 80+ : er(er)
 81+ , isend(false)
 82+{
 83+ fetch();
 84+}
 85+
 86+void
 87+resultset_iterator::fetch(void)
 88+{
 89+ row.reset(er->next_row());
 90+ if (!row)
 91+ isend = true;
 92+}
 93+
 94+resultset_iterator &
 95+resultset_iterator::operator++(void)
 96+{
 97+ if (isend)
 98+ throw db::error("resultset_iterator incremented past end");
 99+ fetch();
 100+ return *this;
 101+}
 102+
 103+resultset_iterator
 104+resultset_iterator::operator++(int)
 105+{
 106+ resultset_iterator n(*this);
 107+ return ++n;
 108+}
 109+
 110+resultset_iterator::resultset_iterator(resultset_iterator const &other)
 111+ : er(other.er)
 112+ , isend(other.isend)
 113+ , row(other.row)
 114+{
 115+}
 116+
 117+bool
 118+resultset_iterator::operator==(resultset_iterator const &other)
 119+{
 120+ if (isend && other.isend)
 121+ return true;
 122+ return false;
 123+}
 124+
 125+bool
 126+resultset_iterator::operator!=(resultset_iterator const &other)
 127+{
 128+ return !(*this == other);
 129+}
 130+
 131+result_row *
 132+resultset_iterator::operator->(void)
 133+{
 134+ return row.get();
 135+}
 136+
 137+resultset_iterator
 138+result::begin(void)
 139+{
 140+ return resultset_iterator(this);
 141+}
 142+
 143+resultset_iterator
 144+result::end(void)
 145+{
 146+ return resultset_iterator();
 147+}
 148+
65149 } // namespace db
Index: trunk/skirmish/mysql.cc
@@ -76,21 +76,33 @@
7777 return err;
7878 }
7979
80 -execution_result *
 80+db::resultptr
8181 connection::execute_sql(std::string const &sql)
8282 {
83 - assert(conn);
84 - if (mysql_real_query(conn, sql.data(), sql.size()) != 0)
85 - throw db::error(mysql_error(conn));
 83+ db::resultptr r = prepare_sql(sql);
 84+ r->execute();
 85+ return r;
 86+}
8687
87 - return new execution_result(conn);
 88+db::resultptr
 89+connection::prepare_sql(std::string const &sql)
 90+{
 91+ return db::resultptr(new result(conn, sql));
8892 }
8993
90 -execution_result::execution_result(MYSQL *c)
91 - : conn(c)
92 - , res(0)
 94+void
 95+result::bind(std::string const &, std::string const &)
9396 {
94 - if (!mysql_field_count(c))
 97+ throw db::error("prepared statements not supported for MySQL");
 98+}
 99+
 100+void
 101+result::execute(void)
 102+{
 103+ if (mysql_real_query(conn, sql.data(), sql.size()) != 0)
 104+ throw db::error(mysql_error(conn));
 105+
 106+ if (!mysql_field_count(conn))
95107 return;
96108
97109 if ((res = mysql_use_result(conn)) == NULL)
@@ -102,32 +114,39 @@
103115 }
104116 }
105117
106 -execution_result::~execution_result()
 118+result::result(MYSQL *c, std::string const &sql)
 119+ : conn(c)
 120+ , res(0)
 121+ , sql(sql)
107122 {
 123+}
 124+
 125+result::~result()
 126+{
108127 if (res)
109128 mysql_free_result(res);
110129 }
111130
112131 bool
113 -execution_result::has_data(void)
 132+result::empty(void)
114133 {
115 - return mysql_field_count(conn);
 134+ return mysql_field_count(conn) == 0;
116135 }
117136
118137 int
119 -execution_result::num_fields(void)
 138+result::num_fields(void)
120139 {
121140 return mysql_field_count(conn);
122141 }
123142
124143 int
125 -execution_result::affected_rows(void)
 144+result::affected_rows(void)
126145 {
127146 return mysql_affected_rows(conn);
128147 }
129148
130149 result_row *
131 -execution_result::next_row(void)
 150+result::next_row(void)
132151 {
133152 MYSQL_ROW r;
134153
@@ -137,14 +156,14 @@
138157 return new result_row(this, r);
139158 }
140159
141 -result_row::result_row(execution_result *er, MYSQL_ROW row)
 160+result_row::result_row(result *er, MYSQL_ROW row)
142161 : row(row)
143162 , er(er)
144163 {
145164 }
146165
147166 std::string
148 -result_row::string_value(int col)
 167+result_row::string_value(int col)
149168 {
150169 if (row[col])
151170 return row[col];
@@ -153,7 +172,7 @@
154173 }
155174
156175 std::string
157 -execution_result::field_name(int col)
 176+result::field_name(int col)
158177 {
159178 return names[col];
160179 }
Index: trunk/skirmish/pgsql.cc
@@ -82,62 +82,81 @@
8383 return err;
8484 }
8585
86 -execution_result *
 86+db::resultptr
8787 connection::execute_sql(std::string const &sql)
8888 {
8989 assert(conn);
 90+ db::resultptr r = prepare_sql(sql);
 91+ r->execute();
 92+ return r;
 93+}
 94+
 95+db::resultptr
 96+connection::prepare_sql(std::string const &sql)
 97+{
 98+ return db::resultptr(new result(conn, sql));
 99+}
 100+
 101+result::result(PGconn *c, std::string const &sql)
 102+ : conn(c)
 103+ , row(0)
 104+ , sql(sql)
 105+{
 106+}
 107+
 108+void
 109+result::execute(void)
 110+{
90111 PGresult *res;
91112 if ((res = PQexec(conn, sql.c_str())) == NULL)
92113 throw db::error(PQerrorMessage(conn));
93114
94115 switch (PQresultStatus(res)) {
95116 case PGRES_COMMAND_OK:
 117+ return;
96118 case PGRES_TUPLES_OK:
97 - return new execution_result(conn, res);
 119+ break;
98120 default:
99121 throw db::error(PQresultErrorMessage(res));
100122 }
101 -}
102123
103 -execution_result::execution_result(PGconn *c, PGresult *res)
104 - : conn(c)
105 - , res(res)
106 - , row(0)
107 -{
108 - if (PQresultStatus(res) == PGRES_COMMAND_OK)
109 - return;
110 -
111124 int nfields = PQnfields(res);
112125 for (int i = 0; i < nfields; ++i)
113126 names.push_back(PQfname(res, i));
114127 }
115128
116 -execution_result::~execution_result()
 129+void
 130+result::bind(std::string const &, std::string const &)
117131 {
 132+ throw db::error("bound variables not supported for PostgreSQL");
 133+}
 134+
 135+result::~result()
 136+{
118137 if (res)
119138 PQclear(res);
120139 }
121140
122141 bool
123 -execution_result::has_data(void)
 142+result::empty(void)
124143 {
125 - return PQresultStatus(res) != PGRES_COMMAND_OK;
 144+ return PQresultStatus(res) != PGRES_TUPLES_OK;
126145 }
127146
128147 int
129 -execution_result::num_fields(void)
 148+result::num_fields(void)
130149 {
131150 return PQnfields(res);
132151 }
133152
134153 int
135 -execution_result::affected_rows(void)
 154+result::affected_rows(void)
136155 {
137156 return boost::lexical_cast<int>(PQcmdTuples(res));
138157 }
139158
140159 result_row *
141 -execution_result::next_row(void)
 160+result::next_row(void)
142161 {
143162 if (row == PQntuples(res))
144163 return NULL;
@@ -145,7 +164,7 @@
146165 return new result_row(this, res, row++);
147166 }
148167
149 -result_row::result_row(execution_result *er, PGresult *res, int row)
 168+result_row::result_row(result *er, PGresult *res, int row)
150169 : row(row)
151170 , res(res)
152171 , er(er)
@@ -162,7 +181,7 @@
163182 }
164183
165184 std::string
166 -execution_result::field_name(int col)
 185+result::field_name(int col)
167186 {
168187 return names[col];
169188 }
Index: trunk/skirmish/db.h
@@ -8,6 +8,30 @@
99
1010 namespace db {
1111
 12+struct result;
 13+struct result_row;
 14+
 15+struct resultset_iterator {
 16+ result_row &operator *(void);
 17+ result_row *operator ->(void);
 18+
 19+ resultset_iterator &operator++(void);
 20+ resultset_iterator operator++(int);
 21+
 22+ bool operator== (resultset_iterator const &);
 23+ bool operator!= (resultset_iterator const &);
 24+
 25+ resultset_iterator();
 26+ resultset_iterator(result *);
 27+ resultset_iterator(resultset_iterator const &other);
 28+
 29+private:
 30+ result *er;
 31+ bool isend;
 32+ boost::shared_ptr<result_row> row;
 33+ void fetch(void);
 34+};
 35+
1236 struct error : std::exception {
1337 std::string err;
1438 error(std::string const &err) : err(err) {}
@@ -20,16 +44,28 @@
2145 virtual std::string string_value(int col) = 0;
2246 };
2347
24 -struct execution_result : boost::noncopyable {
25 - virtual ~execution_result();
 48+struct result : boost::noncopyable {
 49+ typedef resultset_iterator iterator;
 50+ typedef resultset_iterator const_iterator;
2651
27 - virtual bool has_data(void) = 0;
 52+ virtual void bind(std::string const &, std::string const &) = 0;
 53+ virtual void execute() = 0;
 54+
 55+ iterator begin();
 56+ iterator end();
 57+
 58+ virtual ~result();
 59+
 60+ virtual bool empty(void) = 0;
2861 virtual int affected_rows(void) = 0;
2962 virtual int num_fields(void) = 0;
3063 virtual std::string field_name(int col) = 0;
3164
 65+protected:
 66+ friend class resultset_iterator;
3267 virtual result_row *next_row(void) = 0;
3368 };
 69+typedef boost::shared_ptr<result> resultptr;
3470
3571 struct column {
3672 std::string name;
@@ -53,7 +89,7 @@
5490
5591 virtual ~connection();
5692
57 - virtual execution_result *execute_sql(std::string const &) = 0;
 93+ virtual resultptr execute_sql(std::string const &) = 0;
5894 virtual std::vector<table> describe_tables(std::string const & = "") = 0;
5995 virtual table describe_table(std::string const &, std::string const &) = 0;
6096
Index: trunk/skirmish/pgsql.h
@@ -10,33 +10,40 @@
1111
1212 namespace postgres {
1313
14 -struct execution_result;
 14+struct result;
1515
1616 struct result_row : db::result_row {
17 - result_row(execution_result *er, PGresult *, int);
 17+ result_row(result *er, PGresult *, int);
1818
1919 std::string string_value(int col);
2020
 21+private:
2122 int row;
2223 PGresult *res;
23 - execution_result *er;
 24+ result *er;
2425 };
2526
26 -struct execution_result : db::execution_result {
27 - execution_result(PGconn*, PGresult *);
28 - ~execution_result();
 27+struct result : db::result {
 28+ result(PGconn*, std::string const &);
 29+ ~result();
2930
30 - bool has_data(void);
 31+ void bind(std::string const &, std::string const &);
 32+ void execute(void);
 33+
 34+ bool empty(void);
3135 int num_fields(void);
3236 int affected_rows(void);
3337 std::string field_name(int col);
3438
 39+protected:
3540 result_row *next_row(void);
3641
 42+private:
3743 PGconn *conn;
3844 PGresult *res;
3945 std::vector<std::string> names;
4046 int row;
 47+ std::string sql;
4148 };
4249
4350 struct connection : db::connection {
@@ -48,7 +55,9 @@
4956
5057 std::string error(void);
5158
52 - execution_result *execute_sql(std::string const &);
 59+ db::resultptr execute_sql(std::string const &);
 60+ db::resultptr prepare_sql(std::string const &);
 61+
5362 std::vector<db::table> describe_tables(std::string const &);
5463 db::table describe_table(std::string const &, std::string const &);
5564
Index: trunk/skirmish/Makefile
@@ -1,3 +1,4 @@
 2+include config.mk
23 include rules.mk
34
45 SRCS = \
@@ -9,28 +10,19 @@
1011 OBJS = $(SRCS:.cc=.o)
1112
1213 all:
13 - for d in $(SUBDIRS); do \
14 - $(MAKE) -I.. -C $$d all ;\
15 - done
1614 $(MAKE) skirmish
1715
1816 skirmish: $(OBJS)
1917 $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) -o $@ $(LIBS)
2018
2119 clean:
22 - for d in $(SUBDIRS); do \
23 - $(MAKE) -I.. -C $$d clean ;\
24 - done
2520 rm -f $(OBJS) skirmish
2621
2722 distclean: clean
28 - for d in $(SUBDIRS); do \
29 - $(MAKE) -I.. -C $$d distclean ;\
30 - done
3123
32 -linereader.o: linereader.cc linereader.h
33 -skirmish.o: skirmish.cc linereader.h db.h
34 -db.o: db.cc db.h mysqldb.h pgsql.h
35 -mysql.o: mysql.cc mysqldb.h
36 -pgsql.o: pgsql.cc pgsql.h
37 -ora.o: ora.cc ora.h
 24+depend:
 25+ @rm -f .depends
 26+ @echo making dependencies...
 27+ @$(CXX) -M -MG $(CXXFLAGS) $(INCLUDES) $(SRCS) > .depends
 28+
 29+-include .depends
Index: trunk/skirmish/skirmish.cc
@@ -99,7 +99,7 @@
100100
101101 conn = conns[cnr]->conn;
102102
103 - db::execution_result *res;
 103+ db::resultptr res;
104104 int nrows = 0;
105105
106106 if (input[input.size() - 1] == ';')
@@ -108,11 +108,11 @@
109109 try {
110110 res = conn->execute_sql(input);
111111 } catch (db::error &e) {
112 - std::cerr << boost::format("Error: %s\n") % e.what();
 112+ std::cerr << boost::format("[%s]\n") % e.what();
113113 continue;
114114 }
115115
116 - if (res->has_data()) {
 116+ if (!res->empty()) {
117117 db::result_row *r;
118118 int ncols = res->num_fields();
119119 std::vector<int> sizes(ncols);
@@ -123,14 +123,14 @@
124124 names.push_back(res->field_name(i));
125125 data.push_back(names);
126126
127 - while (r = res->next_row()) {
 127+ db::result::iterator it, end;
 128+ for (it = res->begin(), end = res->end(); it != end; ++it) {
128129 std::vector<std::string> thisrow;
129130 for (int i = 0; i < ncols; ++i) {
130 - std::string v = r->string_value(i);
 131+ std::string v = it->string_value(i);
131132 thisrow.push_back(v);
132133 }
133134 data.push_back(thisrow);
134 - delete r;
135135 ++nrows;
136136 }
137137
@@ -164,7 +164,6 @@
165165
166166 if (nrows)
167167 std::cout << boost::format("\nOK (%d rows).\n") % nrows;
168 - delete res;
169168 }
170169 }
171170
@@ -183,7 +182,7 @@
184183
185184 std::map<std::string, boost::function<void (std::string const &)> >::iterator it;
186185 if ((it = commands.find(command)) == commands.end()) {
187 - std::cerr << boost::format("Error: unknown command \"%s\"\n") % command;
 186+ std::cerr << boost::format("[unknown command \"%s\"]\n") % command;
188187 return;
189188 }
190189
@@ -207,12 +206,12 @@
208207 try {
209208 to = boost::lexical_cast<int>(arg);
210209 } catch (boost::bad_lexical_cast &) {
211 - std::cerr << boost::format("Error: invalid connection number \"%s\".\n") % arg;
 210+ std::cerr << boost::format("[invalid connection number \"%s\"]\n") % arg;
212211 return;
213212 }
214213
215214 if (to > conns.size() - 1 || to < 0 || !conns[to]) {
216 - std::cerr << boost::format("Error: no connection %d\n") % to;
 215+ std::cerr << boost::format("[no connection %d]\n") % to;
217216 return;
218217 }
219218
@@ -229,17 +228,17 @@
230229 try {
231230 to = boost::lexical_cast<int>(arg);
232231 } catch (boost::bad_lexical_cast &) {
233 - std::cerr << boost::format("Error: invalid connection number \"%s\".\n") % arg;
 232+ std::cerr << boost::format("[invalid connection number \"%s\"]\n") % arg;
234233 return;
235234 }
236235
237236 if (to > conns.size() - 1 || to < 0 || !conns[to]) {
238 - std::cerr << boost::format("Error: no connection %d\n") % to;
 237+ std::cerr << boost::format("[no connection %d]\n") % to;
239238 return;
240239 }
241240 } else {
242241 if (cnr == -1) {
243 - std::cout << "Error: no connection.\n";
 242+ std::cout << "[no connection to close]\n";
244243 return;
245244 }
246245 to = cnr;
@@ -279,7 +278,7 @@
280279 try {
281280 tables = conns[cnr]->conn->describe_tables(arg);
282281 } catch (db::error const &e) {
283 - std::cout << boost::format("[error: %s]\n") % e.what();
 282+ std::cout << boost::format("[%s]\n") % e.what();
284283 return;
285284 }
286285
@@ -325,7 +324,7 @@
326325
327326 t = conns[cnr]->conn->describe_table(schema, table);
328327 } catch (db::error const &e) {
329 - std::cout << boost::format("[error: %s]\n") % e.what();
 328+ std::cout << boost::format("[%s]\n") % e.what();
330329 return;
331330 }
332331