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