libmysql.c 103 KB
Newer Older
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
1
/* Copyright (C) 2000-2003 MySQL AB
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2 3 4 5 6 7 8

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
9
   but WITHOUT ANY WARRANTY; without even the implied warranty of
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
10 11 12 13 14 15
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
16

17
#include <my_global.h>
bk@work.mysql.com's avatar
bk@work.mysql.com committed
18 19 20 21 22 23 24 25 26 27 28 29
#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>
#include <time.h>
30
#include <assert.h> /* for DBUG_ASSERT() */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
31 32 33 34 35 36 37 38 39
#ifdef	 HAVE_PWD_H
#include <pwd.h>
#endif
#if !defined(MSDOS) && !defined(__WIN__)
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#ifdef HAVE_SELECT_H
40
#include <select.h>
bk@work.mysql.com's avatar
bk@work.mysql.com committed
41 42 43 44
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
45
#endif /* !defined(MSDOS) && !defined(__WIN__) */
46 47
#ifdef HAVE_POLL
#include <sys/poll.h>
bk@work.mysql.com's avatar
bk@work.mysql.com committed
48 49
#endif
#ifdef HAVE_SYS_UN_H
50
#include <sys/un.h>
bk@work.mysql.com's avatar
bk@work.mysql.com committed
51 52 53 54 55 56 57 58
#endif
#if defined(THREAD) && !defined(__WIN__)
#include <my_pthread.h>				/* because of signal()	*/
#endif
#ifndef INADDR_NONE
#define INADDR_NONE	-1
#endif

hf@deer.(none)'s avatar
hf@deer.(none) committed
59
#include <sql_common.h>
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
60
#include "client_settings.h"
hf@deer.(none)'s avatar
hf@deer.(none) committed
61

62
ulong 		net_buffer_length=8192;
63
ulong		max_allowed_packet= 1024L*1024L*1024L;
64 65
ulong		net_read_timeout=  CLIENT_NET_READ_TIMEOUT;
ulong		net_write_timeout= CLIENT_NET_WRITE_TIMEOUT;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
66

hf@deer.(none)'s avatar
hf@deer.(none) committed
67

hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
68 69 70 71 72
#ifdef EMBEDDED_LIBRARY
#undef net_flush
my_bool	net_flush(NET *net);
#endif

bk@work.mysql.com's avatar
bk@work.mysql.com committed
73
#if defined(MSDOS) || defined(__WIN__)
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
74
/* socket_errno is defined in my_global.h for all platforms */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
75 76 77 78
#define perror(A)
#else
#include <errno.h>
#define SOCKET_ERROR -1
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
79
#endif /* __WIN__ */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
80

81 82 83 84 85
/*
  If allowed through some configuration, then this needs to
  be changed
*/
#define MAX_LONG_DATA_LENGTH 8192
86 87
#define unsigned_field(A) ((A)->flags & UNSIGNED_FLAG)

88
static void stmt_update_metadata(MYSQL_STMT *stmt, MYSQL_ROWS *data);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
89
static void append_wild(char *to,char *end,const char *wild);
hf@deer.(none)'s avatar
hf@deer.(none) committed
90
sig_handler pipe_sig_handler(int sig);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
91 92
static ulong mysql_sub_escape_string(CHARSET_INFO *charset_info, char *to,
				     const char *from, ulong length);
93 94 95 96

static my_bool mysql_client_init= 0;
static my_bool org_my_init_done= 0;

monty@mysql.com's avatar
monty@mysql.com committed
97 98

/*
monty@mysql.com's avatar
merge  
monty@mysql.com committed
99
  Initialize the MySQL client library
monty@mysql.com's avatar
monty@mysql.com committed
100 101

  SYNOPSIS
monty@mysql.com's avatar
merge  
monty@mysql.com committed
102
    mysql_server_init()
monty@mysql.com's avatar
monty@mysql.com committed
103 104

  NOTES
monty@mysql.com's avatar
merge  
monty@mysql.com committed
105 106 107 108 109
    Should be called before doing any other calls to the MySQL
    client library to initialize thread specific variables etc.
    It's called by mysql_init() to ensure that things will work for
    old not threaded applications that doesn't call mysql_server_init()
    directly.
monty@mysql.com's avatar
monty@mysql.com committed
110 111 112 113 114 115

  RETURN
    0  ok
    1  could not initialize environment (out of memory or thread keys)
*/

monty@mysql.com's avatar
monty@mysql.com committed
116 117 118
int STDCALL mysql_server_init(int argc __attribute__((unused)),
			      char **argv __attribute__((unused)),
			      char **groups __attribute__((unused)))
119
{
120
  int result= 0;
121 122 123 124
  if (!mysql_client_init)
  {
    mysql_client_init=1;
    org_my_init_done=my_init_done;
monty@mysql.com's avatar
monty@mysql.com committed
125 126
    if (my_init())				/* Will init threads */
      return 1;
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
    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 __WIN__
      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);
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
154 155
#if defined(SIGPIPE) && !defined(__WIN__)
    (void) signal(SIGPIPE, SIG_IGN);
156
#endif
monty@mysql.com's avatar
monty@mysql.com committed
157
#ifdef EMBEDDED_LIBRARY
158
    result= init_embedded_server(argc, argv, groups);
monty@mysql.com's avatar
monty@mysql.com committed
159
#endif
160 161 162
  }
#ifdef THREAD
  else
hf@deer.(none)'s avatar
hf@deer.(none) committed
163
    result= (int)my_thread_init();         /* Init if new thread */
164
#endif
165
  return result;
166
}
tim@black.box's avatar
tim@black.box committed
167

168

169
void STDCALL mysql_server_end()
170
{
monty@mysql.com's avatar
monty@mysql.com committed
171
#ifdef EMBEDDED_LIBRARY
172
  end_embedded_server();
monty@mysql.com's avatar
monty@mysql.com committed
173
#endif
174 175
  /* If library called my_init(), free memory allocated by it */
  if (!org_my_init_done)
176
  {
177
    my_end(0);
178 179 180 181 182
#ifndef THREAD
  /* Remove TRACING, if enabled by mysql_debug() */
    DBUG_POP();
#endif
  }
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
183 184
  else
    mysql_thread_end();
185
  mysql_client_init= org_my_init_done= 0;
186
}
tim@black.box's avatar
tim@black.box committed
187

188
my_bool STDCALL mysql_thread_init()
tim@black.box's avatar
tim@black.box committed
189 190
{
#ifdef THREAD
191
  return my_thread_init();
tim@black.box's avatar
tim@black.box committed
192
#else
193
  return 0;
tim@black.box's avatar
tim@black.box committed
194 195 196
#endif
}

197
void STDCALL mysql_thread_end()
tim@black.box's avatar
tim@black.box committed
198 199
{
#ifdef THREAD
200
  my_thread_end();
tim@black.box's avatar
tim@black.box committed
201 202 203
#endif
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
204 205 206 207
/*
  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.
*/
208 209 210 211 212
static MYSQL* spawn_init(MYSQL* parent, const char* host,
			 unsigned int port,
			 const char* user,
			 const char* passwd);

213

bk@work.mysql.com's avatar
bk@work.mysql.com committed
214 215

/*
216
  Expand wildcard to a sql string
bk@work.mysql.com's avatar
bk@work.mysql.com committed
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
*/

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;
  }
}


/**************************************************************************
241
  Init debugging if MYSQL_DEBUG environment variable is found
bk@work.mysql.com's avatar
bk@work.mysql.com committed
242 243 244
**************************************************************************/

void STDCALL
monty@donna.mysql.com's avatar
monty@donna.mysql.com committed
245
mysql_debug(const char *debug __attribute__((unused)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
{
#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];
268 269
      buff[sizeof(buff)-1]= 0;
      strxnmov(buff,sizeof(buff)-1,"libmysql: ", env, NullS);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
270 271 272 273 274 275 276 277 278
      MessageBox((HWND) 0,"Debugging variable MYSQL_DEBUG used",buff,MB_OK);
    }
#endif
  }
#endif
}


/**************************************************************************
279
  Close the server connection if we get a SIGPIPE
bk@work.mysql.com's avatar
bk@work.mysql.com committed
280 281 282
   ARGSUSED
**************************************************************************/

hf@deer.(none)'s avatar
hf@deer.(none) committed
283
sig_handler
bk@work.mysql.com's avatar
bk@work.mysql.com committed
284 285 286 287 288 289 290 291
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
}

hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
292 293
/* perform query on master */
my_bool STDCALL mysql_master_query(MYSQL *mysql, const char *q,
294
				   unsigned long length)
hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
295 296 297 298
{
  DBUG_ENTER("mysql_master_query");
  if (mysql_master_send_query(mysql, q, length))
    DBUG_RETURN(1);
hf@deer.(none)'s avatar
hf@deer.(none) committed
299
  DBUG_RETURN((*mysql->methods->read_query_result)(mysql));
hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
300
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
301

hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
302 303
my_bool STDCALL mysql_master_send_query(MYSQL *mysql, const char *q,
					unsigned long length)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
304
{
hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
305 306 307 308 309 310
  MYSQL *master = mysql->master;
  DBUG_ENTER("mysql_master_send_query");
  if (!master->net.vio && !mysql_real_connect(master,0,0,0,0,0,0,0))
    DBUG_RETURN(1);
  mysql->last_used_con = master;
  DBUG_RETURN(simple_command(master, COM_QUERY, q, length, 1));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
311 312 313
}


hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
314 315 316
/* perform query on slave */
my_bool STDCALL mysql_slave_query(MYSQL *mysql, const char *q,
				  unsigned long length)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
317
{
hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
318 319 320
  DBUG_ENTER("mysql_slave_query");
  if (mysql_slave_send_query(mysql, q, length))
    DBUG_RETURN(1);
hf@deer.(none)'s avatar
hf@deer.(none) committed
321
  DBUG_RETURN((*mysql->methods->read_query_result)(mysql));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
322 323 324
}


hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
325 326
my_bool STDCALL mysql_slave_send_query(MYSQL *mysql, const char *q,
				   unsigned long length)
327
{
hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
328 329
  MYSQL* last_used_slave, *slave_to_use = 0;
  DBUG_ENTER("mysql_slave_send_query");
330

hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
331 332 333 334 335 336 337 338 339 340
  if ((last_used_slave = mysql->last_used_slave))
    slave_to_use = last_used_slave->next_slave;
  else
    slave_to_use = mysql->next_slave;
  /*
    Next_slave is always safe to use - we have a circular list of slaves
    if there are no slaves, mysql->next_slave == mysql
  */
  mysql->last_used_con = mysql->last_used_slave = slave_to_use;
  if (!slave_to_use->net.vio && !mysql_real_connect(slave_to_use, 0,0,0,
341
						    0,0,0,0))
hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
342 343 344
    DBUG_RETURN(1);
  DBUG_RETURN(simple_command(slave_to_use, COM_QUERY, q, length, 1));
}
345 346


hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
347 348 349
/* enable/disable parsing of all queries to decide
   if they go on master or slave */
void STDCALL mysql_enable_rpl_parse(MYSQL* mysql)
350
{
hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
351
  mysql->options.rpl_parse = 1;
352 353
}

hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
354
void STDCALL mysql_disable_rpl_parse(MYSQL* mysql)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
355
{
hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
356
  mysql->options.rpl_parse = 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
357 358
}

hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
359 360
/* get the value of the parse flag */
int STDCALL mysql_rpl_parse_enabled(MYSQL* mysql)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
361
{
hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
362
  return mysql->options.rpl_parse;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
363 364
}

hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
365 366
/*  enable/disable reads from master */
void STDCALL mysql_enable_reads_from_master(MYSQL* mysql)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
367
{
hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
368 369
  mysql->options.no_master_reads = 0;
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
370

hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
371
void STDCALL mysql_disable_reads_from_master(MYSQL* mysql)
372 373 374 375
{
  mysql->options.no_master_reads = 1;
}

peter@mysql.com's avatar
peter@mysql.com committed
376
/* get the value of the master read flag */
377
my_bool STDCALL mysql_reads_from_master_enabled(MYSQL* mysql)
378 379 380 381
{
  return !(mysql->options.no_master_reads);
}

382 383 384 385 386

/*
  We may get an error while doing replication internals.
  In this case, we add a special explanation to the original
  error
387
*/
388 389

static void expand_error(MYSQL* mysql, int error)
390 391
{
  char tmp[MYSQL_ERRMSG_SIZE];
392 393 394 395 396 397
  char *p;
  uint err_length;
  strmake(tmp, mysql->net.last_error, MYSQL_ERRMSG_SIZE-1);
  p = strmake(mysql->net.last_error, ER(error), MYSQL_ERRMSG_SIZE-1);
  err_length= (uint) (p - mysql->net.last_error);
  strmake(p, tmp, MYSQL_ERRMSG_SIZE-1 - err_length);
398 399 400
  mysql->net.last_errno = error;
}

401 402 403
/*
  This function assumes we have just called SHOW SLAVE STATUS and have
  read the given result and row
404
*/
405

406
static my_bool get_master(MYSQL* mysql, MYSQL_RES* res, MYSQL_ROW row)
407 408
{
  MYSQL* master;
409
  DBUG_ENTER("get_master");
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
410
  if (mysql_num_fields(res) < 3)
411
    DBUG_RETURN(1); /* safety */
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
412

413
  /* use the same username and password as the original connection */
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
414
  if (!(master = spawn_init(mysql, row[0], atoi(row[2]), 0, 0)))
415
    DBUG_RETURN(1);
416
  mysql->master = master;
417
  DBUG_RETURN(0);
418 419
}

420 421 422 423

/*
  Assuming we already know that mysql points to a master connection,
  retrieve all the slaves
424
*/
425

426
static my_bool get_slaves_from_master(MYSQL* mysql)
427
{
428 429
  MYSQL_RES* res = 0;
  MYSQL_ROW row;
430
  my_bool error = 1;
431
  int has_auth_info;
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
432
  int port_ind;
433
  DBUG_ENTER("get_slaves_from_master");
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
434

435 436 437
  if (!mysql->net.vio && !mysql_real_connect(mysql,0,0,0,0,0,0,0))
  {
    expand_error(mysql, CR_PROBE_MASTER_CONNECT);
438
    DBUG_RETURN(1);
439 440 441
  }

  if (mysql_query(mysql, "SHOW SLAVE HOSTS") ||
442
      !(res = mysql_store_result(mysql)))
443 444
  {
    expand_error(mysql, CR_PROBE_SLAVE_HOSTS);
445
    DBUG_RETURN(1);
446 447
  }

448
  switch (mysql_num_fields(res)) {
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
449 450 451 452 453 454 455 456
  case 5:
    has_auth_info = 0;
    port_ind=2;
    break;
  case 7:
    has_auth_info = 1;
    port_ind=4;
    break;
457 458 459 460 461 462 463 464 465 466 467
  default:
    goto err;
  }

  while ((row = mysql_fetch_row(res)))
  {
    MYSQL* slave;
    const char* tmp_user, *tmp_pass;

    if (has_auth_info)
    {
sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
468 469
      tmp_user = row[2];
      tmp_pass = row[3];
470 471 472 473 474 475 476
    }
    else
    {
      tmp_user = mysql->user;
      tmp_pass = mysql->passwd;
    }

sasha@mysql.sashanet.com's avatar
sasha@mysql.sashanet.com committed
477
    if (!(slave = spawn_init(mysql, row[1], atoi(row[port_ind]),
478
			     tmp_user, tmp_pass)))
479
      goto err;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
480

481 482 483 484 485 486
    /* Now add slave into the circular linked list */
    slave->next_slave = mysql->next_slave;
    mysql->next_slave = slave;
  }
  error = 0;
err:
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
487
  if (res)
488
    mysql_free_result(res);
489
  DBUG_RETURN(error);
490 491
}

492

493
my_bool STDCALL mysql_rpl_probe(MYSQL* mysql)
494
{
495
  MYSQL_RES *res= 0;
496
  MYSQL_ROW row;
497
  my_bool error= 1;
498 499
  DBUG_ENTER("mysql_rpl_probe");

500 501 502 503 504
  /*
    First determine the replication role of the server we connected to
    the most reliable way to do this is to run SHOW SLAVE STATUS and see
    if we have a non-empty master host. This is still not fool-proof -
    it is not a sin to have a master that has a dormant slave thread with
peter@mysql.com's avatar
peter@mysql.com committed
505
    a non-empty master host. However, it is more reliable to check
506
    for empty master than whether the slave thread is actually running
507
  */
508
  if (mysql_query(mysql, "SHOW SLAVE STATUS") ||
509
      !(res = mysql_store_result(mysql)))
510 511
  {
    expand_error(mysql, CR_PROBE_SLAVE_STATUS);
512
    DBUG_RETURN(1);
513
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
514

515 516 517 518 519 520
  row= mysql_fetch_row(res);
  /*
    Check master host for emptiness/NULL
    For MySQL 4.0 it's enough to check for row[0]
  */
  if (row && row[0] && *(row[0]))
521 522
  {
    /* this is a slave, ask it for the master */
523
    if (get_master(mysql, res, row) || get_slaves_from_master(mysql))
524 525 526 527 528
      goto err;
  }
  else
  {
    mysql->master = mysql;
529
    if (get_slaves_from_master(mysql))
530 531 532 533 534
      goto err;
  }

  error = 0;
err:
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
535
  if (res)
536
    mysql_free_result(res);
537
  DBUG_RETURN(error);
538 539 540
}


541 542 543 544 545 546 547 548 549 550
/*
  Make a not so fool-proof decision on where the query should go, to
  the master or the slave. Ideally the user should always make this
  decision himself with mysql_master_query() or mysql_slave_query().
  However, to be able to more easily port the old code, we support the
  option of an educated guess - this should work for most applications,
  however, it may make the wrong decision in some particular cases. If
  that happens, the user would have to change the code to call
  mysql_master_query() or mysql_slave_query() explicitly in the place
  where we have made the wrong decision
551
*/
552

553 554
enum mysql_rpl_type
STDCALL mysql_rpl_query_type(const char* q, int len)
555
{
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
556
  const char *q_end= q + len;
557
  for (; q < q_end; ++q)
558 559
  {
    char c;
560
    if (my_isalpha(&my_charset_latin1, (c= *q)))
561
    {
562
      switch (my_tolower(&my_charset_latin1,c)) {
563 564 565 566 567 568 569
      case 'i':  /* insert */
      case 'u':  /* update or unlock tables */
      case 'l':  /* lock tables or load data infile */
      case 'd':  /* drop or delete */
      case 'a':  /* alter */
	return MYSQL_RPL_MASTER;
      case 'c':  /* create or check */
570
	return my_tolower(&my_charset_latin1,q[1]) == 'h' ? MYSQL_RPL_ADMIN :
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
571
	  MYSQL_RPL_MASTER;
572
      case 's': /* select or show */
573
	return my_tolower(&my_charset_latin1,q[1]) == 'h' ? MYSQL_RPL_ADMIN :
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
574
	  MYSQL_RPL_SLAVE;
575 576 577
      case 'f': /* flush */
      case 'r': /* repair */
      case 'g': /* grant */
hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
578 579 580 581 582
	return MYSQL_RPL_ADMIN;
      default:
	return MYSQL_RPL_SLAVE;
      }
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
583
  }
hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
584
  return MYSQL_RPL_MASTER;		/* By default, send to master */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
585 586 587
}


hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
588 589 590 591
/**************************************************************************
  Connect to sql server
  If host == 0 then use localhost
**************************************************************************/
592

hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
593 594 595 596
#ifdef USE_OLD_FUNCTIONS
MYSQL * STDCALL
mysql_connect(MYSQL *mysql,const char *host,
	      const char *user, const char *passwd)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
597
{
hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
598 599
  MYSQL *res;
  mysql=mysql_init(mysql);			/* Make it thread safe */
600
  {
hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
601 602 603 604 605 606 607
    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);
608
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
609
}
hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
610
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
611

612

613 614 615 616 617 618 619 620 621 622 623 624
#ifdef CHECK_LICENSE
/*
  Check server side variable 'license'.
  If the variable does not exist or does not contain 'Commercial', 
  we're talking to non-commercial server from commercial client.
  SYNOPSIS
    check_license()
  RETURN VALUE
    0  success
   !0  network error or the server is not commercial.
       Error code is saved in mysql->net.last_errno.
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
625

626
static int check_license(MYSQL *mysql)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
627
{
628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
  MYSQL_ROW row;
  MYSQL_RES *res;
  NET *net= &mysql->net;
  static const char query[]= "SELECT @@license";
  static const char required_license[]= LICENSE;

  if (mysql_real_query(mysql, query, sizeof(query)-1))
  {
    if (net->last_errno == ER_UNKNOWN_SYSTEM_VARIABLE)
    {
      net->last_errno= CR_WRONG_LICENSE;
      sprintf(net->last_error, ER(net->last_errno), required_license);
    }
    return 1;
  }
  if (!(res= mysql_use_result(mysql)))
    return 1;
  row= mysql_fetch_row(res);
  /* 
    If no rows in result set, or column value is NULL (none of these
    two is ever true for server variables now), or column value
    mismatch, set wrong license error.
  */
  if (!net->last_errno &&
      (!row || !row[0] ||
       strncmp(row[0], required_license, sizeof(required_license))))
  {
    net->last_errno= CR_WRONG_LICENSE;
    sprintf(net->last_error, ER(net->last_errno), required_license);
  }
  mysql_free_result(res);
  return net->last_errno;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
660
}
661
#endif /* CHECK_LICENSE */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
662 663 664


/**************************************************************************
peter@mysql.com's avatar
peter@mysql.com committed
665
  Change user and database
bk@work.mysql.com's avatar
bk@work.mysql.com committed
666
**************************************************************************/
667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697
int cli_read_change_user_result(MYSQL *mysql, char *buff, const char *passwd)
{
  NET *net= &mysql->net;
  ulong pkt_length;

  pkt_length= net_safe_read(mysql);
  
  if (pkt_length == packet_error)
    return 1;

  if (pkt_length == 1 && net->read_pos[0] == 254 &&
      mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
  {
    /*
      By sending this very specific reply server asks us to send scrambled
      password in old format. The reply contains scramble_323.
    */
    scramble_323(buff, mysql->scramble, passwd);
    if (my_net_write(net, buff, SCRAMBLE_LENGTH_323 + 1) || net_flush(net))
    {
      net->last_errno= CR_SERVER_LOST;
      strmov(net->sqlstate, unknown_sqlstate);
      strmov(net->last_error,ER(net->last_errno));
      return 1;
    }
    /* Read what server thinks about out new auth message report */
    if (net_safe_read(mysql) == packet_error)
      return 1;
  }
  return 0;
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
698

699

peter@mysql.com's avatar
peter@mysql.com committed
700
my_bool	STDCALL mysql_change_user(MYSQL *mysql, const char *user,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
701 702
				  const char *passwd, const char *db)
{
peter@mysql.com's avatar
peter@mysql.com committed
703
  char buff[512],*end=buff;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
704 705 706 707 708 709
  DBUG_ENTER("mysql_change_user");

  if (!user)
    user="";
  if (!passwd)
    passwd="";
peter@mysql.com's avatar
peter@mysql.com committed
710

711
  /* Store user into the buffer */
peter@mysql.com's avatar
peter@mysql.com committed
712
  end=strmov(end,user)+1;
peter@mysql.com's avatar
peter@mysql.com committed
713

714 715
  /* write scrambled password according to server capabilities */
  if (passwd[0])
peter@mysql.com's avatar
peter@mysql.com committed
716
  {
717
    if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
peter@mysql.com's avatar
peter@mysql.com committed
718
    {
719 720 721
      *end++= SCRAMBLE_LENGTH;
      scramble(end, mysql->scramble, passwd);
      end+= SCRAMBLE_LENGTH;
peter@mysql.com's avatar
peter@mysql.com committed
722
    }
723
    else
724 725 726
    {
      scramble_323(end, mysql->scramble, passwd);
      end+= SCRAMBLE_LENGTH_323 + 1;
peter@mysql.com's avatar
peter@mysql.com committed
727
    }
peter@mysql.com's avatar
peter@mysql.com committed
728 729
  }
  else
730
    *end++= '\0';                               /* empty password */
peter@mysql.com's avatar
peter@mysql.com committed
731
  /* Add database if needed */
732
  end= strmov(end, db ? db : "") + 1;
peter@mysql.com's avatar
peter@mysql.com committed
733 734

  /* Write authentication package */
peter@mysql.com's avatar
peter@mysql.com committed
735
  simple_command(mysql,COM_CHANGE_USER, buff,(ulong) (end-buff),1);
peter@mysql.com's avatar
peter@mysql.com committed
736

737 738
  if ((*mysql->methods->read_change_user_result)(mysql, buff, passwd))
    DBUG_RETURN(1);
739
  /* Free old connect information */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
740 741 742 743
  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));

744
  /* alloc new connect information */
bk@work.mysql.com's avatar
bk@work.mysql.com committed
745 746 747 748 749 750
  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);
}

hf@deer.(none)'s avatar
hf@deer.(none) committed
751 752 753 754 755 756
#if defined(HAVE_GETPWUID) && defined(NO_GETPWUID_DECL)
struct passwd *getpwuid(uid_t);
char* getlogin(void);
#endif

#if defined(__NETWARE__)
757
/* Default to value of USER on NetWare, if unset use "UNKNOWN_USER" */
hf@deer.(none)'s avatar
hf@deer.(none) committed
758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803
void read_user_name(char *name)
{
  char *str=getenv("USER");
  strmake(name, str ? str : "UNKNOWN_USER", USERNAME_LENGTH);
}

#elif !defined(MSDOS) && ! defined(VMS) && !defined(__WIN__) && !defined(OS2)

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;
    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 */

void read_user_name(char *name)
{
  char *str=getenv("USER");		/* ODBC will send user variable */
  strmake(name,str ? str : "ODBC", USERNAME_LENGTH);
}

#endif

804
my_bool handle_local_infile(MYSQL *mysql, const char *net_filename)
hf@deer.(none)'s avatar
hf@deer.(none) committed
805 806 807 808
{
  my_bool result= 1;
  uint packet_length=MY_ALIGN(mysql->net.max_packet-16,IO_SIZE);
  NET *net= &mysql->net;
809 810
  int readcount;
  void *li_ptr;          /* pass state to local_infile functions */
serg@serg.mylan's avatar
serg@serg.mylan committed
811
  char *buf;		/* buffer to be filled by local_infile_read */
monty@mysql.com's avatar
monty@mysql.com committed
812
  struct st_mysql_options *options= &mysql->options;
813 814 815
  DBUG_ENTER("handle_local_infile");

  /* check that we've got valid callback functions */
monty@mysql.com's avatar
monty@mysql.com committed
816 817 818 819
  if (!(options->local_infile_init &&
	options->local_infile_read &&
	options->local_infile_end &&
	options->local_infile_error))
hf@deer.(none)'s avatar
hf@deer.(none) committed
820
  {
821
    /* if any of the functions is invalid, set the default */
monty@mysql.com's avatar
monty@mysql.com committed
822
    mysql_set_local_infile_default(mysql);
hf@deer.(none)'s avatar
hf@deer.(none) committed
823 824
  }

825
  /* copy filename into local memory and allocate read buffer */
monty@mysql.com's avatar
monty@mysql.com committed
826 827 828 829 830 831
  if (!(buf=my_malloc(packet_length, MYF(0))))
  {
    strmov(net->sqlstate, unknown_sqlstate);
    strmov(net->last_error, ER(net->last_errno=CR_OUT_OF_MEMORY));
    DBUG_RETURN(1);
  }
832 833

  /* initialize local infile (open file, usually) */
834 835
  if ((*options->local_infile_init)(&li_ptr, net_filename,
    options->local_infile_userdata))
hf@deer.(none)'s avatar
hf@deer.(none) committed
836 837 838 839
  {
    my_net_write(net,"",0);		/* Server needs one packet */
    net_flush(net);
    strmov(net->sqlstate, unknown_sqlstate);
monty@mysql.com's avatar
monty@mysql.com committed
840 841 842
    net->last_errno= (*options->local_infile_error)(li_ptr,
						    net->last_error,
						    sizeof(net->last_error)-1);
hf@deer.(none)'s avatar
hf@deer.(none) committed
843 844 845
    goto err;
  }

846
  /* read blocks of data from local infile callback */
monty@mysql.com's avatar
monty@mysql.com committed
847 848 849
  while ((readcount =
	  (*options->local_infile_read)(li_ptr, buf,
					packet_length)) > 0)
hf@deer.(none)'s avatar
hf@deer.(none) committed
850 851 852
  {
    if (my_net_write(net,buf,readcount))
    {
monty@mysql.com's avatar
monty@mysql.com committed
853 854
      DBUG_PRINT("error",
		 ("Lost connection to MySQL server during LOAD DATA of local file"));
hf@deer.(none)'s avatar
hf@deer.(none) committed
855 856 857 858 859 860
      strmov(net->sqlstate, unknown_sqlstate);
      net->last_errno=CR_SERVER_LOST;
      strmov(net->last_error,ER(net->last_errno));
      goto err;
    }
  }
861

hf@deer.(none)'s avatar
hf@deer.(none) committed
862 863 864 865 866 867 868 869
  /* Send empty packet to mark end of file */
  if (my_net_write(net,"",0) || net_flush(net))
  {
    strmov(net->sqlstate, unknown_sqlstate);
    net->last_errno=CR_SERVER_LOST;
    sprintf(net->last_error,ER(net->last_errno),errno);
    goto err;
  }
870

hf@deer.(none)'s avatar
hf@deer.(none) committed
871 872
  if (readcount < 0)
  {
monty@mysql.com's avatar
monty@mysql.com committed
873 874 875
    net->last_errno= (*options->local_infile_error)(li_ptr,
						    net->last_error,
						    sizeof(net->last_error)-1);
hf@deer.(none)'s avatar
hf@deer.(none) committed
876 877
    goto err;
  }
878

hf@deer.(none)'s avatar
hf@deer.(none) committed
879 880 881
  result=0;					/* Ok */

err:
882
  /* free up memory allocated with _init, usually */
monty@mysql.com's avatar
monty@mysql.com committed
883
  (*options->local_infile_end)(li_ptr);
hf@deer.(none)'s avatar
hf@deer.(none) committed
884
  DBUG_RETURN(result);
885 886 887
}


monty@mysql.com's avatar
monty@mysql.com committed
888 889 890 891 892 893
/****************************************************************************
  Default handlers for LOAD LOCAL INFILE
****************************************************************************/

typedef struct st_default_local_infile
{
894 895
  int fd;
  int error_num;
monty@mysql.com's avatar
monty@mysql.com committed
896
  const char *filename;
897 898 899 900
  char error_msg[LOCAL_INFILE_ERROR_LEN];
} default_local_infile_data;


monty@mysql.com's avatar
monty@mysql.com committed
901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918
/*
  Open file for LOAD LOCAL INFILE

  SYNOPSIS
    default_local_infile_init()
    ptr			Store pointer to internal data here
    filename		File name to open. This may be in unix format !


  NOTES
    Even if this function returns an error, the load data interface
    guarantees that default_local_infile_end() is called.

  RETURN
    0	ok
    1	error
*/

919 920
static int default_local_infile_init(void **ptr, const char *filename,
             void *userdata __attribute__ ((unused)))
921 922
{
  default_local_infile_data *data;
monty@mysql.com's avatar
monty@mysql.com committed
923
  char tmp_name[FN_REFLEN];
924 925

  if (!(*ptr= data= ((default_local_infile_data *)
monty@mysql.com's avatar
monty@mysql.com committed
926 927
		     my_malloc(sizeof(default_local_infile_data),  MYF(0)))))
    return 1; /* out of memory */
928 929 930

  data->error_msg[0]= 0;
  data->error_num=    0;
monty@mysql.com's avatar
monty@mysql.com committed
931
  data->filename= filename;
932

monty@mysql.com's avatar
monty@mysql.com committed
933 934
  fn_format(tmp_name, filename, "", "", MY_UNPACK_FILENAME);
  if ((data->fd = my_open(tmp_name, O_RDONLY, MYF(0))) < 0)
935
  {
monty@mysql.com's avatar
monty@mysql.com committed
936
    data->error_num= my_errno;
937
    my_snprintf(data->error_msg, sizeof(data->error_msg)-1,
monty@mysql.com's avatar
monty@mysql.com committed
938 939
                EE(EE_FILENOTFOUND), tmp_name, data->error_num);
    return 1;
940 941 942 943 944
  }
  return 0; /* ok */
}


monty@mysql.com's avatar
monty@mysql.com committed
945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960
/*
  Read data for LOAD LOCAL INFILE

  SYNOPSIS
    default_local_infile_read()
    ptr			Points to handle allocated by _init
    buf			Read data here
    buf_len		Ammount of data to read

  RETURN
    > 0		number of bytes read
    == 0	End of data
    < 0		Error
*/

static int default_local_infile_read(void *ptr, char *buf, uint buf_len)
961
{
monty@mysql.com's avatar
monty@mysql.com committed
962 963
  int count;
  default_local_infile_data*data = (default_local_infile_data *) ptr;
964

monty@mysql.com's avatar
monty@mysql.com committed
965 966 967 968 969 970 971 972
  if ((count= (int) my_read(data->fd, (byte *) buf, buf_len, MYF(0))) < 0)
  {
    data->error_num= EE_READ; /* the errmsg for not entire file read */
    my_snprintf(data->error_msg, sizeof(data->error_msg)-1,
		EE(EE_READ),
		data->filename, my_errno);
  }
  return count;
973 974 975
}


monty@mysql.com's avatar
monty@mysql.com committed
976 977 978 979 980 981 982 983 984 985 986 987
/*
  Read data for LOAD LOCAL INFILE

  SYNOPSIS
    default_local_infile_end()
    ptr			Points to handle allocated by _init
			May be NULL if _init failed!

  RETURN
*/

static void default_local_infile_end(void *ptr)
988
{
monty@mysql.com's avatar
monty@mysql.com committed
989 990
  default_local_infile_data *data= (default_local_infile_data *) ptr;
  if (data)					/* If not error on open */
991
  {
monty@mysql.com's avatar
monty@mysql.com committed
992 993 994
    if (data->fd >= 0)
      my_close(data->fd, MYF(MY_WME));
    my_free(ptr, MYF(MY_WME));
995 996 997 998
  }
}


monty@mysql.com's avatar
monty@mysql.com committed
999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013
/*
  Return error from LOAD LOCAL INFILE

  SYNOPSIS
    default_local_infile_end()
    ptr			Points to handle allocated by _init
			May be NULL if _init failed!
    error_msg		Store error text here
    error_msg_len	Max lenght of error_msg

  RETURN
    error message number
*/

static int
1014 1015
default_local_infile_error(void *ptr, char *error_msg, uint error_msg_len)
{
monty@mysql.com's avatar
monty@mysql.com committed
1016 1017 1018
  default_local_infile_data *data = (default_local_infile_data *) ptr;
  if (data)					/* If not error on open */
  {
1019 1020 1021
    strmake(error_msg, data->error_msg, error_msg_len);
    return data->error_num;
  }
monty@mysql.com's avatar
monty@mysql.com committed
1022 1023 1024
  /* This can only happen if we got error on malloc of handle */
  strmov(error_msg, ER(CR_OUT_OF_MEMORY));
  return CR_OUT_OF_MEMORY;
1025 1026 1027
}


monty@mysql.com's avatar
monty@mysql.com committed
1028
void
1029
mysql_set_local_infile_handler(MYSQL *mysql,
1030 1031
                               int (*local_infile_init)(void **, const char *,
                               void *),
1032
                               int (*local_infile_read)(void *, char *, uint),
monty@mysql.com's avatar
monty@mysql.com committed
1033
                               void (*local_infile_end)(void *),
1034 1035
                               int (*local_infile_error)(void *, char *, uint),
                               void *userdata)
1036
{
monty@mysql.com's avatar
monty@mysql.com committed
1037 1038 1039 1040
  mysql->options.local_infile_init=  local_infile_init;
  mysql->options.local_infile_read=  local_infile_read;
  mysql->options.local_infile_end=   local_infile_end;
  mysql->options.local_infile_error= local_infile_error;
1041
  mysql->options.local_infile_userdata = userdata;
1042 1043 1044
}


monty@mysql.com's avatar
monty@mysql.com committed
1045
void mysql_set_local_infile_default(MYSQL *mysql)
1046 1047 1048 1049 1050
{
  mysql->options.local_infile_init=  default_local_infile_init;
  mysql->options.local_infile_read=  default_local_infile_read;
  mysql->options.local_infile_end=   default_local_infile_end;
  mysql->options.local_infile_error= default_local_infile_error;
hf@deer.(none)'s avatar
hf@deer.(none) committed
1051 1052 1053
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
1054
/**************************************************************************
1055 1056
  Do a query. If query returned rows, free old rows.
  Read data by mysql_store_result or by repeat call of mysql_fetch_row
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1057 1058 1059 1060 1061 1062 1063 1064
**************************************************************************/

int STDCALL
mysql_query(MYSQL *mysql, const char *query)
{
  return mysql_real_query(mysql,query, (uint) strlen(query));
}

hf@deer.(none)'s avatar
hf@deer.(none) committed
1065

1066
static MYSQL* spawn_init(MYSQL* parent, const char* host,
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1067 1068
			 unsigned int port, const char* user,
			 const char* passwd)
1069 1070
{
  MYSQL* child;
1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086
  DBUG_ENTER("spawn_init");
  if (!(child= mysql_init(0)))
    DBUG_RETURN(0);

  child->options.user= my_strdup((user) ? user :
				 (parent->user ? parent->user :
				  parent->options.user), MYF(0));
  child->options.password= my_strdup((passwd) ? passwd :
				     (parent->passwd ?
				      parent->passwd :
				      parent->options.password), MYF(0));
  child->options.port= port;
  child->options.host= my_strdup((host) ? host :
				 (parent->host ?
				  parent->host :
				  parent->options.host), MYF(0));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1087
  if (parent->db)
1088
    child->options.db= my_strdup(parent->db, MYF(0));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1089
  else if (parent->options.db)
1090
    child->options.db= my_strdup(parent->options.db, MYF(0));
1091

1092 1093 1094 1095 1096 1097
  /*
    rpl_pivot is set to 1 in mysql_init();  Reset it as we are not doing
    replication here
  */
  child->rpl_pivot= 0;
  DBUG_RETURN(child);
1098 1099 1100 1101 1102
}


int
STDCALL mysql_set_master(MYSQL* mysql, const char* host,
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1103 1104
			 unsigned int port, const char* user,
			 const char* passwd)
1105 1106 1107
{
  if (mysql->master != mysql && !mysql->master->rpl_pivot)
    mysql_close(mysql->master);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1108
  if (!(mysql->master = spawn_init(mysql, host, port, user, passwd)))
1109 1110 1111 1112
    return 1;
  return 0;
}

1113

1114 1115
int
STDCALL mysql_add_slave(MYSQL* mysql, const char* host,
1116 1117 1118
			unsigned int port,
			const char* user,
			const char* passwd)
1119 1120
{
  MYSQL* slave;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1121
  if (!(slave = spawn_init(mysql, host, port, user, passwd)))
1122 1123 1124 1125 1126 1127
    return 1;
  slave->next_slave = mysql->next_slave;
  mysql->next_slave = slave;
  return 0;
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1128
/**************************************************************************
1129
  Return next field of the query results
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1130 1131 1132 1133 1134 1135 1136 1137 1138 1139
**************************************************************************/

MYSQL_FIELD * STDCALL
mysql_fetch_field(MYSQL_RES *result)
{
  if (result->current_field >= result->field_count)
    return(NULL);
  return &result->fields[result->current_field++];
}

1140 1141 1142 1143 1144 1145 1146

/**************************************************************************
  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.
**************************************************************************/

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1147 1148 1149
ulong * STDCALL
mysql_fetch_lengths(MYSQL_RES *res)
{
1150
  MYSQL_ROW column;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1151 1152 1153 1154

  if (!(column=res->current_row))
    return 0;					/* Something is wrong */
  if (res->data)
1155
    (*res->methods->fetch_lengths)(res->lengths, column, res->field_count);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1156 1157 1158
  return res->lengths;
}

1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175

/**************************************************************************
  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(%ld)",(long) row));
  if (result->data)
    for (tmp=result->data->data; row-- && tmp ; tmp = tmp->next) ;
  result->current_row=0;
  result->data_cursor = tmp;
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
1176
/*************************************************************************
1177 1178 1179
  put the row or field cursor one a position one got from mysql_row_tell()
  This doesn't restore any data. The next mysql_fetch_row or
  mysql_fetch_field will return the next row or field after the last used
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199
*************************************************************************/

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;
}

1200

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1201
/*****************************************************************************
1202
  List all databases
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218
*****************************************************************************/

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));
}


/*****************************************************************************
1219 1220
  List all tables in a database
  If wild is given then only the tables matching wild is returned
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234
*****************************************************************************/

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));
}

1235

1236
MYSQL_FIELD *cli_list_fields(MYSQL *mysql)
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1237 1238 1239 1240 1241 1242
{
  MYSQL_DATA *query;
  if (!(query= cli_read_rows(mysql,(MYSQL_FIELD*) 0, 
			     protocol_41(mysql) ? 8 : 6)))
    return NULL;

1243
  mysql->field_count= (uint) query->rows;
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1244
  return unpack_fields(query,&mysql->field_alloc,
1245
		       mysql->field_count, 1, mysql->server_capabilities);
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1246 1247 1248
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
1249
/**************************************************************************
1250 1251 1252 1253
  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"
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1254 1255 1256
**************************************************************************/

MYSQL_RES * STDCALL
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1257
mysql_list_fields(MYSQL *mysql, const char *table, const char *wild)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1258
{
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1259 1260
  MYSQL_RES   *result;
  MYSQL_FIELD *fields;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1261 1262 1263 1264 1265
  char	     buff[257],*end;
  DBUG_ENTER("mysql_list_fields");
  DBUG_PRINT("enter",("table: '%s'  wild: '%s'",table,wild ? wild : ""));

  end=strmake(strmake(buff, table,128)+1,wild ? wild : "",128);
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1266
  free_old_query(mysql);
1267
  if (simple_command(mysql,COM_FIELD_LIST,buff,(ulong) (end-buff),1) ||
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1268
      !(fields= (*mysql->methods->list_fields)(mysql)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1269 1270 1271 1272 1273
    DBUG_RETURN(NULL);

  if (!(result = (MYSQL_RES *) my_malloc(sizeof(MYSQL_RES),
					 MYF(MY_WME | MY_ZEROFILL))))
    DBUG_RETURN(NULL);
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1274

1275
  result->methods= mysql->methods;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1276 1277
  result->field_alloc=mysql->field_alloc;
  mysql->fields=0;
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1278 1279
  result->field_count = mysql->field_count;
  result->fields= fields;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299
  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);
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1300 1301
  if (!(fields = (*mysql->methods->read_rows)(mysql,(MYSQL_FIELD*) 0,
					      protocol_41(mysql) ? 7 : 5)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1302 1303
    DBUG_RETURN(NULL);
  if (!(mysql->fields=unpack_fields(fields,&mysql->field_alloc,field_count,0,
1304
				    mysql->server_capabilities)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1305 1306 1307 1308 1309 1310
    DBUG_RETURN(0);
  mysql->status=MYSQL_STATUS_GET_RESULT;
  mysql->field_count=field_count;
  DBUG_RETURN(mysql_store_result(mysql));
}

1311

1312
#ifdef USE_OLD_FUNCTIONS
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1313 1314 1315 1316 1317
int  STDCALL
mysql_create_db(MYSQL *mysql, const char *db)
{
  DBUG_ENTER("mysql_createdb");
  DBUG_PRINT("enter",("db: %s",db));
1318
  DBUG_RETURN(simple_command(mysql,COM_CREATE_DB,db, (ulong) strlen(db),0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1319 1320 1321 1322 1323 1324 1325 1326
}


int  STDCALL
mysql_drop_db(MYSQL *mysql, const char *db)
{
  DBUG_ENTER("mysql_drop_db");
  DBUG_PRINT("enter",("db: %s",db));
1327
  DBUG_RETURN(simple_command(mysql,COM_DROP_DB,db,(ulong) strlen(db),0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1328
}
1329
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348


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));
}

1349

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1350 1351 1352
int STDCALL
mysql_kill(MYSQL *mysql,ulong pid)
{
1353
  char buff[4];
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1354 1355
  DBUG_ENTER("mysql_kill");
  int4store(buff,pid);
1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366
  DBUG_RETURN(simple_command(mysql,COM_PROCESS_KILL,buff,sizeof(buff),0));
}


int STDCALL
mysql_set_server_option(MYSQL *mysql, enum enum_mysql_set_option option)
{
  char buff[2];
  DBUG_ENTER("mysql_set_server_option");
  int2store(buff, (uint) option);
  DBUG_RETURN(simple_command(mysql, COM_SET_OPTION, buff, sizeof(buff), 0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1367 1368 1369 1370 1371 1372 1373 1374 1375 1376
}


int STDCALL
mysql_dump_debug_info(MYSQL *mysql)
{
  DBUG_ENTER("mysql_dump_debug_info");
  DBUG_RETURN(simple_command(mysql,COM_DEBUG,0,0,0));
}

1377

konstantin@oak.local's avatar
konstantin@oak.local committed
1378
const char *cli_read_statistics(MYSQL *mysql)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1379 1380 1381 1382
{
  mysql->net.read_pos[mysql->packet_length]=0;	/* End of stat string */
  if (!mysql->net.read_pos[0])
  {
1383
    strmov(mysql->net.sqlstate, unknown_sqlstate);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1384 1385 1386 1387
    mysql->net.last_errno=CR_WRONG_HOST_INFO;
    strmov(mysql->net.last_error, ER(mysql->net.last_errno));
    return mysql->net.last_error;
  }
hf@deer.(none)'s avatar
hf@deer.(none) committed
1388 1389 1390
  return (char*) mysql->net.read_pos;
}

1391

hf@deer.(none)'s avatar
hf@deer.(none) committed
1392 1393 1394 1395 1396 1397
const char * STDCALL
mysql_stat(MYSQL *mysql)
{
  DBUG_ENTER("mysql_stat");
  if (simple_command(mysql,COM_STATISTICS,0,0,0))
    return mysql->net.last_error;
konstantin@oak.local's avatar
konstantin@oak.local committed
1398
  DBUG_RETURN((*mysql->methods->read_statistics)(mysql));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409
}


int STDCALL
mysql_ping(MYSQL *mysql)
{
  DBUG_ENTER("mysql_ping");
  DBUG_RETURN(simple_command(mysql,COM_PING,0,0,0));
}


monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1410
const char * STDCALL
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1411 1412 1413 1414 1415 1416
mysql_get_server_info(MYSQL *mysql)
{
  return((char*) mysql->server_version);
}


1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445
/*
  Get version number for server in a form easy to test on

  SYNOPSIS
    mysql_get_server_version()
    mysql		Connection

  EXAMPLE
    4.1.0-alfa ->  40100
  
  NOTES
    We will ensure that a newer server always has a bigger number.

  RETURN
   Signed number > 323000
*/

ulong STDCALL
mysql_get_server_version(MYSQL *mysql)
{
  uint major, minor, version;
  char *pos= mysql->server_version, *end_pos;
  major=   (uint) strtoul(pos, &end_pos, 10);	pos=end_pos+1;
  minor=   (uint) strtoul(pos, &end_pos, 10);	pos=end_pos+1;
  version= (uint) strtoul(pos, &end_pos, 10);
  return (ulong) major*10000L+(ulong) (minor*100+version);
}


monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1446
const char * STDCALL
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458
mysql_get_host_info(MYSQL *mysql)
{
  return(mysql->host_info);
}


uint STDCALL
mysql_get_proto_info(MYSQL *mysql)
{
  return (mysql->protocol_version);
}

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1459
const char * STDCALL
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1460 1461 1462 1463 1464
mysql_get_client_info(void)
{
  return (char*) MYSQL_SERVER_VERSION;
}

monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
1465 1466 1467 1468 1469
ulong STDCALL mysql_get_client_version(void)
{
  return MYSQL_VERSION_ID;
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484
my_bool STDCALL mysql_eof(MYSQL_RES *res)
{
  return res->eof;
}

MYSQL_FIELD * STDCALL mysql_fetch_field_direct(MYSQL_RES *res,uint fieldnr)
{
  return &(res)->fields[fieldnr];
}

MYSQL_FIELD * STDCALL mysql_fetch_fields(MYSQL_RES *res)
{
  return (res)->fields;
}

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1485
MYSQL_ROW_OFFSET STDCALL mysql_row_tell(MYSQL_RES *res)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1486 1487 1488 1489
{
  return res->data_cursor;
}

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1490
MYSQL_FIELD_OFFSET STDCALL mysql_field_tell(MYSQL_RES *res)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1491 1492 1493 1494 1495 1496 1497 1498
{
  return (res)->current_field;
}

/* MYSQL */

unsigned int STDCALL mysql_field_count(MYSQL *mysql)
{
1499
  return mysql->last_used_con->field_count;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1500 1501 1502 1503
}

my_ulonglong STDCALL mysql_affected_rows(MYSQL *mysql)
{
1504
  return mysql->last_used_con->affected_rows;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1505 1506 1507 1508
}

my_ulonglong STDCALL mysql_insert_id(MYSQL *mysql)
{
1509
  return mysql->last_used_con->insert_id;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1510 1511
}

1512 1513 1514 1515 1516
const char *STDCALL mysql_sqlstate(MYSQL *mysql)
{
  return mysql->net.sqlstate;
}

1517 1518 1519 1520 1521
uint STDCALL mysql_warning_count(MYSQL *mysql)
{
  return mysql->warning_count;
}

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
1522
const char *STDCALL mysql_info(MYSQL *mysql)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1523
{
1524
  return mysql->info;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547
}

ulong STDCALL mysql_thread_id(MYSQL *mysql)
{
  return (mysql)->thread_id;
}

const char * STDCALL mysql_character_set_name(MYSQL *mysql)
{
  return mysql->charset->name;
}


uint STDCALL mysql_thread_safe(void)
{
#ifdef THREAD
  return 1;
#else
  return 0;
#endif
}

/****************************************************************************
1548
  Some support functions
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1549 1550
****************************************************************************/

1551 1552 1553 1554 1555 1556 1557 1558 1559
/*
  Functions called my my_net_init() to set some application specific variables
*/

void my_net_local_init(NET *net)
{
  net->max_packet=   (uint) net_buffer_length;
  net->read_timeout= (uint) net_read_timeout;
  net->write_timeout=(uint) net_write_timeout;
1560
  net->retry_count=  1;
1561 1562 1563
  net->max_packet_size= max(net_buffer_length, max_allowed_packet);
}

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1564
/*
1565 1566 1567
  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
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599
*/

ulong STDCALL
mysql_escape_string(char *to,const char *from,ulong length)
{
  return mysql_sub_escape_string(default_charset_info,to,from,length);
}

ulong STDCALL
mysql_real_escape_string(MYSQL *mysql, char *to,const char *from,
			 ulong length)
{
  return mysql_sub_escape_string(mysql->charset,to,from,length);
}


static ulong
mysql_sub_escape_string(CHARSET_INFO *charset_info, char *to,
			const char *from, ulong length)
{
  const char *to_start=to;
  const char *end;
#ifdef USE_MB
  my_bool use_mb_flag=use_mb(charset_info);
#endif
  for (end=from+length; from != end ; from++)
  {
#ifdef USE_MB
    int l;
    if (use_mb_flag && (l = my_ismbchar(charset_info, from, end)))
    {
      while (l--)
1600
	*to++ = *from++;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743
      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(MYSQL *mysql,
			 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;
#ifdef USE_MB
  my_bool use_mb_flag=use_mb(mysql->charset);
#endif

  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 (use_mb_flag && (l = my_ismbchar(mysql->charset, 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;
}

void STDCALL
myodbc_remove_escape(MYSQL *mysql,char *name)
{
  char *to;
#ifdef USE_MB
  my_bool use_mb_flag=use_mb(mysql->charset);
  char *end;
  LINT_INIT(end);
  if (use_mb_flag)
    for (end=name; *end ; end++) ;
#endif

  for (to=name ; *name ; name++)
  {
#ifdef USE_MB
    int l;
    if (use_mb_flag && (l = my_ismbchar( mysql->charset, name , end ) ) )
    {
      while (l--)
	*to++ = *name++;
      name--;
      continue;
    }
#endif
    if (*name == '\\' && name[1])
      name++;
    *to++= *name;
  }
  *to=0;
}
1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758

/********************************************************************

 Implementation of new client-server prototypes for 4.1 version
 starts from here ..

 mysql_* are real prototypes used by applications

*********************************************************************/

/********************************************************************
 Misc Utility functions
********************************************************************/

/*
1759
  Set the internal stmt error messages
1760 1761
*/

1762 1763
static void set_stmt_error(MYSQL_STMT * stmt, int errcode,
			   const char *sqlstate)
1764 1765
{
  DBUG_ENTER("set_stmt_error");
1766
  DBUG_PRINT("enter", ("error: %d '%s'", errcode, ER(errcode)));
1767 1768
  DBUG_ASSERT(stmt != 0);

1769 1770
  stmt->last_errno= errcode;
  strmov(stmt->last_error, ER(errcode));
1771
  strmov(stmt->sqlstate, sqlstate);
1772 1773 1774 1775

  DBUG_VOID_RETURN;
}

1776

1777
/*
1778
  Copy error message to statement handler
1779 1780
*/

hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1781 1782
void set_stmt_errmsg(MYSQL_STMT * stmt, const char *err, int errcode,
		     const char *sqlstate)
1783 1784
{
  DBUG_ENTER("set_stmt_error_msg");
1785
  DBUG_PRINT("enter", ("error: %d/%s '%s'", errcode, sqlstate, err));
1786 1787
  DBUG_ASSERT(stmt != 0);

1788
  stmt->last_errno= errcode;
1789
  if (err && err[0])
1790
    strmov(stmt->last_error, err);
1791
  strmov(stmt->sqlstate, sqlstate);
1792 1793 1794 1795

  DBUG_VOID_RETURN;
}

1796

1797 1798

/*
1799
  Reallocate the NET package to be at least of 'length' bytes
1800

1801 1802 1803 1804
  SYNPOSIS
   my_realloc_str()
   net			The NET structure to modify
   int length		Ensure that net->buff is at least this big
1805

1806 1807 1808
  RETURN VALUES
  0	ok
  1	Error
1809 1810 1811

*/

1812
static my_bool my_realloc_str(NET *net, ulong length)
1813
{
1814 1815 1816 1817
  ulong buf_length= (ulong) (net->write_pos - net->buff);
  my_bool res=0;
  DBUG_ENTER("my_realloc_str");
  if (buf_length + length > net->max_packet)
1818
  {
1819 1820
    res= net_realloc(net, buf_length + length);
    net->write_pos= net->buff+ buf_length;
1821
  }
1822
  DBUG_RETURN(res);
1823 1824 1825
}

/********************************************************************
1826
  Prepare related implementations
1827 1828
********************************************************************/

1829 1830 1831 1832
static int stmt_read_row_unbuffered(MYSQL_STMT *stmt, unsigned char **row);
static int stmt_read_row_buffered(MYSQL_STMT *stmt, unsigned char **row);
static int stmt_read_row_no_data(MYSQL_STMT *stmt, unsigned char **row);

1833
/*
1834
  Read the prepared statement results ..
1835 1836 1837 1838 1839 1840 1841 1842

  NOTE
    This is only called for connection to servers that supports
    prepared statements (and thus the 4.1 protocol)

  RETURN VALUES
    0	ok
    1	error
1843 1844
*/

1845
my_bool cli_read_prepare_result(MYSQL *mysql, MYSQL_STMT *stmt)
1846 1847
{
  uchar *pos;
1848
  uint field_count, param_count;
1849
  MYSQL_DATA *fields_data;
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1850
  DBUG_ENTER("read_prepare_result");
1851

1852
  mysql= mysql->last_used_con;
1853
  if (net_safe_read(mysql) == packet_error)
1854
    DBUG_RETURN(1);
1855

1856
  pos= (uchar*) mysql->net.read_pos;
1857 1858 1859
  stmt->stmt_id= uint4korr(pos+1); pos+= 5;
  field_count=   uint2korr(pos);   pos+= 2;
  param_count=   uint2korr(pos);   pos+= 2;
1860

1861 1862 1863 1864 1865 1866 1867 1868 1869 1870
  if (param_count != 0)
  {
    MYSQL_DATA *param_data;

    /* skip parameters data: we don't support it yet */
    if (!(param_data= (*mysql->methods->read_rows)(mysql, (MYSQL_FIELD*)0, 7)))
      DBUG_RETURN(1);
    free_rows(param_data);
  }

1871 1872 1873 1874
  if (field_count != 0)
  {
    if (!(mysql->server_status & SERVER_STATUS_AUTOCOMMIT))
      mysql->server_status|= SERVER_STATUS_IN_TRANS;
1875

1876
    mysql->extra_info= net_field_length_ll(&pos);
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1877
    if (!(fields_data= (*mysql->methods->read_rows)(mysql,(MYSQL_FIELD*)0,7)))
1878 1879 1880 1881 1882 1883
      DBUG_RETURN(1);
    if (!(stmt->fields= unpack_fields(fields_data,&stmt->mem_root,
				      field_count,0,
				      mysql->server_capabilities)))
      DBUG_RETURN(1);
  }
1884 1885
  stmt->field_count=  (uint) field_count;
  stmt->param_count=  (ulong) param_count;
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
1886

1887 1888 1889
  DBUG_RETURN(0);
}

1890 1891 1892 1893
#ifdef HAVE_DEPRECATED_411_API
MYSQL_STMT * STDCALL mysql_prepare(MYSQL *mysql, const char *query,
                                   unsigned long query_length)
{
1894
  MYSQL_STMT *stmt;
1895 1896
  DBUG_ENTER("mysql_prepare");

1897
  stmt= mysql_stmt_init(mysql);
1898 1899
  if (stmt && mysql_stmt_prepare(stmt, query, query_length))
  {
1900
    mysql_stmt_close(stmt);
1901 1902 1903 1904 1905
    DBUG_RETURN(0);
  }
  DBUG_RETURN(stmt);
}
#endif
1906 1907

/*
1908 1909 1910 1911
  Allocate memory and init prepared statement structure
  SYNOPSIS
    mysql_stmt_init()
    mysql connection handle
1912

1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930
  RETURN VALUE
    statement structure upon success and NULL if out of
    memory
*/

MYSQL_STMT * STDCALL
mysql_stmt_init(MYSQL *mysql)
{
  MYSQL_STMT *stmt;
  DBUG_ENTER("mysql_stmt_init");

  if (!(stmt= (MYSQL_STMT *) my_malloc(sizeof(MYSQL_STMT),
                                       MYF(MY_WME | MY_ZEROFILL))))
  {
    set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate);
    DBUG_RETURN(0);
  }

1931
  init_alloc_root(&stmt->mem_root, 2048, 2048);
1932 1933
  init_alloc_root(&stmt->result.alloc, 4096, 4096);
  stmt->result.alloc.min_malloc= sizeof(MYSQL_ROWS);
1934 1935
  mysql->stmts= list_add(mysql->stmts, &stmt->list);
  stmt->list.data= stmt;
1936
  stmt->state= MYSQL_STMT_INIT_DONE;
1937
  stmt->mysql= mysql;
1938 1939
  stmt->read_row_func= stmt_read_row_no_data;
  /* The rest of statement members was bzeroed inside malloc */
1940 1941 1942 1943 1944

  DBUG_RETURN(stmt);
}

/*
1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957
  Prepare server side statement with query:
  SYNOPSIS
    mysql_stmt_prepare()
    query  statement to prepare
    length statement length

  DESCRIPTION
  - if this is a re-prepare of the statement, first close previous data 
    structure on the server and free old statement data
  - send the query to server and get back number of placeholders,
    number of columns in result set (if any), and result set metadata.
    At the same time allocate memory for input and output parameters 
    to have less checks in mysql_stmt_bind_{param, result}.
1958

1959 1960 1961
  RETURN VALUES
    0  success
   !0  error 
1962 1963
*/

1964

1965 1966
int STDCALL
mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length)
1967
{
1968 1969 1970
  MYSQL *mysql= stmt->mysql;
  DBUG_ENTER("mysql_stmt_prepare");

1971
  if (!mysql)
1972
  {
1973 1974
    /* mysql can be reset in mysql_close called from mysql_reconnect */
    set_stmt_error(stmt, CR_SERVER_LOST, unknown_sqlstate);
1975
    DBUG_RETURN(1);
1976
  }
1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008

  if ((int) stmt->state > (int) MYSQL_STMT_INIT_DONE)
  {
    /* This is second prepare with another statement */
    char buff[4];

    mysql_stmt_free_result(stmt);
    /*
      These members must be reset for API to 
      function in case of error or misuse.
    */
    stmt->bind_param_done= stmt->bind_result_done= FALSE;
    stmt->param_count= stmt->field_count= 0;
    stmt->last_errno= 0;
    stmt->last_error[0]= '\0';
    free_root(&stmt->mem_root, MYF(MY_KEEP_PREALLOC));

    int4store(buff, stmt->stmt_id);
    /*
      If there was a 'use' result from another statement, or from
      mysql_use_result it won't be freed in mysql_stmt_free_result and
      we should get 'Commands out of sync' here.
    */
    if (simple_command(mysql, COM_CLOSE_STMT, buff, 4, 1))
    {
      set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno,
                      mysql->net.sqlstate);
      DBUG_RETURN(1);
    }
    stmt->state= MYSQL_STMT_INIT_DONE;
  }

2009 2010
  if (simple_command(mysql, COM_PREPARE, query, length, 1))
  {
2011 2012 2013
    set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno,
                    mysql->net.sqlstate);
    DBUG_RETURN(1);
2014 2015
  }

hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
2016
  if ((*mysql->methods->read_prepare_result)(mysql, stmt))
2017 2018 2019
  {
    set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno,
                    mysql->net.sqlstate);
2020
    DBUG_RETURN(1);
2021
  }
hf@deer.(none)'s avatar
hf@deer.(none) committed
2022

2023 2024 2025 2026 2027 2028
  /*
    alloc_root will return valid address even in case param_count 
    and field_count are zero. Thus we should never rely on stmt->bind
    or stmt->params when checking for existence of placeholders or
    result set.
  */
hf@deer.(none)'s avatar
hf@deer.(none) committed
2029 2030 2031 2032 2033 2034
  if (!(stmt->params= (MYSQL_BIND *) alloc_root(&stmt->mem_root,
						sizeof(MYSQL_BIND)*
                                                (stmt->param_count + 
                                                 stmt->field_count))))
  {
    set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate);
2035
    DBUG_RETURN(1);
hf@deer.(none)'s avatar
hf@deer.(none) committed
2036 2037
  }
  stmt->bind= stmt->params + stmt->param_count;
2038
  stmt->state= MYSQL_STMT_PREPARE_DONE;
2039
  DBUG_PRINT("info", ("Parameter count: %ld", stmt->param_count));
2040
  DBUG_RETURN(0);
2041 2042
}

2043 2044
/*
  Get the execute query meta information for non-select 
2045
  statements.
2046 2047
*/

2048
static unsigned int alloc_stmt_fields(MYSQL_STMT *stmt)
2049
{
2050 2051
  MYSQL_FIELD *fields, *field, *end;
  MEM_ROOT *alloc= &stmt->mem_root;
2052
  MYSQL *mysql= stmt->mysql->last_used_con;
2053
  
2054
  stmt->field_count= mysql->field_count;
2055
  
2056 2057 2058 2059 2060
  /*
    Get the field information for non-select statements 
    like SHOW and DESCRIBE commands
  */
  if (!(stmt->fields= (MYSQL_FIELD *) alloc_root(alloc, 
2061 2062
						 sizeof(MYSQL_FIELD) *
						 stmt->field_count)) || 
2063
      !(stmt->bind= (MYSQL_BIND *) alloc_root(alloc, 
2064 2065
					      sizeof(MYSQL_BIND) *
					      stmt->field_count)))
2066
    return 0;
2067
  
2068
  for (fields= mysql->fields, end= fields+stmt->field_count, 
2069
	 field= stmt->fields;
2070 2071 2072 2073 2074 2075 2076
       field && fields < end; fields++, field++)
  {
    field->db       = strdup_root(alloc,fields->db);
    field->table    = strdup_root(alloc,fields->table);
    field->org_table= strdup_root(alloc,fields->org_table);
    field->name     = strdup_root(alloc,fields->name);
    field->org_name = strdup_root(alloc,fields->org_name);
2077
    field->charsetnr= fields->charsetnr;
2078 2079 2080 2081 2082 2083 2084
    field->length   = fields->length;
    field->type     = fields->type;
    field->flags    = fields->flags;
    field->decimals = fields->decimals;
    field->def      = fields->def ? strdup_root(alloc,fields->def): 0;
    field->max_length= 0;
  }
2085 2086
  return stmt->field_count;
}
2087 2088 2089

/*
  Returns prepared meta information in the form of resultset
2090
  to client.
2091 2092 2093
*/

MYSQL_RES * STDCALL
2094
mysql_stmt_result_metadata(MYSQL_STMT *stmt)
2095 2096
{
  MYSQL_RES *result;
2097
  DBUG_ENTER("mysql_stmt_result_metadata");
2098
  
2099 2100 2101 2102 2103 2104 2105 2106 2107
  /*
    stmt->fields is only defined if stmt->field_count is not null;
    stmt->field_count is initialized in prepare.
  */
  if (!stmt->field_count)
     DBUG_RETURN(0);

  if (!(result=(MYSQL_RES*) my_malloc(sizeof(*result),
                                      MYF(MY_WME | MY_ZEROFILL))))
2108
  {
2109 2110 2111
    set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate);
    DBUG_RETURN(0);
  }
2112

2113 2114
  result->methods=	stmt->mysql->methods;
  result->eof=		1;                      /* Marker for buffered */
2115 2116
  result->fields=	stmt->fields;
  result->field_count=	stmt->field_count;
2117
  /* The rest of members of 'result' was bzeroed inside malloc */
2118
  DBUG_RETURN(result);
2119 2120
}

2121 2122 2123 2124 2125 2126
/*
  Returns parameter columns meta information in the form of 
  resultset.
*/

MYSQL_RES * STDCALL
2127
mysql_stmt_param_metadata(MYSQL_STMT *stmt)
2128
{
2129
  DBUG_ENTER("mysql_stmt_param_metadata");
2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141
  
  if (!stmt->param_count)
    DBUG_RETURN(0);

  /*
    TODO: Fix this when server sends the information. 
    Till then keep a dummy prototype 
  */
  DBUG_RETURN(0); 
}


2142 2143 2144 2145
/********************************************************************
 Prepare-execute, and param handling
*********************************************************************/

2146 2147
/****************************************************************************
  Functions to store parameter data from a prepared statement.
2148

2149
  All functions have the following characteristics:
2150

2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163
  SYNOPSIS
    store_param_xxx()
    net			MySQL NET connection
    param		MySQL bind param

  RETURN VALUES
    0	ok
    1	Error	(Can't alloc net->buffer)
****************************************************************************/

static void store_param_tinyint(NET *net, MYSQL_BIND *param)
{
  *(net->write_pos++)= (uchar) *param->buffer;
2164 2165
}

2166 2167 2168 2169 2170 2171
static void store_param_short(NET *net, MYSQL_BIND *param)
{
  short value= *(short*) param->buffer;
  int2store(net->write_pos,value);
  net->write_pos+=2;
}
2172

2173
static void store_param_int32(NET *net, MYSQL_BIND *param)
2174
{
2175 2176 2177 2178
  int32 value= *(int32*) param->buffer;
  int4store(net->write_pos,value);
  net->write_pos+=4;
}
2179

2180 2181 2182 2183 2184 2185
static void store_param_int64(NET *net, MYSQL_BIND *param)
{
  longlong value= *(longlong*) param->buffer;
  int8store(net->write_pos,value);
  net->write_pos+= 8;
}
2186

2187 2188 2189 2190 2191
static void store_param_float(NET *net, MYSQL_BIND *param)
{
  float value= *(float*) param->buffer;
  float4store(net->write_pos, value);
  net->write_pos+= 4;
2192 2193
}

2194 2195 2196 2197 2198 2199
static void store_param_double(NET *net, MYSQL_BIND *param)
{
  double value= *(double*) param->buffer;
  float8store(net->write_pos, value);
  net->write_pos+= 8;
}
2200

2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265
static void store_param_time(NET *net, MYSQL_BIND *param)
{
  MYSQL_TIME *tm= (MYSQL_TIME *) param->buffer;
  char buff[15], *pos;
  uint length;

  pos= buff+1;
  pos[0]= tm->neg ? 1: 0;
  int4store(pos+1, tm->day);
  pos[5]= (uchar) tm->hour;
  pos[6]= (uchar) tm->minute;
  pos[7]= (uchar) tm->second;
  int4store(pos+8, tm->second_part);
  if (tm->second_part)
    length= 11;
  else if (tm->hour || tm->minute || tm->second || tm->day)
    length= 8;
  else
    length= 0;
  buff[0]= (char) length++;  
  memcpy((char *)net->write_pos, buff, length);
  net->write_pos+= length;
}

static void net_store_datetime(NET *net, MYSQL_TIME *tm)
{
  char buff[12], *pos;
  uint length;

  pos= buff+1;

  int2store(pos, tm->year);
  pos[2]= (uchar) tm->month;
  pos[3]= (uchar) tm->day;
  pos[4]= (uchar) tm->hour;
  pos[5]= (uchar) tm->minute;
  pos[6]= (uchar) tm->second;
  int4store(pos+7, tm->second_part);
  if (tm->second_part)
    length= 11;
  else if (tm->hour || tm->minute || tm->second)
    length= 7;
  else if (tm->year || tm->month || tm->day)
    length= 4;
  else
    length= 0;
  buff[0]= (char) length++;  
  memcpy((char *)net->write_pos, buff, length);
  net->write_pos+= length;
}

static void store_param_date(NET *net, MYSQL_BIND *param)
{
  MYSQL_TIME *tm= (MYSQL_TIME *) param->buffer;
  tm->hour= tm->minute= tm->second= 0;
  tm->second_part= 0;
  net_store_datetime(net, tm);
}

static void store_param_datetime(NET *net, MYSQL_BIND *param)
{
  MYSQL_TIME *tm= (MYSQL_TIME *) param->buffer;
  net_store_datetime(net, tm);
}
    
2266
static void store_param_str(NET *net, MYSQL_BIND *param)
2267
{
2268
  ulong length= param->length ? *param->length : param->buffer_length;
2269 2270
  char *to= (char *) net_store_length((char *) net->write_pos, length);
  memcpy(to, param->buffer, length);
2271
  net->write_pos= (uchar*) to+length;
2272 2273
}

2274

2275
/*
2276 2277 2278 2279 2280 2281 2282 2283 2284 2285
  Mark if the parameter is NULL.

  SYNOPSIS
    store_param_null()
    net			MySQL NET connection
    param		MySQL bind param

  DESCRIPTION
    A data package starts with a string of bits where we set a bit
    if a parameter is NULL
2286 2287
*/

2288
static void store_param_null(NET *net, MYSQL_BIND *param)
2289
{
2290
  uint pos= param->param_number;
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
2291
  net->buff[pos/8]|=  (uchar) (1 << (pos & 7));
2292
}
2293 2294 2295 2296


/*
  Set parameter data by reading from input buffers from the
2297
  client application
2298 2299
*/

2300
static my_bool store_param(MYSQL_STMT *stmt, MYSQL_BIND *param)
2301
{
2302
  NET *net= &stmt->mysql->net;
2303
  DBUG_ENTER("store_param");
2304 2305 2306 2307 2308 2309
  DBUG_PRINT("enter",("type: %d, buffer:%lx, length: %lu  is_null: %d",
		      param->buffer_type,
		      param->buffer ? param->buffer : "0", *param->length,
		      *param->is_null));

  if (*param->is_null)
2310 2311
    store_param_null(net, param);
  else
venu@myvenu.com's avatar
venu@myvenu.com committed
2312
  {
2313 2314
    /*
      Param->length should ALWAYS point to the correct length for the type
2315
      Either to the length pointer given by the user or param->buffer_length
2316 2317
    */
    if ((my_realloc_str(net, 9 + *param->length)))
2318
    {
2319
      set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate);
venu@myvenu.com's avatar
venu@myvenu.com committed
2320
      DBUG_RETURN(1);
2321
    }
2322
    (*param->store_param_func)(net, param);
venu@myvenu.com's avatar
venu@myvenu.com committed
2323 2324
  }
  DBUG_RETURN(0);
2325 2326
}

2327

2328
/*
2329
  Send the prepared query to server for execution
2330 2331
*/

2332
static my_bool execute(MYSQL_STMT * stmt, char *packet, ulong length)
2333
{
2334 2335
  MYSQL *mysql= stmt->mysql;
  NET	*net= &mysql->net;
2336 2337
  char buff[4 /* size of stmt id */ + 
            5 /* execution flags */];
2338
  DBUG_ENTER("execute");
2339 2340
  DBUG_PRINT("enter",("packet: %s, length :%d",packet ? packet :" ", length));

2341 2342
  mysql->last_used_con= mysql;
  int4store(buff, stmt->stmt_id);		/* Send stmt id to server */
2343 2344 2345 2346
  buff[4]= (char) 0;                            /* no flags */
  int4store(buff+5, 1);                         /* iteration count */
  if (cli_advanced_command(mysql, COM_EXECUTE, buff, sizeof(buff),
                           packet, length, 1) ||
hf@deer.(none)'s avatar
hf@deer.(none) committed
2347
      (*mysql->methods->read_query_result)(mysql))
2348
  {
2349
    set_stmt_errmsg(stmt, net->last_error, net->last_errno, net->sqlstate);
2350
    DBUG_RETURN(1);
2351
  }
2352
  stmt->affected_rows= mysql->affected_rows;
2353
  stmt->insert_id= mysql->insert_id;
2354 2355 2356
  DBUG_RETURN(0);
}

2357 2358

int cli_stmt_execute(MYSQL_STMT *stmt)
2359
{
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
2360
  DBUG_ENTER("cli_stmt_execute");
2361 2362 2363

  if (stmt->param_count)
  {
2364 2365
    NET        *net= &stmt->mysql->net;
    MYSQL_BIND *param, *param_end;
2366
    char       *param_data;
venu@myvenu.com's avatar
venu@myvenu.com committed
2367 2368 2369
    ulong length;
    uint null_count;
    my_bool    result;
2370

2371 2372 2373 2374 2375 2376
    if (!stmt->bind_param_done)
    {
      set_stmt_error(stmt, CR_PARAMS_NOT_BOUND, unknown_sqlstate);
      DBUG_RETURN(1);
    }

2377 2378 2379 2380 2381 2382 2383 2384
    net_clear(net);				/* Sets net->write_pos */
    /* Reserve place for null-marker bytes */
    null_count= (stmt->param_count+7) /8;
    bzero((char*) net->write_pos, null_count);
    net->write_pos+= null_count;
    param_end= stmt->params + stmt->param_count;

    /* In case if buffers (type) altered, indicate to server */
venu@myvenu.com's avatar
venu@myvenu.com committed
2385 2386
    *(net->write_pos)++= (uchar) stmt->send_types_to_server;
    if (stmt->send_types_to_server)
2387
    {
2388 2389 2390 2391 2392
      /*
	Store types of parameters in first in first package
	that is sent to the server.
      */
      for (param= stmt->params;	param < param_end ; param++)
2393 2394 2395 2396 2397
      {
        uint typecode= param->buffer_type | (param->is_unsigned ? 32768 : 0);
        int2store(net->write_pos, typecode);
        net->write_pos+= 2;
      }
2398 2399 2400
    }

    for (param= stmt->params; param < param_end; param++)
2401
    {
2402 2403 2404
      /* check if mysql_long_data() was used */
      if (param->long_data_used)
	param->long_data_used= 0;	/* Clear for next execute call */
2405
      else if (store_param(stmt, param))
2406
	DBUG_RETURN(1);
2407
    }
2408 2409
    length= (ulong) (net->write_pos - net->buff);
    /* TODO: Look into avoding the following memdup */
2410
    if (!(param_data= my_memdup((const char*) net->buff, length, MYF(0))))
2411
    {
2412
      set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate);
2413
      DBUG_RETURN(1);
2414
    }
2415
    result= execute(stmt, param_data, length);
venu@myvenu.com's avatar
venu@myvenu.com committed
2416
    stmt->send_types_to_server=0;
2417
    my_free(param_data, MYF(MY_WME));
2418 2419 2420
    DBUG_RETURN(result);
  }
  DBUG_RETURN((int) execute(stmt,0,0));
2421 2422
}

2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436
/*
  Read one row from buffered result set.  Result set is created by prior
  call to mysql_stmt_store_result().
  SYNOPSIS
    stmt_read_row_buffered()

  RETURN VALUE
    0             - success; *row is set to valid row pointer (row data
                    is stored in result set buffer)
    MYSQL_NO_DATA - end of result set. *row is set to NULL
*/

static int stmt_read_row_buffered(MYSQL_STMT *stmt, unsigned char **row)
{
2437
  if (stmt->data_cursor)
2438
  {
2439 2440
    *row= (uchar *) stmt->data_cursor->data;
    stmt->data_cursor= stmt->data_cursor->next;
2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521
    return 0;
  }
  *row= 0;
  return MYSQL_NO_DATA;
}

/*
  Read one row from network: unbuffered non-cursor fetch.
  If last row was read, or error occured, erase this statement
  from record pointing to object unbuffered fetch is performed from.

  SYNOPSIS
    stmt_read_row_unbuffered()
    stmt  statement handle
    row   pointer to write pointer to row data;

  RETURN VALUE
    0           - success; *row contains valid address of a row;
                  row data is stored in network buffer
    1           - error; error code is written to
                  stmt->last_{errno,error}; *row is not changed 
  MYSQL_NO_DATA - end of file was read from network;
                  *row is to NULL 
*/

static int stmt_read_row_unbuffered(MYSQL_STMT *stmt, unsigned char **row)
{
  int rc= 1;
  MYSQL *mysql= stmt->mysql;
  /* 
    This function won't be called if stmt->field_count is zero
    or execution wasn't done: this is ensured by mysql_stmt_execute.
  */
  if (!mysql)
  {
    set_stmt_error(stmt, CR_SERVER_LOST, unknown_sqlstate);
    return 1;
  }
  if (mysql->status != MYSQL_STATUS_GET_RESULT)
  {
    set_stmt_error(stmt, stmt->unbuffered_fetch_cancelled ?
                   CR_FETCH_CANCELLED : CR_COMMANDS_OUT_OF_SYNC,
                   unknown_sqlstate);
    goto error;
  }
  if ((*mysql->methods->unbuffered_fetch)(mysql, (char**) row))
  {
    set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno,
                    mysql->net.sqlstate);
    goto error;
  }
  if (!*row)
  {
    mysql->status= MYSQL_STATUS_READY;
    rc= MYSQL_NO_DATA;
    goto error;
  }
  return 0;
error:
  if (mysql->unbuffered_fetch_owner == &stmt->unbuffered_fetch_cancelled)
    mysql->unbuffered_fetch_owner= 0;
  return rc;
}

/*
  Default read row function to not SIGSEGV in client in
  case of wrong sequence of API calls.
*/

static int
stmt_read_row_no_data(MYSQL_STMT *stmt  __attribute__((unused)),
                      unsigned char **row  __attribute__((unused)))
{
  if ((int) stmt->state < (int) MYSQL_STMT_PREPARE_DONE)
  {
    set_stmt_error(stmt, CR_NO_PREPARE_STMT, unknown_sqlstate);
    return 1;
  }
  return MYSQL_NO_DATA;
}

2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559

/*
  Get/set statement attributes

  SYNOPSIS
    mysql_stmt_attr_get()
    mysql_stmt_attr_set()

    attr_type  statemenet attribute
    value      casted to const void * pointer to value.

  RETURN VALUE
    0 success
   !0 wrong attribute type
*/
        
my_bool STDCALL mysql_stmt_attr_set(MYSQL_STMT *stmt,
                                    enum enum_stmt_attr_type attr_type,
                                    const void *value)
{
  switch (attr_type) {
  case STMT_ATTR_UPDATE_MAX_LENGTH:
    stmt->update_max_length= value ? *(const my_bool*) value : 0;
    break;
  default: 
    return TRUE;
  }
  return FALSE;
}


my_bool STDCALL mysql_stmt_attr_get(MYSQL_STMT *stmt, 
                                    enum enum_stmt_attr_type attr_type,
                                    void *value)
{
  switch (attr_type) {
  case STMT_ATTR_UPDATE_MAX_LENGTH:
    *(unsigned long *) value= stmt->update_max_length;
2560
    break;
2561 2562 2563 2564 2565 2566 2567
  default: 
    return TRUE;
  }
  return FALSE;
}


hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
2568
/*
2569
  Execute the prepared query
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
2570 2571
*/

2572
int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt)
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
2573
{
2574
  MYSQL *mysql= stmt->mysql;
2575
  DBUG_ENTER("mysql_stmt_execute");
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
2576

2577 2578 2579
  if (!mysql)
  {
    set_stmt_error(stmt, CR_SERVER_LOST, unknown_sqlstate);
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
2580
    DBUG_RETURN(1);
2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598
  }

  mysql_stmt_free_result(stmt);
  /*
    No need to check for stmt->state: if the statement wasn't
    prepared we'll get 'unknown statemenet handler' error from server.
  */
  if (mysql->methods->stmt_execute(stmt))
    DBUG_RETURN(1);
  if (!stmt->field_count && mysql->field_count)
  {
    /* 
      This is 'SHOW'/'EXPLAIN'-like query.  Current implementation of
      prepared statements can't send result set metadata for this queries
      on prepare stage. Read it now.
    */
    alloc_stmt_fields(stmt);
  }
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
2599
      
2600 2601 2602 2603 2604 2605 2606
  stmt->state= MYSQL_STMT_EXECUTE_DONE;
  if (stmt->field_count)
  {
    stmt->mysql->unbuffered_fetch_owner= &stmt->unbuffered_fetch_cancelled;
    stmt->unbuffered_fetch_cancelled= FALSE;
    stmt->read_row_func= stmt_read_row_unbuffered;
  }
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
2607 2608 2609
  DBUG_RETURN(0);
}

2610

2611 2612 2613 2614
/*
  Return total parameters count in the statement
*/

2615
ulong STDCALL mysql_stmt_param_count(MYSQL_STMT * stmt)
2616
{
2617
  DBUG_ENTER("mysql_stmt_param_count");
2618 2619 2620
  DBUG_RETURN(stmt->param_count);
}

2621 2622 2623 2624 2625 2626
/*
  Return total affected rows from the last statement
*/

my_ulonglong STDCALL mysql_stmt_affected_rows(MYSQL_STMT *stmt)
{
2627
  return stmt->affected_rows;
2628
}
2629

2630

2631 2632 2633 2634 2635 2636 2637 2638 2639
/*
  Return last inserted id for auto_increment columns
*/

my_ulonglong STDCALL mysql_stmt_insert_id(MYSQL_STMT *stmt)
{
  return stmt->insert_id;
}

2640 2641 2642
static my_bool int_is_null_true= 1;		/* Used for MYSQL_TYPE_NULL */
static my_bool int_is_null_false= 0;

2643
/*
2644
  Setup the parameter data buffers from application
2645 2646
*/

2647
my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND * bind)
2648
{
2649 2650
  uint count=0;
  MYSQL_BIND *param, *end;
2651
  DBUG_ENTER("mysql_stmt_bind_param");
2652

2653 2654 2655 2656 2657 2658 2659 2660 2661 2662
  if (!stmt->param_count)
  {
    if ((int) stmt->state < (int) MYSQL_STMT_PREPARE_DONE)
    {
      set_stmt_error(stmt, CR_NO_PREPARE_STMT, unknown_sqlstate);
      DBUG_RETURN(1);
    }
    DBUG_RETURN(0);
  }

2663 2664 2665 2666 2667 2668 2669
  /* Allocated on prepare */
  memcpy((char*) stmt->params, (char*) bind,
	 sizeof(MYSQL_BIND) * stmt->param_count);

  for (param= stmt->params, end= param+stmt->param_count;
       param < end ;
       param++)
2670
  {
2671
    param->param_number= count++;
2672 2673
    param->long_data_used= 0;

2674 2675 2676 2677
    /* If param->is_null is not set, then the value can never be NULL */
    if (!param->is_null)
      param->is_null= &int_is_null_false;

2678 2679
    /* Setup data copy functions for the different supported types */
    switch (param->buffer_type) {
venu@myvenu.com's avatar
venu@myvenu.com committed
2680
    case MYSQL_TYPE_NULL:
2681
      param->is_null= &int_is_null_true;
venu@myvenu.com's avatar
venu@myvenu.com committed
2682
      break;
2683
    case MYSQL_TYPE_TINY:
2684
      /* Force param->length as this is fixed for this type */
2685 2686
      param->length= &param->buffer_length;
      param->buffer_length= 1;
2687 2688 2689
      param->store_param_func= store_param_tinyint;
      break;
    case MYSQL_TYPE_SHORT:
2690 2691
      param->length= &param->buffer_length;
      param->buffer_length= 2;
2692 2693 2694
      param->store_param_func= store_param_short;
      break;
    case MYSQL_TYPE_LONG:
2695 2696
      param->length= &param->buffer_length;
      param->buffer_length= 4;
2697 2698 2699
      param->store_param_func= store_param_int32;
      break;
    case MYSQL_TYPE_LONGLONG:
2700 2701
      param->length= &param->buffer_length;
      param->buffer_length= 8;
2702 2703 2704
      param->store_param_func= store_param_int64;
      break;
    case MYSQL_TYPE_FLOAT:
2705 2706
      param->length= &param->buffer_length;
      param->buffer_length= 4;
2707 2708 2709
      param->store_param_func= store_param_float;
      break;
    case MYSQL_TYPE_DOUBLE:
2710 2711
      param->length= &param->buffer_length;
      param->buffer_length= 8;
2712 2713
      param->store_param_func= store_param_double;
      break;
2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724
    case MYSQL_TYPE_TIME:
      /* Buffer length ignored for DATE, TIME and DATETIME */
      param->store_param_func= store_param_time;
      break;
    case MYSQL_TYPE_DATE:
      param->store_param_func= store_param_date;
      break;
    case MYSQL_TYPE_DATETIME:
    case MYSQL_TYPE_TIMESTAMP:
      param->store_param_func= store_param_datetime;
      break;
2725 2726 2727
    case MYSQL_TYPE_TINY_BLOB:
    case MYSQL_TYPE_MEDIUM_BLOB:
    case MYSQL_TYPE_LONG_BLOB:
venu@myvenu.com's avatar
venu@myvenu.com committed
2728
    case MYSQL_TYPE_BLOB:
2729 2730 2731 2732 2733
    case MYSQL_TYPE_VAR_STRING:
    case MYSQL_TYPE_STRING:
      param->store_param_func= store_param_str;
      break;
    default:
2734
      strmov(stmt->sqlstate, unknown_sqlstate);
2735 2736
      sprintf(stmt->last_error,
	      ER(stmt->last_errno= CR_UNSUPPORTED_PARAM_TYPE),
2737
	      param->buffer_type, count);
2738
      DBUG_RETURN(1);
2739
    }
2740 2741 2742 2743 2744 2745
    /*
      If param->length is not given, change it to point to buffer_length.
      This way we can always use *param->length to get the length of data
    */
    if (!param->length)
      param->length= &param->buffer_length;
2746
  }
2747
  /* We have to send/resendtype information to MySQL */
2748 2749
  stmt->send_types_to_server= TRUE;
  stmt->bind_param_done= TRUE;
2750 2751 2752
  DBUG_RETURN(0);
}

2753

2754 2755 2756 2757 2758
/********************************************************************
 Long data implementation
*********************************************************************/

/*
2759 2760 2761
  Send long data in pieces to the server

  SYNOPSIS
2762
    mysql_stmt_send_long_data()
2763 2764 2765 2766
    stmt			Statement handler
    param_number		Parameter number (0 - N-1)
    data			Data to send to server
    length			Length of data to send (may be 0)
2767

2768
  RETURN VALUES
2769 2770
    0	ok
    1	error
2771 2772 2773
*/


2774
my_bool STDCALL
2775
mysql_stmt_send_long_data(MYSQL_STMT *stmt, uint param_number,
2776
		     const char *data, ulong length)
2777 2778
{
  MYSQL_BIND *param;
2779
  DBUG_ENTER("mysql_stmt_send_long_data");
2780
  DBUG_ASSERT(stmt != 0);
2781
  DBUG_PRINT("enter",("param no : %d, data : %lx, length : %ld",
2782
		      param_number, data, length));
2783 2784 2785 2786 2787 2788 2789 2790 2791 2792
  
  /*
    We only need to check for stmt->param_count, if it's not null
    prepare was done.
  */
  if (param_number >= stmt->param_count)
  {
    set_stmt_error(stmt, CR_INVALID_PARAMETER_NO, unknown_sqlstate);
    DBUG_RETURN(1);
  }
2793

2794
  param= stmt->params+param_number;
2795 2796 2797 2798 2799 2800 2801
  if (param->buffer_type < MYSQL_TYPE_TINY_BLOB ||
      param->buffer_type > MYSQL_TYPE_STRING)
  {
    /*
      Long data handling should be used only for string/binary
      types only
    */
2802
    strmov(stmt->sqlstate, unknown_sqlstate);
2803 2804 2805 2806 2807
    sprintf(stmt->last_error, ER(stmt->last_errno= CR_INVALID_BUFFER_USE),
	    param->param_number);
    DBUG_RETURN(1);
  }
  /* Mark for execute that the result is already sent */
2808
  if (length || param->long_data_used == 0)
2809
  {
2810 2811 2812
    MYSQL *mysql= stmt->mysql;
    char   *packet, extra_data[MYSQL_LONG_DATA_HEADER];

2813 2814
    param->long_data_used= 1;

2815 2816 2817
    packet= extra_data;
    int4store(packet, stmt->stmt_id);	   packet+=4;
    int2store(packet, param_number);	   packet+=2;
2818

2819 2820 2821 2822
    /*
      Note that we don't get any ok packet from the server in this case
      This is intentional to save bandwidth.
    */
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
2823 2824 2825
    if ((*mysql->methods->advanced_command)(mysql, COM_LONG_DATA, extra_data,
					    MYSQL_LONG_DATA_HEADER, data, 
					    length, 1))
2826
    {
2827 2828
      set_stmt_errmsg(stmt, mysql->net.last_error,
		      mysql->net.last_errno, mysql->net.sqlstate);
2829 2830 2831 2832 2833 2834
      DBUG_RETURN(1);
    }
  }
  DBUG_RETURN(0);
}

2835

2836
/********************************************************************
2837
  Fetch-bind related implementations
2838 2839
*********************************************************************/

venu@myvenu.com's avatar
venu@myvenu.com committed
2840 2841 2842
/****************************************************************************
  Functions to fetch data to application buffers

2843
  All functions have the following characteristics:
venu@myvenu.com's avatar
venu@myvenu.com committed
2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854

  SYNOPSIS
    fetch_result_xxx()
    param   MySQL bind param
    row     Row value

  RETURN VALUES
    0	ok
    1	Error	(Can't alloc net->buffer)
****************************************************************************/

2855
static void set_zero_time(MYSQL_TIME *tm)
2856
{
2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927
  tm->year= tm->month= tm->day= 0;
  tm->hour= tm->minute= tm->second= 0;
  tm->second_part= 0;
  tm->neg= (bool)0;
}

/* Read TIME from binary packet and return it to MYSQL_TIME */
static uint read_binary_time(MYSQL_TIME *tm, uchar **pos)
{
  uchar *to;
  uint  length;
 
  if (!(length= net_field_length(pos)))
  {
    set_zero_time(tm);
    return 0;
  }
  
  to= *pos;     
  tm->second_part= (length > 8 ) ? (ulong) sint4korr(to+7): 0;

  tm->day=    (ulong) sint4korr(to+1);
  tm->hour=   (uint) to[5];
  tm->minute= (uint) to[6];
  tm->second= (uint) to[7];

  tm->year= tm->month= 0;
  tm->neg= (bool)to[0];
  return length;
}

/* Read DATETIME from binary packet and return it to MYSQL_TIME */
static uint read_binary_datetime(MYSQL_TIME *tm, uchar **pos)
{
  uchar *to;
  uint  length;
 
  if (!(length= net_field_length(pos)))
  {
    set_zero_time(tm);
    return 0;
  }
  
  to= *pos;     
  tm->second_part= (length > 7 ) ? (ulong) sint4korr(to+7): 0;
    
  if (length > 4)
  {
    tm->hour=   (uint) to[4];
    tm->minute= (uint) to[5];
    tm->second= (uint) to[6];
  }
  else
    tm->hour= tm->minute= tm->second= 0;
    
  tm->year=   (uint) sint2korr(to);
  tm->month=  (uint) to[2];
  tm->day=    (uint) to[3];
  tm->neg=    0;
  return length;
}

/* Read DATE from binary packet and return it to MYSQL_TIME */
static uint read_binary_date(MYSQL_TIME *tm, uchar **pos)
{
  uchar *to;
  uint  length;
 
  if (!(length= net_field_length(pos)))
  {
    set_zero_time(tm);
2928
    return 0;
2929
  }
2930 2931 2932 2933 2934 2935 2936 2937 2938 2939
  
  to= *pos;     
  tm->year =  (uint) sint2korr(to);
  tm->month=  (uint) to[2];
  tm->day= (uint) to[3];

  tm->hour= tm->minute= tm->second= 0;
  tm->second_part= 0;
  tm->neg= 0;
  return length;
2940 2941
}

2942
/* Convert Numeric to buffer types */
2943 2944
static void send_data_long(MYSQL_BIND *param, MYSQL_FIELD *field,
			   longlong value)
2945 2946
{  
  char *buffer= param->buffer;
2947 2948
  uint field_is_unsigned= (field->flags & UNSIGNED_FLAG);

2949
  switch (param->buffer_type) {
2950 2951
  case MYSQL_TYPE_NULL: /* do nothing */
    break;
2952 2953 2954 2955
  case MYSQL_TYPE_TINY:
    *param->buffer= (uchar) value;
    break;
  case MYSQL_TYPE_SHORT:
2956
    int2store(buffer, value);
2957 2958
    break;
  case MYSQL_TYPE_LONG:
2959
    int4store(buffer, value);
2960 2961
    break;
  case MYSQL_TYPE_LONGLONG:
2962
    int8store(buffer, value);
2963 2964
    break;
  case MYSQL_TYPE_FLOAT:
2965
  {
2966 2967
    float data= (field_is_unsigned ? (float) ulonglong2double(value) :
		 (float) value);
2968 2969 2970
    float4store(buffer, data);
    break;
  }
2971
  case MYSQL_TYPE_DOUBLE:
2972
  {
2973 2974
    double data= (field_is_unsigned ? ulonglong2double(value) :
		 (double) value);
2975 2976 2977
    float8store(buffer, data);
    break;
  }
2978
  default:
2979
  {
2980
    char tmp[22];				/* Enough for longlong */
2981 2982 2983
    uint length= (uint)(longlong10_to_str(value,(char *)tmp,
					  field_is_unsigned ? 10: -10) -
			tmp);
2984
    ulong copy_length= min((ulong)length-param->offset, param->buffer_length);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
2985 2986 2987 2988
    if ((long) copy_length < 0)
      copy_length=0;
    else
      memcpy(buffer, (char *)tmp+param->offset, copy_length);
2989
    *param->length= length;		
2990
  
2991 2992 2993
    if (copy_length != param->buffer_length)
      *(buffer+copy_length)= '\0';
  }
2994 2995 2996
  } 
}

2997

2998
/* Convert Double to buffer types */
2999

3000 3001 3002 3003 3004
static void send_data_double(MYSQL_BIND *param, double value)
{  
  char *buffer= param->buffer;

  switch(param->buffer_type) {
3005 3006
  case MYSQL_TYPE_NULL: /* do nothing */
    break;
3007 3008 3009 3010 3011 3012 3013
  case MYSQL_TYPE_TINY:
    *buffer= (uchar)value;
    break;
  case MYSQL_TYPE_SHORT:
    int2store(buffer, (short)value);
    break;
  case MYSQL_TYPE_LONG:
3014
    int4store(buffer, (long)value);
3015 3016 3017 3018 3019
    break;
  case MYSQL_TYPE_LONGLONG:
    int8store(buffer, (longlong)value);
    break;
  case MYSQL_TYPE_FLOAT:
3020 3021 3022 3023 3024
  {
    float data= (float)value;
    float4store(buffer, data);
    break;
  }
3025
  case MYSQL_TYPE_DOUBLE:
3026 3027 3028 3029 3030
  {
    double data= (double)value;
    float8store(buffer, data);
    break;
  }
3031
  default:
3032
  {
3033
    char tmp[128];
3034 3035
    uint length= my_sprintf(tmp,(tmp,"%g",value));
    ulong copy_length= min((ulong)length-param->offset, param->buffer_length);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3036 3037 3038 3039
    if ((long) copy_length < 0)
      copy_length=0;
    else
      memcpy(buffer, (char *)tmp+param->offset, copy_length);
3040
    *param->length= length;		
3041
  
3042 3043 3044
    if (copy_length != param->buffer_length)
      *(buffer+copy_length)= '\0';
  }
3045 3046 3047
  } 
}

3048

3049
/* Convert string to buffer types */
3050

3051
static void send_data_str(MYSQL_BIND *param, char *value, uint length)
3052 3053
{  
  char *buffer= param->buffer;
3054
  int err=0;
3055 3056

  switch(param->buffer_type) {
3057 3058
  case MYSQL_TYPE_NULL: /* do nothing */
    break;
3059
  case MYSQL_TYPE_TINY:
3060
  {
3061
    uchar data= (uchar)my_strntol(&my_charset_latin1,value,length,10,NULL,
3062
				  &err);
3063
    *buffer= data;
3064
    break;
3065
  }
3066 3067
  case MYSQL_TYPE_SHORT:
  {
3068
    short data= (short)my_strntol(&my_charset_latin1,value,length,10,NULL,
3069
				  &err);
3070 3071 3072 3073 3074
    int2store(buffer, data);
    break;
  }
  case MYSQL_TYPE_LONG:
  {
3075
    int32 data= (int32)my_strntol(&my_charset_latin1,value,length,10,NULL,
3076
				  &err);
3077 3078 3079 3080 3081
    int4store(buffer, data);    
    break;
  }
  case MYSQL_TYPE_LONGLONG:
  {
3082
    longlong data= my_strntoll(&my_charset_latin1,value,length,10,NULL,&err);
3083 3084 3085 3086 3087
    int8store(buffer, data);
    break;
  }
  case MYSQL_TYPE_FLOAT:
  {
3088
    float data = (float)my_strntod(&my_charset_latin1,value,length,NULL,&err);
3089 3090 3091 3092 3093
    float4store(buffer, data);
    break;
  }
  case MYSQL_TYPE_DOUBLE:
  {
3094
    double data= my_strntod(&my_charset_latin1,value,length,NULL,&err);
3095 3096 3097
    float8store(buffer, data);
    break;
  }
3098 3099 3100 3101 3102 3103
  case MYSQL_TYPE_TINY_BLOB:
  case MYSQL_TYPE_MEDIUM_BLOB:
  case MYSQL_TYPE_LONG_BLOB:
  case MYSQL_TYPE_BLOB:
    *param->length= length;
    length= min(length-param->offset, param->buffer_length);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3104 3105
    if ((long) length > 0)
      memcpy(buffer, value+param->offset, length);
3106
    break;
3107
  default:
3108
    *param->length= length;
3109
    length= min(length-param->offset, param->buffer_length);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3110 3111 3112 3113
    if ((long) length < 0)
      length= 0;
    else
      memcpy(buffer, value+param->offset, length);
3114
    if (length != param->buffer_length)
3115
      buffer[length]= '\0';
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3116
  }
3117 3118
}

3119

3120 3121 3122 3123
static void send_data_time(MYSQL_BIND *param, MYSQL_TIME ltime, 
                           uint length)
{
  switch (param->buffer_type) {
3124 3125
  case MYSQL_TYPE_NULL: /* do nothing */
    break;
3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154

  case MYSQL_TYPE_DATE:
  case MYSQL_TYPE_TIME:
  case MYSQL_TYPE_DATETIME:
  case MYSQL_TYPE_TIMESTAMP:
  {
    MYSQL_TIME *tm= (MYSQL_TIME *)param->buffer;
    
    tm->year= ltime.year;
    tm->month= ltime.month;
    tm->day= ltime.day;

    tm->hour= ltime.hour;
    tm->minute= ltime.minute;
    tm->second= ltime.second;

    tm->second_part= ltime.second_part;
    tm->neg= ltime.neg;
    break;   
  }
  default:
  {
    char buff[25];
    
    if (!length)
      ltime.time_type= MYSQL_TIMESTAMP_NONE;
    switch (ltime.time_type) {
    case MYSQL_TIMESTAMP_DATE:
      length= my_sprintf(buff,(buff, "%04d-%02d-%02d", ltime.year,
3155
			       ltime.month,ltime.day));      
3156 3157 3158 3159 3160 3161 3162 3163
      break;
    case MYSQL_TIMESTAMP_FULL:
      length= my_sprintf(buff,(buff, "%04d-%02d-%02d %02d:%02d:%02d",
	                       ltime.year,ltime.month,ltime.day,
	                       ltime.hour,ltime.minute,ltime.second));
      break;
    case MYSQL_TIMESTAMP_TIME:
      length= my_sprintf(buff, (buff, "%02d:%02d:%02d",
3164
				ltime.hour,ltime.minute,ltime.second));
3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175
      break;
    default:
      length= 0;
      buff[0]='\0';
    }
    send_data_str(param, (char *)buff, length); 
  }
  }
}
                              

3176
/* Fetch data to buffers */
3177

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3178
static void fetch_results(MYSQL_BIND *param, MYSQL_FIELD *field, uchar **row)
3179 3180
{
  ulong length;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3181 3182
  enum enum_field_types field_type= field->type;

3183 3184 3185
  switch (field_type) {
  case MYSQL_TYPE_TINY:
  {
3186
    char value= (char) **row;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3187
    uint field_is_unsigned= (field->flags & UNSIGNED_FLAG);
3188 3189
    longlong data= ((field_is_unsigned) ? (longlong) (unsigned char) value:
		    (longlong) value);
3190
    send_data_long(param, field, data);
3191
    length= 1;
3192 3193 3194 3195 3196
    break;
  }
  case MYSQL_TYPE_SHORT:
  case MYSQL_TYPE_YEAR:
  {
3197
    short value= sint2korr(*row);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3198
    uint field_is_unsigned= (field->flags & UNSIGNED_FLAG);
3199 3200
    longlong data= ((field_is_unsigned) ? (longlong) (unsigned short) value:
		    (longlong) value);
3201
    send_data_long(param, field, data);
3202
    length= 2;    
3203 3204 3205 3206
    break;
  }
  case MYSQL_TYPE_LONG:
  {
3207
    long value= sint4korr(*row);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3208
    uint field_is_unsigned= (field->flags & UNSIGNED_FLAG);
3209 3210
    longlong data= ((field_is_unsigned) ? (longlong) (unsigned long) value:
		    (longlong) value);
3211
    send_data_long(param, field, data);
3212
    length= 4;
3213 3214 3215 3216 3217
    break;
  }
  case MYSQL_TYPE_LONGLONG:
  {
    longlong value= (longlong)sint8korr(*row);
3218
    send_data_long(param, field, value);
3219
    length= 8;
3220 3221 3222 3223 3224 3225
    break;
  }
  case MYSQL_TYPE_FLOAT:
  {
    float value;
    float4get(value,*row);
3226
    send_data_double(param,value);
3227
    length= 4;
3228 3229 3230 3231 3232 3233
    break;
  }
  case MYSQL_TYPE_DOUBLE:
  {
    double value;
    float8get(value,*row);
3234
    send_data_double(param,value);
3235
    length= 8;
3236 3237 3238 3239
    break;
  }
  case MYSQL_TYPE_DATE:
  {
3240 3241
    MYSQL_TIME tm;
 
hf@deer.mysql.r18.ru's avatar
SCRUM  
hf@deer.mysql.r18.ru committed
3242
    length= read_binary_date(&tm, row);
3243 3244
    tm.time_type= MYSQL_TIMESTAMP_DATE;
    send_data_time(param, tm, length);
3245 3246 3247 3248
    break;
  }
  case MYSQL_TYPE_TIME:
  {
3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263
    MYSQL_TIME tm;
 
    length= read_binary_time(&tm, row);
    tm.time_type= MYSQL_TIMESTAMP_TIME;
    send_data_time(param, tm, length);
    break;
  }
  case MYSQL_TYPE_DATETIME:
  case MYSQL_TYPE_TIMESTAMP:
  {
    MYSQL_TIME tm;
 
    length= read_binary_datetime(&tm, row);
    tm.time_type= MYSQL_TIMESTAMP_FULL;
    send_data_time(param, tm, length);
3264 3265 3266 3267
    break;
  }
  default:      
    length= net_field_length(row); 
3268
    send_data_str(param,(char*) *row,length);
3269 3270 3271 3272 3273
    break;
  }
  *row+= length;
}

3274

venu@myvenu.com's avatar
venu@myvenu.com committed
3275 3276
static void fetch_result_tinyint(MYSQL_BIND *param, uchar **row)
{
3277
  *param->buffer= **row;
3278
  (*row)++;
venu@myvenu.com's avatar
venu@myvenu.com committed
3279 3280 3281 3282
}

static void fetch_result_short(MYSQL_BIND *param, uchar **row)
{
3283
  short value = (short)sint2korr(*row);
3284
  shortstore(param->buffer, value);
3285
  *row+= 2;
venu@myvenu.com's avatar
venu@myvenu.com committed
3286 3287 3288 3289
}

static void fetch_result_int32(MYSQL_BIND *param, uchar **row)
{
3290
  int32 value= (int32)sint4korr(*row);
3291
  longstore(param->buffer, value);
3292
  *row+= 4;
venu@myvenu.com's avatar
venu@myvenu.com committed
3293 3294 3295
}

static void fetch_result_int64(MYSQL_BIND *param, uchar **row)
3296 3297
{  
  longlong value= (longlong)sint8korr(*row);
3298
  longlongstore(param->buffer, value);
3299
  *row+= 8;
venu@myvenu.com's avatar
venu@myvenu.com committed
3300 3301 3302 3303 3304 3305
}

static void fetch_result_float(MYSQL_BIND *param, uchar **row)
{
  float value;
  float4get(value,*row);
3306
  floatstore(param->buffer, value);
3307
  *row+= 4;
venu@myvenu.com's avatar
venu@myvenu.com committed
3308 3309 3310 3311 3312 3313
}

static void fetch_result_double(MYSQL_BIND *param, uchar **row)
{
  double value;
  float8get(value,*row);
3314
  doublestore(param->buffer, value);
3315
  *row+= 8;
venu@myvenu.com's avatar
venu@myvenu.com committed
3316 3317
}

3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335
static void fetch_result_time(MYSQL_BIND *param, uchar **row)
{
  MYSQL_TIME *tm= (MYSQL_TIME *)param->buffer;
  *row+= read_binary_time(tm, row);
}

static void fetch_result_date(MYSQL_BIND *param, uchar **row)
{
  MYSQL_TIME *tm= (MYSQL_TIME *)param->buffer;
  *row+= read_binary_date(tm, row);
}

static void fetch_result_datetime(MYSQL_BIND *param, uchar **row)
{
  MYSQL_TIME *tm= (MYSQL_TIME *)param->buffer;
  *row+= read_binary_datetime(tm, row);
}

3336 3337 3338 3339 3340 3341 3342 3343
static void fetch_result_bin(MYSQL_BIND *param, uchar **row)
{  
  ulong length= net_field_length(row);
  ulong copy_length= min(length, param->buffer_length);
  memcpy(param->buffer, (char *)*row, copy_length);
  *param->length= length;		
  *row+= length;
}
3344

venu@myvenu.com's avatar
venu@myvenu.com committed
3345 3346 3347
static void fetch_result_str(MYSQL_BIND *param, uchar **row)
{
  ulong length= net_field_length(row);
3348 3349
  ulong copy_length= min(length, param->buffer_length);
  memcpy(param->buffer, (char *)*row, copy_length);
3350 3351 3352
  /* Add an end null if there is room in the buffer */
  if (copy_length != param->buffer_length)
    *(param->buffer+copy_length)= '\0';
3353
  *param->length= length;			/* return total length */
3354
  *row+= length;
venu@myvenu.com's avatar
venu@myvenu.com committed
3355 3356
}

3357

3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394
/*
  functions to calculate max lengths for strings during
  mysql_stmt_store_result()
*/

static void skip_result_fixed(MYSQL_BIND *param,
			      MYSQL_FIELD *field __attribute__((unused)),
			      uchar **row)

{
  (*row)+= param->pack_length;
}


static void skip_result_with_length(MYSQL_BIND *param __attribute__((unused)),
				    MYSQL_FIELD *field __attribute__((unused)),
				    uchar **row)

{
  ulong length= net_field_length(row);
  (*row)+= length;
}


static void skip_result_string(MYSQL_BIND *param __attribute__((unused)),
			       MYSQL_FIELD *field,
			       uchar **row)

{
  ulong length= net_field_length(row);
  (*row)+= length;
  if (field->max_length < length)
    field->max_length= length;
}



3395
/*
3396
  Setup the bind buffers for resultset processing
3397 3398
*/

3399
my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind)
3400
{
3401
  MYSQL_BIND *param, *end;
3402
  MYSQL_FIELD *field;
3403
  ulong       bind_count;
3404
  uint        param_count= 0;
3405
  DBUG_ENTER("mysql_stmt_bind_result");
3406 3407
  DBUG_ASSERT(stmt != 0);

3408 3409 3410 3411 3412 3413
  if (!stmt->field_count)
  {
    if ((int) stmt->state < (int) MYSQL_STMT_PREPARE_DONE)
    {
      set_stmt_error(stmt, CR_NO_PREPARE_STMT, unknown_sqlstate);
    }
3414
    DBUG_RETURN(0);
3415 3416 3417 3418 3419 3420 3421
  }
  bind_count= stmt->field_count;

  /*
    We only need to check that stmt->field_count - if it is not null
    stmt->bind was initialized in mysql_stmt_prepare
   */
3422
  
3423
  memcpy((char*) stmt->bind, (char*) bind, sizeof(MYSQL_BIND) * bind_count);
3424

3425 3426 3427
  for (param= stmt->bind, end= param + bind_count, field= stmt->fields ;
       param < end ;
       param++, field++)
3428
  {
3429 3430
    /*
      Set param->is_null to point to a dummy variable if it's not set.
3431
      This is to make the execute code easier
3432 3433
    */
    if (!param->is_null)
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3434
      param->is_null= &param->internal_is_null;
3435

3436
    if (!param->length)
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3437
      param->length= &param->internal_length;
3438

3439
    param->param_number= param_count++;
3440 3441
    param->offset= 0;

venu@myvenu.com's avatar
venu@myvenu.com committed
3442 3443
    /* Setup data copy functions for the different supported types */
    switch (param->buffer_type) {
3444
    case MYSQL_TYPE_NULL: /* for dummy binds */
3445
      *param->length= 0;
3446
      break;
venu@myvenu.com's avatar
venu@myvenu.com committed
3447 3448
    case MYSQL_TYPE_TINY:
      param->fetch_result= fetch_result_tinyint;
3449
      *param->length= 1;
venu@myvenu.com's avatar
venu@myvenu.com committed
3450 3451
      break;
    case MYSQL_TYPE_SHORT:
3452
    case MYSQL_TYPE_YEAR:
venu@myvenu.com's avatar
venu@myvenu.com committed
3453
      param->fetch_result= fetch_result_short;
3454
      *param->length= 2;
venu@myvenu.com's avatar
venu@myvenu.com committed
3455
      break;
3456
    case MYSQL_TYPE_INT24:
venu@myvenu.com's avatar
venu@myvenu.com committed
3457 3458
    case MYSQL_TYPE_LONG:
      param->fetch_result= fetch_result_int32;
3459
      *param->length= 4;
venu@myvenu.com's avatar
venu@myvenu.com committed
3460 3461 3462
      break;
    case MYSQL_TYPE_LONGLONG:
      param->fetch_result= fetch_result_int64;
3463
      *param->length= 8;
venu@myvenu.com's avatar
venu@myvenu.com committed
3464 3465 3466
      break;
    case MYSQL_TYPE_FLOAT:
      param->fetch_result= fetch_result_float;
3467
      *param->length= 4;
venu@myvenu.com's avatar
venu@myvenu.com committed
3468 3469 3470
      break;
    case MYSQL_TYPE_DOUBLE:
      param->fetch_result= fetch_result_double;
3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484
      *param->length= 8;
      break;
    case MYSQL_TYPE_TIME:
      param->fetch_result= fetch_result_time;
      *param->length= sizeof(MYSQL_TIME);
      break;
    case MYSQL_TYPE_DATE:
      param->fetch_result= fetch_result_date;
      *param->length= sizeof(MYSQL_TIME);
      break;
    case MYSQL_TYPE_DATETIME:
    case MYSQL_TYPE_TIMESTAMP:
      param->fetch_result= fetch_result_datetime;
      *param->length= sizeof(MYSQL_TIME);
venu@myvenu.com's avatar
venu@myvenu.com committed
3485 3486 3487 3488
      break;
    case MYSQL_TYPE_TINY_BLOB:
    case MYSQL_TYPE_MEDIUM_BLOB:
    case MYSQL_TYPE_LONG_BLOB:
3489
    case MYSQL_TYPE_BLOB:
3490 3491 3492
      DBUG_ASSERT(param->buffer_length != 0);
      param->fetch_result= fetch_result_bin;
      break;
venu@myvenu.com's avatar
venu@myvenu.com committed
3493 3494
    case MYSQL_TYPE_VAR_STRING:
    case MYSQL_TYPE_STRING:
3495
      DBUG_ASSERT(param->buffer_length != 0);
venu@myvenu.com's avatar
venu@myvenu.com committed
3496 3497 3498
      param->fetch_result= fetch_result_str;
      break;
    default:
3499
      strmov(stmt->sqlstate, unknown_sqlstate);
3500 3501
      sprintf(stmt->last_error,
	      ER(stmt->last_errno= CR_UNSUPPORTED_PARAM_TYPE),
3502
	      param->buffer_type, param_count);
venu@myvenu.com's avatar
venu@myvenu.com committed
3503
      DBUG_RETURN(1);
peter@mysql.com's avatar
peter@mysql.com committed
3504
    }
3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556

    /* Setup skip_result functions (to calculate max_length) */
    param->skip_result= skip_result_fixed;
    switch (field->type) {
    case MYSQL_TYPE_NULL: /* for dummy binds */
      param->pack_length= 0;
      break;
    case MYSQL_TYPE_TINY:
      param->pack_length= 1;
      break;
    case MYSQL_TYPE_YEAR:
    case MYSQL_TYPE_SHORT:
      param->pack_length= 2;
      break;
    case MYSQL_TYPE_INT24:
    case MYSQL_TYPE_LONG:
      param->pack_length= 4;
      break;
    case MYSQL_TYPE_LONGLONG:
      param->pack_length= 8;
      break;
    case MYSQL_TYPE_FLOAT:
      param->pack_length= 4;
      break;
    case MYSQL_TYPE_DOUBLE:
      param->pack_length= 8;
      break;
    case MYSQL_TYPE_TIME:
    case MYSQL_TYPE_DATE:
    case MYSQL_TYPE_DATETIME:
    case MYSQL_TYPE_TIMESTAMP:
      param->skip_result= skip_result_with_length;
      break;
    case MYSQL_TYPE_DECIMAL:
    case MYSQL_TYPE_ENUM:
    case MYSQL_TYPE_SET:
    case MYSQL_TYPE_GEOMETRY:
    case MYSQL_TYPE_TINY_BLOB:
    case MYSQL_TYPE_MEDIUM_BLOB:
    case MYSQL_TYPE_LONG_BLOB:
    case MYSQL_TYPE_BLOB:
    case MYSQL_TYPE_VAR_STRING:
    case MYSQL_TYPE_STRING:
      param->skip_result= skip_result_string;
      break;
    default:
      strmov(stmt->sqlstate, unknown_sqlstate);
      sprintf(stmt->last_error,
	      ER(stmt->last_errno= CR_UNSUPPORTED_PARAM_TYPE),
	      field->type, param_count);
      DBUG_RETURN(1);
    }
3557
  }
3558
  stmt->bind_result_done= TRUE;
3559 3560 3561
  DBUG_RETURN(0);
}

3562

3563
/*
3564
  Fetch row data to bind buffers
3565 3566
*/

3567
static int stmt_fetch_row(MYSQL_STMT *stmt, uchar *row)
3568
{
3569
  MYSQL_BIND  *bind, *end;
3570
  MYSQL_FIELD *field;
3571
  uchar *null_ptr, bit;
3572 3573 3574 3575 3576 3577
  /*
    Precondition: if stmt->field_count is zero or row is NULL, read_row_*
    function must return no data.
  */
  DBUG_ASSERT(stmt->field_count);
  DBUG_ASSERT(row);
3578

3579 3580 3581
  if (!stmt->bind_result_done)
  {
    /* If output parameters were not bound we should just return success */
3582
    return 0;
3583
  }
3584 3585
  
  null_ptr= row; 
3586 3587
  row+= (stmt->field_count+9)/8;		/* skip null bits */
  bit= 4;					/* first 2 bits are reserved */
3588
  
venu@myvenu.com's avatar
venu@myvenu.com committed
3589
  /* Copy complete row to application buffers */
3590 3591
  for (bind= stmt->bind, end= bind + stmt->field_count, field= stmt->fields ;
       bind < end ;
3592 3593
       bind++, field++)
  {         
3594
    if (*null_ptr & bit)
3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605
    {
      /*
        We should set both inter_buffer and is_null to be able to see
        nulls in mysql_stmt_fetch_column. This is because is_null may point
        to user data which can be overwritten between mysql_stmt_fetch and
        mysql_stmt_fetch_column, and in this case nullness of column will be
        lost. See mysql_stmt_fetch_column for details.
      */
      bind->inter_buffer= NULL;
      *bind->is_null= 1;
    }
3606
    else
3607
    { 
3608
      *bind->is_null= 0;
3609
      bind->inter_buffer= row;
3610 3611
      if (field->type == bind->buffer_type)
        (*bind->fetch_result)(bind, &row);
3612
      else 
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3613
        fetch_results(bind, field, &row);
3614
    }
3615
    if (!((bit<<=1) & 255))
3616
    {
3617
      bit= 1;					/* To next byte */
3618
      null_ptr++;
3619 3620 3621 3622 3623
    }
  }
  return 0;
}

3624

3625
int cli_unbuffered_fetch(MYSQL *mysql, char **row)
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
3626 3627 3628 3629
{
  if (packet_error == net_safe_read(mysql))
    return 1;

3630 3631
  *row= ((mysql->net.read_pos[0] == 254) ? NULL :
	 (char*) (mysql->net.read_pos+1));
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
3632 3633
  return 0;
}
3634

3635

3636
/*
3637
  Fetch and return row data to bound buffers, if any
3638 3639
*/

3640
int STDCALL mysql_stmt_fetch(MYSQL_STMT *stmt)
3641
{
3642
  int rc;
3643
  uchar *row;
3644
  DBUG_ENTER("mysql_stmt_fetch");
3645

3646 3647
  if ((rc= (*stmt->read_row_func)(stmt, &row)) ||
      (rc= stmt_fetch_row(stmt, row)))
3648
  {
3649 3650
    stmt->state= MYSQL_STMT_PREPARE_DONE;       /* XXX: this is buggy */
    stmt->read_row_func= stmt_read_row_no_data;
3651
  }
3652
  else
3653
  {
3654 3655
    /* This is to know in mysql_stmt_fetch_column that data was fetched */
    stmt->state= MYSQL_STMT_FETCH_DONE;
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
3656
  }
3657
  DBUG_RETURN(rc);
3658 3659
}

3660

3661
/*
3662
  Fetch data for one specified column data
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3663 3664

  SYNOPSIS
3665
    mysql_stmt_fetch_column()
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3666
    stmt		Prepared statement handler
3667
    bind		Where data should be placed. Should be filled in as
3668
			when calling mysql_stmt_bind_result()
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3669 3670 3671 3672 3673 3674
    column		Column to fetch (first column is 0)
    ulong offset	Offset in result data (to fetch blob in pieces)
			This is normally 0
  RETURN 
    0	ok
    1	error
3675 3676
*/

3677
int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind, 
3678
                                    uint column, ulong offset)
3679
{
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3680
  MYSQL_BIND *param= stmt->bind+column; 
3681
  DBUG_ENTER("mysql_stmt_fetch_column");
3682

3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693
  if ((int) stmt->state < (int) MYSQL_STMT_FETCH_DONE)
  {
    set_stmt_error(stmt, CR_NO_DATA, unknown_sqlstate);
    return 1;
  }
  if (column >= stmt->field_count)
  {
    set_stmt_error(stmt, CR_INVALID_PARAMETER_NO, unknown_sqlstate);
    DBUG_RETURN(1);
  }

3694
  if (param->inter_buffer)
3695
  {
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3696 3697
    MYSQL_FIELD *field= stmt->fields+column; 
    uchar *row= param->inter_buffer;
3698 3699 3700 3701
    bind->offset= offset;
    if (bind->is_null)
      *bind->is_null= 0;
    if (bind->length) /* Set the length if non char/binary types */
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3702
      *bind->length= *param->length;
3703
    else
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
3704 3705
      bind->length= &param->internal_length;	/* Needed for fetch_result() */
    fetch_results(bind, field, &row);
3706
  }
3707 3708 3709 3710 3711
  else
  {
    if (bind->is_null)
      *bind->is_null= 1;
  }
3712 3713 3714 3715
  DBUG_RETURN(0);
}


3716
/*
3717 3718 3719
  Read all rows of data from server  (binary format)
*/

3720
int cli_read_binary_rows(MYSQL_STMT *stmt)
3721 3722 3723 3724
{
  ulong      pkt_len;
  uchar      *cp;
  MYSQL      *mysql= stmt->mysql;
3725 3726
  MYSQL_DATA *result= &stmt->result;
  MYSQL_ROWS *cur, **prev_ptr= &result->data;
3727
  NET        *net = &mysql->net;
3728
  DBUG_ENTER("cli_read_binary_rows");
3729

3730
  mysql= mysql->last_used_con;
3731

3732
  if (stmt->update_max_length && !stmt->bind_result_done)
3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753
  {
    /*
      We must initalize the bind structure to be able to calculate
      max_length
    */
    MYSQL_BIND  *bind, *end;
    MYSQL_FIELD *field;
    bzero((char*) stmt->bind, sizeof(*stmt->bind)* stmt->field_count);

    for (bind= stmt->bind, end= bind + stmt->field_count, field= stmt->fields;
	 bind < end ;
	 bind++, field++)
    {
      bind->buffer_type= field->type;
      bind->buffer_length=1;
    }

    mysql_stmt_bind_result(stmt, stmt->bind);
    stmt->bind_result_done= 0;			/* No normal bind done */
  }

3754
  while ((pkt_len= net_safe_read(mysql)) != packet_error)
3755
  {
3756 3757
    cp= net->read_pos;
    if (cp[0] != 254 || pkt_len >= 8)
3758
    {
3759 3760 3761 3762
      if (!(cur= (MYSQL_ROWS*) alloc_root(&result->alloc,
                                          sizeof(MYSQL_ROWS) + pkt_len - 1)))
      {
        set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate);
3763
        goto err;
3764 3765 3766 3767 3768
      }
      cur->data= (MYSQL_ROW) (cur+1);
      *prev_ptr= cur;
      prev_ptr= &cur->next;
      memcpy((char *) cur->data, (char *) cp+1, pkt_len-1);
3769 3770
      cur->length= pkt_len;		/* To allow us to do sanity checks */
      result->rows++;
3771 3772
      if (stmt->update_max_length)
	stmt_update_metadata(stmt, cur);
3773
    }
3774
    else
3775
    {
3776 3777 3778 3779
      /* end of data */
      *prev_ptr= 0;
      mysql->warning_count= uint2korr(cp+1);
      mysql->server_status= uint2korr(cp+3);
3780 3781 3782
      DBUG_RETURN(0);
    }
  }
3783
  set_stmt_errmsg(stmt, net->last_error, net->last_errno, net->sqlstate);
3784 3785

err:
3786
  DBUG_RETURN(1);
3787 3788
}

3789

3790 3791 3792 3793 3794 3795 3796
/*
  Store or buffer the binary results to stmt
*/

int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt)
{
  MYSQL *mysql= stmt->mysql;
3797
  MYSQL_DATA *result= &stmt->result;
3798
  DBUG_ENTER("mysql_stmt_store_result");
3799 3800 3801 3802 3803

  mysql= mysql->last_used_con;

  if (!stmt->field_count)
    DBUG_RETURN(0);
3804 3805
  if ((int) stmt->state < (int) MYSQL_STMT_EXECUTE_DONE ||
      mysql->status != MYSQL_STATUS_GET_RESULT)
3806
  {
3807
    set_stmt_error(stmt, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate);
3808
    DBUG_RETURN(1);
3809
  }
3810
  if (result->data)
3811
  {
3812 3813 3814 3815
    free_root(&result->alloc, MYF(MY_KEEP_PREALLOC));
    result->data= NULL;
    result->rows= 0;
    stmt->data_cursor= NULL;
3816
  }
3817
  if ((*mysql->methods->read_binary_rows)(stmt))
3818
  {
3819 3820 3821
    free_root(&result->alloc, MYF(MY_KEEP_PREALLOC));
    result->data= NULL;
    result->rows= 0;
3822
    DBUG_RETURN(1);
3823
  }
3824 3825 3826

  stmt->data_cursor= result->data;
  mysql->affected_rows= stmt->affected_rows= result->rows;
3827 3828 3829
  stmt->read_row_func= stmt_read_row_buffered;
  mysql->unbuffered_fetch_owner= 0;             /* set in stmt_execute */
  mysql->status= MYSQL_STATUS_READY;		/* server is ready */
3830
  DBUG_RETURN(0); /* Data buffered, must be fetched with mysql_stmt_fetch() */
3831 3832
}

3833

3834 3835 3836 3837 3838 3839 3840
/*
  Seek to desired row in the statement result set
*/

MYSQL_ROW_OFFSET STDCALL
mysql_stmt_row_seek(MYSQL_STMT *stmt, MYSQL_ROW_OFFSET row)
{
3841
  MYSQL_ROW_OFFSET offset= stmt->data_cursor;
3842 3843
  DBUG_ENTER("mysql_stmt_row_seek");
  
3844 3845
  stmt->data_cursor= row;
  DBUG_RETURN(offset);
3846 3847
}

3848

3849 3850 3851 3852 3853 3854 3855 3856 3857
/*
  Return the current statement row cursor position
*/

MYSQL_ROW_OFFSET STDCALL 
mysql_stmt_row_tell(MYSQL_STMT *stmt)
{
  DBUG_ENTER("mysql_stmt_row_tell");
  
3858
  DBUG_RETURN(stmt->data_cursor);
3859 3860
}

3861

3862 3863 3864 3865 3866 3867 3868
/*
  Move the stmt result set data cursor to specified row
*/

void STDCALL
mysql_stmt_data_seek(MYSQL_STMT *stmt, my_ulonglong row)
{
3869
  MYSQL_ROWS *tmp= stmt->result.data;
3870 3871 3872
  DBUG_ENTER("mysql_stmt_data_seek");
  DBUG_PRINT("enter",("row id to seek: %ld",(long) row));
  
3873 3874 3875
  for (; tmp && row; --row, tmp= tmp->next)
    ;
  stmt->data_cursor= tmp;
3876
  DBUG_VOID_RETURN;
3877 3878
}

3879

3880 3881 3882 3883 3884 3885 3886 3887
/*
  Return total rows the current statement result set
*/

my_ulonglong STDCALL mysql_stmt_num_rows(MYSQL_STMT *stmt)
{
  DBUG_ENTER("mysql_stmt_num_rows");
    
3888
  DBUG_RETURN(stmt->result.rows);
3889
}
3890

3891 3892
my_bool STDCALL mysql_stmt_free_result(MYSQL_STMT *stmt)
{
3893
  MYSQL_DATA *result= &stmt->result;
3894 3895 3896 3897 3898 3899 3900 3901
  DBUG_ENTER("mysql_stmt_free_result");

  DBUG_ASSERT(stmt != 0);
  
  if ((int) stmt->state > (int) MYSQL_STMT_INIT_DONE)
  {
    MYSQL *mysql= stmt->mysql;

3902
    if (result->data)
3903 3904
    {
      /* Result buffered */
3905 3906 3907 3908
      free_root(&result->alloc, MYF(MY_KEEP_PREALLOC));
      result->data= NULL;
      result->rows= 0;
      stmt->data_cursor= NULL;
3909
    }
3910 3911 3912

    if (mysql && stmt->field_count &&
        (int) stmt->state > (int) MYSQL_STMT_PREPARE_DONE)
3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928
    {
      if (mysql->unbuffered_fetch_owner == &stmt->unbuffered_fetch_cancelled)
        mysql->unbuffered_fetch_owner= 0;
      if (mysql->status != MYSQL_STATUS_READY)
      {
        /* There is a result set and it belongs to this statement */
        flush_use_result(mysql);
        mysql->status= MYSQL_STATUS_READY;
      }
    }
    stmt->state= MYSQL_STMT_PREPARE_DONE;
    stmt->read_row_func= stmt_read_row_no_data;
  }
  DBUG_RETURN(0);
}

3929
/********************************************************************
3930
 statement error handling and close
3931
*********************************************************************/
3932

3933
/*
3934
  Close the statement handle by freeing all alloced resources
3935

3936
  SYNOPSIS
3937
    mysql_stmt_close()
3938
    stmt	       Statement handle
3939

3940 3941 3942 3943
  RETURN VALUES
    0	ok
    1	error
*/
3944

3945
my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt)
3946
{
3947 3948 3949
  MYSQL *mysql= stmt->mysql;
  int rc= 0;
  DBUG_ENTER("mysql_stmt_close");
3950

3951
  free_root(&stmt->result.alloc, MYF(0));
3952 3953 3954
  free_root(&stmt->mem_root, MYF(0));

  if (mysql)
3955
  {
3956 3957
    mysql->stmts= list_delete(mysql->stmts, &stmt->list);
    if ((int) stmt->state > (int) MYSQL_STMT_INIT_DONE)
3958
    {
3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979
      char buff[4];

      if (mysql->unbuffered_fetch_owner == &stmt->unbuffered_fetch_cancelled)
        mysql->unbuffered_fetch_owner= 0;
      if (mysql->status != MYSQL_STATUS_READY)
      {
        /* 
          Flush result set of the connection. If it does not belong
          to this statement, set a warning.
        */
        flush_use_result(mysql);
        if (mysql->unbuffered_fetch_owner)
          *mysql->unbuffered_fetch_owner= TRUE;
        mysql->status= MYSQL_STATUS_READY;
      }
      int4store(buff, stmt->stmt_id);
      if ((rc= simple_command(mysql, COM_CLOSE_STMT, buff, 4, 1)))
      {
        set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno,
                        mysql->net.sqlstate);
      }
3980
    }
3981
  }
3982

3983
  my_free((gptr) stmt, MYF(MY_WME));
3984

3985
  DBUG_RETURN(test(rc));
3986 3987
}

3988 3989 3990 3991 3992 3993 3994 3995
/*
  Reset the statement buffers in server
*/

my_bool STDCALL mysql_stmt_reset(MYSQL_STMT *stmt)
{
  char buff[MYSQL_STMT_HEADER];
  MYSQL *mysql;
3996
  MYSQL_BIND *param, *param_end;
3997 3998
  DBUG_ENTER("mysql_stmt_reset");
  DBUG_ASSERT(stmt != 0);
3999 4000 4001 4002

  /* If statement hasnt been prepared there is nothing to reset */
  if ((int) stmt->state < (int) MYSQL_STMT_PREPARE_DONE)
    DBUG_RETURN(0);
4003 4004 4005
  
  mysql= stmt->mysql->last_used_con;
  int4store(buff, stmt->stmt_id);		/* Send stmt id to server */
4006 4007
  if ((*mysql->methods->advanced_command)(mysql, COM_RESET_STMT, buff,
                                          MYSQL_STMT_HEADER,0,0,0))
4008 4009 4010 4011 4012
  {
    set_stmt_errmsg(stmt, mysql->net.last_error, mysql->net.last_errno, 
                    mysql->net.sqlstate);
    DBUG_RETURN(1);
  }
4013 4014 4015 4016 4017 4018 4019

  /* Clear long_data_used for next call (as we do in mysql_stmt_execute() */
  for (param= stmt->params, param_end= param + stmt->param_count;
       param < param_end;
       param++)
    param->long_data_used= 0;

4020 4021 4022
  DBUG_RETURN(0);
}

4023 4024 4025 4026 4027 4028 4029
/*
  Return statement error code
*/

uint STDCALL mysql_stmt_errno(MYSQL_STMT * stmt)
{
  DBUG_ENTER("mysql_stmt_errno");
4030
  DBUG_RETURN(stmt->last_errno);
4031 4032
}

4033 4034 4035 4036 4037 4038
const char *STDCALL mysql_stmt_sqlstate(MYSQL_STMT * stmt)
{
  DBUG_ENTER("mysql_stmt_sqlstate");
  DBUG_RETURN(stmt->sqlstate);
}

4039 4040 4041 4042 4043 4044 4045
/*
  Return statement error message
*/

const char *STDCALL mysql_stmt_error(MYSQL_STMT * stmt)
{
  DBUG_ENTER("mysql_stmt_error");
4046
  DBUG_RETURN(stmt->last_error);
4047 4048
}

4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068

/*
  Update meta data for statement

  SYNOPSIS
    stmt_update_metadata()
    stmt			Statement handler
    row				Binary data

  NOTES
    Only updates MYSQL_FIELD->max_length for strings

*/

static void stmt_update_metadata(MYSQL_STMT *stmt, MYSQL_ROWS *data)
{
  MYSQL_BIND  *bind, *end;
  MYSQL_FIELD *field;
  uchar *null_ptr, bit;
  uchar *row= (uchar*) data->data;
monty@mysql.com's avatar
monty@mysql.com committed
4069
#ifndef DBUG_OFF
4070
  uchar *row_end= row + data->length;
monty@mysql.com's avatar
monty@mysql.com committed
4071
#endif
4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093

  null_ptr= row; 
  row+= (stmt->field_count+9)/8;		/* skip null bits */
  bit= 4;					/* first 2 bits are reserved */
  
  /* Go throw all fields and calculate metadata */
  for (bind= stmt->bind, end= bind + stmt->field_count, field= stmt->fields ;
       bind < end ;
       bind++, field++)
  {
    if (!(*null_ptr & bit))
      (*bind->skip_result)(bind, field, &row);
    DBUG_ASSERT(row <= row_end);
    if (!((bit<<=1) & 255))
    {
      bit= 1;					/* To next byte */
      null_ptr++;
    }
  }
}


4094 4095 4096 4097
/********************************************************************
 Transactional APIs
*********************************************************************/

4098 4099 4100 4101
/*
  Commit the current transaction
*/

4102
my_bool STDCALL mysql_commit(MYSQL * mysql)
4103 4104
{
  DBUG_ENTER("mysql_commit");
4105
  DBUG_RETURN((my_bool) mysql_real_query(mysql, "commit", 6));
4106 4107 4108
}

/*
4109
  Rollback the current transaction
4110 4111
*/

4112
my_bool STDCALL mysql_rollback(MYSQL * mysql)
4113 4114
{
  DBUG_ENTER("mysql_rollback");
4115
  DBUG_RETURN((my_bool) mysql_real_query(mysql, "rollback", 8));
4116 4117 4118 4119 4120 4121 4122
}


/*
  Set autocommit to either true or false
*/

4123
my_bool STDCALL mysql_autocommit(MYSQL * mysql, my_bool auto_mode)
4124 4125 4126 4127 4128
{
  DBUG_ENTER("mysql_autocommit");
  DBUG_PRINT("enter", ("mode : %d", auto_mode));

  if (auto_mode) /* set to true */
4129 4130
    DBUG_RETURN((my_bool) mysql_real_query(mysql, "set autocommit=1", 16));
  DBUG_RETURN((my_bool) mysql_real_query(mysql, "set autocommit=0", 16));
4131
}
4132 4133 4134


/********************************************************************
4135
 Multi query execution + SPs APIs
4136 4137 4138
*********************************************************************/

/*
4139 4140
  Returns true/false to indicate whether any more query results exist
  to be read using mysql_next_result()
4141 4142 4143 4144
*/

my_bool STDCALL mysql_more_results(MYSQL *mysql)
{
4145
  my_bool res;
4146 4147
  DBUG_ENTER("mysql_more_results");
  
4148 4149 4150 4151
  res= ((mysql->last_used_con->server_status & SERVER_MORE_RESULTS_EXISTS) ? 
	1: 0);
  DBUG_PRINT("exit",("More results exists ? %d", res)); 
  DBUG_RETURN(res);
4152 4153
}

4154

4155 4156 4157
/*
  Reads and returns the next query results
*/
4158
int STDCALL mysql_next_result(MYSQL *mysql)
4159
{
4160
  DBUG_ENTER("mysql_next_result");
4161
  
4162 4163 4164 4165 4166 4167 4168 4169
  if (mysql->status != MYSQL_STATUS_READY)
  {
    strmov(mysql->net.sqlstate, unknown_sqlstate);
    strmov(mysql->net.last_error,
	   ER(mysql->net.last_errno=CR_COMMANDS_OUT_OF_SYNC));
    DBUG_RETURN(1);
  }

4170 4171
  mysql->net.last_error[0]= 0;
  mysql->net.last_errno= 0;
4172
  strmov(mysql->net.sqlstate, not_error_sqlstate);
4173 4174 4175
  mysql->affected_rows= ~(my_ulonglong) 0;

  if (mysql->last_used_con->server_status & SERVER_MORE_RESULTS_EXISTS)
4176 4177
    DBUG_RETURN((*mysql->methods->next_result)(mysql));

4178
  DBUG_RETURN(-1);				/* No more results */
4179
}
hf@deer.(none)'s avatar
hf@deer.(none) committed
4180

4181

hf@deer.(none)'s avatar
hf@deer.(none) committed
4182 4183 4184 4185 4186 4187 4188 4189 4190 4191
MYSQL_RES * STDCALL mysql_use_result(MYSQL *mysql)
{
  return (*mysql->methods->use_result)(mysql);
}

my_bool STDCALL mysql_read_query_result(MYSQL *mysql)
{
  return (*mysql->methods->read_query_result)(mysql);
}