"Fossies" - the Fresh Open Source Software Archive

Member "mariadb-connector-c-3.0.9-src/plugins/connection/aurora.c" (8 Feb 2019, 20604 Bytes) of package /linux/misc/mariadb-connector-c-3.0.9-src.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "aurora.c": 3.0.3-src_vs_3.0.4-src.

    1 /************************************************************************************
    2   Copyright (C) 2015-2018 MariaDB Corporation AB
    3 
    4   This library is free software; you can redistribute it and/or
    5   modify it under the terms of the GNU Library General Public
    6   License as published by the Free Software Foundation; either
    7   version 2 of the License, or (at your option) any later version.
    8 
    9   This library is distributed in the hope that it will be useful,
   10   but WITHOUT ANY WARRANTY; without even the implied warranty of
   11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   12   Library General Public License for more details.
   13 
   14   You should have received a copy of the GNU Library General Public
   15   License along with this library; if not see <http://www.gnu.org/licenses>
   16   or write to the Free Software Foundation, Inc., 
   17   51 Franklin St., Fifth Floor, Boston, MA 02110, USA
   18 
   19   Part of this code includes code from the PHP project which
   20   is freely available from http://www.php.net
   21  *************************************************************************************/
   22 
   23 /* MariaDB Connection plugin for Aurora failover  */
   24 
   25 #include <ma_global.h>
   26 #include <ma_sys.h>
   27 #include <errmsg.h>
   28 #include <ma_common.h>
   29 #include <mysql.h>
   30 #include <mysql/client_plugin.h>
   31 #include <string.h>
   32 #include <ma_string.h>
   33 
   34 #ifndef WIN32
   35 #include <sys/time.h>
   36 #endif
   37 
   38 /* function prototypes */
   39 int aurora_init(char *errormsg __attribute__((unused)),
   40     size_t errormsg_size __attribute__((unused)),
   41     int unused  __attribute__((unused)), 
   42     va_list unused1 __attribute__((unused)));
   43 
   44 MYSQL *aurora_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd,
   45     const char *db, unsigned int port, const char *unix_socket, unsigned long clientflag);
   46 void aurora_close(MYSQL *mysql);
   47 int aurora_command(MYSQL *mysql,enum enum_server_command command, const char *arg,
   48     size_t length, my_bool skipp_check, void *opt_arg);
   49 my_bool aurora_reconnect(MYSQL *mysql);
   50 
   51 #define AURORA_MAX_INSTANCES 16
   52 
   53 #define AURORA_UNKNOWN -1
   54 #define AURORA_PRIMARY 0
   55 #define AURORA_REPLICA 1
   56 #define AURORA_UNAVAILABLE 2
   57 
   58 static struct st_mariadb_api *libmariadb_api= NULL;
   59 
   60 #ifndef PLUGIN_DYNAMIC
   61 MARIADB_CONNECTION_PLUGIN aurora_client_plugin =
   62 #else
   63 MARIADB_CONNECTION_PLUGIN _mysql_client_plugin_declaration_ =
   64 #endif
   65 {
   66   MARIADB_CLIENT_CONNECTION_PLUGIN,
   67   MARIADB_CLIENT_CONNECTION_PLUGIN_INTERFACE_VERSION,
   68   "aurora",
   69   "Georg Richter",
   70   "MariaDB connection plugin for Aurora failover",
   71   {1, 0, 0},
   72   "LGPL",
   73   NULL,
   74   aurora_init,
   75   NULL,
   76   NULL,
   77   aurora_connect,
   78   aurora_close,
   79   NULL,
   80   aurora_command,
   81   aurora_reconnect,
   82   NULL
   83 };
   84 
   85 
   86 typedef struct st_aurora_instance {
   87   char *host;
   88   unsigned int  port;
   89   time_t blacklisted;
   90   int type;
   91 } AURORA_INSTANCE;
   92 
   93 typedef struct st_conn_aurora {
   94   MYSQL *mysql[2],
   95         save_mysql;
   96   char *url;
   97   unsigned int num_instances;
   98   AURORA_INSTANCE instance[AURORA_MAX_INSTANCES];
   99   char *username, *password, *database;
  100   unsigned int port;
  101   unsigned long client_flag;
  102   char primary_id[100];
  103 } AURORA;
  104 
  105 #define AURORA_BLACKLIST_TIMEOUT 150
  106 
  107 #define AURORA_IS_BLACKLISTED(a, i) \
  108   ((time(NULL) - (a)->instance[(i)].blacklisted) < AURORA_BLACKLIST_TIMEOUT)
  109 
  110 /* {{{ my_bool aurora_swutch_connection */
  111 my_bool aurora_switch_connection(MYSQL *mysql, AURORA *aurora, int type)
  112 {
  113   switch (type)
  114   {
  115     case AURORA_REPLICA:
  116       if (aurora->mysql[AURORA_REPLICA])
  117       {
  118         *mysql= *aurora->mysql[AURORA_REPLICA];
  119       }
  120       break;
  121     case AURORA_PRIMARY:
  122       if (aurora->mysql[AURORA_PRIMARY])
  123       {
  124         *mysql= *aurora->mysql[AURORA_PRIMARY];
  125       }
  126       break;
  127     default:
  128       return 1;
  129   }
  130   return 0;
  131 }
  132 /* }}} */
  133 
  134 /* {{{ int aurora_init
  135  *
  136  *     plugin initialization function
  137  */
  138 int aurora_init(char *errormsg __attribute__((unused)),
  139     size_t errormsg_size __attribute__((unused)),
  140     int unused  __attribute__((unused)), 
  141     va_list unused1 __attribute__((unused)))
  142 {
  143   /* random generator initialization */
  144 #ifndef WIN32
  145   struct timeval tp;
  146   gettimeofday(&tp,NULL);
  147   srand(tp.tv_usec / 1000 + tp.tv_sec * 1000);
  148 #else
  149   srand(GetTickCount());
  150 #endif
  151   return 0;
  152 }
  153 /* }}} */
  154 
  155 /* {{{ void aurora_close_memory */
  156 void aurora_close_memory(AURORA *aurora)
  157 {
  158   free(aurora->url);
  159   free(aurora->username);
  160   free(aurora->password);
  161   free(aurora->database);
  162   free(aurora);
  163 }
  164 /* }}} */
  165 
  166 /* {{{ my_bool aurora_parse_url 
  167  * 
  168  *    parse url
  169  *   Url has the following format:
  170  *   instance1:port, instance2:port, .., instanceN:port
  171  *
  172  */
  173 my_bool aurora_parse_url(const char *url, AURORA *aurora)
  174 {
  175   char *p, *c;
  176   unsigned int i;
  177 
  178   if (!url || url[0] == 0)
  179     return 1;
  180 
  181   memset(aurora->instance, 0, (AURORA_MAX_INSTANCES + 1) * sizeof(char *));
  182   memset(&aurora->port, 0, (AURORA_MAX_INSTANCES + 1) * sizeof(int));
  183 
  184   if (aurora->url)
  185     free(aurora->url);
  186 
  187   aurora->url= strdup(url);
  188   c= aurora->url;
  189 
  190   /* get instances */ 
  191   while((c))
  192   {
  193     if ((p= strchr(c, ',')))
  194     {
  195       *p= '\0';
  196       p++;
  197     }
  198     if (*c)
  199     {
  200       aurora->instance[aurora->num_instances].host= c;
  201       aurora->num_instances++;
  202     }
  203     c= p;
  204   }
  205 
  206   if (!aurora->num_instances)
  207     return 0;
  208 
  209   /* check ports */
  210   for (i=0; i < aurora->num_instances && aurora->instance[i].host; i++)
  211   { 
  212     aurora->instance[i].type= AURORA_UNKNOWN;
  213 
  214     /* We need to be aware of IPv6 addresses: According to RFC3986 sect. 3.2.2
  215        hostnames have to be enclosed in square brackets if a port is given */
  216     if (aurora->instance[i].host[0]== '[' && 
  217         strchr(aurora->instance[i].host, ':') && 
  218         (p= strchr(aurora->instance[i].host,']')))
  219     {
  220       /* ignore first square bracket */
  221       memmove(aurora->instance[i].host, 
  222           aurora->instance[i].host+1, 
  223           strlen(aurora->instance[i].host) - 1);
  224       p= strchr(aurora->instance[i].host,']');
  225       *p= 0;
  226       p++;
  227     }
  228     else
  229       p= aurora->instance[i].host;
  230     if (p && (p= strchr(p, ':')))
  231     {
  232       *p= '\0';
  233       p++;
  234       aurora->instance[i].port= atoi(p);
  235     }
  236   }
  237   return 0;
  238 }
  239 /* }}} */
  240 
  241 /* {{{ int aurora_get_instance_type 
  242  *
  243  *  RETURNS:
  244  *
  245  *    AURORA_PRIMARY
  246  *    AURORA_REPLICA
  247  *    -1 on error
  248  */
  249 int aurora_get_instance_type(MYSQL *mysql)
  250 {
  251   int rc= -1;
  252   MA_CONNECTION_HANDLER *save_hdlr= mysql->extension->conn_hdlr;
  253 
  254   const char *query= "select variable_value from information_schema.global_variables where variable_name='INNODB_READ_ONLY' AND variable_value='OFF'";
  255 
  256   if (!mysql)
  257     return -1;
  258 
  259   mysql->extension->conn_hdlr= 0;
  260   if (!libmariadb_api->mysql_query(mysql, query))
  261   {
  262     MYSQL_RES *res= libmariadb_api->mysql_store_result(mysql);
  263     rc= libmariadb_api->mysql_num_rows(res) ? AURORA_PRIMARY : AURORA_REPLICA;
  264     libmariadb_api->mysql_free_result(res);
  265   }
  266   mysql->extension->conn_hdlr= save_hdlr;
  267   return rc;
  268 }
  269 /* }}} */
  270 
  271 /* {{{ my_bool aurora_get_primary_id 
  272  *
  273  *   try to find primary instance from slave by retrieving
  274  *   primary_id information_schema.replica_host_status information
  275  *
  276  *   If the function succeeds, primary_id will be copied into
  277  *   aurora->primary_id
  278  *
  279  *   Returns:
  280  *     1 on success
  281  *     0 if an error occurred or primary_id couldn't be 
  282  *       found
  283  */
  284 my_bool aurora_get_primary_id(MYSQL *mysql, AURORA *aurora)
  285 {
  286   my_bool rc= 0;
  287   MA_CONNECTION_HANDLER *save_hdlr= mysql->extension->conn_hdlr;
  288 
  289   mysql->extension->conn_hdlr= 0;
  290   if (!libmariadb_api->mysql_query(mysql, "select server_id from information_schema.replica_host_status "
  291         "where session_id = 'MASTER_SESSION_ID'"))
  292   {
  293     MYSQL_RES *res;
  294     MYSQL_ROW row;
  295 
  296     if ((res= libmariadb_api->mysql_store_result(mysql)))
  297     {
  298       if ((row= libmariadb_api->mysql_fetch_row(res)))
  299       {
  300         if (row[0])
  301         {
  302           strcpy(aurora->primary_id, row[0]);
  303           rc= 1;
  304         }
  305       }
  306       libmariadb_api->mysql_free_result(res);
  307     }
  308   }
  309   mysql->extension->conn_hdlr= save_hdlr;
  310   return rc;
  311 }
  312 /* }}} */
  313 
  314 /* {{{ unsigned int aurora_get_valid_instances 
  315  *
  316  *     returns the number of instances which are
  317  *     not blacklisted or don't have a type assigned.
  318  */
  319 static unsigned int aurora_get_valid_instances(AURORA *aurora, AURORA_INSTANCE **instances)
  320 {
  321   unsigned int i, valid_instances= 0;
  322 
  323   memset(instances, 0, sizeof(AURORA_INSTANCE *) * AURORA_MAX_INSTANCES);
  324 
  325   for (i=0; i < aurora->num_instances; i++)
  326   {
  327     if (aurora->instance[i].type != AURORA_UNAVAILABLE)
  328     {
  329       if (aurora->instance[i].type == AURORA_PRIMARY && aurora->mysql[AURORA_PRIMARY])
  330         continue;
  331       instances[valid_instances]= &aurora->instance[i];
  332       valid_instances++;
  333     }
  334   }
  335   return valid_instances;
  336 }
  337 /* }}} */
  338 
  339 /* {{{ void aurora_refresh_blacklist() */
  340 void aurora_refresh_blacklist(AURORA *aurora)
  341 {
  342   unsigned int i;
  343   for (i=0; i < aurora->num_instances; i++)
  344   {
  345     if (aurora->instance[i].blacklisted &&
  346         !(AURORA_IS_BLACKLISTED(aurora, i)))
  347     {
  348       aurora->instance[i].blacklisted= 0;
  349       aurora->instance[i].type= AURORA_UNKNOWN;
  350     }
  351   }
  352 }
  353 /* }}} */
  354 
  355 /* {{{ MYSQL *aurora_connect_instance() */
  356 MYSQL *aurora_connect_instance(AURORA *aurora, AURORA_INSTANCE *instance, MYSQL *mysql)
  357 {
  358   if (!libmariadb_api->mysql_real_connect(mysql,
  359         instance->host,
  360         aurora->username,
  361         aurora->password,
  362         aurora->database,
  363         instance->port ? instance->port : aurora->port,
  364         NULL,
  365         aurora->client_flag | CLIENT_REMEMBER_OPTIONS))
  366   {
  367     /* connection not available */
  368     instance->blacklisted= time(NULL);
  369     instance->type= AURORA_UNAVAILABLE;
  370     return NULL;
  371   }
  372 
  373   /* check if we are slave or master */
  374   switch (aurora_get_instance_type(mysql))
  375   {
  376     case AURORA_PRIMARY:
  377       instance->type= AURORA_PRIMARY;
  378       return mysql;
  379       break;
  380     case AURORA_REPLICA:
  381       instance->type= AURORA_REPLICA;
  382       break;
  383     default:
  384       instance->type= AURORA_UNAVAILABLE;
  385       instance->blacklisted= time(NULL);
  386       return NULL;
  387   }
  388   if (!aurora->primary_id[0])
  389     if (aurora_get_primary_id(mysql, aurora))
  390       return NULL;
  391   return mysql;
  392 }
  393 /* }}} */
  394 
  395 /* {{{ void aurora_close_internal */
  396 void aurora_close_internal(MYSQL *mysql)
  397 {
  398   if (mysql)
  399   {
  400     mysql->extension->conn_hdlr= 0;
  401     memset(&mysql->options, 0, sizeof(struct st_mysql_options));
  402     libmariadb_api->mysql_close(mysql);
  403   }
  404 }
  405 /* }}} */
  406 
  407 /* {{{ my_bool aurora_find_replica() */
  408 my_bool aurora_find_replica(AURORA *aurora)
  409 {
  410   int valid_instances;
  411   my_bool replica_found= 0;
  412   AURORA_INSTANCE *instance[AURORA_MAX_INSTANCES];
  413   MYSQL *mysql;
  414 
  415   if (aurora->num_instances < 2)
  416     return 0;
  417 
  418 
  419   valid_instances= aurora_get_valid_instances(aurora, instance);
  420 
  421   while (valid_instances && !replica_found)
  422   {
  423     int random_pick= rand() % valid_instances;
  424     mysql= libmariadb_api->mysql_init(NULL);
  425     mysql->options= aurora->save_mysql.options;
  426 
  427     /* don't execute init_command on slave */
  428 //    mysql->extension->conn_hdlr= aurora->save_mysql.extension->conn_hdlr;
  429     if ((aurora_connect_instance(aurora, instance[random_pick], mysql)))
  430     {
  431       switch (instance[random_pick]->type) {
  432         case AURORA_REPLICA:
  433           if (!aurora->mysql[AURORA_REPLICA])
  434             aurora->mysql[AURORA_REPLICA]= mysql;
  435           return 1;
  436           break;
  437         case AURORA_PRIMARY:
  438           if (!aurora->mysql[AURORA_PRIMARY])
  439             aurora->mysql[AURORA_PRIMARY]= mysql;
  440           else
  441             aurora_close_internal(mysql);
  442           continue;
  443           break;
  444         default:
  445           aurora_close_internal(mysql);
  446           return 0;
  447           break;
  448       }
  449     }
  450     else
  451       aurora_close_internal(mysql);
  452     valid_instances= aurora_get_valid_instances(aurora, instance);
  453   }
  454   return 0;
  455 }
  456 /* }}} */
  457 
  458 /* {{{ AURORA_INSTANCE aurora_get_primary_id_instance() */
  459 AURORA_INSTANCE *aurora_get_primary_id_instance(AURORA *aurora)
  460 {
  461   unsigned int i;
  462 
  463   if (!aurora->primary_id[0])
  464     return 0;
  465 
  466   for (i=0; i < aurora->num_instances; i++)
  467   {
  468     if (!strncmp(aurora->instance[i].host, aurora->primary_id, strlen(aurora->primary_id)))
  469       return &aurora->instance[i];
  470   }
  471   return NULL;
  472 }
  473 /* }}} */
  474 
  475 /* {{{ my_bool aurora_find_primary() */
  476 my_bool aurora_find_primary(AURORA *aurora)
  477 {
  478   unsigned int i;
  479   AURORA_INSTANCE *instance= NULL;
  480   MYSQL *mysql;
  481   my_bool check_primary= 1;
  482 
  483   /* We try to find a primary:
  484    * by looking 1st if a replica connect provided primary_id already
  485    * by walking through instances */
  486 
  487   if (!aurora->num_instances)
  488     return 0;
  489 
  490   for (i=0; i < aurora->num_instances; i++)
  491   {
  492     mysql= libmariadb_api->mysql_init(NULL);
  493     mysql->options= aurora->save_mysql.options;
  494 
  495     if (check_primary && aurora->primary_id[0])
  496     {
  497       if ((instance= aurora_get_primary_id_instance(aurora)) &&
  498           aurora_connect_instance(aurora, instance, mysql) &&
  499           instance->type == AURORA_PRIMARY)
  500       {
  501         aurora->primary_id[0]= 0;
  502         aurora->mysql[AURORA_PRIMARY]= mysql;
  503         return 1;
  504       }
  505       /* primary id connect failed, don't try again */
  506       aurora->primary_id[0]= 0;
  507       check_primary= 0;
  508     }
  509     else if (aurora->instance[i].type != AURORA_UNAVAILABLE)
  510     {
  511       if (aurora_connect_instance(aurora, &aurora->instance[i], mysql)
  512           && aurora->instance[i].type == AURORA_PRIMARY)
  513       {
  514         aurora->mysql[AURORA_PRIMARY]= mysql;
  515         return 1;
  516       }
  517     }
  518     aurora_close_internal(mysql);
  519   }
  520   return 0;
  521 }
  522 /* }}} */
  523 
  524 /* {{{ MYSQL *aurora_connect */
  525 MYSQL *aurora_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd,
  526     const char *db, unsigned int port, const char *unix_socket __attribute__((unused)), unsigned long client_flag)
  527 {
  528   AURORA *aurora= NULL;
  529   MA_CONNECTION_HANDLER *save_hdlr= mysql->extension->conn_hdlr;
  530 
  531   if (!libmariadb_api)
  532     libmariadb_api= mysql->methods->api;
  533 
  534   /* we call aurora_connect either from mysql_real_connect or from mysql_reconnect,
  535    * so make sure in case of reconnect we don't allocate aurora twice */
  536   if (!(aurora= (AURORA *)save_hdlr->data))
  537   {
  538     if (!(aurora= (AURORA *)calloc(1, sizeof(AURORA))))
  539     {
  540       mysql->methods->set_error(mysql, CR_OUT_OF_MEMORY, "HY000", 0);
  541       return NULL;
  542     }
  543     aurora->save_mysql= *mysql;
  544 
  545     save_hdlr->data= (void *)aurora;
  546 
  547     if (aurora_parse_url(host, aurora))
  548     {
  549       goto error;
  550     }
  551 
  552     /* store login credentials for connect/reconnect */
  553     if (user)
  554       aurora->username= strdup(user);
  555     if (passwd)
  556       aurora->password= strdup(passwd);
  557     if (db)
  558       aurora->database= strdup(db);
  559     aurora->port= port;
  560     aurora->client_flag= client_flag;
  561   }
  562 
  563   /* we look for replica first:
  564      if it's a primary we don't need to call find_aurora_primary
  565      if it's a replica we can obtain primary_id */
  566   if (!aurora->mysql[AURORA_REPLICA])
  567   {
  568     if (!aurora_find_replica(aurora))
  569       aurora->mysql[AURORA_REPLICA]= NULL;
  570     else
  571       aurora->mysql[AURORA_REPLICA]->extension->conn_hdlr= save_hdlr;
  572   }
  573 
  574   if (!aurora->mysql[AURORA_PRIMARY])
  575   {
  576     if (!aurora_find_primary(aurora))
  577       aurora->mysql[AURORA_PRIMARY]= NULL;
  578     else
  579       aurora->mysql[AURORA_PRIMARY]->extension->conn_hdlr= save_hdlr;
  580   }
  581 
  582   if (!aurora->mysql[AURORA_PRIMARY] && !aurora->mysql[AURORA_REPLICA])
  583     goto error;
  584 
  585   if (aurora->mysql[AURORA_PRIMARY])
  586     aurora_switch_connection(mysql, aurora, AURORA_PRIMARY);
  587   else
  588     aurora_switch_connection(mysql, aurora, AURORA_REPLICA);
  589   mysql->extension->conn_hdlr= save_hdlr;
  590   return mysql;
  591 error:
  592   aurora_close_memory(aurora);
  593   return NULL;
  594 }
  595 /* }}} */
  596 
  597 /* {{{ my_bool aurora_reconnect */
  598 my_bool aurora_reconnect(MYSQL *mysql)
  599 {
  600   AURORA *aurora;
  601   MA_CONNECTION_HANDLER *save_hdlr= mysql->extension->conn_hdlr;
  602   unsigned int i;
  603 
  604   /* We can't determine if a new primary was promotoed, or if
  605    * line just dropped - we will close both primary and replica
  606    * connection and establish a new connection via
  607    * aurora_connect */
  608 
  609   aurora= (AURORA *)save_hdlr->data;
  610 
  611   /* removed blacklisted instances */
  612   for (i=0; i < aurora->num_instances; i++)
  613     aurora->instance[i].type= AURORA_UNKNOWN;
  614 
  615   if (aurora->mysql[AURORA_PRIMARY]->thread_id == mysql->thread_id)
  616   {
  617     /* don't send COM_QUIT */
  618     aurora->mysql[AURORA_PRIMARY]->net.pvio= NULL;
  619     aurora_close_internal(aurora->mysql[AURORA_PRIMARY]);
  620     aurora->mysql[AURORA_PRIMARY]= NULL;
  621     aurora_close_internal(aurora->mysql[AURORA_REPLICA]);
  622     aurora->mysql[AURORA_REPLICA]= NULL;
  623   }
  624   else if (aurora->mysql[AURORA_REPLICA]->thread_id == mysql->thread_id)
  625   {
  626     /* don't send COM_QUIT */
  627     aurora->mysql[AURORA_REPLICA]->net.pvio= NULL;
  628     aurora_close_internal(aurora->mysql[AURORA_REPLICA]);
  629     aurora->mysql[AURORA_REPLICA]= NULL;
  630     aurora_close_internal(aurora->mysql[AURORA_PRIMARY]);
  631     aurora->mysql[AURORA_PRIMARY]= NULL;
  632   }
  633 
  634   /* unset connections, so we can connect to primary and replica again */
  635   aurora->mysql[AURORA_PRIMARY]= aurora->mysql[AURORA_REPLICA]= NULL;
  636 
  637   if (aurora_connect(mysql, NULL, NULL, NULL, NULL, 0, NULL, 0))
  638   {
  639     if (aurora->mysql[AURORA_PRIMARY])
  640       *mysql= *aurora->mysql[AURORA_PRIMARY];
  641     return 0;
  642   }
  643   if (aurora->mysql[AURORA_REPLICA])
  644     *mysql= *aurora->mysql[AURORA_REPLICA];
  645   else
  646     *mysql= aurora->save_mysql;
  647   return 1;
  648 }
  649 /* }}} */
  650 
  651 /* {{{  void aurora_close */
  652 void aurora_close(MYSQL *mysql)
  653 {
  654   MA_CONNECTION_HANDLER *hdlr= mysql->extension->conn_hdlr;
  655   AURORA *aurora;
  656   int i;
  657 
  658   if (!hdlr || !hdlr->data)
  659     return;
  660   
  661   aurora= (AURORA *)hdlr->data;
  662   *mysql= aurora->save_mysql;
  663 
  664   if (!aurora->mysql[AURORA_PRIMARY] && !aurora->mysql[AURORA_REPLICA])
  665     goto end;
  666 
  667   for (i=0; i < 2; i++)
  668   {
  669     if (aurora->mysql[i])
  670     {
  671       /* Make sure that connection wasn't closed before, e.g. after disconnect */
  672       if (mysql->thread_id == aurora->mysql[i]->thread_id && !mysql->net.pvio)
  673         aurora->mysql[i]->net.pvio= 0;
  674 
  675       aurora_close_internal(aurora->mysql[i]);
  676       aurora->mysql[i]= NULL;
  677     }
  678   }
  679   /* free information  */
  680 end:
  681   aurora_close_memory(aurora);
  682   mysql->extension->conn_hdlr= hdlr;
  683 }
  684 /* }}} */
  685 
  686 /* {{{ my_bool is_replica_command */
  687 my_bool is_replica_command(const char *buffer, size_t buffer_len)
  688 {
  689   const char *buffer_end= buffer + buffer_len;
  690 
  691   for (; buffer < buffer_end; ++buffer)
  692   {
  693     char c;
  694     if (isalpha(c=*buffer))
  695     {
  696       if (tolower(c) == 's')
  697         return 1;
  698       return 0;
  699     }
  700   }
  701   return 0;
  702 }
  703 /* }}} */
  704 
  705 /* {{{ my_bool is_replica_stmt */
  706 my_bool is_replica_stmt(MYSQL *mysql, const char *buffer)
  707 {
  708   unsigned long stmt_id= uint4korr(buffer);
  709   LIST *stmt_list= mysql->stmts;
  710 
  711   for (; stmt_list; stmt_list= stmt_list->next)
  712   {
  713     MYSQL_STMT *stmt= (MYSQL_STMT *)stmt_list->data;
  714     if (stmt->stmt_id == stmt_id)
  715       return 1;
  716   }
  717   return 0;
  718 }
  719 /* }}} */
  720 
  721 /* {{{ int aurora_command */
  722 int aurora_command(MYSQL *mysql,enum enum_server_command command, const char *arg,
  723     size_t length __attribute__((unused)), my_bool skipp_check __attribute__((unused)), void *opt_arg __attribute__((unused)))
  724 {
  725   MA_CONNECTION_HANDLER *save_hdlr= mysql->extension->conn_hdlr;
  726   AURORA *aurora= (AURORA *)save_hdlr->data;
  727 
  728   /* if we don't have slave or slave became unavailable root traffic to master */
  729   if (!aurora->mysql[AURORA_REPLICA] || !OPT_EXT_VAL(mysql, read_only))
  730   {
  731     if (command != COM_INIT_DB)
  732     {
  733       aurora_switch_connection(mysql, aurora, AURORA_PRIMARY);
  734       goto end;
  735     }
  736   }
  737 
  738   switch(command) {
  739     case COM_INIT_DB:
  740       /* we need to change default database on primary and replica */
  741       if (aurora->mysql[AURORA_REPLICA] && mysql->thread_id == aurora->mysql[AURORA_PRIMARY]->thread_id)
  742       {
  743         aurora->mysql[AURORA_REPLICA]->extension->conn_hdlr= 0;
  744         libmariadb_api->mysql_select_db(aurora->mysql[AURORA_REPLICA], arg);
  745         aurora->mysql[AURORA_REPLICA]->extension->conn_hdlr= mysql->extension->conn_hdlr;
  746       }
  747       break;
  748     case COM_QUERY:
  749     case COM_STMT_PREPARE:
  750       if (aurora->mysql[AURORA_REPLICA])
  751         aurora_switch_connection(mysql, aurora, AURORA_REPLICA);
  752       break;
  753     case COM_STMT_EXECUTE:
  754     case COM_STMT_FETCH:
  755       if (aurora->mysql[AURORA_REPLICA] && aurora->mysql[AURORA_REPLICA]->stmts && 
  756           is_replica_stmt(aurora->mysql[AURORA_REPLICA], arg))
  757       {
  758         aurora_switch_connection(mysql, aurora, AURORA_REPLICA);
  759       }
  760       else
  761       {
  762         aurora_switch_connection(mysql, aurora, AURORA_PRIMARY);
  763       }
  764       break;
  765     default:
  766       aurora_switch_connection(mysql, aurora, AURORA_PRIMARY);
  767       break; 
  768   }
  769 end:
  770   mysql->extension->conn_hdlr= save_hdlr;
  771   return 0;
  772 }
  773 /* }}} */