Diff for /sql/oracle/parser3oracle.C between versions 1.14 and 1.72

version 1.14, 2001/11/14 10:46:36 version 1.72, 2008/07/08 08:07:49
Line 1 Line 1
 /** @file  /** @file
         Parser Oracle driver.          Parser Oracle driver.
   
         Copyright(c) 2001 ArtLebedev Group(http://www.artlebedev.com)          Copyright(c) 2001, 2003 ArtLebedev Group (http://www.artlebedev.com)
   
         Author: Alexander Petrosyan <paf@design.ru>(http://design.ru/paf)          Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
   
         2001.07.30 using Oracle 8.1.6 [@test tested with Oracle 7.x.x]          2001.07.30 using Oracle 8.1.6 [@test tested with Oracle 7.x.x]
 */  */
   
 static const char *RCSId="$Id$";   static const char *RCSId="$Id$"; 
   
 #include "config_includes.h"  #include "config_includes.h"
Line 19  static const char *RCSId="$Id$"; Line 20  static const char *RCSId="$Id$";
 #define MAX_IN_LOBS 5  #define MAX_IN_LOBS 5
 #define MAX_LOB_NAME_LENGTH 100  #define MAX_LOB_NAME_LENGTH 100
 #define MAX_OUT_STRING_LENGTH 4000  #define MAX_OUT_STRING_LENGTH 4000
   #define MAX_BINDS 100
   
 #define EMPTY_CLOB_FUNC_CALL "empty_clob()"  #define EMPTY_CLOB_FUNC_CALL "empty_clob()"
   
Line 38  inline int max(int a, int b) { return a> Line 40  inline int max(int a, int b) { return a>
 inline int min(int a, int b){ return a<b?a:b; }  inline int min(int a, int b){ return a<b?a:b; }
 #endif  #endif
   
 /// @test setenv version memory. maybe key/value needs ::malloc?  #if _MSC_VER
   // interaction between '_setjmp' and C++ object destruction is non-portable
   // but we forced to do that under HPUX
   #pragma warning(disable:4611)   
   #endif
   
   const sb2 MAGIC_INDICATOR_VALUE_MEANING_NOT_NULL_AND_UNCHANGED=99;
   
   /// @todo small memory leaks here
 static int pa_setenv(const char *name, const char *value, bool do_append) {  static int pa_setenv(const char *name, const char *value, bool do_append) {
         const char *prev_value=0;          const char *prev_value=0;
         if(do_append)          if(do_append)
Line 68  static int pa_setenv(const char *name, c Line 78  static int pa_setenv(const char *name, c
 #else   #else 
         //#ifdef HAVE_SETENV          //#ifdef HAVE_SETENV
         if(value) {          if(value) {
                 char *buf;  
                 if(prev_value) {                  if(prev_value) {
                         // MEM_LEAK_HERE                          // MEM_LEAK_HERE
                         buf=(char *)::malloc(strlen(prev_value)                          char *buf=(char *)::malloc(strlen(prev_value)
                                 +strlen(value)                                  +strlen(value)
                                 +1);                                  +1);
                         strcpy(buf, prev_value);                          strcpy(buf, prev_value);
                         strcat(buf, value);                          strcat(buf, value);
                 } else                          value=buf;
                         buf=value;                  }
                   return setenv(name, value, 1/*overwrite*/); 
                 return setenv(name, buf, 1/*overwrite*/);   
         } else {          } else {
                 unsetenv(name);                  unsetenv(name);
                 return 0;                  return 0;
Line 87  static int pa_setenv(const char *name, c Line 95  static int pa_setenv(const char *name, c
 #endif  #endif
 }  }
   
 static char *lsplit(char *string, char delim) {  static char *lsplit(char *string, char delim){
     if(string) {          if(string){
                 char *v=strchr(string, delim);                  if(char* v=strchr(string, delim)){
                 if(v) {  
                         *v=0;                          *v=0;
                         return v+1;                          return v+1;
                 }                  }
     }          }
     return 0;          return 0;
 }  }
   
 static char *lsplit(char **string_ref, char delim) {  static char *lsplit(char **string_ref, char delim){
     char *result=*string_ref;          char *result=*string_ref;
         char *next=lsplit(*string_ref, delim);          char *next=lsplit(*string_ref, delim);
     *string_ref=next;          *string_ref=next;
     return result;          return result;
 }  }
   
 static const char *options2env(char *options) {  static char* rsplit(char* string, char delim){
         while(options) {          if(string){
                 if(char *key=lsplit(&options, '&')) {                  if(char* v=strrchr(string, delim)){
                         if(*key) {                          *v=0;
                                 if(char *value=lsplit(key, '=')) {                          return v+1;
                                         bool do_append=key[strlen(key)-1]=='+'; // PATH+=  
                                         if(do_append)  
                                                 key[strlen(key)-1]=0; // remove trailing +  
                                         if(strncmp(key, "ORACLE_", 7)==0  // ORACLE_HOME & co  
                                                 || strncmp(key, "ORA_", 4)==0 // ORA_ENCRYPT_LOGIN & co  
                                                 || strncmp(key, "NLS_", 4)==0 // NLS_LANG & co  
                                                 || do_append  
                                                 ) {  
                                                 if(pa_setenv(key, value, do_append)!=0)  
                                                         return "problem changing process environment" /*key*/;  
                                         } else  
                                                 return "unknown option (option must start with ORACLE_, ORA_ or NLS_)" /*key*/;  
                                 } else   
                                         return "option without =value" /*key*/;  
                         }  
                 }                  }
         }          }
         return 0;          return NULL;    
   }
   
   static void tolower_str(char *out, const char *in, size_t size) {
           while(size--)
                   *out++=(char)tolower(*in++);
   }
   static void toupper_str(char *out, const char *in, size_t size) {
           while(size--)
                   *out++=(char)toupper(*in++);
 }  }
   
   struct modified_statement {
           const char* statement;
           bool limit;
           bool offset;
           bool skip_rownum_column;
   };
   
 #ifndef DOXYGEN  #ifndef DOXYGEN
 struct OracleSQL_connection_struct {  struct Connection {
           SQL_Driver_services *services;
   
         jmp_buf mark; char error[MAX_STRING];          jmp_buf mark; char error[MAX_STRING];
           SQL_Error sql_error;
         OCIEnv *envhp;          OCIEnv *envhp;
         OCIServer *srvhp;          OCIServer *srvhp;
         OCIError *errhp;          OCIError *errhp;
         OCISvcCtx *svchp;          OCISvcCtx *svchp;
         OCISession *usrhp;          OCISession *usrhp;
   
           char* fetch_buffers[MAX_COLS];
           char* bind_buffers[MAX_BINDS];
   
           struct Options {
                   bool bLowerCaseColumnNames;
                   bool bDisableQueryModification;
                   const char* client_charset;
           } options;
 };  };
   
 struct OracleSQL_query_lobs {  struct Query_lobs {
         struct return_rows {          struct return_rows {
                 struct return_row {                  struct return_row {
                         OCILobLocator *locator; ub4 len;                          OCILobLocator *locator; ub4 len;
Line 149  struct OracleSQL_query_lobs { Line 169  struct OracleSQL_query_lobs {
                 } *row;                  } *row;
                 int count;                  int count;
         };          };
         struct cbf_context_struct {  
                 SQL_Driver_services *services;  
                 OracleSQL_connection_struct *cs;  
                 return_rows *rows;  
         };  
         struct Item {          struct Item {
                 const char *name_ptr; size_t name_size;                  const char *name_ptr; size_t name_size;
                 char *data_ptr; size_t data_size;                  char *data_ptr; size_t data_size;
                 OCILobLocator *locator;                  OCILobLocator *locator;
                 OCIBind *bind;                  OCIBind *bind;
                 return_rows rows;                  return_rows rows;
                   Connection *connection;
         } items[MAX_IN_LOBS];          } items[MAX_IN_LOBS];
         int count;          int count;
 };  };
   
 #endif  #endif
   
 // forwards  // forwards
 void check(  static void fail(Connection& connection, const char *msg);
                    SQL_Driver_services& services, OracleSQL_connection_struct &cs,   static void check(Connection& connection, const char *step, sword status);
                    const char *step, sword status);  static void check(Connection& connection, bool error);
 static sb4 cbf_no_data(  static sb4 cbf_no_data(
                                            dvoid *ctxp,                                              dvoid *ctxp, 
                                            OCIBind *bindp,                                              OCIBind *bindp, 
Line 185  static sb4 cbf_get_data(dvoid *ctxp, Line 202  static sb4 cbf_get_data(dvoid *ctxp,
                                                 ub1 *piecep,                                                   ub1 *piecep, 
                                                 dvoid **indpp,                                                   dvoid **indpp, 
                                                 ub2 **rcodepp);                                                  ub2 **rcodepp);
 void tolower(char *out, const char *in, size_t size);  
   static  bool transcode_required(Connection& connection);
   
   static const char *options2env(char *s, Connection::Options* options) {
           while(s){
                   if(char *key=lsplit(&s, '&')){
                           if(*key){
                                   if(char *value=lsplit(key, '=')){
                                           if(strcmp(key, "ClientCharset")== 0){
                                                   if(options){
                                                           toupper_str(value, value, strlen(value));
                                                           options->client_charset=value;
                                                   }
                                                   continue;
                                           }
   
                                           if(strcmp(key, "LowerCaseColumnNames")==0){
                                                   if(options)
                                                           options->bLowerCaseColumnNames=atoi(value)!=0;
                                                   continue;
                                           }
   
                                           if(strcmp(key, "DisableQueryModification")==0){
                                                   if(options)
                                                           options->bDisableQueryModification=atoi(value)!=0;
                                                   continue;
                                           }
   
                                           bool do_append=key[strlen(key)-1]=='+'; // PATH+=
                                           if(do_append)
                                                   key[strlen(key)-1]=0; // remove trailing +
                                           if(strncmp(key, "ORACLE_", 7)==0  // ORACLE_HOME & co
                                                   || strncmp(key, "ORA_", 4)==0 // ORA_ENCRYPT_LOGIN & co
                                                   || strncmp(key, "NLS_", 4)==0 // NLS_LANG & co
                                                   || do_append
                                                   ) {
                                                   if(pa_setenv(key, value, do_append)!=0)
                                                           return "problem changing process environment" /*key*/;
                                           } else
                                                   return "unknown option" /*key*/;
                                   } else 
                                           return "option without =value" /*key*/;
                           }
                   }
           }
           return 0;
   }
   
 /**  /**
         OracleSQL server driver          OracleSQL server driver
         @test NLS_LANG=AMERICAN_AMERICA.CL8MSWIN1251  
 */  */
 class OracleSQL_Driver : public SQL_Driver {  class OracleSQL_Driver : public SQL_Driver {
 public:  public:
Line 199  public: Line 261  public:
   
         /// get api version          /// get api version
         int api_version() { return SQL_DRIVER_API_VERSION; }          int api_version() { return SQL_DRIVER_API_VERSION; }
   
         /** initialize driver by loading sql dynamic link library          /** initialize driver by loading sql dynamic link library
                 @todo ?objects=1 which would turn on OCI_OBJECT init flag                  @todo ?objects=1 which would turn on OCI_OBJECT init flag
         */          */
Line 208  public: Line 271  public:
                 const char *error=dlopen_file_spec?                  const char *error=dlopen_file_spec?
                         dlink(dlopen_file_spec):"client library column is empty";                          dlink(dlopen_file_spec):"client library column is empty";
                 if(!error) {                  if(!error) {
                         error=options2env(options);                          error=options2env(options, 0);
   
                         if(!error)                          if(!error)
                                 OCIInitialize((ub4)OCI_THREADED/*| OCI_OBJECT*/, (dvoid *)0,                                   OCIInitialize((ub4)OCI_THREADED/*| OCI_OBJECT*/, (dvoid *)0, 
Line 222  public: Line 285  public:
         }          }
   
         /**     connect          /**     connect
                 @param used_only_in_connect_url                  @param url
                         format: @b user:pass@service?                          format: @b user:pass@service?
                            ORACLE_HOME=/u01/app/oracle/product/8.1.5&                                  ORACLE_HOME=/u01/app/oracle/product/8.1.5&
                            ORA_NLS33=/u01/app/oracle/product/8.1.5/ocommon/nls/admin/data&                                  ORA_NLS33=/u01/app/oracle/product/8.1.5/ocommon/nls/admin/data&
                            NLS_LANG=RUSSIAN_AMERICA.CL8MSWIN1251&                                  NLS_LANG=RUSSIAN_AMERICA.CL8MSWIN1251&
                            ORA_ENCRYPT_LOGIN=TRUE                                  ORA_ENCRYPT_LOGIN=TRUE&
                                   ClientCharset=charset&
                                   LowerCaseColumnNames=0
   
                 @todo environment manupulation doesnt look thread safe                  @todo environment manupulation doesnt look thread safe
                   @todo allocate 'aused_only_in_connect_url' on gc heap, so it can be manipulated directly
         */          */
         void connect(          void connect(
                 char *used_only_in_connect_url,                           char *url, 
                 SQL_Driver_services& services,                           SQL_Driver_services& services, 
                 void **connection ///< output: OracleSQL_connection_struct *                          void **connection_ref ///< output: Connection *
                 ) {                  ) 
           {
                 // connections are cross-request, do not use services._alloc [linked with request]                  // connections are cross-request, do not use services._alloc [linked with request]
                 OracleSQL_connection_struct &cs=                  Connection& connection=*(Connection *)services.malloc(sizeof(Connection));
                         *(OracleSQL_connection_struct  *)::calloc(sizeof(OracleSQL_connection_struct), 1);                  connection.services=&services;
                   connection.options.bLowerCaseColumnNames=true;
                   connection.options.bDisableQueryModification=false;
                   *connection_ref=&connection;
   
                 char *user=used_only_in_connect_url;                  char *user=url;
                 char *service=lsplit(user, '@');                  char *service=rsplit(user, '@');
                 char *pwd=lsplit(user, ':');                  char *pwd=lsplit(user, ':');
                 char *options=lsplit(service, '?');                  char *options=lsplit(service, '?');
   
                 if(!(user && pwd && service))                  if(!(user && pwd && service))
                         services._throw("mailformed connect part, must be 'user:pass@service'");                          services._throw("mailformed connect part, must be 'user:pass@service'");
   
                 if(const char *error=options2env(options))                  if(const char *error=options2env(options, &connection.options))
                         services._throw(error);                          services._throw(error);
   
                 if(setjmp(cs.mark))                  if(setjmp(connection.mark))
                         services._throw(cs.error);                          services._throw(connection.error);
   
                 // Allocate and initialize OCIError handle, attempt #1                  // Allocate and initialize OCIError handle, attempt #1
                 /*                  /*
Line 270  public: Line 340  public:
                         think, this is some sort of backward compatibility wonder.                          think, this is some sort of backward compatibility wonder.
                         leaving as it is, and without check()                          leaving as it is, and without check()
                 */                  */
                 OCIHandleAlloc((dvoid *)NULL, (dvoid **) &cs.envhp, (ub4)OCI_HTYPE_ENV, 0, 0);                  OCIHandleAlloc((dvoid *)NULL, (dvoid **) &connection.envhp, (ub4)OCI_HTYPE_ENV, 0, 0);
                 // Initialize an environment handle, attempt #2                  // Initialize an environment handle, attempt #2
                 check(services, cs, "EnvInit", OCIEnvInit(                  check(connection, "EnvInit", OCIEnvInit(
                         &cs.envhp, (ub4)OCI_DEFAULT, 0, 0));                                      &connection.envhp, (ub4)OCI_DEFAULT, 0, 0));            
                 // Allocate and initialize OCIError handle                  // Allocate and initialize OCIError handle
                 check(services, cs, "HandleAlloc errhp", OCIHandleAlloc(                   check(connection, "HandleAlloc errhp", OCIHandleAlloc( 
                         (dvoid *)cs.envhp, (dvoid **) &cs.errhp, (ub4)OCI_HTYPE_ERROR, 0, 0));                          (dvoid *)connection.envhp, (dvoid **) &connection.errhp, (ub4)OCI_HTYPE_ERROR, 0, 0));
                 // Allocate and initialize OCIServer handle                  // Allocate and initialize OCIServer handle
                 check(services, cs, "HandleAlloc srvhp", OCIHandleAlloc(                   check(connection, "HandleAlloc srvhp", OCIHandleAlloc( 
                         (dvoid *)cs.envhp, (dvoid **) &cs.srvhp, (ub4)OCI_HTYPE_SERVER, 0, 0));                                   (dvoid *)connection.envhp, (dvoid **) &connection.srvhp, (ub4)OCI_HTYPE_SERVER, 0, 0));         
                 // Attach to a 'service'; initialize server context handle                    // Attach to a 'service'; initialize server context handle  
                 check(services, cs, "ServerAttach", OCIServerAttach(                   check(connection, "ServerAttach", OCIServerAttach( 
                         cs.srvhp, cs.errhp, (text *)service, (sb4)strlen(service), (ub4)OCI_DEFAULT));                          connection.srvhp, connection.errhp, (text *)service, (sb4)strlen(service), (ub4)OCI_DEFAULT));
                 // Allocate and initialize OCISvcCtx handle                  // Allocate and initialize OCISvcCtx handle
                 check(services, cs, "HandleAlloc svchp", OCIHandleAlloc(                   check(connection, "HandleAlloc svchp", OCIHandleAlloc( 
                         (dvoid *)cs.envhp, (dvoid **) &cs.svchp, (ub4)OCI_HTYPE_SVCCTX, 0, 0));                                   (dvoid *)connection.envhp, (dvoid **) &connection.svchp, (ub4)OCI_HTYPE_SVCCTX, 0, 0));         
                 // set attribute server context in the service context                  // set attribute server context in the service context
                 check(services, cs, "AttrSet server-service", OCIAttrSet(                   check(connection, "AttrSet server-service", OCIAttrSet( 
                         (dvoid *)cs.svchp, (ub4)OCI_HTYPE_SVCCTX,                           (dvoid *)connection.svchp, (ub4)OCI_HTYPE_SVCCTX, 
                         (dvoid *)cs.srvhp, (ub4)0,                           (dvoid *)connection.srvhp, (ub4)0, 
                         (ub4)OCI_ATTR_SERVER, (OCIError *)cs.errhp));                                     (ub4)OCI_ATTR_SERVER, (OCIError *)connection.errhp));           
                 // allocate a user context handle                  // allocate a user context handle
                 check(services, cs, "HandleAlloc usrhp", OCIHandleAlloc(                  check(connection, "HandleAlloc usrhp", OCIHandleAlloc(
                         (dvoid *)cs.envhp, (dvoid **)&cs.usrhp, (ub4)OCI_HTYPE_SESSION, 0, 0));                          (dvoid *)connection.envhp, (dvoid **)&connection.usrhp, (ub4)OCI_HTYPE_SESSION, 0, 0));
                 // set 'user' name                  // set 'user' name
                 check(services, cs, "AttrSet user-session", OCIAttrSet(                  check(connection, "AttrSet user-session", OCIAttrSet(
                         (dvoid *)cs.usrhp, (ub4)OCI_HTYPE_SESSION,                           (dvoid *)connection.usrhp, (ub4)OCI_HTYPE_SESSION, 
                         (dvoid *)user, (ub4)strlen(user),                           (dvoid *)user, (ub4)strlen(user), 
                         OCI_ATTR_USERNAME, cs.errhp));                                    OCI_ATTR_USERNAME, connection.errhp));          
                 // set 'pwd' password                  // set 'pwd' password
                 check(services, cs, "AttrSet pwd-session", OCIAttrSet(                  check(connection, "AttrSet pwd-session", OCIAttrSet(
                         (dvoid *)cs.usrhp, (ub4)OCI_HTYPE_SESSION,                           (dvoid *)connection.usrhp, (ub4)OCI_HTYPE_SESSION, 
                         (dvoid *)pwd, (ub4)strlen(pwd),                           (dvoid *)pwd, (ub4)strlen(pwd), 
                         OCI_ATTR_PASSWORD, cs.errhp));                          OCI_ATTR_PASSWORD, connection.errhp));
                 // Authenticate a user                    // Authenticate a user  
                 check(services, cs, "SessionBegin", OCISessionBegin(                  check(connection, "SessionBegin", OCISessionBegin(
                         cs.svchp, cs.errhp, cs.usrhp,                           connection.svchp, connection.errhp, connection.usrhp, 
                         OCI_CRED_RDBMS, OCI_DEFAULT));                          OCI_CRED_RDBMS, OCI_DEFAULT));
                 // remember connection in session                  // remember connection in session
                 check(services, cs, "AttrSet service-session", OCIAttrSet(                  check(connection, "AttrSet service-session", OCIAttrSet(
                         (dvoid *)cs.svchp, (ub4)OCI_HTYPE_SVCCTX,                           (dvoid *)connection.svchp, (ub4)OCI_HTYPE_SVCCTX, 
                         (dvoid *)cs.usrhp, (ub4)0,                           (dvoid *)connection.usrhp, (ub4)0, 
                         OCI_ATTR_SESSION, cs.errhp));                          OCI_ATTR_SESSION, connection.errhp));
   
                 // return created connection  
                 *(OracleSQL_connection_struct **)connection=&cs;  
         }          }
         void disconnect(void *connection) {  
             OracleSQL_connection_struct &cs=*(OracleSQL_connection_struct *)connection;          void disconnect(void *aconnection) {
               Connection& connection=*static_cast<Connection *>(aconnection);
   
                   // free fetch buffers. leave that to GC [no such services func. yet?]
                   /*
                   for(int i=0; i<MAX_COLS; i++) {
                           if(void* fetch_buffer=connection.fetch_buffers[i])
                                   connection.services->free(fetch_buffer);
                           else
                                   break;
                   }
                   */
   
                 // Terminate a user session                  // Terminate a user session
                 OCISessionEnd(                  OCISessionEnd(
                         cs.svchp, cs.errhp, cs.usrhp, (ub4)OCI_DEFAULT);                          connection.svchp, connection.errhp, connection.usrhp, (ub4)OCI_DEFAULT);
                 // Detach from a server; uninitialize server context handle                  // Detach from a server; uninitialize server context handle
                 OCIServerDetach(                  OCIServerDetach(
                         cs.srvhp, cs.errhp, (ub4)OCI_DEFAULT);                          connection.srvhp, connection.errhp, (ub4)OCI_DEFAULT);
                 // Free a previously allocated handles                  // Free a previously allocated handles
                 /*                   /* 
                 oci will free them up as belonging to env                  oci will free them up as belonging to env
                 OCIHandleFree(                  OCIHandleFree(
                         (dvoid *)cs.srvhp, (ub4)OCI_HTYPE_SERVER);                          (dvoid *)connection.srvhp, (ub4)OCI_HTYPE_SERVER);
                 OCIHandleFree(                  OCIHandleFree(
                         (dvoid *)cs.svchp, (ub4)OCI_HTYPE_SVCCTX);                          (dvoid *)connection.svchp, (ub4)OCI_HTYPE_SVCCTX);
                 OCIHandleFree(                  OCIHandleFree(
                         (dvoid *)cs.errhp, (ub4)OCI_HTYPE_ERROR);                          (dvoid *)connection.errhp, (ub4)OCI_HTYPE_ERROR);
                 */                  */
                 OCIHandleFree(                  OCIHandleFree(
                         (dvoid *)cs.envhp, (ub4)OCI_HTYPE_ENV);                          (dvoid *)connection.envhp, (ub4)OCI_HTYPE_ENV);
   
                 // connections are cross-request, do not use services._alloc [linked with request]                  // free connection. leave that to GC [no such services func. yet?]
                 ::free(&cs);                  // connection.services->free(&connection);
         }          }
         void commit(SQL_Driver_services& services, void *connection) {  
             OracleSQL_connection_struct &cs=*(OracleSQL_connection_struct *)connection;  
                 if(setjmp(cs.mark))  
                         services._throw(cs.error);  
   
                 check(services, cs, "commit", OCITransCommit(cs.svchp, cs.errhp, 0));          void commit(void *aconnection) {
               Connection& connection=*static_cast<Connection *>(aconnection);
                   if(setjmp(connection.mark))
                           connection.services->_throw(connection.error);
   
                   check(connection, "commit", OCITransCommit(connection.svchp, connection.errhp, 0));
         }          }
         void rollback(SQL_Driver_services& services, void *connection) {  
             OracleSQL_connection_struct &cs=*(OracleSQL_connection_struct *)connection;  
                 if(setjmp(cs.mark))  
                         services._throw(cs.error);  
   
                 check(services, cs, "rollback", OCITransRollback(cs.svchp, cs.errhp, 0));          void rollback(void *aconnection) {
               Connection& connection=*static_cast<Connection *>(aconnection);
                   if(setjmp(connection.mark))
                           connection.services->_throw(connection.error);
   
                   // sometimes rollback is done in context when this yields error which masks previous error
                   // consider consequent errors not very important to report, reporting first one
                   /*check(connection, "rollback", */OCITransRollback(connection.svchp, connection.errhp, 0)/*)*/;
         }          }
   
         bool ping(SQL_Driver_services&, void *connection) {          bool ping(void* /*connection*/) {
                 // maybe OCIServerVersion?                  // maybe OCIServerVersion?
                 // select 0 from dual                  // select 0 from dual
                 return true;                  return true;
         }          }
   
         unsigned int quote(          const char* quote(void *aconnection,
                 SQL_Driver_services&, void *,                   const char *from, unsigned int length) 
                 char *to, const char *from, unsigned int length) {          {
                 /*                  Connection& connection=*static_cast<Connection *>(aconnection);
                         it's already UNTAINT_TIMES_BIGGER                  char *result=(char*)connection.services->malloc_atomic(length*2+1);
                 */                  char *to=result;
                 unsigned int result=length;  
                 while(length--) {                  while(length--) {
                         switch(*from) {                          switch(*from) {
                         case '\'': // "'" -> "''"                          case '\'': // "'" -> "''"
                                 *to++='\''; result++;                                  *to++='\'';
                                 break;                                  break;
                         }                          }
                         *to++=*from++;                          *to++=*from++;
                 }                  }
                   *to=0;
                 return result;                  return result;
         }          }
         void query(  
                 SQL_Driver_services& services, void *connection,           void query(void* aconnection, 
                 const char *astatement, unsigned long offset, unsigned long limit,                           const char* astatement, 
                 SQL_Driver_query_event_handlers& handlers) {                          size_t placeholders_count, Placeholder* placeholders, 
                                           unsigned long offset, unsigned long limit,
                 OracleSQL_connection_struct &cs=*(OracleSQL_connection_struct *)connection;                          SQL_Driver_query_event_handlers& handlers
                 OracleSQL_query_lobs lobs={{0}, 0};          ){
   
                   Connection& connection=*static_cast<Connection *>(aconnection);
                   Query_lobs lobs={{0}, 0};
                 OCIStmt *stmthp=0;                  OCIStmt *stmthp=0;
   
                   SQL_Driver_services& services=*connection.services;
   
                 bool failed=false;                  bool failed=false;
                 if(setjmp(cs.mark)) {                  if(setjmp(connection.mark)) {
                         failed=true;                          failed=true;
                         goto cleanup;                          goto cleanup;
                 } else {                  } else {
                         const char *statement=preprocess_statement(services, cs,                           if(placeholders_count>MAX_BINDS)
                                 astatement, lobs);                                  fail(connection, "too many bind variables");
                           
                           while(isspace((unsigned char)*astatement)) 
                                   astatement++;
   
                           const char* client_charset=connection.options.client_charset;
                           const char* request_charset=services.request_charset();
                           bool transcode_needed=transcode_required(connection);
   
                           if(transcode_needed){
                                   // transcode query from $request:charset to ?ClientCharset
                                   size_t transcoded_xxx_size;
                                   services.transcode(astatement, strlen(astatement),
                                           astatement, transcoded_xxx_size,
                                           request_charset,
                                           client_charset);
                           }
   
                           const char *statement=_preprocess_statement_lobs(connection, astatement, lobs);
   
                         check(services, cs, "HandleAlloc STMT", OCIHandleAlloc(                           modified_statement mstatement=_preprocess_statement_limit(connection, statement, offset, limit);
                                 (dvoid *)cs.envhp, (dvoid **) &stmthp, (ub4)OCI_HTYPE_STMT, 0, 0));                          statement=mstatement.statement;
                         check(services, cs, "syntax",   
                                 OCIStmtPrepare(stmthp, cs.errhp, (unsigned char *)statement,                           if(mstatement.limit) // limit was added in statement
                                   limit=SQL_NO_LIMIT;
                           if(mstatement.offset) // limit was added in statement
                                   offset=0;
   
                           check(connection, "HandleAlloc STMT", OCIHandleAlloc( 
                                   (dvoid *)connection.envhp, (dvoid **) &stmthp, (ub4)OCI_HTYPE_STMT, 0, 0));
                           check(connection, "syntax", 
                                   OCIStmtPrepare(stmthp, connection.errhp, (unsigned char *)statement, 
                                 (ub4)strlen((char *)statement),                                   (ub4)strlen((char *)statement), 
                                 (ub4)OCI_NTV_SYNTAX, (ub4)OCI_DEFAULT));                                  (ub4)OCI_NTV_SYNTAX, (ub4)OCI_DEFAULT));
   
                           struct Bind_info {
                                           OCIBind *bind;
                                           sb2 indicator;
                           };
   
                           int binds_size=sizeof(Bind_info) * placeholders_count;
                           // we DO store OCIBind* into ATOMIC gc memory, 
                           // but we do not allocate/free it, that's done automatically from oracle [using environment handles]
                           // so we don't have to bother with that
                           Bind_info* binds=static_cast<Bind_info*>(services.malloc_atomic(binds_size));
                         {                          {
                                 for(int i=0; i<lobs.count; i++) {                                  for(size_t i=0; i<placeholders_count; i++) {
                                         check(services, cs, "alloc output var desc", OCIDescriptorAlloc(                                          Placeholder& ph=placeholders[i];
                                                 (dvoid *)cs.envhp, (dvoid **)&lobs.items[i].locator, (ub4)OCI_DTYPE_LOB, 0, 0));                                          Bind_info& bi=binds[i];
                                           bi.bind=0;
                                           // http://i/docs/oracle/server.804/a58234/basics.htm#422173
                                           bi.indicator=ph.is_null? -1: MAGIC_INDICATOR_VALUE_MEANING_NOT_NULL_AND_UNCHANGED;
   
                                           size_t value_length;
   
                                           if(transcode_needed){
                                                   // transcode bind variables names and their values from $request:charset to ?ClientCharset
                                                   size_t name_length;
                                                   services.transcode(ph.name, strlen(ph.name),
                                                           ph.name, name_length,
                                                           request_charset,
                                                           client_charset);
   
                                                   if(ph.value)
                                                           services.transcode(ph.value, strlen(ph.value),
                                                                   ph.value, value_length,
                                                                   request_charset,
                                                                   client_charset);
                                           } else {
                                                   value_length=ph.value? strlen(ph.value): 0;
                                           }
   
                                           // clone value for possible output binds
                                           // note: even empty input can be replaced by huge output
                                           char*& value_buf=connection.bind_buffers[i]; // get cached buffer
                                           if(!value_buf) // allocate if needed, caching it
                                                   value_buf=(char *)services.malloc_atomic(MAX_OUT_STRING_LENGTH+1/*terminator*/);
                                           if(value_length)
                                                   memcpy(value_buf, ph.value, value_length+1);
                                           else
                                                   value_buf[0]=0;
   
                                           char name_buf[MAX_STRING];
                                           sb4 placeh_len=snprintf(name_buf, sizeof(name_buf), ":%s", ph.name);
                                           char check_step_buf[MAX_STRING];
                                           snprintf(check_step_buf, sizeof(check_step_buf), "bind by name :%s", ph.name);
                                           check(connection, check_step_buf, OCIBindByName(stmthp, 
                                                   &bi.bind, connection.errhp, 
                                                   (text*)name_buf, placeh_len,
                                                   (dvoid *)value_buf, (sword)(MAX_OUT_STRING_LENGTH+1), SQLT_STR, (dvoid *)&bi.indicator, 
                                                   (ub2 *)0, (ub2 *)0, (ub4)0, (ub4 *)0, OCI_DEFAULT));
                                   }
   
                                         check(services, cs, "bind output", OCIBindByPos(stmthp,                                   for(int i=0; i<lobs.count; i++) {
                                                 &lobs.items[i].bind, cs.errhp,                                           Query_lobs::Item &item=lobs.items[i];
                                                 (ub4)1+i,                                           check(connection, "alloc output var desc", OCIDescriptorAlloc(
                                                 (dvoid *)&lobs.items[i].locator,                                                   (dvoid *)connection.envhp, (dvoid **)&item.locator, (ub4)OCI_DTYPE_LOB, 0, 0));
                                                 (sword)sizeof (lobs.items[i].locator), SQLT_CLOB, (dvoid *)0,   
                                           char placeholder_buf[MAX_STRING];
                                           sb4 placeh_len=snprintf(placeholder_buf, sizeof(placeholder_buf), 
                                                   ":%.*s", item.name_size, item.name_ptr);
                                           check(connection, "bind lob", OCIBindByName(stmthp, 
                                                   &item.bind, connection.errhp, 
                                                   (text*)placeholder_buf, placeh_len,
                                                   (dvoid *)&item.locator, 
                                                   (sword)sizeof (item.locator), SQLT_CLOB, (dvoid *)0, 
                                                 (ub2 *)0, (ub2 *)0, (ub4)0, (ub4 *)0, OCI_DATA_AT_EXEC));                                                  (ub2 *)0, (ub2 *)0, (ub4)0, (ub4 *)0, OCI_DATA_AT_EXEC));
   
                                         lobs.items[i].rows.count=0;                                          item.rows.count=0;
                                         OracleSQL_query_lobs::cbf_context_struct cbf_context={                                          item.connection=&connection;
                                                 &services, &cs, &lobs.items[i].rows};                                          check(connection, "bind dynamic", OCIBindDynamic(
                                         check(services, cs, "bind dynamic", OCIBindDynamic(                                                  item.bind, connection.errhp, 
                                                 lobs.items[i].bind, cs.errhp,                                                   (dvoid *) &item, cbf_no_data, 
                                                 (dvoid *) &cbf_context, cbf_no_data,                                                   (dvoid *) &item, cbf_get_data));
                                                 (dvoid *) &cbf_context, cbf_get_data));  
                                 }                                  }
                         }                          }
   
                         execute_prepared(services, cs,                           execute_prepared(connection, 
                                 statement, stmthp, lobs,                                   statement, stmthp, lobs, 
                                 offset, limit, handlers);                                  offset, limit, handlers, mstatement.skip_rownum_column);
   
                           {
                                   for(size_t i=0; i<placeholders_count; i++) {
                                           Bind_info& bi=binds[i];
                                           if(bi.indicator==MAGIC_INDICATOR_VALUE_MEANING_NOT_NULL_AND_UNCHANGED/*unchanged*/)
                                                   continue;
   
                                           Placeholder& ph=placeholders[i];
                                           if(bi.indicator==-1)
                                                   ph.is_null=true;
                                           else
                                                   if(bi.indicator==0)
                                                           ph.is_null=false;
                                                   else
                                                           fail(connection, bi.indicator<0?
                                                                   "output bind buffer overflow, additionally size too big to be returned in 'indicator'"
                                                                   : "output bind buffer overflow");
   
                                           ph.were_updated=true;
                                           const char* bind_buffer=connection.bind_buffers[i];
                                           if( size_t value_length=strlen(bind_buffer) ) {
                                                   char* returned_value=(char*)services.malloc_atomic(value_length+1/*terminator*/);
                                                   memcpy(returned_value, bind_buffer, value_length+1 );
                                                   ph.value=returned_value;
   
                                                   if(transcode_needed){
                                                           // transcode bind variable output from ?ClientCharset to $request:charset
                                                           services.transcode(ph.value, value_length,
                                                                   ph.value, value_length/*<this new value is not used afterwards, actually*/,
                                                                   client_charset,
                                                                   request_charset);
                                                   }
                                           } else {
                                                   ph.value=0;
                                           }
                                   }
                           }
                 }                  }
 cleanup: // no check call after this point!  cleanup: // no check call after this point!
                 {                  {
Line 436  cleanup: // no check call after this poi Line 648  cleanup: // no check call after this poi
                                         OCIDescriptorFree((dvoid *)locator, (ub4)OCI_DTYPE_LOB);                                          OCIDescriptorFree((dvoid *)locator, (ub4)OCI_DTYPE_LOB);
   
                                 /* free rows descriptors */                                  /* free rows descriptors */
                                 OracleSQL_query_lobs::return_rows &rows=lobs.items[i].rows;                                  Query_lobs::return_rows &rows=lobs.items[i].rows;
                                 for(int r=0; r<rows.count; r++)                                  for(int r=0; r<rows.count; r++)
                                         OCIDescriptorFree((dvoid *)rows.row[r].locator, (ub4)OCI_DTYPE_LOB);                                          OCIDescriptorFree((dvoid *)rows.row[r].locator, (ub4)OCI_DTYPE_LOB);
                         }                          }
Line 444  cleanup: // no check call after this poi Line 656  cleanup: // no check call after this poi
                 if(stmthp)                  if(stmthp)
                         OCIHandleFree((dvoid *)stmthp, (ub4)OCI_HTYPE_STMT);                          OCIHandleFree((dvoid *)stmthp, (ub4)OCI_HTYPE_STMT);
   
                 if(failed)                  if(failed) {
                         services._throw(cs.error);                          if(connection.sql_error.defined())
                                   services._throw(connection.sql_error);
                           services._throw(connection.error);
                   }
         }          }
   
 private: // private funcs  private: // private funcs
   
         const char *preprocess_statement(SQL_Driver_services& services, OracleSQL_connection_struct &cs,           const char* _preprocess_statement_lobs(
                 const char *astatement, OracleSQL_query_lobs &lobs) {                          Connection& connection, 
                 size_t statement_size=strlen(astatement);                          const char* statement,
                           Query_lobs &lobs
                 char *result=(char *)services.malloc(statement_size          ){
                   size_t statement_size=strlen(statement);
                   SQL_Driver_services& services=*connection.services;
                   
                   char *result=(char *)services.malloc_atomic(statement_size
                         +MAX_STRING // in case of short 'strings'                          +MAX_STRING // in case of short 'strings'
                         +11/* returning */+6/* into */+(MAX_LOB_NAME_LENGTH+2/*:, */)*2/*ret into*/*MAX_IN_LOBS                          +11/* returning */+6/* into */+(MAX_LOB_NAME_LENGTH+2/*:, */)*2/*ret into*/*MAX_IN_LOBS
                         +1);                          +1);
                 const char *o=astatement;                  const char *o=statement;
   
                 // /**xxx**/'literal' -> EMPTY_CLOB_FUNC_CALL                  // /**xxx**/'literal' -> EMPTY_CLOB_FUNC_CALL
                 char *n=result;                  char *n=result;
Line 467  private: // private funcs Line 686  private: // private funcs
                                 o[0]=='/' &&                                  o[0]=='/' &&
                                 o[1]=='*' &&                                   o[1]=='*' && 
                                 o[2]=='*') { // name start                                  o[2]=='*') { // name start
                                   const char* saved_o=o;
                                 o+=3;                                  o+=3;
                                 const char *name_begin=o;                                  const char *name_begin=o;
                                 while(*o)                                  while(*o)
Line 475  private: // private funcs Line 695  private: // private funcs
                                                 o[1]=='*' &&                                                  o[1]=='*' &&
                                                 o[2]=='/' &&                                                  o[2]=='/' &&
                                                 o[3]=='\'') { // name end                                                  o[3]=='\'') { // name end
                                                   saved_o=0; // found, marking that
                                                 const char *name_end=o;                                                  const char *name_end=o;
                                                 o+=4;                                                  o+=4;
                                                 OracleSQL_query_lobs::Item &item=lobs.items[lobs.count++];                                                  Query_lobs::Item &item=lobs.items[lobs.count++];
                                                 item.name_ptr=name_begin; item.name_size=name_end-name_begin;                                                  item.name_ptr=name_begin; item.name_size=name_end-name_begin;
                                                 item.data_ptr=(char *)services.malloc(statement_size/*max*/); item.data_size=0;                                                  item.data_ptr=(char *)services.malloc_atomic(statement_size/*max*/); item.data_size=0;
   
                                                 const char *start=o;                                                  const char *start=o;
                                                 bool escaped=false;                                                  bool escaped=false;
                                                 while(*o && !(o[0]=='\'' && o[1]!='\'' && !escaped)) {                                                  while(*o && !(o[0]=='\'' && o[1]!='\'' && !escaped)) {
                                                         escaped=o[0]=='\'' && o[1]=='\'';                                                          escaped=!escaped && (o[0]=='\'' && o[1]=='\'');
                                                         if(escaped) {                                                          if(escaped) {
                                                                 // write pending, skip "\" or "'"                                                                  // write pending, skip "\" or "'"
                                                                 if(size_t size=o-start) {                                                                  if(size_t size=o-start) {
Line 506  private: // private funcs Line 727  private: // private funcs
                                                 break;                                                  break;
                                         } else                                          } else
                                                 o++; // /**skip**/'xxx'                                                  o++; // /**skip**/'xxx'
                                   if(saved_o) {
                                           o=saved_o;
                                           *n++=*o++;
                                   }
                         } else                          } else
                                 *n++=*o++;                                  *n++=*o++;
                 }                  }
Line 518  private: // private funcs Line 743  private: // private funcs
                                 if(i)                                  if(i)
                                         *n++=',';                                          *n++=',';
                                 n+=sprintf(n, "%.*s", lobs.items[i].name_size, lobs.items[i].name_ptr);                                  n+=sprintf(n, "%.*s", lobs.items[i].name_size, lobs.items[i].name_ptr);
                                 /*memcpy(n, lobs.items[i].name_ptr, lobs.items[i].name_size);  
                                 n+=lobs.items[i].name_size;*/  
                         }                          }
                         n+=sprintf(n, " into ");                          n+=sprintf(n, " into ");
                         for(i=0; i<lobs.count; i++) {                          for(i=0; i<lobs.count; i++) {
                                 if(i)                                  if(i)
                                         *n++='x';                                          *n++=',';
                                 n+=sprintf(n, ":%.*s", lobs.items[i].name_size, lobs.items[i].name_ptr);                                  n+=sprintf(n, ":%.*s", lobs.items[i].name_size, lobs.items[i].name_ptr);
                                 /**n++=':';  
                                 memcpy(n, lobs.items[i].name_ptr, lobs.items[i].name_size);  
                                 n+=lobs.items[i].name_size;*/  
                         }                          }
                 }                  }
   
Line 536  private: // private funcs Line 756  private: // private funcs
         }          }
   
         void execute_prepared(          void execute_prepared(
                 SQL_Driver_services& services, OracleSQL_connection_struct &cs,                   Connection& connection, 
                 const char *statement, OCIStmt *stmthp, OracleSQL_query_lobs &lobs,                   const char* astatement, OCIStmt *stmthp, Query_lobs &lobs, 
                 unsigned long offset, unsigned long limit,                   unsigned long offset, unsigned long limit,
                 SQL_Driver_query_event_handlers& handlers) {                  SQL_Driver_query_event_handlers& handlers, bool skip_rownum_column) {
   
                 ub2 stmt_type=0; // UNKNOWN                  ub2 stmt_type=0; // UNKNOWN
         /*          /*
                 //gpfs on sun. paf 000818                  //gpfs on sun. paf 000818
                 //Zanyway, this is needed before.                   //Zanyway, this is needed before. 
                 check(services, cs, "get stmt type", OCIAttrGet(                  check(connection, "get stmt type", OCIAttrGet(
                         (dvoid *)stmthp, (ub4)OCI_HTYPE_STMT, (ub1 *)&stmt_type,                           (dvoid *)stmthp, (ub4)OCI_HTYPE_STMT, (ub1 *)&stmt_type, 
                         (ub4 *)0, OCI_ATTR_STMT_TYPE, cs.errhp));                          (ub4 *)0, OCI_ATTR_STMT_TYPE, connection.errhp));
         */          */
                 if(strncasecmp(statement, "select", 6)==0)   
                   if(strncasecmp(astatement, "select", 6)==0) 
                         stmt_type=OCI_STMT_SELECT;                          stmt_type=OCI_STMT_SELECT;
                 else if(strncasecmp(statement, "insert", 6)==0)                  else if(strncasecmp(astatement, "insert", 6)==0)
                         stmt_type=OCI_STMT_INSERT;                          stmt_type=OCI_STMT_INSERT;
                 else if(strncasecmp(statement, "update", 6)==0)                  else if(strncasecmp(astatement, "update", 6)==0)
                         stmt_type=OCI_STMT_UPDATE;                          stmt_type=OCI_STMT_UPDATE;
   
                 sword status=OCIStmtExecute(cs.svchp, stmthp, cs.errhp,                   sword status=OCIStmtExecute(connection.svchp, stmthp, connection.errhp, 
                         (ub4)stmt_type==OCI_STMT_SELECT?0:1, (ub4)0,                           (ub4)stmt_type==OCI_STMT_SELECT?0:1, (ub4)0, 
                         (OCISnapshot *)NULL,                           (OCISnapshot *)NULL, 
                         (OCISnapshot *)NULL, (ub4)OCI_DEFAULT);                          (OCISnapshot *)NULL, (ub4)OCI_DEFAULT);
   
                 if(status!=OCI_NO_DATA)                  if(status!=OCI_NO_DATA)
                         check(services, cs, "execute", status);                          check(connection, "execute", status);
   
                 {                  {
                         for(int i=0; i<lobs.count; i++)                           for(int i=0; i<lobs.count; i++) 
                                 if(ub4 bytes_to_write=lobs.items[i].data_size) {                                  if(ub4 bytes_to_write=lobs.items[i].data_size) {
                                         OracleSQL_query_lobs::return_rows *rows=&lobs.items[i].rows;                                          Query_lobs::return_rows *rows=&lobs.items[i].rows;
                                         for(int r=0; r<rows->count; r++) {                                          for(int r=0; r<rows->count; r++) {
                                                 OCILobLocator *locator=rows->row[r].locator;                                                  OCILobLocator *locator=rows->row[r].locator;
                                                 check(services, cs, "lobwrite", OCILobWrite (                                                  check(connection, "lobwrite", OCILobWrite (
                                                         cs.svchp, cs.errhp,                                                           connection.svchp, connection.errhp, 
                                                         locator, &bytes_to_write, 1,                                                           locator, &bytes_to_write, 1, 
                                                         (dvoid *)lobs.items[i].data_ptr, (ub4)bytes_to_write, OCI_ONE_PIECE,                                                           (dvoid *)lobs.items[i].data_ptr, (ub4)bytes_to_write, OCI_ONE_PIECE, 
                                                         (dvoid *)0, 0, (ub2)0,                                                           (dvoid *)0, 0, (ub2)0, 
Line 582  private: // private funcs Line 803  private: // private funcs
                                   
                 switch(stmt_type) {                  switch(stmt_type) {
                 case OCI_STMT_SELECT:                  case OCI_STMT_SELECT:
                         fetch_table(services, cs,                          fetch_table(connection,
                                 stmthp, offset, limit,                                   stmthp, offset, limit, 
                                 handlers);                                  handlers, skip_rownum_column);
                         break;                          break;
                 default:                  default:
                 /*                  /*
Line 595  private: // private funcs Line 816  private: // private funcs
                 }                  }
         }          }
   
         void fetch_table(SQL_Driver_services& services, OracleSQL_connection_struct &cs,           void fetch_table(Connection& connection, 
                 OCIStmt *stmthp, unsigned long offset, unsigned long limit,                   OCIStmt *stmthp, unsigned long offset, unsigned long limit,
                 SQL_Driver_query_event_handlers& handlers) {                  SQL_Driver_query_event_handlers& handlers, bool skip_rownum_column) 
           {
                   SQL_Driver_services& services=*connection.services;
   
                 ub4 prefetch_rows=100;                  ub4 prefetch_rows=100;
                 check(services, cs, "AttrSet prefetch-rows", OCIAttrSet(                   check(connection, "AttrSet prefetch-rows", OCIAttrSet( 
                         (dvoid *)stmthp, (ub4)OCI_HTYPE_STMT,                           (dvoid *)stmthp, (ub4)OCI_HTYPE_STMT, 
                         (dvoid *)&prefetch_rows, (ub4)0,                           (dvoid *)&prefetch_rows, (ub4)0, 
                         (ub4)OCI_ATTR_PREFETCH_ROWS, (OCIError *)cs.errhp));                          (ub4)OCI_ATTR_PREFETCH_ROWS, (OCIError *)connection.errhp));
   
                 ub4 prefetch_mem_size=100*1024;                  ub4 prefetch_mem_size=100*0x400;
                 check(services, cs, "AttrSet prefetch-memory", OCIAttrSet(                   check(connection, "AttrSet prefetch-memory", OCIAttrSet( 
                         (dvoid *)stmthp, (ub4)OCI_HTYPE_STMT,                           (dvoid *)stmthp, (ub4)OCI_HTYPE_STMT, 
                         (dvoid *)&prefetch_mem_size, (ub4)0,                           (dvoid *)&prefetch_mem_size, (ub4)0, 
                         (ub4)OCI_ATTR_PREFETCH_MEMORY, (OCIError *)cs.errhp));                          (ub4)OCI_ATTR_PREFETCH_MEMORY, (OCIError *)connection.errhp));
   
                 OCIParam          *mypard;                  OCIParam                *mypard;
                 ub2                    dtype;                  ub2                             dtype;
                 text                  *col_name;                  const char*             col_name;
   
                 struct {                  struct Col {
                         ub2 type;                          ub2 type;
                         char *str;                          char *str;
                         OCILobLocator *var;                          OCILobLocator *var;
Line 625  private: // private funcs Line 848  private: // private funcs
                 int column_count=0;                  int column_count=0;
   
                 bool failed=false;                  bool failed=false;
                 jmp_buf saved_mark; memcpy(saved_mark, cs.mark, sizeof(jmp_buf));                  jmp_buf saved_mark; memcpy(saved_mark, connection.mark, sizeof(jmp_buf));
                 if(setjmp(cs.mark)) {                  if(setjmp(connection.mark)) {
                         failed=true;                          failed=true;
                         goto cleanup;                          goto cleanup;
                 } else {                  } else {
                           bool transcode_needed=transcode_required(connection);
                           const char* client_charset=connection.options.client_charset;
                           const char* request_charset=services.request_charset();
   
                         // idea of preincrementing is that at error time all handles would free up                          // idea of preincrementing is that at error time all handles would free up
                         while(++column_count<=MAX_COLS) {                          while(++column_count<=MAX_COLS) {
                                 /* get next descriptor, if there is one */                                  /* get next descriptor, if there is one */
                                 if(OCIParamGet(stmthp, OCI_HTYPE_STMT, cs.errhp, (void **)&mypard,                                   if(OCIParamGet(stmthp, OCI_HTYPE_STMT, connection.errhp, (void **)&mypard, 
                                         (ub4) column_count)!=OCI_SUCCESS) {                                          (ub4) column_count)!=OCI_SUCCESS) {
                                         --column_count;                                          --column_count;
                                         break;                                          break;
                                 }                                  }
                                   
                                   if(skip_rownum_column && column_count==1)
                                           continue;
   
                                 /* Retrieve the data type attribute */                                  /* Retrieve the data type attribute */
                                 check(services, cs, "get type", OCIAttrGet(                                  check(connection, "get type", OCIAttrGet(
                                         (dvoid*) mypard, (ub4)OCI_DTYPE_PARAM,                                           (dvoid*) mypard, (ub4)OCI_DTYPE_PARAM, 
                                         (dvoid*) &dtype, (ub4 *)0, (ub4)OCI_ATTR_DATA_TYPE,                                           (dvoid*) &dtype, (ub4 *)0, (ub4)OCI_ATTR_DATA_TYPE, 
                                         (OCIError *)cs.errhp));                                          (OCIError *)connection.errhp));
                                                                   
                                 /* Retrieve the column name attribute */                                  /* Retrieve the column name attribute */
                                 ub4 col_name_len;                                  ub4 col_name_len;
                                 check(services, cs, "get name", OCIAttrGet(                                  check(connection, "get name", OCIAttrGet(
                                         (dvoid*) mypard, (ub4)OCI_DTYPE_PARAM,                                           (dvoid*) mypard, (ub4)OCI_DTYPE_PARAM, 
                                         (dvoid**) &col_name, (ub4 *) &col_name_len, (ub4)OCI_ATTR_NAME,                                           (dvoid**) &col_name, (ub4 *) &col_name_len, (ub4)OCI_ATTR_NAME, 
                                         (OCIError *)cs.errhp));                                          (OCIError *)connection.errhp));
                                   
                                   if(transcode_needed){
                                           // transcode column name from ?ClientCharset to $request:charset
                                           services.transcode(col_name, col_name_len,
                                                   col_name, col_name_len,
                                                   client_charset,
                                                   request_charset);
                                   }
   
                                   Col& col=cols[column_count-1];
                                 {                                  {
                                         size_t size=(size_t)col_name_len;                                          size_t length=(size_t)col_name_len;
                                         char *ptr=(char *)services.malloc(size);                                          char *ptr=(char *)services.malloc_atomic(length+1);
                                         tolower(ptr, (char *)col_name, size);                                          if( connection.options.bLowerCaseColumnNames ) 
                                         handlers.add_column(ptr, size);                                                  tolower_str(ptr, col_name, length);
                                           else
                                                   memcpy(ptr, col_name, length);                                          
                                           ptr[length]=0;
                                           check(connection, handlers.add_column(connection.sql_error, ptr, length));
                                 }                                  }
                                                                   
                                 ub2 coerce_type=dtype;                                  ub2 coerce_type=dtype;
Line 664  private: // private funcs Line 907  private: // private funcs
                                 void *ptr;                                  void *ptr;
                                                                   
                                 switch(dtype) {                                  switch(dtype) {
                                 case SQLT_CLOB:                                           case SQLT_CLOB: 
                                         {                                                  {
                                                 check(services, cs, "alloc output var desc", OCIDescriptorAlloc(                                                          check(connection, "alloc output var desc", OCIDescriptorAlloc(
                                                         (dvoid *)cs.envhp, (dvoid **)(ptr=&cols[column_count-1].var),                                                                   (dvoid *)connection.envhp, (dvoid **)(ptr=&col.var), 
                                                         (ub4)OCI_DTYPE_LOB,                                                                   (ub4)OCI_DTYPE_LOB, 
                                                         0, (dvoid **)0));                                                                  0, (dvoid **)0));
                                                                                                           
                                                 size=0;                                                          size=0;
                                                           break;
                                                   }
                                           default:
                                                   coerce_type=SQLT_STR;
                                                   char*& buf=connection.fetch_buffers[column_count-1];
                                                   ptr=buf; // get cached buffer
                                                   if(!ptr) // allocate if needed, caching it
                                                           ptr=buf=(char *)services.malloc_atomic(MAX_OUT_STRING_LENGTH+1/*terminator*/);
                                                   col.str=(char*)ptr;
                                                   size=MAX_OUT_STRING_LENGTH;
                                                 break;                                                  break;
                                         }  
                                 default:  
                                         coerce_type=SQLT_STR;  
                                         ptr=cols[column_count-1].str=(char *)services.malloc(MAX_OUT_STRING_LENGTH+1);  
                                         size=MAX_OUT_STRING_LENGTH;  
                                         break;  
                                 }                                  }
                                                                   
                                 cols[column_count-1].type=coerce_type;                                  col.type=coerce_type;
                                                                   
                                 check(services, cs, "DefineByPos", OCIDefineByPos(                                  // http://i/docs/oracle/server.804/a58234/oci_func.htm#449680
                                         stmthp, &cols[column_count-1].def, cs.errhp,                                   //   this call implicitly allocates the define handle
                                   // http://sunsite.eunnet.net/documentation/oracle.8.0.4/server.804/a58234/basics.htm
                                   //   when a statement handle is freed, any bind and define handles associated with it 
                                   //   are also freed
                                   col.def=0; check(connection, "DefineByPos", OCIDefineByPos(
                                           stmthp, &col.def, connection.errhp, 
                                         column_count, (ub1 *) ptr, size,                                           column_count, (ub1 *) ptr, size, 
                                         coerce_type, (dvoid *) &cols[column_count-1].indicator,                                           coerce_type, (dvoid *) &col.indicator, 
                                         (ub2 *)0, (ub2 *)0, OCI_DEFAULT));                                          (ub2 *)0, (ub2 *)0, OCI_DEFAULT));
                         }                          }
                                                   
                         handlers.before_rows();                          check(connection, handlers.before_rows(connection.sql_error));
                                                   
                         for(unsigned long row=0; !limit||row<limit+offset; row++) {                          for(unsigned long row=0; limit==SQL_NO_LIMIT || row<offset+limit; row++) {
                                 sword status=OCIStmtFetch(stmthp, cs.errhp, (ub4)1,  (ub4)OCI_FETCH_NEXT,                                   sword status=OCIStmtFetch(stmthp, connection.errhp, (ub4)1,  (ub4)OCI_FETCH_NEXT, 
                                         (ub4)OCI_DEFAULT);                                          (ub4)OCI_DEFAULT);
                                 if(status==OCI_NO_DATA)                                  if(status==OCI_NO_DATA)
                                         break;                                          break;
                                 check(services, cs, "fetch", status);                                  check(connection, "fetch", status);
   
                                 if(row>=offset) {                                  if(row>=offset) {
                                         handlers.add_row();                                          check(connection, handlers.add_row(connection.sql_error));
                                         for(int i=0; i<column_count; i++) {                                          for(int i=0; i<column_count; i++) {
                                                 size_t size=0;                                                  if(skip_rownum_column && i==0)
                                                 void *ptr=0;                                                          continue;
                                                 if(!cols[i].indicator) // not NULL  
                                                         switch(cols[i].type) {                                                  size_t length=0;
                                                         case SQLT_CLOB:                                                   char* strm=0;
                                                                 {                                                  bool transcode_value=transcode_needed;
                                                                         ub4   amtp=4096000000UL;  
                                                                         ub4   offset=1;                                                  sb2 indicator=cols[i].indicator;
                                                                         ub4   loblen=0;                                                  if(indicator!=-1) { // not NULL
                                                                         OCILobLocator *var=(OCILobLocator *)cols[i].var;                                                          if(indicator!=0)
                                                                         OCILobGetLength(cs.svchp, cs.errhp, var, &loblen);                                                                  fail(connection, indicator<0?
                                                                         if(loblen) {                                                                          "column return buffer overflow, additionally size too big to be returned in 'indicator'"
                                                                                 size=(size_t)loblen;                                                                          : "column return buffer overflow");
                                                                                 ptr=services.malloc(size);                                                          
                                                                                 check(services, cs, "lobread", OCILobRead(cs.svchp, cs.errhp,                                                           switch(cols[i].type){
                                                                                         var, &amtp, offset, (dvoid *) ptr,                                                                   case SQLT_CLOB: 
                                                                                         loblen, (dvoid *)0,                                                                           {
                                                                                         0,                                                                                   ub4   offset=1;
                                                                                         (ub2)0, (ub1) SQLCS_IMPLICIT));                                                                                  OCILobLocator *var=(OCILobLocator *)cols[i].var;
                                                                                   size_t read_size=0;
                                                                                   strm=(char*)services.malloc_atomic(1/*for terminator*/); // set type of memory block
                                                                                   do {
                                                                                           char buf[MAX_STRING*10];
                                                                                           ub4   amtp=0/*to be read in stream mode*/;
                                                                                           // http://i/docs/oracle/server.804/a58234/oci_func.htm#427818
                                                                                           status=OCILobRead(connection.svchp, connection.errhp, 
                                                                                                   var, &amtp, offset, (dvoid *)buf, 
                                                                                                   sizeof(buf), 
                                                                                                   (dvoid *)0, 0, 
                                                                                                   (ub2)0, (ub1)SQLCS_IMPLICIT);
                                                                                           if(status!=OCI_SUCCESS && status!=OCI_NEED_DATA)
                                                                                                   check(connection, "lobread", status);
   
                                                                                           strm=(char*)services.realloc(strm, read_size+amtp+1/*for termintator*/);
                                                                                           memcpy(strm+read_size, buf, amtp);
                                                                                           read_size+=amtp;
                                                                                           offset+=amtp;
                                                                                   } while(status==OCI_NEED_DATA);
   
                                                                                   length=(size_t)read_size;
                                                                                   strm[length]=0;
                                                                                   break;
                                                                           }
                                                                   case SQLT_NUM:
                                                                   case SQLT_INT:
                                                                   case SQLT_FLT:
                                                                   case SQLT_LNG:
                                                                   case SQLT_RID:
                                                                   case SQLT_UIN:
                                                                   case SQLT_DATE:
                                                                   case SQLT_TIME:
                                                                   case SQLT_TIMESTAMP:
                                                                           transcode_value=false; // transcode calls not needed for numbers and dates
                                                                   default:
                                                                           if(const char *value=cols[i].str) {
                                                                                   length=strlen(value);
                                                                                   strm=(char*)services.malloc_atomic(length+1);
                                                                                   memcpy(strm, value, length+1);
                                                                           } else {
                                                                                   length=0;
                                                                                   strm=0;
                                                                         }                                                                          }
                                                                         break;                                                                          break;
                                                                 }  
                                                         default:  
                                                                 if(const char *str=cols[i].str) {  
                                                                         size=strlen(str);  
                                                                         ptr=services.malloc(size);  
                                                                         memcpy(ptr, str, size);  
                                                                 } else {  
                                                                         size=0;  
                                                                         ptr=0;  
                                                                 }  
                                                                 break;  
                                                         }                                                          }
                                                 handlers.add_row_cell(ptr, size);                                                  }
   
                                                   const char* str=strm;
                                                   if(transcode_value && str && length){
                                                           // transcode cell value from ?ClientCharset to $request:charset
                                                           services.transcode(str, length,
                                                                   str, length,
                                                                   client_charset,
                                                                   request_charset);
                                                   }
   
                                                   check(connection, handlers.add_row_cell(connection.sql_error, str, length));
                                         }                                          }
                                 }                                  }
                         }                          }
Line 757  cleanup: // no check call after this poi Line 1051  cleanup: // no check call after this poi
                         longjmp(saved_mark, 1);                          longjmp(saved_mark, 1);
         }          }
   
           modified_statement _preprocess_statement_limit(
                   Connection& connection, 
                   const char* astatement,
                   unsigned long offset,
                   unsigned long limit
           ){
                   modified_statement result={astatement, false, false, false};
   
                   if(!connection.options.bDisableQueryModification && limit!=SQL_NO_LIMIT && strncasecmp(astatement, "select", 6)==0){
                           result.limit=true;
   
                           size_t statement_size=strlen(astatement);
                           char* statement_limited;
                           
                           if(offset && limit/* throwing offset away if limit==0 */){
                                   
                                   result.skip_rownum_column=true;
                                   result.offset=true;
   
                                   // SELECT * FROM (SELECT ROWNUM r__, z__.* FROM (user_query) z__) WHERE r__<=limit+offset AND r__>offset
                                   statement_limited=(char *)connection.services->malloc_atomic(
                                                   statement_size
                                                   +64/*SELECT * FROM (SELECT ROWNUM r__, z__.* FROM () z__) WHERE r__<=*/
                                                   +MAX_NUMBER
                                                   +9/* AND r__>*/
                                                   +MAX_NUMBER
                                                   +1/*terminator*/
                                           );
   
                                   result.statement=statement_limited;
   
                                   strcpy(statement_limited, "SELECT * FROM (SELECT ROWNUM r__, z__.* FROM (");
                                   strcat(statement_limited, astatement);
   
                                   statement_limited+=46+statement_size;
                                   statement_limited+=snprintf(statement_limited, 18+MAX_NUMBER, ") z__) WHERE r__<=%u", limit+offset);
                                   statement_limited+=snprintf(statement_limited, 9+MAX_NUMBER, " AND r__>%u", offset);
   
                           } else {
   
                                   // SELECT * FROM (user_query) WHERE ROWNUM<=limit
                                   // this statement can be easy for the sql server but we can't use it with offset
   
                                   statement_limited=(char *)connection.services->malloc_atomic(
                                                   statement_size
                                                   +31/*SELECT * FROM () WHERE ROWNUM<=*/
                                                   +MAX_NUMBER
                                                   +1/*terminator*/
                                           );
   
                                   result.statement=statement_limited;
   
                                   strcpy(statement_limited, "SELECT * FROM (");
                                   strcat(statement_limited, astatement);
   
                                   statement_limited+=15+statement_size;
                                   statement_limited+=snprintf(statement_limited, 16+MAX_NUMBER, ") WHERE ROWNUM<=%u", limit);
   
                           }
                           *statement_limited=0;
   
                           //connection.services->_throw(result.statement);
                   }
                   return result;
           }
   
 private: // conn client library funcs  private: // conn client library funcs
                   
         friend void check(          friend void fail(Connection& connection, const char *msg);
                 SQL_Driver_services& services, OracleSQL_connection_struct &cs,           friend void check(Connection& connection, const char *step, sword status);
                 const char *step, sword status);  
         friend sb4 cbf_get_data(dvoid *ctxp,           friend sb4 cbf_get_data(dvoid *ctxp, 
                 OCIBind *bindp,                   OCIBind *bindp, 
                 ub4 iter, ub4 index,                   ub4 iter, ub4 index, 
Line 795  private: // conn client library funcs Line 1154  private: // conn client library funcs
                 ub2 dty, dvoid *indp, ub2 *alenp, ub2 *rcodep,                  ub2 dty, dvoid *indp, ub2 *alenp, ub2 *rcodep,
                 ub4 maxarr_len, ub4 *curelep, ub4 mode));                  ub4 maxarr_len, ub4 *curelep, ub4 mode));
   
           OCI_DECL(BindByName, (OCIStmt *stmtp, OCIBind **bindp, OCIError *errhp,
                   text* placeholder, sb4 placeh_len, dvoid *valuep, sb4 value_sz,
                   ub2 dty, dvoid *indp, ub2 *alenp, ub2 *rcodep,
                   ub4 maxarr_len, ub4 *curelep, ub4 mode));
   
         OCI_DECL(BindDynamic, (OCIBind *bindp, OCIError *errhp, dvoid *ictxp,          OCI_DECL(BindDynamic, (OCIBind *bindp, OCIError *errhp, dvoid *ictxp,
                 OCICallbackInBind icbfp, dvoid *octxp,                  OCICallbackInBind icbfp, dvoid *octxp,
                 OCICallbackOutBind ocbfp));                  OCICallbackOutBind ocbfp));
Line 871  private: // conn client library funcs li Line 1235  private: // conn client library funcs li
   
         const char *dlink(const char *dlopen_file_spec) {          const char *dlink(const char *dlopen_file_spec) {
                 if(lt_dlinit())                  if(lt_dlinit())
   
                         return lt_dlerror();                          return lt_dlerror();
   
   
   
         lt_dlhandle handle=lt_dlopen(dlopen_file_spec);          lt_dlhandle handle=lt_dlopen(dlopen_file_spec);
   
                 //return "hren31";  
   
         if(!handle)          if(!handle)
                         return lt_dlerror(); //"can not open the dynamic link module";                          return lt_dlerror(); //"can not open the dynamic link module";
   
Line 893  private: // conn client library funcs li Line 1250  private: // conn client library funcs li
                 OCI_LINK(Initialize);                  OCI_LINK(Initialize);
                 OCI_LINK(EnvInit);                  OCI_LINK(EnvInit);
                 OCI_LINK(AttrGet);              OCI_LINK(AttrSet);                  OCI_LINK(AttrGet);              OCI_LINK(AttrSet);
                 OCI_LINK(BindByPos);            OCI_LINK(BindDynamic);                  OCI_LINK(BindByPos);            OCI_LINK(BindByName);   OCI_LINK(BindDynamic);
                 OCI_LINK(DefineByPos);                  OCI_LINK(DefineByPos);
                 OCI_LINK(DescriptorAlloc);              OCI_LINK(DescriptorFree);                  OCI_LINK(DescriptorAlloc);              OCI_LINK(DescriptorFree);
                 OCI_LINK(ErrorGet);                  OCI_LINK(ErrorGet);
Line 911  private: // conn client library funcs li Line 1268  private: // conn client library funcs li
   
 } *OracleSQL_driver;  } *OracleSQL_driver;
   
 void check(  void check(Connection& connection, const char *step, sword status) {
         SQL_Driver_services& services, OracleSQL_connection_struct &cs,   
         const char *step, sword status) {  
   
         const char *msg;          const char *msg;
         char reason[MAX_STRING/2];          char reason[MAX_STRING/2];
   
         switch (status) {          switch (status) {
         case OCI_SUCCESS:          case OCI_SUCCESS: // hurrah
                 return; // hurrah          case OCI_SUCCESS_WITH_INFO:             // ignoring. example: count(column) when column contains NULLs, 
                                                                                                                   // count() not counting them and gives that status
                   return;
         case OCI_ERROR:          case OCI_ERROR:
                 {                  {
                 sb4 errcode;                          sb4 errcode;
                 if(OracleSQL_driver->OCIErrorGet((dvoid *)cs.errhp, (ub4)1, (text *)NULL, &errcode,                           if(OracleSQL_driver->OCIErrorGet((dvoid *)connection.errhp, (ub4)1, (text *)NULL, &errcode, 
                         (text *)reason, (ub4)sizeof(reason), OCI_HTYPE_ERROR)==OCI_SUCCESS)                                  (text *)reason, (ub4)sizeof(reason), OCI_HTYPE_ERROR)==OCI_SUCCESS) {
                         msg=reason;                                  msg=reason;
                 else  
                         msg="[can not get error description]";                                  if(msg && transcode_required(connection)){
                 break;                                          // transcode server error message from ?ClientCharset to $request:charset
                                           if(size_t msg_length=strlen(msg)){
                                                   connection.services->transcode(msg, msg_length,
                                                           msg, msg_length,
                                                           connection.options.client_charset,
                                                           connection.services->request_charset());
                                           }
                                   }
                           } else
                                   msg="[can not get error description]";
                           break;
                 }                  }
         case OCI_SUCCESS_WITH_INFO:  
                 msg="SUCCESS_WITH_INFO"; break;  
         case OCI_NEED_DATA:          case OCI_NEED_DATA:
                 msg="NEED_DATA"; break;                  msg="NEED_DATA"; break;
         case OCI_NO_DATA:          case OCI_NO_DATA:
Line 947  void check( Line 1312  void check(
                 msg="unknown"; break;                  msg="unknown"; break;
         }          }
   
         snprintf(cs.error, sizeof(cs.error), "%s (%s, %d)",           snprintf(connection.error, sizeof(connection.error), "%s (%s, %d)", 
                 msg, step, (int)status);                  msg, step, (int)status);
         longjmp(cs.mark, 1);          longjmp(connection.mark, 1);
   }
   
   bool transcode_required(Connection& connection){
           return (connection.options.client_charset && strcmp(connection.options.client_charset, connection.services->request_charset())!=0);
   }
   
   void fail(Connection& connection, const char* msg) {
           snprintf(connection.error, sizeof(connection.error), "%s", msg);
           longjmp(connection.mark, 1);
 }  }
   
   void check(Connection& connection, bool error) {
           if(error)
                   longjmp(connection.mark, 1);
   }
   
 /* ----------------------------------------------------------------- */  /* ----------------------------------------------------------------- */
 /* Intbind callback that does not do any data input.                 */  /* Intbind callback that does not do any data input.                 */
 /* ----------------------------------------------------------------- */  /* ----------------------------------------------------------------- */
 static sb4 cbf_no_data(  sb4 cbf_no_data(
                                 dvoid *ctxp,                                   dvoid* /*ctxp*/, 
                                 OCIBind *bindp,                                   OCIBind* /*bindp*/, 
                                 ub4 iter, ub4 index,                                   ub4 /*iter*/, ub4 /*index*/, 
                                 dvoid **bufpp,                                   dvoid **bufpp, 
                                 ub4 *alenpp,                                   ub4 *alenpp, 
                                 ub1 *piecep,                                   ub1 *piecep, 
Line 978  static sb4 cbf_no_data( Line 1356  static sb4 cbf_no_data(
 /* ----------------------------------------------------------------- */  /* ----------------------------------------------------------------- */
 static sb4 cbf_get_data(dvoid *ctxp,   static sb4 cbf_get_data(dvoid *ctxp, 
                                  OCIBind *bindp,                                    OCIBind *bindp, 
                                  ub4 iter, ub4 index,                                    ub4 /*iter*/, ub4 index, 
                                  dvoid **bufpp,                                    dvoid **bufpp, 
                                  ub4 **alenp,                                    ub4 **alenp, 
                                  ub1 *piecep,                                    ub1 *piecep, 
                                  dvoid **indpp,                                    dvoid **indpp, 
                                  ub2 **rcodepp) {                                   ub2 **rcodepp) {
         OracleSQL_query_lobs::cbf_context_struct &context=          Query_lobs::Item& context=*static_cast<Query_lobs::Item*>(ctxp);
                 *(OracleSQL_query_lobs::cbf_context_struct *)ctxp;  
   
         if(index==0) {          if(index==0) {
                 static ub4  rows;                  static ub4  rows;
                 check(*context.services, *context.cs, "AttrGet cbf_get_data ROWS_RETURNED",                   check(*context.connection, "AttrGet cbf_get_data ROWS_RETURNED", 
                         OracleSQL_driver->OCIAttrGet(                          OracleSQL_driver->OCIAttrGet(
                                 (CONST dvoid *) bindp, OCI_HTYPE_BIND, (dvoid *)&rows,                                   (CONST dvoid *) bindp, OCI_HTYPE_BIND, (dvoid *)&rows, 
                                 (ub4 *)sizeof(ub2), OCI_ATTR_ROWS_RETURNED, context.cs->errhp)) ;                                  (ub4 *)sizeof(ub2), OCI_ATTR_ROWS_RETURNED, context.connection->errhp)) ;
                 context.rows->count=(ub2)rows;                  context.rows.count=(ub2)rows;
                 context.rows->row=(OracleSQL_query_lobs::return_rows::return_row *)                  context.rows.row=(Query_lobs::return_rows::return_row *)
                         context.services->malloc(sizeof(OracleSQL_query_lobs::return_rows::return_row)*rows);                          context.connection->services->malloc_atomic(sizeof(Query_lobs::return_rows::return_row)*rows);
         }          }
   
         OracleSQL_query_lobs::return_rows::return_row &var=context.rows->row[index];          Query_lobs::return_rows::return_row &var=context.rows.row[index];
   
         check(*context.services, *context.cs, "alloc output var desc dynamic", OracleSQL_driver->OCIDescriptorAlloc(          check(*context.connection, "alloc output var desc dynamic", OracleSQL_driver->OCIDescriptorAlloc(
                 (dvoid *) context.cs->envhp, (dvoid **)&var.locator,                   (dvoid *) context.connection->envhp, (dvoid **)&var.locator, 
                 (ub4)OCI_DTYPE_LOB,                   (ub4)OCI_DTYPE_LOB, 
                 0, (dvoid **)0));                  0, (dvoid **)0));
   
Line 1014  static sb4 cbf_get_data(dvoid *ctxp, Line 1391  static sb4 cbf_get_data(dvoid *ctxp,
         return OCI_CONTINUE;          return OCI_CONTINUE;
 }  }
   
 void tolower(char *out, const char *in, size_t size) {  
         while(size--)  
                 *out++=tolower(*in++);  
 }  
   
 extern "C" SQL_Driver *SQL_DRIVER_CREATE() {  extern "C" SQL_Driver *SQL_DRIVER_CREATE() {
         //_asm int 3;  
         return OracleSQL_driver=new OracleSQL_Driver();          return OracleSQL_driver=new OracleSQL_Driver();
 }  }

Removed from v.1.14  
changed lines
  Added in v.1.72


E-mail: