|
|
| version 1.3, 2001/11/16 12:38:59 | version 1.15, 2003/09/29 06:14:16 |
|---|---|
| Line 1 | Line 1 |
| /** @file | /** @file |
| Parser ODBC driver. | Parser ODBC driver. |
| Copyright(c) 2001 ArtLebedev Group(http://www.artlebedev.com) | Copyright(c) 2001, 2003 ArtLebedev Group (http://www.artlebedev.com) |
| Author: Alexander Petrosyan <paf@design.ru>(http://design.ru/paf) | Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru) |
| */ | */ |
| static const char *RCSId="$Id$"; | static const char *RCSId="$Id$"; |
| Line 20 static const char *RCSId="$Id$"; | Line 20 static const char *RCSId="$Id$"; |
| #include <AFXDB.H> | #include <AFXDB.H> |
| // defines | |
| #define MAX_COLS 500 | |
| #define MAX_STRING 0x400 | #define MAX_STRING 0x400 |
| #define MAX_NUMBER 40 | |
| // new in MSSQL2000, no MFC constants | |
| #ifndef SQL_NVARCHAR | |
| #define SQL_NVARCHAR (-9) | |
| #endif | |
| #ifndef SQL_NTEXT | |
| #define SQL_NTEXT (-10) | |
| #endif | |
| #ifndef SQL_SMALLDATETIME | |
| #define SQL_SMALLDATETIME 11 | |
| #endif | |
| // create table test (id int, a smalldatetime, b ntext, c nvarchar(100)) | |
| #define snprintf _snprintf | #define snprintf _snprintf |
| #ifndef strncasecmp | #ifndef strncasecmp |
| Line 109 public: | Line 126 public: |
| return true; | return true; |
| } | } |
| unsigned int quote( | const char* quote( |
| SQL_Driver_services&, void *connection, | SQL_Driver_services& services, void *connection, |
| char *to, const char *from, unsigned int length) { | const char *from, unsigned int length) { |
| if(to) { // store mode | char *result=(char*)services.malloc_atomic(length*2+1); |
| unsigned int result=length; | char *to=result; |
| while(length--) { | while(length--) { |
| if(*from=='\'') { // ' -> '' | if(*from=='\'') { // ' -> '' |
| *to++='\''; result++; | *to++='\''; |
| } | |
| *to++=*from++; | |
| } | } |
| return result; | *to++=*from++; |
| } else // estimate mode | } |
| return length*2; | *to=0; |
| return result; | |
| } | } |
| void query( | void query( |
| SQL_Driver_services& services, void *connection, | SQL_Driver_services& services, void *connection, |
| Line 135 public: | Line 151 public: |
| statement++; | statement++; |
| TRY { | TRY { |
| if(strncasecmp(statement, "select", 6)==0) { | // mk:@MSITStore:C:\Program%20Files\Microsoft%20SQL%20Server\80\Tools\Books\adosql.chm::/adoprg02_4g33.htm |
| // Server cursors are created only for statements that begin with: | |
| // SELECT | |
| // EXEC[ute] procedure_name | |
| // call procedure_name | |
| // mk:@MSITStore:C:\Program%20Files\Microsoft%20SQL%20Server\80\Tools\Books\odbcsql.chm::/od_6_035_5dnp.htm | |
| // The ODBC CALL escape sequence for calling a procedure is: | |
| // {[?=]call procedure_name[([parameter][,[parameter]]...)]} | |
| if(strncasecmp(statement, "select", 6)==0 | |
| || strncasecmp(statement, "EXEC", 4)==0 | |
| || strncasecmp(statement, "call", 4)==0 | |
| || strncasecmp(statement, "{", 1)==0) { | |
| CRecordset rs(db); | CRecordset rs(db); |
| rs.Open( | TRY { |
| CRecordset::forwardOnly, | rs.Open( |
| statement, | CRecordset::forwardOnly, |
| CRecordset::executeDirect | statement, |
| ); | CRecordset::executeDirect |
| ); | |
| } CATCH_ALL (e) { | |
| // could not fetch a table | |
| TRY { | |
| // then try resultless query | |
| db->ExecuteSQL(statement); | |
| // OK then | |
| return; | |
| } CATCH_ALL (e2) { | |
| // still nothing good | |
| _throw(services, e); // throw ORIGINAL exception | |
| } END_CATCH_ALL | |
| } END_CATCH_ALL | |
| int column_count=rs.GetODBCFieldCount(); | int column_count=rs.GetODBCFieldCount(); |
| if(!column_count) | if(!column_count) |
| services._throw("result contains no columns"); | services._throw("result contains no columns"); |
| SWORD column_types[MAX_COLS]; | |
| if(column_count>MAX_COLS) | |
| column_count=MAX_COLS; | |
| SQL_Error sql_error; | |
| #define CHECK(afailed) if(afailed) services._throw(sql_error) | |
| for(int i=0; i<column_count; i++){ | for(int i=0; i<column_count; i++){ |
| CString string; | CString string; |
| CODBCFieldInfo fieldinfo; | CODBCFieldInfo fieldinfo; |
| rs.GetODBCFieldInfo(i, fieldinfo); | rs.GetODBCFieldInfo(i, fieldinfo); |
| column_types[i]=fieldinfo.m_nSQLType; | |
| size_t size=fieldinfo.m_strName.GetLength(); | size_t size=fieldinfo.m_strName.GetLength(); |
| void *ptr=0; | char *str=0; |
| if(size) { | if(size) { |
| ptr=services.malloc(size); | str=(char*)services.malloc_atomic(size+1); |
| memcpy(ptr, (char *)LPCTSTR(fieldinfo.m_strName), size); | memcpy(str, (char *)LPCTSTR(fieldinfo.m_strName), size+1); |
| } | } |
| handlers.add_column(ptr, size); | CHECK(handlers.add_column(sql_error, str, size)); |
| } | } |
| handlers.before_rows(); | CHECK(handlers.before_rows(sql_error)); |
| unsigned long row=0; | unsigned long row=0; |
| CDBVariant v; | |
| CString s; | |
| while(!rs.IsEOF() && (!limit||(row<offset+limit))) { | while(!rs.IsEOF() && (!limit||(row<offset+limit))) { |
| if(row>=offset) { | if(row>=offset) { |
| handlers.add_row(); | CHECK(handlers.add_row(sql_error)); |
| for(int i=0; i<column_count; i++) { | for(int i=0; i<column_count; i++) { |
| CString string; | size_t size; |
| rs.GetFieldValue(i, string); | char* str; |
| size_t size=string.GetLength(); | switch(column_types[i]) { |
| void *ptr=0; | //case xBOOL: |
| if(size) { | // case SQL_INTEGER: // serg@design.ru did that in parser2. test first! |
| ptr=services.malloc(size); | //case SQL_DATETIME: << default: handles that more properly (?) |
| memcpy(ptr, (char *)LPCTSTR(string), size); | case SQL_BINARY: |
| case SQL_VARBINARY: | |
| case SQL_LONGVARBINARY: | |
| case SQL_SMALLDATETIME: | |
| rs.GetFieldValue(i, v); | |
| getFromDBVariant(services, v, str, size); | |
| break; | |
| default: | |
| rs.GetFieldValue(i, s); | |
| getFromString(services, s, str, size); | |
| break; | |
| } | } |
| handlers.add_row_cell(ptr, size); | CHECK(handlers.add_row_cell(sql_error, str, size)); |
| } | } |
| } | } |
| rs.MoveNext(); row++; | rs.MoveNext(); row++; |
| Line 185 public: | Line 245 public: |
| } else { | } else { |
| db->ExecuteSQL(statement); | db->ExecuteSQL(statement); |
| } | } |
| } | } CATCH_ALL (e) { |
| CATCH_ALL (e) { | |
| _throw(services, e); | _throw(services, e); |
| } END_CATCH_ALL | |
| } | |
| void getFromDBVariant(SQL_Driver_services& services, CDBVariant& v, char*& str, size_t& size) { | |
| switch(v.m_dwType) { | |
| case DBVT_BINARY: /* << would cause problems with current String implementation | |
| now falling into NULL case, effectively ignoring such columns [not failing] | |
| { | |
| if(size=v.m_pbinary->m_dwDataLength) { | |
| str=services.malloc_atomic(size+1); | |
| memcpy(ptr, ::GlobalLock(v.m_pbinary->m_hData), size); | |
| ::GlobalUnlock(v.m_pbinary->m_hData); | |
| } else | |
| str=0; | |
| break; | |
| }*/ | |
| case DBVT_NULL: // No union member is valid for access. | |
| str=0; | |
| size=0; | |
| break; | |
| /* case DBVT_BOOL: | |
| ptr=v.m_boolVal?"1":"0"; | |
| size=1; | |
| break;*/ | |
| /* case DBVT_UCHAR: | |
| size=strlen(ptr=v.m_chVal); | |
| break; | |
| case DBVT_SHORT: | |
| char buf[MAX_NUMBER]; | |
| size=snprintf(HEAPIZE buf, "%d", v.m_iVal); | |
| break;*/ | |
| /* case DBVT_LONG: | |
| { | |
| char local_buf[MAX_NUMBER]; | |
| size=snprintf(local_buf, MAX_NUMBER, "%ld", v.m_lVal); | |
| ptr=services.malloc_atomic(size); | |
| memcpy(ptr, local_buf, size); | |
| break; | |
| }*/ | |
| /*case DBVT_SINGLE: | |
| m_fltVal | |
| break; | |
| case DBVT_DOUBLE m_dblVal | |
| case DBVT_STRING m_pstring */ | |
| case DBVT_DATE: | |
| { | |
| char local_buf[MAX_STRING]; | |
| size=snprintf(local_buf, MAX_STRING, | |
| "%04d-%02d-%02d %02d:%02d:%02d.%03d", | |
| v.m_pdate->year, | |
| v.m_pdate->month, | |
| v.m_pdate->day, | |
| v.m_pdate->hour, | |
| v.m_pdate->minute, | |
| v.m_pdate->second, | |
| v.m_pdate->fraction); | |
| str=(char*)services.malloc_atomic(size+1); | |
| memcpy(str, local_buf, size+1); | |
| break; | |
| } | |
| default: | |
| char msg[MAX_STRING]; | |
| snprintf(msg, MAX_STRING, "unknown column return variant type (%d)", | |
| v.m_dwType); | |
| services._throw(msg); | |
| } | |
| } | |
| void getFromString(SQL_Driver_services& services, CString& s, char*& astr, size_t& size) { | |
| if(s.IsEmpty()) { | |
| astr=0; | |
| size=0; | |
| } else { | |
| const char *cstr=LPCTSTR(s); | |
| size=strlen(cstr); //string.GetLength() works wrong with non-string types: | |
| astr=(char*)services.malloc_atomic(size+1); | |
| memcpy(astr, cstr, size+1); | |
| } | } |
| END_CATCH_ALL | |
| } | } |
| void _throw(SQL_Driver_services& services, CException *e) { | void _throw(SQL_Driver_services& services, CException *e) { |