summaryrefslogtreecommitdiff
path: root/libmysqld
diff options
context:
space:
mode:
authorunknown <tim@black.box>2001-09-16 22:10:50 -0400
committerunknown <tim@black.box>2001-09-16 22:10:50 -0400
commitdd02abb187d00ed7d0f2aa0baace2d3d0e730511 (patch)
tree6784f5457669bae83fdca495c6affbe7e3e41b30 /libmysqld
parent5810866b69ab1ef14b26305fa45943686377da2b (diff)
parent5fffbb123615ad0b256652390467b33ed2919afc (diff)
downloadmariadb-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.am33
-rwxr-xr-xlibmysqld/examples/test-run131
-rw-r--r--libmysqld/libmysqld.c300
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
****************************************************************************/