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

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

E-mail: