diff options
Diffstat (limited to 'ext/mysql/libmysql/libmysql.c')
| -rw-r--r-- | ext/mysql/libmysql/libmysql.c | 2361 | 
1 files changed, 2361 insertions, 0 deletions
diff --git a/ext/mysql/libmysql/libmysql.c b/ext/mysql/libmysql/libmysql.c new file mode 100644 index 0000000000..6be7bd7b7b --- /dev/null +++ b/ext/mysql/libmysql/libmysql.c @@ -0,0 +1,2361 @@ +/* Copyright Abandoned 1996 TCX DataKonsult AB & Monty Program KB & Detron HB +   This file is public domain and comes with NO WARRANTY of any kind */ + +#define DONT_USE_RAID +#if defined(__WIN32__) || defined(WIN32) +#include <winsock.h> +#include <odbcinst.h> +#endif +#include <global.h> +#include <my_sys.h> +#include <mysys_err.h> +#include <m_string.h> +#include <m_ctype.h> +#include "mysql.h" +#include "mysql_version.h" +#include "mysqld_error.h" +#include "errmsg.h" +#include <violite.h> +#include <sys/stat.h> +#include <signal.h> +#ifdef	 HAVE_PWD_H +#include <pwd.h> +#endif +#if !defined(MSDOS) && !defined(__WIN32__) +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#ifdef HAVE_SELECT_H +#  include <select.h> +#endif +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif +#endif +#ifdef HAVE_SYS_UN_H +#  include <sys/un.h> +#endif +#if defined(THREAD) && !defined(__WIN32__) +#include <my_pthread.h>				/* because of signal()	*/ +#endif +#ifndef INADDR_NONE +#define INADDR_NONE	-1 +#endif + +static my_bool	mysql_client_init=0; +static MYSQL	*current_mysql; +uint		mysql_port=0; +my_string	mysql_unix_port=0; + +#define CLIENT_CAPABILITIES	(CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_LOCAL_FILES) + +#if defined(MSDOS) || defined(__WIN32__) +#define ERRNO WSAGetLastError() +#define perror(A) +#else +#include <sys/errno.h> +#define ERRNO errno +#define SOCKET_ERROR -1 +#define closesocket(A) close(A) +#endif + +static void mysql_once_init(void); +static MYSQL_DATA *read_rows (MYSQL *mysql,MYSQL_FIELD *fields, +			      uint field_count); +static int read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row, +			ulong *lengths); +static void end_server(MYSQL *mysql); +static void remember_connection(MYSQL *mysql); +static void read_user_name(char *name); +static void append_wild(char *to,char *end,const char *wild); +static my_bool mysql_reconnect(MYSQL *mysql); +static int send_file_to_server(MYSQL *mysql,const char *filename); +static sig_handler pipe_sig_handler(int sig); + +/* +  Let the user specify that we don't want SIGPIPE;  This doesn't however work +  with threaded applications as we can have multiple read in progress. +*/ + +#if !defined(__WIN32__) && defined(SIGPIPE) && !defined(THREAD) +#define init_sigpipe_variables  sig_return old_signal_handler; +#define set_sigpipe(mysql)     if ((mysql)->client_flag & CLIENT_IGNORE_SIGPIPE) old_signal_handler=signal(SIGPIPE,pipe_sig_handler) +#define reset_sigpipe(mysql) if ((mysql)->client_flag & CLIENT_IGNORE_SIGPIPE) signal(SIGPIPE,old_signal_handler); +#else +#define init_sigpipe_variables +#define set_sigpipe(mysql) +#define reset_sigpipe(mysql) +#endif + +/**************************************************************************** +* A modified version of connect().  connect2() allows you to specify +* a timeout value, in seconds, that we should wait until we +* derermine we can't connect to a particular host.  If timeout is 0, +* connect2() will behave exactly like connect(). +* +* Base version coded by Steve Bernacki, Jr. <steve@navinet.net> +*****************************************************************************/ + +static int connect2(File s, const struct sockaddr *name, uint namelen, uint to) +{ +#if defined(__WIN32__) +  return connect(s, (struct sockaddr*) name, namelen); +#else +  int flags, res, s_err; +  size_socket s_err_size = sizeof(uint); +  fd_set sfds; +  struct timeval tv; + +  /* If they passed us a timeout of zero, we should behave +   * exactly like the normal connect() call does. +   */ + +  if (to == 0) +    return connect(s, (struct sockaddr*) name, namelen); + +  flags = fcntl(s, F_GETFL, 0);		  /* Set socket to not block */ +#ifdef O_NONBLOCK +  fcntl(s, F_SETFL, flags | O_NONBLOCK);  /* and save the flags..  */ +#endif + +  res = connect(s, (struct sockaddr*) name, namelen); +  s_err = errno;			/* Save the error... */ +  fcntl(s, F_SETFL, flags); +  if ((res != 0) && (s_err != EINPROGRESS)) +  { +    errno = s_err;			/* Restore it */ +    return(-1); +  } +  if (res == 0)				/* Connected quickly! */ +    return(0); + +  /* Otherwise, our connection is "in progress."  We can use +   * the select() call to wait up to a specified period of time +   * for the connection to suceed.  If select() returns 0 +   * (after waiting howevermany seconds), our socket never became +   * writable (host is probably unreachable.)  Otherwise, if +   * select() returns 1, then one of two conditions exist: +   * +   * 1. An error occured.  We use getsockopt() to check for this. +   * 2. The connection was set up sucessfully: getsockopt() will +   * return 0 as an error. +   * +   * Thanks goes to Andrew Gierth <andrew@erlenstar.demon.co.uk> +   * who posted this method of timing out a connect() in +   * comp.unix.programmer on August 15th, 1997. +   */ + +  FD_ZERO(&sfds); +  FD_SET(s, &sfds); +  tv.tv_sec = (long) to; +  tv.tv_usec = 0; +  res = select(s+1, NULL, &sfds, NULL, &tv); +  if (res <= 0)					/* Never became writable */ +    return(-1); + +  /* select() returned something more interesting than zero, let's +   * see if we have any errors.  If the next two statements pass, +   * we've got an open socket! +   */ + +  s_err=0; +  if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char*) &s_err, &s_err_size) != 0) +    return(-1); + +  if (s_err) +  {						/* getsockopt() could suceed */ +    errno = s_err; +    return(-1);					/* but return an error... */ +  } +  return(0);					/* It's all good! */ +#endif +} + +/* +** Create a named pipe connection +*/ + +#ifdef __WIN32__ + +HANDLE create_named_pipe(NET *net, uint connect_timeout, char **arg_host, +			 char **arg_unix_socket) +{ +  HANDLE hPipe=INVALID_HANDLE_VALUE; +  char szPipeName [ 257 ]; +  DWORD dwMode; +  int i; +  my_bool testing_named_pipes=0; +  char *host= *arg_host, *unix_socket= *arg_unix_socket; + +  if ( ! unix_socket || (unix_socket)[0] == 0x00) +    unix_socket = mysql_unix_port; +  if (!host || !strcmp(host,LOCAL_HOST)) +    host=LOCAL_HOST_NAMEDPIPE; + +  sprintf( szPipeName, "\\\\%s\\pipe\\%s", host, unix_socket); +  DBUG_PRINT("info",("Server name: '%s'.  Named Pipe: %s", +		     host, unix_socket)); + +  for (i=0 ; i < 100 ; i++)			/* Don't retry forever */ +  { +    if ((hPipe = CreateFile(szPipeName, +			    GENERIC_READ | GENERIC_WRITE, +			    0, +			    NULL, +			    OPEN_EXISTING, +			    0, +			    NULL )) != INVALID_HANDLE_VALUE) +      break; +    if (GetLastError() != ERROR_PIPE_BUSY) +    { +      net->last_errno=CR_NAMEDPIPEOPEN_ERROR; +      sprintf(net->last_error,ER(net->last_errno),host, unix_socket, +	      (ulong) GetLastError()); +      return INVALID_HANDLE_VALUE; +    } +    /* wait for for an other instance */ +    if (! WaitNamedPipe(szPipeName, connect_timeout*1000) ) +    { +      net->last_errno=CR_NAMEDPIPEWAIT_ERROR; +      sprintf(net->last_error,ER(net->last_errno),host, unix_socket, +	      (ulong) GetLastError()); +      return INVALID_HANDLE_VALUE; +    } +  } +  if (hPipe == INVALID_HANDLE_VALUE) +  { +    net->last_errno=CR_NAMEDPIPEOPEN_ERROR; +    sprintf(net->last_error,ER(net->last_errno),host, unix_socket, +	    (ulong) GetLastError()); +    return INVALID_HANDLE_VALUE; +  } +  dwMode = PIPE_READMODE_BYTE | PIPE_WAIT; +  if ( !SetNamedPipeHandleState(hPipe, &dwMode, NULL, NULL) ) +  { +    CloseHandle( hPipe ); +    net->last_errno=CR_NAMEDPIPESETSTATE_ERROR; +    sprintf(net->last_error,ER(net->last_errno),host, unix_socket, +	    (ulong) GetLastError()); +    return INVALID_HANDLE_VALUE; +  } +  *arg_host=host ; *arg_unix_socket=unix_socket;	/* connect arg */ +  return (hPipe); +} +#endif + + +/***************************************************************************** +** read a packet from server. Give error message if socket was down +** or packet is an error message +*****************************************************************************/ + +static uint +net_safe_read(MYSQL *mysql) +{ +  NET *net= &mysql->net; +  uint len=0; +  init_sigpipe_variables + +  /* Don't give sigpipe errors if the client doesn't want them */ +  set_sigpipe(mysql); +  if (net->vio != 0) +    len=my_net_read(net); +  reset_sigpipe(mysql); + +  if (len == packet_error || len == 0) +  { +    DBUG_PRINT("error",("Wrong connection or packet. fd: %s  len: %d", +			vio_description(net->vio),len)); +    end_server(mysql); +    net->last_errno=CR_SERVER_LOST; +    strmov(net->last_error,ER(net->last_errno)); +    return(packet_error); +  } +  if (net->read_pos[0] == 255) +  { +    if (len > 3) +    { +      char *pos=(char*) net->read_pos+1; +      if (mysql->protocol_version > 9) +      {						/* New client protocol */ +	net->last_errno=uint2korr(pos); +	pos+=2; +	len-=2; +      } +      else +      { +	net->last_errno=CR_UNKNOWN_ERROR; +	len--; +      } +      (void) strmake(net->last_error,(char*) pos, +		     min(len,sizeof(net->last_error)-1)); +    } +    else +    { +      net->last_errno=CR_UNKNOWN_ERROR; +      (void) strmov(net->last_error,ER(net->last_errno)); +    } +    DBUG_PRINT("error",("Got error: %d (%s)", net->last_errno, +			net->last_error)); +    return(packet_error); +  } +  return len; +} + + +/* Get the length of next field. Change parameter to point at fieldstart */ +static ulong +net_field_length(uchar **packet) +{ +  reg1 uchar *pos= *packet; +  if (*pos < 251) +  { +    (*packet)++; +    return (ulong) *pos; +  } +  if (*pos == 251) +  { +    (*packet)++; +    return NULL_LENGTH; +  } +  if (*pos == 252) +  { +    (*packet)+=3; +    return (ulong) uint2korr(pos+1); +  } +  if (*pos == 253) +  { +    (*packet)+=4; +    return (ulong) uint3korr(pos+1); +  } +  (*packet)+=9;					/* Must be 254 when here */ +  return (ulong) uint4korr(pos+1); +} + +/* Same as above, but returns ulonglong values */ + +static my_ulonglong +net_field_length_ll(uchar **packet) +{ +  reg1 uchar *pos= *packet; +  if (*pos < 251) +  { +    (*packet)++; +    return (my_ulonglong) *pos; +  } +  if (*pos == 251) +  { +    (*packet)++; +    return (my_ulonglong) NULL_LENGTH; +  } +  if (*pos == 252) +  { +    (*packet)+=3; +    return (my_ulonglong) uint2korr(pos+1); +  } +  if (*pos == 253) +  { +    (*packet)+=4; +    return (my_ulonglong) uint3korr(pos+1); +  } +  (*packet)+=9;					/* Must be 254 when here */ +#ifdef NO_CLIENT_LONGLONG +  return (my_ulonglong) uint4korr(pos+1); +#else +  return (my_ulonglong) uint8korr(pos+1); +#endif +} + + +static void free_rows(MYSQL_DATA *cur) +{ +  if (cur) +  { +    free_root(&cur->alloc); +    my_free((gptr) cur,MYF(0)); +  } +} + + +static int +simple_command(MYSQL *mysql,enum enum_server_command command, const char *arg, +	       uint length, my_bool skipp_check) +{ +  NET *net= &mysql->net; +  int result= -1; +  init_sigpipe_variables + +  /* Don't give sigpipe errors if the client doesn't want them */ +  set_sigpipe(mysql); +  if (mysql->net.vio == 0) +  {						/* Do reconnect if possible */ +    if (mysql_reconnect(mysql)) +    { +      net->last_errno=CR_SERVER_GONE_ERROR; +      strmov(net->last_error,ER(net->last_errno)); +      goto end; +    } +  } +  if (mysql->status != MYSQL_STATUS_READY) +  { +    strmov(net->last_error,ER(mysql->net.last_errno=CR_COMMANDS_OUT_OF_SYNC)); +    goto end; +  } + +  mysql->net.last_error[0]=0; +  mysql->net.last_errno=0; +  mysql->info=0; +  mysql->affected_rows= ~(my_ulonglong) 0; +  remember_connection(mysql); +  net_clear(net);			/* Clear receive buffer */ +  if (!arg) +    arg=""; + +  if (net_write_command(net,(uchar) command,arg, +			length ? length :strlen(arg))) +  { +    DBUG_PRINT("error",("Can't send command to server. Error: %d",errno)); +    end_server(mysql); +    if (mysql_reconnect(mysql) || +	net_write_command(net,(uchar) command,arg, +			  length ? length :strlen(arg))) +    { +      net->last_errno=CR_SERVER_GONE_ERROR; +      strmov(net->last_error,ER(net->last_errno)); +      reset_sigpipe(mysql); +      goto end; +    } +  } +  result=0; +  if (!skipp_check) +    result= ((mysql->packet_length=net_safe_read(mysql)) == packet_error ? +	     -1 : 0); + end: +  reset_sigpipe(mysql); +  return result; +} + + +static void free_old_query(MYSQL *mysql) +{ +  DBUG_ENTER("free_old_query"); +  if (mysql->fields) +    free_root(&mysql->field_alloc); +  init_alloc_root(&mysql->field_alloc,8192);	/* Assume rowlength < 8192 */ +  mysql->fields=0; +  mysql->field_count=0;				/* For API */ +  DBUG_VOID_RETURN; +} + +#ifdef HAVE_GETPWUID +struct passwd *getpwuid(uid_t); +char* getlogin(void); +#endif + +#if !defined(MSDOS) && ! defined(VMS) && !defined(__WIN32__) +static void read_user_name(char *name) +{ +  DBUG_ENTER("read_user_name"); +  if (geteuid() == 0) +    (void) strmov(name,"root");		/* allow use of surun */ +  else +  { +#ifdef HAVE_GETPWUID +    struct passwd *skr; +    const char *str; +/*#ifdef __cplusplus +    extern "C" struct passwd *getpwuid(uid_t); +    extern "C" { char* getlogin(void); } +#else +    char * getlogin(); +    struct passwd *getpwuid(uid_t); +#endif +*/ +    if ((str=getlogin()) == NULL) +    { +      if ((skr=getpwuid(geteuid())) != NULL) +	str=skr->pw_name; +      else if (!(str=getenv("USER")) && !(str=getenv("LOGNAME")) && +	       !(str=getenv("LOGIN"))) +	str="UNKNOWN_USER"; +    } +    (void) strmake(name,str,USERNAME_LENGTH); +#elif HAVE_CUSERID +    (void) cuserid(name); +#else +    strmov(name,"UNKNOWN_USER"); +#endif +  } +  DBUG_VOID_RETURN; +} + +#else /* If MSDOS || VMS */ + +static void read_user_name(char *name) +{ +  char *str=getenv("USER"); +  strmov(name,str ? str : "ODBC");	 /* ODBC will send user variable */ +} + +#endif + +#ifdef __WIN32__ +static my_bool is_NT(void) +{ +  char *os=getenv("OS"); +  return (os && !strcmp(os, "Windows_NT")) ? 1 : 0; +} +#endif + +/* +** Expand wildcard to a sql string +*/ + +static void +append_wild(char *to, char *end, const char *wild) +{ +  end-=5;					/* Some extra */ +  if (wild && wild[0]) +  { +    to=strmov(to," like '"); +    while (*wild && to < end) +    { +      if (*wild == '\\' || *wild == '\'') +	*to++='\\'; +      *to++= *wild++; +    } +    if (*wild)					/* Too small buffer */ +      *to++='%';				/* Nicer this way */ +    to[0]='\''; +    to[1]=0; +  } +} + + + +/************************************************************************** +** Init debugging if MYSQL_DEBUG environment variable is found +**************************************************************************/ + +void STDCALL +mysql_debug(const char *debug) +{ +#ifndef DBUG_OFF +  char	*env; +  if (_db_on_) +    return;					/* Already using debugging */ +  if (debug) +  { +    DEBUGGER_ON; +    DBUG_PUSH(debug); +  } +  else if ((env = getenv("MYSQL_DEBUG"))) +  { +    DEBUGGER_ON; +    DBUG_PUSH(env); +#if !defined(_WINVER) && !defined(WINVER) +    puts("\n-------------------------------------------------------"); +    puts("MYSQL_DEBUG found. libmysql started with the following:"); +    puts(env); +    puts("-------------------------------------------------------\n"); +#else +    { +      char buff[80]; +      strmov(strmov(buff,"libmysql: "),env); +      MessageBox((HWND) 0,"Debugging variable MYSQL_DEBUG used",buff,MB_OK); +    } +#endif +  } +#endif +} + + +/************************************************************************** +** Store the server socket currently in use +** Used by pipe_handler if error on socket interrupt +**************************************************************************/ + +static void +remember_connection(MYSQL *mysql) +{ +  current_mysql = mysql; +} + +/************************************************************************** +** Close the server connection if we get a SIGPIPE +   ARGSUSED +**************************************************************************/ + +static sig_handler +pipe_sig_handler(int sig __attribute__((unused))) +{ +  DBUG_PRINT("info",("Hit by signal %d",sig)); +#ifdef DONT_REMEMBER_SIGNAL +  (void) signal(SIGPIPE,pipe_sig_handler); +#endif +} + + +/************************************************************************** +** Shut down connection +**************************************************************************/ + +static void +end_server(MYSQL *mysql) +{ +  DBUG_ENTER("end_server"); +  if (mysql->net.vio != 0) +  { +    init_sigpipe_variables +    DBUG_PRINT("info",("Net: %s", vio_description(mysql->net.vio))); +    set_sigpipe(mysql); +    vio_delete(mysql->net.vio); +    reset_sigpipe(mysql); +    mysql->net.vio= 0;          /* Marker */ +  } +  net_end(&mysql->net); +  free_old_query(mysql); +  DBUG_VOID_RETURN; +} + + +void STDCALL +mysql_free_result(MYSQL_RES *result) +{ +  DBUG_ENTER("mysql_free_result"); +  DBUG_PRINT("enter",("mysql_res: %lx",result)); +  if (result) +  { +    if (result->handle && result->handle->status == MYSQL_STATUS_USE_RESULT) +    { +      DBUG_PRINT("warning",("Not all rows in set were read; Ignoring rows")); +      for (;;) +      { +	uint pkt_len; +	if ((pkt_len=(uint) net_safe_read(result->handle)) == packet_error) +	  break; +	if (pkt_len == 1 && result->handle->net.read_pos[0] == 254) +	  break;				/* End of data */ +      } +      result->handle->status=MYSQL_STATUS_READY; +    } +    free_rows(result->data); +    if (result->fields) +      free_root(&result->field_alloc); +    if (result->row) +      my_free((gptr) result->row,MYF(0)); +    my_free((gptr) result,MYF(0)); +  } +  DBUG_VOID_RETURN; +} + + +/**************************************************************************** +** Get options from my.cnf +****************************************************************************/ + +static const char *default_options[]= +{"port","socket","compress","password","pipe", "timeout", "user", + "init-command", "host", "database", "debug", "return-found-rows", +#ifdef HAVE_OPENSSL + "ssl_key" ,"ssl_cert" ,"ssl_ca" ,"ssl_capath", +#endif /* HAVE_OPENSSL */ + NullS +}; +static TYPELIB option_types={array_elements(default_options)-1,(char*) "options",(char **) default_options}; + +static void mysql_read_default_options(struct st_mysql_options *options, +				       const char *filename,const char *group) +{ +  int argc; +  char *argv_buff[1],**argv; +  const char *groups[3]; +  DBUG_ENTER("mysql_read_default_options"); +  DBUG_PRINT("enter",("file: %s  group: %s",filename,group ? group :"NULL")); + +  argc=1; argv=argv_buff; argv_buff[0]= (char*) "client"; +  groups[0]= (char*) "client"; groups[1]= (char*) group; groups[2]=0; + +  load_defaults(filename, groups, &argc, &argv); +  if (argc != 1)				/* If some default option */ +  { +    char **option=argv; +    while (*++option) +    { +      /* DBUG_PRINT("info",("option: %s",option[0])); */ +      if (option[0][0] == '-' && option[0][1] == '-') +      { +	char *end=strcend(*option,'='); +	char *opt_arg=0; +	if (*end) +	{ +	  opt_arg=end+1; +	  *end=0;				/* Remove '=' */ +	} +	switch (find_type(*option+2,&option_types,2)) { +	case 1:				/* port */ +	  if (opt_arg) +	    options->port=atoi(opt_arg); +	  break; +	case 2:				/* socket */ +	  if (opt_arg) +	  { +	    my_free(options->unix_socket,MYF(MY_ALLOW_ZERO_PTR)); +	    options->unix_socket=my_strdup(opt_arg,MYF(MY_WME)); +	  } +	  break; +	case 3:				/* compress */ +	  options->compress=1; +	  break; +	case 4:				/* password */ +	  if (opt_arg) +	  { +	    my_free(options->password,MYF(MY_ALLOW_ZERO_PTR)); +	    options->password=my_strdup(opt_arg,MYF(MY_WME)); +	  } +	  break; +	case 5:				/* pipe */ +	  options->named_pipe=1;	/* Force named pipe */ +	  break; +	case 6:				/* timeout */ +	  if (opt_arg) +	    options->connect_timeout=atoi(opt_arg); +	  break; +	case 7:				/* user */ +	  if (opt_arg) +	  { +	    my_free(options->user,MYF(MY_ALLOW_ZERO_PTR)); +	    options->user=my_strdup(opt_arg,MYF(MY_WME)); +	  } +	  break; +	case 8:				/* init-command */ +	  if (opt_arg) +	  { +	    my_free(options->init_command,MYF(MY_ALLOW_ZERO_PTR)); +	    options->init_command=my_strdup(opt_arg,MYF(MY_WME)); +	  } +	  break; +	case 9:				/* host */ +	  if (opt_arg) +	  { +	    my_free(options->host,MYF(MY_ALLOW_ZERO_PTR)); +	    options->host=my_strdup(opt_arg,MYF(MY_WME)); +	  } +	  break; +	case 10:			/* database */ +	  if (opt_arg) +	  { +	    my_free(options->db,MYF(MY_ALLOW_ZERO_PTR)); +	    options->db=my_strdup(opt_arg,MYF(MY_WME)); +	  } +	  break; +	case 11:			/* debug */ +	  mysql_debug(opt_arg ? opt_arg : "d:t:o,/tmp/client.trace"); +	  break; +	case 12:			/* return-found-rows */ +	  options->client_flag|=CLIENT_FOUND_ROWS; +	  break; +#ifdef HAVE_OPENSSL +	case 13:			/* ssl_key */ +	  my_free(options->ssl_key, MYF(MY_ALLOW_ZERO_PTR)); +          options->ssl_key = my_strdup(opt_arg, MYF(MY_WME)); +          break; +	case 14:			/* ssl_cert */ +	  my_free(options->ssl_cert, MYF(MY_ALLOW_ZERO_PTR)); +          options->ssl_cert = my_strdup(opt_arg, MYF(MY_WME)); +          break; +	case 15:			/* ssl_ca */ +	  my_free(options->ssl_ca, MYF(MY_ALLOW_ZERO_PTR)); +          options->ssl_ca = my_strdup(opt_arg, MYF(MY_WME)); +          break; +	case 16:			/* ssl_capath */ +	  my_free(options->ssl_capath, MYF(MY_ALLOW_ZERO_PTR)); +          options->ssl_capath = my_strdup(opt_arg, MYF(MY_WME)); +          break; +#endif /* HAVE_OPENSSL */ +	default: +	  DBUG_PRINT("warning",("unknown option: %s",option[0])); +	} +      } +    } +  } +  free_defaults(argv); +  DBUG_VOID_RETURN; +} + + +/*************************************************************************** +** Change field rows to field structs +***************************************************************************/ + +static MYSQL_FIELD * +unpack_fields(MYSQL_DATA *data,MEM_ROOT *alloc,uint fields, +	      my_bool default_value, my_bool long_flag_protocol) +{ +  MYSQL_ROWS	*row; +  MYSQL_FIELD	*field,*result; +  DBUG_ENTER("unpack_fields"); + +  field=result=(MYSQL_FIELD*) alloc_root(alloc,sizeof(MYSQL_FIELD)*fields); +  if (!result) +    DBUG_RETURN(0); + +  for (row=data->data; row ; row = row->next,field++) +  { +    field->table=  strdup_root(alloc,(char*) row->data[0]); +    field->name=   strdup_root(alloc,(char*) row->data[1]); +    field->length= (uint) uint3korr(row->data[2]); +    field->type=   (enum enum_field_types) (uchar) row->data[3][0]; +    if (long_flag_protocol) +    { +      field->flags=   uint2korr(row->data[4]); +      field->decimals=(uint) (uchar) row->data[4][2]; +    } +    else +    { +      field->flags=   (uint) (uchar) row->data[4][0]; +      field->decimals=(uint) (uchar) row->data[4][1]; +    } +    if (default_value && row->data[5]) +      field->def=strdup_root(alloc,(char*) row->data[5]); +    else +      field->def=0; +    field->max_length= 0; +  } +  free_rows(data);				/* Free old data */ +  DBUG_RETURN(result); +} + + +/* Read all rows (fields or data) from server */ + +static MYSQL_DATA *read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, +			     uint fields) +{ +  uint	field,pkt_len; +  ulong len; +  uchar *cp; +  char	*to; +  MYSQL_DATA *result; +  MYSQL_ROWS **prev_ptr,*cur; +  NET *net = &mysql->net; +  DBUG_ENTER("read_rows"); + +  if ((pkt_len=(uint) net_safe_read(mysql)) == packet_error) +    DBUG_RETURN(0); +  if (!(result=(MYSQL_DATA*) my_malloc(sizeof(MYSQL_DATA), +				       MYF(MY_WME | MY_ZEROFILL)))) +  { +    net->last_errno=CR_OUT_OF_MEMORY; +    strmov(net->last_error,ER(net->last_errno)); +    DBUG_RETURN(0); +  } +  init_alloc_root(&result->alloc,8192);		/* Assume rowlength < 8192 */ +  result->alloc.min_malloc=sizeof(MYSQL_ROWS); +  prev_ptr= &result->data; +  result->rows=0; +  result->fields=fields; + +  while (*(cp=net->read_pos) != 254 || pkt_len != 1) +  { +    result->rows++; +    if (!(cur= (MYSQL_ROWS*) alloc_root(&result->alloc, +					    sizeof(MYSQL_ROWS))) || +	!(cur->data= ((MYSQL_ROW) +		      alloc_root(&result->alloc, +				     (fields+1)*sizeof(char *)+pkt_len)))) +    { +      free_rows(result); +      net->last_errno=CR_OUT_OF_MEMORY; +      strmov(net->last_error,ER(net->last_errno)); +      DBUG_RETURN(0); +    } +    *prev_ptr=cur; +    prev_ptr= &cur->next; +    to= (char*) (cur->data+fields+1); +    for (field=0 ; field < fields ; field++) +    { +      if ((len=(ulong) net_field_length(&cp)) == NULL_LENGTH) +      {						/* null field */ +	cur->data[field] = 0; +      } +      else +      { +	cur->data[field] = to; +	memcpy(to,(char*) cp,len); to[len]=0; +	to+=len+1; +	cp+=len; +	if (mysql_fields) +	{ +	  if (mysql_fields[field].max_length < len) +	    mysql_fields[field].max_length=len; +	} +      } +    } +    cur->data[field]=to;			/* End of last field */ +    if ((pkt_len=net_safe_read(mysql)) == packet_error) +    { +      free_rows(result); +      DBUG_RETURN(0); +    } +  } +  *prev_ptr=0;					/* last pointer is null */ +  DBUG_PRINT("exit",("Got %d rows",result->rows)); +  DBUG_RETURN(result); +} + + +/* +** Read one row. Uses packet buffer as storage for fields. +** When next packet is read, the previous field values are destroyed +*/ + + +static int +read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row, ulong *lengths) +{ +  uint field; +  ulong pkt_len,len; +  uchar *pos,*prev_pos; + +  if ((pkt_len=(uint) net_safe_read(mysql)) == packet_error) +    return -1; +  if (pkt_len == 1 && mysql->net.read_pos[0] == 254) +    return 1;				/* End of data */ +  prev_pos= 0;				/* allowed to write at packet[-1] */ +  pos=mysql->net.read_pos; +  for (field=0 ; field < fields ; field++) +  { +    if ((len=(ulong) net_field_length(&pos)) == NULL_LENGTH) +    {						/* null field */ +      row[field] = 0; +      *lengths++=0; +    } +    else +    { +      row[field] = (char*) pos; +      pos+=len; +      *lengths++=len; +    } +    if (prev_pos) +      *prev_pos=0;				/* Terminate prev field */ +    prev_pos=pos; +  } +  row[field]=(char*) prev_pos+1;		/* End of last field */ +  *prev_pos=0;					/* Terminate last field */ +  return 0; +} + +/**************************************************************************** +** Init MySQL structure or allocate one +****************************************************************************/ + +MYSQL * STDCALL +mysql_init(MYSQL *mysql) +{ +  mysql_once_init(); +  if (!mysql) +  { +    if (!(mysql=(MYSQL*) my_malloc(sizeof(*mysql),MYF(MY_WME | MY_ZEROFILL)))) +      return 0; +    mysql->free_me=1; +    mysql->net.vio = 0; +  } +  else +    bzero((char*) (mysql),sizeof(*(mysql))); +#ifdef __WIN32__ +  mysql->options.connect_timeout=20; +#endif +  return mysql; +} + + +static void mysql_once_init() +{ +  if (!mysql_client_init) +  { +    mysql_client_init=1; +    my_init();					/* Will init threads */ +    init_client_errs(); +    if (!mysql_port) +    { +      mysql_port = MYSQL_PORT; +#ifndef MSDOS +      { +	struct servent *serv_ptr; +	char	*env; +	if ((serv_ptr = getservbyname("mysql", "tcp"))) +	  mysql_port = (uint) ntohs((ushort) serv_ptr->s_port); +	if ((env = getenv("MYSQL_TCP_PORT"))) +	  mysql_port =(uint) atoi(env); +      } +#endif +    } +    if (!mysql_unix_port) +    { +      char *env; +#ifdef __WIN32__ +      mysql_unix_port = (char*) MYSQL_NAMEDPIPE; +#else +      mysql_unix_port = (char*) MYSQL_UNIX_ADDR; +#endif +      if ((env = getenv("MYSQL_UNIX_PORT"))) +	mysql_unix_port = env; +    } +    mysql_debug(NullS); +#if defined(SIGPIPE) +    (void) signal(SIGPIPE,SIG_IGN); +#endif +  } +#ifdef THREAD +  else +    my_thread_init();         /* Init if new thread */ +#endif +} + +#ifdef HAVE_OPENSSL +/************************************************************************** +** Fill in SSL part of MYSQL structure and set 'use_ssl' flag. +** NB! Errors are not reported until you do mysql_real_connect. +**************************************************************************/ + +int STDCALL +mysql_ssl_set(MYSQL *mysql, const char *key, const char *cert, +              const char *ca, const char *capath) +{ +  mysql->options.ssl_key = key==0 ? 0 : my_strdup(key,MYF(0)); +  mysql->options.ssl_cert = cert==0 ? 0 : my_strdup(cert,MYF(0)); +  mysql->options.ssl_ca = ca==0 ? 0 : my_strdup(ca,MYF(0)); +  mysql->options.ssl_capath = capath==0 ? 0 : my_strdup(capath,MYF(0)); +  mysql->options.use_ssl = true; +  mysql->connector_fd = new_VioSSLConnectorFd(key, cert, ca, capath); +  return 0; +} + +/************************************************************************** +**************************************************************************/ + +char * STDCALL +mysql_ssl_cipher(MYSQL *mysql) +{ +  return (char *)mysql->net.vio->cipher_description(); +} + + +/************************************************************************** +** Free strings in the SSL structure and clear 'use_ssl' flag. +** NB! Errors are not reported until you do mysql_real_connect. +**************************************************************************/ + +int STDCALL +mysql_ssl_clear(MYSQL *mysql) +{ +  my_free(mysql->options.ssl_key, MYF(MY_ALLOW_ZERO_PTR)); +  my_free(mysql->options.ssl_cert, MYF(MY_ALLOW_ZERO_PTR)); +  my_free(mysql->options.ssl_ca, MYF(MY_ALLOW_ZERO_PTR)); +  my_free(mysql->options.ssl_capath, MYF(MY_ALLOW_ZERO_PTR)); +  mysql->options.ssl_key = 0; +  mysql->options.ssl_cert = 0; +  mysql->options.ssl_ca = 0; +  mysql->options.ssl_capath = 0; +  mysql->options.use_ssl = false; +  delete reinterpret_cast<VioConnectorFd*>(mysql->connector_fd); +  mysql->connector_fd = 0; +  return 0; +} +#endif /* HAVE_OPENSSL */ + +/************************************************************************** +** Connect to sql server +** If host == 0 then use localhost +**************************************************************************/ + +MYSQL * STDCALL +mysql_connect(MYSQL *mysql,const char *host, +	      const char *user, const char *passwd) +{ +  MYSQL *res; +  mysql=mysql_init(mysql);			/* Make it thread safe */ +  { +    DBUG_ENTER("mysql_connect"); +    if (!(res=mysql_real_connect(mysql,host,user,passwd,NullS,0,NullS,0))) +    { +      if (mysql->free_me) +	my_free((gptr) mysql,MYF(0)); +    } +    DBUG_RETURN(res); +  } +} + + +/* +** Note that the mysql argument must be initialized with mysql_init() +** before calling mysql_real_connect ! +*/ + +MYSQL * STDCALL +mysql_real_connect(MYSQL *mysql,const char *host, const char *user, +		   const char *passwd, const char *db, +		   uint port, const char *unix_socket,uint client_flag) +{ +  char		buff[100],*end,*host_info; +  int		sock; +  ulong		ip_addr; +  struct	sockaddr_in sock_addr; +  uint		pkt_length; +  NET		*net; +#ifdef __WIN32__ +  HANDLE	hPipe=INVALID_HANDLE_VALUE; +#endif +#ifdef HAVE_SYS_UN_H +  struct	sockaddr_un UNIXaddr; +#endif +  init_sigpipe_variables +  DBUG_ENTER("mysql_real_connect"); + +  DBUG_PRINT("enter",("host: %s  db: %s  user: %s", +		      host ? host : "(Null)", +		      db ? db : "(Null)", +		      user ? user : "(Null)")); + +  /* Don't give sigpipe errors if the client doesn't want them */ +  set_sigpipe(mysql); +  /* use default options */ +  if (mysql->options.my_cnf_file || mysql->options.my_cnf_group) +  { +    mysql_read_default_options(&mysql->options, +			       (mysql->options.my_cnf_file ? +				mysql->options.my_cnf_file : "my"), +			       mysql->options.my_cnf_group); +    my_free(mysql->options.my_cnf_file,MYF(MY_ALLOW_ZERO_PTR)); +    my_free(mysql->options.my_cnf_group,MYF(MY_ALLOW_ZERO_PTR)); +    mysql->options.my_cnf_file=mysql->options.my_cnf_group=0; +  } + +  /* Some empty-string-tests are done because of ODBC */ +  if (!host || !host[0]) +    host=mysql->options.host; +  if (!user || !user[0]) +    user=mysql->options.user; +  if (!passwd) +  { +    passwd=mysql->options.password; +#ifndef DONT_USE_MYSQL_PWD +    if (!passwd) +      passwd=getenv("MYSQL_PWD");  /* get it from environment (haneke) */ +#endif +  } +  if (!db || !db[0]) +    db=mysql->options.db; +  if (!port) +    port=mysql->options.port; +  if (!unix_socket) +    unix_socket=mysql->options.unix_socket; + +  remember_connection(mysql); +  mysql->reconnect=1;			/* Reconnect as default */ +  net= &mysql->net; +  net->vio = 0;				/* If something goes wrong */ + +  /* +  ** Grab a socket and connect it to the server +  */ + +#if defined(HAVE_SYS_UN_H) +  if ((!host || !strcmp(host,LOCAL_HOST)) && (unix_socket || mysql_unix_port)) +  { +    host=LOCAL_HOST; +    if (!unix_socket) +      unix_socket=mysql_unix_port; +    host_info=(char*) ER(CR_LOCALHOST_CONNECTION); +    DBUG_PRINT("info",("Using UNIX sock '%s'",unix_socket)); +    if ((sock = socket(AF_UNIX,SOCK_STREAM,0)) == SOCKET_ERROR) +    { +      net->last_errno=CR_SOCKET_CREATE_ERROR; +      sprintf(net->last_error,ER(net->last_errno),ERRNO); +      goto error; +    } +    net->vio = vio_new(sock, VIO_TYPE_SOCKET, TRUE); +    bzero((char*) &UNIXaddr,sizeof(UNIXaddr)); +    UNIXaddr.sun_family = AF_UNIX; +    strmov(UNIXaddr.sun_path, unix_socket); +    if (connect2(sock,(struct sockaddr *) &UNIXaddr, sizeof(UNIXaddr), +		 mysql->options.connect_timeout) <0) +    { +      DBUG_PRINT("error",("Got error %d on connect to local server",ERRNO)); +      net->last_errno=CR_CONNECTION_ERROR; +      sprintf(net->last_error,ER(net->last_errno),unix_socket,ERRNO); +      goto error; +    } +  } +  else +#elif defined(__WIN32__) +  { +    if ((unix_socket || +	 !host && is_NT() || +	 host && !strcmp(host,LOCAL_HOST_NAMEDPIPE) || +	 mysql->options.named_pipe || !have_tcpip)) +    { +      sock=0; +      if ((hPipe=create_named_pipe(net, mysql->options.connect_timeout, +				   (char**) &host, (char**) &unix_socket)) == +	  INVALID_HANDLE_VALUE) +      { +	DBUG_PRINT("error", +		   ("host: '%s'  socket: '%s'  named_pipe: %d  have_tcpip: %d", +		    host ? host : "<null>", +		    unix_socket ? unix_socket : "<null>", +		    (int) mysql->options.named_pipe, +		    (int) have_tcpip)); +	if (mysql->options.named_pipe || +	    (host && !strcmp(host,LOCAL_HOST_NAMEDPIPE)) || +	    (unix_socket && !strcmp(unix_socket,MYSQL_NAMEDPIPE))) +	  goto error;		/* User only requested named pipes */ +	/* Try also with TCP/IP */ +      } +      else +      { +	net->vio=vio_new_win32pipe(hPipe); +	sprintf(host_info=buff, ER(CR_NAMEDPIPE_CONNECTION), host, +		unix_socket); +      } +    } +  } +  if (hPipe == INVALID_HANDLE_VALUE) +#endif +  { +    unix_socket=0;				/* This is not used */ +    if (!port) +      port=mysql_port; +    if (!host) +      host=LOCAL_HOST; +    sprintf(host_info=buff,ER(CR_TCP_CONNECTION),host); +    DBUG_PRINT("info",("Server name: '%s'.  TCP sock: %d", host,port)); +    if ((sock = socket(AF_INET,SOCK_STREAM,0)) == SOCKET_ERROR) +    { +      net->last_errno=CR_IPSOCK_ERROR; +      sprintf(net->last_error,ER(net->last_errno),ERRNO); +      goto error; +    } +    net->vio = vio_new(sock,VIO_TYPE_TCPIP,FALSE); +    bzero((char*) &sock_addr,sizeof(sock_addr)); +    sock_addr.sin_family = AF_INET; + +    /* +    ** The server name may be a host name or IP address +    */ + +    if ((int) (ip_addr = inet_addr(host)) != (int) INADDR_NONE) +    { +      memcpy(&sock_addr.sin_addr,&ip_addr,sizeof(ip_addr)); +    } +    else +#if defined(HAVE_GETHOSTBYNAME_R) && defined(_REENTRANT) && defined(THREAD) +    { +      int tmp_errno; +      struct hostent tmp_hostent,*hp; +      char buff2[GETHOSTBYNAME_BUFF_SIZE]; +      hp = my_gethostbyname_r(host,&tmp_hostent,buff2,sizeof(buff2), +			      &tmp_errno); +      if (!hp) +      { +	net->last_errno=CR_UNKNOWN_HOST; +	sprintf(net->last_error, ER(CR_UNKNOWN_HOST), host, tmp_errno); +	goto error; +      } +      memcpy(&sock_addr.sin_addr,hp->h_addr, (size_t) hp->h_length); +    } +#else +    { +      struct hostent *hp; +      if (!(hp=gethostbyname(host))) +      { +	net->last_errno=CR_UNKNOWN_HOST; +	sprintf(net->last_error, ER(CR_UNKNOWN_HOST), host, errno); +	goto error; +      } +      memcpy(&sock_addr.sin_addr,hp->h_addr, (size_t) hp->h_length); +    } +#endif +    sock_addr.sin_port = (ushort) htons((ushort) port); +    if (connect2(sock,(struct sockaddr *) &sock_addr, sizeof(sock_addr), +		 mysql->options.connect_timeout) <0) +    { +      DBUG_PRINT("error",("Got error %d on connect to '%s'",ERRNO,host)); +      net->last_errno= CR_CONN_HOST_ERROR; +      sprintf(net->last_error ,ER(CR_CONN_HOST_ERROR), host, ERRNO); +      goto error; +    } +  } + +  if (!net->vio || my_net_init(net, net->vio)) +  { +    vio_delete(net->vio); +    net->last_errno=CR_OUT_OF_MEMORY; +    strmov(net->last_error,ER(net->last_errno)); +    goto error; +  } +  vio_keepalive(net->vio,TRUE); + +  /* Get version info */ +  mysql->protocol_version= PROTOCOL_VERSION;	/* Assume this */ +  if ((pkt_length=net_safe_read(mysql)) == packet_error) +    goto error; + +  /* Check if version of protocoll matches current one */ + +  mysql->protocol_version= net->read_pos[0]; +  DBUG_DUMP("packet",(char*) net->read_pos,10); +  DBUG_PRINT("info",("mysql protocol version %d, server=%d", +		     PROTOCOL_VERSION, mysql->protocol_version)); +  if (mysql->protocol_version != PROTOCOL_VERSION && +      mysql->protocol_version != PROTOCOL_VERSION-1) +  { +    net->last_errno= CR_VERSION_ERROR; +    sprintf(net->last_error, ER(CR_VERSION_ERROR), mysql->protocol_version, +	    PROTOCOL_VERSION); +    goto error; +  } +  end=strend((char*) net->read_pos+1); +  mysql->thread_id=uint4korr(end+1); +  end+=5; +  strmake(mysql->scramble_buff,end,8); +  if (pkt_length > (uint) (end+9 - (char*) net->read_pos)) +    mysql->server_capabilities=uint2korr(end+9); + +  /* Save connection information */ +  if (!user) user=""; +  if (!passwd) passwd=""; +  if (!my_multi_malloc(MYF(0), +		       &mysql->host_info,strlen(host_info)+1, +		       &mysql->host,strlen(host)+1, +		       &mysql->unix_socket,unix_socket ? strlen(unix_socket)+1 +		       :1, +		       &mysql->server_version, +		       (uint) (end - (char*) net->read_pos), +		       NullS) || +      !(mysql->user=my_strdup(user,MYF(0))) || +      !(mysql->passwd=my_strdup(passwd,MYF(0)))) +  { +    strmov(net->last_error, ER(net->last_errno=CR_OUT_OF_MEMORY)); +    goto error; +  } +  strmov(mysql->host_info,host_info); +  strmov(mysql->host,host); +  if (unix_socket) +    strmov(mysql->unix_socket,unix_socket); +  else +    mysql->unix_socket=0; +  strmov(mysql->server_version,(char*) net->read_pos+1); +  mysql->port=port; +  mysql->client_flag=client_flag | mysql->options.client_flag; +  DBUG_PRINT("info",("Server version = '%s'  capabilites: %ld", +		     mysql->server_version,mysql->server_capabilities)); + +  /* Send client information for access check */ +  client_flag|=CLIENT_CAPABILITIES; + +#ifdef HAVE_OPENSSL +  if (mysql->options.use_ssl) +    client_flag|=CLIENT_SSL; +#endif /* HAVE_OPENSSL */ + +  if (db) +    client_flag|=CLIENT_CONNECT_WITH_DB; +#ifdef HAVE_COMPRESS +  if (mysql->server_capabilities & CLIENT_COMPRESS && +      (mysql->options.compress || client_flag & CLIENT_COMPRESS)) +    client_flag|=CLIENT_COMPRESS;		/* We will use compression */ +  else +#endif +    client_flag&= ~CLIENT_COMPRESS; + +#ifdef HAVE_OPENSSL +  if ((mysql->server_capabilities & CLIENT_SSL) && +      (mysql->options.use_ssl || (client_flag & CLIENT_SSL))) +  { +    DBUG_PRINT("info", ("Changing IO layer to SSL")); +    client_flag |= CLIENT_SSL; +  } +  else +  { +    if (client_flag & CLIENT_SSL) +    { +      DBUG_PRINT("info", ("Leaving IO layer intact because server doesn't support SSL")); +    } +    client_flag &= ~CLIENT_SSL; +  } +#endif /* HAVE_OPENSSL */ + +  int2store(buff,client_flag); +  mysql->client_flag=client_flag; + +#ifdef HAVE_OPENSSL +  /* Oops.. are we careful enough to not send ANY information */ +  /* without encryption? */ +  if (client_flag & CLIENT_SSL) +  { +    if (my_net_write(net,buff,(uint) (2)) || net_flush(net)) +      goto error; +    /* Do the SSL layering. */ +    DBUG_PRINT("info", ("IO layer change in progress...")); +    VioSSLConnectorFd* connector_fd = (VioSSLConnectorFd*) +      (mysql->connector_fd); +    VioSocket*	vio_socket = (VioSocket*)(mysql->net.vio); +    VioSSL*	vio_ssl =    connector_fd->connect(vio_socket); +    mysql->net.vio =         (NetVio*)(vio_ssl); +  } +#endif /* HAVE_OPENSSL */ + +  int3store(buff+2,max_allowed_packet); +  if (user && user[0]) +    strmake(buff+5,user,32); +  else +    read_user_name((char*) buff+5); +  DBUG_PRINT("info",("user: %s",buff+5)); +  end=scramble(strend(buff+5)+1, mysql->scramble_buff, passwd, +	       (my_bool) (mysql->protocol_version == 9)); +  if (db && (mysql->server_capabilities & CLIENT_CONNECT_WITH_DB)) +  { +    end=strmov(end+1,db); +    mysql->db=my_strdup(db,MYF(MY_WME)); +    db=0; +  } +  if (my_net_write(net,buff,(uint) (end-buff)) || net_flush(net) || +      net_safe_read(mysql) == packet_error) +    goto error; +  if (client_flag & CLIENT_COMPRESS)		/* We will use compression */ +    net->compress=1; +  if (db && mysql_select_db(mysql,db)) +    goto error; +  if (mysql->options.init_command) +  { +    my_bool reconnect=mysql->reconnect; +    mysql->reconnect=0; +    if (mysql_query(mysql,mysql->options.init_command)) +      goto error; +    mysql_free_result(mysql_use_result(mysql)); +    mysql->reconnect=reconnect; +  } + +  DBUG_PRINT("exit",("Mysql handler: %lx",mysql)); +  reset_sigpipe(mysql); +  DBUG_RETURN(mysql); + +error: +  reset_sigpipe(mysql); +  DBUG_PRINT("error",("message: %u (%s)",net->last_errno,net->last_error)); +  { +    /* Free alloced memory */ +    my_bool free_me=mysql->free_me; +    end_server(mysql); +    mysql->free_me=0; +    mysql_close(mysql); +    mysql->free_me=free_me; +  } +  DBUG_RETURN(0); +} + + +static my_bool mysql_reconnect(MYSQL *mysql) +{ +  MYSQL tmp_mysql; +  DBUG_ENTER("mysql_reconnect"); + +  if (!mysql->reconnect || !mysql->host_info) +    DBUG_RETURN(1); +  mysql_init(&tmp_mysql); +  tmp_mysql.options=mysql->options; +  if (!mysql_real_connect(&tmp_mysql,mysql->host,mysql->user,mysql->passwd, +			  mysql->db, mysql->port, mysql->unix_socket, +			  mysql->client_flag)) +    DBUG_RETURN(1); +  tmp_mysql.free_me=mysql->free_me; +  mysql->free_me=0; +  bzero((char*) &mysql->options,sizeof(&mysql->options)); +  mysql_close(mysql); +  memcpy(mysql,&tmp_mysql,sizeof(tmp_mysql)); +  net_clear(&mysql->net); +  mysql->affected_rows= ~(my_ulonglong) 0; +  DBUG_RETURN(0); +} + + +/************************************************************************** +** Change user and database  +**************************************************************************/ + +my_bool	STDCALL mysql_change_user(MYSQL *mysql, const char *user,  +				  const char *passwd, const char *db) +{ +  char buff[512],*pos=buff; +  DBUG_ENTER("mysql_change_user"); + +  if (!user) +    user=""; +  if (!passwd) +    passwd=""; + +  pos=strmov(pos,user)+1; +  pos=scramble(pos, mysql->scramble_buff, passwd, +	       (my_bool) (mysql->protocol_version == 9)); +  pos=strmov(pos+1,db ? db : ""); +  if (simple_command(mysql,COM_CHANGE_USER, buff,(uint) (pos-buff),0)) +    DBUG_RETURN(1); + +  my_free(mysql->user,MYF(MY_ALLOW_ZERO_PTR)); +  my_free(mysql->passwd,MYF(MY_ALLOW_ZERO_PTR)); +  my_free(mysql->db,MYF(MY_ALLOW_ZERO_PTR)); + +  mysql->user=  my_strdup(user,MYF(MY_WME)); +  mysql->passwd=my_strdup(passwd,MYF(MY_WME)); +  mysql->db=    db ? my_strdup(db,MYF(MY_WME)) : 0; +  DBUG_RETURN(0); +} + + +/************************************************************************** +** Set current database +**************************************************************************/ + +int STDCALL +mysql_select_db(MYSQL *mysql, const char *db) +{ +  int error; +  DBUG_ENTER("mysql_select_db"); +  DBUG_PRINT("enter",("db: '%s'",db)); + +  if ((error=simple_command(mysql,COM_INIT_DB,db,strlen(db),0))) +    DBUG_RETURN(error); +  my_free(mysql->db,MYF(MY_ALLOW_ZERO_PTR)); +  mysql->db=my_strdup(db,MYF(MY_WME)); +  DBUG_RETURN(0); +} + + +/************************************************************************* +** Send a QUIT to the server and close the connection +** If handle is alloced by mysql connect free it. +*************************************************************************/ + +void STDCALL +mysql_close(MYSQL *mysql) +{ +  DBUG_ENTER("mysql_close"); +  if (mysql)					/* Some simple safety */ +  { +    if (mysql->net.vio != 0) +    { +      free_old_query(mysql); +      mysql->status=MYSQL_STATUS_READY; /* Force command */ +      simple_command(mysql,COM_QUIT,NullS,0,1); +      end_server(mysql); +    } +    my_free((gptr) mysql->host_info,MYF(MY_ALLOW_ZERO_PTR)); +    my_free(mysql->user,MYF(MY_ALLOW_ZERO_PTR)); +    my_free(mysql->passwd,MYF(MY_ALLOW_ZERO_PTR)); +    my_free(mysql->db,MYF(MY_ALLOW_ZERO_PTR)); +    my_free(mysql->options.init_command,MYF(MY_ALLOW_ZERO_PTR)); +    my_free(mysql->options.user,MYF(MY_ALLOW_ZERO_PTR)); +    my_free(mysql->options.password,MYF(MY_ALLOW_ZERO_PTR)); +    my_free(mysql->options.unix_socket,MYF(MY_ALLOW_ZERO_PTR)); +    my_free(mysql->options.db,MYF(MY_ALLOW_ZERO_PTR)); +    my_free(mysql->options.my_cnf_file,MYF(MY_ALLOW_ZERO_PTR)); +    my_free(mysql->options.my_cnf_group,MYF(MY_ALLOW_ZERO_PTR)); +    /* Clear pointers for better safety */ +    mysql->host_info=mysql->user=mysql->passwd=mysql->db=0; +    bzero((char*) &mysql->options,sizeof(mysql->options)); +    mysql->net.vio = 0; +#ifdef HAVE_OPENSSL +    ((VioConnectorFd*)(mysql->connector_fd))->delete(); +    mysql->connector_fd = 0; +#endif /* HAVE_OPENSSL */ +    if (mysql->free_me) +      my_free((gptr) mysql,MYF(0)); +  } +  DBUG_VOID_RETURN; +} + + +/************************************************************************** +** Do a query. If query returned rows, free old rows. +** Read data by mysql_store_result or by repeat call of mysql_fetch_row +**************************************************************************/ + +int STDCALL +mysql_query(MYSQL *mysql, const char *query) +{ +  return mysql_real_query(mysql,query,strlen(query)); +} + + +int STDCALL +mysql_real_query(MYSQL *mysql, const char *query,uint length) +{ +  uchar *pos; +  ulong field_count; +  MYSQL_DATA *fields; +  DBUG_ENTER("mysql_real_query"); +  DBUG_PRINT("enter",("handle: %lx",mysql)); +  DBUG_PRINT("query",("Query = \"%s\"",query)); + +  if (simple_command(mysql,COM_QUERY,query,length,1) || +      (length=net_safe_read(mysql)) == packet_error) +    DBUG_RETURN(-1); +  free_old_query(mysql);			/* Free old result */ + get_info: +  pos=(uchar*) mysql->net.read_pos; +  if ((field_count= net_field_length(&pos)) == 0) +  { +    mysql->affected_rows= net_field_length_ll(&pos); +    mysql->insert_id=	  net_field_length_ll(&pos); +    if (pos < mysql->net.read_pos+length && net_field_length(&pos)) +      mysql->info=(char*) pos; +    DBUG_RETURN(0); +  } +  if (field_count == NULL_LENGTH)		/* LOAD DATA LOCAL INFILE */ +  { +    int error=send_file_to_server(mysql,(char*) pos); +    if ((length=net_safe_read(mysql)) == packet_error || error) +      DBUG_RETURN(-1); +    goto get_info;				/* Get info packet */ +  } +  mysql->extra_info= net_field_length_ll(&pos); /* Maybe number of rec */ +  if (!(fields=read_rows(mysql,(MYSQL_FIELD*) 0,5))) +    DBUG_RETURN(-1); +  if (!(mysql->fields=unpack_fields(fields,&mysql->field_alloc, +				    (uint) field_count,0, +				    (my_bool) test(mysql->server_capabilities & +						   CLIENT_LONG_FLAG)))) +    DBUG_RETURN(-1); +  mysql->status=MYSQL_STATUS_GET_RESULT; +  mysql->field_count=field_count; +  DBUG_RETURN(0); +} + + +static int +send_file_to_server(MYSQL *mysql, const char *filename) +{ +  int fd, readcount; +  char buf[IO_SIZE*15],*tmp_name; +  DBUG_ENTER("send_file_to_server"); + +  fn_format(buf,filename,"","",4);		/* Convert to client format */ +  if (!(tmp_name=my_strdup(buf,MYF(0)))) +  { +    strmov(mysql->net.last_error, ER(mysql->net.last_errno=CR_OUT_OF_MEMORY)); +    DBUG_RETURN(-1); +  } +  if ((fd = my_open(tmp_name,O_RDONLY, MYF(0))) < 0) +  { +    mysql->net.last_errno=EE_FILENOTFOUND; +    sprintf(buf,EE(mysql->net.last_errno),tmp_name,errno); +    strmake(mysql->net.last_error,buf,sizeof(mysql->net.last_error)-1); +    my_net_write(&mysql->net,"",0); net_flush(&mysql->net); +    my_free(tmp_name,MYF(0)); +    DBUG_RETURN(-1); +  } + +  while ((readcount = (int) my_read(fd,buf,sizeof(buf),MYF(0))) > 0) +  { +    if (my_net_write(&mysql->net,buf,readcount)) +    { +      mysql->net.last_errno=CR_SERVER_LOST; +      strmov(mysql->net.last_error,ER(mysql->net.last_errno)); +      DBUG_PRINT("error",("Lost connection to MySQL server during LOAD DATA of local file")); +      (void) my_close(fd,MYF(0)); +      my_free(tmp_name,MYF(0)); +      DBUG_RETURN(-1); +    } +  } +  (void) my_close(fd,MYF(0)); +  /* Send empty packet to mark end of file */ +  if (my_net_write(&mysql->net,"",0) || net_flush(&mysql->net)) +  { +    mysql->net.last_errno=CR_SERVER_LOST; +    sprintf(mysql->net.last_error,ER(mysql->net.last_errno),errno); +    my_free(tmp_name,MYF(0)); +    DBUG_RETURN(-1); +  } +  if (readcount < 0) +  { +    mysql->net.last_errno=EE_READ; /* the errmsg for not entire file read */ +    sprintf(buf,EE(mysql->net.last_errno),tmp_name,errno); +    strmake(mysql->net.last_error,buf,sizeof(mysql->net.last_error)-1); +    my_free(tmp_name,MYF(0)); +    DBUG_RETURN(-1); +  } +  DBUG_RETURN(0); +} + + +/************************************************************************** +** Alloc result struct for buffered results. All rows are read to buffer. +** mysql_data_seek may be used. +**************************************************************************/ + +MYSQL_RES * STDCALL +mysql_store_result(MYSQL *mysql) +{ +  MYSQL_RES *result; +  DBUG_ENTER("mysql_store_result"); + +  if (!mysql->fields) +    DBUG_RETURN(0); +  if (mysql->status != MYSQL_STATUS_GET_RESULT) +  { +    strmov(mysql->net.last_error, +	   ER(mysql->net.last_errno=CR_COMMANDS_OUT_OF_SYNC)); +    DBUG_RETURN(0); +  } +  mysql->status=MYSQL_STATUS_READY;		/* server is ready */ +  if (!(result=(MYSQL_RES*) my_malloc(sizeof(MYSQL_RES)+ +				      sizeof(ulong)*mysql->field_count, +				      MYF(MY_WME | MY_ZEROFILL)))) +  { +    mysql->net.last_errno=CR_OUT_OF_MEMORY; +    strmov(mysql->net.last_error, ER(mysql->net.last_errno)); +    DBUG_RETURN(0); +  } +  result->eof=1;				/* Marker for buffered */ +  result->lengths=(ulong*) (result+1); +  if (!(result->data=read_rows(mysql,mysql->fields,mysql->field_count))) +  { +    my_free((gptr) result,MYF(0)); +    DBUG_RETURN(0); +  } +  mysql->affected_rows= result->row_count= result->data->rows; +  result->data_cursor=	result->data->data; +  result->fields=	mysql->fields; +  result->field_alloc=	mysql->field_alloc; +  result->field_count=	mysql->field_count; +  result->current_field=0; +  result->current_row=0;			/* Must do a fetch first */ +  mysql->fields=0;				/* fields is now in result */ +  DBUG_RETURN(result);				/* Data fetched */ +} + + +/************************************************************************** +** Alloc struct for use with unbuffered reads. Data is fetched by domand +** when calling to mysql_fetch_row. +** mysql_data_seek is a noop. +** +** No other queries may be specified with the same MYSQL handle. +** There shouldn't be much processing per row because mysql server shouldn't +** have to wait for the client (and will not wait more than 30 sec/packet). +**************************************************************************/ + +MYSQL_RES * STDCALL +mysql_use_result(MYSQL *mysql) +{ +  MYSQL_RES *result; +  DBUG_ENTER("mysql_use_result"); + +  if (!mysql->fields) +    DBUG_RETURN(0); +  if (mysql->status != MYSQL_STATUS_GET_RESULT) +  { +    strmov(mysql->net.last_error, +	   ER(mysql->net.last_errno=CR_COMMANDS_OUT_OF_SYNC)); +    DBUG_RETURN(0); +  } +  if (!(result=(MYSQL_RES*) my_malloc(sizeof(*result)+ +				      sizeof(ulong)*mysql->field_count, +				      MYF(MY_WME | MY_ZEROFILL)))) +    DBUG_RETURN(0); +  result->lengths=(ulong*) (result+1); +  if (!(result->row=(MYSQL_ROW) +	my_malloc(sizeof(result->row[0])*(mysql->field_count+1), MYF(MY_WME)))) +  {					/* Ptrs: to one row */ +    my_free((gptr) result,MYF(0)); +    DBUG_RETURN(0); +  } +  result->fields=	mysql->fields; +  result->field_alloc=	mysql->field_alloc; +  result->field_count=	mysql->field_count; +  result->current_field=0; +  result->handle=	mysql; +  result->current_row=	0; +  mysql->fields=0;			/* fields is now in result */ +  mysql->status=MYSQL_STATUS_USE_RESULT; +  DBUG_RETURN(result);			/* Data is read to be fetched */ +} + + + +/************************************************************************** +** Return next field of the query results +**************************************************************************/ + +MYSQL_FIELD * STDCALL +mysql_fetch_field(MYSQL_RES *result) +{ +  if (result->current_field >= result->field_count) +    return(NULL); +  return &result->fields[result->current_field++]; +} + + +/************************************************************************** +**  Return next row of the query results +**************************************************************************/ + +MYSQL_ROW STDCALL +mysql_fetch_row(MYSQL_RES *res) +{ +  DBUG_ENTER("mysql_fetch_row"); +  if (!res->data) +  {						/* Unbufferred fetch */ +    if (!res->eof) +    { +      if (!(read_one_row(res->handle,res->field_count,res->row, res->lengths))) +      { +	res->row_count++; +	DBUG_RETURN(res->current_row=res->row); +      } +      else +      { +	res->eof=1; +	res->handle->status=MYSQL_STATUS_READY; +      } +    } +    DBUG_RETURN((MYSQL_ROW) NULL); +  } +  { +    MYSQL_ROW tmp; +    if (!res->data_cursor) +      DBUG_RETURN(res->current_row=(MYSQL_ROW) NULL); +    tmp = res->data_cursor->data; +    res->data_cursor = res->data_cursor->next; +    DBUG_RETURN(res->current_row=tmp); +  } +} + +/************************************************************************** +** Get column lengths of the current row +** If one uses mysql_use_result, res->lengths contains the length information, +** else the lengths are calculated from the offset between pointers. +**************************************************************************/ + +ulong * STDCALL +mysql_fetch_lengths(MYSQL_RES *res) +{ +  ulong *lengths,*prev_length; +  byte *start; +  MYSQL_ROW column,end; + +  if (!(column=res->current_row)) +    return 0;					/* Something is wrong */ +  if (res->data) +  { +    start=0; +    prev_length=0;				/* Keep gcc happy */ +    lengths=res->lengths; +    for (end=column+res->field_count+1 ; column != end ; column++,lengths++) +    { +      if (!*column) +      { +	*lengths=0;				/* Null */ +	continue; +      } +      if (start)				/* Found end of prev string */ +	*prev_length= (uint) (*column-start-1); +      start= *column; +      prev_length=lengths; +    } +  } +  return res->lengths; +} + +/************************************************************************** +** Move to a specific row and column +**************************************************************************/ + +void STDCALL +mysql_data_seek(MYSQL_RES *result, my_ulonglong row) +{ +  MYSQL_ROWS	*tmp=0; +  DBUG_PRINT("info",("mysql_data_seek(%d)",row)); +  if (result->data) +    for (tmp=result->data->data; row-- && tmp ; tmp = tmp->next) ; +  result->current_row=0; +  result->data_cursor = tmp; +} + +/************************************************************************* +** put the row or field cursor one a position one got from mysql_row_tell() +** This dosen't restore any data. The next mysql_fetch_row or +** mysql_fetch_field will return the next row or field after the last used +*************************************************************************/ + +MYSQL_ROW_OFFSET STDCALL +mysql_row_seek(MYSQL_RES *result, MYSQL_ROW_OFFSET row) +{ +  MYSQL_ROW_OFFSET return_value=result->data_cursor; +  result->current_row= 0; +  result->data_cursor= row; +  return return_value; +} + + +MYSQL_FIELD_OFFSET STDCALL +mysql_field_seek(MYSQL_RES *result, MYSQL_FIELD_OFFSET field_offset) +{ +  MYSQL_FIELD_OFFSET return_value=result->current_field; +  result->current_field=field_offset; +  return return_value; +} + +/***************************************************************************** +** List all databases +*****************************************************************************/ + +MYSQL_RES * STDCALL +mysql_list_dbs(MYSQL *mysql, const char *wild) +{ +  char buff[255]; +  DBUG_ENTER("mysql_list_dbs"); + +  append_wild(strmov(buff,"show databases"),buff+sizeof(buff),wild); +  if (mysql_query(mysql,buff)) +    DBUG_RETURN(0); +  DBUG_RETURN (mysql_store_result(mysql)); +} + + +/***************************************************************************** +** List all tables in a database +** If wild is given then only the tables matching wild is returned +*****************************************************************************/ + +MYSQL_RES * STDCALL +mysql_list_tables(MYSQL *mysql, const char *wild) +{ +  char buff[255]; +  DBUG_ENTER("mysql_list_tables"); + +  append_wild(strmov(buff,"show tables"),buff+sizeof(buff),wild); +  if (mysql_query(mysql,buff)) +    DBUG_RETURN(0); +  DBUG_RETURN (mysql_store_result(mysql)); +} + + +/************************************************************************** +** List all fields in a table +** If wild is given then only the fields matching wild is returned +** Instead of this use query: +** show fields in 'table' like "wild" +**************************************************************************/ + +MYSQL_RES * STDCALL +mysql_list_fields(MYSQL *mysql, const char *table, const char *wild) +{ +  MYSQL_RES *result; +  MYSQL_DATA *query; +  char	     buff[257],*end; +  DBUG_ENTER("mysql_list_fields"); +  DBUG_PRINT("enter",("table: '%s'  wild: '%s'",table,wild ? wild : "")); + +  LINT_INIT(query); + +  end=strmake(strmake(buff, table,128)+1,wild ? wild : "",128); +  if (simple_command(mysql,COM_FIELD_LIST,buff,(uint) (end-buff),1) || +      !(query = read_rows(mysql,(MYSQL_FIELD*) 0,6))) +    DBUG_RETURN(NULL); + +  free_old_query(mysql); +  if (!(result = (MYSQL_RES *) my_malloc(sizeof(MYSQL_RES), +					 MYF(MY_WME | MY_ZEROFILL)))) +  { +    free_rows(query); +    DBUG_RETURN(NULL); +  } +  result->field_alloc=mysql->field_alloc; +  mysql->fields=0; +  result->field_count = (uint) query->rows; +  result->fields= unpack_fields(query,&result->field_alloc, +				result->field_count,1, +				(my_bool) test(mysql->server_capabilities & +					       CLIENT_LONG_FLAG)); +  result->eof=1; +  DBUG_RETURN(result); +} + +/* List all running processes (threads) in server */ + +MYSQL_RES * STDCALL +mysql_list_processes(MYSQL *mysql) +{ +  MYSQL_DATA *fields; +  uint field_count; +  uchar *pos; +  DBUG_ENTER("mysql_list_processes"); + +  LINT_INIT(fields); +  if (simple_command(mysql,COM_PROCESS_INFO,0,0,0)) +    DBUG_RETURN(0); +  free_old_query(mysql); +  pos=(uchar*) mysql->net.read_pos; +  field_count=(uint) net_field_length(&pos); +  if (!(fields = read_rows(mysql,(MYSQL_FIELD*) 0,5))) +    DBUG_RETURN(NULL); +  if (!(mysql->fields=unpack_fields(fields,&mysql->field_alloc,field_count,0, +				    (my_bool) test(mysql->server_capabilities & +						   CLIENT_LONG_FLAG)))) +    DBUG_RETURN(0); +  mysql->status=MYSQL_STATUS_GET_RESULT; +  mysql->field_count=field_count; +  DBUG_RETURN(mysql_store_result(mysql)); +} + + +int  STDCALL +mysql_create_db(MYSQL *mysql, const char *db) +{ +  DBUG_ENTER("mysql_createdb"); +  DBUG_PRINT("enter",("db: %s",db)); +  DBUG_RETURN(simple_command(mysql,COM_CREATE_DB,db, (uint) strlen(db),0)); +} + + +int  STDCALL +mysql_drop_db(MYSQL *mysql, const char *db) +{ +  DBUG_ENTER("mysql_drop_db"); +  DBUG_PRINT("enter",("db: %s",db)); +  DBUG_RETURN(simple_command(mysql,COM_DROP_DB,db,(uint) strlen(db),0)); +} + + +int STDCALL +mysql_shutdown(MYSQL *mysql) +{ +  DBUG_ENTER("mysql_shutdown"); +  DBUG_RETURN(simple_command(mysql,COM_SHUTDOWN,0,0,0)); +} + + +int STDCALL +mysql_refresh(MYSQL *mysql,uint options) +{ +  uchar bits[1]; +  DBUG_ENTER("mysql_refresh"); +  bits[0]= (uchar) options; +  DBUG_RETURN(simple_command(mysql,COM_REFRESH,(char*) bits,1,0)); +} + +int STDCALL +mysql_kill(MYSQL *mysql,ulong pid) +{ +  char buff[12]; +  DBUG_ENTER("mysql_kill"); +  int4store(buff,pid); +  DBUG_RETURN(simple_command(mysql,COM_PROCESS_KILL,buff,4,0)); +} + + +int STDCALL +mysql_dump_debug_info(MYSQL *mysql) +{ +  DBUG_ENTER("mysql_dump_debug_info"); +  DBUG_RETURN(simple_command(mysql,COM_DEBUG,0,0,0)); +} + +char * STDCALL +mysql_stat(MYSQL *mysql) +{ +  DBUG_ENTER("mysql_stat"); +  if (simple_command(mysql,COM_STATISTICS,0,0,0)) +    return mysql->net.last_error; +  mysql->net.read_pos[mysql->packet_length]=0;	/* End of stat string */ +  if (!mysql->net.read_pos[0]) +  { +    mysql->net.last_errno=CR_WRONG_HOST_INFO; +    strmov(mysql->net.last_error, ER(mysql->net.last_errno)); +    return mysql->net.last_error; +  } +  DBUG_RETURN((char*) mysql->net.read_pos); +} + + +int STDCALL +mysql_ping(MYSQL *mysql) +{ +  DBUG_ENTER("mysql_ping"); +  DBUG_RETURN(simple_command(mysql,COM_PING,0,0,0)); +} + + +char * STDCALL +mysql_get_server_info(MYSQL *mysql) +{ +  return((char*) mysql->server_version); +} + + +char * STDCALL +mysql_get_host_info(MYSQL *mysql) +{ +  return(mysql->host_info); +} + + +uint STDCALL +mysql_get_proto_info(MYSQL *mysql) +{ +  return (mysql->protocol_version); +} + +char * STDCALL +mysql_get_client_info(void) +{ +  return (char*) MYSQL_SERVER_VERSION; +} + + +int STDCALL +mysql_options(MYSQL *mysql,enum mysql_option option, const char *arg) +{ +  DBUG_ENTER("mysql_option"); +  DBUG_PRINT("enter",("option: %d",(int) option)); +  switch (option) { +  case MYSQL_OPT_CONNECT_TIMEOUT: +    mysql->options.connect_timeout= *(uint*) arg; +    break; +  case MYSQL_OPT_COMPRESS: +    mysql->options.compress=1;			/* Remember for connect */ +    break; +  case MYSQL_OPT_NAMED_PIPE: +    mysql->options.named_pipe=1;		/* Force named pipe */ +    break; +  case MYSQL_INIT_COMMAND: +    my_free(mysql->options.init_command,MYF(MY_ALLOW_ZERO_PTR)); +    mysql->options.init_command=my_strdup(arg,MYF(MY_WME)); +    break; +  case MYSQL_READ_DEFAULT_FILE: +    my_free(mysql->options.my_cnf_file,MYF(MY_ALLOW_ZERO_PTR)); +    mysql->options.my_cnf_file=my_strdup(arg,MYF(MY_WME)); +    break; +  case MYSQL_READ_DEFAULT_GROUP: +    my_free(mysql->options.my_cnf_group,MYF(MY_ALLOW_ZERO_PTR)); +    mysql->options.my_cnf_group=my_strdup(arg,MYF(MY_WME)); +    break; +  default: +    DBUG_RETURN(-1); +  } +  DBUG_RETURN(0); +} + +/**************************************************************************** +** Functions to get information from the MySQL structure +** These are functions to make shared libraries more usable. +****************************************************************************/ + +/* MYSQL_RES */ +my_ulonglong mysql_num_rows(MYSQL_RES *res) +{ +  return res->row_count; +} + +unsigned int mysql_num_fields(MYSQL_RES *res) +{ +  return res->field_count; +} + +my_bool mysql_eof(MYSQL_RES *res) +{ +  return res->eof; +} + +MYSQL_FIELD *mysql_fetch_field_direct(MYSQL_RES *res,uint fieldnr) +{ +  return &(res)->fields[fieldnr]; +} + +MYSQL_FIELD *mysql_fetch_fields(MYSQL_RES *res) +{ +  return (res)->fields; +} + +MYSQL_ROWS *mysql_row_tell(MYSQL_RES *res) +{ +  return res->data_cursor; +} + +uint mysql_field_tell(MYSQL_RES *res) +{ +  return (res)->current_field; +} + +/* MYSQL */ + +unsigned int mysql_field_count(MYSQL *mysql) +{ +  return mysql->field_count; +} + +my_ulonglong mysql_affected_rows(MYSQL *mysql) +{ +  return (mysql)->affected_rows; +} + +my_ulonglong mysql_insert_id(MYSQL *mysql) +{ +  return (mysql)->insert_id; +} + +uint mysql_errno(MYSQL *mysql) +{ +  return (mysql)->net.last_errno; +} + +char *mysql_error(MYSQL *mysql) +{ +  return (mysql)->net.last_error; +} + +char *mysql_info(MYSQL *mysql) +{ +  return (mysql)->info; +} + +ulong mysql_thread_id(MYSQL *mysql) +{ +  return (mysql)->thread_id; +} + +/**************************************************************************** +** Some support functions +****************************************************************************/ + +/* +** Add escape characters to a string (blob?) to make it suitable for a insert +** to should at least have place for length*2+1 chars +** Returns the length of the to string +*/ + +ulong STDCALL +mysql_escape_string(char *to,const char *from,ulong length) +{ +  const char *to_start=to; +  const char *end; +  for (end=from+length; from != end ; from++) +  { +#ifdef USE_MB +    int l; +    if ((l = ismbchar(from, end))) +    { +      while (l--) +	  *to++ = *from++; +      from--; +      continue; +    } +#endif +    switch (*from) { +    case 0:				/* Must be escaped for 'mysql' */ +      *to++= '\\'; +      *to++= '0'; +      break; +    case '\n':				/* Must be escaped for logs */ +      *to++= '\\'; +      *to++= 'n'; +      break; +    case '\r': +      *to++= '\\'; +      *to++= 'r'; +      break; +    case '\\': +      *to++= '\\'; +      *to++= '\\'; +      break; +    case '\'': +      *to++= '\\'; +      *to++= '\''; +      break; +    case '"':				/* Better safe than sorry */ +      *to++= '\\'; +      *to++= '"'; +      break; +    case '\032':			/* This gives problems on Win32 */ +      *to++= '\\'; +      *to++= 'Z'; +      break; +    default: +      *to++= *from; +    } +  } +  *to=0; +  return (ulong) (to-to_start); +} + + +char * STDCALL +mysql_odbc_escape_string(char *to, ulong to_length, +			 const char *from, ulong from_length, +			 void *param, +			 char * (*extend_buffer) +			 (void *, char *, ulong *)) +{ +  char *to_end=to+to_length-5; +  const char *end; + +  for (end=from+from_length; from != end ; from++) +  { +    if (to >= to_end) +    { +      to_length = (ulong) (end-from)+512;	/* We want this much more */ +      if (!(to=(*extend_buffer)(param, to, &to_length))) +	return to; +      to_end=to+to_length-5; +    } +#ifdef USE_MB +    { +      int l; +      if ((l = ismbchar(from, end))) +      { +	while (l--) +	  *to++ = *from++; +	from--; +	continue; +      } +    } +#endif +    switch (*from) { +    case 0:				/* Must be escaped for 'mysql' */ +      *to++= '\\'; +      *to++= '0'; +      break; +    case '\n':				/* Must be escaped for logs */ +      *to++= '\\'; +      *to++= 'n'; +      break; +    case '\r': +      *to++= '\\'; +      *to++= 'r'; +      break; +    case '\\': +      *to++= '\\'; +      *to++= '\\'; +      break; +    case '\'': +      *to++= '\\'; +      *to++= '\''; +      break; +    case '"':				/* Better safe than sorry */ +      *to++= '\\'; +      *to++= '"'; +      break; +    case '\032':			/* This gives problems on Win32 */ +      *to++= '\\'; +      *to++= 'Z'; +      break; +    default: +      *to++= *from; +    } +  } +  return to; +}  | 
