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