Diff for /sql/oracle/parser3oracle.C between versions 1.48 and 1.79

version 1.48, 2003/12/24 12:47:36 version 1.79, 2015/10/26 16:00:50
Line 1 Line 1
 /** @file  /** @file
         Parser Oracle driver.          Parser Oracle driver.
   
         Copyright(c) 2001, 2003 ArtLebedev Group (http://www.artlebedev.com)          Copyright (c) 2001-2015 Art. Lebedev Studio (http://www.artlebedev.com)
   
         Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)          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$";   
   
 #include "config_includes.h"  #include "config_includes.h"
   
Line 15  static const char *RCSId="$Id$"; Line 14  static const char *RCSId="$Id$";
   
 #include <oci.h>  #include <oci.h>
   
   volatile const char * IDENT_PARSER3ORACLE_C="$Id$" IDENT_PA_SQL_DRIVER_H;
   
 #define MAX_COLS 500  #define MAX_COLS 500
 #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 44  inline int min(int a, int b){ return a<b Line 46  inline int min(int a, int b){ return a<b
 #pragma warning(disable:4611)     #pragma warning(disable:4611)   
 #endif  #endif
   
   const sb2 MAGIC_INDICATOR_VALUE_MEANING_NOT_NULL_AND_UNCHANGED=99;
   
 /// @todo small memory leaks here  /// @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;
Line 83  static int pa_setenv(const char *name, c Line 87  static int pa_setenv(const char *name, c
                         strcat(buf, value);                          strcat(buf, value);
                         value=buf;                          value=buf;
                 }                  }
   
                 return setenv(name, value, 1/*overwrite*/);                   return setenv(name, value, 1/*overwrite*/); 
         } else {          } else {
                 unsetenv(name);                  unsetenv(name);
Line 92  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 char* rsplit(char* string, char delim){
           if(string){
                   if(char* v=strrchr(string, delim)){
                           *v=0;
                           return v+1;
                   }
           }
           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;          SQL_Driver_services *services;
   
         jmp_buf mark; char error[MAX_STRING];          jmp_buf mark; char error[MAX_STRING];
Line 123  struct OracleSQL_connection_struct { Line 151  struct OracleSQL_connection_struct {
         OCISession *usrhp;          OCISession *usrhp;
   
         char* fetch_buffers[MAX_COLS];          char* fetch_buffers[MAX_COLS];
           char* bind_buffers[MAX_BINDS];
   
         struct Options {          struct Options {
                 bool bLowerCaseColumnNames;                  bool bLowerCaseColumnNames;
                 const char* cstrClientCharset;                  bool bDisableQueryModification;
                   const char* client_charset;
         } options;          } 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 139  struct OracleSQL_query_lobs { Line 169  struct OracleSQL_query_lobs {
                 } *row;                  } *row;
                 int count;                  int count;
         };          };
         struct cbf_context_struct {  
                 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(OracleSQL_connection_struct &cs, const char *step, sword status);  static void fail(Connection& connection, const char *msg);
 void check(OracleSQL_connection_struct &cs, bool error);  static void check(Connection& connection, 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 173  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);  
 void toupper(char *out, const char *in, size_t size);  
   
 static const char *options2env(char *s, OracleSQL_connection_struct::Options* options) {  static  bool transcode_required(Connection& connection);
         while(s) {  
                 if(char *key=lsplit(&s, '&')) {  static const char *options2env(char *s, Connection::Options* options) {
                         if(*key) {          while(s){
                                 if(char *value=lsplit(key, '=')) {                  if(char *key=lsplit(&s, '&')){
                                         if( strcmp( key, "ClientCharset" ) == 0 ) {                          if(*key){
                                                 if(options) {                                  if(char *value=lsplit(key, '=')){
                                                         toupper(value, value, strlen(value));                                          if(strcmp(key, "ClientCharset")== 0){
                                                         options->cstrClientCharset = value;                                                  if(options){
                                                           toupper_str(value, value, strlen(value));
                                                           options->client_charset=value;
                                                 }                                                  }
                                                 continue;                                                  continue;
                                         }                                          }
   
                                         if( strcmp( key, "LowerCaseColumnNames" ) == 0 ) {                                          if(strcmp(key, "LowerCaseColumnNames")==0){
                                                   if(options)
                                                           options->bLowerCaseColumnNames=atoi(value)!=0;
                                                   continue;
                                           }
   
                                           if(strcmp(key, "DisableQueryModification")==0){
                                                 if(options)                                                  if(options)
                                                         options->bLowerCaseColumnNames = atoi(value)!=0;                                                          options->bDisableQueryModification=atoi(value)!=0;
                                                 continue;                                                  continue;
                                         }                                          }
   
Line 226  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
         */          */
         const char *initialize(char *dlopen_file_spec) {          const char *initialize(char *dlopen_file_spec) {
                 char *options=lsplit(dlopen_file_spec, '?');                  char *options=lsplit(dlopen_file_spec, '?');
   
                 const char *error=dlopen_file_spec?                  const char *error=options2env(options, 0);
                         dlink(dlopen_file_spec):"client library column is empty";              
                 if(!error) {                  if(!error) {
                         error=options2env(options, 0);                          error=dlopen_file_spec ? dlink(dlopen_file_spec) : "client library column is empty";
   
                         if(!error)                          if(!error)
                                 OCIInitialize((ub4)OCI_THREADED/*| OCI_OBJECT*/, (dvoid *)0,                                   OCIInitialize((ub4)OCI_THREADED/*| OCI_OBJECT*/, (dvoid *)0, 
                                         (dvoid * (*)(void *, unsigned int))0,                                           (dvoid * (*)(void *, size_t))0, 
                                         (dvoid * (*)(void*, void*, unsigned int))0,                                            (dvoid * (*)(void*, void*, size_t))0,  
                                         (void (*)(void*, void*))0                                           (void (*)(void*, void*))0 
                                 );                                  );
                 }                  }
Line 249  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;
                 cs.services=&services;                  connection.options.bLowerCaseColumnNames=true;
                 cs.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, &cs.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 299  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(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(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(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(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(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(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(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(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(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(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(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;  
   
                 // free fetch buffers          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++) {                  for(int i=0; i<MAX_COLS; i++) {
                         if(void* fetch_buffer=cs.fetch_buffers[i])                          if(void* fetch_buffer=connection.fetch_buffers[i])
                                 ::free(fetch_buffer);                                  connection.services->free(fetch_buffer);
                         else                          else
                                 break;                                  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(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 commit(void *connection) {  
             OracleSQL_connection_struct &cs=*(OracleSQL_connection_struct *)connection;          void rollback(void *aconnection) {
                 if(setjmp(cs.mark))              Connection& connection=*static_cast<Connection *>(aconnection);
                         cs.services->_throw(cs.error);                  if(setjmp(connection.mark))
                           connection.services->_throw(connection.error);
                 check(cs, "commit", OCITransCommit(cs.svchp, cs.errhp, 0));  
         }  
         void rollback(void *connection) {  
             OracleSQL_connection_struct &cs=*(OracleSQL_connection_struct *)connection;  
                 if(setjmp(cs.mark))  
                         cs.services->_throw(cs.error);  
   
                 // sometimes rollback is done in context when this yields error which masks previous 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                  // consider consequent errors not very important to report, reporting first one
                 /*check(cs, "rollback", */OCITransRollback(cs.svchp, cs.errhp, 0)/*)*/;                  /*check(connection, "rollback", */OCITransRollback(connection.svchp, connection.errhp, 0)/*)*/;
         }          }
   
         bool ping(void* /*connection*/) {          bool ping(void* /*connection*/) {
Line 402  public: Line 445  public:
                 return true;                  return true;
         }          }
   
         const char* quote(void *connection,          // charset here is services.request_charset(), not connection.client_charset
                 const char *from, unsigned int length)           // thus we can't use the sql server quoting support
           const char* quote(void *aconnection, const char *str, unsigned int length) 
         {          {
                 OracleSQL_connection_struct &cs=*(OracleSQL_connection_struct *)connection;                  const char* from;
                 char *result=(char*)cs.services->malloc_atomic(length*2+1);                  const char* from_end=str+length;
                 char *to=result;  
                 while(length--) {                  size_t quoted=0;
                         switch(*from) {  
                         case '\'': // "'" -> "''"                  for(from=str; from<from_end; from++){
                                 *to++='\'';                          if(*from=='\'')
                                 break;                                  quoted++;
                         }                  }
                         *to++=*from++;  
                   if(!quoted)
                           return str;
   
                   Connection& connection=*static_cast<Connection*>(aconnection);
                   char *result=(char*)connection.services->malloc_atomic(length + quoted + 1);
                   char *to = result;
   
                   for(from=str; from<from_end; from++){
                           if(*from=='\'')
                                   *to++= '\''; // ' -> ''
                           *to++=*from;
                 }                  }
                   
                 *to=0;                  *to=0;
                 return result;                  return result;
         }          }
         void query(void *connection,   
                 const char *astatement, unsigned long offset, unsigned long limit,   
                 SQL_Driver_query_event_handlers& handlers)   
         {  
                 OracleSQL_connection_struct &cs=*(OracleSQL_connection_struct *)connection;  
                 OracleSQL_query_lobs lobs={{0}, 0};  
                 OCIStmt *stmthp=0;  
   
                 SQL_Driver_services& services=*cs.services;          void query(void* aconnection, 
                           const char* astatement, 
                           size_t placeholders_count, Placeholder* placeholders, 
                           unsigned long offset, unsigned long limit,
                           SQL_Driver_query_event_handlers& handlers
           ){
   
                 // transcode from $request:charset to connect-string?client_charset                  Connection& connection=*static_cast<Connection *>(aconnection);
                 size_t transcoded_statement_size;                  Query_lobs lobs={{0}, 0};
                 if(const char* cstrClientCharset=cs.options.cstrClientCharset)                  OCIStmt *stmthp=0;
                         services.transcode(astatement, strlen(astatement),  
                                 astatement, transcoded_statement_size,                  SQL_Driver_services& services=*connection.services;
                                 services.request_charset(),  
                                 cstrClientCharset);  
   
                 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(cs, astatement, lobs);                          if(placeholders_count>MAX_BINDS)
                                   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(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(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(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;
   
                                         check(cs, "bind output", OCIBindByPos(stmthp,                                           char name_buf[MAX_STRING];
                                                 &lobs.items[i].bind, cs.errhp,                                           sb4 placeh_len=snprintf(name_buf, sizeof(name_buf), ":%s", ph.name);
                                                 (ub4)1+i,                                           char check_step_buf[MAX_STRING];
                                                 (dvoid *)&lobs.items[i].locator,                                           snprintf(check_step_buf, sizeof(check_step_buf), "bind by name :%s", ph.name);
                                                 (sword)sizeof (lobs.items[i].locator), SQLT_CLOB, (dvoid *)0,                                           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));
                                   }
   
                                   for(int i=0; i<lobs.count; i++) {
                                           Query_lobs::Item &item=lobs.items[i];
                                           check(connection, "alloc output var desc", OCIDescriptorAlloc(
                                                   (dvoid *)connection.envhp, (dvoid **)&item.locator, (ub4)OCI_DTYPE_LOB, 0, 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={&cs, &lobs.items[i].rows};                                          item.connection=&connection;
                                         check(cs, "bind dynamic", OCIBindDynamic(                                          check(connection, "bind dynamic", OCIBindDynamic(
                                                 lobs.items[i].bind, cs.errhp,                                                   item.bind, connection.errhp, 
                                                 (dvoid *) &cbf_context, cbf_no_data,                                                   (dvoid *) &item, cbf_no_data, 
                                                 (dvoid *) &cbf_context, cbf_get_data));                                                  (dvoid *) &item, cbf_get_data));
                                 }                                  }
                         }                          }
   
                         execute_prepared(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 483  cleanup: // no check call after this poi Line 661  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 492  cleanup: // no check call after this poi Line 670  cleanup: // no check call after this poi
                         OCIHandleFree((dvoid *)stmthp, (ub4)OCI_HTYPE_STMT);                          OCIHandleFree((dvoid *)stmthp, (ub4)OCI_HTYPE_STMT);
   
                 if(failed) {                  if(failed) {
                         if(cs.sql_error.defined())                          if(connection.sql_error.defined())
                                 services._throw(cs.sql_error);                                  services._throw(connection.sql_error);
                         services._throw(cs.error);                          services._throw(connection.error);
                 }                  }
         }          }
   
 private: // private funcs  private: // private funcs
   
         const char *preprocess_statement(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,
                 SQL_Driver_services& services=*cs.services;                          Query_lobs &lobs
           ){
                   size_t statement_size=strlen(statement);
                   SQL_Driver_services& services=*connection.services;
                   
                 char *result=(char *)services.malloc_atomic(statement_size                  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 530  private: // private funcs Line 711  private: // private funcs
                                                 saved_o=0; // found, marking that                                                  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_atomic(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 588  private: // private funcs Line 769  private: // private funcs
         }          }
   
         void execute_prepared(          void execute_prepared(
                 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(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));
         */          */
   
                 while(isspace(*statement))                   if(strncasecmp(astatement, "select", 6)==0) 
                         statement++;  
                 if(strncasecmp(statement, "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(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(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 637  private: // private funcs Line 816  private: // private funcs
                                   
                 switch(stmt_type) {                  switch(stmt_type) {
                 case OCI_STMT_SELECT:                  case OCI_STMT_SELECT:
                         fetch_table(cs,                          fetch_table(connection,
                                 stmthp, offset, limit,                                   stmthp, offset, limit, 
                                 handlers);                                  handlers, skip_rownum_column);
                         break;                          break;
                 default:                  default:
                 /*                  /*
Line 650  private: // private funcs Line 829  private: // private funcs
                 }                  }
         }          }
   
         void fetch_table(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=*cs.services;                  SQL_Driver_services& services=*connection.services;
   
                 ub4 prefetch_rows=100;                  ub4 prefetch_rows=100;
                 check(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*0x400;                  ub4 prefetch_mem_size=100*0x400;
                 check(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 Col {                  struct Col {
                         ub2 type;                          ub2 type;
Line 682  private: // private funcs Line 861  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(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(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));
                                   
                                   size_t length=(size_t)col_name_len;
                                   if(transcode_needed){
                                           // transcode column name from ?ClientCharset to $request:charset
                                           services.transcode(col_name, col_name_len,
                                                   col_name, length,
                                                   client_charset,
                                                   request_charset);
                                   }
   
                                 Col& col=cols[column_count-1];                                  Col& col=cols[column_count-1];
                                 {                                  {
                                         size_t length=(size_t)col_name_len;  
                                         char *ptr=(char *)services.malloc_atomic(length+1);                                          char *ptr=(char *)services.malloc_atomic(length+1);
                                         if( cs.options.bLowerCaseColumnNames )                                           if( connection.options.bLowerCaseColumnNames ) 
                                                 tolower(ptr, (char *)col_name, length);                                                  tolower_str(ptr, col_name, length);
                                         else                                          else
                                                 memcpy(ptr, col_name, length);                                                                                            memcpy(ptr, col_name, length);                                          
                                         ptr[length]=0;                                          ptr[length]=0;
                                         check(cs, handlers.add_column(cs.sql_error, ptr, length));                                          check(connection, handlers.add_column(connection.sql_error, ptr, length));
                                 }                                  }
                                                                   
                                 ub2 coerce_type=dtype;                                  ub2 coerce_type=dtype;
Line 726  private: // private funcs Line 920  private: // private funcs
                                 void *ptr;                                  void *ptr;
                                                                   
                                 switch(dtype) {                                  switch(dtype) {
                                 case SQLT_CLOB:                                           case SQLT_CLOB: 
                                         {                                                  {
                                                 check(cs, "alloc output var desc", OCIDescriptorAlloc(                                                          check(connection, "alloc output var desc", OCIDescriptorAlloc(
                                                         (dvoid *)cs.envhp, (dvoid **)(ptr=&col.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;  
                                         char*& buf=cs.fetch_buffers[column_count-1];  
                                         ptr=buf; // get cached buffer  
                                         if(!ptr) // allocate if needed, caching it  
                                                 ptr=buf=(char *)::/*see disconnect*/malloc(MAX_OUT_STRING_LENGTH+1/*terminator*/);  
                                         col.str=(char*)ptr;  
                                         size=MAX_OUT_STRING_LENGTH;  
                                         break;  
                                 }                                  }
                                                                   
                                 col.type=coerce_type;                                  col.type=coerce_type;
Line 754  private: // private funcs Line 948  private: // private funcs
                                 // http://sunsite.eunnet.net/documentation/oracle.8.0.4/server.804/a58234/basics.htm                                  // 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                                   //   when a statement handle is freed, any bind and define handles associated with it 
                                 //   are also freed                                  //   are also freed
                                 col.def=0; check(cs, "DefineByPos", OCIDefineByPos(                                  col.def=0; check(connection, "DefineByPos", OCIDefineByPos(
                                         stmthp, &col.def, cs.errhp,                                           stmthp, &col.def, connection.errhp, 
                                         column_count, (ub1 *) ptr, size,                                           column_count, (ub1 *) ptr, size, 
                                         coerce_type, (dvoid *) &col.indicator,                                           coerce_type, (dvoid *) &col.indicator, 
                                         (ub2 *)0, (ub2 *)0, OCI_DEFAULT));                                          (ub2 *)0, (ub2 *)0, OCI_DEFAULT));
                         }                          }
                                                   
                         check(cs, handlers.before_rows(cs.sql_error));                          check(connection, handlers.before_rows(connection.sql_error));
                                                   
                         for(unsigned long row=0; !limit||row<offset+limit; 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(cs, "fetch", status);                                  check(connection, "fetch", status);
   
                                 if(row>=offset) {                                  if(row>=offset) {
                                         check(cs, handlers.add_row(cs.sql_error));                                          check(connection, handlers.add_row(connection.sql_error));
                                         for(int i=0; i<column_count; i++) {                                          for(int i=0; i<column_count; i++) {
                                                   if(skip_rownum_column && i==0)
                                                           continue;
   
                                                 size_t length=0;                                                  size_t length=0;
                                                 char* strm=0;                                                  char* strm=0;
                                                 if(!cols[i].indicator) // not NULL                                                  bool transcode_value=transcode_needed;
                                                         switch(cols[i].type) {  
                                                         case SQLT_CLOB:   
                                                                 {  
                                                                         ub4   offset=1;  
                                                                         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(cs.svchp, cs.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(cs, "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;                                                  sb2 indicator=cols[i].indicator;
                                                                         strm[length]=0;                                                  if(indicator!=-1) { // not NULL
                                                           if(indicator!=0)
                                                                   fail(connection, indicator<0?
                                                                           "column return buffer overflow, additionally size too big to be returned in 'indicator'"
                                                                           : "column return buffer overflow");
                                                           
                                                           switch(cols[i].type){
                                                                   case SQLT_CLOB: 
                                                                           {
                                                                                   ub4   offset=1;
                                                                                   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 *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;  
                                                         }                                                          }
                                                   }
   
                                                 const char* str=strm;                                                  const char* str=strm;
                                                 if(str && length)                                                  if(transcode_value && str && length){
                                                 {                                                          // transcode cell value from ?ClientCharset to $request:charset
                                                         // transcode to $request:charset from connect-string?client_charset                                                          services.transcode(str, length,
                                                         if(const char* cstrClientCharset=cs.options.cstrClientCharset) {                                                                  str, length,
                                                                 const char* dest;                                                                  client_charset,
                                                                 size_t dest_length;                                                                  request_charset);
                                                                 services.transcode(str, length,  
                                                                         dest, dest_length,  
                                                                         cstrClientCharset,  
                                                                         services.request_charset());  
                                                                 str=dest;  
                                                                 length=dest_length;  
                                                         }  
                                                 }                                                  }
   
                                                 check(cs, handlers.add_row_cell(cs.sql_error, str, length));                                                  check(connection, handlers.add_row_cell(connection.sql_error, str, length));
                                         }                                          }
                                 }                                  }
                         }                          }
Line 855  cleanup: // no check call after this poi Line 1064  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__<=%lu", limit+offset);
                                   statement_limited+=snprintf(statement_limited, 9+MAX_NUMBER, " AND r__>%lu", 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<=%lu", limit);
   
                           }
                           *statement_limited=0;
   
                           //connection.services->_throw(result.statement);
                   }
                   return result;
           }
   
 private: // conn client library funcs  private: // conn client library funcs
                   
         friend void check(OracleSQL_connection_struct &cs, const char *step, sword status);          friend void fail(Connection& connection, const char *msg);
           friend void check(Connection& connection, 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 891  private: // conn client library funcs Line 1167  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 966  private: // conn client library funcs Line 1247  private: // conn client library funcs
 private: // conn client library funcs linking  private: // conn client library funcs linking
   
         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();                          if(const char* result=lt_dlerror())
         lt_dlhandle handle=lt_dlopen(dlopen_file_spec);                                  return result;
         if(!handle)                          return "can not prepare to dynamic loading";
                         return lt_dlerror(); //"can not open the dynamic link module";                  }
   
                   lt_dlhandle handle=lt_dlopen(dlopen_file_spec);
   
                   if(!handle){
                           if(const char* result=lt_dlerror())
                                   return result;
                           return "can not open the dynamic link module";
                   }
   
                 #define DSLINK(name, action) \                  #define DSLINK(name, action) \
                         name=(t_##name)lt_dlsym(handle, #name); \                          name=(t_##name)lt_dlsym(handle, #name); \
Line 982  private: // conn client library funcs li Line 1271  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 1000  private: // conn client library funcs li Line 1289  private: // conn client library funcs li
   
 } *OracleSQL_driver;  } *OracleSQL_driver;
   
 void check(OracleSQL_connection_struct &cs, const char *step, sword status) {  void check(Connection& connection, const char *step, sword status) {
   
         const char *msg;          const char *msg;
         char reason[MAX_STRING/2];          char reason[MAX_STRING/2];
Line 1013  void check(OracleSQL_connection_struct & Line 1302  void check(OracleSQL_connection_struct &
         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;
   
                                 // transcode to $request:charset from connect-string?client_charset                                  if(msg && transcode_required(connection)){
                                 if(const char* cstrClientCharset=cs.options.cstrClientCharset) {                                          // transcode server error message from ?ClientCharset to $request:charset
                                         if(msg) {                                          if(size_t msg_length=strlen(msg)){
                                                 if(size_t msg_length=strlen(msg)) {                                                  connection.services->transcode(msg, msg_length,
                                                         cs.services->transcode(msg, msg_length,                                                          msg, msg_length,
                                                                 msg, msg_length,                                                          connection.options.client_charset,
                                                                 cstrClientCharset,                                                          connection.services->request_charset());
                                                                 cs.services->request_charset());  
                                                 }  
                                         }                                          }
                                 }                                  }
                         } else                          } else
Line 1046  void check(OracleSQL_connection_struct & Line 1333  void check(OracleSQL_connection_struct &
                 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(OracleSQL_connection_struct &cs, bool error) {  void check(Connection& connection, bool error) {
         if(error)          if(error)
                 longjmp(cs.mark, 1);                  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*/, 
Line 1087  static sb4 cbf_get_data(dvoid *ctxp, Line 1383  static sb4 cbf_get_data(dvoid *ctxp,
                                  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.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.cs->services->malloc_atomic(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.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 1117  static sb4 cbf_get_data(dvoid *ctxp, Line 1412  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++=(char)tolower(*in++);  
 }  
 void toupper(char *out, const char *in, size_t size) {  
         while(size--)  
                 *out++=(char)toupper(*in++);  
 }  
   
   
 extern "C" SQL_Driver *SQL_DRIVER_CREATE() {  extern "C" SQL_Driver *SQL_DRIVER_CREATE() {
         return OracleSQL_driver=new OracleSQL_Driver();          return OracleSQL_driver=new OracleSQL_Driver();
 }  }

Removed from v.1.48  
changed lines
  Added in v.1.79


E-mail: