Index: trunk/skirmish/db.cc |
— | — | @@ -22,6 +22,10 @@ |
23 | 23 | # include "odbc.h" |
24 | 24 | #endif |
25 | 25 | |
| 26 | +#ifdef SKIRMISH_MAXDB |
| 27 | +# include "maxdb.h" |
| 28 | +#endif |
| 29 | + |
26 | 30 | namespace db { |
27 | 31 | |
28 | 32 | connection::connection() |
— | — | @@ -67,6 +71,10 @@ |
68 | 72 | #ifdef SKIRMISH_ODBC |
69 | 73 | ("odbc", construct<odbc::connection>) |
70 | 74 | #endif |
| 75 | +#ifdef SKIRMISH_MAXDB |
| 76 | + ("maxdb", construct<maxdb::connection>) |
| 77 | + ("sapdb", construct<maxdb::connection>) |
| 78 | +#endif |
71 | 79 | ; |
72 | 80 | |
73 | 81 | schemelist_t::iterator it = schemes.find(type); |
Index: trunk/skirmish/maxdb.cc |
— | — | @@ -0,0 +1,293 @@ |
| 2 | +#include <iostream> |
| 3 | +#include <boost/lexical_cast.hpp> |
| 4 | +#include <boost/format.hpp> |
| 5 | +#include <boost/algorithm/string/case_conv.hpp> |
| 6 | +#include <unistd.h> |
| 7 | + |
| 8 | +#include "maxdb.h" |
| 9 | + |
| 10 | +namespace maxdb { |
| 11 | + |
| 12 | +connection::connection(std::string const &desc) |
| 13 | + : runtime(0) |
| 14 | + , env(0) |
| 15 | + , conn(0) |
| 16 | +{ |
| 17 | + /* desc is "user[/password]@[host[/database]]" */ |
| 18 | + std::string::size_type i; |
| 19 | + std::string userpart; |
| 20 | + std::string dbpart; |
| 21 | + std::string d; |
| 22 | + |
| 23 | + d = desc.substr(desc.find(':') + 1); |
| 24 | + |
| 25 | + if ((i = d.find('@')) != std::string::npos) { |
| 26 | + userpart = d.substr(0, i); |
| 27 | + dbpart = d.substr(i + 1); |
| 28 | + } else |
| 29 | + userpart = d; |
| 30 | + |
| 31 | + if ((i = userpart.find('/')) != std::string::npos) { |
| 32 | + user = userpart.substr(0, i); |
| 33 | + password = userpart.substr(i + 1); |
| 34 | + } else { |
| 35 | + user = userpart; |
| 36 | + char *p = getpass("Enter password: "); |
| 37 | + if (p) |
| 38 | + password = p; |
| 39 | + } |
| 40 | + |
| 41 | + if ((i = dbpart.find('/')) != std::string::npos) { |
| 42 | + host = dbpart.substr(0, i); |
| 43 | + dbname = dbpart.substr(i + 1); |
| 44 | + } else |
| 45 | + host = dbpart; |
| 46 | +} |
| 47 | + |
| 48 | +connection::~connection() |
| 49 | +{ |
| 50 | + close(); |
| 51 | +} |
| 52 | + |
| 53 | +void |
| 54 | +connection::open(void) |
| 55 | +{ |
| 56 | + char err[1024]; |
| 57 | + if ((runtime = SQLDBC::GetClientRuntime(err, sizeof(err))) == NULL) |
| 58 | + throw db::error(err); |
| 59 | + env = new SQLDBC::SQLDBC_Environment(runtime); |
| 60 | + conn = env->createConnection(); |
| 61 | + if (conn->connect(host.c_str(), dbname.c_str(), user.c_str(), password.c_str()) != SQLDBC_OK) |
| 62 | + throw db::error(error(conn->error())); |
| 63 | +} |
| 64 | + |
| 65 | +void |
| 66 | +connection::close(void) |
| 67 | +{ |
| 68 | + delete env; |
| 69 | +} |
| 70 | + |
| 71 | +std::string |
| 72 | +connection::error(SQLDBC::SQLDBC_ErrorHndl &err) |
| 73 | +{ |
| 74 | + return str(boost::format("%d:%s:%s") % err.getErrorCode() % err.getSQLState() % err.getErrorText()); |
| 75 | +} |
| 76 | + |
| 77 | +db::resultptr |
| 78 | +connection::execute_sql(std::string const &sql) |
| 79 | +{ |
| 80 | + db::resultptr r = prepare_sql(sql); |
| 81 | + r->execute(); |
| 82 | + return r; |
| 83 | +} |
| 84 | + |
| 85 | +db::resultptr |
| 86 | +connection::prepare_sql(std::string const &sql) |
| 87 | +{ |
| 88 | + return db::resultptr(new result(this, sql)); |
| 89 | +} |
| 90 | + |
| 91 | +result::result(connection *conn, std::string const &q) |
| 92 | + : stmt(0) |
| 93 | + , conn(conn) |
| 94 | + , sql(q) |
| 95 | +{ |
| 96 | +std::cout << "q = ["<<q<<"]\n"; |
| 97 | + if ((stmt = conn->conn->createPreparedStatement()) == NULL) |
| 98 | + throw db::error(conn->error(conn->conn->error())); |
| 99 | + if (stmt->prepare(q.c_str(), q.size(), SQLDBC_StringEncodingUTF8) != SQLDBC_OK) |
| 100 | + throw db::error(conn->error(stmt->error())); |
| 101 | + if (!stmt->isQuery()) |
| 102 | + return; |
| 103 | + |
| 104 | + SQLDBC::SQLDBC_ParameterMetaData *pmd = stmt->getParameterMetaData(); |
| 105 | + SQLDBC_Int2 nparams = pmd->getParameterCount(); |
| 106 | + SQLDBC_Length namelen; |
| 107 | + for (int i = 1; i <= nparams; ++i) { |
| 108 | +#if 0 |
| 109 | + if (pmd->getParameterName(i, NULL, SQLDBC_StringEncodingUTF8, 0, &namelen) != SQLDBC_DATA_TRUNC) |
| 110 | + throw db::error("could not retrieve parameter length"); |
| 111 | +#endif |
| 112 | + std::vector<char> paramname(namelen + 1); |
| 113 | +std::cout << "namelen = " << namelen << '\n'; |
| 114 | + if (pmd->getParameterName(i, ¶mname[0], SQLDBC_StringEncodingUTF8, paramname.size() + 1, &namelen) != SQLDBC_OK) |
| 115 | + throw db::error("could not retrieve parameter name"); |
| 116 | + param p; |
| 117 | + p.name.assign(¶mname[0], ¶mname[0] + namelen); |
| 118 | + p.pos = i; |
| 119 | +std::cout << "i = " << i << " nparams = " << nparams << " name = [" << p.name << "] len = " << namelen << "\n"; |
| 120 | + params.push_back(p); |
| 121 | + } |
| 122 | +} |
| 123 | + |
| 124 | +void |
| 125 | +result::execute(void) |
| 126 | +{ |
| 127 | + if (stmt->execute() != SQLDBC_OK) |
| 128 | + throw db::error(conn->error(stmt->error())); |
| 129 | + |
| 130 | + SQLDBC::SQLDBC_ResultSetMetaData *md = stmt->getResultSetMetaData(); |
| 131 | + if (md == NULL) |
| 132 | + return; |
| 133 | + |
| 134 | + int ncols = md->getColumnCount(); |
| 135 | + fields.resize(ncols); |
| 136 | + for (int i = 0; i < ncols; ++i) { |
| 137 | + int colsize = md->getColumnLength(i + 1); |
| 138 | + fields[i].size = colsize; |
| 139 | + fields[i].data.resize(colsize + 1); |
| 140 | + |
| 141 | + SQLDBC_Retcode r; |
| 142 | + std::vector<char> name(256); |
| 143 | + SQLDBC_Length namelen; |
| 144 | + r = md->getColumnName(i + 1, &name[0], SQLDBC_StringEncodingUTF8, name.size(), &namelen); |
| 145 | + if (r != SQLDBC_OK && r != SQLDBC_DATA_TRUNC) |
| 146 | + throw db::error(conn->error(stmt->error())); |
| 147 | + if (r == SQLDBC_DATA_TRUNC) { |
| 148 | + name.resize(namelen + 1); |
| 149 | + if (md->getColumnName(i + 1, &name[0], SQLDBC_StringEncodingUTF8, name.size(), &namelen) |
| 150 | + != SQLDBC_OK) |
| 151 | + throw db::error(conn->error(stmt->error())); |
| 152 | + } |
| 153 | + fields[i].name.assign(&name[0], &name[0] + namelen); |
| 154 | + } |
| 155 | + |
| 156 | + res = stmt->getResultSet(); |
| 157 | +} |
| 158 | + |
| 159 | +void |
| 160 | +result::bind(std::string const &key, std::string const &value) |
| 161 | +{ |
| 162 | + for (int i = 0; i < params.size(); ++i) { |
| 163 | + if (params[i].name != key) |
| 164 | + return; |
| 165 | + SQLDBC_Length len = value.size(); |
| 166 | + if (stmt->bindParameter(params[i].pos, SQLDBC_HOSTTYPE_UTF8, |
| 167 | + (void *)value.data(), &len, value.size(), SQLDBC_FALSE) != SQLDBC_OK) |
| 168 | + throw db::error(conn->error(stmt->error())); |
| 169 | + return; |
| 170 | + } |
| 171 | + throw db::error(str(boost::format("parameter \"%s\" does not exist in statement") % key)); |
| 172 | +} |
| 173 | + |
| 174 | +result::~result() |
| 175 | +{ |
| 176 | +} |
| 177 | + |
| 178 | +bool |
| 179 | +result::empty(void) |
| 180 | +{ |
| 181 | + return fields.size() == 0; |
| 182 | +} |
| 183 | + |
| 184 | +int |
| 185 | +result::num_fields(void) |
| 186 | +{ |
| 187 | + return fields.size(); |
| 188 | +} |
| 189 | + |
| 190 | +int |
| 191 | +result::affected_rows(void) |
| 192 | +{ |
| 193 | + return 0; /* XXX */ |
| 194 | +} |
| 195 | + |
| 196 | +result_row * |
| 197 | +result::next_row(void) |
| 198 | +{ |
| 199 | + switch (res->next()) { |
| 200 | + case SQLDBC_OK: |
| 201 | + break; |
| 202 | + case SQLDBC_NO_DATA_FOUND: |
| 203 | + return NULL; |
| 204 | + case SQLDBC_NOT_OK: |
| 205 | + throw db::error(conn->error(res->error())); |
| 206 | + } |
| 207 | + |
| 208 | + for (int i = 0; i < fields.size(); ++i) { |
| 209 | + if (res->getObject(i + 1, SQLDBC_HOSTTYPE_UTF8, &fields[i].data[0], |
| 210 | + &fields[i].len, fields[i].size + 1, SQLDBC_TRUE) != SQLDBC_OK) |
| 211 | + throw db::error(conn->error(res->error())); |
| 212 | + } |
| 213 | + return new result_row(this); |
| 214 | +} |
| 215 | + |
| 216 | +result_row::result_row(result *er) |
| 217 | + : er(er) |
| 218 | +{ |
| 219 | +} |
| 220 | + |
| 221 | +std::string |
| 222 | +result_row::string_value(int col) |
| 223 | +{ |
| 224 | + if (er->fields[col].len == SQLDBC_NULL_DATA) |
| 225 | + return "NULL"; |
| 226 | + return std::string(&er->fields[col].data[0], &er->fields[col].data[er->fields[col].len]); |
| 227 | + |
| 228 | +} |
| 229 | + |
| 230 | +std::string |
| 231 | +result::field_name(int col) |
| 232 | +{ |
| 233 | + return fields[col].name; |
| 234 | +} |
| 235 | + |
| 236 | +std::vector<db::table> |
| 237 | +connection::describe_tables(std::string const &schema) |
| 238 | +{ |
| 239 | + std::string n = boost::algorithm::to_upper_copy(schema); |
| 240 | + |
| 241 | + db::resultptr r; |
| 242 | + if (schema.empty()) |
| 243 | + r = prepare_sql("SELECT owner, table_name FROM all_tables"); |
| 244 | + else { |
| 245 | + r = prepare_sql("SELECT owner, table_name FROM all_tables WHERE owner = :towner"); |
| 246 | + r->bind(":towner", n); |
| 247 | + } |
| 248 | + r->execute(); |
| 249 | + |
| 250 | + std::vector<std::pair<std::string, std::string> > names; |
| 251 | + |
| 252 | + std::vector<db::table> ret; |
| 253 | + result::iterator it = r->begin(), end = r->end(); |
| 254 | + for (; it != end; ++it) { |
| 255 | + names.push_back(std::pair<std::string, std::string>( |
| 256 | + it->string_value(0), it->string_value(1))); |
| 257 | + } |
| 258 | + |
| 259 | + for (int i = 0; i < names.size(); ++i) { |
| 260 | + ret.push_back(describe_table(names[i].first, names[i].second)); |
| 261 | + } |
| 262 | + |
| 263 | + return ret; |
| 264 | +} |
| 265 | + |
| 266 | +db::table |
| 267 | +connection::describe_table(std::string const &schema, std::string const &name) |
| 268 | +{ |
| 269 | + db::table ret; |
| 270 | + ret.name = name; |
| 271 | + ret.schema = schema; |
| 272 | + |
| 273 | + db::resultptr r = prepare_sql( |
| 274 | + "SELECT column_name, data_type, nullable FROM all_tab_columns WHERE owner = :tabowner AND table_name = :name"); |
| 275 | + std::string n = boost::algorithm::to_upper_copy(name); |
| 276 | + std::string o = boost::algorithm::to_upper_copy(schema); |
| 277 | + |
| 278 | + r->bind(":name", n); |
| 279 | + r->bind(":tabowner", o); |
| 280 | + r->execute(); |
| 281 | + |
| 282 | + result::iterator it = r->begin(), end = r->end(); |
| 283 | + for (; it != end; ++it) { |
| 284 | + db::column c; |
| 285 | + c.name = it->string_value(0); |
| 286 | + c.type = it->string_value(1); |
| 287 | + c.nullable = it->string_value(2) == "Y"; |
| 288 | + ret.columns.push_back(c); |
| 289 | + } |
| 290 | + |
| 291 | + return ret; |
| 292 | +} |
| 293 | + |
| 294 | +} // namespace maxdb |
Property changes on: trunk/skirmish/maxdb.cc |
___________________________________________________________________ |
Added: svn:keywords |
1 | 295 | + Id Revision |
Index: trunk/skirmish/maxdb.h |
— | — | @@ -0,0 +1,84 @@ |
| 2 | +#ifndef MAXDB_H |
| 3 | +#define MAXDB_H |
| 4 | + |
| 5 | +#include <string> |
| 6 | +#include <vector> |
| 7 | + |
| 8 | +#include "db.h" |
| 9 | +#include <SQLDBC.h> |
| 10 | + |
| 11 | +namespace maxdb { |
| 12 | + |
| 13 | +struct result; |
| 14 | +struct connection; |
| 15 | + |
| 16 | +struct maxdbfield { |
| 17 | + std::string name; |
| 18 | + SQLDBC_Length size; |
| 19 | + SQLDBC_Length len; |
| 20 | + std::vector<char> data; |
| 21 | +}; |
| 22 | + |
| 23 | +struct result_row : db::result_row { |
| 24 | + result_row(result *er); |
| 25 | + |
| 26 | + std::string string_value(int col); |
| 27 | + |
| 28 | + result *er; |
| 29 | +}; |
| 30 | + |
| 31 | +struct param { |
| 32 | + std::string name; |
| 33 | + int pos; |
| 34 | +}; |
| 35 | + |
| 36 | +struct result : db::result { |
| 37 | + result(connection *, std::string const &); |
| 38 | + ~result(); |
| 39 | + |
| 40 | + void bind(std::string const &, std::string const &); |
| 41 | + void execute(void); |
| 42 | + |
| 43 | + bool empty(void); |
| 44 | + int num_fields(void); |
| 45 | + int affected_rows(void); |
| 46 | + std::string field_name(int col); |
| 47 | + |
| 48 | + result_row *next_row(void); |
| 49 | + |
| 50 | + std::string sql; |
| 51 | + std::vector<maxdbfield> fields; |
| 52 | + connection *conn; |
| 53 | + SQLDBC::SQLDBC_PreparedStatement *stmt; |
| 54 | + SQLDBC::SQLDBC_ResultSet *res; |
| 55 | + std::vector<param> params; |
| 56 | +}; |
| 57 | + |
| 58 | +struct connection : db::connection { |
| 59 | + connection(std::string const &desc); |
| 60 | + ~connection(); |
| 61 | + |
| 62 | + void open(void); |
| 63 | + void close(void); |
| 64 | + |
| 65 | + std::string error(SQLDBC::SQLDBC_ErrorHndl &); |
| 66 | + |
| 67 | + db::resultptr execute_sql(std::string const &); |
| 68 | + db::resultptr prepare_sql(std::string const &); |
| 69 | + |
| 70 | + std::vector<db::table> describe_tables(std::string const &); |
| 71 | + db::table describe_table(std::string const &, std::string const &); |
| 72 | + |
| 73 | +private: |
| 74 | + friend class result; |
| 75 | + |
| 76 | + SQLDBC_IRuntime *runtime; |
| 77 | + SQLDBC::SQLDBC_Environment *env; |
| 78 | + SQLDBC::SQLDBC_Connection *conn; |
| 79 | + |
| 80 | + std::string host, dbname, user, password; |
| 81 | +}; |
| 82 | + |
| 83 | +} // namespace oracle |
| 84 | + |
| 85 | +#endif |
Property changes on: trunk/skirmish/maxdb.h |
___________________________________________________________________ |
Added: svn:keywords |
1 | 86 | + Id Revision |
Index: trunk/skirmish/config.mk.sample |
— | — | @@ -24,3 +24,9 @@ |
25 | 25 | # Build ODBC client (currently requires unixODBC) |
26 | 26 | BUILD_ODBC = YES |
27 | 27 | # BUILD_ODBC = NO |
| 28 | + |
| 29 | +# Build MaxDB (requires MaxDB SDK) |
| 30 | +BUILD_MAXDB = YES |
| 31 | +# BUILD_MAXDB = NO |
| 32 | + |
| 33 | +MAXDB_ROOT = /opt/sdb |
Index: trunk/skirmish/rules.mk |
— | — | @@ -27,6 +27,13 @@ |
28 | 28 | DB_SRCS += odbc.cc |
29 | 29 | endif |
30 | 30 | |
| 31 | +ifeq ($(BUILD_MAXDB),YES) |
| 32 | +INCLUDES += -I$(MAXDB_ROOT)/programs/sdk/sqldbc/incl |
| 33 | +CPPFLAGS += -DSKIRMISH_MAXDB |
| 34 | +LIBS += -L$(MAXDB_ROOT)/programs/lib -lSQLDBC |
| 35 | +DB_SRCS += maxdb.cc |
| 36 | +endif |
| 37 | + |
31 | 38 | .cc.o: |
32 | 39 | $(CXX) $(CPPFLAGS) $(INCLUDES) $(CXXFLAGS) -c $< |
33 | 40 | |