|
|
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.16 ! paf 8: static const char *RCSId="$Id: parser3odbc.C,v 1.15.2.1 2003/10/31 14:09:18 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:
1.16 ! paf 21: #define WINVER 0x0400
1.1 parser 22: #include <AFXDB.H>
23:
1.10 paf 24: // defines
25:
26: #define MAX_COLS 500
27:
1.1 parser 28: #define MAX_STRING 0x400
1.7 paf 29: #define MAX_NUMBER 40
30:
1.10 paf 31: // new in MSSQL2000, no MFC constants
1.7 paf 32: #ifndef SQL_NVARCHAR
33: #define SQL_NVARCHAR (-9)
34: #endif
35: #ifndef SQL_NTEXT
36: #define SQL_NTEXT (-10)
37: #endif
38: #ifndef SQL_SMALLDATETIME
39: #define SQL_SMALLDATETIME 11
40: #endif
41: // create table test (id int, a smalldatetime, b ntext, c nvarchar(100))
1.1 parser 42:
43: #define snprintf _snprintf
44: #ifndef strncasecmp
45: # define strncasecmp _strnicmp
46: #endif
47:
48: static char *lsplit(char *string, char delim) {
49: if(string) {
50: char *v=strchr(string, delim);
51: if(v) {
52: *v=0;
53: return v+1;
54: }
55: }
56: return 0;
57: }
58:
59: /**
60: ODBC server driver
61: */
62: class ODBC_Driver : public SQL_Driver {
63: public:
64:
65: ODBC_Driver() : SQL_Driver() {
66: }
67:
68: /// get api version
69: int api_version() { return SQL_DRIVER_API_VERSION; }
1.3 paf 70: const char *initialize(char *dlopen_file_spec) { return 0; }
1.1 parser 71: /** connect
72: @param used_only_in_connect_url
73: format: @b DSN=dsn;UID=user;PWD=password (ODBC connect string)
74: WARNING: must be used only to connect, for buffer doesn't live long
75: */
76: void connect(
77: char *used_only_in_connect_url,
78: SQL_Driver_services& services,
79: void **connection ///< output: CDatabase *
80: ) {
81: // _asm int 3;
82: CDatabase *db;
83: TRY {
84: db=new CDatabase();
85: db->OpenEx(used_only_in_connect_url, CDatabase::noOdbcDialog);
86: db->BeginTrans();
87: }
88: CATCH_ALL (e) {
89: _throw(services, e);
90: db=0; // calm, compiler
91: }
92: END_CATCH_ALL
93:
94: *(CDatabase **)connection=db;
95: }
96: void disconnect(void *connection) {
97: CDatabase *db=static_cast<CDatabase *>(connection);
98: TRY
99: delete db;
100: CATCH_ALL (e) {
101: // nothing
102: }
103: END_CATCH_ALL
104: }
105: void commit(SQL_Driver_services& services, void *connection) {
106: CDatabase *db=static_cast<CDatabase *>(connection);
107: TRY
108: db->CommitTrans();
109: db->BeginTrans();
110: CATCH_ALL (e) {
111: _throw(services, e);
112: }
113: END_CATCH_ALL
114: }
115: void rollback(SQL_Driver_services& services, void *connection) {
116: CDatabase *db=static_cast<CDatabase *>(connection);
117: TRY
118: db->Rollback();
119: db->BeginTrans();
120: CATCH_ALL (e) {
121: _throw(services, e);
122: }
123: END_CATCH_ALL
124: }
125:
126: bool ping(SQL_Driver_services&, void *connection) {
127: return true;
128: }
129:
1.14 paf 130: const char* quote(
131: SQL_Driver_services& services, void *connection,
132: const char *from, unsigned int length) {
133: char *result=(char*)services.malloc_atomic(length*2+1);
134: char *to=result;
135: while(length--) {
136: if(*from=='\'') { // ' -> ''
1.15 paf 137: *to++='\'';
1.3 paf 138: }
1.14 paf 139: *to++=*from++;
140: }
141: *to=0;
142: return result;
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();
1.14 paf 204: char *str=0;
1.1 parser 205: if(size) {
1.14 paf 206: str=(char*)services.malloc_atomic(size+1);
207: memcpy(str, (char *)LPCTSTR(fieldinfo.m_strName), size+1);
1.1 parser 208: }
1.14 paf 209: CHECK(handlers.add_column(sql_error, str, 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;
1.14 paf 222: char* str;
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!
1.14 paf 226: //case SQL_DATETIME: << default: handles that more properly (?)
227: case SQL_BINARY:
1.11 paf 228: case SQL_VARBINARY:
229: case SQL_LONGVARBINARY:
1.7 paf 230: case SQL_SMALLDATETIME:
1.10 paf 231: rs.GetFieldValue(i, v);
1.14 paf 232: getFromDBVariant(services, v, str, size);
1.10 paf 233: break;
1.7 paf 234: default:
1.10 paf 235: rs.GetFieldValue(i, s);
1.14 paf 236: getFromString(services, s, str, size);
1.10 paf 237: break;
1.1 parser 238: }
1.14 paf 239: CHECK(handlers.add_row_cell(sql_error, str, 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:
1.14 paf 254: void getFromDBVariant(SQL_Driver_services& services, CDBVariant& v, char*& str, size_t& size) {
1.7 paf 255: switch(v.m_dwType) {
1.14 paf 256: case DBVT_BINARY: /* << would cause problems with current String implementation
257: now falling into NULL case, effectively ignoring such columns [not failing]
258: {
259: if(size=v.m_pbinary->m_dwDataLength) {
260: str=services.malloc_atomic(size+1);
261: memcpy(ptr, ::GlobalLock(v.m_pbinary->m_hData), size);
262: ::GlobalUnlock(v.m_pbinary->m_hData);
263: } else
264: str=0;
265: break;
266: }*/
1.7 paf 267: case DBVT_NULL: // No union member is valid for access.
1.14 paf 268: str=0;
1.7 paf 269: size=0;
270: break;
271: /* case DBVT_BOOL:
272: ptr=v.m_boolVal?"1":"0";
273: size=1;
274: break;*/
275: /* case DBVT_UCHAR:
276: size=strlen(ptr=v.m_chVal);
277: break;
278: case DBVT_SHORT:
279: char buf[MAX_NUMBER];
280: size=snprintf(HEAPIZE buf, "%d", v.m_iVal);
281: break;*/
282: /* case DBVT_LONG:
283: {
284: char local_buf[MAX_NUMBER];
285: size=snprintf(local_buf, MAX_NUMBER, "%ld", v.m_lVal);
1.14 paf 286: ptr=services.malloc_atomic(size);
1.7 paf 287: memcpy(ptr, local_buf, size);
288: break;
289: }*/
290: /*case DBVT_SINGLE:
291: m_fltVal
292: break;
293: case DBVT_DOUBLE m_dblVal
1.14 paf 294: case DBVT_STRING m_pstring */
1.7 paf 295: case DBVT_DATE:
296: {
297: char local_buf[MAX_STRING];
298: size=snprintf(local_buf, MAX_STRING,
299: "%04d-%02d-%02d %02d:%02d:%02d.%03d",
300: v.m_pdate->year,
301: v.m_pdate->month,
302: v.m_pdate->day,
303: v.m_pdate->hour,
304: v.m_pdate->minute,
305: v.m_pdate->second,
306: v.m_pdate->fraction);
1.14 paf 307: str=(char*)services.malloc_atomic(size+1);
308: memcpy(str, local_buf, size+1);
1.7 paf 309: break;
310: }
311: default:
312: char msg[MAX_STRING];
313: snprintf(msg, MAX_STRING, "unknown column return variant type (%d)",
314: v.m_dwType);
315: services._throw(msg);
316: }
317: }
318:
1.14 paf 319: void getFromString(SQL_Driver_services& services, CString& s, char*& astr, size_t& size) {
1.7 paf 320: if(s.IsEmpty()) {
1.14 paf 321: astr=0;
1.7 paf 322: size=0;
323: } else {
324: const char *cstr=LPCTSTR(s);
325: size=strlen(cstr); //string.GetLength() works wrong with non-string types:
1.14 paf 326: astr=(char*)services.malloc_atomic(size+1);
327: memcpy(astr, cstr, size+1);
1.7 paf 328: }
1.1 parser 329: }
330:
331: void _throw(SQL_Driver_services& services, CException *e) {
332: char szCause[MAX_STRING]; szCause[0]=0;
333: e->GetErrorMessage(szCause, MAX_STRING);
334: char msg[MAX_STRING];
335: snprintf(msg, MAX_STRING, "%s: %s",
336: e->GetRuntimeClass()->m_lpszClassName,
337: *szCause?szCause:"unknown");
338: services._throw(msg);
339: }
340:
341: };
342:
343: extern "C" SQL_Driver *SQL_DRIVER_CREATE() {
344: return new ODBC_Driver();
345: }