Develop and Download Open Source Software

Browse Subversion Repository

Contents of /kz_odbc.h

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (show annotations) (download) (as text)
Fri Apr 15 01:04:03 2011 UTC (13 years, 1 month ago) by ohfuji
File MIME type: text/x-chdr
File size: 17203 byte(s)


1 /*
2 * KZ ODBC
3 * Copyright (C) 2010 Katsuhisa Ohfuji <katsuhisa@ohfuji.name>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 * MA 02110-1301, USA.
18 */
19 #ifndef KZ_ODBC_H
20 #define KZ_ODBC_H
21 /**********************************************************************
22 kz_odbc.h ODBC APIの簡易ラッパークラス
23 Authors Katsuhisa Ohfuji
24 Version 0.1a(開発版)2008/03/11
25 Version 0.2a(開発版)2008/04/14 NULL値の取得エラーの修正
26 (NULL値を空文字として取得する)
27 Version 0.3a(開発版)2008/05/16 カタログ関数(tables,columns)の追加
28 Version 0.4a(開発版)2009/01/06 Linux対応(+一部関数の追加)
29 Version 0.5a(開発版)2009/02/12 colinfoの添え字調整
30 Version 0.6a(開発版)2009/02/27 トランザクション対応
31 Fetch関数(next)の追加
32 Version 0.7a(開発版)2009/04/10 データ取得のバグ修正
33 Version 0.7b(開発版)2009/05/07 引数のデータ型の訂正
34 Version 0.8a(開発版)2009/05/12 NULLデーター挿入の対応
35 Version 0.8b(開発版)2009/12/11 64bit修正
36 Version 0.9 2011/04/14 バグフィックス
37
38 使い方(Ver0.6から使い方が変わりました)
39 1.kz_odbcオブジェクトを作成(コンストラクタにODBC接続文字列を指定)
40
41 kz_odbc db("DSN=myaccess", false);
42
43 ※DSNでデーターソースの指定のほか、Driverでの指定も可
44 ※コンストラクタの2つ目の引数はコミットモードを指定する
45 trueで手動コミットモード、falseで自動コミットモード
46 手動コミットモードにした場合、最後にCommit()を呼び出す必要がある。
47
48 2.SQLの発行
49 stmtオブジェクトを作成し、SQLを発行する。
50
51 kz_stmt stmt(&db);
52
53 stmt, "INSERT INTO TEST(col1, col2) VALUE(?, ?)", "val1", 10, endsql;
54
55 ※各要素をカンマ(,)で区切る
56 ※最初にkz_stmtオブジェクトを指定し、次にSQLを文字列で指定する。
57 ※パラメーター(?に対する値)を続けて指定
58 ※パラメーターで指定できる型は、整数(各int)、文字(char*)、
59 浮動小数点(double,float)、時間(struct tm)
60 それ以外の型を指定するとSQLが発行されない(式全体が無視される)。
61 ※整数の0をパラメータに指定する時は一度変数に入れてから指定する。
62  (C++コンパイラでは、整数の0は、NULLポインタと解釈されるので)
63 ※NULL値を指定したいときは、kz_odbc_nullを指定する。
64 stmt, "INSERT INTO TEST(col1) VALUE(?)", kz_odbc_null, endsql;
65 ※最後にendsqlと記述する(記述がないとSQLが発行されない)。
66
67 3.値を返すSQLの発行
68
69 kz_resultset_array result = (stmt, "SELECT * FROM TEST", endsqlrs);
70
71 ※ 結果セットは、kz_resultset_array(後述)で返る
72 ※ SQL発行部のカンマ式の全体を()で括る。
73 演算子の優先順位の関係で全体を括らないと値が返らない。
74 ※ 最後は、endsqlrsと記述する(endsqlでは値が返らない)。
75
76 4.結果セットの参照方法
77 結果セット(kz_resultset_array)は、以下のtypdefになっている。
78 std::vector< std::map< std::string, std::string> >
79 ※SELECTの結果を全て受け取る
80 ※行はvectorに列はmap(列名と値のmap)になっている。
81   cout << result[1]["ID"]
82  のようにアクセスできる。
83 ※結果セットは、文字列に変換されて返る
84 5.エラーの確認
85 ※iserrメンバ関数でエラーの有無が分かる
86 ※errorsメンバ関数でエラー情報が取得できる
87  結果は、kz_string_array( std::vector< std::string > )で返る。
88 サンプル
89  kz_odbc_sample.cppをご参照下さい。
90 ライセンス
91  本ソフトウェアはGPLで配布しています。
92 **********************************************************************/
93 #include <string>
94 #include <strstream>
95 #include <vector>
96 #include <map>
97 #include <time.h>
98
99 #ifdef _WIN32
100 #include <windows.h>
101 #endif
102 #include <sql.h>
103 #include <sqlext.h>
104
105 class kz_odbc_marker_end_sql{
106 void *dmy;
107 public:
108 kz_odbc_marker_end_sql() : dmy(0) {}
109 };
110 inline kz_odbc_marker_end_sql get_kz_odbc_marker_end_sql() { return kz_odbc_marker_end_sql(); }
111 #define endsql get_kz_odbc_marker_end_sql()
112
113 class kz_odbc_marker_end_sql_resultsets{
114 void *dmy;
115 public:
116 kz_odbc_marker_end_sql_resultsets() : dmy(0) {}
117 };
118 inline kz_odbc_marker_end_sql_resultsets get_kz_odbc_marker_end_sql_resultsets() { return kz_odbc_marker_end_sql_resultsets(); }
119 #define endsqlrs get_kz_odbc_marker_end_sql_resultsets()
120
121 class kz_odbc_maker_null {
122 void *dmy;
123 public:
124 kz_odbc_maker_null() : dmy(0) {}
125 };
126 inline kz_odbc_maker_null get_kz_odbc_maker_null() { return kz_odbc_maker_null(); }
127 #define kz_odbc_null get_kz_odbc_maker_null()
128
129 #define KZSQLSUCCESS(rc) ((rc==SQL_SUCCESS)||(rc==SQL_SUCCESS_WITH_INFO)||(rc==SQL_NO_DATA))
130 #define MAX_RESULT_COLUMN_NAME_LEN 128
131
132 typedef std::map< std::string, std::string> kz_resultset;
133 typedef std::pair< std::string, std::string> kz_resultset_pair;
134 typedef std::vector< kz_resultset > kz_resultset_array;
135 typedef std::vector< std::string > kz_string_array;
136
137 class kz_odbc {
138 friend class kz_stmt;
139 private:
140 RETCODE rc;
141 HENV henv;
142 HDBC hdbc;
143 const char* constr;
144 bool tranflg;
145
146 void connect() {
147 SQLAllocEnv(&henv);
148 SQLAllocConnect(henv,&hdbc);
149 rc = SQLDriverConnect(hdbc, 0, (SQLCHAR*)constr, SQL_NTS, 0, 0, 0,0);
150 if ( !iserr() && tranflg ) {
151 BeginTran();
152 }
153 }
154
155 void dissconnect() {
156 if ( tranflg ) { Rollback(); }
157 if ( hdbc ) { SQLDisconnect(hdbc); }
158 if ( hdbc ) { SQLFreeConnect(hdbc); hdbc = 0; }
159 if ( henv ) { SQLFreeEnv(henv); henv = 0; }
160 }
161 public:
162 bool iserr() { return !KZSQLSUCCESS(rc); }
163 public:
164 void errclear() { rc = SQL_SUCCESS; }
165
166 public:
167 kz_odbc() : rc(SQL_SUCCESS), henv(0), hdbc(0), constr(0), tranflg(false) {}
168 kz_odbc( const kz_odbc &src) : rc(SQL_SUCCESS), henv(0), hdbc(0), constr(src.constr), tranflg(src.tranflg) {}
169 kz_odbc( const char *l_constr, bool tranflg_) : rc(SQL_SUCCESS), henv(0), hdbc(0), constr(l_constr), tranflg(tranflg_) {}
170 kz_odbc( const std::string &l_constr, bool tranflg_) : rc(SQL_SUCCESS), henv(0), constr(l_constr.c_str()), tranflg(tranflg_) {}
171 void set_constr(const char *l_constr) { constr = l_constr; }
172 ~kz_odbc() { dissconnect(); }
173
174 void BeginTran() {
175 rc = SQLSetConnectAttr(hdbc, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF,0 );
176 }
177
178 void Commit() {
179 rc = SQLEndTran( SQL_HANDLE_DBC, hdbc, SQL_COMMIT);
180 }
181
182 void Rollback() {
183 rc = SQLEndTran( SQL_HANDLE_DBC, hdbc, SQL_ROLLBACK);
184 }
185
186 kz_string_array errors() {
187 kz_string_array result;
188 char state[6];
189 SQLINTEGER nerr;
190 char msg[SQL_MAX_MESSAGE_LENGTH+1];
191 SQLSMALLINT msglen;
192 msg[SQL_MAX_MESSAGE_LENGTH] = '\0';
193
194 while ( SQLError( henv, hdbc, 0, (SQLCHAR*)state, &nerr, (SQLCHAR*)msg, sizeof(msg), &msglen) == SQL_SUCCESS ) {
195 std::strstream err;
196 err << state << "(" << nerr << "):" << msg;
197 result.push_back( std::string(std::istreambuf_iterator<char>(err), std::istreambuf_iterator<char>()));
198 }
199 return result;
200 }
201 };
202
203 struct describe_col {
204 std::string name;
205 SQLSMALLINT typ;
206 SQLULEN siz;
207 SQLSMALLINT dd;
208 describe_col( char *name_, SQLSMALLINT typ_, SQLULEN siz_, SQLSMALLINT dd_) : name(name_), typ(typ_), siz(siz_), dd(dd_) {}
209 };
210
211 class kz_stmt {
212 kz_odbc *con;
213 RETCODE rc;
214 HSTMT hstmt;
215 const char* sql;
216 int count;
217 std::vector<char> buff;
218 std::vector<TIMESTAMP_STRUCT> tsary;
219 std::vector< describe_col > colinfo;
220
221 public:
222 bool iserr() { return !KZSQLSUCCESS(rc); }
223 void errclear() { rc = SQL_SUCCESS; count = 0; }
224
225 void freestmt() {
226 if ( hstmt ) { SQLFreeStmt(hstmt,SQL_DROP); hstmt = 0; count = 0; }
227 }
228 void allocstmt() {
229 errclear();
230 tsary.clear();
231 freestmt();
232 rc = SQLAllocStmt(con->hdbc,&hstmt);
233 }
234
235 void tables_(void) {
236 if ( !con ) return;
237 if ( !con->hdbc ) { con->connect(); }
238 allocstmt();
239 colinfo.clear();
240 rc = SQLTables( hstmt, 0, 0, 0, 0, 0, 0, (SQLCHAR*)" 'TABLE', 'VIEW', 'ALIAS', 'SYNONYM' ", SQL_NTS);
241 }
242
243 void columns_(const char*table) {
244 if ( !con ) return;
245 if ( !con->hdbc ) { con->connect(); }
246 allocstmt();
247 colinfo.clear();
248 rc = SQLColumns(hstmt, 0, 0, 0, 0, (SQLCHAR*)table, SQL_NTS, 0, 0);
249 }
250 private:
251 void execdirect(const char *sql) {
252 if ( !con ) return;
253 if ( !con->hdbc ) { con->connect(); }
254 allocstmt();
255 colinfo.clear();
256 this->sql = sql;
257 rc = SQLExecDirect(hstmt,(SQLCHAR*)sql,SQL_NTS);
258 }
259 void prepare(const char *sql) {
260 if ( count != 0 ) return;
261 if ( !con ) return;
262 if ( !con->hdbc ) { con->connect(); }
263 if ( iserr() ) return;
264 allocstmt();
265 if ( iserr() ) return;
266 colinfo.clear();
267 this->sql = sql;
268 rc = SQLPrepare(hstmt, (SQLCHAR*)sql, SQL_NTS);
269 }
270 void bind( int ValueType, int ParameterType, const void *param, int ColumnSize = 0, int DecimalDigits = 0) {
271 if ( count == 0 ) { count = 1; return; }
272 if ( iserr() ) return;
273 rc = SQLBindParameter(hstmt, count++, SQL_PARAM_INPUT, ValueType, ParameterType, ColumnSize, DecimalDigits, (SQLPOINTER)param, 0, 0);
274 }
275 void bind_null() {
276 static SQLLEN sqlnull = SQL_NULL_DATA;
277 if ( count == 0 ) { count = 1; return; }
278 if ( iserr() ) return;
279 rc = SQLBindParameter(hstmt, count++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, 0, 0, 0, 0, &sqlnull);
280 }
281
282 void execute() {
283 if ( iserr() ) return;
284 rc = SQLExecute(hstmt);
285 count = 0;
286 }
287 void get_colinfo() {
288 if ( !colinfo.empty() ) return;
289 if ( iserr() ) return;
290 SQLSMALLINT colnum;
291 SQLNumResultCols( hstmt, &colnum);
292 //colinfo.push_back( std::string() ); DescribeColとSQLGetDataの添え字は1始まりの為、ダミーを挿入していたが、添え字を調整することにする
293 for ( int i = 1; i <= colnum; i++ ) {
294 char cname[MAX_RESULT_COLUMN_NAME_LEN+1];
295 SQLSMALLINT cnamelength;
296 SQLSMALLINT ctype;
297 SQLULEN csize;
298 SQLSMALLINT cdd;
299 SQLDescribeCol( hstmt, i, (SQLCHAR*)cname, MAX_RESULT_COLUMN_NAME_LEN, &cnamelength, &ctype, &csize, &cdd, 0);
300 cname[MAX_RESULT_COLUMN_NAME_LEN] = '\0';
301 colinfo.push_back(describe_col(cname, ctype, csize, cdd));
302 }
303 }
304
305 public:
306 void get(size_t i, int &value, bool &nflg) {
307 SQLLEN siz;
308 rc = SQLGetData(hstmt, (SQLUSMALLINT)i, SQL_C_SLONG, &value, 0, &siz);
309 nflg = (siz == SQL_NULL_DATA);
310 }
311 void get(size_t i, unsigned int &value, bool &nflg) {
312 SQLLEN siz;
313 rc = SQLGetData(hstmt, (SQLUSMALLINT)i, SQL_C_ULONG, &value, 0, &siz);
314 nflg = (siz == SQL_NULL_DATA);
315 }
316 void get(size_t i, double &value, bool &nflg) {
317 SQLLEN siz;
318 rc = SQLGetData(hstmt, (SQLUSMALLINT)i, SQL_C_DOUBLE, &value, 0, &siz);
319 nflg = (siz == SQL_NULL_DATA);
320 }
321 private:
322 void get_buffer(size_t i, size_t &readtotal, bool &nflg) {
323 SQLLEN rbufsize = 0;
324 SQLLEN buffsize = (SQLLEN)buff.size(); // 読み込み可能なバッファサイズ
325 size_t ofs = 0;
326 readtotal = 0;
327 SQLSMALLINT ctype = SQL_C_CHAR;
328 SQLSMALLINT typ = colinfo[i-1].typ;
329 if ( typ == SQL_CHAR || typ == SQL_VARCHAR || typ == SQL_BINARY || typ == SQL_LONGVARCHAR ) ctype = SQL_C_BINARY;
330 while ( (rc = SQLGetData( hstmt, (SQLUSMALLINT)i, ctype , &buff[0] + ofs, buffsize, &rbufsize)) != SQL_NO_DATA && rbufsize >= buffsize ) {
331 size_t readsize = (rbufsize > buffsize) || (rbufsize == SQL_NO_TOTAL) ? buffsize : rbufsize;
332 readtotal += readsize;
333 ofs += readsize;
334 // Windows版のMySQL ODBC Driver で異常終了した為に、同じカラムに対して3回以上SQLGetDataを呼び出さないように2回目の呼び出しで確実に読み込めるようにバッファサイズを調整する。
335 // rbufsizeが本来必要なバッファサイズを返す
336 SQLLEN oldbufsize = (SQLLEN)buff.size();
337 SQLLEN newbufsize = (rbufsize > oldbufsize + buffsize) ? rbufsize : oldbufsize+ buffsize;
338 buffsize = newbufsize - oldbufsize; // 読み込み可能(あいている)バッファサイズ
339 buff.resize( newbufsize, 0);
340 }
341 readtotal += rbufsize;
342 if ( rbufsize == SQL_NULL_DATA ) {
343 // NULL は空文字にする
344 buff[0] = '\0';
345 readtotal = 1;
346 }
347 nflg = (rbufsize == SQL_NULL_DATA);
348 }
349 public:
350 void get(size_t i, std::string &str, bool &nflg) {
351 size_t readtotal = 0;
352 get_buffer(i, readtotal, nflg);
353 str = std::string(buff.begin(), buff.begin() + readtotal);
354 }
355
356 bool fetch() {
357 rc=SQLFetch(hstmt);
358 return rc == SQL_SUCCESS;
359 };
360
361 private:
362 kz_resultset resultset() {
363 get_colinfo();
364 kz_resultset result;
365 // SQLGetDataのiは1始まりだが、colinfoは0始まりで格納している。
366 for ( int i = 1; (size_t)i < colinfo.size() + 1; i++ ) {
367 size_t readtotal = 0;
368 bool nflg = false;
369 get_buffer(i, readtotal, nflg);
370 result.insert( kz_resultset_pair(colinfo[i-1].name, std::string(buff.begin(), buff.begin() + readtotal)));
371 }
372 return result;
373 }
374
375 kz_resultset_array resultset_array() {
376 kz_resultset_array result;
377 if ( iserr() ) return result;
378 for (rc=SQLFetch(hstmt); rc == SQL_SUCCESS; rc=SQLFetch(hstmt)) {
379 result.push_back( resultset() );
380 }
381 return result;
382 }
383
384
385 kz_string_array resultset_string() {
386 kz_string_array result;
387 rc=SQLFetch(hstmt);
388 if ( rc != SQL_SUCCESS ) return result;
389 get_colinfo();
390 // SQLGetDataのiは1始まりだが、colinfoは0始まりで格納している。
391 for ( int i = 1; (size_t)i < colinfo.size() + 1; i++ ) {
392 size_t readtotal = 0;
393 bool nflg = false;
394 get_buffer(i, readtotal, nflg);
395 result.push_back(std::string(buff.begin(), buff.begin() + readtotal));
396 }
397 return result;
398 }
399
400 public:
401 kz_stmt() : con(0), rc(SQL_SUCCESS), hstmt(0), count(0), sql(""), buff(1024,0), tsary(), colinfo() {}
402 kz_stmt(kz_odbc *con_) : con(con_), rc(SQL_SUCCESS), hstmt(0), count(0), sql(""), buff(1024,0), tsary(), colinfo() {}
403 ~kz_stmt() { /*freestmt();*/ } // connectionと同期を取っていないのでこれが呼ばれたときに落ちる場合がある(stmtの開放が問題になる場合は直接freestmtを呼び出すようにすること。
404 void setODBC(kz_odbc *con_) { con = con_; }
405
406 kz_stmt& exec(const char *sql ) { execdirect(sql); return *this; }
407 kz_resultset_array select( const char *sql ) { execdirect(sql); return resultset_array(); }
408 kz_resultset_array tables() { tables_(); return resultset_array(); }
409 kz_resultset_array columns( const char *table) { columns_(table); return resultset_array(); }
410 kz_stmt& operator , ( const std::string &param) { *this, param.c_str(); return *this;}
411 kz_stmt& operator , ( const char *param ) { prepare(param); bind( SQL_C_CHAR, SQL_VARCHAR, param); return *this; }
412 kz_stmt& operator , ( const unsigned char &param ) { bind( SQL_C_UTINYINT, SQL_CHAR, &param); return *this; }
413 kz_stmt& operator , ( const short &param ) { bind( SQL_C_SSHORT, SQL_SMALLINT, &param); return *this; }
414 kz_stmt& operator , ( const unsigned short &param ) { bind( SQL_C_USHORT, SQL_SMALLINT, &param); return *this; }
415 kz_stmt& operator , ( const int &param ) { bind( SQL_C_SLONG, SQL_INTEGER, &param); return *this; }
416 kz_stmt& operator , ( const unsigned int &param ) { bind( SQL_C_ULONG, SQL_INTEGER, &param); return *this; }
417 kz_stmt& operator , ( const long &param ) { bind( SQL_C_SLONG, SQL_INTEGER, &param); return *this; }
418 kz_stmt& operator , ( const unsigned long &param ) { bind( SQL_C_ULONG, SQL_INTEGER, &param); return *this; }
419 kz_stmt& operator , ( const float &param ) { bind( SQL_C_FLOAT, SQL_FLOAT, &param); return *this; }
420 kz_stmt& operator , ( const double &param ) { bind( SQL_C_DOUBLE, SQL_DOUBLE, &param); return *this; }
421 kz_stmt& operator , ( const struct tm &param ) {
422 tsary.push_back(TIMESTAMP_STRUCT());
423 TIMESTAMP_STRUCT &ts = tsary.back();
424 ts.year = param.tm_year + 1900;
425 ts.month = param.tm_mon + 1;
426 ts.day = param.tm_mday;
427 ts.hour = param.tm_hour;
428 ts.minute = param.tm_min;
429 ts.second = param.tm_sec;
430 bind( SQL_C_TIMESTAMP, SQL_TIMESTAMP, &(tsary.back()));
431 return *this;
432 }
433 kz_stmt& operator , ( const struct tm *param ) { return *this, *param; }
434 kz_stmt& operator , (const kz_odbc_marker_end_sql& dmy) { execute(); return *this; }
435 kz_resultset_array operator , (const kz_odbc_marker_end_sql_resultsets& dmy) { execute(); return resultset_array(); }
436 kz_stmt& operator , ( const kz_odbc_maker_null& dmy) { bind_null(); return *this; }
437
438 kz_string_array next() { return resultset_string();}
439 std::vector< describe_col > &cinfo() { get_colinfo(); return this->colinfo;}
440
441 kz_string_array errors() {
442 kz_string_array result;
443 char state[6];
444 SQLINTEGER nerr;
445 char msg[SQL_MAX_MESSAGE_LENGTH+1];
446 SQLSMALLINT msglen;
447 msg[SQL_MAX_MESSAGE_LENGTH] = '\0';
448
449 while ( SQLError( con->henv, con->hdbc, hstmt, (SQLCHAR*)state, &nerr, (SQLCHAR*)msg, sizeof(msg), &msglen) == SQL_SUCCESS ) {
450 std::strstream err;
451 err << state << "(" << nerr << "):" << msg;
452 result.push_back( std::string(std::istreambuf_iterator<char>(err), std::istreambuf_iterator<char>()));
453 }
454 if ( !result.empty() && hstmt && sql != 0 ) {
455 result.push_back( std::string("Last sql:") + sql);
456 }
457 return result;
458 }
459
460 };
461
462 #endif

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26