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

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

E-mail: