diff options
author | unknown <tim@black.box> | 2001-09-16 22:10:50 -0400 |
---|---|---|
committer | unknown <tim@black.box> | 2001-09-16 22:10:50 -0400 |
commit | dd02abb187d00ed7d0f2aa0baace2d3d0e730511 (patch) | |
tree | 6784f5457669bae83fdca495c6affbe7e3e41b30 /libmysqld | |
parent | 5810866b69ab1ef14b26305fa45943686377da2b (diff) | |
parent | 5fffbb123615ad0b256652390467b33ed2919afc (diff) | |
download | mariadb-git-dd02abb187d00ed7d0f2aa0baace2d3d0e730511.tar.gz |
Merge
BitKeeper/etc/ignore:
auto-union
configure.in:
SCCS merged
client/mysql.cc:
SCCS merged
client/mysqltest.c:
SCCS merged
mysql-test/mysql-test-run.sh:
Auto merged
libmysqld/libmysqld.c:
SCCS merged
Diffstat (limited to 'libmysqld')
-rw-r--r-- | libmysqld/examples/Makefile.am | 33 | ||||
-rwxr-xr-x | libmysqld/examples/test-run | 131 | ||||
-rw-r--r-- | libmysqld/libmysqld.c | 300 |
3 files changed, 464 insertions, 0 deletions
diff --git a/libmysqld/examples/Makefile.am b/libmysqld/examples/Makefile.am new file mode 100644 index 00000000000..2a8ac304e9a --- /dev/null +++ b/libmysqld/examples/Makefile.am @@ -0,0 +1,33 @@ +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) diff --git a/libmysqld/examples/test-run b/libmysqld/examples/test-run new file mode 100755 index 00000000000..1e15126b126 --- /dev/null +++ b/libmysqld/examples/test-run @@ -0,0 +1,131 @@ +#! /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 diff --git a/libmysqld/libmysqld.c b/libmysqld/libmysqld.c index 53a05a24265..cb8b2f02773 100644 --- a/libmysqld/libmysqld.c +++ b/libmysqld/libmysqld.c @@ -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 ****************************************************************************/ |