r20380 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r20379‎ | r20380 | r20381 >
Date:02:08, 13 March 2007
Author:river
Status:old
Tags:
Comment:
ODBC client
Modified paths:
  • /trunk/skirmish/config.mk.sample (modified) (history)
  • /trunk/skirmish/db.cc (modified) (history)
  • /trunk/skirmish/db.h (modified) (history)
  • /trunk/skirmish/odbc.cc (added) (history)
  • /trunk/skirmish/odbc.h (added) (history)
  • /trunk/skirmish/rules.mk (modified) (history)

Diff [purge]

Index: trunk/skirmish/config.mk.sample
@@ -20,3 +20,7 @@
2121 # Build Oracle client ($ORACLE_HOME must be set)
2222 BUILD_ORACLE = YES
2323 # BUILD_ORACLE = NO
 24+
 25+# Build ODBC client (currently requires unixODBC)
 26+BUILD_ODBC = YES
 27+# BUILD_ODBC = NO
Index: trunk/skirmish/odbc.cc
@@ -0,0 +1,300 @@
 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+#include <sql.h>
 8+#include <sqlext.h>
 9+
 10+#include "odbc.h"
 11+
 12+namespace odbc {
 13+
 14+connection::connection(std::string const &desc)
 15+ : env(0)
 16+ , dbc(0)
 17+{
 18+ /* desc is "user[/password]@SID" */
 19+ std::string::size_type i;
 20+ std::string userpart;
 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+ sid = 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+
 42+connection::~connection()
 43+{
 44+ close();
 45+}
 46+
 47+void
 48+connection::open(void)
 49+{
 50+ if (!SQL_SUCCEEDED(SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env)))
 51+ throw db::error("cannot create ODBC environment");
 52+
 53+ if (!SQL_SUCCEEDED(SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0)))
 54+ throw db::error(error(env, SQL_HANDLE_ENV));
 55+
 56+ if (!SQL_SUCCEEDED(SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc)))
 57+ throw db::error(error(env, SQL_HANDLE_ENV));
 58+
 59+ std::string conn = "DSN=" + sid;
 60+ if (!user.empty())
 61+ conn += ";UID=" + user;
 62+ if (!password.empty())
 63+ conn += ";PWD=" + password;
 64+
 65+ if (!SQL_SUCCEEDED(SQLDriverConnect(dbc, 0, (SQLCHAR *)conn.c_str(), conn.size(), 0, 0, 0, SQL_DRIVER_NOPROMPT)))
 66+ throw db::error(error(dbc, SQL_HANDLE_DBC));
 67+}
 68+
 69+void
 70+connection::close(void)
 71+{
 72+ if (dbc) {
 73+ SQLDisconnect(dbc);
 74+ SQLFreeHandle(SQL_HANDLE_DBC, dbc);
 75+ dbc = 0;
 76+ }
 77+ if (env) {
 78+ SQLFreeHandle(SQL_HANDLE_ENV, env);
 79+ env = 0;
 80+ }
 81+}
 82+
 83+std::string
 84+connection::error(SQLHANDLE handle, int type)
 85+{
 86+ SQLCHAR state[7];
 87+ SQLCHAR text[1024];
 88+ SQLSMALLINT len;
 89+ SQLINTEGER native;
 90+ int i = 0;
 91+ std::string ret;
 92+
 93+ while (SQL_SUCCEEDED(SQLGetDiagRec(type, handle, ++i, state, &native, text, sizeof(text), &len))) {
 94+ ret += str(boost::format("%s:%d") % state % text);
 95+ }
 96+
 97+ return ret;
 98+}
 99+
 100+db::resultptr
 101+connection::execute_sql(std::string const &sql)
 102+{
 103+ db::resultptr r = prepare_sql(sql);
 104+ r->execute();
 105+ return r;
 106+}
 107+
 108+db::resultptr
 109+connection::prepare_sql(std::string const &sql)
 110+{
 111+ return db::resultptr(new result(this, sql));
 112+}
 113+
 114+result::result(connection *conn, std::string const &q)
 115+ : stmt(0)
 116+ , conn(conn)
 117+ , sql(q)
 118+{
 119+ if (!SQL_SUCCEEDED(SQLAllocHandle(SQL_HANDLE_STMT, conn->dbc, &stmt)))
 120+ throw db::error(conn->error(conn->dbc, SQL_HANDLE_DBC));
 121+}
 122+
 123+void
 124+result::execute(void)
 125+{
 126+ if (!SQL_SUCCEEDED(SQLExecDirect(stmt, (SQLCHAR *)sql.c_str(), sql.size())))
 127+ throw db::error(conn->error(stmt, SQL_HANDLE_STMT));
 128+
 129+ SQLSMALLINT ncols;
 130+ SQLNumResultCols(stmt, &ncols);
 131+ if (ncols == 0)
 132+ return;
 133+
 134+ SQLCHAR name[128];
 135+ SQLSMALLINT namelen;
 136+ SQLUINTEGER colsize;
 137+ fields.resize(ncols);
 138+ for (int i = 0; i < ncols; ++i) {
 139+ if (!SQL_SUCCEEDED(SQLDescribeCol(stmt, i + 1,
 140+ name, sizeof(name), &namelen,
 141+ NULL, &colsize, NULL, NULL)))
 142+ throw db::error(conn->error(stmt, SQL_HANDLE_STMT));
 143+ fields[i].name.assign(name, name + namelen);
 144+ fields[i].size = colsize;
 145+ fields[i].data.resize(colsize + 1);
 146+ }
 147+}
 148+
 149+void
 150+result::bind(std::string const &key, std::string const &value)
 151+{
 152+ throw db::error("prepared statements not supported for ODBC");
 153+}
 154+
 155+result::~result()
 156+{
 157+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
 158+}
 159+
 160+bool
 161+result::empty(void)
 162+{
 163+ return fields.size() == 0;
 164+}
 165+
 166+int
 167+result::num_fields(void)
 168+{
 169+ return fields.size();
 170+}
 171+
 172+int
 173+result::affected_rows(void)
 174+{
 175+ return 0; /* XXX */
 176+}
 177+
 178+result_row *
 179+result::next_row(void)
 180+{
 181+ SQLRETURN r;
 182+ r = SQLFetch(stmt);
 183+ if (!SQL_SUCCEEDED(r)) {
 184+ if (r == SQL_NO_DATA)
 185+ return NULL;
 186+ throw db::error(conn->error(stmt, SQL_HANDLE_STMT));
 187+ }
 188+
 189+ for (int i = 0; i < fields.size(); ++i) {
 190+ SQLINTEGER indicator;
 191+ r = SQLGetData(stmt, i + 1, SQL_C_CHAR,
 192+ &fields[i].data[0], fields[i].data.size(),
 193+ &indicator);
 194+ if (!SQL_SUCCEEDED(r))
 195+ throw db::error(conn->error(stmt, SQL_HANDLE_STMT));
 196+ if (indicator == SQL_NULL_DATA) {
 197+ fields[i].data.resize(5);
 198+ std::strcpy(&fields[i].data[0], "NULL");
 199+ }
 200+ }
 201+ return new result_row(this);
 202+}
 203+
 204+result_row::result_row(result *er)
 205+ : er(er)
 206+{
 207+}
 208+
 209+std::string
 210+result_row::string_value(int col)
 211+{
 212+ return std::string(&er->fields[col].data[0]);
 213+
 214+}
 215+
 216+std::string
 217+result::field_name(int col)
 218+{
 219+ return fields[col].name;
 220+}
 221+
 222+std::vector<db::table>
 223+connection::describe_tables(std::string const &schema)
 224+{
 225+ SQLHANDLE stmt;
 226+ SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
 227+ SQLTables(stmt, NULL, 0, (SQLCHAR *)schema.c_str(), schema.size(), NULL, 0, NULL, 0);
 228+
 229+ SQLRETURN r;
 230+ std::vector<std::pair<std::string, std::string> > names;
 231+ for (;;) {
 232+ r = SQLFetch(stmt);
 233+ if (!SQL_SUCCEEDED(r)) {
 234+ if (r == SQL_NO_DATA)
 235+ break;
 236+
 237+ std::string e = error(stmt, SQL_HANDLE_STMT);
 238+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
 239+ throw db::error(e);
 240+ }
 241+
 242+ SQLINTEGER ind;
 243+ char name[256];
 244+ char schema[256];
 245+ SQLGetData(stmt, 2, SQL_C_CHAR, schema, sizeof(schema), &ind);
 246+ SQLGetData(stmt, 3, SQL_C_CHAR, name, sizeof(name), &ind);
 247+ names.push_back(std::pair<std::string, std::string>(
 248+ schema, name));
 249+ }
 250+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
 251+
 252+ std::vector<db::table> ret;
 253+ for (int i = 0; i < names.size(); ++i) {
 254+ ret.push_back(describe_table(names[i].first, names[i].second));
 255+ }
 256+
 257+ return ret;
 258+}
 259+
 260+db::table
 261+connection::describe_table(std::string const &schema, std::string const &name)
 262+{
 263+ db::table ret;
 264+ ret.name = name;
 265+ ret.schema = schema;
 266+
 267+ SQLHANDLE stmt;
 268+ SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
 269+ SQLColumns(stmt, NULL, 0, (SQLCHAR *)schema.c_str(), schema.size(),
 270+ (SQLCHAR *)name.c_str(), name.size(), NULL, 0);
 271+ SQLRETURN r;
 272+ for (;;) {
 273+ r = SQLFetch(stmt);
 274+ if (!SQL_SUCCEEDED(r)) {
 275+ if (r == SQL_NO_DATA)
 276+ break;
 277+
 278+ std::string e = error(stmt, SQL_HANDLE_STMT);
 279+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
 280+ throw db::error(e);
 281+ }
 282+
 283+ db::column c;
 284+ SQLINTEGER ind;
 285+ char name[256];
 286+ char type[256];
 287+ char nullable[5];
 288+ SQLGetData(stmt, 4, SQL_C_CHAR, name, sizeof(name), &ind);
 289+ SQLGetData(stmt, 6, SQL_C_CHAR, type, sizeof(type), &ind);
 290+ SQLGetData(stmt, 18, SQL_C_CHAR, nullable, sizeof(nullable), &ind);
 291+ c.name = name;
 292+ c.type = type;
 293+ c.nullable = !strcmp(nullable, "YES");
 294+ ret.columns.push_back(c);
 295+ }
 296+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
 297+
 298+ return ret;
 299+}
 300+
 301+} // namespace odbc
Property changes on: trunk/skirmish/odbc.cc
___________________________________________________________________
Added: svn:keywords
1302 + Id Revision
Index: trunk/skirmish/odbc.h
@@ -0,0 +1,77 @@
 2+#ifndef ODBC_H
 3+#define ODBC_H
 4+
 5+#include <string>
 6+#include <vector>
 7+
 8+#include <sql.h>
 9+
 10+#include "db.h"
 11+
 12+namespace odbc {
 13+
 14+struct result;
 15+struct connection;
 16+
 17+struct odbcfield {
 18+ std::string name;
 19+ SQLUINTEGER size;
 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 result : db::result {
 32+ result(connection *, std::string const &);
 33+ ~result();
 34+
 35+ void bind(std::string const &, std::string const &);
 36+ void execute(void);
 37+
 38+ bool empty(void);
 39+ int num_fields(void);
 40+ int affected_rows(void);
 41+ std::string field_name(int col);
 42+
 43+ result_row *next_row(void);
 44+
 45+ std::string sql;
 46+ std::vector<odbcfield> fields;
 47+ connection *conn;
 48+ SQLHANDLE stmt;
 49+};
 50+
 51+struct connection : db::connection {
 52+ connection(std::string const &desc);
 53+ ~connection();
 54+
 55+ void open(void);
 56+ void close(void);
 57+
 58+ std::string error(SQLHANDLE, int);
 59+
 60+ db::resultptr execute_sql(std::string const &);
 61+ db::resultptr prepare_sql(std::string const &);
 62+
 63+ std::vector<db::table> describe_tables(std::string const &);
 64+ db::table describe_table(std::string const &, std::string const &);
 65+
 66+private:
 67+ friend class result;
 68+
 69+ SQLHENV env;
 70+ SQLHDBC dbc;
 71+ SQLRETURN err;
 72+
 73+ std::string sid, user, password;
 74+};
 75+
 76+} // namespace oracle
 77+
 78+#endif
Property changes on: trunk/skirmish/odbc.h
___________________________________________________________________
Added: svn:keywords
179 + Id Revision
Index: trunk/skirmish/rules.mk
@@ -20,6 +20,13 @@
2121 DB_SRCS += pgsql.cc
2222 endif
2323
 24+ifeq ($(BUILD_ODBC),YES)
 25+INCLUDES +=
 26+CPPFLAGS += $(shell odbc_config --cflags) -DSKIRMISH_ODBC
 27+LIBS += $(shell odbc_config --libs)
 28+DB_SRCS += odbc.cc
 29+endif
 30+
2431 .cc.o:
2532 $(CXX) $(CPPFLAGS) $(INCLUDES) $(CXXFLAGS) -c $<
2633
Index: trunk/skirmish/db.cc
@@ -18,6 +18,10 @@
1919 # include "ora.h"
2020 #endif
2121
 22+#ifdef SKIRMISH_ODBC
 23+# include "odbc.h"
 24+#endif
 25+
2226 namespace db {
2327
2428 connection::connection()
@@ -51,9 +55,18 @@
5256 typedef std::map<std::string, boost::function<connectionptr (std::string const &)> > schemelist_t;
5357
5458 static schemelist_t schemes = boost::assign::map_list_of
 59+#ifdef SKIRMISH_MYSQL
5560 ("mysql", construct<mysql::connection>)
 61+#endif
 62+#ifdef SKIRMISH_POSTGRES
5663 ("postgres", construct<postgres::connection>)
 64+#endif
 65+#ifdef SKIRMISH_ORACLE
5766 ("oracle", construct<oracle::connection>)
 67+#endif
 68+#ifdef SKIRMISH_ODBC
 69+ ("odbc", construct<odbc::connection>)
 70+#endif
5871 ;
5972
6073 schemelist_t::iterator it = schemes.find(type);
Index: trunk/skirmish/db.h
@@ -85,8 +85,6 @@
8686 virtual void open(void) = 0;
8787 virtual void close(void) = 0;
8888
89 - virtual std::string error(void) = 0;
90 -
9189 virtual ~connection();
9290
9391 virtual resultptr execute_sql(std::string const &) = 0;