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

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.37    ! moko        8: static const char *RCSId="$Id: parser3odbc.C,v 1.36 2008-07-08 10:53:32 misha 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
1.28      misha      47: #ifndef strcasecmp
                     48: #      define strcasecmp _stricmp
                     49: #endif
1.1       parser     50: 
1.27      misha      51: static char *lsplit(char *string, char delim){
                     52:        if(string){
                     53:                if(char* v=strchr(string, delim)){
1.1       parser     54:                        *v=0;
                     55:                        return v+1;
                     56:                }
1.27      misha      57:        }
                     58:        return 0;
1.1       parser     59: }
                     60: 
1.28      misha      61: static char *lsplit(char **string_ref, char delim){
                     62:        char *result=*string_ref;
                     63:        char *next=lsplit(*string_ref, delim);
                     64:        *string_ref=next;
                     65:        return result;
                     66: }
                     67: 
1.27      misha      68: static void toupper_str(char *out, const char *in, size_t size){
1.20      paf        69:        while(size--)
                     70:                *out++=(char)toupper(*in++);
                     71: }
                     72: 
1.33      misha      73: struct modified_statement {
                     74:        const char* statement;
                     75:        bool limit;
                     76:        bool offset;
                     77: };
                     78: 
                     79: // todo: MySQL, SQLite, PgSQL (add LIMIT at the end of statement)
                     80: struct SQL {
                     81:        enum SQLEnum {
                     82:                Undefined,
                     83:                MSSQL,
                     84:                Pervasive,
                     85:                FireBird
                     86:        };
                     87: };
                     88: 
1.18      paf        89: struct Connection {
                     90:        SQL_Driver_services* services;
                     91: 
                     92:        CDatabase* db;
1.27      misha      93:        const char* client_charset;
1.33      misha      94:        SQL::SQLEnum sql_specific;
1.27      misha      95:        bool autocommit;
1.18      paf        96: };
                     97: 
1.1       parser     98: /**
                     99:        ODBC server driver
                    100: */
                    101: class ODBC_Driver : public SQL_Driver {
                    102: public:
                    103: 
                    104:        ODBC_Driver() : SQL_Driver() {
                    105:        }
                    106: 
                    107:        /// get api version
                    108:        int api_version() { return SQL_DRIVER_API_VERSION; }
1.27      misha     109: 
1.3       paf       110:        const char *initialize(char *dlopen_file_spec) { return 0; }
1.1       parser    111:        /**     connect
1.22      paf       112:                @param url
1.28      misha     113:                        format: @b DSN=dsn;UID=user;PWD=password? (ODBC connect string)
                    114:                                ClientCharset=charset&  // transcode with parser
                    115:                                autocommit=1&           // 0 -- disable auto commit
1.30      misha     116:                                FastOffsetSearch=0
1.1       parser    117:                        WARNING: must be used only to connect, for buffer doesn't live long
                    118:        */
1.27      misha     119: 
1.1       parser    120:        void connect(
1.27      misha     121:                        char *url, 
                    122:                        SQL_Driver_services& services, 
                    123:                        void **connection_ref ///< output: Connection*
                    124:        ){
                    125:                Connection& connection=*(Connection *)services.malloc(sizeof(Connection));
1.18      paf       126:                *connection_ref=&connection;
                    127:                connection.services=&services;
1.27      misha     128:                connection.client_charset=0;
1.33      misha     129:                connection.sql_specific=SQL::Undefined;
1.27      misha     130:                connection.autocommit=true;
                    131: 
                    132:                size_t url_length=strlen(url);
1.28      misha     133:                char *options=lsplit(url, '?');
1.20      paf       134: 
1.32      misha     135:                // todo: analize connect string and if 'SQL Server' found, modify query and add TOP into SELECTs
                    136: 
1.28      misha     137:                while(options){
                    138:                        if(char *key=lsplit(&options, '&')){
                    139:                                if(*key){
                    140:                                        if(char *value=lsplit(key, '=')){
                    141:                                                if(strcmp(key, "ClientCharset")==0){
                    142:                                                        toupper_str(value, value, strlen(value));
                    143:                                                        connection.client_charset=value;
                    144:                                                } else if(strcasecmp(key, "autocommit")==0){
                    145:                                                        if(atoi(value)==0)
                    146:                                                                connection.autocommit=false;
1.33      misha     147:                                                } else if(strcmp(key, "SQL")==0){
                    148:                                                        if(strcasecmp(value, "MSSQL")==0){
                    149:                                                                connection.sql_specific=SQL::MSSQL;
                    150:                                                        } else if(strcasecmp(value, "Pervasive")==0){
                    151:                                                                connection.sql_specific=SQL::Pervasive;
                    152:                                                        } else if(strcasecmp(value, "FireBird")==0){
                    153:                                                                connection.sql_specific=SQL::FireBird;
                    154:                                                        } else {
                    155:                                                                services._throw("unknown value of SQL option was specified" /*key*/);
                    156:                                                        }
1.28      misha     157:                                                } else
                    158:                                                        services._throw("unknown connect option" /*key*/);
                    159:                                        } else 
                    160:                                                services._throw("connect option without =value" /*key*/);
                    161:                                }
1.20      paf       162:                        }
                    163:                }
1.18      paf       164: 
1.1       parser    165:                TRY {
1.18      paf       166:                        connection.db=new CDatabase();
1.22      paf       167:                        connection.db->OpenEx(url, CDatabase::noOdbcDialog);
1.32      misha     168:                        connection.db->BeginTrans();
1.1       parser    169:                } 
                    170:                CATCH_ALL (e) {
                    171:                        _throw(services, e);
                    172:                }
                    173:                END_CATCH_ALL
                    174:        }
1.27      misha     175: 
                    176:        void disconnect(void *aconnection){
1.18      paf       177:                Connection& connection=*static_cast<Connection*>(aconnection);
1.1       parser    178:                TRY
1.18      paf       179:                        delete connection.db;
                    180:                        connection.db=0;
1.1       parser    181:                CATCH_ALL (e) {
                    182:                        // nothing
                    183:                }
                    184:                END_CATCH_ALL
                    185:        }
1.27      misha     186: 
                    187:        void commit(void *aconnection){
1.18      paf       188:                Connection& connection=*static_cast<Connection*>(aconnection);
1.32      misha     189:                TRY
                    190:                        connection.db->CommitTrans();
                    191:                        connection.db->BeginTrans();
                    192:                CATCH_ALL (e) {
                    193:                        _throw(*connection.services, e);
1.1       parser    194:                }
1.32      misha     195:                END_CATCH_ALL
1.1       parser    196:        }
1.27      misha     197: 
                    198:        void rollback(void *aconnection){
1.18      paf       199:                Connection& connection=*static_cast<Connection*>(aconnection);
1.32      misha     200:                TRY
                    201:                        connection.db->Rollback();
                    202:                        connection.db->BeginTrans();
                    203:                CATCH_ALL (e) {
                    204:                        _throw(*connection.services, e);
1.1       parser    205:                }
1.32      misha     206:                END_CATCH_ALL
1.1       parser    207:        }
                    208: 
1.27      misha     209:        bool ping(void *connection){
1.1       parser    210:                return true;
                    211:        }
                    212: 
1.37    ! moko      213:        // charset here is services.request_charset(), not connection.client_charset
        !           214:        // thus we can't use the sql server quoting support
        !           215:        const char* quote(void *aconnection, const char *str, unsigned int length) 
        !           216:        {
        !           217:                const char* from;
        !           218:                const char* from_end=str+length;
        !           219: 
        !           220:                size_t quoted=0;
        !           221: 
        !           222:                for(from=str; from<from_end; from++){
        !           223:                        if(*from=='\'')
        !           224:                                quoted++;
        !           225:                }
        !           226: 
        !           227:                if(!quoted)
        !           228:                        return str;
        !           229: 
1.18      paf       230:                Connection& connection=*static_cast<Connection*>(aconnection);
1.37    ! moko      231:                char *result=(char*)connection.services->malloc_atomic(length + quoted + 1);
        !           232:                char *to = result;
        !           233: 
        !           234:                for(from=str; from<from_end; from++){
        !           235:                        if(*from=='\'')
        !           236:                                *to++= '\''; // ' -> ''
        !           237:                        *to++=*from;
1.14      paf       238:                }
1.37    ! moko      239:                
1.14      paf       240:                *to=0;
                    241:                return result;
1.1       parser    242:        }
1.27      misha     243: 
1.18      paf       244:        void query(void *aconnection, 
1.33      misha     245:                        const char *astatement, 
1.27      misha     246:                        size_t placeholders_count,
                    247:                        Placeholder* placeholders, 
                    248:                        unsigned long offset,
                    249:                        unsigned long limit,
                    250:                        SQL_Driver_query_event_handlers& handlers
                    251:        ){
1.18      paf       252:                Connection& connection=*static_cast<Connection*>(aconnection);
                    253:                CDatabase *db=connection.db;
                    254:                SQL_Driver_services& services=*connection.services;
1.23      paf       255: 
                    256:                if(placeholders_count>0)
                    257:                        services._throw("bind variables not supported (yet)");
1.1       parser    258: 
1.33      misha     259:                while(isspace((unsigned char)*astatement)) 
                    260:                        astatement++;
                    261: 
                    262:                modified_statement mstatement=_preprocess_statement(connection, astatement, offset, limit);
                    263:                const char* statement=mstatement.statement;
                    264: 
1.30      misha     265:                const char* client_charset=connection.client_charset;
                    266:                const char* request_charset=services.request_charset();
                    267:                bool transcode_needed=(client_charset && strcmp(client_charset, request_charset)!=0);
1.27      misha     268:                if(transcode_needed){
1.33      misha     269:                        // transcode query from $request:charset to ?ClientCharset
1.27      misha     270:                        size_t length=strlen(statement);
                    271:                        services.transcode(statement, length,
                    272:                                statement, length,
1.30      misha     273:                                request_charset,
                    274:                                client_charset);
1.20      paf       275:                }
                    276: 
1.1       parser    277:                TRY {
1.8       paf       278:                        // mk:@MSITStore:C:\Program%20Files\Microsoft%20SQL%20Server\80\Tools\Books\adosql.chm::/adoprg02_4g33.htm
1.27      misha     279:                        // or http://msdn.microsoft.com/en-us/library/aa905899(SQL.80).aspx
1.8       paf       280:                        // Server cursors are created only for statements that begin with: 
                    281:                        // SELECT
                    282:                        // EXEC[ute] procedure_name
                    283:                        // call procedure_name
                    284:                        // mk:@MSITStore:C:\Program%20Files\Microsoft%20SQL%20Server\80\Tools\Books\odbcsql.chm::/od_6_035_5dnp.htm
                    285:                        // The ODBC CALL escape sequence for calling a procedure is:
                    286:                        // {[?=]call procedure_name[([parameter][,[parameter]]...)]}
1.33      misha     287:                        if(
                    288:                                strncasecmp(statement, "SELECT", 6)==0
1.8       paf       289:                                || strncasecmp(statement, "EXEC", 4)==0
                    290:                                || strncasecmp(statement, "call", 4)==0
1.27      misha     291:                                || strncasecmp(statement, "{", 1)==0
                    292:                        ){
                    293:                                CRecordset rs(db);
                    294:                                DWORD options=CRecordset::executeDirect|CRecordset::readOnly;
1.9       paf       295:                                TRY {
                    296:                                        rs.Open(
1.33      misha     297:                                                CRecordset::forwardOnly,
1.9       paf       298:                                                statement,
1.27      misha     299:                                                options
                    300:                                        );
1.9       paf       301:                                } CATCH_ALL (e) {
                    302:                                        // could not fetch a table
                    303:                                        TRY {
                    304:                                                // then try resultless query
                    305:                                                db->ExecuteSQL(statement);
                    306:                                                // OK then
                    307:                                                return;
                    308:                                        } CATCH_ALL (e2) {
                    309:                                                // still nothing good
                    310:                                                _throw(services, e); // throw ORIGINAL exception
                    311:                                        } END_CATCH_ALL
                    312:                                } END_CATCH_ALL
1.1       parser    313: 
                    314:                                int column_count=rs.GetODBCFieldCount();
                    315:                                if(!column_count)
                    316:                                        services._throw("result contains no columns");
                    317: 
1.10      paf       318:                                if(column_count>MAX_COLS)
                    319:                                        column_count=MAX_COLS;
                    320: 
1.27      misha     321:                                SWORD column_types[MAX_COLS];
1.30      misha     322:                                bool transcode_column[MAX_COLS];
1.27      misha     323: 
1.12      paf       324:                                SQL_Error sql_error;
                    325: #define CHECK(afailed) if(afailed) services._throw(sql_error)
                    326: 
1.1       parser    327:                                for(int i=0; i<column_count; i++){
                    328:                                        CString string;
                    329:                                        CODBCFieldInfo fieldinfo;
                    330:                                        rs.GetODBCFieldInfo(i, fieldinfo);
1.10      paf       331:                                        column_types[i]=fieldinfo.m_nSQLType;
1.30      misha     332:                                        switch(fieldinfo.m_nSQLType){
                    333:                                                case SQL_NUMERIC:
                    334:                                                case SQL_DECIMAL:
                    335:                                                case SQL_INTEGER:
                    336:                                                case SQL_SMALLINT:
                    337:                                                case SQL_FLOAT:
                    338:                                                case SQL_REAL:
                    339:                                                case SQL_DOUBLE:
                    340:                                                case SQL_DATETIME:
                    341:                                                case SQL_SMALLDATETIME:
                    342:                                                case SQL_BIGINT:
                    343:                                                case SQL_TINYINT:
                    344:                                                        transcode_column[i]=false;
                    345:                                                        break;
                    346:                                                default:
                    347:                                                        transcode_column[i]=transcode_needed;
1.32      misha     348:                                                        break;
1.30      misha     349:                                        }
1.20      paf       350:                                        size_t length=fieldinfo.m_strName.GetLength();
1.14      paf       351:                                        char *str=0;
1.27      misha     352:                                        if(length){
1.20      paf       353:                                                str=(char*)services.malloc_atomic(length+1);
1.27      misha     354:                                                memcpy(str, (char*)LPCTSTR(fieldinfo.m_strName), length+1);
1.20      paf       355: 
1.27      misha     356:                                                // transcode column name from ?ClientCharset to $request:charset
                    357:                                                if(transcode_needed){
1.20      paf       358:                                                        services.transcode(str, length,
                    359:                                                                str, length,
1.30      misha     360:                                                                client_charset,
                    361:                                                                request_charset);
1.20      paf       362:                                                }
1.1       parser    363:                                        }
1.20      paf       364:                                        CHECK(handlers.add_column(sql_error, str, length));
1.1       parser    365:                                }
                    366: 
1.12      paf       367:                                CHECK(handlers.before_rows(sql_error));
1.27      misha     368:                                
                    369:                                // skip offset rows
1.36      misha     370:                                if(offset && limit!=0 && !mstatement.offset){
1.33      misha     371:                                        unsigned long row=offset;
                    372:                                        while(!rs.IsEOF() && row--)
                    373:                                                rs.MoveNext();
1.27      misha     374:                                }
1.1       parser    375: 
                    376:                                unsigned long row=0;
1.10      paf       377:                                CDBVariant v;
                    378:                                CString s;
1.27      misha     379:                                while(!rs.IsEOF() && (limit==SQL_NO_LIMIT || row<limit)){
                    380:                                        CHECK(handlers.add_row(sql_error));
                    381:                                        for(int i=0; i<column_count; i++){
                    382:                                                size_t length;
                    383:                                                char* str;
                    384:                                                switch(column_types[i]){
1.7       paf       385:                                                        //case xBOOL:
1.14      paf       386:                                                        //case SQL_DATETIME: << default: handles that more properly (?)
                    387:                                                        case SQL_BINARY: 
1.11      paf       388:                                                        case SQL_VARBINARY:
                    389:                                                        case SQL_LONGVARBINARY:
1.7       paf       390:                                                        case SQL_SMALLDATETIME:
1.27      misha     391:                                                        //case SQL_NVARCHAR:    // mfc 7.1 has errors with nvarchar(length): SQLGetData in dbcore.cpp truncates last byte for unknown reason.
                    392:                                                                                                        // could be fixed by uncommenting this and handing DBVT_WSTRING inside, but it's UNICODE
1.10      paf       393:                                                                rs.GetFieldValue(i, v);
1.20      paf       394:                                                                getFromDBVariant(services, v, str, length);
1.10      paf       395:                                                                break;
1.7       paf       396:                                                        default:
1.10      paf       397:                                                                rs.GetFieldValue(i, s);
1.20      paf       398:                                                                getFromString(services, s, str, length);
1.10      paf       399:                                                                break;
1.27      misha     400:                                                }
1.20      paf       401: 
1.27      misha     402:                                                // transcode cell value from ?ClientCharset to $request:charset
1.30      misha     403:                                                if(length && transcode_column[i]){
1.27      misha     404:                                                        services.transcode(str, length,
                    405:                                                                str, length,
1.30      misha     406:                                                                client_charset,
                    407:                                                                request_charset);
                    408:                                                }
1.20      paf       409: 
1.27      misha     410:                                                CHECK(handlers.add_row_cell(sql_error, str, length));
1.1       parser    411:                                        }
1.27      misha     412:                                        rs.MoveNext();
                    413:                                        row++;
1.1       parser    414:                                }
                    415:                                
                    416:                                rs.Close();
                    417:                        } else {
                    418:                                db->ExecuteSQL(statement);
                    419:                        }
1.9       paf       420:                } CATCH_ALL (e) {
1.1       parser    421:                        _throw(services, e);
1.9       paf       422:                } END_CATCH_ALL
1.32      misha     423: 
                    424:                if(connection.autocommit)
                    425:                        commit(aconnection);
1.7       paf       426:        }
                    427: 
1.27      misha     428: private:
                    429:        void getFromDBVariant(SQL_Driver_services& services, CDBVariant& v, char*& str, size_t& length){
                    430:                switch(v.m_dwType){
1.14      paf       431:                case DBVT_BINARY: /* << would cause problems with current String implementation
                    432:                                  now falling into NULL case, effectively ignoring such columns [not failing]
                    433:                        {
1.17      paf       434:                                if(length=v.m_pbinary->m_dwDataLength) {
                    435:                                        str=services.malloc_atomic(length+1);
                    436:                                        memcpy(ptr, ::GlobalLock(v.m_pbinary->m_hData), length);
1.14      paf       437:                                        ::GlobalUnlock(v.m_pbinary->m_hData);
                    438:                                } else 
                    439:                                        str=0;
                    440:                                break;
                    441:                        }*/ 
1.7       paf       442:                case DBVT_NULL: // No union member is valid for access. 
1.14      paf       443:                        str=0;
1.17      paf       444:                        length=0;
1.7       paf       445:                        break;
                    446: /*             case DBVT_BOOL:
                    447:                        ptr=v.m_boolVal?"1":"0";
1.17      paf       448:                        length=1;
1.7       paf       449:                        break;*/
1.32      misha     450: /*             case DBVT_UCHAR:
1.17      paf       451:                        length=strlen(ptr=v.m_chVal);
1.7       paf       452:                        break;
                    453:                case DBVT_SHORT:
                    454:                        char buf[MAX_NUMBER];
1.17      paf       455:                        length=snprintf(HEAPIZE buf, "%d", v.m_iVal);
1.27      misha     456:                        break;
                    457: */
1.7       paf       458: /*             case DBVT_LONG: 
                    459:                        {
                    460:                                char local_buf[MAX_NUMBER];
1.17      paf       461:                                length=snprintf(local_buf, MAX_NUMBER, "%ld", v.m_lVal);
                    462:                                ptr=services.malloc_atomic(length);
                    463:                                memcpy(ptr, local_buf, length);
1.7       paf       464:                                break;
1.27      misha     465:                        }
                    466: */
                    467: /*
                    468:                case DBVT_SINGLE:
1.7       paf       469:                        m_fltVal 
                    470:                        break;
1.27      misha     471:                case DBVT_DOUBLE m_dblVal 
                    472:                case DBVT_STRING m_pstring
                    473: */ 
1.7       paf       474:                case DBVT_DATE:
                    475:                        {
                    476:                                char local_buf[MAX_STRING];
1.17      paf       477:                                length=snprintf(local_buf, MAX_STRING, 
1.7       paf       478:                                        "%04d-%02d-%02d %02d:%02d:%02d.%03d",
                    479:                                        v.m_pdate->year, 
                    480:                                        v.m_pdate->month,
                    481:                                        v.m_pdate->day,
                    482:                                        v.m_pdate->hour,
                    483:                                        v.m_pdate->minute,
                    484:                                        v.m_pdate->second,
1.25      paf       485:                                        v.m_pdate->fraction/1000000); // lexical parser of INCOMING literal choked on times like hh:mm:ss.123000000
1.17      paf       486:                                str=(char*)services.malloc_atomic(length+1);
                    487:                                memcpy(str, local_buf, length+1);
1.7       paf       488:                                break;
                    489:                        }
                    490:                default:
                    491:                        char msg[MAX_STRING];
                    492:                        snprintf(msg, MAX_STRING, "unknown column return variant type (%d)",
                    493:                                v.m_dwType);
                    494:                        services._throw(msg);
                    495:                }
                    496:        }
                    497: 
1.27      misha     498:        void getFromString(SQL_Driver_services& services, CString& s, char*& astr, size_t& length){
                    499:                if(s.IsEmpty()){
1.14      paf       500:                        astr=0;
1.17      paf       501:                        length=0;
1.7       paf       502:                } else {
                    503:                        const char *cstr=LPCTSTR(s);
1.17      paf       504:                        length=strlen(cstr); //string.GetLength() works wrong with non-string types: 
                    505:                        astr=(char*)services.malloc_atomic(length+1);
                    506:                        memcpy(astr, cstr, length+1);
1.7       paf       507:                }
1.1       parser    508:        }
                    509: 
1.33      misha     510:        modified_statement _preprocess_statement(
                    511:                        Connection& connection, 
                    512:                        const char* astatement,
                    513:                        unsigned long offset,
                    514:                        unsigned long limit
                    515:        ){
                    516:                modified_statement result={astatement, false, false};
                    517: 
                    518:                if(limit!=SQL_NO_LIMIT && connection.sql_specific!=SQL::Undefined && strncasecmp(astatement, "select", 6)==0){
                    519:                        switch(connection.sql_specific){
                    520:                                case SQL::MSSQL:
                    521:                                case SQL::Pervasive: // uses TOP as well
                    522:                                        {
                    523:                                                // add ' TOP limit+offset' after 'SELECT'
                    524:                                                char* statement_limited=(char *)connection.services->malloc_atomic(
                    525:                                                                strlen(astatement)
                    526:                                                                +MAX_NUMBER
                    527:                                                                +5/* TOP */
                    528:                                                                +1/*terminator*/
                    529:                                                        );
                    530: 
1.35      misha     531:                                                result.limit=true;
1.33      misha     532:                                                result.statement=statement_limited;
                    533: 
1.35      misha     534:                                                snprintf(statement_limited, MAX_NUMBER+11, "SELECT TOP %u", (limit)?limit+offset:0/*no point to skip anything if we need 0 rows*/);
1.33      misha     535: 
                    536:                                                astatement+=6;/*skip 'select'*/
                    537:                                                strcat(statement_limited, astatement);
                    538: 
                    539:                                                //connection.services->_throw(result.statement);
                    540:                                                break;
                    541:                                        }
                    542:                                case SQL::FireBird:
                    543:                                        {
1.34      misha     544:                                                // add ' FIRST (limit) SKIP (offset)' after 'SELECT'
1.33      misha     545:                                                char* statement_limited=(char *)connection.services->malloc_atomic(
                    546:                                                                strlen(astatement)
                    547:                                                                +MAX_NUMBER*2
                    548:                                                                +9/* FIRST ()*/
                    549:                                                                +offset?8:0/* SKIP ()*/
                    550:                                                                +1/*terminator*/
                    551:                                                        );
                    552: 
                    553:                                                result.limit=true;
                    554:                                                result.offset=true;
                    555:                                                result.statement=statement_limited;
                    556: 
                    557:                                                statement_limited+=snprintf(statement_limited, MAX_NUMBER+15, "SELECT FIRST (%u)", limit);
                    558:                                                if(offset && limit/*no reasons to skip something if we need 0 rows*/)
                    559:                                                        statement_limited+=snprintf(statement_limited, MAX_NUMBER+8, " SKIP (%u)", offset);
                    560: 
                    561:                                                astatement+=6;/*skip 'select'*/
                    562:                                                strcat((char*)result.statement, astatement);
                    563: 
                    564:                                                //connection.services->_throw(result.statement);
                    565:                                                break;
                    566:                                        }
                    567:                                default:
1.34      misha     568:                                        connection.services->_throw("Unknown SQL specifics");
1.33      misha     569:                        }
                    570:                }
                    571:                return result;
                    572:        }
                    573: 
1.27      misha     574:        void _throw(SQL_Driver_services& services, CException *e){
                    575:                char szCause[MAX_STRING];
                    576:                szCause[0]=0;
1.1       parser    577:                e->GetErrorMessage(szCause, MAX_STRING);
                    578:                char msg[MAX_STRING];
                    579:                snprintf(msg, MAX_STRING, "%s: %s",
                    580:                        e->GetRuntimeClass()->m_lpszClassName,
                    581:                        *szCause?szCause:"unknown");
                    582:                services._throw(msg);
                    583:        }
                    584: 
1.32      misha     585:        void _throw(Connection& connection, long value){
                    586:                char msg[MAX_STRING];
                    587:                snprintf(msg, MAX_STRING, "%u", value);
                    588:                connection.services->_throw(msg);
                    589:        }
                    590: 
1.1       parser    591: };
                    592: 
                    593: extern "C" SQL_Driver *SQL_DRIVER_CREATE() {
                    594:        return new ODBC_Driver();
1.26      paf       595: }

E-mail: