Index: trunk/skirmish/rules.mk |
— | — | @@ -1,5 +1,3 @@ |
2 | | -include config.mk |
3 | | - |
4 | 2 | ifeq ($(BUILD_MYSQL),YES) |
5 | 3 | INCLUDES += $(shell mysql_config --include) |
6 | 4 | LIBS += $(shell mysql_config --libs) |
— | — | @@ -8,8 +6,8 @@ |
9 | 7 | endif |
10 | 8 | |
11 | 9 | 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 |
14 | 12 | SUBDIRS += orapp |
15 | 13 | CPPFLAGS += -DSKIRMISH_ORACLE |
16 | 14 | DB_SRCS += ora.cc |
Index: trunk/skirmish/mysqldb.h |
— | — | @@ -10,22 +10,25 @@ |
11 | 11 | |
12 | 12 | namespace mysql { |
13 | 13 | |
14 | | -struct execution_result; |
| 14 | +struct result; |
15 | 15 | |
16 | 16 | struct result_row : db::result_row { |
17 | | - result_row(execution_result *er, MYSQL_ROW row); |
| 17 | + result_row(result *er, MYSQL_ROW row); |
18 | 18 | |
19 | 19 | std::string string_value(int col); |
20 | 20 | |
21 | 21 | MYSQL_ROW row; |
22 | | - execution_result *er; |
| 22 | + result *er; |
23 | 23 | }; |
24 | 24 | |
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(); |
28 | 28 | |
29 | | - bool has_data(void); |
| 29 | + void bind(std::string const &, std::string const &); |
| 30 | + void execute(); |
| 31 | + |
| 32 | + bool empty(void); |
30 | 33 | int num_fields(void); |
31 | 34 | int affected_rows(void); |
32 | 35 | std::string field_name(int col); |
— | — | @@ -35,6 +38,7 @@ |
36 | 39 | MYSQL *conn; |
37 | 40 | MYSQL_RES *res; |
38 | 41 | std::vector<std::string> names; |
| 42 | + std::string sql; |
39 | 43 | }; |
40 | 44 | |
41 | 45 | struct connection : db::connection { |
— | — | @@ -46,7 +50,8 @@ |
47 | 51 | |
48 | 52 | std::string error(void); |
49 | 53 | |
50 | | - execution_result *execute_sql(std::string const &); |
| 54 | + db::resultptr execute_sql(std::string const &); |
| 55 | + db::resultptr prepare_sql(std::string const &); |
51 | 56 | std::vector<db::table> describe_tables(std::string const &); |
52 | 57 | db::table describe_table(std::string const &, std::string const &); |
53 | 58 | |
Index: trunk/skirmish/ora.cc |
— | — | @@ -7,6 +7,16 @@ |
8 | 8 | |
9 | 9 | #include "ora.h" |
10 | 10 | |
| 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 | + |
11 | 21 | namespace oracle { |
12 | 22 | |
13 | 23 | connection::connection(std::string const &desc) |
— | — | @@ -43,127 +53,204 @@ |
44 | 54 | void |
45 | 55 | connection::open(void) |
46 | 56 | { |
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()); |
49 | 69 | } |
50 | 70 | |
51 | 71 | void |
52 | 72 | connection::close(void) |
53 | 73 | { |
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; |
55 | 80 | } |
56 | 81 | |
57 | 82 | std::string |
58 | 83 | connection::error(void) |
59 | 84 | { |
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; |
61 | 94 | } |
62 | 95 | |
63 | | -execution_result * |
| 96 | +db::resultptr |
64 | 97 | connection::execute_sql(std::string const &sql) |
65 | 98 | { |
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 | +} |
70 | 103 | |
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)); |
72 | 108 | } |
73 | 109 | |
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) |
78 | 113 | { |
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()); |
87 | 116 | |
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()); |
89 | 169 | } |
90 | 170 | } |
91 | 171 | |
92 | | -execution_result::~execution_result() |
| 172 | +void |
| 173 | +result::bind(std::string const &key, std::string const &value) |
93 | 174 | { |
| 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()); |
94 | 180 | } |
95 | 181 | |
| 182 | +result::~result() |
| 183 | +{ |
| 184 | +} |
| 185 | + |
96 | 186 | bool |
97 | | -execution_result::has_data(void) |
| 187 | +result::empty(void) |
98 | 188 | { |
99 | | - return !rows.empty(); |
| 189 | + return type != OCI_STMT_SELECT; |
100 | 190 | } |
101 | 191 | |
102 | 192 | int |
103 | | -execution_result::num_fields(void) |
| 193 | +result::num_fields(void) |
104 | 194 | { |
105 | | - return names.size(); |
| 195 | + return fields.size(); |
106 | 196 | } |
107 | 197 | |
108 | 198 | int |
109 | | -execution_result::affected_rows(void) |
| 199 | +result::affected_rows(void) |
110 | 200 | { |
111 | 201 | return 0; /* XXX */ |
112 | 202 | } |
113 | 203 | |
114 | 204 | result_row * |
115 | | -execution_result::next_row(void) |
| 205 | +result::next_row(void) |
116 | 206 | { |
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) |
118 | 209 | return NULL; |
| 210 | + if (!ora_success(conn->last_error)) |
| 211 | + throw db::error(conn->error()); |
119 | 212 | |
120 | | - result_row *ret = new result_row(this, row); |
121 | | - ++row; |
122 | | - return ret; |
| 213 | + return new result_row(this); |
123 | 214 | } |
124 | 215 | |
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) |
128 | 218 | { |
129 | 219 | } |
130 | 220 | |
131 | 221 | std::string |
132 | | -result_row::string_value(int col) |
| 222 | +result_row::string_value(int col) |
133 | 223 | { |
134 | | - return er->rows[row].content[col]; |
| 224 | + return std::string(&er->fields[col].data[0]); |
135 | 225 | |
136 | 226 | } |
137 | 227 | |
138 | 228 | std::string |
139 | | -execution_result::field_name(int col) |
| 229 | +result::field_name(int col) |
140 | 230 | { |
141 | | - return names[col]; |
| 231 | + return fields[col].name; |
142 | 232 | } |
143 | 233 | |
144 | 234 | std::vector<db::table> |
145 | 235 | connection::describe_tables(std::string const &schema) |
146 | 236 | { |
147 | | - ORAPP::Query *q; |
| 237 | + std::string n = boost::algorithm::to_upper_copy(schema); |
148 | 238 | |
| 239 | + db::resultptr r; |
149 | 240 | 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"); |
151 | 242 | 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); |
155 | 245 | } |
| 246 | + r->execute(); |
156 | 247 | |
157 | 248 | std::vector<std::pair<std::string, std::string> > names; |
158 | 249 | |
159 | | - if (!q->execute()) { |
160 | | - throw db::error(db.error()); |
161 | | - } |
162 | | - |
163 | | - ORAPP::Row *r; |
164 | 250 | std::vector<db::table> ret; |
165 | | - while (r = q->fetch()) { |
| 251 | + result::iterator it = r->begin(), end = r->end(); |
| 252 | + for (; it != end; ++it) { |
166 | 253 | 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))); |
168 | 255 | } |
169 | 256 | |
170 | 257 | for (int i = 0; i < names.size(); ++i) { |
— | — | @@ -180,28 +267,25 @@ |
181 | 268 | ret.name = name; |
182 | 269 | ret.schema = schema; |
183 | 270 | |
184 | | - ORAPP::Query *q = db.query( |
| 271 | + db::resultptr r = prepare_sql( |
185 | 272 | "SELECT column_name, data_type, nullable FROM all_tab_columns WHERE owner = :tabowner AND table_name = :name"); |
186 | 273 | std::string n = boost::algorithm::to_upper_copy(name); |
187 | 274 | std::string o = boost::algorithm::to_upper_copy(schema); |
188 | 275 | |
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(); |
191 | 279 | |
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) { |
198 | 282 | 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"; |
202 | 286 | ret.columns.push_back(c); |
203 | 287 | } |
204 | 288 | |
205 | 289 | return ret; |
206 | 290 | } |
207 | 291 | |
208 | | -} // namespace pgsql |
| 292 | +} // namespace oracle |
Index: trunk/skirmish/ora.h |
— | — | @@ -4,44 +4,54 @@ |
5 | 5 | #include <string> |
6 | 6 | #include <vector> |
7 | 7 | |
| 8 | +#include <oci.h> |
| 9 | + |
8 | 10 | #include "db.h" |
9 | | -#include "row.hh" |
10 | | -#include "query.hh" |
11 | | -#include "conn.hh" |
12 | 11 | |
13 | 12 | namespace oracle { |
14 | 13 | |
15 | | -struct execution_result; |
| 14 | +struct result; |
| 15 | +struct connection; |
16 | 16 | |
17 | 17 | struct orarow { |
18 | 18 | std::vector<std::string> content; |
19 | 19 | }; |
20 | 20 | |
| 21 | +struct orafield { |
| 22 | + std::string name; |
| 23 | + ub2 width; |
| 24 | + unsigned isnull; |
| 25 | + std::vector<char> data; |
| 26 | + OCIDefine *define; |
| 27 | +}; |
| 28 | + |
21 | 29 | struct result_row : db::result_row { |
22 | | - result_row(execution_result *er, int); |
| 30 | + result_row(result *er); |
23 | 31 | |
24 | 32 | std::string string_value(int col); |
25 | 33 | |
26 | | - int row; |
27 | | - execution_result *er; |
| 34 | + result *er; |
28 | 35 | }; |
29 | 36 | |
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(); |
33 | 40 | |
34 | | - bool has_data(void); |
| 41 | + void bind(std::string const &, std::string const &); |
| 42 | + void execute(void); |
| 43 | + |
| 44 | + bool empty(void); |
35 | 45 | int num_fields(void); |
36 | 46 | int affected_rows(void); |
37 | 47 | std::string field_name(int col); |
38 | 48 | |
39 | 49 | result_row *next_row(void); |
40 | 50 | |
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; |
46 | 56 | }; |
47 | 57 | |
48 | 58 | struct connection : db::connection { |
— | — | @@ -53,15 +63,21 @@ |
54 | 64 | |
55 | 65 | std::string error(void); |
56 | 66 | |
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 | + |
58 | 70 | std::vector<db::table> describe_tables(std::string const &); |
59 | 71 | db::table describe_table(std::string const &, std::string const &); |
60 | 72 | |
61 | 73 | private: |
62 | | - ORAPP::Connection db; |
| 74 | + friend class result; |
63 | 75 | |
64 | | - std::string err; |
| 76 | + OCIEnv *env; |
| 77 | + OCIError *err; |
| 78 | + OCISvcCtx *svc; |
| 79 | + |
65 | 80 | std::string sid, user, password; |
| 81 | + unsigned last_error; |
66 | 82 | }; |
67 | 83 | |
68 | 84 | } // namespace oracle |
Index: trunk/skirmish/db.cc |
— | — | @@ -1,5 +1,8 @@ |
2 | 2 | #include <string> |
| 3 | +#include <map> |
3 | 4 | #include <boost/format.hpp> |
| 5 | +#include <boost/function.hpp> |
| 6 | +#include <boost/assign/list_of.hpp> |
4 | 7 | |
5 | 8 | #include "db.h" |
6 | 9 | |
— | — | @@ -25,7 +28,16 @@ |
26 | 29 | { |
27 | 30 | } |
28 | 31 | |
| 32 | +namespace { |
| 33 | +template<typename T> |
29 | 34 | connectionptr |
| 35 | +construct(std::string const &desc) |
| 36 | +{ |
| 37 | + return connectionptr(new T(desc)); |
| 38 | +} |
| 39 | +} |
| 40 | + |
| 41 | +connectionptr |
30 | 42 | connection::create(std::string const &desc) |
31 | 43 | { |
32 | 44 | std::string type; |
— | — | @@ -35,25 +47,22 @@ |
36 | 48 | throw db::error("invalid scheme in description"); |
37 | 49 | |
38 | 50 | 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()) |
54 | 62 | throw db::error(str(boost::format("unknown scheme \"%s\" in description") % desc)); |
| 63 | + return it->second(desc); |
55 | 64 | } |
56 | 65 | |
57 | | -execution_result::~execution_result() |
| 66 | +result::~result() |
58 | 67 | { |
59 | 68 | } |
60 | 69 | |
— | — | @@ -61,4 +70,79 @@ |
62 | 71 | { |
63 | 72 | } |
64 | 73 | |
| 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 | + |
65 | 149 | } // namespace db |
Index: trunk/skirmish/mysql.cc |
— | — | @@ -76,21 +76,33 @@ |
77 | 77 | return err; |
78 | 78 | } |
79 | 79 | |
80 | | -execution_result * |
| 80 | +db::resultptr |
81 | 81 | connection::execute_sql(std::string const &sql) |
82 | 82 | { |
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 | +} |
86 | 87 | |
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)); |
88 | 92 | } |
89 | 93 | |
90 | | -execution_result::execution_result(MYSQL *c) |
91 | | - : conn(c) |
92 | | - , res(0) |
| 94 | +void |
| 95 | +result::bind(std::string const &, std::string const &) |
93 | 96 | { |
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)) |
95 | 107 | return; |
96 | 108 | |
97 | 109 | if ((res = mysql_use_result(conn)) == NULL) |
— | — | @@ -102,32 +114,39 @@ |
103 | 115 | } |
104 | 116 | } |
105 | 117 | |
106 | | -execution_result::~execution_result() |
| 118 | +result::result(MYSQL *c, std::string const &sql) |
| 119 | + : conn(c) |
| 120 | + , res(0) |
| 121 | + , sql(sql) |
107 | 122 | { |
| 123 | +} |
| 124 | + |
| 125 | +result::~result() |
| 126 | +{ |
108 | 127 | if (res) |
109 | 128 | mysql_free_result(res); |
110 | 129 | } |
111 | 130 | |
112 | 131 | bool |
113 | | -execution_result::has_data(void) |
| 132 | +result::empty(void) |
114 | 133 | { |
115 | | - return mysql_field_count(conn); |
| 134 | + return mysql_field_count(conn) == 0; |
116 | 135 | } |
117 | 136 | |
118 | 137 | int |
119 | | -execution_result::num_fields(void) |
| 138 | +result::num_fields(void) |
120 | 139 | { |
121 | 140 | return mysql_field_count(conn); |
122 | 141 | } |
123 | 142 | |
124 | 143 | int |
125 | | -execution_result::affected_rows(void) |
| 144 | +result::affected_rows(void) |
126 | 145 | { |
127 | 146 | return mysql_affected_rows(conn); |
128 | 147 | } |
129 | 148 | |
130 | 149 | result_row * |
131 | | -execution_result::next_row(void) |
| 150 | +result::next_row(void) |
132 | 151 | { |
133 | 152 | MYSQL_ROW r; |
134 | 153 | |
— | — | @@ -137,14 +156,14 @@ |
138 | 157 | return new result_row(this, r); |
139 | 158 | } |
140 | 159 | |
141 | | -result_row::result_row(execution_result *er, MYSQL_ROW row) |
| 160 | +result_row::result_row(result *er, MYSQL_ROW row) |
142 | 161 | : row(row) |
143 | 162 | , er(er) |
144 | 163 | { |
145 | 164 | } |
146 | 165 | |
147 | 166 | std::string |
148 | | -result_row::string_value(int col) |
| 167 | +result_row::string_value(int col) |
149 | 168 | { |
150 | 169 | if (row[col]) |
151 | 170 | return row[col]; |
— | — | @@ -153,7 +172,7 @@ |
154 | 173 | } |
155 | 174 | |
156 | 175 | std::string |
157 | | -execution_result::field_name(int col) |
| 176 | +result::field_name(int col) |
158 | 177 | { |
159 | 178 | return names[col]; |
160 | 179 | } |
Index: trunk/skirmish/pgsql.cc |
— | — | @@ -82,62 +82,81 @@ |
83 | 83 | return err; |
84 | 84 | } |
85 | 85 | |
86 | | -execution_result * |
| 86 | +db::resultptr |
87 | 87 | connection::execute_sql(std::string const &sql) |
88 | 88 | { |
89 | 89 | 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 | +{ |
90 | 111 | PGresult *res; |
91 | 112 | if ((res = PQexec(conn, sql.c_str())) == NULL) |
92 | 113 | throw db::error(PQerrorMessage(conn)); |
93 | 114 | |
94 | 115 | switch (PQresultStatus(res)) { |
95 | 116 | case PGRES_COMMAND_OK: |
| 117 | + return; |
96 | 118 | case PGRES_TUPLES_OK: |
97 | | - return new execution_result(conn, res); |
| 119 | + break; |
98 | 120 | default: |
99 | 121 | throw db::error(PQresultErrorMessage(res)); |
100 | 122 | } |
101 | | -} |
102 | 123 | |
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 | | - |
111 | 124 | int nfields = PQnfields(res); |
112 | 125 | for (int i = 0; i < nfields; ++i) |
113 | 126 | names.push_back(PQfname(res, i)); |
114 | 127 | } |
115 | 128 | |
116 | | -execution_result::~execution_result() |
| 129 | +void |
| 130 | +result::bind(std::string const &, std::string const &) |
117 | 131 | { |
| 132 | + throw db::error("bound variables not supported for PostgreSQL"); |
| 133 | +} |
| 134 | + |
| 135 | +result::~result() |
| 136 | +{ |
118 | 137 | if (res) |
119 | 138 | PQclear(res); |
120 | 139 | } |
121 | 140 | |
122 | 141 | bool |
123 | | -execution_result::has_data(void) |
| 142 | +result::empty(void) |
124 | 143 | { |
125 | | - return PQresultStatus(res) != PGRES_COMMAND_OK; |
| 144 | + return PQresultStatus(res) != PGRES_TUPLES_OK; |
126 | 145 | } |
127 | 146 | |
128 | 147 | int |
129 | | -execution_result::num_fields(void) |
| 148 | +result::num_fields(void) |
130 | 149 | { |
131 | 150 | return PQnfields(res); |
132 | 151 | } |
133 | 152 | |
134 | 153 | int |
135 | | -execution_result::affected_rows(void) |
| 154 | +result::affected_rows(void) |
136 | 155 | { |
137 | 156 | return boost::lexical_cast<int>(PQcmdTuples(res)); |
138 | 157 | } |
139 | 158 | |
140 | 159 | result_row * |
141 | | -execution_result::next_row(void) |
| 160 | +result::next_row(void) |
142 | 161 | { |
143 | 162 | if (row == PQntuples(res)) |
144 | 163 | return NULL; |
— | — | @@ -145,7 +164,7 @@ |
146 | 165 | return new result_row(this, res, row++); |
147 | 166 | } |
148 | 167 | |
149 | | -result_row::result_row(execution_result *er, PGresult *res, int row) |
| 168 | +result_row::result_row(result *er, PGresult *res, int row) |
150 | 169 | : row(row) |
151 | 170 | , res(res) |
152 | 171 | , er(er) |
— | — | @@ -162,7 +181,7 @@ |
163 | 182 | } |
164 | 183 | |
165 | 184 | std::string |
166 | | -execution_result::field_name(int col) |
| 185 | +result::field_name(int col) |
167 | 186 | { |
168 | 187 | return names[col]; |
169 | 188 | } |
Index: trunk/skirmish/db.h |
— | — | @@ -8,6 +8,30 @@ |
9 | 9 | |
10 | 10 | namespace db { |
11 | 11 | |
| 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 | + |
12 | 36 | struct error : std::exception { |
13 | 37 | std::string err; |
14 | 38 | error(std::string const &err) : err(err) {} |
— | — | @@ -20,16 +44,28 @@ |
21 | 45 | virtual std::string string_value(int col) = 0; |
22 | 46 | }; |
23 | 47 | |
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; |
26 | 51 | |
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; |
28 | 61 | virtual int affected_rows(void) = 0; |
29 | 62 | virtual int num_fields(void) = 0; |
30 | 63 | virtual std::string field_name(int col) = 0; |
31 | 64 | |
| 65 | +protected: |
| 66 | + friend class resultset_iterator; |
32 | 67 | virtual result_row *next_row(void) = 0; |
33 | 68 | }; |
| 69 | +typedef boost::shared_ptr<result> resultptr; |
34 | 70 | |
35 | 71 | struct column { |
36 | 72 | std::string name; |
— | — | @@ -53,7 +89,7 @@ |
54 | 90 | |
55 | 91 | virtual ~connection(); |
56 | 92 | |
57 | | - virtual execution_result *execute_sql(std::string const &) = 0; |
| 93 | + virtual resultptr execute_sql(std::string const &) = 0; |
58 | 94 | virtual std::vector<table> describe_tables(std::string const & = "") = 0; |
59 | 95 | virtual table describe_table(std::string const &, std::string const &) = 0; |
60 | 96 | |
Index: trunk/skirmish/pgsql.h |
— | — | @@ -10,33 +10,40 @@ |
11 | 11 | |
12 | 12 | namespace postgres { |
13 | 13 | |
14 | | -struct execution_result; |
| 14 | +struct result; |
15 | 15 | |
16 | 16 | struct result_row : db::result_row { |
17 | | - result_row(execution_result *er, PGresult *, int); |
| 17 | + result_row(result *er, PGresult *, int); |
18 | 18 | |
19 | 19 | std::string string_value(int col); |
20 | 20 | |
| 21 | +private: |
21 | 22 | int row; |
22 | 23 | PGresult *res; |
23 | | - execution_result *er; |
| 24 | + result *er; |
24 | 25 | }; |
25 | 26 | |
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(); |
29 | 30 | |
30 | | - bool has_data(void); |
| 31 | + void bind(std::string const &, std::string const &); |
| 32 | + void execute(void); |
| 33 | + |
| 34 | + bool empty(void); |
31 | 35 | int num_fields(void); |
32 | 36 | int affected_rows(void); |
33 | 37 | std::string field_name(int col); |
34 | 38 | |
| 39 | +protected: |
35 | 40 | result_row *next_row(void); |
36 | 41 | |
| 42 | +private: |
37 | 43 | PGconn *conn; |
38 | 44 | PGresult *res; |
39 | 45 | std::vector<std::string> names; |
40 | 46 | int row; |
| 47 | + std::string sql; |
41 | 48 | }; |
42 | 49 | |
43 | 50 | struct connection : db::connection { |
— | — | @@ -48,7 +55,9 @@ |
49 | 56 | |
50 | 57 | std::string error(void); |
51 | 58 | |
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 | + |
53 | 62 | std::vector<db::table> describe_tables(std::string const &); |
54 | 63 | db::table describe_table(std::string const &, std::string const &); |
55 | 64 | |
Index: trunk/skirmish/Makefile |
— | — | @@ -1,3 +1,4 @@ |
| 2 | +include config.mk |
2 | 3 | include rules.mk |
3 | 4 | |
4 | 5 | SRCS = \ |
— | — | @@ -9,28 +10,19 @@ |
10 | 11 | OBJS = $(SRCS:.cc=.o) |
11 | 12 | |
12 | 13 | all: |
13 | | - for d in $(SUBDIRS); do \ |
14 | | - $(MAKE) -I.. -C $$d all ;\ |
15 | | - done |
16 | 14 | $(MAKE) skirmish |
17 | 15 | |
18 | 16 | skirmish: $(OBJS) |
19 | 17 | $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) -o $@ $(LIBS) |
20 | 18 | |
21 | 19 | clean: |
22 | | - for d in $(SUBDIRS); do \ |
23 | | - $(MAKE) -I.. -C $$d clean ;\ |
24 | | - done |
25 | 20 | rm -f $(OBJS) skirmish |
26 | 21 | |
27 | 22 | distclean: clean |
28 | | - for d in $(SUBDIRS); do \ |
29 | | - $(MAKE) -I.. -C $$d distclean ;\ |
30 | | - done |
31 | 23 | |
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 @@ |
100 | 100 | |
101 | 101 | conn = conns[cnr]->conn; |
102 | 102 | |
103 | | - db::execution_result *res; |
| 103 | + db::resultptr res; |
104 | 104 | int nrows = 0; |
105 | 105 | |
106 | 106 | if (input[input.size() - 1] == ';') |
— | — | @@ -108,11 +108,11 @@ |
109 | 109 | try { |
110 | 110 | res = conn->execute_sql(input); |
111 | 111 | } catch (db::error &e) { |
112 | | - std::cerr << boost::format("Error: %s\n") % e.what(); |
| 112 | + std::cerr << boost::format("[%s]\n") % e.what(); |
113 | 113 | continue; |
114 | 114 | } |
115 | 115 | |
116 | | - if (res->has_data()) { |
| 116 | + if (!res->empty()) { |
117 | 117 | db::result_row *r; |
118 | 118 | int ncols = res->num_fields(); |
119 | 119 | std::vector<int> sizes(ncols); |
— | — | @@ -123,14 +123,14 @@ |
124 | 124 | names.push_back(res->field_name(i)); |
125 | 125 | data.push_back(names); |
126 | 126 | |
127 | | - while (r = res->next_row()) { |
| 127 | + db::result::iterator it, end; |
| 128 | + for (it = res->begin(), end = res->end(); it != end; ++it) { |
128 | 129 | std::vector<std::string> thisrow; |
129 | 130 | for (int i = 0; i < ncols; ++i) { |
130 | | - std::string v = r->string_value(i); |
| 131 | + std::string v = it->string_value(i); |
131 | 132 | thisrow.push_back(v); |
132 | 133 | } |
133 | 134 | data.push_back(thisrow); |
134 | | - delete r; |
135 | 135 | ++nrows; |
136 | 136 | } |
137 | 137 | |
— | — | @@ -164,7 +164,6 @@ |
165 | 165 | |
166 | 166 | if (nrows) |
167 | 167 | std::cout << boost::format("\nOK (%d rows).\n") % nrows; |
168 | | - delete res; |
169 | 168 | } |
170 | 169 | } |
171 | 170 | |
— | — | @@ -183,7 +182,7 @@ |
184 | 183 | |
185 | 184 | std::map<std::string, boost::function<void (std::string const &)> >::iterator it; |
186 | 185 | 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; |
188 | 187 | return; |
189 | 188 | } |
190 | 189 | |
— | — | @@ -207,12 +206,12 @@ |
208 | 207 | try { |
209 | 208 | to = boost::lexical_cast<int>(arg); |
210 | 209 | } 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; |
212 | 211 | return; |
213 | 212 | } |
214 | 213 | |
215 | 214 | 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; |
217 | 216 | return; |
218 | 217 | } |
219 | 218 | |
— | — | @@ -229,17 +228,17 @@ |
230 | 229 | try { |
231 | 230 | to = boost::lexical_cast<int>(arg); |
232 | 231 | } 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; |
234 | 233 | return; |
235 | 234 | } |
236 | 235 | |
237 | 236 | 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; |
239 | 238 | return; |
240 | 239 | } |
241 | 240 | } else { |
242 | 241 | if (cnr == -1) { |
243 | | - std::cout << "Error: no connection.\n"; |
| 242 | + std::cout << "[no connection to close]\n"; |
244 | 243 | return; |
245 | 244 | } |
246 | 245 | to = cnr; |
— | — | @@ -279,7 +278,7 @@ |
280 | 279 | try { |
281 | 280 | tables = conns[cnr]->conn->describe_tables(arg); |
282 | 281 | } catch (db::error const &e) { |
283 | | - std::cout << boost::format("[error: %s]\n") % e.what(); |
| 282 | + std::cout << boost::format("[%s]\n") % e.what(); |
284 | 283 | return; |
285 | 284 | } |
286 | 285 | |
— | — | @@ -325,7 +324,7 @@ |
326 | 325 | |
327 | 326 | t = conns[cnr]->conn->describe_table(schema, table); |
328 | 327 | } catch (db::error const &e) { |
329 | | - std::cout << boost::format("[error: %s]\n") % e.what(); |
| 328 | + std::cout << boost::format("[%s]\n") % e.what(); |
330 | 329 | return; |
331 | 330 | } |
332 | 331 | |