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