| 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 | |