|
|
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.13 ! paf 8: static const char *RCSId="$Id: parser3odbc.C,v 1.12 2002/12/09 12:33:28 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.12 paf 195: SQL_Error sql_error;
196: #define CHECK(afailed) if(afailed) services._throw(sql_error)
197:
1.1 parser 198: for(int i=0; i<column_count; i++){
199: CString string;
200: CODBCFieldInfo fieldinfo;
201: rs.GetODBCFieldInfo(i, fieldinfo);
1.10 paf 202: column_types[i]=fieldinfo.m_nSQLType;
1.1 parser 203: size_t size=fieldinfo.m_strName.GetLength();
204: void *ptr=0;
205: if(size) {
206: ptr=services.malloc(size);
207: memcpy(ptr, (char *)LPCTSTR(fieldinfo.m_strName), size);
208: }
1.12 paf 209: CHECK(handlers.add_column(sql_error, ptr, size));
1.1 parser 210: }
211:
1.12 paf 212: CHECK(handlers.before_rows(sql_error));
1.1 parser 213:
214: unsigned long row=0;
1.10 paf 215: CDBVariant v;
216: CString s;
1.1 parser 217: while(!rs.IsEOF() && (!limit||(row<offset+limit))) {
218: if(row>=offset) {
1.12 paf 219: CHECK(handlers.add_row(sql_error));
1.10 paf 220: for(int i=0; i<column_count; i++) {
1.6 paf 221: size_t size;
222: void *ptr;
1.10 paf 223: switch(column_types[i]) {
1.7 paf 224: //case xBOOL:
225: // case SQL_INTEGER: // serg@design.ru did that in parser2. test first!
226: //case SQL_DATETIME:
1.11 paf 227: case SQL_BINARY:
228: case SQL_VARBINARY:
229: case SQL_LONGVARBINARY:
1.7 paf 230: case SQL_SMALLDATETIME:
1.10 paf 231: rs.GetFieldValue(i, v);
232: getFromDBVariant(services, v, ptr, size);
233: break;
1.7 paf 234: default:
1.10 paf 235: rs.GetFieldValue(i, s);
236: getFromString(services, s, ptr, size);
237: break;
1.1 parser 238: }
1.12 paf 239: CHECK(handlers.add_row_cell(sql_error, ptr, size));
1.1 parser 240: }
241: }
242: rs.MoveNext(); row++;
243: }
244:
245: rs.Close();
246: } else {
247: db->ExecuteSQL(statement);
248: }
1.9 paf 249: } CATCH_ALL (e) {
1.1 parser 250: _throw(services, e);
1.9 paf 251: } END_CATCH_ALL
1.7 paf 252: }
253:
254: void getFromDBVariant(SQL_Driver_services& services, CDBVariant& v, void *& ptr, size_t& size) {
255: switch(v.m_dwType) {
256: case DBVT_NULL: // No union member is valid for access.
257: ptr=0;
258: size=0;
259: break;
260: /* case DBVT_BOOL:
261: ptr=v.m_boolVal?"1":"0";
262: size=1;
263: break;*/
264: /* case DBVT_UCHAR:
265: size=strlen(ptr=v.m_chVal);
266: break;
267: case DBVT_SHORT:
268: char buf[MAX_NUMBER];
269: size=snprintf(HEAPIZE buf, "%d", v.m_iVal);
270: break;*/
271: /* case DBVT_LONG:
272: {
273: char local_buf[MAX_NUMBER];
274: size=snprintf(local_buf, MAX_NUMBER, "%ld", v.m_lVal);
275: ptr=services.malloc(size);
276: memcpy(ptr, local_buf, size);
277: break;
278: }*/
279: /*case DBVT_SINGLE:
280: m_fltVal
281: break;
282: case DBVT_DOUBLE m_dblVal
1.11 paf 283: case DBVT_STRING m_pstring */
284: case DBVT_BINARY:
285: {
286: if(size=v.m_pbinary->m_dwDataLength) {
287: ptr=services.malloc(size);
288: memcpy(ptr, ::GlobalLock(v.m_pbinary->m_hData), size);
289: ::GlobalUnlock(v.m_pbinary->m_hData);
290: } else
291: ptr=0;
292: break;
293: }
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);
306: ptr=services.malloc(size);
307: memcpy(ptr, local_buf, size);
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:
318: void getFromString(SQL_Driver_services& services, CString& s, void *& ptr, size_t& size) {
319: if(s.IsEmpty()) {
320: ptr=0;
321: size=0;
322: } else {
323: const char *cstr=LPCTSTR(s);
324: size=strlen(cstr); //string.GetLength() works wrong with non-string types:
325: ptr=services.malloc(size);
326: memcpy(ptr, cstr, size);
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: }