summaryrefslogtreecommitdiff
path: root/libmysqld/libmysqld.c
diff options
context:
space:
mode:
authorunknown <tim@black.box>2001-09-16 21:41:22 -0400
committerunknown <tim@black.box>2001-09-16 21:41:22 -0400
commit5fffbb123615ad0b256652390467b33ed2919afc (patch)
tree7ff6a4243043fa71bd04fa3434bd8e13992df771 /libmysqld/libmysqld.c
parent34925f8d823c700c939f0622b6af85001458a226 (diff)
downloadmariadb-git-5fffbb123615ad0b256652390467b33ed2919afc.tar.gz
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. sql/mysqld.cc: have unireg_end() exit(), instead of pthread_exit() if inside the EMBEDDED_LIBRARY. This is a hack which hopefully won't be needed. But without it, the program hangs at end. client/mysql.cc: Don't call mysql_ssl_clear() unless HAVE_OPENSSL. client/mysqltest.c: Add mysql_server_init() and _end(). acinclude.m4: change .. to $(top_builddir) in innodb_libs Makefile.am: Add libmysqld/examples to link_sources target configure.in: output libmysqld/examples/Makefile Also, change .. to $(top_builddir) in readline_link BitKeeper/etc/ignore: added linked_libmysqldex_sources mysql-test/mysql-test-run.sh: use latin1, not latin1_de, in tests libmysqld/libmysqld.c: Add replication functions.
Diffstat (limited to 'libmysqld/libmysqld.c')
-rw-r--r--libmysqld/libmysqld.c300
1 files changed, 300 insertions, 0 deletions
diff --git a/libmysqld/libmysqld.c b/libmysqld/libmysqld.c
index 0e66ceb2f40..42b50a4bb1f 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
****************************************************************************/