Diff for /sql/mysql/parser3mysql.C between versions 1.5 and 1.30

version 1.5, 2001/11/16 12:38:52 version 1.30, 2008/05/04 07:37:31
Line 1 Line 1
 /** @file  /** @file
         Parser MySQL driver.          Parser MySQL driver.
   
         Copyright(c) 2001 ArtLebedev Group(http://www.artlebedev.com)          Copyright(c) 2001, 2003 ArtLebedev Group (http://www.artlebedev.com)
   
         Author: Alexander Petrosyan <paf@design.ru>(http://design.ru/paf)          Author: Alexandr Petrosian <paf@design.ru> (http://paf.design.ru)
   
         2001.07.30 using MySQL 3.23.22b          2001.07.30 using MySQL 3.23.22b
   
Line 46  static char *lsplit(char **string_ref, c Line 46  static char *lsplit(char **string_ref, c
     return result;      return result;
 }  }
   
   static char* rsplit(char* string, char delim) {
       if(string) {
                   char* v=strrchr(string, delim); 
                   if(v) {
                           *v=0;
                           return v+1;
                   }
       }
       return NULL;        
   }
   
   static void toupper_str(char *out, const char *in, size_t size) {
           while(size--)
                   *out++=(char)toupper(*in++);
   }
   
   struct Connection {
           SQL_Driver_services* services;
   
           MYSQL* handle;
           const char* cstrClientCharset;
           bool autocommit;
   };
   
    
 /**  /**
         MySQL server driver          MySQL server driver
 */  */
 class MySQL_Driver : public SQL_Driver {  class MySQL_Driver : public SQL_Driver {
 public:  public:
   
         MySQL_Driver() : SQL_Driver() {          MySQL_Driver() : SQL_Driver() {}
   
           ~MySQL_Driver() {
                   if(mysql_server_end)
                           mysql_server_end();
         }          }
   
         /// get api version          /// get api version
Line 63  public: Line 92  public:
                         dlink(dlopen_file_spec):"client library column is empty";                          dlink(dlopen_file_spec):"client library column is empty";
         }          }
         /**     connect          /**     connect
                 @param used_only_in_connect_url                  @param url
                         format: @b user:pass@host[:port]|[/unix/socket]/database?                          format: @b user:pass@host[:port]|[/unix/socket]/database?
                                 charset=cp1251_koi8&                                  charset=cp1251_koi8&
                                 timeout=3&                                  timeout=3&
                                 compress=1&                                  compress=1&
                                 named_pipe=1                                  named_pipe=1
                         3.23.22b                          3.23.22b
                         Currently the only option for @b character_set_name is cp1251_koi8.                                  Currently the only option for @b character_set_name is cp1251_koi8.
                         WARNING: must be used only to connect, for buffer doesn't live long                                  WARNING: must be used only to connect, for buffer doesn't live long
                           4.1+ accept not only 'cp1251_koi8' but 'cp1251', 'utf8' and much more
                                   it can be usable for transcoding using sql server
         */          */
         void connect(          void connect(
                 char *used_only_in_connect_url,                   char *url, 
                 SQL_Driver_services& services,                   SQL_Driver_services& services, 
                 void **connection ///< output: MYSQL *                  void **connection_ref ///< output: Connection*
                 ) {                  ) {
                 char *user=used_only_in_connect_url;                  char *user=url;
                 char *s=lsplit(user, '@');                  char *s=rsplit(user, '@');
                 char *host=0;                  char *host=0;
                 char *unix_socket=0;                  char *unix_socket=0;
                 if(s && s[0]=='[') { // unix socket                  if(s && s[0]=='[') { // unix socket
Line 95  public: Line 126  public:
                 int port=port_cstr?strtol(port_cstr, &error_pos, 0):0;                  int port=port_cstr?strtol(port_cstr, &error_pos, 0):0;
                 char *options=lsplit(db, '?');                  char *options=lsplit(db, '?');
   
                 char *charset=0;                  char *cstrBackwardCompAskServerToTranscode=0;
   
             MYSQL *mysql=mysql_init(NULL);                  Connection& connection=*(Connection  *)services.malloc(sizeof(Connection));
                   connection.services=&services;
               connection.handle=mysql_init(NULL);
                   connection.cstrClientCharset=0; 
                   connection.autocommit=true;
                   *connection_ref=&connection;
   
                 while(options) {                  while(options) {
                         if(char *key=lsplit(&options, '&')) {                          if(char *key=lsplit(&options, '&')) {
                                 if(*key) {                                  if(*key) {
                                         if(char *value=lsplit(key, '=')) {                                          if(char *value=lsplit(key, '=')) {
                                                 if(strcasecmp(key, "charset")==0) {                                                  if(strcmp(key, "ClientCharset" ) == 0) {
                                                         charset=value;                                                          toupper_str(value, value, strlen(value));
                                                           connection.cstrClientCharset=value;
                                                   } else if(strcasecmp(key, "charset")==0) { // left for backward compatibility, consider using ClientCharset
                                                           cstrBackwardCompAskServerToTranscode=value;
                                                 } else if(strcasecmp(key, "timeout")==0) {                                                  } else if(strcasecmp(key, "timeout")==0) {
                                                         unsigned int timeout=(unsigned int)atoi(value);                                                          unsigned int timeout=(unsigned int)atoi(value);
                                                         if(mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, (const char *)&timeout)!=0)                                                          if(mysql_options(connection.handle, MYSQL_OPT_CONNECT_TIMEOUT, (const char *)&timeout)!=0)
                                                                 services._throw(mysql_error(mysql));                                                                  services._throw(mysql_error(connection.handle));
                                                 } else if(strcasecmp(key, "compress")==0) {                                                  } else if(strcasecmp(key, "compress")==0) {
                                                         if(atoi(value))                                                          if(atoi(value))
                                                                 if(mysql_options(mysql, MYSQL_OPT_COMPRESS, 0)!=0)                                                                  if(mysql_options(connection.handle, MYSQL_OPT_COMPRESS, 0)!=0)
                                                                         services._throw(mysql_error(mysql));                                                                          services._throw(mysql_error(connection.handle));
                                                 } else if(strcasecmp(key, "named_pipe")==0) {                                                  } else if(strcasecmp(key, "named_pipe")==0) {
                                                         if(atoi(value))                                                          if(atoi(value))
                                                                 if(mysql_options(mysql, MYSQL_OPT_NAMED_PIPE , 0)!=0)                                                                  if(mysql_options(connection.handle, MYSQL_OPT_NAMED_PIPE , 0)!=0)
                                                                         services._throw(mysql_error(mysql));                                                                          services._throw(mysql_error(connection.handle));
                                                   } else if(strcasecmp(key, "autocommit")==0) {
                                                           if(atoi(value)==0) {
                                                                   connection.autocommit=false;
                                                           }
                                                 } else                                                  } else
                                                         services._throw("unknown connect option" /*key*/);                                                          services._throw("unknown connect option" /*key*/);
                                         } else                                           } else 
Line 125  public: Line 168  public:
                         }                          }
                 }                  }
   
                 if(!mysql_real_connect(mysql,                   // if(connection.cstrClientCharset && cstrBackwardCompAskServerToTranscode)
                         host, user, pwd, db, port?port:MYSQL_PORT, unix_socket, 0))                  //      services._throw("use 'ClientCharset' option only, "
                         services._throw(mysql_error(mysql));                  //              "'charset' option is obsolete and should not be used with new 'ClientCharset' option");
   
                   if(!mysql_real_connect(connection.handle, 
                           host, user, pwd, db, port?port:MYSQL_PORT, unix_socket, CLIENT_MULTI_RESULTS))
                           services._throw(mysql_error(connection.handle));
   
                 if(charset) {                  if(cstrBackwardCompAskServerToTranscode) {
                         // set charset                          // set charset
                         char statement[MAX_STRING]="set CHARACTER SET "; // cp1251_koi8                          char statement[MAX_STRING]="set CHARACTER SET ";
                         strncat(statement, charset, MAX_STRING);                          strncat(statement, cstrBackwardCompAskServerToTranscode, MAX_STRING);
                                                   
                         if(mysql_query(mysql, statement))                           exec(connection, statement);
                                 services._throw(mysql_error(mysql));  
                         (*mysql_store_result)(mysql); // throw out the result [don't need but must call]  
                 }                  }
   
                 *(MYSQL **)connection=mysql;                  if(!connection.autocommit)
                           exec(connection, "set autocommit=0");
         }          }
         void disconnect(void *connection) {  
             mysql_close((MYSQL *)connection);          void exec(Connection& connection, const char* statement) {
                   if(mysql_query(connection.handle, statement)) 
                           connection.services->_throw(mysql_error(connection.handle));
                   (*mysql_store_result)(connection.handle); // throw out the result [don't need but must call]
         }          }
         void commit(SQL_Driver_services&, void *) {}  
         void rollback(SQL_Driver_services&, void *) {}  
   
         bool ping(SQL_Driver_services&, void *connection) {          void disconnect(void *aconnection) {
                 return mysql_ping((MYSQL *)connection)==0;                  Connection& connection=*static_cast<Connection*>(aconnection);
   
                   mysql_close(connection.handle);
                   connection.handle=0;
           }
           void commit(void *aconnection) {
                   //_asm int 3;
                   Connection& connection=*static_cast<Connection*>(aconnection);
   
                   if(!connection.autocommit)
                           exec(connection, "commit");
         }          }
           void rollback(void *aconnection) {
                   Connection& connection=*static_cast<Connection*>(aconnection);
   
         unsigned int quote(                  if(!connection.autocommit)
                 SQL_Driver_services&, void *connection,                          exec(connection, "rollback");
                 char *to, const char *from, unsigned int length) {          }
   
           bool ping(void *aconnection) {
                   Connection& connection=*static_cast<Connection*>(aconnection);
   
                   return mysql_ping(connection.handle)==0;
           }
   
           const char* quote(void *aconnection, const char *from, unsigned int length) {
                   Connection& connection=*static_cast<Connection*>(aconnection);
                 /*                  /*
                         3.23.22b                          3.23.22b
                         You must allocate the to buffer to be at least length*2+1 bytes long.                           You must allocate the to buffer to be at least length*2+1 bytes long. 
                         (In the worse case, each character may need to be encoded as using two bytes,                           (In the worse case, each character may need to be encoded as using two bytes, 
                         and you need room for the terminating null byte.)                          and you need room for the terminating null byte.)
                 */                  */
                 if(to) // store mode                  char *result=(char*)connection.services->malloc_atomic(length*2+1);
                         return mysql_escape_string(to, from, length);                  mysql_escape_string(result, from, length);
                 else // estimate mode                  return result;
                         return length*2+1;          }
         }          void query(void *aconnection, 
         void query(                  const char *astatement, 
                 SQL_Driver_services& services, void *connection,                   size_t placeholders_count, Placeholder* placeholders, 
                 const char *astatement, unsigned long offset, unsigned long limit,                  unsigned long offset, unsigned long limit,
                 SQL_Driver_query_event_handlers& handlers) {                  SQL_Driver_query_event_handlers& handlers) {
                   Connection& connection=*static_cast<Connection*>(aconnection);
                 MYSQL *mysql=(MYSQL *)connection;                  SQL_Driver_services& services=*connection.services;
                   const char* cstrClientCharset=connection.cstrClientCharset;
                 MYSQL_RES *res=NULL;                  MYSQL_RES *res=NULL;
   
                   if(placeholders_count>0)
                           services._throw("bind variables not supported (yet)");
   
                   // transcode from $request:charset to connect-string?client_charset
                   if(cstrClientCharset) {
                           size_t transcoded_statement_size;
                           services.transcode(astatement, strlen(astatement),
                                   astatement, transcoded_statement_size,
                                   services.request_charset(),
                                   cstrClientCharset);
                   }
   
                 const char *statement;                  const char *statement;
                 if(offset || limit) {                  if(offset || limit) {
                         size_t statement_size=strlen(astatement);                          size_t statement_size=strlen(astatement);
                         char *statement_limited=(char *)services.malloc(                          char *statement_limited=(char *)services.malloc_atomic(
                                 statement_size+MAX_NUMBER*2+8/* limit #,#*/+1);                                  statement_size+MAX_NUMBER*2+8/* limit #,#*/+1);
                         char *cur=statement_limited;                          char *cur=statement_limited;
                         memcpy(cur, astatement, statement_size); cur+=statement_size;                          memcpy(cur, astatement, statement_size); cur+=statement_size;
Line 189  public: Line 270  public:
                 } else                  } else
                         statement=astatement;                          statement=astatement;
   
                 if(mysql_query(mysql, statement))                   if(mysql_query(connection.handle, statement)) 
                         services._throw(mysql_error(mysql));                          services._throw(mysql_error(connection.handle));
                 if(!(res=mysql_store_result(mysql)) && mysql_field_count(mysql))                   if(!(res=mysql_store_result(connection.handle)) && mysql_field_count(connection.handle)) 
                         services._throw(mysql_error(mysql));                          services._throw(mysql_error(connection.handle));
                 if(!res) // empty result: insert|delete|update|...                  if(!res) // empty result: insert|delete|update|...
                         return;                          return;
                                   
                 int column_count=mysql_num_fields(res);                  int column_count=mysql_num_fields(res);
                 if(!column_count) // old client                  if(!column_count) // old client
                         column_count=mysql_field_count(mysql);                          column_count=mysql_field_count(connection.handle);
   
                 if(!column_count) {                  if(!column_count) {
                         mysql_free_result(res);                          mysql_free_result(res);
                         services._throw("result contains no columns");                          services._throw("result contains no columns");
                 }                  }
                   
                   bool failed=false;
                   SQL_Error sql_error;
   #define CHECK(afailed) \
                   if(afailed) { \
                           failed=true; \
                           goto cleanup; \
                   }
   
                 for(int i=0; i<column_count; i++){                  for(int i=0; i<column_count; i++){
                         MYSQL_FIELD *field=mysql_fetch_field(res);                          if(MYSQL_FIELD *field=mysql_fetch_field(res)) {
                         size_t size=strlen(field->name);                                  size_t length=strlen(field->name);
                         void *ptr=services.malloc(size);                                  char* strm=(char*)services.malloc_atomic(length+1);
                         memcpy(ptr, field->name, size);                                  memcpy(strm, field->name, length+1);
                         handlers.add_column(ptr, size);                                  const char* str=strm;
   
                                   // transcode to $request:charset from connect-string?client_charset
                                   if(cstrClientCharset) {
                                           services.transcode(str, length,
                                                   str, length,
                                                   cstrClientCharset,
                                                   services.request_charset());
                                   }
                                   
                                   CHECK(handlers.add_column(sql_error, str, length));
                           } else {
                               // seen some broken client, 
                               // which reported "44" for column count of response to "select 2+2"
                               column_count=i;
                               break;
                           }
                 }                  }
   
                 handlers.before_rows();                  CHECK(handlers.before_rows(sql_error));
                                   
                 while(MYSQL_ROW mysql_row=mysql_fetch_row(res)) {                  while(MYSQL_ROW mysql_row=mysql_fetch_row(res)) {
                         handlers.add_row();                          CHECK(handlers.add_row(sql_error));
                         unsigned long *lengths=mysql_fetch_lengths(res);                          unsigned long *lengths=mysql_fetch_lengths(res);
                         for(int i=0; i<column_count; i++){                          for(int i=0; i<column_count; i++){
                                 size_t size=(size_t)lengths[i];                                  size_t length=(size_t)lengths[i];
                                 void *ptr;                                  const char* str;
                                 if(size) {                                  if(length) {
                                         ptr=services.malloc(size);                                          char *strm=(char*)services.malloc_atomic(length+1);
                                         memcpy(ptr, mysql_row[i], size);                                          memcpy(strm, mysql_row[i], length);
                                           strm[length]=0;
                                           str=strm;
   
                                           // transcode to $request:charset from connect-string?client_charset
                                           if(cstrClientCharset)
                                                   services.transcode(str, length,
                                                           str, length,
                                                           cstrClientCharset,
                                                           services.request_charset());
                                 } else                                  } else
                                         ptr=0;                                          str=0;
                                 handlers.add_row_cell(ptr, size);                                  CHECK(handlers.add_row_cell(sql_error, str, length));
                         }                          }
                 }                  }
   cleanup:
                 mysql_free_result(res);                  mysql_free_result(res);
                   if(failed)
                           services._throw(sql_error);
         }          }
   
 private: // mysql client library funcs  private: // mysql client library funcs
   
         typedef MYSQL* (STDCALL *t_mysql_init)(MYSQL *);        t_mysql_init mysql_init;          typedef MYSQL* (STDCALL *t_mysql_init)(MYSQL *);        t_mysql_init mysql_init;
                   
           typedef void (STDCALL *t_mysql_server_end)();   t_mysql_server_end mysql_server_end;
   
         typedef int (STDCALL *t_mysql_options)(MYSQL *mysql, enum mysql_option option, const char *arg); t_mysql_options mysql_options;          typedef int (STDCALL *t_mysql_options)(MYSQL *mysql, enum mysql_option option, const char *arg); t_mysql_options mysql_options;
                   
         typedef MYSQL_RES* (STDCALL *t_mysql_store_result)(MYSQL *); t_mysql_store_result mysql_store_result;          typedef MYSQL_RES* (STDCALL *t_mysql_store_result)(MYSQL *); t_mysql_store_result mysql_store_result;
Line 278  private: // mysql client library funcs Line 396  private: // mysql client library funcs
 private: // mysql client library funcs linking  private: // mysql client library funcs linking
   
         const char *dlink(const char *dlopen_file_spec) {          const char *dlink(const char *dlopen_file_spec) {
                   if(lt_dlinit())
                           return lt_dlerror();
         lt_dlhandle handle=lt_dlopen(dlopen_file_spec);          lt_dlhandle handle=lt_dlopen(dlopen_file_spec);
         if (!handle)          if (!handle) {
               if(const char* result=lt_dlerror())
                   return result;
   
                         return "can not open the dynamic link module";                          return "can not open the dynamic link module";
                   }
   
                   #define GLINK(name) \
                           name=(t_##name)lt_dlsym(handle, #name);
   
                 #define DSLINK(name, action) \                  #define DSLINK(name, action) \
                         name=(t_##name)lt_dlsym(handle, #name); \                          GLINK(name) \
                                 if(!name) \                                  if(!name) \
                                         action;                                          action;
   
Line 291  private: // mysql client library funcs l Line 418  private: // mysql client library funcs l
                 #define SLINK(name) DSLINK(name, name=subst_##name)                  #define SLINK(name) DSLINK(name, name=subst_##name)
                                   
                 DLINK(mysql_init);                  DLINK(mysql_init);
                   GLINK(mysql_server_end);
                 DLINK(mysql_options);                  DLINK(mysql_options);
                 DLINK(mysql_store_result);                  DLINK(mysql_store_result);
                 DLINK(mysql_query);                  DLINK(mysql_query);
Line 311  private: // mysql client library funcs l Line 439  private: // mysql client library funcs l
 };  };
   
 extern "C" SQL_Driver *SQL_DRIVER_CREATE() {  extern "C" SQL_Driver *SQL_DRIVER_CREATE() {
         return new MySQL_Driver();          static MySQL_Driver Driver;
           return &Driver;
 }  }

Removed from v.1.5  
changed lines
  Added in v.1.30


E-mail: