1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-27 18:02:13 +03:00

Auto-merge from mysql-next-mr.

This commit is contained in:
Alexander Nozdrin
2009-11-05 15:08:37 +03:00
457 changed files with 19019 additions and 8786 deletions

View File

@ -73,7 +73,7 @@ SET(CLIENT_SOURCES ../mysys/array.c ../strings/bchange.c ../strings/bmove.c
../mysys/hash.c ../mysys/my_sleep.c ../mysys/default_modify.c
get_password.c ../strings/int2str.c ../strings/is_prefix.c
libmysql.c ../mysys/list.c ../strings/llstr.c
../strings/longlong2str.c manager.c ../mysys/mf_arr_appstr.c ../mysys/mf_cache.c
../strings/longlong2str.c ../mysys/mf_arr_appstr.c ../mysys/mf_cache.c
../mysys/mf_dirname.c ../mysys/mf_fn_ext.c ../mysys/mf_format.c
../mysys/mf_iocache.c ../mysys/mf_iocache2.c ../mysys/mf_loadpath.c
../mysys/mf_pack.c ../mysys/mf_path.c ../mysys/mf_tempfile.c ../mysys/mf_unixpath.c

View File

@ -31,7 +31,7 @@ pkglib_LTLIBRARIES = $(target)
noinst_PROGRAMS = conf_to_src
target_sources = libmysql.c password.c manager.c \
target_sources = libmysql.c password.c \
get_password.c errmsg.c
mystringsobjects = strmov.lo strxmov.lo strxnmov.lo strnmov.lo \

View File

@ -22,9 +22,13 @@
extern uint mysql_port;
extern char * mysql_unix_port;
#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | \
CLIENT_TRANSACTIONS | \
CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION)
#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | \
CLIENT_LONG_FLAG | \
CLIENT_TRANSACTIONS | \
CLIENT_PROTOCOL_41 | \
CLIENT_SECURE_CONNECTION | \
CLIENT_MULTI_RESULTS | \
CLIENT_PS_MULTI_RESULTS)
sig_handler my_pipe_sig_handler(int sig);
void read_user_name(char *name);

View File

@ -85,6 +85,7 @@ const char *client_errors[]=
"Lost connection to MySQL server at '%s', system error: %d",
"Statement closed indirectly because of a preceeding %s() call",
"The number of columns in the result set differs from the number of bound buffers. You must reset the statement, rebind the result set columns, and execute the statement again",
"This handle is already connected. Use a separate handle for each connection."
""
};
@ -151,6 +152,7 @@ const char *client_errors[]=
"Lost connection to MySQL server at '%s', system error: %d",
"Statement closed indirectly because of a preceeding %s() call",
"The number of columns in the result set differs from the number of bound buffers. You must reset the statement, rebind the result set columns, and execute the statement again",
"This handle is already connected. Use a separate handle for each connection."
""
};
@ -215,6 +217,7 @@ const char *client_errors[]=
"Lost connection to MySQL server at '%s', system error: %d",
"Statement closed indirectly because of a preceeding %s() call",
"The number of columns in the result set differs from the number of bound buffers. You must reset the statement, rebind the result set columns, and execute the statement again",
"This handle is already connected. Use a separate handle for each connection."
""
};
#endif

View File

@ -249,16 +249,6 @@ void STDCALL mysql_thread_end()
#endif
}
/*
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.
*/
static MYSQL* spawn_init(MYSQL* parent, const char* host,
unsigned int port,
const char* user,
const char* passwd);
/*
Expand wildcard to a sql string
@ -320,7 +310,7 @@ mysql_debug(const char *debug __attribute__((unused)))
/**************************************************************************
Close the server connection if we get a SIGPIPE
Ignore SIGPIPE handler
ARGSUSED
**************************************************************************/
@ -333,305 +323,6 @@ my_pipe_sig_handler(int sig __attribute__((unused)))
#endif
}
/* perform query on master */
my_bool STDCALL mysql_master_query(MYSQL *mysql, const char *q,
unsigned long length)
{
DBUG_ENTER("mysql_master_query");
if (mysql_master_send_query(mysql, q, length))
DBUG_RETURN(1);
DBUG_RETURN((*mysql->methods->read_query_result)(mysql));
}
my_bool STDCALL mysql_master_send_query(MYSQL *mysql, const char *q,
unsigned long length)
{
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);
master->reconnect= 1;
mysql->last_used_con = master;
DBUG_RETURN(simple_command(master, COM_QUERY, (const uchar*) q, length, 1));
}
/* perform query on slave */
my_bool STDCALL mysql_slave_query(MYSQL *mysql, const char *q,
unsigned long length)
{
DBUG_ENTER("mysql_slave_query");
if (mysql_slave_send_query(mysql, q, length))
DBUG_RETURN(1);
DBUG_RETURN((*mysql->methods->read_query_result)(mysql));
}
my_bool STDCALL mysql_slave_send_query(MYSQL *mysql, const char *q,
unsigned long length)
{
MYSQL* last_used_slave, *slave_to_use = 0;
DBUG_ENTER("mysql_slave_send_query");
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,
0,0,0,0))
DBUG_RETURN(1);
slave_to_use->reconnect= 1;
DBUG_RETURN(simple_command(slave_to_use, COM_QUERY, (const uchar*) q,
length, 1));
}
/* enable/disable parsing of all queries to decide
if they go on master or slave */
void STDCALL mysql_enable_rpl_parse(MYSQL* mysql)
{
mysql->options.rpl_parse = 1;
}
void STDCALL mysql_disable_rpl_parse(MYSQL* mysql)
{
mysql->options.rpl_parse = 0;
}
/* get the value of the parse flag */
int STDCALL mysql_rpl_parse_enabled(MYSQL* mysql)
{
return mysql->options.rpl_parse;
}
/* enable/disable reads from master */
void STDCALL mysql_enable_reads_from_master(MYSQL* mysql)
{
mysql->options.no_master_reads = 0;
}
void STDCALL mysql_disable_reads_from_master(MYSQL* mysql)
{
mysql->options.no_master_reads = 1;
}
/* get the value of the master read flag */
my_bool STDCALL mysql_reads_from_master_enabled(MYSQL* mysql)
{
return !(mysql->options.no_master_reads);
}
/*
We may get an error while doing replication internals.
In this case, we add a special explanation to the original
error
*/
static void expand_error(MYSQL* mysql, int error)
{
char tmp[MYSQL_ERRMSG_SIZE];
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);
mysql->net.last_errno = error;
}
/*
This function assumes we have just called SHOW SLAVE STATUS and have
read the given result and row
*/
static my_bool get_master(MYSQL* mysql, MYSQL_RES* res, MYSQL_ROW row)
{
MYSQL* master;
DBUG_ENTER("get_master");
if (mysql_num_fields(res) < 3)
DBUG_RETURN(1); /* safety */
/* use the same username and password as the original connection */
if (!(master = spawn_init(mysql, row[0], atoi(row[2]), 0, 0)))
DBUG_RETURN(1);
mysql->master = master;
DBUG_RETURN(0);
}
/*
Assuming we already know that mysql points to a master connection,
retrieve all the slaves
*/
static my_bool get_slaves_from_master(MYSQL* mysql)
{
MYSQL_RES* res = 0;
MYSQL_ROW row;
my_bool error = 1;
int has_auth_info;
int port_ind;
DBUG_ENTER("get_slaves_from_master");
if (!mysql->net.vio && !mysql_real_connect(mysql,0,0,0,0,0,0,0))
{
expand_error(mysql, CR_PROBE_MASTER_CONNECT);
DBUG_RETURN(1);
}
mysql->reconnect= 1;
if (mysql_query(mysql, "SHOW SLAVE HOSTS") ||
!(res = mysql_store_result(mysql)))
{
expand_error(mysql, CR_PROBE_SLAVE_HOSTS);
DBUG_RETURN(1);
}
switch (mysql_num_fields(res)) {
case 5:
has_auth_info = 0;
port_ind=2;
break;
case 7:
has_auth_info = 1;
port_ind=4;
break;
default:
goto err;
}
while ((row = mysql_fetch_row(res)))
{
MYSQL* slave;
const char* tmp_user, *tmp_pass;
if (has_auth_info)
{
tmp_user = row[2];
tmp_pass = row[3];
}
else
{
tmp_user = mysql->user;
tmp_pass = mysql->passwd;
}
if (!(slave = spawn_init(mysql, row[1], atoi(row[port_ind]),
tmp_user, tmp_pass)))
goto err;
/* Now add slave into the circular linked list */
slave->next_slave = mysql->next_slave;
mysql->next_slave = slave;
}
error = 0;
err:
if (res)
mysql_free_result(res);
DBUG_RETURN(error);
}
my_bool STDCALL mysql_rpl_probe(MYSQL* mysql)
{
MYSQL_RES *res= 0;
MYSQL_ROW row;
my_bool error= 1;
DBUG_ENTER("mysql_rpl_probe");
/*
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
a non-empty master host. However, it is more reliable to check
for empty master than whether the slave thread is actually running
*/
if (mysql_query(mysql, "SHOW SLAVE STATUS") ||
!(res = mysql_store_result(mysql)))
{
expand_error(mysql, CR_PROBE_SLAVE_STATUS);
DBUG_RETURN(1);
}
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]))
{
/* this is a slave, ask it for the master */
if (get_master(mysql, res, row) || get_slaves_from_master(mysql))
goto err;
}
else
{
mysql->master = mysql;
if (get_slaves_from_master(mysql))
goto err;
}
error = 0;
err:
if (res)
mysql_free_result(res);
DBUG_RETURN(error);
}
/*
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
*/
enum mysql_rpl_type
STDCALL mysql_rpl_query_type(const char* q, int len)
{
const char *q_end= q + len;
for (; q < q_end; ++q)
{
char c;
if (my_isalpha(&my_charset_latin1, (c= *q)))
{
switch (my_tolower(&my_charset_latin1,c)) {
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 */
return my_tolower(&my_charset_latin1,q[1]) == 'h' ? MYSQL_RPL_ADMIN :
MYSQL_RPL_MASTER;
case 's': /* select or show */
return my_tolower(&my_charset_latin1,q[1]) == 'h' ? MYSQL_RPL_ADMIN :
MYSQL_RPL_SLAVE;
case 'f': /* flush */
case 'r': /* repair */
case 'g': /* grant */
return MYSQL_RPL_ADMIN;
default:
return MYSQL_RPL_SLAVE;
}
}
}
return MYSQL_RPL_MASTER; /* By default, send to master */
}
/**************************************************************************
Connect to sql server
@ -1093,68 +784,6 @@ mysql_query(MYSQL *mysql, const char *query)
}
static MYSQL* spawn_init(MYSQL* parent, const char* host,
unsigned int port, const char* user,
const char* passwd)
{
MYSQL* child;
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));
if (parent->db)
child->options.db= my_strdup(parent->db, MYF(0));
else if (parent->options.db)
child->options.db= my_strdup(parent->options.db, MYF(0));
/*
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);
}
int
STDCALL mysql_set_master(MYSQL* mysql, const char* host,
unsigned int port, const char* user,
const char* passwd)
{
if (mysql->master != mysql && !mysql->master->rpl_pivot)
mysql_close(mysql->master);
if (!(mysql->master = spawn_init(mysql, host, port, user, passwd)))
return 1;
return 0;
}
int
STDCALL mysql_add_slave(MYSQL* mysql, const char* host,
unsigned int port,
const char* user,
const char* passwd)
{
MYSQL* slave;
if (!(slave = spawn_init(mysql, host, port, user, passwd)))
return 1;
slave->next_slave = mysql->next_slave;
mysql->next_slave = slave;
return 0;
}
/**************************************************************************
Return next field of the query results
**************************************************************************/
@ -1483,17 +1112,17 @@ MYSQL_FIELD_OFFSET STDCALL mysql_field_tell(MYSQL_RES *res)
unsigned int STDCALL mysql_field_count(MYSQL *mysql)
{
return mysql->last_used_con->field_count;
return mysql->field_count;
}
my_ulonglong STDCALL mysql_affected_rows(MYSQL *mysql)
{
return mysql->last_used_con->affected_rows;
return mysql->affected_rows;
}
my_ulonglong STDCALL mysql_insert_id(MYSQL *mysql)
{
return mysql->last_used_con->insert_id;
return mysql->insert_id;
}
const char *STDCALL mysql_sqlstate(MYSQL *mysql)
@ -1858,7 +1487,6 @@ my_bool cli_read_prepare_result(MYSQL *mysql, MYSQL_STMT *stmt)
MYSQL_DATA *fields_data;
DBUG_ENTER("cli_read_prepare_result");
mysql= mysql->last_used_con;
if ((packet_length= cli_safe_read(mysql)) == packet_error)
DBUG_RETURN(1);
mysql->warning_count= 0;
@ -2092,7 +1720,9 @@ static void alloc_stmt_fields(MYSQL_STMT *stmt)
{
MYSQL_FIELD *fields, *field, *end;
MEM_ROOT *alloc= &stmt->mem_root;
MYSQL *mysql= stmt->mysql->last_used_con;
MYSQL *mysql= stmt->mysql;
DBUG_ASSERT(mysql->field_count);
stmt->field_count= mysql->field_count;
@ -2115,18 +1745,21 @@ static void alloc_stmt_fields(MYSQL_STMT *stmt)
field= stmt->fields;
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);
field->charsetnr= fields->charsetnr;
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;
*field= *fields; /* To copy all numeric parts. */
field->catalog= strmake_root(alloc, fields->catalog,
fields->catalog_length);
field->db= strmake_root(alloc, fields->db, fields->db_length);
field->table= strmake_root(alloc, fields->table, fields->table_length);
field->org_table= strmake_root(alloc, fields->org_table,
fields->org_table_length);
field->name= strmake_root(alloc, fields->name, fields->name_length);
field->org_name= strmake_root(alloc, fields->org_name,
fields->org_name_length);
field->def= fields->def ? strmake_root(alloc, fields->def,
fields->def_length) : 0;
field->def_length= field->def ? fields->def_length : 0;
field->extension= 0; /* Avoid dangling links. */
field->max_length= 0; /* max_length is set in mysql_stmt_store_result() */
}
}
@ -2479,7 +2112,6 @@ static my_bool execute(MYSQL_STMT *stmt, char *packet, ulong length)
DBUG_ENTER("execute");
DBUG_DUMP("packet", (uchar *) packet, length);
mysql->last_used_con= mysql;
int4store(buff, stmt->stmt_id); /* Send stmt id to server */
buff[4]= (char) stmt->flags;
int4store(buff+5, 1); /* iteration count */
@ -2854,6 +2486,33 @@ static void reinit_result_set_metadata(MYSQL_STMT *stmt)
}
static void prepare_to_fetch_result(MYSQL_STMT *stmt)
{
if (stmt->server_status & SERVER_STATUS_CURSOR_EXISTS)
{
stmt->mysql->status= MYSQL_STATUS_READY;
stmt->read_row_func= stmt_read_row_from_cursor;
}
else if (stmt->flags & CURSOR_TYPE_READ_ONLY)
{
/*
This is a single-row result set, a result set with no rows, EXPLAIN,
SHOW VARIABLES, or some other command which either a) bypasses the
cursors framework in the server and writes rows directly to the
network or b) is more efficient if all (few) result set rows are
precached on client and server's resources are freed.
*/
mysql_stmt_store_result(stmt);
}
else
{
stmt->mysql->unbuffered_fetch_owner= &stmt->unbuffered_fetch_cancelled;
stmt->unbuffered_fetch_cancelled= FALSE;
stmt->read_row_func= stmt_read_row_unbuffered;
}
}
/*
Send placeholders data to server (if there are placeholders)
and execute prepared statement.
@ -2921,28 +2580,7 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt)
if (mysql->field_count)
{
reinit_result_set_metadata(stmt);
if (stmt->server_status & SERVER_STATUS_CURSOR_EXISTS)
{
mysql->status= MYSQL_STATUS_READY;
stmt->read_row_func= stmt_read_row_from_cursor;
}
else if (stmt->flags & CURSOR_TYPE_READ_ONLY)
{
/*
This is a single-row result set, a result set with no rows, EXPLAIN,
SHOW VARIABLES, or some other command which either a) bypasses the
cursors framework in the server and writes rows directly to the
network or b) is more efficient if all (few) result set rows are
precached on client and server's resources are freed.
*/
mysql_stmt_store_result(stmt);
}
else
{
stmt->mysql->unbuffered_fetch_owner= &stmt->unbuffered_fetch_cancelled;
stmt->unbuffered_fetch_cancelled= FALSE;
stmt->read_row_func= stmt_read_row_unbuffered;
}
prepare_to_fetch_result(stmt);
}
DBUG_RETURN(test(stmt->last_errno));
}
@ -4407,7 +4045,6 @@ static my_bool setup_one_fetch_function(MYSQL_BIND *param, MYSQL_FIELD *field)
field->max_length= 10; /* 2003-11-11 */
param->skip_result= skip_result_with_length;
break;
break;
case MYSQL_TYPE_DATETIME:
case MYSQL_TYPE_TIMESTAMP:
param->skip_result= skip_result_with_length;
@ -4689,7 +4326,6 @@ int cli_read_binary_rows(MYSQL_STMT *stmt)
}
net = &mysql->net;
mysql= mysql->last_used_con;
while ((pkt_len= cli_safe_read(mysql)) != packet_error)
{
@ -4787,8 +4423,6 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt)
DBUG_RETURN(1);
}
mysql= mysql->last_used_con;
if (!stmt->field_count)
DBUG_RETURN(0);
@ -4990,7 +4624,7 @@ static my_bool reset_stmt_handle(MYSQL_STMT *stmt, uint flags)
if (stmt->field_count && mysql->status != MYSQL_STATUS_READY)
{
/* There is a result set and it belongs to this statement */
(*mysql->methods->flush_use_result)(mysql);
(*mysql->methods->flush_use_result)(mysql, FALSE);
if (mysql->unbuffered_fetch_owner)
*mysql->unbuffered_fetch_owner= TRUE;
mysql->status= MYSQL_STATUS_READY;
@ -5074,7 +4708,7 @@ my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt)
Flush result set of the connection. If it does not belong
to this statement, set a warning.
*/
(*mysql->methods->flush_use_result)(mysql);
(*mysql->methods->flush_use_result)(mysql, TRUE);
if (mysql->unbuffered_fetch_owner)
*mysql->unbuffered_fetch_owner= TRUE;
mysql->status= MYSQL_STATUS_READY;
@ -5193,8 +4827,7 @@ my_bool STDCALL mysql_more_results(MYSQL *mysql)
my_bool res;
DBUG_ENTER("mysql_more_results");
res= ((mysql->last_used_con->server_status & SERVER_MORE_RESULTS_EXISTS) ?
1: 0);
res= ((mysql->server_status & SERVER_MORE_RESULTS_EXISTS) ? 1: 0);
DBUG_PRINT("exit",("More results exists ? %d", res));
DBUG_RETURN(res);
}
@ -5216,13 +4849,56 @@ int STDCALL mysql_next_result(MYSQL *mysql)
net_clear_error(&mysql->net);
mysql->affected_rows= ~(my_ulonglong) 0;
if (mysql->last_used_con->server_status & SERVER_MORE_RESULTS_EXISTS)
if (mysql->server_status & SERVER_MORE_RESULTS_EXISTS)
DBUG_RETURN((*mysql->methods->next_result)(mysql));
DBUG_RETURN(-1); /* No more results */
}
int STDCALL mysql_stmt_next_result(MYSQL_STMT *stmt)
{
MYSQL *mysql= stmt->mysql;
int rc;
DBUG_ENTER("mysql_stmt_next_result");
if (!mysql)
DBUG_RETURN(1);
if (stmt->last_errno)
DBUG_RETURN(stmt->last_errno);
if (mysql->server_status & SERVER_MORE_RESULTS_EXISTS)
{
if (reset_stmt_handle(stmt, RESET_STORE_RESULT))
DBUG_RETURN(1);
}
rc= mysql_next_result(mysql);
if (rc)
{
set_stmt_errmsg(stmt, &mysql->net);
DBUG_RETURN(rc);
}
stmt->state= MYSQL_STMT_EXECUTE_DONE;
stmt->bind_result_done= FALSE;
if (mysql->field_count)
{
alloc_stmt_fields(stmt);
prepare_to_fetch_result(stmt);
}
else
{
stmt->field_count= mysql->field_count;
}
DBUG_RETURN(0);
}
MYSQL_RES * STDCALL mysql_use_result(MYSQL *mysql)
{
return (*mysql->methods->use_result)(mysql);

View File

@ -135,15 +135,6 @@ EXPORTS
client_errors
mysql_set_local_infile_default
mysql_set_local_infile_handler
mysql_disable_reads_from_master
mysql_disable_rpl_parse
mysql_enable_reads_from_master
mysql_enable_rpl_parse
mysql_master_query
mysql_rpl_parse_enabled
mysql_rpl_probe
mysql_rpl_query_type
mysql_slave_query
mysql_embedded
mysql_server_init
mysql_server_end

View File

@ -1,269 +0,0 @@
/* Copyright (C) 2000-2004 MySQL AB
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.
There are special exceptions to the terms and conditions of the GPL as it
is applied to this software. View the full text of the exception in file
EXCEPTIONS-CLIENT in the directory of this software distribution.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
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 */
#include <my_global.h>
#if defined(THREAD)
#include <my_pthread.h> /* because of signal() */
#endif
#include "mysql.h"
#include "mysql_version.h"
#include "mysqld_error.h"
#include <my_sys.h>
#include <mysys_err.h>
#include <m_string.h>
#include <m_ctype.h>
#include <my_net.h>
#include <errmsg.h>
#include <violite.h>
#include <sys/stat.h>
#include <signal.h>
#include <errno.h>
#if defined(__NETWARE__)
#include <netdb.h>
#include <sys/select.h>
#include <sys/utsname.h>
#elif !defined( __WIN__)
#include <sys/resource.h>
#ifdef HAVE_SYS_UN_H
# include <sys/un.h>
#endif
#include <netdb.h>
#ifdef HAVE_SELECT_H
# include <select.h>
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#include <sys/utsname.h>
#endif /* __WIN__ */
#ifndef INADDR_NONE
#define INADDR_NONE -1
#endif
#define RES_BUF_SHIFT 5
#define NET_BUF_SIZE 2048
MYSQL_MANAGER* STDCALL mysql_manager_init(MYSQL_MANAGER* con)
{
int net_buf_size=NET_BUF_SIZE;
if (!con)
{
if (!(con=(MYSQL_MANAGER*)my_malloc(sizeof(*con)+net_buf_size,
MYF(MY_WME|MY_ZEROFILL))))
return 0;
con->free_me=1;
con->net_buf=(char*)con+sizeof(*con);
}
else
{
bzero((char*)con,sizeof(*con));
if (!(con->net_buf=my_malloc(net_buf_size,MYF(0))))
return 0;
}
con->net_buf_pos=con->net_data_end=con->net_buf;
con->net_buf_size=net_buf_size;
return con;
}
MYSQL_MANAGER* STDCALL mysql_manager_connect(MYSQL_MANAGER* con,
const char* host,
const char* user,
const char* passwd,
unsigned int port)
{
my_socket sock;
struct sockaddr_in sock_addr;
in_addr_t ip_addr;
char msg_buf[MAX_MYSQL_MANAGER_MSG];
int msg_len;
Vio* vio;
my_bool not_used;
if (!host)
host="localhost";
if (!user)
user="root";
if (!passwd)
passwd="";
if ((sock=(my_socket)socket(AF_INET,SOCK_STREAM,0)) == INVALID_SOCKET)
{
con->last_errno=errno;
strmov(con->last_error,"Cannot create socket");
goto err;
}
if (!(vio=vio_new(sock,VIO_TYPE_TCPIP,FALSE)))
{
con->last_errno=ENOMEM;
strmov(con->last_error,"Cannot create network I/O object");
goto err;
}
vio_blocking(vio, TRUE, &not_used);
my_net_init(&con->net,vio);
bzero((char*) &sock_addr,sizeof(sock_addr));
sock_addr.sin_family = AF_INET;
if ((int) (ip_addr = inet_addr(host)) != (int) INADDR_NONE)
{
memcpy_fixed(&sock_addr.sin_addr,&ip_addr,sizeof(ip_addr));
}
else
{
int tmp_errno;
struct hostent tmp_hostent,*hp;
char buff2[GETHOSTBYNAME_BUFF_SIZE];
hp = my_gethostbyname_r(host,&tmp_hostent,buff2,sizeof(buff2),
&tmp_errno);
if (!hp)
{
con->last_errno=tmp_errno;
sprintf(con->last_error,"Could not resolve host '%-.64s'",host);
my_gethostbyname_r_free();
goto err;
}
memcpy(&sock_addr.sin_addr,hp->h_addr, (size_t) hp->h_length);
my_gethostbyname_r_free();
}
sock_addr.sin_port = (ushort) htons((ushort) port);
if (my_connect(sock,(struct sockaddr *) &sock_addr, sizeof(sock_addr),
0))
{
con->last_errno=errno;
sprintf(con->last_error ,"Could not connect to %-.64s", host);
goto err;
}
/* read the greating */
if (my_net_read(&con->net) == packet_error)
{
con->last_errno=errno;
strmov(con->last_error,"Read error on socket");
goto err;
}
sprintf(msg_buf,"%-.16s %-.16s\n",user,passwd);
msg_len=strlen(msg_buf);
if (my_net_write(&con->net,(uchar*) msg_buf,msg_len) || net_flush(&con->net))
{
con->last_errno=con->net.last_errno;
strmov(con->last_error,"Write error on socket");
goto err;
}
if (my_net_read(&con->net) == packet_error)
{
con->last_errno=errno;
strmov(con->last_error,"Read error on socket");
goto err;
}
if ((con->cmd_status=atoi((char*) con->net.read_pos)) != MANAGER_OK)
{
strmov(con->last_error,"Access denied");
goto err;
}
if (!my_multi_malloc(MYF(0), &con->host, (uint)strlen(host)+1,
&con->user, (uint)strlen(user)+1,
&con->passwd, (uint)strlen(passwd)+1,
NullS))
{
con->last_errno=ENOMEM;
strmov(con->last_error,"Out of memory");
goto err;
}
strmov(con->host,host);
strmov(con->user,user);
strmov(con->passwd,passwd);
return con;
err:
{
my_bool free_me=con->free_me;
con->free_me=0;
mysql_manager_close(con);
con->free_me=free_me;
}
return 0;
}
void STDCALL mysql_manager_close(MYSQL_MANAGER* con)
{
/*
No need to free con->user and con->passwd, because they were
allocated in my_multimalloc() along with con->host, freeing
con->hosts frees the whole block
*/
my_free((uchar*)con->host,MYF(MY_ALLOW_ZERO_PTR));
net_end(&con->net);
if (con->free_me)
my_free((uchar*)con,MYF(0));
}
int STDCALL mysql_manager_command(MYSQL_MANAGER* con,const char* cmd,
int cmd_len)
{
if (!cmd_len)
cmd_len=strlen(cmd);
if (my_net_write(&con->net,(const uchar*)cmd,cmd_len) || net_flush(&con->net))
{
con->last_errno=errno;
strmov(con->last_error,"Write error on socket");
return 1;
}
con->eof=0;
return 0;
}
int STDCALL mysql_manager_fetch_line(MYSQL_MANAGER* con, char* res_buf,
int res_buf_size)
{
char* res_buf_end=res_buf+res_buf_size;
char* net_buf=(char*) con->net.read_pos, *net_buf_end;
int res_buf_shift=RES_BUF_SHIFT;
ulong num_bytes;
if (res_buf_size<RES_BUF_SHIFT)
{
con->last_errno=ENOMEM;
strmov(con->last_error,"Result buffer too small");
return 1;
}
if ((num_bytes=my_net_read(&con->net)) == packet_error)
{
con->last_errno=errno;
strmov(con->last_error,"socket read failed");
return 1;
}
net_buf_end=net_buf+num_bytes;
if ((con->eof=(net_buf[3]==' ')))
res_buf_shift--;
net_buf+=res_buf_shift;
res_buf_end[-1]=0;
for (;net_buf<net_buf_end && res_buf < res_buf_end;res_buf++,net_buf++)
{
if ((*res_buf=*net_buf) == '\r')
{
*res_buf=0;
break;
}
}
return 0;
}