Annotation of sql/oracle/parser3oracle.C, revision 1.75

1.1       parser      1: /** @file
                      2:        Parser Oracle driver.
                      3: 
1.75    ! moko        4:        Copyright (c) 2001-2012 Art. Lebedev Studio (http://www.artlebedev.com)
1.1       parser      5: 
1.19      paf         6:        Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
1.1       parser      7: 
                      8:        2001.07.30 using Oracle 8.1.6 [@test tested with Oracle 7.x.x]
                      9: */
1.63      paf        10: 
1.1       parser     11: #include "config_includes.h"
                     12: 
                     13: #include "pa_sql_driver.h"
                     14: 
                     15: #include <oci.h>
                     16: 
1.75    ! moko       17: volatile const char * IDENT_PARSER3ORACLE_C="$Id: parser3oracle.C,v 1.74 2011-07-28 07:15:08 moko Exp $" IDENT_PA_SQL_DRIVER_H;
        !            18: 
1.1       parser     19: #define MAX_COLS 500
                     20: #define MAX_IN_LOBS 5
                     21: #define MAX_LOB_NAME_LENGTH 100
                     22: #define MAX_OUT_STRING_LENGTH 4000
1.61      paf        23: #define MAX_BINDS 100
1.1       parser     24: 
                     25: #define EMPTY_CLOB_FUNC_CALL "empty_clob()"
                     26: 
                     27: #include "ltdl.h"
                     28: 
                     29: #define MAX_STRING 0x400
                     30: #define MAX_NUMBER 20
                     31: 
                     32: #if _MSC_VER
                     33: #      define snprintf _snprintf
                     34: #      define strcasecmp _stricmp
                     35: #      define strncasecmp _strnicmp
                     36: #endif
                     37: 
                     38: #ifndef max
                     39: inline int max(int a, int b) { return a>b?a:b; }
                     40: inline int min(int a, int b){ return a<b?a:b; }
                     41: #endif
                     42: 
1.45      paf        43: #if _MSC_VER
                     44: // interaction between '_setjmp' and C++ object destruction is non-portable
                     45: // but we forced to do that under HPUX
                     46: #pragma warning(disable:4611)   
                     47: #endif
                     48: 
1.61      paf        49: const sb2 MAGIC_INDICATOR_VALUE_MEANING_NOT_NULL_AND_UNCHANGED=99;
                     50: 
1.33      paf        51: /// @todo small memory leaks here
1.10      paf        52: static int pa_setenv(const char *name, const char *value, bool do_append) {
                     53:        const char *prev_value=0;
                     54:        if(do_append)
                     55:                prev_value=getenv(name);
1.4       paf        56: #ifdef HAVE_PUTENV
                     57:     // MEM_LEAK_HERE. refer to EOF man putenv
1.10      paf        58:        char *buf=(char *)::malloc(strlen(name)
                     59:                +1
                     60:                +(prev_value?strlen(prev_value):0)
                     61:                +strlen(value)
                     62:                +1);
1.4       paf        63:        strcpy(buf, name);
                     64:        strcat(buf, "=");
1.10      paf        65:        if(prev_value)
                     66:                strcat(buf, prev_value);
1.4       paf        67:        strcat(buf, value);
1.5       paf        68: /*
                     69:        if(FILE *f=fopen("f", "at")) {
                     70:                fprintf(f, "****************************%s\n", buf);
                     71: //             for (char **env = environ; env != NULL && *env != NULL; env++)
                     72: //                     fputs(*env, f);
                     73:        
                     74:                fclose(f);
                     75:        }
                     76: */     
1.4       paf        77:        return putenv(buf);
                     78: #else 
                     79:        //#ifdef HAVE_SETENV
1.10      paf        80:        if(value) {
                     81:                if(prev_value) {
                     82:                        // MEM_LEAK_HERE
1.33      paf        83:                        char *buf=(char *)::malloc(strlen(prev_value)
1.10      paf        84:                                +strlen(value)
                     85:                                +1);
                     86:                        strcpy(buf, prev_value);
                     87:                        strcat(buf, value);
1.24      paf        88:                        value=buf;
                     89:                }
                     90:                return setenv(name, value, 1/*overwrite*/); 
1.10      paf        91:        } else {
1.4       paf        92:                unsetenv(name);
                     93:                return 0;
                     94:        }
                     95: #endif
                     96: }
                     97: 
1.69      misha      98: static char *lsplit(char *string, char delim){
                     99:        if(string){
                    100:                if(char* v=strchr(string, delim)){
1.1       parser    101:                        *v=0;
                    102:                        return v+1;
                    103:                }
1.69      misha     104:        }
                    105:        return 0;
1.1       parser    106: }
                    107: 
1.69      misha     108: static char *lsplit(char **string_ref, char delim){
                    109:        char *result=*string_ref;
1.4       paf       110:        char *next=lsplit(*string_ref, delim);
1.69      misha     111:        *string_ref=next;
                    112:        return result;
1.4       paf       113: }
                    114: 
1.69      misha     115: static char* rsplit(char* string, char delim){
                    116:        if(string){
                    117:                if(char* v=strrchr(string, delim)){
1.67      paf       118:                        *v=0;
                    119:                        return v+1;
                    120:                }
1.69      misha     121:        }
                    122:        return NULL;    
                    123: }
                    124: 
                    125: static void tolower_str(char *out, const char *in, size_t size) {
                    126:        while(size--)
                    127:                *out++=(char)tolower(*in++);
                    128: }
                    129: static void toupper_str(char *out, const char *in, size_t size) {
                    130:        while(size--)
                    131:                *out++=(char)toupper(*in++);
1.67      paf       132: }
                    133: 
1.71      misha     134: struct modified_statement {
                    135:        const char* statement;
                    136:        bool limit;
                    137:        bool offset;
                    138:        bool skip_rownum_column;
                    139: };
                    140: 
1.1       parser    141: #ifndef DOXYGEN
1.49      paf       142: struct Connection {
1.42      paf       143:        SQL_Driver_services *services;
                    144: 
1.1       parser    145:        jmp_buf mark; char error[MAX_STRING];
1.27      paf       146:        SQL_Error sql_error;
1.1       parser    147:        OCIEnv *envhp;
                    148:        OCIServer *srvhp;
                    149:        OCIError *errhp;
                    150:        OCISvcCtx *svchp;
                    151:        OCISession *usrhp;
1.39      paf       152: 
1.46      paf       153:        char* fetch_buffers[MAX_COLS];
1.61      paf       154:        char* bind_buffers[MAX_BINDS];
1.46      paf       155: 
1.42      paf       156:        struct Options {
                    157:                bool bLowerCaseColumnNames;
1.72      misha     158:                bool bDisableQueryModification;
1.69      misha     159:                const char* client_charset;
1.42      paf       160:        } options;
1.1       parser    161: };
                    162: 
1.60      paf       163: struct Query_lobs {
1.1       parser    164:        struct return_rows {
                    165:                struct return_row {
                    166:                        OCILobLocator *locator; ub4 len;
                    167:                        int ind;                
                    168:                        ub2 rcode;
                    169:                } *row;
                    170:                int count;
                    171:        };
                    172:        struct Item {
                    173:                const char *name_ptr; size_t name_size;
                    174:                char *data_ptr; size_t data_size;
                    175:                OCILobLocator *locator;
                    176:                OCIBind *bind;
                    177:                return_rows rows;
1.57      paf       178:                Connection *connection;
1.1       parser    179:        } items[MAX_IN_LOBS];
                    180:        int count;
                    181: };
1.60      paf       182: 
1.1       parser    183: #endif
                    184: 
                    185: // forwards
1.71      misha     186: static void fail(Connection& connection, const char *msg);
1.60      paf       187: static void check(Connection& connection, const char *step, sword status);
                    188: static void check(Connection& connection, bool error);
1.1       parser    189: static sb4 cbf_no_data(
                    190:                                           dvoid *ctxp, 
                    191:                                           OCIBind *bindp, 
                    192:                                           ub4 iter, ub4 index, 
                    193:                                           dvoid **bufpp, 
                    194:                                           ub4 *alenpp, 
                    195:                                           ub1 *piecep, 
                    196:                                           dvoid **indpp);
                    197: static sb4 cbf_get_data(dvoid *ctxp, 
                    198:                                                OCIBind *bindp, 
                    199:                                                ub4 iter, ub4 index, 
                    200:                                                dvoid **bufpp, 
                    201:                                                ub4 **alenp, 
                    202:                                                ub1 *piecep, 
                    203:                                                dvoid **indpp, 
                    204:                                                ub2 **rcodepp);
                    205: 
1.71      misha     206: static bool transcode_required(Connection& connection);
                    207: 
1.49      paf       208: static const char *options2env(char *s, Connection::Options* options) {
1.69      misha     209:        while(s){
                    210:                if(char *key=lsplit(&s, '&')){
                    211:                        if(*key){
                    212:                                if(char *value=lsplit(key, '=')){
                    213:                                        if(strcmp(key, "ClientCharset")== 0){
                    214:                                                if(options){
1.55      paf       215:                                                        toupper_str(value, value, strlen(value));
1.69      misha     216:                                                        options->client_charset=value;
1.44      paf       217:                                                }
1.42      paf       218:                                                continue;
                    219:                                        }
                    220: 
1.69      misha     221:                                        if(strcmp(key, "LowerCaseColumnNames")==0){
1.42      paf       222:                                                if(options)
1.69      misha     223:                                                        options->bLowerCaseColumnNames=atoi(value)!=0;
1.42      paf       224:                                                continue;
                    225:                                        }
                    226: 
1.72      misha     227:                                        if(strcmp(key, "DisableQueryModification")==0){
1.71      misha     228:                                                if(options)
1.72      misha     229:                                                        options->bDisableQueryModification=atoi(value)!=0;
1.71      misha     230:                                                continue;
                    231:                                        }
                    232: 
1.42      paf       233:                                        bool do_append=key[strlen(key)-1]=='+'; // PATH+=
                    234:                                        if(do_append)
                    235:                                                key[strlen(key)-1]=0; // remove trailing +
                    236:                                        if(strncmp(key, "ORACLE_", 7)==0  // ORACLE_HOME & co
                    237:                                                || strncmp(key, "ORA_", 4)==0 // ORA_ENCRYPT_LOGIN & co
                    238:                                                || strncmp(key, "NLS_", 4)==0 // NLS_LANG & co
                    239:                                                || do_append
                    240:                                                ) {
                    241:                                                if(pa_setenv(key, value, do_append)!=0)
                    242:                                                        return "problem changing process environment" /*key*/;
                    243:                                        } else
                    244:                                                return "unknown option" /*key*/;
                    245:                                } else 
                    246:                                        return "option without =value" /*key*/;
                    247:                        }
                    248:                }
                    249:        }
                    250:        return 0;
                    251: }
                    252: 
1.1       parser    253: /**
                    254:        OracleSQL server driver
                    255: */
                    256: class OracleSQL_Driver : public SQL_Driver {
                    257: public:
                    258: 
                    259:        OracleSQL_Driver() : SQL_Driver() {
                    260:        }
                    261: 
                    262:        /// get api version
                    263:        int api_version() { return SQL_DRIVER_API_VERSION; }
1.69      misha     264: 
1.6       paf       265:        /** initialize driver by loading sql dynamic link library
                    266:                @todo ?objects=1 which would turn on OCI_OBJECT init flag
                    267:        */
1.5       paf       268:        const char *initialize(char *dlopen_file_spec) {
                    269:                char *options=lsplit(dlopen_file_spec, '?');
1.1       parser    270: 
                    271:                const char *error=dlopen_file_spec?
                    272:                        dlink(dlopen_file_spec):"client library column is empty";
                    273:                if(!error) {
1.39      paf       274:                        error=options2env(options, 0);
1.1       parser    275: 
1.5       paf       276:                        if(!error)
                    277:                                OCIInitialize((ub4)OCI_THREADED/*| OCI_OBJECT*/, (dvoid *)0, 
1.74      moko      278:                                        (dvoid * (*)(void *, size_t))0, 
                    279:                                        (dvoid * (*)(void*, void*, size_t))0,  
1.5       paf       280:                                        (void (*)(void*, void*))0 
                    281:                                );
1.1       parser    282:                }
                    283: 
                    284:                return error;
                    285:        }
                    286: 
                    287:        /**     connect
1.58      paf       288:                @param url
1.4       paf       289:                        format: @b user:pass@service?
1.69      misha     290:                                ORACLE_HOME=/u01/app/oracle/product/8.1.5&
                    291:                                ORA_NLS33=/u01/app/oracle/product/8.1.5/ocommon/nls/admin/data&
                    292:                                NLS_LANG=RUSSIAN_AMERICA.CL8MSWIN1251&
                    293:                                ORA_ENCRYPT_LOGIN=TRUE&
                    294:                                ClientCharset=charset&
                    295:                                LowerCaseColumnNames=0
1.4       paf       296: 
                    297:                @todo environment manupulation doesnt look thread safe
1.58      paf       298:                @todo allocate 'aused_only_in_connect_url' on gc heap, so it can be manipulated directly
1.1       parser    299:        */
                    300:        void connect(
1.69      misha     301:                        char *url, 
                    302:                        SQL_Driver_services& services, 
                    303:                        void **connection_ref ///< output: Connection *
1.56      paf       304:                ) 
                    305:        {
                    306:                // connections are cross-request, do not use services._alloc [linked with request]
1.69      misha     307:                Connection& connection=*(Connection *)services.malloc(sizeof(Connection));
1.49      paf       308:                connection.services=&services;
1.69      misha     309:                connection.options.bLowerCaseColumnNames=true;
1.72      misha     310:                connection.options.bDisableQueryModification=false;
1.50      paf       311:                *connection_ref=&connection;
1.1       parser    312: 
1.58      paf       313:                char *user=url;
1.67      paf       314:                char *service=rsplit(user, '@');
1.1       parser    315:                char *pwd=lsplit(user, ':');
1.4       paf       316:                char *options=lsplit(service, '?');
1.1       parser    317: 
                    318:                if(!(user && pwd && service))
                    319:                        services._throw("mailformed connect part, must be 'user:pass@service'");
                    320: 
1.49      paf       321:                if(const char *error=options2env(options, &connection.options))
1.5       paf       322:                        services._throw(error);
1.4       paf       323: 
1.49      paf       324:                if(setjmp(connection.mark))
                    325:                        services._throw(connection.error);
1.1       parser    326: 
                    327:                // Allocate and initialize OCIError handle, attempt #1
                    328:                /*
                    329:                        grabbed from sample 
                    330:                        /server.804/a58234/oci_func.htm#446192
                    331:                        but doc 
                    332:                        /server.804/a58234/oci_func.htm#446100
                    333:                        doesnt have this param listed as allowed
                    334:                        8.1.6 client library barks as OCI_INVALID_HANDLE
                    335:                        and debugging revealed that OCI_HTYPE_ENV param value is invalid
                    336:                        later in doc 
                    337:                        /server.804/a58234/oci_func.htm#446192
                    338:                        on OCIEnvInit thay say
                    339:                        "No changes are done to an already initialized handle"
                    340:                        think, this is some sort of backward compatibility wonder.
                    341:                        leaving as it is, and without check()
                    342:                */
1.49      paf       343:                OCIHandleAlloc((dvoid *)NULL, (dvoid **) &connection.envhp, (ub4)OCI_HTYPE_ENV, 0, 0);
1.1       parser    344:                // Initialize an environment handle, attempt #2
1.49      paf       345:                check(connection, "EnvInit", OCIEnvInit(
                    346:                        &connection.envhp, (ub4)OCI_DEFAULT, 0, 0));            
1.1       parser    347:                // Allocate and initialize OCIError handle
1.49      paf       348:                check(connection, "HandleAlloc errhp", OCIHandleAlloc( 
                    349:                        (dvoid *)connection.envhp, (dvoid **) &connection.errhp, (ub4)OCI_HTYPE_ERROR, 0, 0));
1.1       parser    350:                // Allocate and initialize OCIServer handle
1.49      paf       351:                check(connection, "HandleAlloc srvhp", OCIHandleAlloc( 
                    352:                        (dvoid *)connection.envhp, (dvoid **) &connection.srvhp, (ub4)OCI_HTYPE_SERVER, 0, 0));         
1.1       parser    353:                // Attach to a 'service'; initialize server context handle  
1.49      paf       354:                check(connection, "ServerAttach", OCIServerAttach( 
                    355:                        connection.srvhp, connection.errhp, (text *)service, (sb4)strlen(service), (ub4)OCI_DEFAULT));
1.1       parser    356:                // Allocate and initialize OCISvcCtx handle
1.49      paf       357:                check(connection, "HandleAlloc svchp", OCIHandleAlloc( 
                    358:                        (dvoid *)connection.envhp, (dvoid **) &connection.svchp, (ub4)OCI_HTYPE_SVCCTX, 0, 0));         
1.1       parser    359:                // set attribute server context in the service context
1.49      paf       360:                check(connection, "AttrSet server-service", OCIAttrSet( 
                    361:                        (dvoid *)connection.svchp, (ub4)OCI_HTYPE_SVCCTX, 
                    362:                        (dvoid *)connection.srvhp, (ub4)0, 
                    363:                        (ub4)OCI_ATTR_SERVER, (OCIError *)connection.errhp));           
1.1       parser    364:                // allocate a user context handle
1.49      paf       365:                check(connection, "HandleAlloc usrhp", OCIHandleAlloc(
                    366:                        (dvoid *)connection.envhp, (dvoid **)&connection.usrhp, (ub4)OCI_HTYPE_SESSION, 0, 0));
1.1       parser    367:                // set 'user' name
1.49      paf       368:                check(connection, "AttrSet user-session", OCIAttrSet(
                    369:                        (dvoid *)connection.usrhp, (ub4)OCI_HTYPE_SESSION, 
1.1       parser    370:                        (dvoid *)user, (ub4)strlen(user), 
1.49      paf       371:                        OCI_ATTR_USERNAME, connection.errhp));          
1.1       parser    372:                // set 'pwd' password
1.49      paf       373:                check(connection, "AttrSet pwd-session", OCIAttrSet(
                    374:                        (dvoid *)connection.usrhp, (ub4)OCI_HTYPE_SESSION, 
1.1       parser    375:                        (dvoid *)pwd, (ub4)strlen(pwd), 
1.49      paf       376:                        OCI_ATTR_PASSWORD, connection.errhp));
1.1       parser    377:                // Authenticate a user  
1.49      paf       378:                check(connection, "SessionBegin", OCISessionBegin(
                    379:                        connection.svchp, connection.errhp, connection.usrhp, 
1.1       parser    380:                        OCI_CRED_RDBMS, OCI_DEFAULT));
                    381:                // remember connection in session
1.49      paf       382:                check(connection, "AttrSet service-session", OCIAttrSet(
                    383:                        (dvoid *)connection.svchp, (ub4)OCI_HTYPE_SVCCTX, 
                    384:                        (dvoid *)connection.usrhp, (ub4)0, 
                    385:                        OCI_ATTR_SESSION, connection.errhp));
1.1       parser    386:        }
1.69      misha     387: 
1.49      paf       388:        void disconnect(void *aconnection) {
                    389:            Connection& connection=*static_cast<Connection *>(aconnection);
1.47      paf       390: 
1.58      paf       391:                // free fetch buffers. leave that to GC [no such services func. yet?]
                    392:                /*
1.47      paf       393:                for(int i=0; i<MAX_COLS; i++) {
1.49      paf       394:                        if(void* fetch_buffer=connection.fetch_buffers[i])
1.58      paf       395:                                connection.services->free(fetch_buffer);
1.47      paf       396:                        else
                    397:                                break;
1.58      paf       398:                }
                    399:                */
1.47      paf       400: 
1.1       parser    401:                // Terminate a user session
                    402:                OCISessionEnd(
1.49      paf       403:                        connection.svchp, connection.errhp, connection.usrhp, (ub4)OCI_DEFAULT);
1.1       parser    404:                // Detach from a server; uninitialize server context handle
                    405:                OCIServerDetach(
1.49      paf       406:                        connection.srvhp, connection.errhp, (ub4)OCI_DEFAULT);
1.1       parser    407:                // Free a previously allocated handles
                    408:                /* 
                    409:                oci will free them up as belonging to env
                    410:                OCIHandleFree(
1.49      paf       411:                        (dvoid *)connection.srvhp, (ub4)OCI_HTYPE_SERVER);
1.1       parser    412:                OCIHandleFree(
1.49      paf       413:                        (dvoid *)connection.svchp, (ub4)OCI_HTYPE_SVCCTX);
1.1       parser    414:                OCIHandleFree(
1.49      paf       415:                        (dvoid *)connection.errhp, (ub4)OCI_HTYPE_ERROR);
1.1       parser    416:                */
                    417:                OCIHandleFree(
1.49      paf       418:                        (dvoid *)connection.envhp, (ub4)OCI_HTYPE_ENV);
1.1       parser    419: 
1.58      paf       420:                // free connection. leave that to GC [no such services func. yet?]
                    421:                // connection.services->free(&connection);
1.1       parser    422:        }
1.69      misha     423: 
1.49      paf       424:        void commit(void *aconnection) {
                    425:            Connection& connection=*static_cast<Connection *>(aconnection);
                    426:                if(setjmp(connection.mark))
                    427:                        connection.services->_throw(connection.error);
1.1       parser    428: 
1.49      paf       429:                check(connection, "commit", OCITransCommit(connection.svchp, connection.errhp, 0));
1.1       parser    430:        }
1.69      misha     431: 
1.49      paf       432:        void rollback(void *aconnection) {
                    433:            Connection& connection=*static_cast<Connection *>(aconnection);
                    434:                if(setjmp(connection.mark))
                    435:                        connection.services->_throw(connection.error);
1.1       parser    436: 
1.42      paf       437:                // sometimes rollback is done in context when this yields error which masks previous error
                    438:                // consider consequent errors not very important to report, reporting first one
1.49      paf       439:                /*check(connection, "rollback", */OCITransRollback(connection.svchp, connection.errhp, 0)/*)*/;
1.1       parser    440:        }
                    441: 
1.45      paf       442:        bool ping(void* /*connection*/) {
1.1       parser    443:                // maybe OCIServerVersion?
1.4       paf       444:                // select 0 from dual
1.1       parser    445:                return true;
                    446:        }
                    447: 
1.73      moko      448:        // charset here is services.request_charset(), not connection.client_charset
                    449:        // thus we can't use the sql server quoting support
                    450:        const char* quote(void *aconnection, const char *str, unsigned int length) 
1.42      paf       451:        {
1.73      moko      452:                const char* from;
                    453:                const char* from_end=str+length;
                    454: 
                    455:                size_t quoted=0;
                    456: 
                    457:                for(from=str; from<from_end; from++){
                    458:                        if(*from=='\'')
                    459:                                quoted++;
                    460:                }
                    461: 
                    462:                if(!quoted)
                    463:                        return str;
                    464: 
                    465:                Connection& connection=*static_cast<Connection*>(aconnection);
                    466:                char *result=(char*)connection.services->malloc_atomic(length + quoted + 1);
                    467:                char *to = result;
                    468: 
                    469:                for(from=str; from<from_end; from++){
                    470:                        if(*from=='\'')
                    471:                                *to++= '\''; // ' -> ''
                    472:                        *to++=*from;
1.32      paf       473:                }
1.73      moko      474:                
1.32      paf       475:                *to=0;
                    476:                return result;
1.1       parser    477:        }
1.69      misha     478: 
1.60      paf       479:        void query(void* aconnection, 
1.69      misha     480:                        const char* astatement, 
                    481:                        size_t placeholders_count, Placeholder* placeholders, 
                    482:                        unsigned long offset, unsigned long limit,
                    483:                        SQL_Driver_query_event_handlers& handlers
                    484:        ){
1.61      paf       485: 
1.49      paf       486:                Connection& connection=*static_cast<Connection *>(aconnection);
1.60      paf       487:                Query_lobs lobs={{0}, 0};
1.1       parser    488:                OCIStmt *stmthp=0;
                    489: 
1.49      paf       490:                SQL_Driver_services& services=*connection.services;
1.42      paf       491: 
1.1       parser    492:                bool failed=false;
1.49      paf       493:                if(setjmp(connection.mark)) {
1.1       parser    494:                        failed=true;
                    495:                        goto cleanup;
                    496:                } else {
1.61      paf       497:                        if(placeholders_count>MAX_BINDS)
                    498:                                fail(connection, "too many bind variables");
1.71      misha     499:                        
                    500:                        while(isspace((unsigned char)*astatement)) 
                    501:                                astatement++;
1.61      paf       502: 
1.71      misha     503:                        const char* client_charset=connection.options.client_charset;
                    504:                        const char* request_charset=services.request_charset();
                    505:                        bool transcode_needed=transcode_required(connection);
                    506: 
                    507:                        if(transcode_needed){
                    508:                                // transcode query from $request:charset to ?ClientCharset
                    509:                                size_t transcoded_xxx_size;
                    510:                                services.transcode(astatement, strlen(astatement),
                    511:                                        astatement, transcoded_xxx_size,
                    512:                                        request_charset,
                    513:                                        client_charset);
                    514:                        }
                    515: 
                    516:                        const char *statement=_preprocess_statement_lobs(connection, astatement, lobs);
                    517: 
                    518:                        modified_statement mstatement=_preprocess_statement_limit(connection, statement, offset, limit);
                    519:                        statement=mstatement.statement;
                    520: 
                    521:                        if(mstatement.limit) // limit was added in statement
                    522:                                limit=SQL_NO_LIMIT;
                    523:                        if(mstatement.offset) // limit was added in statement
                    524:                                offset=0;
1.1       parser    525: 
1.49      paf       526:                        check(connection, "HandleAlloc STMT", OCIHandleAlloc( 
                    527:                                (dvoid *)connection.envhp, (dvoid **) &stmthp, (ub4)OCI_HTYPE_STMT, 0, 0));
                    528:                        check(connection, "syntax", 
                    529:                                OCIStmtPrepare(stmthp, connection.errhp, (unsigned char *)statement, 
1.1       parser    530:                                (ub4)strlen((char *)statement), 
                    531:                                (ub4)OCI_NTV_SYNTAX, (ub4)OCI_DEFAULT));
1.60      paf       532: 
                    533:                        struct Bind_info {
                    534:                                        OCIBind *bind;
                    535:                                        sb2 indicator;
                    536:                        };
                    537: 
                    538:                        int binds_size=sizeof(Bind_info) * placeholders_count;
1.65      paf       539:                        // we DO store OCIBind* into ATOMIC gc memory, 
                    540:                        // but we do not allocate/free it, that's done automatically from oracle [using environment handles]
                    541:                        // so we don't have to bother with that
1.60      paf       542:                        Bind_info* binds=static_cast<Bind_info*>(services.malloc_atomic(binds_size));
1.1       parser    543:                        {
1.60      paf       544:                                for(size_t i=0; i<placeholders_count; i++) {
                    545:                                        Placeholder& ph=placeholders[i];
                    546:                                        Bind_info& bi=binds[i];
                    547:                                        bi.bind=0;
1.61      paf       548:                                        // http://i/docs/oracle/server.804/a58234/basics.htm#422173
                    549:                                        bi.indicator=ph.is_null? -1: MAGIC_INDICATOR_VALUE_MEANING_NOT_NULL_AND_UNCHANGED;
1.60      paf       550: 
                    551:                                        size_t value_length;
                    552: 
1.69      misha     553:                                        if(transcode_needed){
                    554:                                                // transcode bind variables names and their values from $request:charset to ?ClientCharset
1.60      paf       555:                                                size_t name_length;
                    556:                                                services.transcode(ph.name, strlen(ph.name),
                    557:                                                        ph.name, name_length,
1.71      misha     558:                                                        request_charset,
                    559:                                                        client_charset);
1.60      paf       560: 
                    561:                                                if(ph.value)
                    562:                                                        services.transcode(ph.value, strlen(ph.value),
                    563:                                                                ph.value, value_length,
1.71      misha     564:                                                                request_charset,
                    565:                                                                client_charset);
1.60      paf       566:                                        } else {
                    567:                                                value_length=ph.value? strlen(ph.value): 0;
                    568:                                        }
                    569: 
1.65      paf       570:                                        // clone value for possible output binds
                    571:                                        // note: even empty input can be replaced by huge output
                    572:                                        char*& value_buf=connection.bind_buffers[i]; // get cached buffer
                    573:                                        if(!value_buf) // allocate if needed, caching it
                    574:                                                value_buf=(char *)services.malloc_atomic(MAX_OUT_STRING_LENGTH+1/*terminator*/);
                    575:                                        if(value_length)
                    576:                                                memcpy(value_buf, ph.value, value_length+1);
1.66      paf       577:                                        else
                    578:                                                value_buf[0]=0;
1.61      paf       579: 
1.65      paf       580:                                        char name_buf[MAX_STRING];
                    581:                                        sb4 placeh_len=snprintf(name_buf, sizeof(name_buf), ":%s", ph.name);
1.61      paf       582:                                        char check_step_buf[MAX_STRING];
                    583:                                        snprintf(check_step_buf, sizeof(check_step_buf), "bind by name :%s", ph.name);
                    584:                                        check(connection, check_step_buf, OCIBindByName(stmthp, 
1.60      paf       585:                                                &bi.bind, connection.errhp, 
1.65      paf       586:                                                (text*)name_buf, placeh_len,
                    587:                                                (dvoid *)value_buf, (sword)(MAX_OUT_STRING_LENGTH+1), SQLT_STR, (dvoid *)&bi.indicator, 
1.60      paf       588:                                                (ub2 *)0, (ub2 *)0, (ub4)0, (ub4 *)0, OCI_DEFAULT));
                    589:                                }
                    590: 
1.1       parser    591:                                for(int i=0; i<lobs.count; i++) {
1.60      paf       592:                                        Query_lobs::Item &item=lobs.items[i];
1.49      paf       593:                                        check(connection, "alloc output var desc", OCIDescriptorAlloc(
1.57      paf       594:                                                (dvoid *)connection.envhp, (dvoid **)&item.locator, (ub4)OCI_DTYPE_LOB, 0, 0));
1.1       parser    595: 
1.59      paf       596:                                        char placeholder_buf[MAX_STRING];
                    597:                                        sb4 placeh_len=snprintf(placeholder_buf, sizeof(placeholder_buf), 
                    598:                                                ":%.*s", item.name_size, item.name_ptr);
                    599:                                        check(connection, "bind lob", OCIBindByName(stmthp, 
1.57      paf       600:                                                &item.bind, connection.errhp, 
1.59      paf       601:                                                (text*)placeholder_buf, placeh_len,
1.57      paf       602:                                                (dvoid *)&item.locator, 
                    603:                                                (sword)sizeof (item.locator), SQLT_CLOB, (dvoid *)0, 
1.1       parser    604:                                                (ub2 *)0, (ub2 *)0, (ub4)0, (ub4 *)0, OCI_DATA_AT_EXEC));
                    605: 
1.57      paf       606:                                        item.rows.count=0;
                    607:                                        item.connection=&connection;
1.49      paf       608:                                        check(connection, "bind dynamic", OCIBindDynamic(
1.57      paf       609:                                                item.bind, connection.errhp, 
                    610:                                                (dvoid *) &item, cbf_no_data, 
                    611:                                                (dvoid *) &item, cbf_get_data));
1.1       parser    612:                                }
                    613:                        }
                    614: 
1.49      paf       615:                        execute_prepared(connection, 
1.26      paf       616:                                statement, stmthp, lobs, 
1.71      misha     617:                                offset, limit, handlers, mstatement.skip_rownum_column);
1.60      paf       618: 
                    619:                        {
                    620:                                for(size_t i=0; i<placeholders_count; i++) {
                    621:                                        Bind_info& bi=binds[i];
1.61      paf       622:                                        if(bi.indicator==MAGIC_INDICATOR_VALUE_MEANING_NOT_NULL_AND_UNCHANGED/*unchanged*/)
1.60      paf       623:                                                continue;
                    624: 
1.65      paf       625:                                        Placeholder& ph=placeholders[i];
1.60      paf       626:                                        if(bi.indicator==-1)
                    627:                                                ph.is_null=true;
                    628:                                        else
                    629:                                                if(bi.indicator==0)
                    630:                                                        ph.is_null=false;
                    631:                                                else
                    632:                                                        fail(connection, bi.indicator<0?
1.61      paf       633:                                                                "output bind buffer overflow, additionally size too big to be returned in 'indicator'"
                    634:                                                                : "output bind buffer overflow");
1.60      paf       635: 
                    636:                                        ph.were_updated=true;
1.65      paf       637:                                        const char* bind_buffer=connection.bind_buffers[i];
                    638:                                        if( size_t value_length=strlen(bind_buffer) ) {
                    639:                                                char* returned_value=(char*)services.malloc_atomic(value_length+1/*terminator*/);
                    640:                                                memcpy(returned_value, bind_buffer, value_length+1 );
                    641:                                                ph.value=returned_value;
                    642: 
1.69      misha     643:                                                if(transcode_needed){
                    644:                                                        // transcode bind variable output from ?ClientCharset to $request:charset
1.65      paf       645:                                                        services.transcode(ph.value, value_length,
                    646:                                                                ph.value, value_length/*<this new value is not used afterwards, actually*/,
1.71      misha     647:                                                                client_charset,
                    648:                                                                request_charset);
1.60      paf       649:                                                }
1.65      paf       650:                                        } else {
                    651:                                                ph.value=0;
1.60      paf       652:                                        }
                    653:                                }
                    654:                        }
1.1       parser    655:                }
                    656: cleanup: // no check call after this point!
                    657:                {
                    658:                        for(int i=0; i<lobs.count; i++) {
                    659:                                /* free var locator */
                    660:                                if(OCILobLocator *locator=lobs.items[i].locator)
                    661:                                        OCIDescriptorFree((dvoid *)locator, (ub4)OCI_DTYPE_LOB);
                    662: 
                    663:                                /* free rows descriptors */
1.60      paf       664:                                Query_lobs::return_rows &rows=lobs.items[i].rows;
1.1       parser    665:                                for(int r=0; r<rows.count; r++)
                    666:                                        OCIDescriptorFree((dvoid *)rows.row[r].locator, (ub4)OCI_DTYPE_LOB);
                    667:                        }
                    668:                }
                    669:                if(stmthp)
                    670:                        OCIHandleFree((dvoid *)stmthp, (ub4)OCI_HTYPE_STMT);
                    671: 
1.26      paf       672:                if(failed) {
1.49      paf       673:                        if(connection.sql_error.defined())
                    674:                                services._throw(connection.sql_error);
                    675:                        services._throw(connection.error);
1.26      paf       676:                }
1.1       parser    677:        }
                    678: 
                    679: private: // private funcs
                    680: 
1.71      misha     681:        const char* _preprocess_statement_lobs(
                    682:                        Connection& connection, 
                    683:                        const char* statement,
                    684:                        Query_lobs &lobs
                    685:        ){
                    686:                size_t statement_size=strlen(statement);
1.49      paf       687:                SQL_Driver_services& services=*connection.services;
1.71      misha     688:                
1.32      paf       689:                char *result=(char *)services.malloc_atomic(statement_size
1.1       parser    690:                        +MAX_STRING // in case of short 'strings'
                    691:                        +11/* returning */+6/* into */+(MAX_LOB_NAME_LENGTH+2/*:, */)*2/*ret into*/*MAX_IN_LOBS
                    692:                        +1);
1.71      misha     693:                const char *o=statement;
1.1       parser    694: 
                    695:                // /**xxx**/'literal' -> EMPTY_CLOB_FUNC_CALL
                    696:                char *n=result;
                    697:                while(*o) {
                    698:                        if(
                    699:                                o[0]=='/' &&
                    700:                                o[1]=='*' && 
                    701:                                o[2]=='*') { // name start
1.34      paf       702:                                const char* saved_o=o;
1.1       parser    703:                                o+=3;
                    704:                                const char *name_begin=o;
                    705:                                while(*o)
                    706:                                        if(
                    707:                                                o[0]=='*' &&
                    708:                                                o[1]=='*' &&
                    709:                                                o[2]=='/' &&
                    710:                                                o[3]=='\'') { // name end
1.34      paf       711:                                                saved_o=0; // found, marking that
1.1       parser    712:                                                const char *name_end=o;
                    713:                                                o+=4;
1.60      paf       714:                                                Query_lobs::Item &item=lobs.items[lobs.count++];
1.1       parser    715:                                                item.name_ptr=name_begin; item.name_size=name_end-name_begin;
1.32      paf       716:                                                item.data_ptr=(char *)services.malloc_atomic(statement_size/*max*/); item.data_size=0;
1.1       parser    717: 
                    718:                                                const char *start=o;
                    719:                                                bool escaped=false;
                    720:                                                while(*o && !(o[0]=='\'' && o[1]!='\'' && !escaped)) {
1.68      misha     721:                                                        escaped=!escaped && (o[0]=='\'' && o[1]=='\'');
1.1       parser    722:                                                        if(escaped) {
                    723:                                                                // write pending, skip "\" or "'"
                    724:                                                                if(size_t size=o-start) {
                    725:                                                                        memcpy(item.data_ptr+item.data_size, start, size);
                    726:                                                                        item.data_size+=size;
                    727:                                                                }
                    728:                                                                start=++o;
                    729:                                                        } else
                    730:                                                                o++;
                    731:                                                }
                    732:                                                if(size_t size=o-start) {
                    733:                                                        memcpy(item.data_ptr+item.data_size, start, size);
                    734:                                                        item.data_size+=size;
                    735:                                                }
                    736:                                                if(*o)
                    737:                                                        o++; // skip "'"
                    738: 
                    739:                                                n+=sprintf(n, EMPTY_CLOB_FUNC_CALL);
                    740:                                                break;
                    741:                                        } else
                    742:                                                o++; // /**skip**/'xxx'
1.34      paf       743:                                if(saved_o) {
                    744:                                        o=saved_o;
                    745:                                        *n++=*o++;
                    746:                                }
1.1       parser    747:                        } else
                    748:                                *n++=*o++;
                    749:                }
                    750:                *n=0;
                    751: 
                    752:                if(lobs.count) {
                    753:                        int i;
                    754:                        n+=sprintf(n, " returning ");
                    755:                        for(i=0; i<lobs.count; i++) {
                    756:                                if(i)
                    757:                                        *n++=',';
                    758:                                n+=sprintf(n, "%.*s", lobs.items[i].name_size, lobs.items[i].name_ptr);
                    759:                        }
                    760:                        n+=sprintf(n, " into ");
                    761:                        for(i=0; i<lobs.count; i++) {
                    762:                                if(i)
1.41      paf       763:                                        *n++=',';
1.1       parser    764:                                n+=sprintf(n, ":%.*s", lobs.items[i].name_size, lobs.items[i].name_ptr);
                    765:                        }
                    766:                }
                    767: 
                    768:                return result;
                    769:        }
                    770: 
                    771:        void execute_prepared(
1.49      paf       772:                Connection& connection, 
1.71      misha     773:                const char* astatement, OCIStmt *stmthp, Query_lobs &lobs, 
                    774:                unsigned long offset, unsigned long limit,
                    775:                SQL_Driver_query_event_handlers& handlers, bool skip_rownum_column) {
1.1       parser    776: 
                    777:                ub2 stmt_type=0; // UNKNOWN
                    778:        /*
                    779:                //gpfs on sun. paf 000818
                    780:                //Zanyway, this is needed before. 
1.49      paf       781:                check(connection, "get stmt type", OCIAttrGet(
1.1       parser    782:                        (dvoid *)stmthp, (ub4)OCI_HTYPE_STMT, (ub1 *)&stmt_type, 
1.49      paf       783:                        (ub4 *)0, OCI_ATTR_STMT_TYPE, connection.errhp));
1.1       parser    784:        */
1.16      paf       785: 
1.71      misha     786:                if(strncasecmp(astatement, "select", 6)==0) 
1.1       parser    787:                        stmt_type=OCI_STMT_SELECT;
1.71      misha     788:                else if(strncasecmp(astatement, "insert", 6)==0)
1.1       parser    789:                        stmt_type=OCI_STMT_INSERT;
1.71      misha     790:                else if(strncasecmp(astatement, "update", 6)==0)
1.1       parser    791:                        stmt_type=OCI_STMT_UPDATE;
                    792: 
1.49      paf       793:                sword status=OCIStmtExecute(connection.svchp, stmthp, connection.errhp, 
1.1       parser    794:                        (ub4)stmt_type==OCI_STMT_SELECT?0:1, (ub4)0, 
                    795:                        (OCISnapshot *)NULL, 
                    796:                        (OCISnapshot *)NULL, (ub4)OCI_DEFAULT);
                    797: 
                    798:                if(status!=OCI_NO_DATA)
1.49      paf       799:                        check(connection, "execute", status);
1.1       parser    800: 
                    801:                {
                    802:                        for(int i=0; i<lobs.count; i++) 
                    803:                                if(ub4 bytes_to_write=lobs.items[i].data_size) {
1.60      paf       804:                                        Query_lobs::return_rows *rows=&lobs.items[i].rows;
1.1       parser    805:                                        for(int r=0; r<rows->count; r++) {
                    806:                                                OCILobLocator *locator=rows->row[r].locator;
1.49      paf       807:                                                check(connection, "lobwrite", OCILobWrite (
                    808:                                                        connection.svchp, connection.errhp, 
1.1       parser    809:                                                        locator, &bytes_to_write, 1, 
                    810:                                                        (dvoid *)lobs.items[i].data_ptr, (ub4)bytes_to_write, OCI_ONE_PIECE, 
                    811:                                                        (dvoid *)0, 0, (ub2)0, 
                    812:                                                        (ub1) SQLCS_IMPLICIT));
                    813:                                        }
                    814:                                }
                    815:                }
                    816:                
                    817:                switch(stmt_type) {
                    818:                case OCI_STMT_SELECT:
1.49      paf       819:                        fetch_table(connection,
1.1       parser    820:                                stmthp, offset, limit, 
1.71      misha     821:                                handlers, skip_rownum_column);
1.1       parser    822:                        break;
                    823:                default:
                    824:                /*
                    825:                case OCI_STMT_INSERT:
                    826:                case OCI_STMT_UPDATE:
                    827:                */
                    828:                        break;
                    829:                }
                    830:        }
                    831: 
1.49      paf       832:        void fetch_table(Connection& connection, 
1.71      misha     833:                OCIStmt *stmthp, unsigned long offset, unsigned long limit,
                    834:                SQL_Driver_query_event_handlers& handlers, bool skip_rownum_column) 
1.42      paf       835:        {
1.49      paf       836:                SQL_Driver_services& services=*connection.services;
1.12      paf       837: 
1.10      paf       838:                ub4 prefetch_rows=100;
1.49      paf       839:                check(connection, "AttrSet prefetch-rows", OCIAttrSet( 
1.9       paf       840:                        (dvoid *)stmthp, (ub4)OCI_HTYPE_STMT, 
                    841:                        (dvoid *)&prefetch_rows, (ub4)0, 
1.49      paf       842:                        (ub4)OCI_ATTR_PREFETCH_ROWS, (OCIError *)connection.errhp));
1.9       paf       843: 
1.20      paf       844:                ub4 prefetch_mem_size=100*0x400;
1.49      paf       845:                check(connection, "AttrSet prefetch-memory", OCIAttrSet( 
1.9       paf       846:                        (dvoid *)stmthp, (ub4)OCI_HTYPE_STMT, 
                    847:                        (dvoid *)&prefetch_mem_size, (ub4)0, 
1.49      paf       848:                        (ub4)OCI_ATTR_PREFETCH_MEMORY, (OCIError *)connection.errhp));
1.1       parser    849: 
1.69      misha     850:                OCIParam                *mypard;
                    851:                ub2                             dtype;
                    852:                const char*             col_name;
1.1       parser    853: 
1.40      paf       854:                struct Col {
1.1       parser    855:                        ub2 type;
                    856:                        char *str;
                    857:                        OCILobLocator *var;
                    858:                        OCIDefine *def;
                    859:                        sb2 indicator;
                    860:                } cols[MAX_COLS]={0};
                    861:                int column_count=0;
                    862: 
                    863:                bool failed=false;
1.49      paf       864:                jmp_buf saved_mark; memcpy(saved_mark, connection.mark, sizeof(jmp_buf));
                    865:                if(setjmp(connection.mark)) {
1.1       parser    866:                        failed=true;
                    867:                        goto cleanup;
                    868:                } else {
1.71      misha     869:                        bool transcode_needed=transcode_required(connection);
                    870:                        const char* client_charset=connection.options.client_charset;
                    871:                        const char* request_charset=services.request_charset();
                    872: 
1.27      paf       873:                        // idea of preincrementing is that at error time all handles would free up
                    874:                        while(++column_count<=MAX_COLS) {
                    875:                                /* get next descriptor, if there is one */
1.49      paf       876:                                if(OCIParamGet(stmthp, OCI_HTYPE_STMT, connection.errhp, (void **)&mypard, 
1.27      paf       877:                                        (ub4) column_count)!=OCI_SUCCESS) {
                    878:                                        --column_count;
                    879:                                        break;
                    880:                                }
1.71      misha     881: 
                    882:                                if(skip_rownum_column && column_count==1)
                    883:                                        continue;
                    884: 
1.27      paf       885:                                /* Retrieve the data type attribute */
1.49      paf       886:                                check(connection, "get type", OCIAttrGet(
1.27      paf       887:                                        (dvoid*) mypard, (ub4)OCI_DTYPE_PARAM, 
                    888:                                        (dvoid*) &dtype, (ub4 *)0, (ub4)OCI_ATTR_DATA_TYPE, 
1.49      paf       889:                                        (OCIError *)connection.errhp));
1.27      paf       890:                                
                    891:                                /* Retrieve the column name attribute */
                    892:                                ub4 col_name_len;
1.49      paf       893:                                check(connection, "get name", OCIAttrGet(
1.27      paf       894:                                        (dvoid*) mypard, (ub4)OCI_DTYPE_PARAM, 
                    895:                                        (dvoid**) &col_name, (ub4 *) &col_name_len, (ub4)OCI_ATTR_NAME, 
1.49      paf       896:                                        (OCIError *)connection.errhp));
1.69      misha     897: 
1.74      moko      898:                                size_t length=(size_t)col_name_len;
1.69      misha     899:                                if(transcode_needed){
                    900:                                        // transcode column name from ?ClientCharset to $request:charset
1.51      paf       901:                                        services.transcode(col_name, col_name_len,
1.74      moko      902:                                                col_name, length,
1.71      misha     903:                                                client_charset,
                    904:                                                request_charset);
1.51      paf       905:                                }
                    906: 
1.40      paf       907:                                Col& col=cols[column_count-1];
1.27      paf       908:                                {
1.38      paf       909:                                        char *ptr=(char *)services.malloc_atomic(length+1);
1.49      paf       910:                                        if( connection.options.bLowerCaseColumnNames ) 
1.55      paf       911:                                                tolower_str(ptr, col_name, length);
1.39      paf       912:                                        else
                    913:                                                memcpy(ptr, col_name, length);                                          
1.38      paf       914:                                        ptr[length]=0;
1.49      paf       915:                                        check(connection, handlers.add_column(connection.sql_error, ptr, length));
1.27      paf       916:                                }
                    917:                                
                    918:                                ub2 coerce_type=dtype;
                    919:                                sb4 size=0;
                    920:                                void *ptr;
                    921:                                
                    922:                                switch(dtype) {
1.69      misha     923:                                        case SQLT_CLOB: 
                    924:                                                {
                    925:                                                        check(connection, "alloc output var desc", OCIDescriptorAlloc(
                    926:                                                                (dvoid *)connection.envhp, (dvoid **)(ptr=&col.var), 
                    927:                                                                (ub4)OCI_DTYPE_LOB, 
                    928:                                                                0, (dvoid **)0));
                    929:                                                        
                    930:                                                        size=0;
                    931:                                                        break;
                    932:                                                }
                    933:                                        default:
                    934:                                                coerce_type=SQLT_STR;
                    935:                                                char*& buf=connection.fetch_buffers[column_count-1];
                    936:                                                ptr=buf; // get cached buffer
                    937:                                                if(!ptr) // allocate if needed, caching it
                    938:                                                        ptr=buf=(char *)services.malloc_atomic(MAX_OUT_STRING_LENGTH+1/*terminator*/);
                    939:                                                col.str=(char*)ptr;
                    940:                                                size=MAX_OUT_STRING_LENGTH;
1.1       parser    941:                                                break;
                    942:                                }
                    943:                                
1.40      paf       944:                                col.type=coerce_type;
1.1       parser    945:                                
1.48      paf       946:                                // http://i/docs/oracle/server.804/a58234/oci_func.htm#449680
                    947:                                //   this call implicitly allocates the define handle
                    948:                                // http://sunsite.eunnet.net/documentation/oracle.8.0.4/server.804/a58234/basics.htm
                    949:                                //   when a statement handle is freed, any bind and define handles associated with it 
                    950:                                //   are also freed
1.49      paf       951:                                col.def=0; check(connection, "DefineByPos", OCIDefineByPos(
                    952:                                        stmthp, &col.def, connection.errhp, 
1.27      paf       953:                                        column_count, (ub1 *) ptr, size, 
1.40      paf       954:                                        coerce_type, (dvoid *) &col.indicator, 
1.27      paf       955:                                        (ub2 *)0, (ub2 *)0, OCI_DEFAULT));
                    956:                        }
                    957:                        
1.49      paf       958:                        check(connection, handlers.before_rows(connection.sql_error));
1.27      paf       959:                        
1.69      misha     960:                        for(unsigned long row=0; limit==SQL_NO_LIMIT || row<offset+limit; row++) {
1.49      paf       961:                                sword status=OCIStmtFetch(stmthp, connection.errhp, (ub4)1,  (ub4)OCI_FETCH_NEXT, 
1.27      paf       962:                                        (ub4)OCI_DEFAULT);
                    963:                                if(status==OCI_NO_DATA)
                    964:                                        break;
1.49      paf       965:                                check(connection, "fetch", status);
1.3       paf       966: 
1.27      paf       967:                                if(row>=offset) {
1.49      paf       968:                                        check(connection, handlers.add_row(connection.sql_error));
1.27      paf       969:                                        for(int i=0; i<column_count; i++) {
1.71      misha     970:                                                if(skip_rownum_column && i==0)
                    971:                                                        continue;
                    972: 
1.37      paf       973:                                                size_t length=0;
1.42      paf       974:                                                char* strm=0;
1.69      misha     975:                                                bool transcode_value=transcode_needed;
1.60      paf       976: 
                    977:                                                sb2 indicator=cols[i].indicator;
                    978:                                                if(indicator!=-1) { // not NULL
                    979:                                                        if(indicator!=0)
                    980:                                                                fail(connection, indicator<0?
                    981:                                                                        "column return buffer overflow, additionally size too big to be returned in 'indicator'"
                    982:                                                                        : "column return buffer overflow");
1.69      misha     983:                                                        
                    984:                                                        switch(cols[i].type){
                    985:                                                                case SQLT_CLOB: 
                    986:                                                                        {
                    987:                                                                                ub4   offset=1;
                    988:                                                                                OCILobLocator *var=(OCILobLocator *)cols[i].var;
                    989:                                                                                size_t read_size=0;
                    990:                                                                                strm=(char*)services.malloc_atomic(1/*for terminator*/); // set type of memory block
                    991:                                                                                do {
                    992:                                                                                        char buf[MAX_STRING*10];
                    993:                                                                                        ub4   amtp=0/*to be read in stream mode*/;
                    994:                                                                                        // http://i/docs/oracle/server.804/a58234/oci_func.htm#427818
                    995:                                                                                        status=OCILobRead(connection.svchp, connection.errhp, 
                    996:                                                                                                var, &amtp, offset, (dvoid *)buf, 
                    997:                                                                                                sizeof(buf), 
                    998:                                                                                                (dvoid *)0, 0, 
                    999:                                                                                                (ub2)0, (ub1)SQLCS_IMPLICIT);
                   1000:                                                                                        if(status!=OCI_SUCCESS && status!=OCI_NEED_DATA)
                   1001:                                                                                                check(connection, "lobread", status);
                   1002: 
                   1003:                                                                                        strm=(char*)services.realloc(strm, read_size+amtp+1/*for termintator*/);
                   1004:                                                                                        memcpy(strm+read_size, buf, amtp);
                   1005:                                                                                        read_size+=amtp;
                   1006:                                                                                        offset+=amtp;
                   1007:                                                                                } while(status==OCI_NEED_DATA);
                   1008: 
                   1009:                                                                                length=(size_t)read_size;
                   1010:                                                                                strm[length]=0;
                   1011:                                                                                break;
                   1012:                                                                        }
                   1013:                                                                case SQLT_NUM:
                   1014:                                                                case SQLT_INT:
                   1015:                                                                case SQLT_FLT:
                   1016:                                                                case SQLT_LNG:
                   1017:                                                                case SQLT_RID:
                   1018:                                                                case SQLT_UIN:
                   1019:                                                                case SQLT_DATE:
                   1020:                                                                case SQLT_TIME:
                   1021:                                                                case SQLT_TIMESTAMP:
1.71      misha    1022:                                                                        transcode_value=false; // transcode calls not needed for numbers and dates
1.69      misha    1023:                                                                default:
                   1024:                                                                        if(const char *value=cols[i].str) {
                   1025:                                                                                length=strlen(value);
                   1026:                                                                                strm=(char*)services.malloc_atomic(length+1);
                   1027:                                                                                memcpy(strm, value, length+1);
                   1028:                                                                        } else {
                   1029:                                                                                length=0;
                   1030:                                                                                strm=0;
                   1031:                                                                        }
1.1       parser   1032:                                                                        break;
1.27      paf      1033:                                                        }
1.60      paf      1034:                                                }
1.42      paf      1035: 
                   1036:                                                const char* str=strm;
1.69      misha    1037:                                                if(transcode_value && str && length){
                   1038:                                                        // transcode cell value from ?ClientCharset to $request:charset
                   1039:                                                        services.transcode(str, length,
                   1040:                                                                str, length,
1.71      misha    1041:                                                                client_charset,
                   1042:                                                                request_charset);
1.42      paf      1043:                                                }
                   1044: 
1.49      paf      1045:                                                check(connection, handlers.add_row_cell(connection.sql_error, str, length));
1.1       parser   1046:                                        }
                   1047:                                }
                   1048:                        }
                   1049:                }
                   1050: 
                   1051: cleanup: // no check call after this point!
                   1052:                for(int i=0; i<column_count; i++) {
                   1053:                        switch(cols[i].type) {
                   1054:                        case SQLT_CLOB:
                   1055:                                /* free var locator */
                   1056:                                OCIDescriptorFree((dvoid *) cols[i].var, (ub4)OCI_DTYPE_LOB);
                   1057:                                break;
                   1058:                        default:
                   1059:                                break;
                   1060:                        }
                   1061:                }
                   1062: 
                   1063:                if(failed) // need rethrow?
                   1064:                        longjmp(saved_mark, 1);
                   1065:        }
                   1066: 
1.71      misha    1067:        modified_statement _preprocess_statement_limit(
                   1068:                Connection& connection, 
                   1069:                const char* astatement,
                   1070:                unsigned long offset,
                   1071:                unsigned long limit
                   1072:        ){
                   1073:                modified_statement result={astatement, false, false, false};
                   1074: 
1.72      misha    1075:                if(!connection.options.bDisableQueryModification && limit!=SQL_NO_LIMIT && strncasecmp(astatement, "select", 6)==0){
1.71      misha    1076:                        result.limit=true;
                   1077: 
                   1078:                        size_t statement_size=strlen(astatement);
                   1079:                        char* statement_limited;
                   1080:                        
1.72      misha    1081:                        if(offset && limit/* throwing offset away if limit==0 */){
1.71      misha    1082:                                
                   1083:                                result.skip_rownum_column=true;
                   1084:                                result.offset=true;
                   1085: 
                   1086:                                // SELECT * FROM (SELECT ROWNUM r__, z__.* FROM (user_query) z__) WHERE r__<=limit+offset AND r__>offset
                   1087:                                statement_limited=(char *)connection.services->malloc_atomic(
                   1088:                                                statement_size
                   1089:                                                +64/*SELECT * FROM (SELECT ROWNUM r__, z__.* FROM () z__) WHERE r__<=*/
                   1090:                                                +MAX_NUMBER
                   1091:                                                +9/* AND r__>*/
                   1092:                                                +MAX_NUMBER
                   1093:                                                +1/*terminator*/
                   1094:                                        );
                   1095: 
                   1096:                                result.statement=statement_limited;
                   1097: 
                   1098:                                strcpy(statement_limited, "SELECT * FROM (SELECT ROWNUM r__, z__.* FROM (");
                   1099:                                strcat(statement_limited, astatement);
                   1100: 
                   1101:                                statement_limited+=46+statement_size;
                   1102:                                statement_limited+=snprintf(statement_limited, 18+MAX_NUMBER, ") z__) WHERE r__<=%u", limit+offset);
                   1103:                                statement_limited+=snprintf(statement_limited, 9+MAX_NUMBER, " AND r__>%u", offset);
                   1104: 
                   1105:                        } else {
                   1106: 
1.72      misha    1107:                                // SELECT * FROM (user_query) WHERE ROWNUM<=limit
                   1108:                                // this statement can be easy for the sql server but we can't use it with offset
1.71      misha    1109: 
                   1110:                                statement_limited=(char *)connection.services->malloc_atomic(
                   1111:                                                statement_size
                   1112:                                                +31/*SELECT * FROM () WHERE ROWNUM<=*/
                   1113:                                                +MAX_NUMBER
                   1114:                                                +1/*terminator*/
                   1115:                                        );
                   1116: 
                   1117:                                result.statement=statement_limited;
                   1118: 
                   1119:                                strcpy(statement_limited, "SELECT * FROM (");
                   1120:                                strcat(statement_limited, astatement);
                   1121: 
                   1122:                                statement_limited+=15+statement_size;
1.72      misha    1123:                                statement_limited+=snprintf(statement_limited, 16+MAX_NUMBER, ") WHERE ROWNUM<=%u", limit);
1.71      misha    1124: 
                   1125:                        }
                   1126:                        *statement_limited=0;
                   1127: 
                   1128:                        //connection.services->_throw(result.statement);
                   1129:                }
                   1130:                return result;
1.69      misha    1131:        }
                   1132: 
1.1       parser   1133: private: // conn client library funcs
                   1134:        
1.60      paf      1135:        friend void fail(Connection& connection, const char *msg);
1.49      paf      1136:        friend void check(Connection& connection, const char *step, sword status);
1.1       parser   1137:        friend sb4 cbf_get_data(dvoid *ctxp, 
                   1138:                OCIBind *bindp, 
                   1139:                ub4 iter, ub4 index, 
                   1140:                dvoid **bufpp, 
                   1141:                ub4 **alenp, 
                   1142:                ub1 *piecep, 
                   1143:                dvoid **indpp, 
                   1144:                ub2 **rcodepp);
                   1145: 
                   1146: 
                   1147: #define OCI_DECL(name, params) \
                   1148:        typedef sword (*t_OCI##name)params; t_OCI##name OCI##name
                   1149: 
                   1150:        OCI_DECL(Initialize, (ub4 mode, dvoid *ctxp, 
                   1151:                dvoid * (*malocfp)(dvoid *ctxp, size_t size),
                   1152:                dvoid * (*ralocfp)(dvoid *ctxp, dvoid *memptr, size_t newsize),
                   1153:                void (*mfreefp)(dvoid *ctxp, dvoid *memptr) ));
                   1154: 
                   1155:        OCI_DECL(EnvInit, (OCIEnv **envp, ub4 mode, 
                   1156:                size_t xtramem_sz, dvoid **usrmempp)); 
                   1157:                
                   1158:        OCI_DECL(AttrGet, (CONST dvoid *trgthndlp, ub4 trghndltyp, 
                   1159:                dvoid *attributep, ub4 *sizep, ub4 attrtype, 
                   1160:                OCIError *errhp));
                   1161:        
                   1162:        OCI_DECL(AttrSet, (dvoid *trgthndlp, ub4 trghndltyp, dvoid *attributep,
                   1163:                                                                ub4 size, ub4 attrtype, OCIError *errhp));
                   1164: 
                   1165:        OCI_DECL(BindByPos, (OCIStmt *stmtp, OCIBind **bindp, OCIError *errhp,
                   1166:                ub4 position, dvoid *valuep, sb4 value_sz,
                   1167:                ub2 dty, dvoid *indp, ub2 *alenp, ub2 *rcodep,
                   1168:                ub4 maxarr_len, ub4 *curelep, ub4 mode));
                   1169: 
1.59      paf      1170:        OCI_DECL(BindByName, (OCIStmt *stmtp, OCIBind **bindp, OCIError *errhp,
                   1171:                text* placeholder, sb4 placeh_len, dvoid *valuep, sb4 value_sz,
                   1172:                ub2 dty, dvoid *indp, ub2 *alenp, ub2 *rcodep,
                   1173:                ub4 maxarr_len, ub4 *curelep, ub4 mode));
                   1174: 
1.1       parser   1175:        OCI_DECL(BindDynamic, (OCIBind *bindp, OCIError *errhp, dvoid *ictxp,
                   1176:                OCICallbackInBind icbfp, dvoid *octxp,
                   1177:                OCICallbackOutBind ocbfp));
                   1178:        
                   1179:        OCI_DECL(DefineByPos, (OCIStmt *stmtp, OCIDefine **defnp, OCIError *errhp,
                   1180:                ub4 position, dvoid *valuep, sb4 value_sz, ub2 dty,
                   1181:                dvoid *indp, ub2 *rlenp, ub2 *rcodep, ub4 mode));
                   1182:        
                   1183:        OCI_DECL(DescriptorAlloc, (CONST dvoid *parenth, dvoid **descpp, 
                   1184:                CONST ub4 type, CONST size_t xtramem_sz, 
                   1185:                dvoid **usrmempp));
                   1186:        
                   1187:        OCI_DECL(DescriptorFree, (dvoid *descp, CONST ub4 type));
                   1188: 
                   1189:        
                   1190:        OCI_DECL(ErrorGet, (dvoid *hndlp, ub4 recordno, OraText *sqlstate,
                   1191:                        sb4 *errcodep, OraText *bufp, ub4 bufsiz, ub4 type));
                   1192: 
                   1193:        OCI_DECL(HandleAlloc, (CONST dvoid *parenth, dvoid **hndlpp, CONST ub4 type, 
                   1194:                        CONST size_t xtramem_sz, dvoid **usrmempp));
                   1195: 
                   1196:        OCI_DECL(HandleFree, (dvoid *hndlp, CONST ub4 type));
                   1197:                                           
                   1198:        OCI_DECL(LobGetLength, (OCISvcCtx *svchp, OCIError *errhp, 
                   1199:                           OCILobLocator *locp,
                   1200:                           ub4 *lenp));
                   1201: 
                   1202:        OCI_DECL(LobRead, (OCISvcCtx *svchp, OCIError *errhp, OCILobLocator *locp,
                   1203:                      ub4 *amtp, ub4 offset, dvoid *bufp, ub4 bufl, 
                   1204:                      dvoid *ctxp, sb4 (*cbfp)(dvoid *ctxp, 
                   1205:                                               CONST dvoid *bufp, 
                   1206:                                               ub4 len, 
                   1207:                                               ub1 piece),
                   1208:                      ub2 csid, ub1 csfrm));
                   1209: 
                   1210:        OCI_DECL(LobWrite, (OCISvcCtx *svchp, OCIError *errhp, OCILobLocator *locp,
                   1211:                       ub4 *amtp, ub4 offset, dvoid *bufp, ub4 buflen, 
                   1212:                       ub1 piece, dvoid *ctxp, 
                   1213:                       sb4 (*cbfp)(dvoid *ctxp, 
                   1214:                                   dvoid *bufp, 
                   1215:                                   ub4 *len, 
                   1216:                                   ub1 *piece),
                   1217:                       ub2 csid, ub1 csfrm));
                   1218: 
                   1219:        OCI_DECL(ParamGet, (CONST dvoid *hndlp, ub4 htype, OCIError *errhp, 
                   1220:                      dvoid **parmdpp, ub4 pos));
                   1221: 
                   1222:        OCI_DECL(ServerAttach, (OCIServer *srvhp, OCIError *errhp,
                   1223:                           CONST OraText *dblink, sb4 dblink_len, ub4 mode));
                   1224: 
                   1225:        OCI_DECL(ServerDetach, (OCIServer *srvhp, OCIError *errhp, ub4 mode));
                   1226: 
                   1227:        OCI_DECL(SessionBegin, (OCISvcCtx *svchp, OCIError *errhp, OCISession *usrhp,
                   1228:                           ub4 credt, ub4 mode));
                   1229: 
                   1230:        OCI_DECL(SessionEnd, (OCISvcCtx *svchp, OCIError *errhp, OCISession *usrhp, 
                   1231:                          ub4 mode));
                   1232: 
                   1233:        OCI_DECL(StmtExecute, (OCISvcCtx *svchp, OCIStmt *stmtp, OCIError *errhp, 
                   1234:                          ub4 iters, ub4 rowoff, CONST OCISnapshot *snap_in, 
                   1235:                          OCISnapshot *snap_out, ub4 mode));
                   1236: 
                   1237:        OCI_DECL(StmtFetch, (OCIStmt *stmtp, OCIError *errhp, ub4 nrows, 
                   1238:                         ub2 orientation, ub4 mode));
                   1239: 
                   1240:        OCI_DECL(StmtPrepare, (OCIStmt *stmtp, OCIError *errhp, CONST OraText *stmt,
                   1241:                           ub4 stmt_len, ub4 language, ub4 mode));
                   1242: 
                   1243:        OCI_DECL(TransCommit, (OCISvcCtx *svchp, OCIError *errhp, ub4 flags));
                   1244: 
                   1245:        OCI_DECL(TransRollback, (OCISvcCtx *svchp, OCIError *errhp, ub4 flags));
                   1246: 
                   1247: private: // conn client library funcs linking
                   1248: 
                   1249:        const char *dlink(const char *dlopen_file_spec) {
                   1250:                if(lt_dlinit())
                   1251:                        return lt_dlerror();
                   1252:         lt_dlhandle handle=lt_dlopen(dlopen_file_spec);
                   1253:         if(!handle)
                   1254:                        return lt_dlerror(); //"can not open the dynamic link module";
                   1255: 
                   1256:                #define DSLINK(name, action) \
                   1257:                        name=(t_##name)lt_dlsym(handle, #name); \
                   1258:                                if(!name) \
                   1259:                                        action;
                   1260: 
                   1261:                #define OCI_LINK(name) DSLINK(OCI##name, return "function OCI" #name " was not found")
                   1262:                
                   1263:                OCI_LINK(Initialize);
                   1264:                OCI_LINK(EnvInit);
                   1265:                OCI_LINK(AttrGet);              OCI_LINK(AttrSet);
1.59      paf      1266:                OCI_LINK(BindByPos);            OCI_LINK(BindByName);   OCI_LINK(BindDynamic);
1.1       parser   1267:                OCI_LINK(DefineByPos);
                   1268:                OCI_LINK(DescriptorAlloc);              OCI_LINK(DescriptorFree);
                   1269:                OCI_LINK(ErrorGet);
                   1270:                OCI_LINK(HandleAlloc);          OCI_LINK(HandleFree);
                   1271:                OCI_LINK(LobGetLength);
                   1272:                OCI_LINK(LobRead);              OCI_LINK(LobWrite);
                   1273:                OCI_LINK(ParamGet);
                   1274:                OCI_LINK(ServerAttach);         OCI_LINK(ServerDetach);
                   1275:                OCI_LINK(SessionBegin);         OCI_LINK(SessionEnd);
                   1276:                OCI_LINK(StmtExecute);          OCI_LINK(StmtFetch);            OCI_LINK(StmtPrepare);
                   1277:                OCI_LINK(TransCommit);          OCI_LINK(TransRollback);
                   1278: 
                   1279:                return 0;
                   1280:        }
                   1281: 
                   1282: } *OracleSQL_driver;
                   1283: 
1.49      paf      1284: void check(Connection& connection, const char *step, sword status) {
1.1       parser   1285: 
                   1286:        const char *msg;
                   1287:        char reason[MAX_STRING/2];
                   1288: 
                   1289:        switch (status) {
1.22      paf      1290:        case OCI_SUCCESS: // hurrah
                   1291:        case OCI_SUCCESS_WITH_INFO:             // ignoring. example: count(column) when column contains NULLs, 
                   1292:                                                                                                                // count() not counting them and gives that status
                   1293:                return;
1.1       parser   1294:        case OCI_ERROR:
                   1295:                {
1.45      paf      1296:                        sb4 errcode;
1.49      paf      1297:                        if(OracleSQL_driver->OCIErrorGet((dvoid *)connection.errhp, (ub4)1, (text *)NULL, &errcode, 
1.45      paf      1298:                                (text *)reason, (ub4)sizeof(reason), OCI_HTYPE_ERROR)==OCI_SUCCESS) {
                   1299:                                msg=reason;
                   1300: 
1.71      misha    1301:                                if(msg && transcode_required(connection)){
                   1302:                                        // transcode server error message from ?ClientCharset to $request:charset
1.69      misha    1303:                                        if(size_t msg_length=strlen(msg)){
                   1304:                                                connection.services->transcode(msg, msg_length,
                   1305:                                                        msg, msg_length,
                   1306:                                                        connection.options.client_charset,
                   1307:                                                        connection.services->request_charset());
1.42      paf      1308:                                        }
                   1309:                                }
1.45      paf      1310:                        } else
                   1311:                                msg="[can not get error description]";
                   1312:                        break;
1.1       parser   1313:                }
                   1314:        case OCI_NEED_DATA:
                   1315:                msg="NEED_DATA"; break;
                   1316:        case OCI_NO_DATA:
                   1317:                msg="NODATA"; break;
                   1318:        case OCI_INVALID_HANDLE:
                   1319:                msg="INVALID_HANDLE"; break;
                   1320:        case OCI_STILL_EXECUTING:
                   1321:                msg="STILL_EXECUTE"; break;
                   1322:        case OCI_CONTINUE:
                   1323:                msg="CONTINUE"; break;
                   1324:        default:
                   1325:                msg="unknown"; break;
                   1326:        }
                   1327: 
1.49      paf      1328:        snprintf(connection.error, sizeof(connection.error), "%s (%s, %d)", 
1.22      paf      1329:                msg, step, (int)status);
1.49      paf      1330:        longjmp(connection.mark, 1);
1.1       parser   1331: }
                   1332: 
1.71      misha    1333: bool transcode_required(Connection& connection){
                   1334:        return (connection.options.client_charset && strcmp(connection.options.client_charset, connection.services->request_charset())!=0);
                   1335: }
                   1336: 
1.60      paf      1337: void fail(Connection& connection, const char* msg) {
                   1338:        snprintf(connection.error, sizeof(connection.error), "%s", msg);
                   1339:        longjmp(connection.mark, 1);
                   1340: }
                   1341: 
1.49      paf      1342: void check(Connection& connection, bool error) {
1.27      paf      1343:        if(error)
1.49      paf      1344:                longjmp(connection.mark, 1);
1.27      paf      1345: }
1.1       parser   1346: 
                   1347: /* ----------------------------------------------------------------- */
                   1348: /* Intbind callback that does not do any data input.                 */
                   1349: /* ----------------------------------------------------------------- */
1.60      paf      1350: sb4 cbf_no_data(
1.45      paf      1351:                                dvoid* /*ctxp*/, 
                   1352:                                OCIBind* /*bindp*/, 
                   1353:                                ub4 /*iter*/, ub4 /*index*/, 
1.1       parser   1354:                                dvoid **bufpp, 
                   1355:                                ub4 *alenpp, 
                   1356:                                ub1 *piecep, 
                   1357:                                dvoid **indpp) {
                   1358:        *bufpp=(dvoid *)0;
                   1359:        *alenpp=0;
                   1360:        static sb2 null_ind=-1;
                   1361:        *indpp=(dvoid *) &null_ind;
                   1362:        *piecep=OCI_ONE_PIECE;
                   1363:        
                   1364:        return OCI_CONTINUE;
                   1365: }
                   1366: 
                   1367: /* ----------------------------------------------------------------- */
                   1368: /* Outbind callback for returning data.                              */
                   1369: /* ----------------------------------------------------------------- */
                   1370: static sb4 cbf_get_data(dvoid *ctxp, 
                   1371:                                 OCIBind *bindp, 
1.45      paf      1372:                                 ub4 /*iter*/, ub4 index, 
1.1       parser   1373:                                 dvoid **bufpp, 
                   1374:                                 ub4 **alenp, 
                   1375:                                 ub1 *piecep, 
                   1376:                                 dvoid **indpp, 
                   1377:                                 ub2 **rcodepp) {
1.60      paf      1378:        Query_lobs::Item& context=*static_cast<Query_lobs::Item*>(ctxp);
1.1       parser   1379: 
                   1380:        if(index==0) {
                   1381:                static ub4  rows;
1.49      paf      1382:                check(*context.connection, "AttrGet cbf_get_data ROWS_RETURNED", 
1.1       parser   1383:                        OracleSQL_driver->OCIAttrGet(
                   1384:                                (CONST dvoid *) bindp, OCI_HTYPE_BIND, (dvoid *)&rows, 
1.49      paf      1385:                                (ub4 *)sizeof(ub2), OCI_ATTR_ROWS_RETURNED, context.connection->errhp)) ;
1.57      paf      1386:                context.rows.count=(ub2)rows;
1.60      paf      1387:                context.rows.row=(Query_lobs::return_rows::return_row *)
                   1388:                        context.connection->services->malloc_atomic(sizeof(Query_lobs::return_rows::return_row)*rows);
1.1       parser   1389:        }
                   1390: 
1.60      paf      1391:        Query_lobs::return_rows::return_row &var=context.rows.row[index];
1.1       parser   1392: 
1.49      paf      1393:        check(*context.connection, "alloc output var desc dynamic", OracleSQL_driver->OCIDescriptorAlloc(
                   1394:                (dvoid *) context.connection->envhp, (dvoid **)&var.locator, 
1.1       parser   1395:                (ub4)OCI_DTYPE_LOB, 
                   1396:                0, (dvoid **)0));
                   1397: 
                   1398:        *bufpp=var.locator;
                   1399:        *alenp=&var.len;
                   1400:        *indpp=(dvoid *) &var.ind;
                   1401:        *piecep=OCI_ONE_PIECE;
                   1402:        *rcodepp=&var.rcode;
                   1403:        
                   1404:        return OCI_CONTINUE;
                   1405: }
                   1406: 
                   1407: extern "C" SQL_Driver *SQL_DRIVER_CREATE() {
                   1408:        return OracleSQL_driver=new OracleSQL_Driver();
                   1409: }

E-mail: