|
|
1.1 parser 1: /** @file
2: Parser ODBC driver.
3:
1.13 paf 4: Copyright(c) 2001, 2003 ArtLebedev Group (http://www.artlebedev.com)
1.1 parser 5:
1.5 paf 6: Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
1.1 parser 7: */
1.14 ! paf 8: static const char *RCSId="$Id: parser3odbc.C,v 1.13.6.2 2003/07/22 11:23:09 paf Exp $";
1.1 parser 9:
10: #ifndef _MSC_VER
11: # error compile ISAPI module with MSVC [no urge for now to make it autoconf-ed (PAF)]
12: #endif
13:
14: #include <string.h>
15: #include <stdio.h>
16: #include <stdlib.h>
1.2 paf 17: #include <setjmp.h>
1.1 parser 18:
19: #include "pa_sql_driver.h"
20:
21: #include <AFXDB.H>
22:
1.10 paf 23: // defines
24:
25: #define MAX_COLS 500
26:
1.1 parser 27: #define MAX_STRING 0x400
1.7 paf 28: #define MAX_NUMBER 40
29:
1.10 paf 30: // new in MSSQL2000, no MFC constants
1.7 paf 31: #ifndef SQL_NVARCHAR
32: #define SQL_NVARCHAR (-9)
33: #endif
34: #ifndef SQL_NTEXT
35: #define SQL_NTEXT (-10)
36: #endif
37: #ifndef SQL_SMALLDATETIME
38: #define SQL_SMALLDATETIME 11
39: #endif
40: // create table test (id int, a smalldatetime, b ntext, c nvarchar(100))
1.1 parser 41:
42: #define snprintf _snprintf
43: #ifndef strncasecmp
44: # define strncasecmp _strnicmp
45: #endif
46:
47: static char *lsplit(char *string, char delim) {
48: if(string) {
49: char *v=strchr(string, delim);
50: if(v) {
51: *v=0;
52: return v+1;
53: }
54: }
55: return 0;
56: }
57:
58: /**
59: ODBC server driver
60: */
61: class ODBC_Driver : public SQL_Driver {
62: public:
63:
64: ODBC_Driver() : SQL_Driver() {
65: }
66:
67: /// get api version
68: int api_version() { return SQL_DRIVER_API_VERSION; }
1.3 paf 69: const char *initialize(char *dlopen_file_spec) { return 0; }
1.1 parser 70: /** connect
71: @param used_only_in_connect_url
72: format: @b DSN=dsn;UID=user;PWD=password (ODBC connect string)
73: WARNING: must be used only to connect, for buffer doesn't live long
74: */
75: void connect(
76: char *used_only_in_connect_url,
77: SQL_Driver_services& services,
78: void **connection ///< output: CDatabase *
79: ) {
80: // _asm int 3;
81: CDatabase *db;
82: TRY {
83: db=new CDatabase();
84: db->OpenEx(used_only_in_connect_url, CDatabase::noOdbcDialog);
85: db->BeginTrans();
86: }
87: CATCH_ALL (e) {
88: _throw(services, e);
89: db=0; // calm, compiler
90: }
91: END_CATCH_ALL
92:
93: *(CDatabase **)connection=db;
94: }
95: void disconnect(void *connection) {
96: CDatabase *db=static_cast<CDatabase *>(connection);
97: TRY
98: delete db;
99: CATCH_ALL (e) {
100: // nothing
101: }
102: END_CATCH_ALL
103: }
104: void commit(SQL_Driver_services& services, void *connection) {
105: CDatabase *db=static_cast<CDatabase *>(connection);
106: TRY
107: db->CommitTrans();
108: db->BeginTrans();
109: CATCH_ALL (e) {
110: _throw(services, e);
111: }
112: END_CATCH_ALL
113: }
114: void rollback(SQL_Driver_services& services, void *connection) {
115: CDatabase *db=static_cast<CDatabase *>(connection);
116: TRY
117: db->Rollback();
118: db->BeginTrans();
119: CATCH_ALL (e) {
120: _throw(services, e);
121: }
122: END_CATCH_ALL
123: }
124:
125: bool ping(SQL_Driver_services&, void *connection) {
126: return true;
127: }
128:
1.14 ! paf 129: const char* quote(
! 130: SQL_Driver_services& services, void *connection,
! 131: const char *from, unsigned int length) {
! 132: char *result=(char*)services.malloc_atomic(length*2+1);
! 133: char *to=result;
! 134: while(length--) {
! 135: if(*from=='\'') { // ' -> ''
! 136: *to++='\''; result++;
1.3 paf 137: }
1.14 ! paf 138: *to++=*from++;
! 139: }
! 140: *to=0;
! 141: return result;
1.1 parser 142: }
143: void query(
144: SQL_Driver_services& services, void *connection,
145: const char *statement, unsigned long offset, unsigned long limit,
146: SQL_Driver_query_event_handlers& handlers) {
147:
148: CDatabase *db=static_cast<CDatabase *>(connection);
149:
150: while(isspace(*statement))
151: statement++;
152:
153: TRY {
1.8 paf 154: // mk:@MSITStore:C:\Program%20Files\Microsoft%20SQL%20Server\80\Tools\Books\adosql.chm::/adoprg02_4g33.htm
155: // Server cursors are created only for statements that begin with:
156: // SELECT
157: // EXEC[ute] procedure_name
158: // call procedure_name
159: // mk:@MSITStore:C:\Program%20Files\Microsoft%20SQL%20Server\80\Tools\Books\odbcsql.chm::/od_6_035_5dnp.htm
160: // The ODBC CALL escape sequence for calling a procedure is:
161: // {[?=]call procedure_name[([parameter][,[parameter]]...)]}
162: if(strncasecmp(statement, "select", 6)==0
163: || strncasecmp(statement, "EXEC", 4)==0
164: || strncasecmp(statement, "call", 4)==0
165: || strncasecmp(statement, "{", 1)==0) {
1.1 parser 166: CRecordset rs(db);
1.9 paf 167: TRY {
168: rs.Open(
169: CRecordset::forwardOnly,
170: statement,
171: CRecordset::executeDirect
172: );
173: } CATCH_ALL (e) {
174: // could not fetch a table
175: TRY {
176: // then try resultless query
177: db->ExecuteSQL(statement);
178: // OK then
179: return;
180: } CATCH_ALL (e2) {
181: // still nothing good
182: _throw(services, e); // throw ORIGINAL exception
183: } END_CATCH_ALL
184: } END_CATCH_ALL
1.1 parser 185:
186: int column_count=rs.GetODBCFieldCount();
187: if(!column_count)
188: services._throw("result contains no columns");
189:
1.10 paf 190: SWORD column_types[MAX_COLS];
191: if(column_count>MAX_COLS)
192: column_count=MAX_COLS;
193:
1.12 paf 194: SQL_Error sql_error;
195: #define CHECK(afailed) if(afailed) services._throw(sql_error)
196:
1.1 parser 197: for(int i=0; i<column_count; i++){
198: CString string;
199: CODBCFieldInfo fieldinfo;
200: rs.GetODBCFieldInfo(i, fieldinfo);
1.10 paf 201: column_types[i]=fieldinfo.m_nSQLType;
1.1 parser 202: size_t size=fieldinfo.m_strName.GetLength();
1.14 ! paf 203: char *str=0;
1.1 parser 204: if(size) {
1.14 ! paf 205: str=(char*)services.malloc_atomic(size+1);
! 206: memcpy(str, (char *)LPCTSTR(fieldinfo.m_strName), size+1);
1.1 parser 207: }
1.14 ! paf 208: CHECK(handlers.add_column(sql_error, str, size));
1.1 parser 209: }
210:
1.12 paf 211: CHECK(handlers.before_rows(sql_error));
1.1 parser 212:
213: unsigned long row=0;
1.10 paf 214: CDBVariant v;
215: CString s;
1.1 parser 216: while(!rs.IsEOF() && (!limit||(row<offset+limit))) {
217: if(row>=offset) {
1.12 paf 218: CHECK(handlers.add_row(sql_error));
1.10 paf 219: for(int i=0; i<column_count; i++) {
1.6 paf 220: size_t size;
1.14 ! paf 221: char* str;
1.10 paf 222: switch(column_types[i]) {
1.7 paf 223: //case xBOOL:
224: // case SQL_INTEGER: // serg@design.ru did that in parser2. test first!
1.14 ! paf 225: //case SQL_DATETIME: << default: handles that more properly (?)
! 226: case SQL_BINARY:
1.11 paf 227: case SQL_VARBINARY:
228: case SQL_LONGVARBINARY:
1.7 paf 229: case SQL_SMALLDATETIME:
1.10 paf 230: rs.GetFieldValue(i, v);
1.14 ! paf 231: getFromDBVariant(services, v, str, size);
1.10 paf 232: break;
1.7 paf 233: default:
1.10 paf 234: rs.GetFieldValue(i, s);
1.14 ! paf 235: getFromString(services, s, str, size);
1.10 paf 236: break;
1.1 parser 237: }
1.14 ! paf 238: CHECK(handlers.add_row_cell(sql_error, str, size));
1.1 parser 239: }
240: }
241: rs.MoveNext(); row++;
242: }
243:
244: rs.Close();
245: } else {
246: db->ExecuteSQL(statement);
247: }
1.9 paf 248: } CATCH_ALL (e) {
1.1 parser 249: _throw(services, e);
1.9 paf 250: } END_CATCH_ALL
1.7 paf 251: }
252:
1.14 ! paf 253: void getFromDBVariant(SQL_Driver_services& services, CDBVariant& v, char*& str, size_t& size) {
1.7 paf 254: switch(v.m_dwType) {
1.14 ! paf 255: case DBVT_BINARY: /* << would cause problems with current String implementation
! 256: now falling into NULL case, effectively ignoring such columns [not failing]
! 257: {
! 258: if(size=v.m_pbinary->m_dwDataLength) {
! 259: str=services.malloc_atomic(size+1);
! 260: memcpy(ptr, ::GlobalLock(v.m_pbinary->m_hData), size);
! 261: ::GlobalUnlock(v.m_pbinary->m_hData);
! 262: } else
! 263: str=0;
! 264: break;
! 265: }*/
1.7 paf 266: case DBVT_NULL: // No union member is valid for access.
1.14 ! paf 267: str=0;
1.7 paf 268: size=0;
269: break;
270: /* case DBVT_BOOL:
271: ptr=v.m_boolVal?"1":"0";
272: size=1;
273: break;*/
274: /* case DBVT_UCHAR:
275: size=strlen(ptr=v.m_chVal);
276: break;
277: case DBVT_SHORT:
278: char buf[MAX_NUMBER];
279: size=snprintf(HEAPIZE buf, "%d", v.m_iVal);
280: break;*/
281: /* case DBVT_LONG:
282: {
283: char local_buf[MAX_NUMBER];
284: size=snprintf(local_buf, MAX_NUMBER, "%ld", v.m_lVal);
1.14 ! paf 285: ptr=services.malloc_atomic(size);
1.7 paf 286: memcpy(ptr, local_buf, size);
287: break;
288: }*/
289: /*case DBVT_SINGLE:
290: m_fltVal
291: break;
292: case DBVT_DOUBLE m_dblVal
1.14 ! paf 293: case DBVT_STRING m_pstring */
1.7 paf 294: case DBVT_DATE:
295: {
296: char local_buf[MAX_STRING];
297: size=snprintf(local_buf, MAX_STRING,
298: "%04d-%02d-%02d %02d:%02d:%02d.%03d",
299: v.m_pdate->year,
300: v.m_pdate->month,
301: v.m_pdate->day,
302: v.m_pdate->hour,
303: v.m_pdate->minute,
304: v.m_pdate->second,
305: v.m_pdate->fraction);
1.14 ! paf 306: str=(char*)services.malloc_atomic(size+1);
! 307: memcpy(str, local_buf, size+1);
1.7 paf 308: break;
309: }
310: default:
311: char msg[MAX_STRING];
312: snprintf(msg, MAX_STRING, "unknown column return variant type (%d)",
313: v.m_dwType);
314: services._throw(msg);
315: }
316: }
317:
1.14 ! paf 318: void getFromString(SQL_Driver_services& services, CString& s, char*& astr, size_t& size) {
1.7 paf 319: if(s.IsEmpty()) {
1.14 ! paf 320: astr=0;
1.7 paf 321: size=0;
322: } else {
323: const char *cstr=LPCTSTR(s);
324: size=strlen(cstr); //string.GetLength() works wrong with non-string types:
1.14 ! paf 325: astr=(char*)services.malloc_atomic(size+1);
! 326: memcpy(astr, cstr, size+1);
1.7 paf 327: }
1.1 parser 328: }
329:
330: void _throw(SQL_Driver_services& services, CException *e) {
331: char szCause[MAX_STRING]; szCause[0]=0;
332: e->GetErrorMessage(szCause, MAX_STRING);
333: char msg[MAX_STRING];
334: snprintf(msg, MAX_STRING, "%s: %s",
335: e->GetRuntimeClass()->m_lpszClassName,
336: *szCause?szCause:"unknown");
337: services._throw(msg);
338: }
339:
340: };
341:
342: extern "C" SQL_Driver *SQL_DRIVER_CREATE() {
343: return new ODBC_Driver();
344: }