Commit e6e25a56 authored by tim@black.box's avatar tim@black.box

Add some examples for using libmysqld, including a hack for running the

mysql test suite.

A few minor libmysqld fixes.

Add mysql_server_init() and _end() to mysql.cc and mysqltest.c, so they
can be linked against libmysqlclient or libmysqld.
parent fa23b207
......@@ -400,3 +400,4 @@ vio/test-sslclient
vio/test-sslserver
vio/viotest-ssl
tools/mysqlmanager
linked_libmysqldex_sources
......@@ -29,7 +29,8 @@ SUBDIRS = include @docs_dirs@ @readline_dir@ \
# Relink after clean
linked_sources = linked_client_sources linked_server_sources \
linked_libmysql_sources linked_libmysql_r_sources \
linked_libmysqld_sources linked_include_sources
linked_libmysqld_sources linked_libmysqldex_sources \
linked_include_sources
CLEANFILES = $(linked_sources)
......@@ -56,6 +57,10 @@ linked_libmysqld_sources:
cd libmysqld; $(MAKE) link_sources
echo timestamp > linked_libmysqld_sources
linked_libmysqldex_sources:
cd libmysqld/examples; $(MAKE) link_sources
echo timestamp > linked_libmysqldex_sources
#avoid recursive make calls in sql directory
linked_server_sources:
cd sql; rm -f mini_client_errors.c;@LN_CP_F@ ../libmysql/errmsg.c mini_client_errors.c
......
......@@ -1071,42 +1071,42 @@ AC_DEFUN([MYSQL_CHECK_INNODB], [
dnl Some libs are listed several times, in order for gcc to sort out
dnl circular references.
innodb_libs="\
../innobase/usr/libusr.a\
../innobase/odbc/libodbc.a\
../innobase/srv/libsrv.a\
../innobase/que/libque.a\
../innobase/srv/libsrv.a\
../innobase/dict/libdict.a\
../innobase/ibuf/libibuf.a\
../innobase/row/librow.a\
../innobase/pars/libpars.a\
../innobase/btr/libbtr.a\
../innobase/trx/libtrx.a\
../innobase/read/libread.a\
../innobase/usr/libusr.a\
../innobase/buf/libbuf.a\
../innobase/ibuf/libibuf.a\
../innobase/eval/libeval.a\
../innobase/log/liblog.a\
../innobase/fsp/libfsp.a\
../innobase/fut/libfut.a\
../innobase/fil/libfil.a\
../innobase/lock/liblock.a\
../innobase/mtr/libmtr.a\
../innobase/page/libpage.a\
../innobase/rem/librem.a\
../innobase/thr/libthr.a\
../innobase/com/libcom.a\
../innobase/sync/libsync.a\
../innobase/data/libdata.a\
../innobase/mach/libmach.a\
../innobase/ha/libha.a\
../innobase/dyn/libdyn.a\
../innobase/mem/libmem.a\
../innobase/sync/libsync.a\
../innobase/ut/libut.a\
../innobase/os/libos.a\
../innobase/ut/libut.a"
$(top_builddir)/innobase/usr/libusr.a\
$(top_builddir)/innobase/odbc/libodbc.a\
$(top_builddir)/innobase/srv/libsrv.a\
$(top_builddir)/innobase/que/libque.a\
$(top_builddir)/innobase/srv/libsrv.a\
$(top_builddir)/innobase/dict/libdict.a\
$(top_builddir)/innobase/ibuf/libibuf.a\
$(top_builddir)/innobase/row/librow.a\
$(top_builddir)/innobase/pars/libpars.a\
$(top_builddir)/innobase/btr/libbtr.a\
$(top_builddir)/innobase/trx/libtrx.a\
$(top_builddir)/innobase/read/libread.a\
$(top_builddir)/innobase/usr/libusr.a\
$(top_builddir)/innobase/buf/libbuf.a\
$(top_builddir)/innobase/ibuf/libibuf.a\
$(top_builddir)/innobase/eval/libeval.a\
$(top_builddir)/innobase/log/liblog.a\
$(top_builddir)/innobase/fsp/libfsp.a\
$(top_builddir)/innobase/fut/libfut.a\
$(top_builddir)/innobase/fil/libfil.a\
$(top_builddir)/innobase/lock/liblock.a\
$(top_builddir)/innobase/mtr/libmtr.a\
$(top_builddir)/innobase/page/libpage.a\
$(top_builddir)/innobase/rem/librem.a\
$(top_builddir)/innobase/thr/libthr.a\
$(top_builddir)/innobase/com/libcom.a\
$(top_builddir)/innobase/sync/libsync.a\
$(top_builddir)/innobase/data/libdata.a\
$(top_builddir)/innobase/mach/libmach.a\
$(top_builddir)/innobase/ha/libha.a\
$(top_builddir)/innobase/dyn/libdyn.a\
$(top_builddir)/innobase/mem/libmem.a\
$(top_builddir)/innobase/sync/libsync.a\
$(top_builddir)/innobase/ut/libut.a\
$(top_builddir)/innobase/os/libos.a\
$(top_builddir)/innobase/ut/libut.a"
AC_CHECK_LIB(rt, aio_read, [innodb_libs="$innodb_libs -lrt"])
;;
......
......@@ -248,6 +248,7 @@ static COMMANDS commands[] = {
};
static const char *load_default_groups[]= { "mysql","client",0 };
static const char *server_default_groups[]= { "server", "mysql_SERVER", 0 };
#ifdef HAVE_READLINE
extern "C" void add_history(char *command); /* From readline directory */
......@@ -275,6 +276,7 @@ int main(int argc,char *argv[])
{
char buff[80];
mysql_server_init(0, NULL, server_default_groups);
MY_INIT(argv[0]);
DBUG_ENTER("main");
DBUG_PROCESS(argv[0]);
......@@ -368,6 +370,7 @@ int main(int argc,char *argv[])
if (opt_outfile)
end_tee();
mysql_end(0);
mysql_server_end();
#ifndef _lint
DBUG_RETURN(0); // Keep compiler happy
#endif
......@@ -377,9 +380,11 @@ sig_handler mysql_end(int sig)
{
if (connected)
mysql_close(&mysql);
#ifdef HAVE_OPENSSL
else
mysql_ssl_clear(&mysql); /* SSL data structres should be freed
even if connection was not made */
#endif
#ifdef HAVE_READLINE
if (!status.batch && !quick && !opt_html && !opt_xml)
{
......@@ -2217,9 +2222,11 @@ sql_real_connect(char *host,char *database,char *user,char *password,
mysql_close(&mysql);
connected= 0;
}
#ifdef HAVE_OPENSSL
else
mysql_ssl_clear(&mysql); /* SSL data structres should be freed
even if connection was not made */
#endif
mysql_init(&mysql);
if (opt_connect_timeout)
{
......@@ -2571,6 +2578,7 @@ static void mysql_end_timer(ulong start_time,char *buff)
strmov(strend(buff),")");
}
#ifndef EMBEDDED_SERVER
/* Keep sql_string library happy */
gptr sql_alloc(unsigned int Size)
......@@ -2582,3 +2590,4 @@ void sql_element_free(void *ptr)
{
my_free((gptr) ptr,MYF(0));
}
#endif /* EMBEDDED_SERVER */
......@@ -1931,12 +1931,26 @@ static void init_var_hash()
var_from_env("BIG_TEST", opt_big_test ? "1" : "0");
}
static const char *embedded_server_args[] = {
"", /* XXX: argv[0] is program name - we should fix the API */
"--datadir=.",
"--language=/home/tim/my/4/sql/share/english",
"--skip-innodb",
NullS
};
static const char *embedded_server_groups[] = {
"mysql-test-server",
NullS
};
int main(int argc, char** argv)
{
int error = 0;
struct st_query* q;
my_bool require_file=0, q_send_flag=0;
char save_file[FN_REFLEN];
mysql_server_init(sizeof(embedded_server_args) / sizeof(char *) - 1,
embedded_server_args, embedded_server_groups);
MY_INIT(argv[0]);
save_file[0]=0;
......@@ -2100,6 +2114,7 @@ int main(int argc, char** argv)
printf("ok\n");
}
mysql_server_end();
free_used_memory();
exit(error ? 1 : 0);
return error ? 1 : 0; /* Keep compiler happy */
......
......@@ -1804,7 +1804,7 @@ AC_ARG_WITH(readline,
if test "$with_readline" = "yes"
then
readline_dir="readline"
readline_link="../readline/libreadline.a"
readline_link="\$(top_builddir)/readline/libreadline.a"
else
# This requires readline to be in a standard place. Mosty for linux
# there readline may be a shared library.
......@@ -2219,7 +2219,8 @@ AC_OUTPUT(Makefile extra/Makefile mysys/Makefile isam/Makefile \
bdb/Makefile \
myisam/Makefile myisammrg/Makefile \
man/Makefile readline/Makefile vio/Makefile \
libmysql_r/Makefile libmysqld/Makefile libmysql/Makefile client/Makefile \
libmysql_r/Makefile libmysqld/Makefile libmysqld/examples/Makefile \
libmysql/Makefile client/Makefile \
pstack/Makefile sql/Makefile sql/share/Makefile \
merge/Makefile dbug/Makefile scripts/Makefile \
include/Makefile sql-bench/Makefile tools/Makefile \
......
bin_PROGRAMS = mysqltest mysql
client_sources = $(mysqltest_SOURCES) $(mysql_SOURCES)
link_sources:
for f in $(client_sources); do \
rm -f $(srcdir)/$$f; \
@LN_CP_F@ $(srcdir)/../../client/$$f $(srcdir)/$$f; \
done;
DEFS = -DEMBEDDED_SERVER
INCLUDES = -I$(top_srcdir)/include $(openssl_includes) \
-I$(srcdir) -I$(top_srcdir) -I..
LIBS = @LIBS@
LDADD = $(top_builddir)/libmysqld/libmysqld.la \
$(top_builddir)/isam/libnisam.a \
$(top_builddir)/myisam/libmyisam.a \
$(top_builddir)/heap/libheap.a \
$(top_builddir)/merge/libmerge.a \
$(top_builddir)/myisammrg/libmyisammrg.a \
@innodb_libs@ @bdb_libs@ \
$(top_builddir)/mysys/libmysys.a \
$(top_builddir)/strings/libmystrings.a \
$(top_builddir)/dbug/libdbug.a \
$(top_builddir)/regex/libregex.a
mysqltest_DEPENDENCIES = ../libmysqld.la
mysqltest_SOURCES = mysqltest.c
mysql_SOURCES = mysql.cc readline.cc sql_string.cc completion_hash.cc \
my_readline.h sql_string.h completion_hash.h
mysql_LDADD = @readline_link@ @TERMCAP_LIB@ $(LDADD)
clean:
rm -f $(client_sources)
#! /bin/sh
# This is slapped together as a quick way to run the tests and
# is not meant for prime time. Please hack at it and submit
# changes, though, so we can gradually turn it into something
# that will run on all platforms (or incorporate it into the
# standard mysql-test-run).
#test_data_dir=/tmp/mysql-data
test_data_dir=../../mysql-test/var/master-data
cd "$test_data_dir" || {
echo "can't cd to $test_data_dir" >&2
exit 1
}
# All paths below must be relative to $test_data_dir
#top_builddir=/home/tim/my/4
top_builddir=../../..
mysql_test_dir=$top_builddir/mysql-test
examples=$top_builddir/libmysqld/examples
mysqltest=$examples/mysqltest
testdir=./test
gdb=0
list=0
run=
tests=
start=
cr="
"
er="\b\b\b\b\b\b\b\b"
usage () {
cat <<EOF
usage: $0 [-g|-h|-r] [test-name ...]
-g | --gdb run $mysqltest in gdb
-h | --help show this help
-l | --list ) list all available tests
-r | --run automatically 'run' program in gdb
-s t | --start=t start with test t (skip all tests before t)
EOF
}
while test $# -gt 0
do
arg=
argset=0
case "$1" in
--?*=* ) arg=`echo "$1" | sed -e 's,^[^=][^=]*=,,'`; argset=1 ;;
esac
case "$1" in
-g | --gdb ) gdb=1; shift;;
-h | --help | -\? ) usage; exit 0;;
-l | --list ) list=1 ; shift ;;
-r | --run ) run="${cr}run"; shift;;
-s | --start=* )
test $argset -eq 0 && { shift; arg="$1"; }
start="$arg"
shift
;;
-* ) usage; exit 1;;
* ) tests="$tests $1"; shift;;
esac
done
test -d "$mysql_test_dir/t" -a -d "$mysql_test_dir/r" -a \
-f $mysqltest -a -d $testdir || {
echo "bad setup (is '$testdir', from '$test_data_dir', missing?)" >&2
exit 1
}
test -n "$tests" ||
tests=`/bin/ls -1 "$mysql_test_dir"/t/*.test | grep -v '^.*/rpl[^/]*$' | \
sed -e 's,^.*/,,' -e 's,.test$,,'`
echo "cleaning data directory '$test_data_dir'"
rm -f $test_data_dir/ib_* $test_data_dir/ibdata* log.*
echo "cleaning test directory '$testdir'"
rm -f $testdir/*
rm -f test-gdbinit
TZ=GMT-3; export TZ
skip=1
test -z "$start" && skip=0
for b in $tests
do
test $list -eq 1 && { echo " $b"; continue; }
test $skip -eq 1 && test -n "$start" && test "$start" = "$b" && skip=0
test $skip -eq 1 && { echo "skipping '$b'"; continue; }
t="$mysql_test_dir/t/$b.test"
r="$mysql_test_dir/r/$b.result"
c="$mysql_test_dir/r/$b.reject"
# Only test if $t exists; there is no $r for some tests
test -f $t || {
echo "test '$b' doesn't exist" >&2
continue
}
args="-v -S /tmp/mysql.sock -R $r -x $t test"
echo "set args $args$run" > test-gdbinit
#if false && test -n "$run"
if test -n "$run" -o $gdb -eq 1
then
echo -e "$er>>> $b"
else
echo -e "$er>>> $b> \c"
read junk
fi
if test $gdb -eq 1
then
if [ -x "$top_builddir/libtool" ]; then
$top_builddir/libtool gdb -x test-gdbinit -q $mysqltest
else
gdb -x test-gdbinit -q $mysqltest
fi
res=$?
rm -f test-gdbinit
else
$mysqltest $args
res=$?
fi
test $res -eq 0 || echo "!!! error: $res"
done
......@@ -98,6 +98,11 @@ static ulong mysql_sub_escape_string(CHARSET_INFO *charset_info, char *to,
#define set_sigpipe(mysql)
#define reset_sigpipe(mysql)
static MYSQL* spawn_init(MYSQL* parent, const char* host,
unsigned int port,
const char* user,
const char* passwd);
/*****************************************************************************
** read a packet from server. Give error message if socket was down
** or packet is an error message
......@@ -762,6 +767,301 @@ read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row, ulong *lengths)
return 0;
}
/* perform query on master */
int STDCALL mysql_master_query(MYSQL *mysql, const char *q,
unsigned int length)
{
if(mysql_master_send_query(mysql, q, length))
return 1;
return mysql_read_query_result(mysql);
}
int STDCALL mysql_master_send_query(MYSQL *mysql, const char *q,
unsigned int length)
{
MYSQL*master = mysql->master;
if (!length)
length = strlen(q);
if (!master->net.vio && !mysql_real_connect(master,0,0,0,0,0,0,0))
return 1;
mysql->last_used_con = master;
return simple_command(master, COM_QUERY, q, length, 1);
}
/* perform query on slave */
int STDCALL mysql_slave_query(MYSQL *mysql, const char *q,
unsigned int length)
{
if(mysql_slave_send_query(mysql, q, length))
return 1;
return mysql_read_query_result(mysql);
}
int STDCALL mysql_slave_send_query(MYSQL *mysql, const char *q,
unsigned int length)
{
MYSQL* last_used_slave, *slave_to_use = 0;
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(!length)
length = strlen(q);
if(!slave_to_use->net.vio && !mysql_real_connect(slave_to_use, 0,0,0,
0,0,0,0))
return 1;
return simple_command(slave_to_use, COM_QUERY, 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 */
int 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 inline void expand_error(MYSQL* mysql, int error)
{
char tmp[MYSQL_ERRMSG_SIZE];
char* p, *tmp_end;
tmp_end = strnmov(tmp, mysql->net.last_error, MYSQL_ERRMSG_SIZE);
p = strnmov(mysql->net.last_error, ER(error), MYSQL_ERRMSG_SIZE);
memcpy(p, tmp, tmp_end - tmp);
mysql->net.last_errno = error;
}
/* This function assumes we have just called SHOW SLAVE STATUS and have
read the given result and row
*/
static inline int get_master(MYSQL* mysql, MYSQL_RES* res, MYSQL_ROW row)
{
MYSQL* master;
if(mysql_num_fields(res) < 3)
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)))
return 1;
mysql->master = master;
return 0;
}
/* assuming we already know that mysql points to a master connection,
retrieve all the slaves
*/
static inline int get_slaves_from_master(MYSQL* mysql)
{
MYSQL_RES* res = 0;
MYSQL_ROW row;
int error = 1;
int has_auth_info;
if (!mysql->net.vio && !mysql_real_connect(mysql,0,0,0,0,0,0,0))
{
expand_error(mysql, CR_PROBE_MASTER_CONNECT);
return 1;
}
if (mysql_query(mysql, "SHOW SLAVE HOSTS") ||
!(res = mysql_store_result(mysql)))
{
expand_error(mysql, CR_PROBE_SLAVE_HOSTS);
return 1;
}
switch (mysql_num_fields(res))
{
case 3: has_auth_info = 0; break;
case 5: has_auth_info = 1; 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[3];
tmp_pass = row[4];
}
else
{
tmp_user = mysql->user;
tmp_pass = mysql->passwd;
}
if(!(slave = spawn_init(mysql, row[1], atoi(row[2]),
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);
return error;
}
int STDCALL mysql_rpl_probe(MYSQL* mysql)
{
MYSQL_RES* res = 0;
MYSQL_ROW row;
int error = 1;
/* 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);
return 1;
}
if (!(row = mysql_fetch_row(res)))
goto err;
/* check master host for emptiness/NULL */
if (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);
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_end = (len) ? q + len : strend(q);
for(; q < q_end; ++q)
{
char c;
if(isalpha(c=*q))
switch(tolower(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 tolower(q[1]) == 'h' ? MYSQL_RPL_ADMIN : MYSQL_RPL_MASTER ;
case 's': /* select or show */
return tolower(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 0;
}
static MYSQL* spawn_init(MYSQL* parent, const char* host,
unsigned int port,
const char* user,
const char* passwd)
{
MYSQL* child;
if (!(child = mysql_init(0)))
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));
child->options.rpl_parse = child->options.rpl_probe = child->rpl_pivot = 0;
return child;
}
/****************************************************************************
** Init MySQL structure or allocate one
****************************************************************************/
......
......@@ -128,7 +128,7 @@ DO_GDB=""
DO_DDD=""
DO_CLIENT_GDB=""
SLEEP_TIME=2
CHARACTER_SET=latin1_de
CHARACTER_SET=latin1
DBUSER=""
while test $# -gt 0; do
......
......@@ -684,7 +684,9 @@ static sig_handler print_signal_warning(int sig)
void unireg_end(int signal_number __attribute__((unused)))
{
clean_up();
#ifndef OS2
#if defined(EMBEDDED_LIBRARY)
exit(0); // XXX QQ: this is a temporary hack (I hope)
#elif !defined(OS2)
pthread_exit(0); // Exit is in main thread
#endif
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment