1 /* Copyright (c) 2000, 2021, Oracle and/or its affiliates.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License, version 2.0, for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software Foundation,
21    51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
22 
23 
24 /*
25   The servers are saved in the system table "servers"
26 
27   Currently, when the user performs an ALTER SERVER or a DROP SERVER
28   operation, it will cause all open tables which refer to the named
29   server connection to be flushed. This may cause some undesirable
30   behaviour with regard to currently running transactions. It is
31   expected that the DBA knows what s/he is doing when s/he performs
32   the ALTER SERVER or DROP SERVER operation.
33 
34   TODO:
35   It is desirable for us to implement a callback mechanism instead where
36   callbacks can be registered for specific server protocols. The callback
37   will be fired when such a server name has been created/altered/dropped
38   or when statistics are to be gathered such as how many actual connections.
39   Storage engines etc will be able to make use of the callback so that
40   currently running transactions etc will not be disrupted.
41 */
42 
43 #include "sql_servers.h"
44 #include "sql_base.h"                           // close_mysql_tables
45 #include "records.h"          // init_read_record, end_read_record
46 #include "hash_filo.h"
47 #include <m_ctype.h>
48 #include <stdarg.h>
49 #include "log.h"
50 #include "auth_common.h"
51 #include "sql_parse.h"
52 #include "lock.h"                               // MYSQL_LOCK_IGNORE_TIMEOUT
53 #include "transaction.h"      // trans_rollback_stmt, trans_commit_stmt
54 /*
55   We only use 1 mutex to guard the data structures - THR_LOCK_servers.
56   Read locked when only reading data and write-locked for all other access.
57 */
58 
59 static HASH servers_cache;
60 static MEM_ROOT mem;
61 static mysql_rwlock_t THR_LOCK_servers;
62 
63 /**
64    This enum describes the structure of the mysql.servers table.
65 */
66 enum enum_servers_table_field
67 {
68   SERVERS_FIELD_NAME= 0,
69   SERVERS_FIELD_HOST,
70   SERVERS_FIELD_DB,
71   SERVERS_FIELD_USERNAME,
72   SERVERS_FIELD_PASSWORD,
73   SERVERS_FIELD_PORT,
74   SERVERS_FIELD_SOCKET,
75   SERVERS_FIELD_SCHEME,
76   SERVERS_FIELD_OWNER
77 };
78 
79 static bool get_server_from_table_to_cache(TABLE *table);
80 
servers_cache_get_key(FOREIGN_SERVER * server,size_t * length,my_bool not_used MY_ATTRIBUTE ((unused)))81 static uchar *servers_cache_get_key(FOREIGN_SERVER *server, size_t *length,
82                                     my_bool not_used MY_ATTRIBUTE((unused)))
83 {
84   *length= (uint) server->server_name_length;
85   return (uchar*) server->server_name;
86 }
87 
88 static PSI_memory_key key_memory_servers;
89 
90 #ifdef HAVE_PSI_INTERFACE
91 static PSI_rwlock_key key_rwlock_THR_LOCK_servers;
92 
93 static PSI_rwlock_info all_servers_cache_rwlocks[]=
94 {
95   { &key_rwlock_THR_LOCK_servers, "THR_LOCK_servers", PSI_FLAG_GLOBAL}
96 };
97 
98 static PSI_memory_info all_servers_cache_memory[]=
99 {
100   { &key_memory_servers, "servers_cache", PSI_FLAG_GLOBAL}
101 };
102 
init_servers_cache_psi_keys(void)103 static void init_servers_cache_psi_keys(void)
104 {
105   const char* category= "sql";
106   int count;
107 
108   count= array_elements(all_servers_cache_rwlocks);
109   mysql_rwlock_register(category, all_servers_cache_rwlocks, count);
110 
111   count= array_elements(all_servers_cache_memory);
112   mysql_memory_register(category, all_servers_cache_memory, count);
113 }
114 #endif /* HAVE_PSI_INTERFACE */
115 
116 /*
117   Initialize structures responsible for servers used in federated
118   server scheme information for them from the server
119   table in the 'mysql' database.
120 
121   SYNOPSIS
122     servers_init()
123       dont_read_server_table  TRUE if we want to skip loading data from
124                             server table and disable privilege checking.
125 
126   NOTES
127     This function is mostly responsible for preparatory steps, main work
128     on initialization and grants loading is done in servers_reload().
129 
130   RETURN VALUES
131     0	ok
132     1	Could not initialize servers
133 */
134 
servers_init(bool dont_read_servers_table)135 bool servers_init(bool dont_read_servers_table)
136 {
137   THD  *thd;
138   bool return_val= FALSE;
139   DBUG_ENTER("servers_init");
140 
141 #ifdef HAVE_PSI_INTERFACE
142   init_servers_cache_psi_keys();
143 #endif
144 
145   /* init the mutex */
146   if (mysql_rwlock_init(key_rwlock_THR_LOCK_servers, &THR_LOCK_servers))
147     DBUG_RETURN(TRUE);
148 
149   /* initialise our servers cache */
150   if (my_hash_init(&servers_cache, system_charset_info, 32, 0, 0,
151                    (my_hash_get_key) servers_cache_get_key, 0, 0,
152                    key_memory_servers))
153   {
154     return_val= TRUE; /* we failed, out of memory? */
155     goto end;
156   }
157 
158   /* Initialize the mem root for data */
159   init_sql_alloc(key_memory_servers, &mem, ACL_ALLOC_BLOCK_SIZE, 0);
160 
161   if (dont_read_servers_table)
162     goto end;
163 
164   /*
165     To be able to run this from boot, we allocate a temporary THD
166   */
167   if (!(thd=new THD))
168     DBUG_RETURN(TRUE);
169   thd->thread_stack= (char*) &thd;
170   thd->store_globals();
171   /*
172     It is safe to call servers_reload() since servers_* arrays and hashes which
173     will be freed there are global static objects and thus are initialized
174     by zeros at startup.
175   */
176   return_val= servers_reload(thd);
177   delete thd;
178 
179 end:
180   DBUG_RETURN(return_val);
181 }
182 
183 /*
184   Initialize server structures
185 
186   SYNOPSIS
187     servers_load()
188       thd     Current thread
189       tables  List containing open "mysql.servers"
190 
191   RETURN VALUES
192     FALSE  Success
193     TRUE   Error
194 
195   TODO
196     Revert back to old list if we failed to load new one.
197 */
198 
servers_load(THD * thd,TABLE * table)199 static bool servers_load(THD *thd, TABLE *table)
200 {
201   READ_RECORD read_record_info;
202   bool return_val= TRUE;
203   DBUG_ENTER("servers_load");
204 
205   my_hash_reset(&servers_cache);
206   free_root(&mem, MYF(0));
207   init_sql_alloc(key_memory_servers, &mem, ACL_ALLOC_BLOCK_SIZE, 0);
208 
209   if (init_read_record(&read_record_info, thd, table,
210                        NULL, 1, 1, FALSE))
211     DBUG_RETURN(TRUE);
212 
213   while (!(read_record_info.read_record(&read_record_info)))
214   {
215     /* return_val is already TRUE, so no need to set */
216     if ((get_server_from_table_to_cache(table)))
217       goto end;
218   }
219 
220   return_val= FALSE;
221 
222 end:
223   end_read_record(&read_record_info);
224   DBUG_RETURN(return_val);
225 }
226 
227 
228 /*
229   Forget current servers cache and read new servers
230   from the conneciton table.
231 
232   SYNOPSIS
233     servers_reload()
234       thd  Current thread
235 
236   NOTE
237     All tables of calling thread which were open and locked by LOCK TABLES
238     statement will be unlocked and closed.
239     This function is also used for initialization of structures responsible
240     for user/db-level privilege checking.
241 
242   RETURN VALUE
243     FALSE  Success
244     TRUE   Failure
245 */
246 
servers_reload(THD * thd)247 bool servers_reload(THD *thd)
248 {
249   TABLE_LIST tables[1];
250   bool return_val= true;
251   DBUG_ENTER("servers_reload");
252 
253   DBUG_PRINT("info", ("locking servers_cache"));
254   mysql_rwlock_wrlock(&THR_LOCK_servers);
255 
256   tables[0].init_one_table("mysql", 5, "servers", 7, "servers", TL_READ);
257   if (open_trans_system_tables_for_read(thd, tables))
258   {
259     /*
260       Execution might have been interrupted; only print the error message
261       if an error condition has been raised.
262     */
263     if (thd->get_stmt_da()->is_error())
264       sql_print_error("Can't open and lock privilege tables: %s",
265                       thd->get_stmt_da()->message_text());
266     goto end;
267   }
268 
269   if ((return_val= servers_load(thd, tables[0].table)))
270   {					// Error. Revert to old list
271     /* blast, for now, we have no servers, discuss later way to preserve */
272 
273     DBUG_PRINT("error",("Reverting to old privileges"));
274     servers_free();
275   }
276 
277   close_trans_system_tables(thd);
278 end:
279   DBUG_PRINT("info", ("unlocking servers_cache"));
280   mysql_rwlock_unlock(&THR_LOCK_servers);
281   DBUG_RETURN(return_val);
282 }
283 
284 
285 /*
286   Initialize structures responsible for servers used in federated
287   server scheme information for them from the server
288   table in the 'mysql' database.
289 
290   SYNOPSIS
291     get_server_from_table_to_cache()
292       TABLE *table         open table pointer
293 
294 
295   NOTES
296     This function takes a TABLE pointer (pointing to an opened
297     table). With this open table, a FOREIGN_SERVER struct pointer
298     is allocated into root memory, then each member of the FOREIGN_SERVER
299     struct is populated. A char pointer takes the return value of get_field
300     for each column we're interested in obtaining, and if that pointer
301     isn't 0x0, the FOREIGN_SERVER member is set to that value, otherwise,
302     is set to the value of an empty string, since get_field would set it to
303     0x0 if the column's value is empty, even if the default value for that
304     column is NOT NULL.
305 
306   RETURN VALUES
307     0	ok
308     1	could not insert server struct into global servers cache
309 */
310 
get_server_from_table_to_cache(TABLE * table)311 static bool get_server_from_table_to_cache(TABLE *table)
312 {
313   /* alloc a server struct */
314   char *ptr;
315   char * const blank= (char*)"";
316   FOREIGN_SERVER *server= new (&mem) FOREIGN_SERVER();
317 
318   DBUG_ENTER("get_server_from_table_to_cache");
319   table->use_all_columns();
320 
321   /* get each field into the server struct ptr */
322   ptr= get_field(&mem, table->field[SERVERS_FIELD_NAME]);
323   server->server_name= ptr ? ptr : blank;
324   server->server_name_length= strlen(server->server_name);
325   ptr= get_field(&mem, table->field[SERVERS_FIELD_HOST]);
326   server->host= ptr ? ptr : blank;
327   ptr= get_field(&mem, table->field[SERVERS_FIELD_DB]);
328   server->db= ptr ? ptr : blank;
329   ptr= get_field(&mem, table->field[SERVERS_FIELD_USERNAME]);
330   server->username= ptr ? ptr : blank;
331   ptr= get_field(&mem, table->field[SERVERS_FIELD_PASSWORD]);
332   server->password= ptr ? ptr : blank;
333   ptr= get_field(&mem, table->field[SERVERS_FIELD_PORT]);
334   server->sport= ptr ? ptr : blank;
335 
336   server->port= server->sport ? atoi(server->sport) : 0;
337 
338   ptr= get_field(&mem, table->field[SERVERS_FIELD_SOCKET]);
339   server->socket= ptr && strlen(ptr) ? ptr : blank;
340   ptr= get_field(&mem, table->field[SERVERS_FIELD_SCHEME]);
341   server->scheme= ptr ? ptr : blank;
342   ptr= get_field(&mem, table->field[SERVERS_FIELD_OWNER]);
343   server->owner= ptr ? ptr : blank;
344   DBUG_PRINT("info", ("server->server_name %s", server->server_name));
345   DBUG_PRINT("info", ("server->host %s", server->host));
346   DBUG_PRINT("info", ("server->db %s", server->db));
347   DBUG_PRINT("info", ("server->username %s", server->username));
348   DBUG_PRINT("info", ("server->password %s", server->password));
349   DBUG_PRINT("info", ("server->socket %s", server->socket));
350   if (my_hash_insert(&servers_cache, (uchar*) server))
351   {
352     DBUG_PRINT("info", ("had a problem inserting server %s at %lx",
353                         server->server_name, (long unsigned int) server));
354     // error handling needed here
355     DBUG_RETURN(TRUE);
356   }
357   DBUG_RETURN(FALSE);
358 }
359 
360 
361 /**
362   Close all tables which match specified connection string or
363   if specified string is NULL, then any table with a connection string.
364 */
365 
close_cached_connection_tables(THD * thd,const char * connection_string,size_t connection_length)366 static bool close_cached_connection_tables(THD *thd,
367                                            const char *connection_string,
368                                            size_t connection_length)
369 {
370   uint idx;
371   TABLE_LIST tmp, *tables= NULL;
372   bool result= FALSE;
373   DBUG_ENTER("close_cached_connection_tables");
374   assert(thd);
375 
376   mysql_mutex_lock(&LOCK_open);
377 
378   for (idx= 0; idx < table_def_cache.records; idx++)
379   {
380     TABLE_SHARE *share= (TABLE_SHARE *) my_hash_element(&table_def_cache, idx);
381 
382     /*
383       Skip table shares being opened to avoid comparison reading into
384       uninitialized memory further below.
385 
386       Thus, in theory, there is a risk that shares are left in the
387       cache that should really be closed (matching the submitted
388       connection string), and this risk is already present since
389       LOCK_open is unlocked before calling this function. However,
390       this function is called as the final step of DROP/ALTER SERVER,
391       so its goal is to flush all tables which were open before
392       DROP/ALTER SERVER started. Thus, if a share gets opened after
393       this function is called, the information about the server has
394       already been updated, so the new table will use the new
395       definition of the server.
396 
397       It might have been an issue, however if one thread started
398       opening a federated table, read the old server definition into a
399       share, and then a switch to another thread doing ALTER SERVER
400       happened right before setting m_open_in_progress to false for
401       the share. Because in this case ALTER SERVER would not flush
402       the share opened by the first thread as it should have been. But
403       luckily, server definitions affected by * SERVER statements are
404       not read into TABLE_SHARE structures, but are read when we
405       create the TABLE object in ha_federated::open().
406 
407       This means that ignoring shares that are in the process of being
408       opened is safe, because such shares don't have TABLE objects
409       associated with them yet.
410     */
411     if (share->m_open_in_progress)
412       continue;
413 
414     /* Ignore if table is not open or does not have a connect_string */
415     if (!share->connect_string.length || !share->ref_count)
416       continue;
417 
418     /* Compare the connection string */
419     if (connection_string &&
420         (connection_length > share->connect_string.length ||
421          (connection_length < share->connect_string.length &&
422           (share->connect_string.str[connection_length] != '/' &&
423            share->connect_string.str[connection_length] != '\\')) ||
424          native_strncasecmp(connection_string, share->connect_string.str,
425                      connection_length)))
426       continue;
427 
428     /* close_cached_tables() only uses these elements */
429     tmp.db= share->db.str;
430     tmp.table_name= share->table_name.str;
431     tmp.next_local= tables;
432 
433     tables= (TABLE_LIST *) memdup_root(thd->mem_root, (char*)&tmp,
434                                        sizeof(TABLE_LIST));
435   }
436   mysql_mutex_unlock(&LOCK_open);
437 
438   if (tables)
439     result= close_cached_tables(thd, tables, FALSE, LONG_TIMEOUT);
440 
441   DBUG_RETURN(result);
442 }
443 
444 
reset()445 void Server_options::reset()
446 {
447   m_server_name.str= NULL;
448   m_server_name.length= 0;
449   m_port= PORT_NOT_SET;
450   m_host.str= NULL;
451   m_host.length= 0;
452   m_db.str= NULL;
453   m_db.length= 0;
454   m_username.str= NULL;
455   m_db.length= 0;
456   m_password.str= NULL;
457   m_password.length= 0;
458   m_scheme.str= NULL;
459   m_scheme.length= 0;
460   m_socket.str= NULL;
461   m_socket.length= 0;
462   m_owner.str= NULL;
463   m_owner.length= 0;
464 }
465 
466 
insert_into_cache() const467 bool Server_options::insert_into_cache() const
468 {
469   char *unset_ptr= (char*)"";
470   DBUG_ENTER("Server_options::insert_into_cache");
471 
472   FOREIGN_SERVER *server= new (&mem) FOREIGN_SERVER();
473   if (!server)
474     DBUG_RETURN(true);
475 
476   /* these two MUST be set */
477   if (!(server->server_name= strdup_root(&mem, m_server_name.str)))
478     DBUG_RETURN(true);
479   server->server_name_length= m_server_name.length;
480 
481   if (!(server->host= m_host.str ? strdup_root(&mem, m_host.str) : unset_ptr))
482     DBUG_RETURN(true);
483 
484   if (!(server->db= m_db.str ? strdup_root(&mem, m_db.str) : unset_ptr))
485     DBUG_RETURN(true);
486 
487   if (!(server->username= m_username.str ?
488         strdup_root(&mem, m_username.str) : unset_ptr))
489     DBUG_RETURN(true);
490 
491   if (!(server->password= m_password.str ?
492         strdup_root(&mem, m_password.str) : unset_ptr))
493     DBUG_RETURN(true);
494 
495   /* set to 0 if not specified */
496   server->port= m_port != PORT_NOT_SET ? m_port : 0;
497 
498   if (!(server->socket= m_socket.str ?
499         strdup_root(&mem, m_socket.str) : unset_ptr))
500     DBUG_RETURN(true);
501 
502   if (!(server->scheme= m_scheme.str ?
503         strdup_root(&mem, m_scheme.str) : unset_ptr))
504     DBUG_RETURN(true);
505 
506   if (!(server->owner= m_owner.str ?
507         strdup_root(&mem, m_owner.str) : unset_ptr))
508     DBUG_RETURN(true);
509 
510   DBUG_RETURN(my_hash_insert(&servers_cache, (uchar*) server));
511 }
512 
513 
update_cache(FOREIGN_SERVER * existing) const514 bool Server_options::update_cache(FOREIGN_SERVER *existing) const
515 {
516   DBUG_ENTER("Server_options::update_cache");
517 
518   /*
519     Note: Since the name can't change, we don't need to set it.
520     This also means we can just update the existing cache entry.
521   */
522 
523   /*
524     The logic here is this: is this value set AND is it different
525     than the existing value?
526   */
527   if (m_host.str && strcmp(m_host.str, existing->host) &&
528       !(existing->host= strdup_root(&mem, m_host.str)))
529     DBUG_RETURN(true);
530 
531   if (m_db.str && strcmp(m_db.str, existing->db) &&
532       !(existing->db= strdup_root(&mem, m_db.str)))
533     DBUG_RETURN(true);
534 
535   if (m_username.str && strcmp(m_username.str, existing->username) &&
536       !(existing->username= strdup_root(&mem, m_username.str)))
537     DBUG_RETURN(true);
538 
539   if (m_password.str && strcmp(m_password.str, existing->password) &&
540       !(existing->password= strdup_root(&mem, m_password.str)))
541     DBUG_RETURN(true);
542 
543   /*
544     port is initialised to PORT_NOT_SET, so if unset, it will be -1
545   */
546   if (m_port != PORT_NOT_SET && m_port != existing->port)
547     existing->port= m_port;
548 
549   if (m_socket.str && strcmp(m_socket.str, existing->socket) &&
550       !(existing->socket= strdup_root(&mem, m_socket.str)))
551     DBUG_RETURN(true);
552 
553   if (m_scheme.str && strcmp(m_scheme.str, existing->scheme) &&
554       !(existing->scheme= strdup_root(&mem, m_scheme.str)))
555     DBUG_RETURN(true);
556 
557   if (m_owner.str && strcmp(m_owner.str, existing->owner) &&
558       !(existing->owner= strdup_root(&mem, m_owner.str)))
559     DBUG_RETURN(true);
560 
561   DBUG_RETURN(false);
562 }
563 
564 
565 /**
566    Helper function for creating a record for inserting
567    a new server into the mysql.servers table.
568 
569    Set a field to the given parser string. If the parser
570    string is empty, set the field to "" instead.
571 */
572 
store_new_field(TABLE * table,enum_servers_table_field field,const LEX_STRING * val)573 static inline void store_new_field(TABLE *table,
574                                    enum_servers_table_field field,
575                                    const LEX_STRING *val)
576 {
577   if (val->str)
578     table->field[field]->store(val->str, val->length,
579                                   system_charset_info);
580   else
581     table->field[field]->store("", 0U, system_charset_info);
582 }
583 
584 
store_new_server(TABLE * table) const585 void Server_options::store_new_server(TABLE *table) const
586 {
587   store_new_field(table, SERVERS_FIELD_HOST, &m_host);
588   store_new_field(table, SERVERS_FIELD_DB, &m_db);
589   store_new_field(table, SERVERS_FIELD_USERNAME, &m_username);
590   store_new_field(table, SERVERS_FIELD_PASSWORD, &m_password);
591 
592   if (m_port != PORT_NOT_SET)
593     table->field[SERVERS_FIELD_PORT]->store(m_port);
594   else
595     table->field[SERVERS_FIELD_PORT]->store(0);
596 
597   store_new_field(table, SERVERS_FIELD_SOCKET, &m_socket);
598   store_new_field(table, SERVERS_FIELD_SCHEME, &m_scheme);
599   store_new_field(table, SERVERS_FIELD_OWNER, &m_owner);
600 }
601 
602 
603 /**
604    Helper function for creating a record for updating
605    an existing server in the mysql.servers table.
606 
607    Set a field to the given parser string unless
608    the parser string is empty or equal to the existing value.
609 */
610 
store_updated_field(TABLE * table,enum_servers_table_field field,const char * existing_val,const LEX_STRING * new_val)611 static inline void store_updated_field(TABLE *table,
612                                        enum_servers_table_field field,
613                                        const char *existing_val,
614                                        const LEX_STRING *new_val)
615 {
616   if (new_val->str && strcmp(new_val->str, existing_val))
617     table->field[field]->store(new_val->str, new_val->length,
618                                system_charset_info);
619 }
620 
621 
store_altered_server(TABLE * table,FOREIGN_SERVER * existing) const622 void Server_options::store_altered_server(TABLE *table,
623                                           FOREIGN_SERVER *existing) const
624 {
625   store_updated_field(table, SERVERS_FIELD_HOST, existing->host, &m_host);
626   store_updated_field(table, SERVERS_FIELD_DB, existing->db, &m_db);
627   store_updated_field(table, SERVERS_FIELD_USERNAME,
628                       existing->username, &m_username);
629   store_updated_field(table, SERVERS_FIELD_PASSWORD,
630                       existing->password, &m_password);
631 
632   if (m_port != PORT_NOT_SET && m_port != existing->port)
633     table->field[SERVERS_FIELD_PORT]->store(m_port);
634 
635   store_updated_field(table, SERVERS_FIELD_SOCKET, existing->socket, &m_socket);
636   store_updated_field(table, SERVERS_FIELD_SCHEME, existing->scheme, &m_scheme);
637   store_updated_field(table, SERVERS_FIELD_OWNER, existing->owner, &m_owner);
638 }
639 
640 
check_and_open_table(THD * thd)641 bool Sql_cmd_common_server::check_and_open_table(THD *thd)
642 {
643   if (check_global_access(thd, SUPER_ACL))
644     return true;
645 
646   TABLE_LIST tables;
647   tables.init_one_table("mysql", 5, "servers", 7, "servers", TL_WRITE);
648 
649   table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT);
650   return (table == NULL);
651 }
652 
653 
execute(THD * thd)654 bool Sql_cmd_create_server::execute(THD *thd)
655 {
656   DBUG_ENTER("Sql_cmd_create_server::execute");
657 
658   if (Sql_cmd_common_server::check_and_open_table(thd))
659     DBUG_RETURN(true);
660 
661   // Check for existing cache entries with same name
662   mysql_rwlock_wrlock(&THR_LOCK_servers);
663   if (my_hash_search(&servers_cache,
664                      (uchar*) m_server_options->m_server_name.str,
665                      m_server_options->m_server_name.length))
666   {
667     mysql_rwlock_unlock(&THR_LOCK_servers);
668     my_error(ER_FOREIGN_SERVER_EXISTS, MYF(0),
669              m_server_options->m_server_name.str);
670     trans_rollback_stmt(thd);
671     close_mysql_tables(thd);
672     DBUG_RETURN(true);
673   }
674 
675   int error;
676   tmp_disable_binlog(table->in_use);
677   table->use_all_columns();
678   empty_record(table);
679 
680   /* set the field that's the PK to the value we're looking for */
681   table->field[SERVERS_FIELD_NAME]->store(
682     m_server_options->m_server_name.str,
683     m_server_options->m_server_name.length,
684     system_charset_info);
685 
686   /* read index until record is that specified in server_name */
687   error= table->file->ha_index_read_idx_map(
688     table->record[0], 0,
689     table->field[SERVERS_FIELD_NAME]->ptr,
690     HA_WHOLE_KEY,
691     HA_READ_KEY_EXACT);
692 
693   if (!error)
694   {
695     my_error(ER_FOREIGN_SERVER_EXISTS, MYF(0),
696              m_server_options->m_server_name.str);
697     error= 1;
698   }
699   else if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
700   {
701     /* if not found, err */
702     table->file->print_error(error, MYF(0));
703   }
704   else
705   {
706     /* store each field to be inserted */
707     m_server_options->store_new_server(table);
708 
709     /* write/insert the new server */
710     if ((error= table->file->ha_write_row(table->record[0])))
711       table->file->print_error(error, MYF(0));
712     else
713     {
714       /* insert the server into the cache */
715       if ((error= m_server_options->insert_into_cache()))
716         my_error(ER_OUT_OF_RESOURCES, MYF(0));
717     }
718   }
719 
720   reenable_binlog(table->in_use);
721   mysql_rwlock_unlock(&THR_LOCK_servers);
722 
723   if (error)
724     trans_rollback_stmt(thd);
725   else
726     trans_commit_stmt(thd);
727   close_mysql_tables(thd);
728 
729   if (error == 0 && !thd->killed)
730     my_ok(thd, 1);
731   DBUG_RETURN(error != 0 || thd->killed);
732 }
733 
734 
execute(THD * thd)735 bool Sql_cmd_alter_server::execute(THD *thd)
736 {
737   DBUG_ENTER("Sql_cmd_alter_server::execute");
738 
739   if (Sql_cmd_common_server::check_and_open_table(thd))
740     DBUG_RETURN(true);
741 
742   // Find existing cache entry to update
743   mysql_rwlock_wrlock(&THR_LOCK_servers);
744   FOREIGN_SERVER *existing=
745     (FOREIGN_SERVER *) my_hash_search(&servers_cache,
746                                   (uchar*) m_server_options->m_server_name.str,
747                                   m_server_options->m_server_name.length);
748   if (!existing)
749   {
750     my_error(ER_FOREIGN_SERVER_DOESNT_EXIST, MYF(0),
751              m_server_options->m_server_name.str);
752     mysql_rwlock_unlock(&THR_LOCK_servers);
753     trans_rollback_stmt(thd);
754     close_mysql_tables(thd);
755     DBUG_RETURN(true);
756   }
757 
758   int error;
759   tmp_disable_binlog(table->in_use);
760   table->use_all_columns();
761 
762   /* set the field that's the PK to the value we're looking for */
763   table->field[SERVERS_FIELD_NAME]->store(
764     m_server_options->m_server_name.str,
765     m_server_options->m_server_name.length,
766     system_charset_info);
767 
768   error= table->file->ha_index_read_idx_map(
769     table->record[0], 0,
770     table->field[SERVERS_FIELD_NAME]->ptr,
771     ~(longlong)0,
772     HA_READ_KEY_EXACT);
773   if (error)
774   {
775     if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
776       table->file->print_error(error, MYF(0));
777     else
778       my_error(ER_FOREIGN_SERVER_DOESNT_EXIST, MYF(0),
779                m_server_options->m_server_name.str);
780   }
781   else
782   {
783     /* ok, so we can update since the record exists in the table */
784     store_record(table, record[1]);
785     m_server_options->store_altered_server(table, existing);
786     if ((error=table->file->ha_update_row(table->record[1],
787                                           table->record[0])) &&
788         error != HA_ERR_RECORD_IS_THE_SAME)
789       table->file->print_error(error, MYF(0));
790     else
791     {
792       // Update cache entry
793       if ((error= m_server_options->update_cache(existing)))
794         my_error(ER_OUT_OF_RESOURCES, MYF(0));
795     }
796   }
797 
798   reenable_binlog(table->in_use);
799 
800   /* Perform a reload so we don't have a 'hole' in our mem_root */
801   servers_load(thd, table);
802 
803   // NOTE: servers_load() must be called under acquired THR_LOCK_servers.
804   mysql_rwlock_unlock(&THR_LOCK_servers);
805 
806   if (error)
807     trans_rollback_stmt(thd);
808   else
809     trans_commit_stmt(thd);
810   close_mysql_tables(thd);
811 
812   if (close_cached_connection_tables(thd, m_server_options->m_server_name.str,
813                                      m_server_options->m_server_name.length))
814   {
815     push_warning_printf(thd, Sql_condition::SL_WARNING,
816                         ER_UNKNOWN_ERROR, "Server connection in use");
817   }
818 
819   if (error == 0 && !thd->killed)
820     my_ok(thd, 1);
821   DBUG_RETURN(error != 0 || thd->killed);
822 }
823 
824 
execute(THD * thd)825 bool Sql_cmd_drop_server::execute(THD *thd)
826 {
827   DBUG_ENTER("Sql_cmd_drop_server::execute");
828 
829   if (Sql_cmd_common_server::check_and_open_table(thd))
830     DBUG_RETURN(true);
831 
832   int error;
833   mysql_rwlock_wrlock(&THR_LOCK_servers);
834   tmp_disable_binlog(table->in_use);
835   table->use_all_columns();
836 
837   /* set the field that's the PK to the value we're looking for */
838   table->field[SERVERS_FIELD_NAME]->store(m_server_name.str,
839                                           m_server_name.length,
840                                           system_charset_info);
841 
842   error= table->file->ha_index_read_idx_map(
843     table->record[0], 0,
844     table->field[SERVERS_FIELD_NAME]->ptr,
845     HA_WHOLE_KEY, HA_READ_KEY_EXACT);
846   if (error)
847   {
848     if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
849       table->file->print_error(error, MYF(0));
850     else if (!m_if_exists)
851       my_error(ER_FOREIGN_SERVER_DOESNT_EXIST, MYF(0), m_server_name.str);
852     else
853       error= 0; // Reset error - we will report my_ok() in this case.
854   }
855   else
856   {
857     // Delete from table
858     if ((error= table->file->ha_delete_row(table->record[0])))
859       table->file->print_error(error, MYF(0));
860     else
861     {
862       // Remove from cache
863       FOREIGN_SERVER *server=
864         (FOREIGN_SERVER *)my_hash_search(&servers_cache,
865                                          (uchar*) m_server_name.str,
866                                          m_server_name.length);
867       if (server)
868         my_hash_delete(&servers_cache, (uchar*) server);
869       else if (!m_if_exists)
870       {
871         my_error(ER_FOREIGN_SERVER_DOESNT_EXIST, MYF(0),  m_server_name.str);
872         error= 1;
873       }
874     }
875   }
876 
877   reenable_binlog(table->in_use);
878   mysql_rwlock_unlock(&THR_LOCK_servers);
879 
880   if (error)
881     trans_rollback_stmt(thd);
882   else
883     trans_commit_stmt(thd);
884   close_mysql_tables(thd);
885 
886   if (close_cached_connection_tables(thd, m_server_name.str,
887                                      m_server_name.length))
888   {
889     push_warning_printf(thd, Sql_condition::SL_WARNING,
890                         ER_UNKNOWN_ERROR, "Server connection in use");
891   }
892 
893   if (error == 0 && !thd->killed)
894     my_ok(thd, 1);
895   DBUG_RETURN(error != 0 || thd->killed);
896 }
897 
898 
servers_free(bool end)899 void servers_free(bool end)
900 {
901   DBUG_ENTER("servers_free");
902   if (!my_hash_inited(&servers_cache))
903     DBUG_VOID_RETURN;
904   if (!end)
905   {
906     free_root(&mem, MYF(MY_MARK_BLOCKS_FREE));
907 	my_hash_reset(&servers_cache);
908     DBUG_VOID_RETURN;
909   }
910   mysql_rwlock_destroy(&THR_LOCK_servers);
911   free_root(&mem,MYF(0));
912   my_hash_free(&servers_cache);
913   DBUG_VOID_RETURN;
914 }
915 
916 
917 /*
918   SYNOPSIS
919 
920   clone_server(MEM_ROOT *mem_root, FOREIGN_SERVER *orig, FOREIGN_SERVER *buff)
921 
922   Create a clone of FOREIGN_SERVER. If the supplied mem_root is of
923   thd->mem_root then the copy is automatically disposed at end of statement.
924 
925   NOTES
926 
927   ARGS
928    MEM_ROOT pointer (strings are copied into this mem root)
929    FOREIGN_SERVER pointer (made a copy of)
930    FOREIGN_SERVER buffer (if not-NULL, this pointer is returned)
931 
932   RETURN VALUE
933    FOREIGN_SEVER pointer (copy of one supplied FOREIGN_SERVER)
934 */
935 
clone_server(MEM_ROOT * mem,const FOREIGN_SERVER * server,FOREIGN_SERVER * buffer)936 static FOREIGN_SERVER *clone_server(MEM_ROOT *mem, const FOREIGN_SERVER *server,
937                                     FOREIGN_SERVER *buffer)
938 {
939   DBUG_ENTER("sql_server.cc:clone_server");
940 
941   if (!buffer)
942     buffer= new (mem) FOREIGN_SERVER();
943 
944   buffer->server_name= strmake_root(mem, server->server_name,
945                                     server->server_name_length);
946   buffer->port= server->port;
947   buffer->server_name_length= server->server_name_length;
948 
949   /* TODO: We need to examine which of these can really be NULL */
950   buffer->db= server->db ? strdup_root(mem, server->db) : NULL;
951   buffer->scheme= server->scheme ? strdup_root(mem, server->scheme) : NULL;
952   buffer->username= server->username? strdup_root(mem, server->username): NULL;
953   buffer->password= server->password? strdup_root(mem, server->password): NULL;
954   buffer->socket= server->socket ? strdup_root(mem, server->socket) : NULL;
955   buffer->owner= server->owner ? strdup_root(mem, server->owner) : NULL;
956   buffer->host= server->host ? strdup_root(mem, server->host) : NULL;
957 
958  DBUG_RETURN(buffer);
959 }
960 
961 
get_server_by_name(MEM_ROOT * mem,const char * server_name,FOREIGN_SERVER * buff)962 FOREIGN_SERVER *get_server_by_name(MEM_ROOT *mem, const char *server_name,
963                                    FOREIGN_SERVER *buff)
964 {
965   size_t server_name_length;
966   FOREIGN_SERVER *server;
967   DBUG_ENTER("get_server_by_name");
968   DBUG_PRINT("info", ("server_name %s", server_name));
969 
970   server_name_length= strlen(server_name);
971 
972   if (! server_name || !strlen(server_name))
973   {
974     DBUG_PRINT("info", ("server_name not defined!"));
975     DBUG_RETURN((FOREIGN_SERVER *)NULL);
976   }
977 
978   DBUG_PRINT("info", ("locking servers_cache"));
979   mysql_rwlock_rdlock(&THR_LOCK_servers);
980   if (!(server= (FOREIGN_SERVER *) my_hash_search(&servers_cache,
981                                                   (uchar*) server_name,
982                                                   server_name_length)))
983   {
984     DBUG_PRINT("info", ("server_name %s length %u not found!",
985                         server_name, (unsigned) server_name_length));
986     server= (FOREIGN_SERVER *) NULL;
987   }
988   /* otherwise, make copy of server */
989   else
990     server= clone_server(mem, server, buff);
991 
992   DBUG_PRINT("info", ("unlocking servers_cache"));
993   mysql_rwlock_unlock(&THR_LOCK_servers);
994   DBUG_RETURN(server);
995 }
996