|
|
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.8 ! paf 8: static const char *RCSId="$Id: parser3odbc.C,v 1.7 2002/04/25 17:51:38 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);
164: rs.Open(
165: CRecordset::forwardOnly,
166: statement,
167: CRecordset::executeDirect
168: );
169:
170: int column_count=rs.GetODBCFieldCount();
171: if(!column_count)
172: services._throw("result contains no columns");
173:
174: for(int i=0; i<column_count; i++){
175: CString string;
176: CODBCFieldInfo fieldinfo;
177: rs.GetODBCFieldInfo(i, fieldinfo);
178: size_t size=fieldinfo.m_strName.GetLength();
179: void *ptr=0;
180: if(size) {
181: ptr=services.malloc(size);
182: memcpy(ptr, (char *)LPCTSTR(fieldinfo.m_strName), size);
183: }
184: handlers.add_column(ptr, size);
185: }
186:
187: handlers.before_rows();
188:
189: unsigned long row=0;
190: while(!rs.IsEOF() && (!limit||(row<offset+limit))) {
191: if(row>=offset) {
192: handlers.add_row();
193: for(int i=0; i<column_count; i++) {
1.7 paf 194: CODBCFieldInfo fieldinfo;
195: rs.GetODBCFieldInfo(i, fieldinfo);
1.6 paf 196: size_t size;
197: void *ptr;
1.7 paf 198: switch(fieldinfo.m_nSQLType) {
199: //case xBOOL:
200: // case SQL_INTEGER: // serg@design.ru did that in parser2. test first!
201: //case SQL_DATETIME:
202: case SQL_SMALLDATETIME:
203: {
204: CDBVariant v;
205: rs.GetFieldValue(i, v);
206: getFromDBVariant(services, v, ptr, size);
207: break;
208: }
209: default:
210: {
211: CString s;
212: rs.GetFieldValue(i, s);
213: getFromString(services, s, ptr, size);
214: break;
215: }
1.1 parser 216: }
217: handlers.add_row_cell(ptr, size);
218: }
219: }
220: rs.MoveNext(); row++;
221: }
222:
223: rs.Close();
224: } else {
225: db->ExecuteSQL(statement);
226: }
227: }
228: CATCH_ALL (e) {
229: _throw(services, e);
230: }
231: END_CATCH_ALL
1.7 paf 232: }
233:
234: void getFromDBVariant(SQL_Driver_services& services, CDBVariant& v, void *& ptr, size_t& size) {
235: switch(v.m_dwType) {
236: case DBVT_NULL: // No union member is valid for access.
237: ptr=0;
238: size=0;
239: break;
240: /* case DBVT_BOOL:
241: ptr=v.m_boolVal?"1":"0";
242: size=1;
243: break;*/
244: /* case DBVT_UCHAR:
245: size=strlen(ptr=v.m_chVal);
246: break;
247: case DBVT_SHORT:
248: char buf[MAX_NUMBER];
249: size=snprintf(HEAPIZE buf, "%d", v.m_iVal);
250: break;*/
251: /* case DBVT_LONG:
252: {
253: char local_buf[MAX_NUMBER];
254: size=snprintf(local_buf, MAX_NUMBER, "%ld", v.m_lVal);
255: ptr=services.malloc(size);
256: memcpy(ptr, local_buf, size);
257: break;
258: }*/
259: /*case DBVT_SINGLE:
260: m_fltVal
261: break;
262: case DBVT_DOUBLE m_dblVal
263: case DBVT_STRING m_pstring
264: case DBVT_BINARY m_pbinary */
265: case DBVT_DATE:
266: {
267: char local_buf[MAX_STRING];
268: size=snprintf(local_buf, MAX_STRING,
269: "%04d-%02d-%02d %02d:%02d:%02d.%03d",
270: v.m_pdate->year,
271: v.m_pdate->month,
272: v.m_pdate->day,
273: v.m_pdate->hour,
274: v.m_pdate->minute,
275: v.m_pdate->second,
276: v.m_pdate->fraction);
277: ptr=services.malloc(size);
278: memcpy(ptr, local_buf, size);
279: break;
280: }
281: default:
282: char msg[MAX_STRING];
283: snprintf(msg, MAX_STRING, "unknown column return variant type (%d)",
284: v.m_dwType);
285: services._throw(msg);
286: }
287: }
288:
289: void getFromString(SQL_Driver_services& services, CString& s, void *& ptr, size_t& size) {
290: if(s.IsEmpty()) {
291: ptr=0;
292: size=0;
293: } else {
294: const char *cstr=LPCTSTR(s);
295: size=strlen(cstr); //string.GetLength() works wrong with non-string types:
296: ptr=services.malloc(size);
297: memcpy(ptr, cstr, size);
298: }
1.1 parser 299: }
300:
301: void _throw(SQL_Driver_services& services, CException *e) {
302: char szCause[MAX_STRING]; szCause[0]=0;
303: e->GetErrorMessage(szCause, MAX_STRING);
304: char msg[MAX_STRING];
305: snprintf(msg, MAX_STRING, "%s: %s",
306: e->GetRuntimeClass()->m_lpszClassName,
307: *szCause?szCause:"unknown");
308: services._throw(msg);
309: }
310:
311: };
312:
313: extern "C" SQL_Driver *SQL_DRIVER_CREATE() {
314: return new ODBC_Driver();
315: }