Annotation of sql/odbc/parser3odbc.C, revision 1.20

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

E-mail: