|
|
1.1 parser 1: /** @file
2: Parser ODBC driver.
3:
1.5 paf 4: Copyright(c) 2001, 2002 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.11 ! paf 8: static const char *RCSId="$Id: parser3odbc.C,v 1.10 2002/10/22 10:12:46 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:
129: unsigned int quote(
130: SQL_Driver_services&, void *connection,
131: char *to, const char *from, unsigned int length) {
1.3 paf 132: if(to) { // store mode
133: unsigned int result=length;
134: while(length--) {
135: if(*from=='\'') { // ' -> ''
136: *to++='\''; result++;
137: }
138: *to++=*from++;
139: }
140: return result;
141: } else // estimate mode
142: return length*2;
1.1 parser 143: }
144: void query(
145: SQL_Driver_services& services, void *connection,
146: const char *statement, unsigned long offset, unsigned long limit,
147: SQL_Driver_query_event_handlers& handlers) {
148:
149: CDatabase *db=static_cast<CDatabase *>(connection);
150:
151: while(isspace(*statement))
152: statement++;
153:
154: TRY {
1.8 paf 155: // mk:@MSITStore:C:\Program%20Files\Microsoft%20SQL%20Server\80\Tools\Books\adosql.chm::/adoprg02_4g33.htm
156: // Server cursors are created only for statements that begin with:
157: // SELECT
158: // EXEC[ute] procedure_name
159: // call procedure_name
160: // mk:@MSITStore:C:\Program%20Files\Microsoft%20SQL%20Server\80\Tools\Books\odbcsql.chm::/od_6_035_5dnp.htm
161: // The ODBC CALL escape sequence for calling a procedure is:
162: // {[?=]call procedure_name[([parameter][,[parameter]]...)]}
163: if(strncasecmp(statement, "select", 6)==0
164: || strncasecmp(statement, "EXEC", 4)==0
165: || strncasecmp(statement, "call", 4)==0
166: || strncasecmp(statement, "{", 1)==0) {
1.1 parser 167: CRecordset rs(db);
1.9 paf 168: TRY {
169: rs.Open(
170: CRecordset::forwardOnly,
171: statement,
172: CRecordset::executeDirect
173: );
174: } CATCH_ALL (e) {
175: // could not fetch a table
176: TRY {
177: // then try resultless query
178: db->ExecuteSQL(statement);
179: // OK then
180: return;
181: } CATCH_ALL (e2) {
182: // still nothing good
183: _throw(services, e); // throw ORIGINAL exception
184: } END_CATCH_ALL
185: } END_CATCH_ALL
1.1 parser 186:
187: int column_count=rs.GetODBCFieldCount();
188: if(!column_count)
189: services._throw("result contains no columns");
190:
1.10 paf 191: SWORD column_types[MAX_COLS];
192: if(column_count>MAX_COLS)
193: column_count=MAX_COLS;
194:
1.1 parser 195: for(int i=0; i<column_count; i++){
196: CString string;
197: CODBCFieldInfo fieldinfo;
198: rs.GetODBCFieldInfo(i, fieldinfo);
1.10 paf 199: column_types[i]=fieldinfo.m_nSQLType;
1.1 parser 200: size_t size=fieldinfo.m_strName.GetLength();
201: void *ptr=0;
202: if(size) {
203: ptr=services.malloc(size);
204: memcpy(ptr, (char *)LPCTSTR(fieldinfo.m_strName), size);
205: }
206: handlers.add_column(ptr, size);
207: }
208:
209: handlers.before_rows();
210:
211: unsigned long row=0;
1.10 paf 212: CDBVariant v;
213: CString s;
1.1 parser 214: while(!rs.IsEOF() && (!limit||(row<offset+limit))) {
215: if(row>=offset) {
216: handlers.add_row();
1.10 paf 217: for(int i=0; i<column_count; i++) {
1.6 paf 218: size_t size;
219: void *ptr;
1.10 paf 220: switch(column_types[i]) {
1.7 paf 221: //case xBOOL:
222: // case SQL_INTEGER: // serg@design.ru did that in parser2. test first!
223: //case SQL_DATETIME:
1.11 ! paf 224: case SQL_BINARY:
! 225: case SQL_VARBINARY:
! 226: case SQL_LONGVARBINARY:
1.7 paf 227: case SQL_SMALLDATETIME:
1.10 paf 228: rs.GetFieldValue(i, v);
229: getFromDBVariant(services, v, ptr, size);
230: break;
1.7 paf 231: default:
1.10 paf 232: rs.GetFieldValue(i, s);
233: getFromString(services, s, ptr, size);
234: break;
1.1 parser 235: }
236: handlers.add_row_cell(ptr, size);
237: }
238: }
239: rs.MoveNext(); row++;
240: }
241:
242: rs.Close();
243: } else {
244: db->ExecuteSQL(statement);
245: }
1.9 paf 246: } CATCH_ALL (e) {
1.1 parser 247: _throw(services, e);
1.9 paf 248: } END_CATCH_ALL
1.7 paf 249: }
250:
251: void getFromDBVariant(SQL_Driver_services& services, CDBVariant& v, void *& ptr, size_t& size) {
252: switch(v.m_dwType) {
253: case DBVT_NULL: // No union member is valid for access.
254: ptr=0;
255: size=0;
256: break;
257: /* case DBVT_BOOL:
258: ptr=v.m_boolVal?"1":"0";
259: size=1;
260: break;*/
261: /* case DBVT_UCHAR:
262: size=strlen(ptr=v.m_chVal);
263: break;
264: case DBVT_SHORT:
265: char buf[MAX_NUMBER];
266: size=snprintf(HEAPIZE buf, "%d", v.m_iVal);
267: break;*/
268: /* case DBVT_LONG:
269: {
270: char local_buf[MAX_NUMBER];
271: size=snprintf(local_buf, MAX_NUMBER, "%ld", v.m_lVal);
272: ptr=services.malloc(size);
273: memcpy(ptr, local_buf, size);
274: break;
275: }*/
276: /*case DBVT_SINGLE:
277: m_fltVal
278: break;
279: case DBVT_DOUBLE m_dblVal
1.11 ! paf 280: case DBVT_STRING m_pstring */
! 281: case DBVT_BINARY:
! 282: {
! 283: if(size=v.m_pbinary->m_dwDataLength) {
! 284: ptr=services.malloc(size);
! 285: memcpy(ptr, ::GlobalLock(v.m_pbinary->m_hData), size);
! 286: ::GlobalUnlock(v.m_pbinary->m_hData);
! 287: } else
! 288: ptr=0;
! 289: break;
! 290: }
1.7 paf 291: case DBVT_DATE:
292: {
293: char local_buf[MAX_STRING];
294: size=snprintf(local_buf, MAX_STRING,
295: "%04d-%02d-%02d %02d:%02d:%02d.%03d",
296: v.m_pdate->year,
297: v.m_pdate->month,
298: v.m_pdate->day,
299: v.m_pdate->hour,
300: v.m_pdate->minute,
301: v.m_pdate->second,
302: v.m_pdate->fraction);
303: ptr=services.malloc(size);
304: memcpy(ptr, local_buf, size);
305: break;
306: }
307: default:
308: char msg[MAX_STRING];
309: snprintf(msg, MAX_STRING, "unknown column return variant type (%d)",
310: v.m_dwType);
311: services._throw(msg);
312: }
313: }
314:
315: void getFromString(SQL_Driver_services& services, CString& s, void *& ptr, size_t& size) {
316: if(s.IsEmpty()) {
317: ptr=0;
318: size=0;
319: } else {
320: const char *cstr=LPCTSTR(s);
321: size=strlen(cstr); //string.GetLength() works wrong with non-string types:
322: ptr=services.malloc(size);
323: memcpy(ptr, cstr, size);
324: }
1.1 parser 325: }
326:
327: void _throw(SQL_Driver_services& services, CException *e) {
328: char szCause[MAX_STRING]; szCause[0]=0;
329: e->GetErrorMessage(szCause, MAX_STRING);
330: char msg[MAX_STRING];
331: snprintf(msg, MAX_STRING, "%s: %s",
332: e->GetRuntimeClass()->m_lpszClassName,
333: *szCause?szCause:"unknown");
334: services._throw(msg);
335: }
336:
337: };
338:
339: extern "C" SQL_Driver *SQL_DRIVER_CREATE() {
340: return new ODBC_Driver();
341: }