1/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
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 as published by
5 the Free Software Foundation; version 2 of the License.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11
12 You should have received a copy of the GNU General Public License
13 along with this program; if not, write to the Free Software
14 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
15
16
17/*
18 The servers are saved in the system table "servers"
19
20 Currently, when the user performs an ALTER SERVER or a DROP SERVER
21 operation, it will cause all open tables which refer to the named
22 server connection to be flushed. This may cause some undesirable
23 behaviour with regard to currently running transactions. It is
24 expected that the DBA knows what s/he is doing when s/he performs
25 the ALTER SERVER or DROP SERVER operation.
26
27 TODO:
28 It is desirable for us to implement a callback mechanism instead where
29 callbacks can be registered for specific server protocols. The callback
30 will be fired when such a server name has been created/altered/dropped
31 or when statistics are to be gathered such as how many actual connections.
32 Storage engines etc will be able to make use of the callback so that
33 currently running transactions etc will not be disrupted.
34*/
35
36#include "mariadb.h"
37#include "sql_priv.h"
38#include "sql_servers.h"
39#include "unireg.h"
40#include "sql_base.h" // close_mysql_tables
41#include "records.h" // init_read_record, end_read_record
42#include <m_ctype.h>
43#include <stdarg.h>
44#include "sp_head.h"
45#include "sp.h"
46#include "transaction.h"
47#include "lock.h" // MYSQL_LOCK_IGNORE_TIMEOUT
48
49/*
50 We only use 1 mutex to guard the data structures - THR_LOCK_servers.
51 Read locked when only reading data and write-locked for all other access.
52*/
53
54static HASH servers_cache;
55static MEM_ROOT mem;
56static mysql_rwlock_t THR_LOCK_servers;
57static LEX_CSTRING MYSQL_SERVERS_NAME= {STRING_WITH_LEN("servers") };
58
59
60static bool get_server_from_table_to_cache(TABLE *table);
61
62/* insert functions */
63static int insert_server(THD *thd, FOREIGN_SERVER *server_options);
64static int insert_server_record(TABLE *table, FOREIGN_SERVER *server);
65static int insert_server_record_into_cache(FOREIGN_SERVER *server);
66static FOREIGN_SERVER *
67prepare_server_struct_for_insert(LEX_SERVER_OPTIONS *server_options);
68/* drop functions */
69static int delete_server_record(TABLE *table, LEX_CSTRING *name);
70static int delete_server_record_in_cache(LEX_SERVER_OPTIONS *server_options);
71
72/* update functions */
73static void prepare_server_struct_for_update(LEX_SERVER_OPTIONS *server_options,
74 FOREIGN_SERVER *existing,
75 FOREIGN_SERVER *altered);
76static int update_server(THD *thd, FOREIGN_SERVER *existing,
77 FOREIGN_SERVER *altered);
78static int update_server_record(TABLE *table, FOREIGN_SERVER *server);
79static int update_server_record_in_cache(FOREIGN_SERVER *existing,
80 FOREIGN_SERVER *altered);
81/* utility functions */
82static void merge_server_struct(FOREIGN_SERVER *from, FOREIGN_SERVER *to);
83
84static uchar *servers_cache_get_key(FOREIGN_SERVER *server, size_t *length,
85 my_bool not_used __attribute__((unused)))
86{
87 DBUG_ENTER("servers_cache_get_key");
88 DBUG_PRINT("info", ("server_name_length %zd server_name %s",
89 server->server_name_length,
90 server->server_name));
91
92 *length= (uint) server->server_name_length;
93 DBUG_RETURN((uchar*) server->server_name);
94}
95
96#ifdef HAVE_PSI_INTERFACE
97static PSI_rwlock_key key_rwlock_THR_LOCK_servers;
98
99static PSI_rwlock_info all_servers_cache_rwlocks[]=
100{
101 { &key_rwlock_THR_LOCK_servers, "THR_LOCK_servers", PSI_FLAG_GLOBAL}
102};
103
104static void init_servers_cache_psi_keys(void)
105{
106 const char* category= "sql";
107 int count;
108
109 if (PSI_server == NULL)
110 return;
111
112 count= array_elements(all_servers_cache_rwlocks);
113 PSI_server->register_rwlock(category, all_servers_cache_rwlocks, count);
114}
115#endif /* HAVE_PSI_INTERFACE */
116
117/*
118 Initialize structures responsible for servers used in federated
119 server scheme information for them from the server
120 table in the 'mysql' database.
121
122 SYNOPSIS
123 servers_init()
124 dont_read_server_table TRUE if we want to skip loading data from
125 server table and disable privilege checking.
126
127 NOTES
128 This function is mostly responsible for preparatory steps, main work
129 on initialization and grants loading is done in servers_reload().
130
131 RETURN VALUES
132 0 ok
133 1 Could not initialize servers
134*/
135
136bool servers_init(bool dont_read_servers_table)
137{
138 THD *thd;
139 bool return_val= FALSE;
140 DBUG_ENTER("servers_init");
141
142#ifdef HAVE_PSI_INTERFACE
143 init_servers_cache_psi_keys();
144#endif
145
146 /* init the mutex */
147 if (mysql_rwlock_init(key_rwlock_THR_LOCK_servers, &THR_LOCK_servers))
148 DBUG_RETURN(TRUE);
149
150 /* initialise our servers cache */
151 if (my_hash_init(&servers_cache, system_charset_info, 32, 0, 0,
152 (my_hash_get_key) servers_cache_get_key, 0, 0))
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(&mem, "servers", ACL_ALLOC_BLOCK_SIZE, 0,
160 MYF(MY_THREAD_SPECIFIC));
161
162 if (dont_read_servers_table)
163 goto end;
164
165 /*
166 To be able to run this from boot, we allocate a temporary THD
167 */
168 if (!(thd=new THD(0)))
169 DBUG_RETURN(TRUE);
170 thd->thread_stack= (char*) &thd;
171 thd->store_globals();
172 /*
173 It is safe to call servers_reload() since servers_* arrays and hashes which
174 will be freed there are global static objects and thus are initialized
175 by zeros at startup.
176 */
177 return_val= servers_reload(thd);
178 delete thd;
179
180end:
181 DBUG_RETURN(return_val);
182}
183
184/*
185 Initialize server structures
186
187 SYNOPSIS
188 servers_load()
189 thd Current thread
190 tables List containing open "mysql.servers"
191
192 RETURN VALUES
193 FALSE Success
194 TRUE Error
195
196 TODO
197 Revert back to old list if we failed to load new one.
198*/
199
200static bool servers_load(THD *thd, TABLE_LIST *tables)
201{
202 TABLE *table;
203 READ_RECORD read_record_info;
204 bool return_val= TRUE;
205 DBUG_ENTER("servers_load");
206
207 my_hash_reset(&servers_cache);
208 free_root(&mem, MYF(0));
209 init_sql_alloc(&mem, "servers_load", ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
210
211 if (init_read_record(&read_record_info,thd,table=tables[0].table, NULL, NULL,
212 1,0, FALSE))
213 DBUG_RETURN(1);
214 while (!(read_record_info.read_record()))
215 {
216 /* return_val is already TRUE, so no need to set */
217 if ((get_server_from_table_to_cache(table)))
218 goto end;
219 }
220
221 return_val= FALSE;
222
223end:
224 end_read_record(&read_record_info);
225 DBUG_RETURN(return_val);
226}
227
228
229/*
230 Forget current servers cache and read new servers
231 from the conneciton table.
232
233 SYNOPSIS
234 servers_reload()
235 thd Current thread
236
237 NOTE
238 All tables of calling thread which were open and locked by LOCK TABLES
239 statement will be unlocked and closed.
240 This function is also used for initialization of structures responsible
241 for user/db-level privilege checking.
242
243 RETURN VALUE
244 FALSE Success
245 TRUE Failure
246*/
247
248bool servers_reload(THD *thd)
249{
250 TABLE_LIST tables[1];
251 bool return_val= TRUE;
252 DBUG_ENTER("servers_reload");
253
254 DBUG_PRINT("info", ("locking servers_cache"));
255 mysql_rwlock_wrlock(&THR_LOCK_servers);
256
257 tables[0].init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_SERVERS_NAME, 0, TL_READ);
258
259 if (unlikely(open_and_lock_tables(thd, tables, FALSE,
260 MYSQL_LOCK_IGNORE_TIMEOUT)))
261 {
262 /*
263 Execution might have been interrupted; only print the error message
264 if an error condition has been raised.
265 */
266 if (thd->get_stmt_da()->is_error())
267 sql_print_error("Can't open and lock privilege tables: %s",
268 thd->get_stmt_da()->message());
269 return_val= FALSE;
270 goto end;
271 }
272
273 if ((return_val= servers_load(thd, tables)))
274 { // Error. Revert to old list
275 /* blast, for now, we have no servers, discuss later way to preserve */
276
277 DBUG_PRINT("error",("Reverting to old privileges"));
278 servers_free();
279 }
280
281end:
282 close_mysql_tables(thd);
283 DBUG_PRINT("info", ("unlocking servers_cache"));
284 mysql_rwlock_unlock(&THR_LOCK_servers);
285 DBUG_RETURN(return_val);
286}
287
288
289/*
290 Initialize structures responsible for servers used in federated
291 server scheme information for them from the server
292 table in the 'mysql' database.
293
294 SYNOPSIS
295 get_server_from_table_to_cache()
296 TABLE *table open table pointer
297
298
299 NOTES
300 This function takes a TABLE pointer (pointing to an opened
301 table). With this open table, a FOREIGN_SERVER struct pointer
302 is allocated into root memory, then each member of the FOREIGN_SERVER
303 struct is populated. A char pointer takes the return value of get_field
304 for each column we're interested in obtaining, and if that pointer
305 isn't 0x0, the FOREIGN_SERVER member is set to that value, otherwise,
306 is set to the value of an empty string, since get_field would set it to
307 0x0 if the column's value is empty, even if the default value for that
308 column is NOT NULL.
309
310 RETURN VALUES
311 0 ok
312 1 could not insert server struct into global servers cache
313*/
314
315static bool
316get_server_from_table_to_cache(TABLE *table)
317{
318 /* alloc a server struct */
319 char *ptr;
320 char * const blank= (char*)"";
321 FOREIGN_SERVER *server= (FOREIGN_SERVER *)alloc_root(&mem,
322 sizeof(FOREIGN_SERVER));
323 DBUG_ENTER("get_server_from_table_to_cache");
324 table->use_all_columns();
325
326 /* get each field into the server struct ptr */
327 ptr= get_field(&mem, table->field[0]);
328 server->server_name= ptr ? ptr : blank;
329 server->server_name_length= (uint) strlen(server->server_name);
330 ptr= get_field(&mem, table->field[1]);
331 server->host= ptr ? ptr : blank;
332 ptr= get_field(&mem, table->field[2]);
333 server->db= ptr ? ptr : blank;
334 ptr= get_field(&mem, table->field[3]);
335 server->username= ptr ? ptr : blank;
336 ptr= get_field(&mem, table->field[4]);
337 server->password= ptr ? ptr : blank;
338 ptr= get_field(&mem, table->field[5]);
339 server->sport= ptr ? ptr : blank;
340
341 server->port= server->sport ? atoi(server->sport) : 0;
342
343 ptr= get_field(&mem, table->field[6]);
344 server->socket= ptr && strlen(ptr) ? ptr : blank;
345 ptr= get_field(&mem, table->field[7]);
346 server->scheme= ptr ? ptr : blank;
347 ptr= get_field(&mem, table->field[8]);
348 server->owner= ptr ? ptr : blank;
349 DBUG_PRINT("info", ("server->server_name %s", server->server_name));
350 DBUG_PRINT("info", ("server->host %s", server->host));
351 DBUG_PRINT("info", ("server->db %s", server->db));
352 DBUG_PRINT("info", ("server->username %s", server->username));
353 DBUG_PRINT("info", ("server->password %s", server->password));
354 DBUG_PRINT("info", ("server->socket %s", server->socket));
355 if (my_hash_insert(&servers_cache, (uchar*) server))
356 {
357 DBUG_PRINT("info", ("had a problem inserting server %s at %p",
358 server->server_name, server));
359 // error handling needed here
360 DBUG_RETURN(TRUE);
361 }
362 DBUG_RETURN(FALSE);
363}
364
365
366/*
367 SYNOPSIS
368 insert_server()
369 THD *thd - thread pointer
370 FOREIGN_SERVER *server - pointer to prepared FOREIGN_SERVER struct
371
372 NOTES
373 This function takes a server object that is has all members properly
374 prepared, ready to be inserted both into the mysql.servers table and
375 the servers cache.
376
377 THR_LOCK_servers must be write locked.
378
379 RETURN VALUES
380 0 - no error
381 other - error code
382*/
383
384static int
385insert_server(THD *thd, FOREIGN_SERVER *server)
386{
387 int error= -1;
388 TABLE_LIST tables;
389 TABLE *table;
390 DBUG_ENTER("insert_server");
391
392 tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_SERVERS_NAME, 0, TL_WRITE);
393
394 /* need to open before acquiring THR_LOCK_plugin or it will deadlock */
395 if (! (table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
396 goto end;
397
398 /* insert the server into the table */
399 if (unlikely(error= insert_server_record(table, server)))
400 goto end;
401
402 /* insert the server into the cache */
403 if (unlikely((error= insert_server_record_into_cache(server))))
404 goto end;
405
406end:
407 DBUG_RETURN(error);
408}
409
410
411/*
412 SYNOPSIS
413 int insert_server_record_into_cache()
414 FOREIGN_SERVER *server
415
416 NOTES
417 This function takes a FOREIGN_SERVER pointer to an allocated (root mem)
418 and inserts it into the global servers cache
419
420 THR_LOCK_servers must be write locked.
421
422 RETURN VALUE
423 0 - no error
424 >0 - error code
425
426*/
427
428static int
429insert_server_record_into_cache(FOREIGN_SERVER *server)
430{
431 int error=0;
432 DBUG_ENTER("insert_server_record_into_cache");
433 /*
434 We succeded in insertion of the server to the table, now insert
435 the server to the cache
436 */
437 DBUG_PRINT("info", ("inserting server %s at %p, length %zd",
438 server->server_name, server,
439 server->server_name_length));
440 if (my_hash_insert(&servers_cache, (uchar*) server))
441 {
442 DBUG_PRINT("info", ("had a problem inserting server %s at %p",
443 server->server_name, server));
444 // error handling needed here
445 error= 1;
446 }
447 DBUG_RETURN(error);
448}
449
450
451/*
452 SYNOPSIS
453 store_server_fields()
454 TABLE *table
455 FOREIGN_SERVER *server
456
457 NOTES
458 This function takes an opened table object, and a pointer to an
459 allocated FOREIGN_SERVER struct, and then stores each member of
460 the FOREIGN_SERVER to the appropriate fields in the table, in
461 advance of insertion into the mysql.servers table
462
463 RETURN VALUE
464 VOID
465
466*/
467
468static void
469store_server_fields(TABLE *table, FOREIGN_SERVER *server)
470{
471
472 table->use_all_columns();
473 /*
474 "server" has already been prepped by prepare_server_struct_for_<>
475 so, all we need to do is check if the value is set (> -1 for port)
476
477 If this happens to be an update, only the server members that
478 have changed will be set. If an insert, then all will be set,
479 even if with empty strings
480 */
481 if (server->host)
482 table->field[1]->store(server->host,
483 (uint) strlen(server->host), system_charset_info);
484 if (server->db)
485 table->field[2]->store(server->db,
486 (uint) strlen(server->db), system_charset_info);
487 if (server->username)
488 table->field[3]->store(server->username,
489 (uint) strlen(server->username), system_charset_info);
490 if (server->password)
491 table->field[4]->store(server->password,
492 (uint) strlen(server->password), system_charset_info);
493 if (server->port > -1)
494 table->field[5]->store(server->port);
495
496 if (server->socket)
497 table->field[6]->store(server->socket,
498 (uint) strlen(server->socket), system_charset_info);
499 if (server->scheme)
500 table->field[7]->store(server->scheme,
501 (uint) strlen(server->scheme), system_charset_info);
502 if (server->owner)
503 table->field[8]->store(server->owner,
504 (uint) strlen(server->owner), system_charset_info);
505}
506
507/*
508 SYNOPSIS
509 insert_server_record()
510 TABLE *table
511 FOREIGN_SERVER *server
512
513 NOTES
514 This function takes the arguments of an open table object and a pointer
515 to an allocated FOREIGN_SERVER struct. It stores the server_name into
516 the first field of the table (the primary key, server_name column). With
517 this, index_read_idx is called, if the record is found, an error is set
518 to ER_FOREIGN_SERVER_EXISTS (the server with that server name exists in the
519 table), if not, then store_server_fields stores all fields of the
520 FOREIGN_SERVER to the table, then ha_write_row is inserted. If an error
521 is encountered in either index_read_idx or ha_write_row, then that error
522 is returned
523
524 RETURN VALUE
525 0 - no errors
526 >0 - error code
527
528 */
529
530static
531int insert_server_record(TABLE *table, FOREIGN_SERVER *server)
532{
533 int error;
534 DBUG_ENTER("insert_server_record");
535 tmp_disable_binlog(table->in_use);
536 table->use_all_columns();
537
538 empty_record(table);
539
540 /* set the field that's the PK to the value we're looking for */
541 table->field[0]->store(server->server_name,
542 server->server_name_length,
543 system_charset_info);
544
545 /* read index until record is that specified in server_name */
546 if (unlikely((error=
547 table->file->ha_index_read_idx_map(table->record[0], 0,
548 (uchar *)table->field[0]->
549 ptr,
550 HA_WHOLE_KEY,
551 HA_READ_KEY_EXACT))))
552 {
553 /* if not found, err */
554 if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
555 {
556 table->file->print_error(error, MYF(0));
557 error= 1;
558 }
559 /* store each field to be inserted */
560 store_server_fields(table, server);
561
562 DBUG_PRINT("info",("record for server '%s' not found!",
563 server->server_name));
564 /* write/insert the new server */
565 if (unlikely(error=table->file->ha_write_row(table->record[0])))
566 table->file->print_error(error, MYF(0));
567 }
568 else
569 error= ER_FOREIGN_SERVER_EXISTS;
570
571 reenable_binlog(table->in_use);
572 DBUG_RETURN(error);
573}
574
575/*
576 SYNOPSIS
577 drop_server()
578 THD *thd
579 LEX_SERVER_OPTIONS *server_options
580
581 NOTES
582 This function takes as its arguments a THD object pointer and a pointer
583 to a LEX_SERVER_OPTIONS struct from the parser. The member 'server_name'
584 of this LEX_SERVER_OPTIONS struct contains the value of the server to be
585 deleted. The mysql.servers table is opened via open_ltable,
586 a table object returned, then delete_server_record is
587 called with this table object and LEX_SERVER_OPTIONS server_name and
588 server_name_length passed, containing the name of the server to be
589 dropped/deleted, then delete_server_record_in_cache is called to delete
590 the server from the servers cache.
591
592 RETURN VALUE
593 0 - no error
594 > 0 - error code
595*/
596
597static int drop_server_internal(THD *thd, LEX_SERVER_OPTIONS *server_options)
598{
599 int error;
600 TABLE_LIST tables;
601 TABLE *table;
602
603 DBUG_ENTER("drop_server_internal");
604 DBUG_PRINT("info", ("server name server->server_name %s",
605 server_options->server_name.str));
606
607 tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_SERVERS_NAME, 0, TL_WRITE);
608
609 /* hit the memory hit first */
610 if (unlikely((error= delete_server_record_in_cache(server_options))))
611 goto end;
612
613 if (unlikely(!(table= open_ltable(thd, &tables, TL_WRITE,
614 MYSQL_LOCK_IGNORE_TIMEOUT))))
615 {
616 error= my_errno;
617 goto end;
618 }
619
620 error= delete_server_record(table, &server_options->server_name);
621
622 /* close the servers table before we call closed_cached_connection_tables */
623 close_mysql_tables(thd);
624
625 if (close_cached_connection_tables(thd, &server_options->server_name))
626 {
627 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
628 ER_UNKNOWN_ERROR, "Server connection in use");
629 }
630
631end:
632 DBUG_RETURN(error);
633}
634
635
636/**
637 Drop a server with servers cache mutex lock.
638*/
639int drop_server(THD *thd, LEX_SERVER_OPTIONS *server_options)
640{
641 mysql_rwlock_wrlock(&THR_LOCK_servers);
642 int rc= drop_server_internal(thd, server_options);
643 mysql_rwlock_unlock(&THR_LOCK_servers);
644 return rc;
645}
646
647
648/*
649
650 SYNOPSIS
651 delete_server_record_in_cache()
652 LEX_SERVER_OPTIONS *server_options
653
654 NOTES
655 This function's argument is a LEX_SERVER_OPTIONS struct pointer. This
656 function uses the "server_name" and "server_name_length" members of the
657 lex->server_options to search for the server in the servers_cache. Upon
658 returned the server (pointer to a FOREIGN_SERVER struct), it then deletes
659 that server from the servers_cache hash.
660
661 RETURN VALUE
662 0 - no error
663
664*/
665
666static int
667delete_server_record_in_cache(LEX_SERVER_OPTIONS *server_options)
668{
669 int error= ER_FOREIGN_SERVER_DOESNT_EXIST;
670 FOREIGN_SERVER *server;
671 DBUG_ENTER("delete_server_record_in_cache");
672
673 DBUG_PRINT("info",("trying to obtain server name %s length %zu",
674 server_options->server_name.str,
675 server_options->server_name.length));
676
677
678 if (!(server= (FOREIGN_SERVER *)
679 my_hash_search(&servers_cache,
680 (uchar*) server_options->server_name.str,
681 server_options->server_name.length)))
682 {
683 DBUG_PRINT("info", ("server_name %s length %zu not found!",
684 server_options->server_name.str,
685 server_options->server_name.length));
686 goto end;
687 }
688 /*
689 We succeded in deletion of the server to the table, now delete
690 the server from the cache
691 */
692 DBUG_PRINT("info",("deleting server %s length %zd",
693 server->server_name,
694 server->server_name_length));
695
696 my_hash_delete(&servers_cache, (uchar*) server);
697
698 error= 0;
699
700end:
701 DBUG_RETURN(error);
702}
703
704
705/*
706
707 SYNOPSIS
708 update_server()
709 THD *thd
710 FOREIGN_SERVER *existing
711 FOREIGN_SERVER *altered
712
713 NOTES
714 This function takes as arguments a THD object pointer, and two pointers,
715 one pointing to the existing FOREIGN_SERVER struct "existing" (which is
716 the current record as it is) and another pointer pointing to the
717 FOREIGN_SERVER struct with the members containing the modified/altered
718 values that need to be updated in both the mysql.servers table and the
719 servers_cache. It opens a table, passes the table and the altered
720 FOREIGN_SERVER pointer, which will be used to update the mysql.servers
721 table for the particular server via the call to update_server_record,
722 and in the servers_cache via update_server_record_in_cache.
723
724 THR_LOCK_servers must be write locked.
725
726 RETURN VALUE
727 0 - no error
728 >0 - error code
729
730*/
731
732int update_server(THD *thd, FOREIGN_SERVER *existing, FOREIGN_SERVER *altered)
733{
734 int error;
735 TABLE *table;
736 TABLE_LIST tables;
737 DBUG_ENTER("update_server");
738
739 tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_SERVERS_NAME, 0, TL_WRITE);
740
741 if (!(table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
742 {
743 error= my_errno;
744 goto end;
745 }
746
747 if (unlikely((error= update_server_record(table, altered))))
748 goto end;
749
750 error= update_server_record_in_cache(existing, altered);
751
752 /*
753 Perform a reload so we don't have a 'hole' in our mem_root
754 */
755 servers_load(thd, &tables);
756
757end:
758 DBUG_RETURN(error);
759}
760
761
762/*
763
764 SYNOPSIS
765 update_server_record_in_cache()
766 FOREIGN_SERVER *existing
767 FOREIGN_SERVER *altered
768
769 NOTES
770 This function takes as an argument the FOREIGN_SERVER structi pointer
771 for the existing server and the FOREIGN_SERVER struct populated with only
772 the members which have been updated. It then "merges" the "altered" struct
773 members to the existing server, the existing server then represents an
774 updated server. Then, the existing record is deleted from the servers_cache
775 HASH, then the updated record inserted, in essence replacing the old
776 record.
777
778 THR_LOCK_servers must be write locked.
779
780 RETURN VALUE
781 0 - no error
782 1 - error
783
784*/
785
786int update_server_record_in_cache(FOREIGN_SERVER *existing,
787 FOREIGN_SERVER *altered)
788{
789 int error= 0;
790 DBUG_ENTER("update_server_record_in_cache");
791
792 /*
793 update the members that haven't been change in the altered server struct
794 with the values of the existing server struct
795 */
796 merge_server_struct(existing, altered);
797
798 /*
799 delete the existing server struct from the server cache
800 */
801 my_hash_delete(&servers_cache, (uchar*)existing);
802
803 /*
804 Insert the altered server struct into the server cache
805 */
806 if (my_hash_insert(&servers_cache, (uchar*)altered))
807 {
808 DBUG_PRINT("info", ("had a problem inserting server %s at %p",
809 altered->server_name,altered));
810 error= ER_OUT_OF_RESOURCES;
811 }
812
813 DBUG_RETURN(error);
814}
815
816
817/*
818
819 SYNOPSIS
820 merge_server_struct()
821 FOREIGN_SERVER *from
822 FOREIGN_SERVER *to
823
824 NOTES
825 This function takes as its arguments two pointers each to an allocated
826 FOREIGN_SERVER struct. The first FOREIGN_SERVER struct represents the struct
827 that we will obtain values from (hence the name "from"), the second
828 FOREIGN_SERVER struct represents which FOREIGN_SERVER struct we will be
829 "copying" any members that have a value to (hence the name "to")
830
831 RETURN VALUE
832 VOID
833
834*/
835
836void merge_server_struct(FOREIGN_SERVER *from, FOREIGN_SERVER *to)
837{
838 DBUG_ENTER("merge_server_struct");
839 if (!to->host)
840 to->host= strdup_root(&mem, from->host);
841 if (!to->db)
842 to->db= strdup_root(&mem, from->db);
843 if (!to->username)
844 to->username= strdup_root(&mem, from->username);
845 if (!to->password)
846 to->password= strdup_root(&mem, from->password);
847 if (to->port == -1)
848 to->port= from->port;
849 if (!to->socket && from->socket)
850 to->socket= strdup_root(&mem, from->socket);
851 if (!to->scheme && from->scheme)
852 to->scheme= strdup_root(&mem, from->scheme);
853 if (!to->owner)
854 to->owner= strdup_root(&mem, from->owner);
855
856 DBUG_VOID_RETURN;
857}
858
859
860/*
861
862 SYNOPSIS
863 update_server_record()
864 TABLE *table
865 FOREIGN_SERVER *server
866
867 NOTES
868 This function takes as its arguments an open TABLE pointer, and a pointer
869 to an allocated FOREIGN_SERVER structure representing an updated record
870 which needs to be inserted. The primary key, server_name is stored to field
871 0, then index_read_idx is called to read the index to that record, the
872 record then being ready to be updated, if found. If not found an error is
873 set and error message printed. If the record is found, store_record is
874 called, then store_server_fields stores each field from the the members of
875 the updated FOREIGN_SERVER struct.
876
877 RETURN VALUE
878 0 - no error
879
880*/
881
882
883static int
884update_server_record(TABLE *table, FOREIGN_SERVER *server)
885{
886 int error=0;
887 DBUG_ENTER("update_server_record");
888 tmp_disable_binlog(table->in_use);
889 table->use_all_columns();
890 /* set the field that's the PK to the value we're looking for */
891 table->field[0]->store(server->server_name,
892 server->server_name_length,
893 system_charset_info);
894
895 if (unlikely((error=
896 table->file->ha_index_read_idx_map(table->record[0], 0,
897 (uchar *)table->field[0]->
898 ptr,
899 ~(longlong)0,
900 HA_READ_KEY_EXACT))))
901 {
902 if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
903 table->file->print_error(error, MYF(0));
904 DBUG_PRINT("info",("server not found!"));
905 error= ER_FOREIGN_SERVER_DOESNT_EXIST;
906 }
907 else
908 {
909 /* ok, so we can update since the record exists in the table */
910 store_record(table,record[1]);
911 store_server_fields(table, server);
912 if (unlikely((error=table->file->ha_update_row(table->record[1],
913 table->record[0])) &&
914 error != HA_ERR_RECORD_IS_THE_SAME))
915 {
916 DBUG_PRINT("info",("problems with ha_update_row %d", error));
917 goto end;
918 }
919 else
920 error= 0;
921 }
922
923end:
924 reenable_binlog(table->in_use);
925 DBUG_RETURN(error);
926}
927
928
929/*
930
931 SYNOPSIS
932 delete_server_record()
933 TABLE *table
934 char *server_name
935 int server_name_length
936
937 NOTES
938
939 RETURN VALUE
940 0 - no error
941
942*/
943
944static int
945delete_server_record(TABLE *table, LEX_CSTRING *name)
946{
947 int error;
948 DBUG_ENTER("delete_server_record");
949 tmp_disable_binlog(table->in_use);
950 table->use_all_columns();
951
952 /* set the field that's the PK to the value we're looking for */
953 table->field[0]->store(name->str, name->length, system_charset_info);
954
955 if (unlikely((error=
956 table->file->ha_index_read_idx_map(table->record[0], 0,
957 (uchar *)table->field[0]->
958 ptr,
959 HA_WHOLE_KEY,
960 HA_READ_KEY_EXACT))))
961 {
962 if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
963 table->file->print_error(error, MYF(0));
964 DBUG_PRINT("info",("server not found!"));
965 error= ER_FOREIGN_SERVER_DOESNT_EXIST;
966 }
967 else
968 {
969 if (unlikely((error= table->file->ha_delete_row(table->record[0]))))
970 table->file->print_error(error, MYF(0));
971 }
972
973 reenable_binlog(table->in_use);
974 DBUG_RETURN(error);
975}
976
977/*
978
979 SYNOPSIS
980 create_server()
981 THD *thd
982 LEX_SERVER_OPTIONS *server_options
983
984 NOTES
985
986 RETURN VALUE
987 0 - no error
988
989*/
990
991int create_server(THD *thd, LEX_SERVER_OPTIONS *server_options)
992{
993 int error= ER_FOREIGN_SERVER_EXISTS;
994 FOREIGN_SERVER *server;
995
996 DBUG_ENTER("create_server");
997 DBUG_PRINT("info", ("server_options->server_name %s",
998 server_options->server_name.str));
999
1000 mysql_rwlock_wrlock(&THR_LOCK_servers);
1001
1002 /* hit the memory first */
1003 if (my_hash_search(&servers_cache, (uchar*) server_options->server_name.str,
1004 server_options->server_name.length))
1005 {
1006 if (thd->lex->create_info.or_replace())
1007 {
1008 if (unlikely((error= drop_server_internal(thd, server_options))))
1009 goto end;
1010 }
1011 else if (thd->lex->create_info.if_not_exists())
1012 {
1013 push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
1014 ER_FOREIGN_SERVER_EXISTS,
1015 ER_THD(thd, ER_FOREIGN_SERVER_EXISTS),
1016 server_options->server_name.str);
1017 error= 0;
1018 goto end;
1019 }
1020 else
1021 goto end;
1022 }
1023
1024 if (!(server= prepare_server_struct_for_insert(server_options)))
1025 {
1026 /* purecov: begin inspected */
1027 error= ER_OUT_OF_RESOURCES;
1028 goto end;
1029 /* purecov: end */
1030 }
1031
1032 error= insert_server(thd, server);
1033
1034 DBUG_PRINT("info", ("error returned %d", error));
1035
1036end:
1037 mysql_rwlock_unlock(&THR_LOCK_servers);
1038
1039 if (unlikely(error))
1040 {
1041 DBUG_PRINT("info", ("problem creating server <%s>",
1042 server_options->server_name.str));
1043 my_error(error, MYF(0), server_options->server_name.str);
1044 }
1045 else
1046 my_ok(thd);
1047
1048 DBUG_RETURN(error);
1049}
1050
1051
1052/*
1053
1054 SYNOPSIS
1055 alter_server()
1056 THD *thd
1057 LEX_SERVER_OPTIONS *server_options
1058
1059 NOTES
1060
1061 RETURN VALUE
1062 0 - no error
1063
1064*/
1065
1066int alter_server(THD *thd, LEX_SERVER_OPTIONS *server_options)
1067{
1068 int error= ER_FOREIGN_SERVER_DOESNT_EXIST;
1069 FOREIGN_SERVER altered, *existing;
1070 DBUG_ENTER("alter_server");
1071 DBUG_PRINT("info", ("server_options->server_name %s",
1072 server_options->server_name.str));
1073
1074 mysql_rwlock_wrlock(&THR_LOCK_servers);
1075
1076 if (!(existing= (FOREIGN_SERVER *) my_hash_search(&servers_cache,
1077 (uchar*) server_options->server_name.str,
1078 server_options->server_name.length)))
1079 goto end;
1080
1081 prepare_server_struct_for_update(server_options, existing, &altered);
1082
1083 error= update_server(thd, existing, &altered);
1084
1085 /* close the servers table before we call closed_cached_connection_tables */
1086 close_mysql_tables(thd);
1087
1088 if (close_cached_connection_tables(thd, &server_options->server_name))
1089 {
1090 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
1091 ER_UNKNOWN_ERROR, "Server connection in use");
1092 }
1093
1094end:
1095 DBUG_PRINT("info", ("error returned %d", error));
1096 mysql_rwlock_unlock(&THR_LOCK_servers);
1097 DBUG_RETURN(error);
1098}
1099
1100
1101/*
1102
1103 SYNOPSIS
1104 prepare_server_struct_for_insert()
1105 LEX_SERVER_OPTIONS *server_options
1106
1107 NOTES
1108 As FOREIGN_SERVER members are allocated on mem_root, we do not need to
1109 free them in case of error.
1110
1111 RETURN VALUE
1112 On success filled FOREIGN_SERVER, or NULL in case out of memory.
1113
1114*/
1115
1116static FOREIGN_SERVER *
1117prepare_server_struct_for_insert(LEX_SERVER_OPTIONS *server_options)
1118{
1119 FOREIGN_SERVER *server;
1120 ulong default_port= 0;
1121 DBUG_ENTER("prepare_server_struct");
1122
1123 if (!(server= (FOREIGN_SERVER *)alloc_root(&mem, sizeof(FOREIGN_SERVER))))
1124 DBUG_RETURN(NULL); /* purecov: inspected */
1125
1126#define SET_SERVER_OR_RETURN(X, DEFAULT) \
1127 do { \
1128 if (!(server->X= server_options->X.str ? \
1129 strmake_root(&mem, server_options->X.str, \
1130 server_options->X.length) : "")) \
1131 DBUG_RETURN(NULL); \
1132 } while(0)
1133
1134 /* name and scheme are always set (the parser guarantees it) */
1135 SET_SERVER_OR_RETURN(server_name, NULL);
1136 SET_SERVER_OR_RETURN(scheme, NULL);
1137
1138 /* scheme-specific checks */
1139 if (!strcasecmp(server->scheme, "mysql"))
1140 {
1141 default_port= MYSQL_PORT;
1142 if (!server_options->host.str && !server_options->socket.str)
1143 {
1144 my_error(ER_CANT_CREATE_FEDERATED_TABLE, MYF(0),
1145 "either HOST or SOCKET must be set");
1146 DBUG_RETURN(NULL);
1147 }
1148 }
1149
1150 SET_SERVER_OR_RETURN(host, "");
1151 SET_SERVER_OR_RETURN(db, "");
1152 SET_SERVER_OR_RETURN(username, "");
1153 SET_SERVER_OR_RETURN(password, "");
1154 SET_SERVER_OR_RETURN(socket, "");
1155 SET_SERVER_OR_RETURN(owner, "");
1156
1157 server->server_name_length= server_options->server_name.length;
1158
1159 /* set to default_port if not specified */
1160 server->port= server_options->port > -1 ?
1161 server_options->port : default_port;
1162
1163 DBUG_RETURN(server);
1164}
1165
1166/*
1167
1168 SYNOPSIS
1169 prepare_server_struct_for_update()
1170 LEX_SERVER_OPTIONS *server_options
1171
1172 NOTES
1173
1174 RETURN VALUE
1175 0 - no error
1176
1177*/
1178
1179static void
1180prepare_server_struct_for_update(LEX_SERVER_OPTIONS *server_options,
1181 FOREIGN_SERVER *existing,
1182 FOREIGN_SERVER *altered)
1183{
1184 DBUG_ENTER("prepare_server_struct_for_update");
1185
1186 altered->server_name= existing->server_name;
1187 altered->server_name_length= existing->server_name_length;
1188 DBUG_PRINT("info", ("existing name %s altered name %s",
1189 existing->server_name, altered->server_name));
1190
1191 /*
1192 The logic here is this: is this value set AND is it different
1193 than the existing value?
1194 */
1195#define SET_ALTERED(X) \
1196 do { \
1197 altered->X= \
1198 (server_options->X.str && strcmp(server_options->X.str, existing->X)) \
1199 ? strmake_root(&mem, server_options->X.str, server_options->X.length) \
1200 : 0; \
1201 } while(0)
1202
1203 SET_ALTERED(host);
1204 SET_ALTERED(db);
1205 SET_ALTERED(username);
1206 SET_ALTERED(password);
1207 SET_ALTERED(socket);
1208 SET_ALTERED(scheme);
1209 SET_ALTERED(owner);
1210
1211 /*
1212 port is initialised to -1, so if unset, it will be -1
1213 */
1214 altered->port= (server_options->port > -1 &&
1215 server_options->port != existing->port) ?
1216 server_options->port : -1;
1217
1218 DBUG_VOID_RETURN;
1219}
1220
1221/*
1222
1223 SYNOPSIS
1224 servers_free()
1225 bool end
1226
1227 NOTES
1228
1229 RETURN VALUE
1230 void
1231
1232*/
1233
1234void servers_free(bool end)
1235{
1236 DBUG_ENTER("servers_free");
1237 if (!my_hash_inited(&servers_cache))
1238 DBUG_VOID_RETURN;
1239 if (!end)
1240 {
1241 free_root(&mem, MYF(MY_MARK_BLOCKS_FREE));
1242 my_hash_reset(&servers_cache);
1243 DBUG_VOID_RETURN;
1244 }
1245 mysql_rwlock_destroy(&THR_LOCK_servers);
1246 free_root(&mem,MYF(0));
1247 my_hash_free(&servers_cache);
1248 DBUG_VOID_RETURN;
1249}
1250
1251
1252/*
1253 SYNOPSIS
1254
1255 clone_server(MEM_ROOT *mem_root, FOREIGN_SERVER *orig, FOREIGN_SERVER *buff)
1256
1257 Create a clone of FOREIGN_SERVER. If the supplied mem_root is of
1258 thd->mem_root then the copy is automatically disposed at end of statement.
1259
1260 NOTES
1261
1262 ARGS
1263 MEM_ROOT pointer (strings are copied into this mem root)
1264 FOREIGN_SERVER pointer (made a copy of)
1265 FOREIGN_SERVER buffer (if not-NULL, this pointer is returned)
1266
1267 RETURN VALUE
1268 FOREIGN_SEVER pointer (copy of one supplied FOREIGN_SERVER)
1269*/
1270
1271static FOREIGN_SERVER *clone_server(MEM_ROOT *mem, const FOREIGN_SERVER *server,
1272 FOREIGN_SERVER *buffer)
1273{
1274 DBUG_ENTER("sql_server.cc:clone_server");
1275
1276 if (!buffer)
1277 buffer= (FOREIGN_SERVER *) alloc_root(mem, sizeof(FOREIGN_SERVER));
1278
1279 buffer->server_name= strmake_root(mem, server->server_name,
1280 server->server_name_length);
1281 buffer->port= server->port;
1282 buffer->server_name_length= server->server_name_length;
1283
1284 /* TODO: We need to examine which of these can really be NULL */
1285 buffer->db= safe_strdup_root(mem, server->db);
1286 buffer->scheme= safe_strdup_root(mem, server->scheme);
1287 buffer->username= safe_strdup_root(mem, server->username);
1288 buffer->password= safe_strdup_root(mem, server->password);
1289 buffer->socket= safe_strdup_root(mem, server->socket);
1290 buffer->owner= safe_strdup_root(mem, server->owner);
1291 buffer->host= safe_strdup_root(mem, server->host);
1292
1293 DBUG_RETURN(buffer);
1294}
1295
1296
1297/*
1298
1299 SYNOPSIS
1300 get_server_by_name()
1301 const char *server_name
1302
1303 NOTES
1304
1305 RETURN VALUE
1306 FOREIGN_SERVER *
1307
1308*/
1309
1310FOREIGN_SERVER *get_server_by_name(MEM_ROOT *mem, const char *server_name,
1311 FOREIGN_SERVER *buff)
1312{
1313 size_t server_name_length;
1314 FOREIGN_SERVER *server;
1315 DBUG_ENTER("get_server_by_name");
1316 DBUG_PRINT("info", ("server_name %s", server_name));
1317
1318 server_name_length= strlen(server_name);
1319
1320 if (! server_name || !strlen(server_name))
1321 {
1322 DBUG_PRINT("info", ("server_name not defined!"));
1323 DBUG_RETURN((FOREIGN_SERVER *)NULL);
1324 }
1325
1326 DBUG_PRINT("info", ("locking servers_cache"));
1327 mysql_rwlock_rdlock(&THR_LOCK_servers);
1328 if (!(server= (FOREIGN_SERVER *) my_hash_search(&servers_cache,
1329 (uchar*) server_name,
1330 server_name_length)))
1331 {
1332 DBUG_PRINT("info", ("server_name %s length %u not found!",
1333 server_name, (unsigned) server_name_length));
1334 server= (FOREIGN_SERVER *) NULL;
1335 }
1336 /* otherwise, make copy of server */
1337 else
1338 server= clone_server(mem, server, buff);
1339
1340 DBUG_PRINT("info", ("unlocking servers_cache"));
1341 mysql_rwlock_unlock(&THR_LOCK_servers);
1342 DBUG_RETURN(server);
1343
1344}
1345