diff --git a/include/ma_common.h b/include/ma_common.h index 9a76dc10..d99e81e2 100644 --- a/include/ma_common.h +++ b/include/ma_common.h @@ -82,6 +82,8 @@ struct st_mysql_options_extension { int (*io_wait)(my_socket handle, my_bool is_read, int timeout); my_bool skip_read_response; char *restricted_auth; + char *rpl_host; + unsigned short rpl_port; }; typedef struct st_connection_handler diff --git a/include/mysql.h b/include/mysql.h index 8a3884c0..d87a4d66 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -248,7 +248,8 @@ extern const char *SQLSTATE_UNKNOWN; MARIADB_OPT_PROXY_HEADER, MARIADB_OPT_IO_WAIT, MARIADB_OPT_SKIP_READ_RESPONSE, - MARIADB_OPT_RESTRICTED_AUTH + MARIADB_OPT_RESTRICTED_AUTH, + MARIADB_OPT_RPL_REGISTER_REPLICA }; enum mariadb_value { diff --git a/libmariadb/mariadb_lib.c b/libmariadb/mariadb_lib.c index b5ec5016..5006e02b 100644 --- a/libmariadb/mariadb_lib.c +++ b/libmariadb/mariadb_lib.c @@ -38,6 +38,7 @@ #include #include #include +#include #ifndef __has_feature # define __has_feature(x) 0 @@ -704,7 +705,7 @@ struct st_default_options mariadb_defaults[] = (OPTS)->extension= (struct st_mysql_options_extension *) \ calloc(1, sizeof(struct st_mysql_options_extension)); -#define OPT_SET_EXTENDED_VALUE_BIN(OPTS, KEY, KEY_LEN, VAL, LEN) \ +#define OPT_SET_EXTENDED_VALUE_BIN(OPTS, KEY, KEY_LEN, VAL, LEN)\ CHECK_OPT_EXTENSION_SET(OPTS) \ free((gptr)(OPTS)->extension->KEY); \ if((VAL) && (LEN)) { \ @@ -719,7 +720,7 @@ struct st_default_options mariadb_defaults[] = #define OPT_SET_EXTENDED_VALUE_STR(OPTS, KEY, VAL) \ CHECK_OPT_EXTENSION_SET(OPTS) \ free((gptr)(OPTS)->extension->KEY); \ - if((VAL)) \ + if((VAL)) \ (OPTS)->extension->KEY= strdup((char *)(VAL)); \ else \ (OPTS)->extension->KEY= NULL @@ -2280,6 +2281,7 @@ static void mysql_close_options(MYSQL *mysql) if (ma_hashtbl_inited(&mysql->options.extension->userdata)) ma_hashtbl_free(&mysql->options.extension->userdata); free(mysql->options.extension->restricted_auth); + free(mysql->options.extension->rpl_host); } free(mysql->options.extension); @@ -3654,6 +3656,13 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...) case MARIADB_OPT_RESTRICTED_AUTH: OPT_SET_EXTENDED_VALUE_STR(&mysql->options, restricted_auth, (char *)arg1); break; + case MARIADB_OPT_RPL_REGISTER_REPLICA: + { + unsigned int arg2 = va_arg(ap, unsigned int); + OPT_SET_EXTENDED_VALUE_STR(&mysql->options, rpl_host,(char *)arg1); + OPT_SET_EXTENDED_VALUE(&mysql->options, rpl_port, (ushort)arg2); + } + break; default: va_end(ap); SET_CLIENT_ERROR(mysql, CR_NOT_IMPLEMENTED, SQLSTATE_UNKNOWN, 0); diff --git a/libmariadb/mariadb_rpl.c b/libmariadb/mariadb_rpl.c index cfaa009e..737278b1 100644 --- a/libmariadb/mariadb_rpl.c +++ b/libmariadb/mariadb_rpl.c @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -95,7 +96,51 @@ int STDCALL mariadb_rpl_open(MARIADB_RPL *rpl) * = filename length */ - ptr= buf= + + /* if replica was specified, we will register replica via + COM_REGISTER_SLAVE */ + if (rpl->mysql->options.extension && rpl->mysql->options.extension->rpl_host) + { + /* Protocol: + Ofs Len Data + 0 1 COM_REGISTER_SLAVE + 1 4 server id + 5 1 replica host name length + 6 replica host name + 1 user name length + user name + 1 password length + password + 2 replica port + 4 replication rank (unused) + 4 source server id (unused) + */ + unsigned char *p, buffer[1024]; + size_t len= MIN(strlen(rpl->mysql->options.extension->rpl_host), 255); + + p= buffer; + int4store(p, rpl->server_id); + p+= 4; + *p++= len; + memcpy(p, rpl->mysql->options.extension->rpl_host, len); + p+= len; + + /* Don't send user, password, rank and server_id */ + *p++= 0; + *p++= 0; + int2store(p, rpl->mysql->options.extension->rpl_port); + p+= 2; + + int4store(p, 0); + p+= 4; + int4store(p, 0); + p+= 4; + + if (ma_simple_command(rpl->mysql, COM_REGISTER_SLAVE, (const char *)buffer, p - buffer, 1, 0)) + return 1; + } + + ptr= buf= #ifdef WIN32 (unsigned char *)_alloca(rpl->filename_length + 11); #else diff --git a/unittest/libmariadb/rpl_api.c b/unittest/libmariadb/rpl_api.c index e8414dba..7facba25 100644 --- a/unittest/libmariadb/rpl_api.c +++ b/unittest/libmariadb/rpl_api.c @@ -241,7 +241,98 @@ static int test_conc467(MYSQL *my __attribute__((unused))) return rc; } +static int test_conc592(MYSQL *my __attribute__((unused))) +{ + MARIADB_RPL *rpl; + MYSQL *mysql= mysql_init(NULL); + MYSQL *mysql_check= mysql_init(NULL); + const char *host= "myhost"; + MYSQL_RES *result; + MYSQL_ROW row; + int rc; + int found= 0; + + + mysql_optionsv(mysql, MARIADB_OPT_RPL_REGISTER_REPLICA, host, 123); + + SKIP_SKYSQL; + SKIP_MAXSCALE; + + if (!is_mariadb) + return SKIP; + + if (!my_test_connect(mysql, hostname, username, + password, schema, port, socketname, 0)) + { + diag("Error: %s", mysql_error(mysql)); + mysql_close(mysql); + return FAIL; + } + + if (!my_test_connect(mysql_check, hostname, username, + password, schema, port, socketname, 0)) + { + diag("Error: %s", mysql_error(mysql)); + mysql_close(mysql); + return FAIL; + } + + rc= mysql_query(mysql, "SELECT @@log_bin"); + check_mysql_rc(rc, mysql); + + result= mysql_store_result(mysql); + row= mysql_fetch_row(result); + if (!atoi(row[0])) + rc= SKIP; + mysql_free_result(result); + + if (rc == SKIP) + { + diag("binary log disabled -> skip"); + mysql_close(mysql); + return SKIP; + } + + rpl = mariadb_rpl_init(mysql); + + mysql_query(mysql, "SET @mariadb_slave_capability=4"); + mysql_query(mysql, "SET NAMES latin1"); + mysql_query(mysql, "SET @slave_gtid_strict_mode=1"); + mysql_query(mysql, "SET @slave_gtid_ignore_duplicates=1"); + mysql_query(mysql, "SET NAMES utf8"); + mysql_query(mysql, "SET @master_binlog_checksum= @@global.binlog_checksum"); + mysql_query(mysql, "SET @rpl_semi_sync_slave=1"); + rpl->server_id= 12; + rpl->start_position= 4; + rpl->flags= MARIADB_RPL_BINLOG_SEND_ANNOTATE_ROWS; + + if (mariadb_rpl_open(rpl)) + return FAIL; + + rc= mysql_query(mysql_check, "SHOW SLAVE HOSTS"); + check_mysql_rc(rc, mysql_check); + + result= mysql_store_result(mysql_check); + + while ((row= mysql_fetch_row(result))) + if (!strcmp(row[1], host)) + found= 1; + + mysql_free_result(result); + mysql_close(mysql); + mysql_close(mysql_check); + + if (!found) + { + diag("Host '%s' not found in replica list", host); + return FAIL; + } + + return OK; +} + struct my_tests_st my_tests[] = { + {"test_conc592", test_conc592, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_rpl_async", test_rpl_async, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_rpl_semisync", test_rpl_semisync, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_conc467", test_conc467, TEST_CONNECTION_NEW, 0, NULL, NULL},