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