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