mirror of
https://github.com/MariaDB/server.git
synced 2025-01-17 12:32:27 +01:00
2a0d2fef51
cause ROLLBACK of statement", part 1. Review fixes. Do not send OK/EOF packets to the client until we reached the end of the current statement. This is a consolidation, to keep the functionality that is shared by all SQL statements in one place in the server. Currently this functionality includes: - close_thread_tables() - log_slow_statement(). After this patch and the subsequent patch for Bug#12713, it shall also include: - ha_autocommit_or_rollback() - net_end_statement() - query_cache_end_of_result(). In future it may also include: - mysql_reset_thd_for_next_command(). include/mysql_com.h: Rename now unused members of NET: no_send_ok, no_send_error, report_error. These were server-specific variables related to the client/server protocol. They have been made obsolete by this patch. Previously the same members of NET were used to store the error message both on the client and on the server. The error message was stored in net.last_error (client: mysql->net.last_error, server: thd->net.last_error). The error code was stored in net.last_errno (client: mysql->net.last_errno, server: thd->net.last_errno). The server error code and message are now stored elsewhere (in the Diagnostics_area), thus NET members are no longer used by the server. Rename last_error to client_last_error, last_errno to client_last_errno to avoid potential bugs introduced by merges. include/mysql_h.ic: Update the ABI file to reflect a rename. Renames do not break the binary compatibility. libmysql/libmysql.c: Rename last_error to client_last_error, last_errno to client_last_errno. This is necessary to ensure no unnoticed bugs introduced by merged changesets. Remove net.report_error, net.no_send_ok, net.no_send_error. libmysql/manager.c: Rename net.last_errno to net.client_last_errno. libmysqld/lib_sql.cc: Rename net.last_errno to net.client_last_errno. Update the embedded implementation of the client-server protocol to reflect the refactoring of protocol.cc. libmysqld/libmysqld.c: Rename net.last_errno to net.client_last_errno. mysql-test/r/events.result: Update to reflect the change in mysql_rm_db(). Now we drop stored routines and events for a given database name only if there is a directory for this database name. ha_drop_database() and query_cache_invalidate() are called likewise. Previously we would attempt to drop routines/events even if database directory was not found (it worked, since routines and events are stored in tables). This fixes Bug 29958 "Weird message on DROP DATABASE if mysql.proc does not exist". The change was done because the previous code used to call send_ok() twice, which led to an assertion failure when asserts against it were added by this patch. mysql-test/r/grant.result: Fix the patch for Bug 16470, now FLUSH PRIVILEGES produces an error if mysql.procs_priv is missing. This fixes the assert that send_ok() must not called after send_error() (the original patch for Bug 16470 was prone to this). mysql-test/suite/rpl/r/rpl_row_tabledefs_2myisam.result: Produce a more detailed error message. mysql-test/suite/rpl/r/rpl_row_tabledefs_3innodb.result: Produce a more detailed error message. mysql-test/t/grant.test: Update the test, now FLUSH PRIVILEGES returns an error if mysql.procs_priv is missing. server-tools/instance-manager/mysql_connection.cc: Rename net.last_errno to net.client_last_errno. sql/ha_ndbcluster_binlog.cc: Add asserts. Use getters to access statement status information. Add a comment why run_query() is broken. Reset the diagnostics area in the end of run_query() to fulfill the invariant that the diagnostics_area is never assigned twice per statement (see the comment in the code when this can happen). We still do not clear thd->is_fatal_error and thd->is_slave_error, which may lead to bugs, I consider the whole affair as something to be dealt with separately. sql/ha_partition.cc: fatal_error() doesn't set an error by itself. Perhaps we should remove this method altogether and instead add a flag to my_error to set thd->is_fatal_error property. Meanwhile, this change is a part of inspection made to the entire source code with the goal to ensure that fatal_error() is always accompanied by my_error(). sql/item_func.cc: There is no net.last_error anymore. Remove the obsolete assignment. sql/log_event.cc: Use getters to access statement error status information. sql/log_event_old.cc: Use getters to access statement error status information. sql/mysqld.cc: Previously, if a continue handler for an error was found, my_message_sql() would not set an error in THD. Since the current statement must be aborted in any case, find_handler() had a hack to assign thd->net.report_error to 1. Remove this hack. Set an error in my_message_sql() even if the continue handler is found. The error will be cleared anyway when the handler is executed. This is one action among many in this patch to ensure the invariant that whenever thd->is_error() is TRUE, we have a message in thd->main_da.message(). sql/net_serv.cc: Use a full-blown my_error() in net_serv.cc to report an error, instead of just setting net->last_errno. This ensures the invariant that whenever thd->is_error() returns TRUE, we have a message in thd->main_da.message(). Remove initialization of removed NET members. sql/opt_range.cc: Use my_error() instead of just raising thd->net.report_error. This ensures the invariant that whenever thd->is_error() returns TRUE, there is a message in thd->main_da.message(). sql/opt_sum.cc: Move invocation of fatal_error() right next to the place where we set the error message. That makes it easier to track that whenever fatal_error() is called, there is a message in THD. sql/protocol.cc: Rename send_ok() and send_eof() to net_send_ok() and net_send_eof() respectively. These functions write directly to the network and are not for use anywhere outside the client/server protocol code. Remove the code that was responsible for cases when either there is no error code, or no error message, or both. Instead the calling code ensures that they are always present. Asserts are added to enforce the invariant. Instead of a direct access to thd->server_status and thd->total_warn_count use function parameters, since these from now on don't always come directly from THD. Introduce net_end_statement(), the single-entry-point replacement API for send_ok(), send_eof() and net_send_error(). Implement Protocol::end_partial_result_set to use in select_send::abort() when there is a continue handler. sql/protocol.h: Update declarations. sql/repl_failsafe.cc: Use getters to access statement status information in THD. Rename net.last_error to net.client_last_error. sql/rpl_record.cc: Set an error message in prepare_record() if there is no default value for the field -- later we do print this message to the client. sql/rpl_rli.cc: Use getters to access statement status information in THD. sql/slave.cc: In create_table_from_dump() (a common function that is used in LOAD MASTER TABLE SQL statement and COM_LOAD_MASTER_DATA), instead of hacks with no_send_ok, clear the diagnostics area when mysql_rm_table() succeeded. Update has_temporary_error() to work correctly when no error is set. This is the case when Incident_log_event is executed: it always returns an error but does not set an error message. Use getters to access error status information. sql/sp_head.cc: Instead of hacks with no_send_error, work through the diagnostics area interface to suppress sending of OK/ERROR packets to the client. Move query_cache_end_of_result before log_slow_statement(), similarly to how it's done in dispatch_command(). sql/sp_rcontext.cc: Remove hacks with assignment of thd->net.report_error, they are not necessary any more (see the changes in mysqld.cc). sql/sql_acl.cc: Use getters to access error status information in THD. sql/sql_base.cc: Access thd->main_da.sql_errno() only if there is an error. This fixes a bug when auto-discovery, that was effectively disabled under pre-locking. sql/sql_binlog.cc: Remove hacks with no_send_ok/no_send_error, they are not necessary anymore: the caller is responsible for network communication. sql/sql_cache.cc: Disable sending of OK/ERROR/EOF packet in the end of dispatch_command if the response has been served from the query cache. This raises the question whether we should store EOF packet in the query cache at all, or generate it anew for each statement (we should generate it anew), but this is to be addressed separately. sql/sql_class.cc: Implement class Diagnostics_area. Please see comments in sql_class.h for details. Fix a subtle coding mistake in select_send::send_data: when on slave, an error in Item::send() was ignored. The problem became visible due to asserts that the diagnostics area is never double assigned. Remove initialization of removed NET members. In select_send::abort() do not call select_send::send_eof(). This is not inheritance-safe. Even if a stored procedure continue handler is found, the current statement is aborted, not succeeded. Instead introduce a Protocol API to send the required response, Protocol::end_partial_result_set(). This simplifies implementation of select_send::send_eof(). No need to add more asserts that there is no error, there is an assert inside Diagnostics_area::set_ok_status() already. Leave no trace of no_send_* in the code. sql/sql_class.h: Declare class Diagnostics_area. Remove the hack with no_send_ok from Substatement_state. Provide inline implementations of send_ok/send_eof. Add commetns. sql/sql_connect.cc: Remove hacks with no_send_error. Since now an error in THD is always set if net->error, it's not necessary to check both net->error and thd->is_error() in the do_command loop. Use thd->main_da.message() instead of net->last_errno. Remove the hack with is_slave_error in sys_init_connect. Since now we do not reset the diagnostics area in net_send_error (it's reset at the beginning of the next statement), we can access it safely even after execute_init_command. sql/sql_db.cc: Update the code to satisfy the invariant that the diagnostics area is never assigned twice. Incidentally, this fixes Bug 29958 "Weird message on DROP DATABASE if mysql.proc does not exist". sql/sql_delete.cc: Change multi-delete to abort in abort(), as per select_send protocol. Fixes the merge error with the test for Bug 29136 sql/sql_derived.cc: Use getters to access error information. sql/sql_insert.cc: Use getters to access error information. sql-common/client.c: Rename last_error to client_last_error, last_errno to client_last_errno. sql/sql_parse.cc: Remove hacks with no_send_error. Deploy net_end_statement(). The story of COM_SHUTDOWN is interesting. Long story short, the server would become on its death's door, and only no_send_ok/no_send_error assigned by send_ok()/net_send_error() would hide its babbling from the client. First of all, COM_QUIT does not require a response. So, the comment saying "Let's send a response to possible COM_QUIT" is not only groundless (even mysqladmin shutdown/mysql_shutdown() doesn't send COM_QUIT after COM_SHUTDOWN), it's plainly incorrect. Secondly, besides this additional 'OK' packet to respond to a hypothetical COM_QUIT, there was the following code in dispatch_command(): if (thd->killed) thd->send_kill_message(); if (thd->is_error() net_send_error(thd); This worked out really funny for the thread through which COM_SHUTDOWN was delivered: we would get COM_SHUTDOWN, say okay, say okay again, kill everybody, get the kill signal ourselves, and then attempt to say "Server shutdown in progress" to the client that is very likely long gone. This all became visible when asserts were added that the Diagnostics_area is not assigned twice. Move query_cache_end_of_result() to the end of dispatch_command(), since net_send_eof() has been moved there. This is safe, query_cache_end_of_result() is a no-op if there is no started query in the cache. Consistently use select_send interface to call abort() or send_eof() depending on the operation result. Remove thd->fatal_error() from reset_master(), it was a no-op. in hacks with no_send_error woudl save us from complete breakage of the client/server protocol. Consistently use select_send::abort() whenever there is an error, and select_send::send_eof() in case of success. The issue became visible due to added asserts. sql/sql_partition.cc: Always set an error in THD whenever there is a call to fatal_error(). sql/sql_prepare.cc: Deploy class Diagnostics_area. Remove the unnecessary juggling with the protocol in Select_fetch_protocol_binary::send_eof(). EOF packet format is protocol-independent. sql/sql_select.cc: Call fatal_error() directly in opt_sum_query. Call my_error() whenever we call thd->fatal_error(). sql/sql_servers.cc: Use getters to access error information in THD. sql/sql_show.cc: Use getters to access error information in THD. Add comments. Call my_error() whenever we call fatal_error(). sql/sql_table.cc: Replace hacks with no_send_ok with the interface of the diagnostics area. Clear the error if ENOENT error in ha_delete_table(). sql/sql_update.cc: Introduce multi_update::abort(), which is the proper way to abort a multi-update. This fixes the merge conflict between this patch and the patch for Bug 29136. sql/table.cc: Use a getter to access error information in THD. sql/tztime.cc: Use a getter to access error information in THD.
1286 lines
36 KiB
C++
1286 lines
36 KiB
C++
/* Copyright (C) 2000-2003 MySQL AB
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|
|
|
|
|
/*
|
|
The servers are saved in the system table "servers"
|
|
|
|
Currently, when the user performs an ALTER SERVER or a DROP SERVER
|
|
operation, it will cause all open tables which refer to the named
|
|
server connection to be flushed. This may cause some undesirable
|
|
behaviour with regard to currently running transactions. It is
|
|
expected that the DBA knows what s/he is doing when s/he performs
|
|
the ALTER SERVER or DROP SERVER operation.
|
|
|
|
TODO:
|
|
It is desirable for us to implement a callback mechanism instead where
|
|
callbacks can be registered for specific server protocols. The callback
|
|
will be fired when such a server name has been created/altered/dropped
|
|
or when statistics are to be gathered such as how many actual connections.
|
|
Storage engines etc will be able to make use of the callback so that
|
|
currently running transactions etc will not be disrupted.
|
|
*/
|
|
|
|
#include "mysql_priv.h"
|
|
#include "hash_filo.h"
|
|
#include <m_ctype.h>
|
|
#include <stdarg.h>
|
|
#include "sp_head.h"
|
|
#include "sp.h"
|
|
|
|
/*
|
|
We only use 1 mutex to guard the data structures - THR_LOCK_servers.
|
|
Read locked when only reading data and write-locked for all other access.
|
|
*/
|
|
|
|
static HASH servers_cache;
|
|
static MEM_ROOT mem;
|
|
static rw_lock_t THR_LOCK_servers;
|
|
|
|
static bool get_server_from_table_to_cache(TABLE *table);
|
|
|
|
/* insert functions */
|
|
static int insert_server(THD *thd, FOREIGN_SERVER *server_options);
|
|
static int insert_server_record(TABLE *table, FOREIGN_SERVER *server);
|
|
static int insert_server_record_into_cache(FOREIGN_SERVER *server);
|
|
static void prepare_server_struct_for_insert(LEX_SERVER_OPTIONS *server_options,
|
|
FOREIGN_SERVER *server);
|
|
/* drop functions */
|
|
static int delete_server_record(TABLE *table,
|
|
char *server_name,
|
|
int server_name_length);
|
|
static int delete_server_record_in_cache(LEX_SERVER_OPTIONS *server_options);
|
|
|
|
/* update functions */
|
|
static void prepare_server_struct_for_update(LEX_SERVER_OPTIONS *server_options,
|
|
FOREIGN_SERVER *existing,
|
|
FOREIGN_SERVER *altered);
|
|
static int update_server(THD *thd, FOREIGN_SERVER *existing,
|
|
FOREIGN_SERVER *altered);
|
|
static int update_server_record(TABLE *table, FOREIGN_SERVER *server);
|
|
static int update_server_record_in_cache(FOREIGN_SERVER *existing,
|
|
FOREIGN_SERVER *altered);
|
|
/* utility functions */
|
|
static void merge_server_struct(FOREIGN_SERVER *from, FOREIGN_SERVER *to);
|
|
|
|
|
|
|
|
static uchar *servers_cache_get_key(FOREIGN_SERVER *server, size_t *length,
|
|
my_bool not_used __attribute__((unused)))
|
|
{
|
|
DBUG_ENTER("servers_cache_get_key");
|
|
DBUG_PRINT("info", ("server_name_length %d server_name %s",
|
|
server->server_name_length,
|
|
server->server_name));
|
|
|
|
*length= (uint) server->server_name_length;
|
|
DBUG_RETURN((uchar*) server->server_name);
|
|
}
|
|
|
|
|
|
/*
|
|
Initialize structures responsible for servers used in federated
|
|
server scheme information for them from the server
|
|
table in the 'mysql' database.
|
|
|
|
SYNOPSIS
|
|
servers_init()
|
|
dont_read_server_table TRUE if we want to skip loading data from
|
|
server table and disable privilege checking.
|
|
|
|
NOTES
|
|
This function is mostly responsible for preparatory steps, main work
|
|
on initialization and grants loading is done in servers_reload().
|
|
|
|
RETURN VALUES
|
|
0 ok
|
|
1 Could not initialize servers
|
|
*/
|
|
|
|
bool servers_init(bool dont_read_servers_table)
|
|
{
|
|
THD *thd;
|
|
bool return_val= FALSE;
|
|
DBUG_ENTER("servers_init");
|
|
|
|
/* init the mutex */
|
|
if (my_rwlock_init(&THR_LOCK_servers, NULL))
|
|
DBUG_RETURN(TRUE);
|
|
|
|
/* initialise our servers cache */
|
|
if (hash_init(&servers_cache, system_charset_info, 32, 0, 0,
|
|
(hash_get_key) servers_cache_get_key, 0, 0))
|
|
{
|
|
return_val= TRUE; /* we failed, out of memory? */
|
|
goto end;
|
|
}
|
|
|
|
/* Initialize the mem root for data */
|
|
init_alloc_root(&mem, ACL_ALLOC_BLOCK_SIZE, 0);
|
|
|
|
if (dont_read_servers_table)
|
|
goto end;
|
|
|
|
/*
|
|
To be able to run this from boot, we allocate a temporary THD
|
|
*/
|
|
if (!(thd=new THD))
|
|
DBUG_RETURN(TRUE);
|
|
thd->thread_stack= (char*) &thd;
|
|
thd->store_globals();
|
|
lex_start(thd);
|
|
/*
|
|
It is safe to call servers_reload() since servers_* arrays and hashes which
|
|
will be freed there are global static objects and thus are initialized
|
|
by zeros at startup.
|
|
*/
|
|
return_val= servers_reload(thd);
|
|
delete thd;
|
|
/* Remember that we don't have a THD */
|
|
my_pthread_setspecific_ptr(THR_THD, 0);
|
|
|
|
end:
|
|
DBUG_RETURN(return_val);
|
|
}
|
|
|
|
/*
|
|
Initialize server structures
|
|
|
|
SYNOPSIS
|
|
servers_load()
|
|
thd Current thread
|
|
tables List containing open "mysql.servers"
|
|
|
|
RETURN VALUES
|
|
FALSE Success
|
|
TRUE Error
|
|
*/
|
|
|
|
static bool servers_load(THD *thd, TABLE_LIST *tables)
|
|
{
|
|
TABLE *table;
|
|
READ_RECORD read_record_info;
|
|
bool return_val= TRUE;
|
|
DBUG_ENTER("servers_load");
|
|
|
|
/* first, send all cached rows to sleep with the fishes, oblivion!
|
|
I expect this crappy comment replaced */
|
|
free_root(&mem, MYF(MY_MARK_BLOCKS_FREE));
|
|
my_hash_reset(&servers_cache);
|
|
|
|
init_read_record(&read_record_info,thd,table=tables[0].table,NULL,1,0);
|
|
while (!(read_record_info.read_record(&read_record_info)))
|
|
{
|
|
/* return_val is already TRUE, so no need to set */
|
|
if ((get_server_from_table_to_cache(table)))
|
|
goto end;
|
|
}
|
|
|
|
return_val= FALSE;
|
|
|
|
end:
|
|
end_read_record(&read_record_info);
|
|
DBUG_RETURN(return_val);
|
|
}
|
|
|
|
|
|
/*
|
|
Forget current servers cache and read new servers
|
|
from the conneciton table.
|
|
|
|
SYNOPSIS
|
|
servers_reload()
|
|
thd Current thread
|
|
|
|
NOTE
|
|
All tables of calling thread which were open and locked by LOCK TABLES
|
|
statement will be unlocked and closed.
|
|
This function is also used for initialization of structures responsible
|
|
for user/db-level privilege checking.
|
|
|
|
RETURN VALUE
|
|
FALSE Success
|
|
TRUE Failure
|
|
*/
|
|
|
|
bool servers_reload(THD *thd)
|
|
{
|
|
TABLE_LIST tables[1];
|
|
bool return_val= TRUE;
|
|
DBUG_ENTER("servers_reload");
|
|
|
|
if (thd->locked_tables)
|
|
{ // Can't have locked tables here
|
|
thd->lock=thd->locked_tables;
|
|
thd->locked_tables=0;
|
|
close_thread_tables(thd);
|
|
}
|
|
|
|
DBUG_PRINT("info", ("locking servers_cache"));
|
|
rw_wrlock(&THR_LOCK_servers);
|
|
|
|
bzero((char*) tables, sizeof(tables));
|
|
tables[0].alias= tables[0].table_name= (char*) "servers";
|
|
tables[0].db= (char*) "mysql";
|
|
tables[0].lock_type= TL_READ;
|
|
|
|
if (simple_open_n_lock_tables(thd, tables))
|
|
{
|
|
sql_print_error("Can't open and lock privilege tables: %s",
|
|
thd->main_da.message());
|
|
goto end;
|
|
}
|
|
|
|
if ((return_val= servers_load(thd, tables)))
|
|
{ // Error. Revert to old list
|
|
/* blast, for now, we have no servers, discuss later way to preserve */
|
|
|
|
DBUG_PRINT("error",("Reverting to old privileges"));
|
|
servers_free();
|
|
}
|
|
|
|
end:
|
|
close_thread_tables(thd);
|
|
DBUG_PRINT("info", ("unlocking servers_cache"));
|
|
rw_unlock(&THR_LOCK_servers);
|
|
DBUG_RETURN(return_val);
|
|
}
|
|
|
|
|
|
/*
|
|
Initialize structures responsible for servers used in federated
|
|
server scheme information for them from the server
|
|
table in the 'mysql' database.
|
|
|
|
SYNOPSIS
|
|
get_server_from_table_to_cache()
|
|
TABLE *table open table pointer
|
|
|
|
|
|
NOTES
|
|
This function takes a TABLE pointer (pointing to an opened
|
|
table). With this open table, a FOREIGN_SERVER struct pointer
|
|
is allocated into root memory, then each member of the FOREIGN_SERVER
|
|
struct is populated. A char pointer takes the return value of get_field
|
|
for each column we're interested in obtaining, and if that pointer
|
|
isn't 0x0, the FOREIGN_SERVER member is set to that value, otherwise,
|
|
is set to the value of an empty string, since get_field would set it to
|
|
0x0 if the column's value is empty, even if the default value for that
|
|
column is NOT NULL.
|
|
|
|
RETURN VALUES
|
|
0 ok
|
|
1 could not insert server struct into global servers cache
|
|
*/
|
|
|
|
static bool
|
|
get_server_from_table_to_cache(TABLE *table)
|
|
{
|
|
/* alloc a server struct */
|
|
char *ptr;
|
|
char * const blank= (char*)"";
|
|
FOREIGN_SERVER *server= (FOREIGN_SERVER *)alloc_root(&mem,
|
|
sizeof(FOREIGN_SERVER));
|
|
DBUG_ENTER("get_server_from_table_to_cache");
|
|
table->use_all_columns();
|
|
|
|
/* get each field into the server struct ptr */
|
|
server->server_name= get_field(&mem, table->field[0]);
|
|
server->server_name_length= strlen(server->server_name);
|
|
ptr= get_field(&mem, table->field[1]);
|
|
server->host= ptr ? ptr : blank;
|
|
ptr= get_field(&mem, table->field[2]);
|
|
server->db= ptr ? ptr : blank;
|
|
ptr= get_field(&mem, table->field[3]);
|
|
server->username= ptr ? ptr : blank;
|
|
ptr= get_field(&mem, table->field[4]);
|
|
server->password= ptr ? ptr : blank;
|
|
ptr= get_field(&mem, table->field[5]);
|
|
server->sport= ptr ? ptr : blank;
|
|
|
|
server->port= server->sport ? atoi(server->sport) : 0;
|
|
|
|
ptr= get_field(&mem, table->field[6]);
|
|
server->socket= ptr && strlen(ptr) ? ptr : blank;
|
|
ptr= get_field(&mem, table->field[7]);
|
|
server->scheme= ptr ? ptr : blank;
|
|
ptr= get_field(&mem, table->field[8]);
|
|
server->owner= ptr ? ptr : blank;
|
|
DBUG_PRINT("info", ("server->server_name %s", server->server_name));
|
|
DBUG_PRINT("info", ("server->host %s", server->host));
|
|
DBUG_PRINT("info", ("server->db %s", server->db));
|
|
DBUG_PRINT("info", ("server->username %s", server->username));
|
|
DBUG_PRINT("info", ("server->password %s", server->password));
|
|
DBUG_PRINT("info", ("server->socket %s", server->socket));
|
|
if (my_hash_insert(&servers_cache, (uchar*) server))
|
|
{
|
|
DBUG_PRINT("info", ("had a problem inserting server %s at %lx",
|
|
server->server_name, (long unsigned int) server));
|
|
// error handling needed here
|
|
DBUG_RETURN(TRUE);
|
|
}
|
|
DBUG_RETURN(FALSE);
|
|
}
|
|
|
|
|
|
/*
|
|
SYNOPSIS
|
|
insert_server()
|
|
THD *thd - thread pointer
|
|
FOREIGN_SERVER *server - pointer to prepared FOREIGN_SERVER struct
|
|
|
|
NOTES
|
|
This function takes a server object that is has all members properly
|
|
prepared, ready to be inserted both into the mysql.servers table and
|
|
the servers cache.
|
|
|
|
THR_LOCK_servers must be write locked.
|
|
|
|
RETURN VALUES
|
|
0 - no error
|
|
other - error code
|
|
*/
|
|
|
|
static int
|
|
insert_server(THD *thd, FOREIGN_SERVER *server)
|
|
{
|
|
int error= -1;
|
|
TABLE_LIST tables;
|
|
TABLE *table;
|
|
|
|
DBUG_ENTER("insert_server");
|
|
|
|
bzero((char*) &tables, sizeof(tables));
|
|
tables.db= (char*) "mysql";
|
|
tables.alias= tables.table_name= (char*) "servers";
|
|
|
|
/* need to open before acquiring THR_LOCK_plugin or it will deadlock */
|
|
if (! (table= open_ltable(thd, &tables, TL_WRITE, 0)))
|
|
goto end;
|
|
|
|
/* insert the server into the table */
|
|
if ((error= insert_server_record(table, server)))
|
|
goto end;
|
|
|
|
/* insert the server into the cache */
|
|
if ((error= insert_server_record_into_cache(server)))
|
|
goto end;
|
|
|
|
end:
|
|
DBUG_RETURN(error);
|
|
}
|
|
|
|
|
|
/*
|
|
SYNOPSIS
|
|
int insert_server_record_into_cache()
|
|
FOREIGN_SERVER *server
|
|
|
|
NOTES
|
|
This function takes a FOREIGN_SERVER pointer to an allocated (root mem)
|
|
and inserts it into the global servers cache
|
|
|
|
THR_LOCK_servers must be write locked.
|
|
|
|
RETURN VALUE
|
|
0 - no error
|
|
>0 - error code
|
|
|
|
*/
|
|
|
|
static int
|
|
insert_server_record_into_cache(FOREIGN_SERVER *server)
|
|
{
|
|
int error=0;
|
|
DBUG_ENTER("insert_server_record_into_cache");
|
|
/*
|
|
We succeded in insertion of the server to the table, now insert
|
|
the server to the cache
|
|
*/
|
|
DBUG_PRINT("info", ("inserting server %s at %lx, length %d",
|
|
server->server_name, (long unsigned int) server,
|
|
server->server_name_length));
|
|
if (my_hash_insert(&servers_cache, (uchar*) server))
|
|
{
|
|
DBUG_PRINT("info", ("had a problem inserting server %s at %lx",
|
|
server->server_name, (long unsigned int) server));
|
|
// error handling needed here
|
|
error= 1;
|
|
}
|
|
DBUG_RETURN(error);
|
|
}
|
|
|
|
|
|
/*
|
|
SYNOPSIS
|
|
store_server_fields()
|
|
TABLE *table
|
|
FOREIGN_SERVER *server
|
|
|
|
NOTES
|
|
This function takes an opened table object, and a pointer to an
|
|
allocated FOREIGN_SERVER struct, and then stores each member of
|
|
the FOREIGN_SERVER to the appropriate fields in the table, in
|
|
advance of insertion into the mysql.servers table
|
|
|
|
RETURN VALUE
|
|
VOID
|
|
|
|
*/
|
|
|
|
static void
|
|
store_server_fields(TABLE *table, FOREIGN_SERVER *server)
|
|
{
|
|
|
|
table->use_all_columns();
|
|
/*
|
|
"server" has already been prepped by prepare_server_struct_for_<>
|
|
so, all we need to do is check if the value is set (> -1 for port)
|
|
|
|
If this happens to be an update, only the server members that
|
|
have changed will be set. If an insert, then all will be set,
|
|
even if with empty strings
|
|
*/
|
|
if (server->host)
|
|
table->field[1]->store(server->host,
|
|
(uint) strlen(server->host), system_charset_info);
|
|
if (server->db)
|
|
table->field[2]->store(server->db,
|
|
(uint) strlen(server->db), system_charset_info);
|
|
if (server->username)
|
|
table->field[3]->store(server->username,
|
|
(uint) strlen(server->username), system_charset_info);
|
|
if (server->password)
|
|
table->field[4]->store(server->password,
|
|
(uint) strlen(server->password), system_charset_info);
|
|
if (server->port > -1)
|
|
table->field[5]->store(server->port);
|
|
|
|
if (server->socket)
|
|
table->field[6]->store(server->socket,
|
|
(uint) strlen(server->socket), system_charset_info);
|
|
if (server->scheme)
|
|
table->field[7]->store(server->scheme,
|
|
(uint) strlen(server->scheme), system_charset_info);
|
|
if (server->owner)
|
|
table->field[8]->store(server->owner,
|
|
(uint) strlen(server->owner), system_charset_info);
|
|
}
|
|
|
|
/*
|
|
SYNOPSIS
|
|
insert_server_record()
|
|
TABLE *table
|
|
FOREIGN_SERVER *server
|
|
|
|
NOTES
|
|
This function takes the arguments of an open table object and a pointer
|
|
to an allocated FOREIGN_SERVER struct. It stores the server_name into
|
|
the first field of the table (the primary key, server_name column). With
|
|
this, index_read_idx is called, if the record is found, an error is set
|
|
to ER_FOREIGN_SERVER_EXISTS (the server with that server name exists in the
|
|
table), if not, then store_server_fields stores all fields of the
|
|
FOREIGN_SERVER to the table, then ha_write_row is inserted. If an error
|
|
is encountered in either index_read_idx or ha_write_row, then that error
|
|
is returned
|
|
|
|
RETURN VALUE
|
|
0 - no errors
|
|
>0 - error code
|
|
|
|
*/
|
|
|
|
static
|
|
int insert_server_record(TABLE *table, FOREIGN_SERVER *server)
|
|
{
|
|
int error;
|
|
DBUG_ENTER("insert_server_record");
|
|
table->use_all_columns();
|
|
|
|
empty_record(table);
|
|
|
|
/* set the field that's the PK to the value we're looking for */
|
|
table->field[0]->store(server->server_name,
|
|
server->server_name_length,
|
|
system_charset_info);
|
|
|
|
/* read index until record is that specified in server_name */
|
|
if ((error= table->file->index_read_idx_map(table->record[0], 0,
|
|
(uchar *)table->field[0]->ptr,
|
|
HA_WHOLE_KEY,
|
|
HA_READ_KEY_EXACT)))
|
|
{
|
|
/* if not found, err */
|
|
if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
|
|
{
|
|
table->file->print_error(error, MYF(0));
|
|
error= 1;
|
|
}
|
|
/* store each field to be inserted */
|
|
store_server_fields(table, server);
|
|
|
|
DBUG_PRINT("info",("record for server '%s' not found!",
|
|
server->server_name));
|
|
/* write/insert the new server */
|
|
if ((error=table->file->ha_write_row(table->record[0])))
|
|
{
|
|
table->file->print_error(error, MYF(0));
|
|
}
|
|
else
|
|
error= 0;
|
|
}
|
|
else
|
|
error= ER_FOREIGN_SERVER_EXISTS;
|
|
DBUG_RETURN(error);
|
|
}
|
|
|
|
/*
|
|
SYNOPSIS
|
|
drop_server()
|
|
THD *thd
|
|
LEX_SERVER_OPTIONS *server_options
|
|
|
|
NOTES
|
|
This function takes as its arguments a THD object pointer and a pointer
|
|
to a LEX_SERVER_OPTIONS struct from the parser. The member 'server_name'
|
|
of this LEX_SERVER_OPTIONS struct contains the value of the server to be
|
|
deleted. The mysql.servers table is opened via open_ltable, a table object
|
|
returned, the servers cache mutex locked, then delete_server_record is
|
|
called with this table object and LEX_SERVER_OPTIONS server_name and
|
|
server_name_length passed, containing the name of the server to be
|
|
dropped/deleted, then delete_server_record_in_cache is called to delete
|
|
the server from the servers cache.
|
|
|
|
RETURN VALUE
|
|
0 - no error
|
|
> 0 - error code
|
|
*/
|
|
|
|
int drop_server(THD *thd, LEX_SERVER_OPTIONS *server_options)
|
|
{
|
|
int error;
|
|
TABLE_LIST tables;
|
|
TABLE *table;
|
|
LEX_STRING name= { server_options->server_name,
|
|
server_options->server_name_length };
|
|
|
|
DBUG_ENTER("drop_server");
|
|
DBUG_PRINT("info", ("server name server->server_name %s",
|
|
server_options->server_name));
|
|
|
|
bzero((char*) &tables, sizeof(tables));
|
|
tables.db= (char*) "mysql";
|
|
tables.alias= tables.table_name= (char*) "servers";
|
|
|
|
rw_wrlock(&THR_LOCK_servers);
|
|
|
|
/* hit the memory hit first */
|
|
if ((error= delete_server_record_in_cache(server_options)))
|
|
goto end;
|
|
|
|
if (! (table= open_ltable(thd, &tables, TL_WRITE, 0)))
|
|
{
|
|
error= my_errno;
|
|
goto end;
|
|
}
|
|
|
|
error= delete_server_record(table, name.str, name.length);
|
|
|
|
/* close the servers table before we call closed_cached_connection_tables */
|
|
close_thread_tables(thd);
|
|
|
|
if (close_cached_connection_tables(thd, TRUE, &name))
|
|
{
|
|
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
ER_UNKNOWN_ERROR, "Server connection in use");
|
|
}
|
|
|
|
end:
|
|
rw_unlock(&THR_LOCK_servers);
|
|
DBUG_RETURN(error);
|
|
}
|
|
|
|
|
|
/*
|
|
|
|
SYNOPSIS
|
|
delete_server_record_in_cache()
|
|
LEX_SERVER_OPTIONS *server_options
|
|
|
|
NOTES
|
|
This function's argument is a LEX_SERVER_OPTIONS struct pointer. This
|
|
function uses the "server_name" and "server_name_length" members of the
|
|
lex->server_options to search for the server in the servers_cache. Upon
|
|
returned the server (pointer to a FOREIGN_SERVER struct), it then deletes
|
|
that server from the servers_cache hash.
|
|
|
|
RETURN VALUE
|
|
0 - no error
|
|
|
|
*/
|
|
|
|
static int
|
|
delete_server_record_in_cache(LEX_SERVER_OPTIONS *server_options)
|
|
{
|
|
int error= ER_FOREIGN_SERVER_DOESNT_EXIST;
|
|
FOREIGN_SERVER *server;
|
|
DBUG_ENTER("delete_server_record_in_cache");
|
|
|
|
DBUG_PRINT("info",("trying to obtain server name %s length %d",
|
|
server_options->server_name,
|
|
server_options->server_name_length));
|
|
|
|
|
|
if (!(server= (FOREIGN_SERVER *) hash_search(&servers_cache,
|
|
(uchar*) server_options->server_name,
|
|
server_options->server_name_length)))
|
|
{
|
|
DBUG_PRINT("info", ("server_name %s length %d not found!",
|
|
server_options->server_name,
|
|
server_options->server_name_length));
|
|
goto end;
|
|
}
|
|
/*
|
|
We succeded in deletion of the server to the table, now delete
|
|
the server from the cache
|
|
*/
|
|
DBUG_PRINT("info",("deleting server %s length %d",
|
|
server->server_name,
|
|
server->server_name_length));
|
|
|
|
VOID(hash_delete(&servers_cache, (uchar*) server));
|
|
|
|
error= 0;
|
|
|
|
end:
|
|
DBUG_RETURN(error);
|
|
}
|
|
|
|
|
|
/*
|
|
|
|
SYNOPSIS
|
|
update_server()
|
|
THD *thd
|
|
FOREIGN_SERVER *existing
|
|
FOREIGN_SERVER *altered
|
|
|
|
NOTES
|
|
This function takes as arguments a THD object pointer, and two pointers,
|
|
one pointing to the existing FOREIGN_SERVER struct "existing" (which is
|
|
the current record as it is) and another pointer pointing to the
|
|
FOREIGN_SERVER struct with the members containing the modified/altered
|
|
values that need to be updated in both the mysql.servers table and the
|
|
servers_cache. It opens a table, passes the table and the altered
|
|
FOREIGN_SERVER pointer, which will be used to update the mysql.servers
|
|
table for the particular server via the call to update_server_record,
|
|
and in the servers_cache via update_server_record_in_cache.
|
|
|
|
THR_LOCK_servers must be write locked.
|
|
|
|
RETURN VALUE
|
|
0 - no error
|
|
>0 - error code
|
|
|
|
*/
|
|
|
|
int update_server(THD *thd, FOREIGN_SERVER *existing, FOREIGN_SERVER *altered)
|
|
{
|
|
int error;
|
|
TABLE *table;
|
|
TABLE_LIST tables;
|
|
DBUG_ENTER("update_server");
|
|
|
|
bzero((char*) &tables, sizeof(tables));
|
|
tables.db= (char*)"mysql";
|
|
tables.alias= tables.table_name= (char*)"servers";
|
|
|
|
if (!(table= open_ltable(thd, &tables, TL_WRITE, 0)))
|
|
{
|
|
error= my_errno;
|
|
goto end;
|
|
}
|
|
|
|
if ((error= update_server_record(table, altered)))
|
|
goto end;
|
|
|
|
error= update_server_record_in_cache(existing, altered);
|
|
|
|
/*
|
|
Perform a reload so we don't have a 'hole' in our mem_root
|
|
*/
|
|
servers_load(thd, &tables);
|
|
|
|
end:
|
|
DBUG_RETURN(error);
|
|
}
|
|
|
|
|
|
/*
|
|
|
|
SYNOPSIS
|
|
update_server_record_in_cache()
|
|
FOREIGN_SERVER *existing
|
|
FOREIGN_SERVER *altered
|
|
|
|
NOTES
|
|
This function takes as an argument the FOREIGN_SERVER structi pointer
|
|
for the existing server and the FOREIGN_SERVER struct populated with only
|
|
the members which have been updated. It then "merges" the "altered" struct
|
|
members to the existing server, the existing server then represents an
|
|
updated server. Then, the existing record is deleted from the servers_cache
|
|
HASH, then the updated record inserted, in essence replacing the old
|
|
record.
|
|
|
|
THR_LOCK_servers must be write locked.
|
|
|
|
RETURN VALUE
|
|
0 - no error
|
|
1 - error
|
|
|
|
*/
|
|
|
|
int update_server_record_in_cache(FOREIGN_SERVER *existing,
|
|
FOREIGN_SERVER *altered)
|
|
{
|
|
int error= 0;
|
|
DBUG_ENTER("update_server_record_in_cache");
|
|
|
|
/*
|
|
update the members that haven't been change in the altered server struct
|
|
with the values of the existing server struct
|
|
*/
|
|
merge_server_struct(existing, altered);
|
|
|
|
/*
|
|
delete the existing server struct from the server cache
|
|
*/
|
|
VOID(hash_delete(&servers_cache, (uchar*)existing));
|
|
|
|
/*
|
|
Insert the altered server struct into the server cache
|
|
*/
|
|
if (my_hash_insert(&servers_cache, (uchar*)altered))
|
|
{
|
|
DBUG_PRINT("info", ("had a problem inserting server %s at %lx",
|
|
altered->server_name, (long unsigned int) altered));
|
|
error= ER_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
DBUG_RETURN(error);
|
|
}
|
|
|
|
|
|
/*
|
|
|
|
SYNOPSIS
|
|
merge_server_struct()
|
|
FOREIGN_SERVER *from
|
|
FOREIGN_SERVER *to
|
|
|
|
NOTES
|
|
This function takes as its arguments two pointers each to an allocated
|
|
FOREIGN_SERVER struct. The first FOREIGN_SERVER struct represents the struct
|
|
that we will obtain values from (hence the name "from"), the second
|
|
FOREIGN_SERVER struct represents which FOREIGN_SERVER struct we will be
|
|
"copying" any members that have a value to (hence the name "to")
|
|
|
|
RETURN VALUE
|
|
VOID
|
|
|
|
*/
|
|
|
|
void merge_server_struct(FOREIGN_SERVER *from, FOREIGN_SERVER *to)
|
|
{
|
|
DBUG_ENTER("merge_server_struct");
|
|
if (!to->host)
|
|
to->host= strdup_root(&mem, from->host);
|
|
if (!to->db)
|
|
to->db= strdup_root(&mem, from->db);
|
|
if (!to->username)
|
|
to->username= strdup_root(&mem, from->username);
|
|
if (!to->password)
|
|
to->password= strdup_root(&mem, from->password);
|
|
if (to->port == -1)
|
|
to->port= from->port;
|
|
if (!to->socket && from->socket)
|
|
to->socket= strdup_root(&mem, from->socket);
|
|
if (!to->scheme && from->scheme)
|
|
to->scheme= strdup_root(&mem, from->scheme);
|
|
if (!to->owner)
|
|
to->owner= strdup_root(&mem, from->owner);
|
|
|
|
DBUG_VOID_RETURN;
|
|
}
|
|
|
|
|
|
/*
|
|
|
|
SYNOPSIS
|
|
update_server_record()
|
|
TABLE *table
|
|
FOREIGN_SERVER *server
|
|
|
|
NOTES
|
|
This function takes as its arguments an open TABLE pointer, and a pointer
|
|
to an allocated FOREIGN_SERVER structure representing an updated record
|
|
which needs to be inserted. The primary key, server_name is stored to field
|
|
0, then index_read_idx is called to read the index to that record, the
|
|
record then being ready to be updated, if found. If not found an error is
|
|
set and error message printed. If the record is found, store_record is
|
|
called, then store_server_fields stores each field from the the members of
|
|
the updated FOREIGN_SERVER struct.
|
|
|
|
RETURN VALUE
|
|
0 - no error
|
|
|
|
*/
|
|
|
|
|
|
static int
|
|
update_server_record(TABLE *table, FOREIGN_SERVER *server)
|
|
{
|
|
int error=0;
|
|
DBUG_ENTER("update_server_record");
|
|
table->use_all_columns();
|
|
/* set the field that's the PK to the value we're looking for */
|
|
table->field[0]->store(server->server_name,
|
|
server->server_name_length,
|
|
system_charset_info);
|
|
|
|
if ((error= table->file->index_read_idx_map(table->record[0], 0,
|
|
(uchar *)table->field[0]->ptr,
|
|
~(longlong)0,
|
|
HA_READ_KEY_EXACT)))
|
|
{
|
|
if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
|
|
table->file->print_error(error, MYF(0));
|
|
DBUG_PRINT("info",("server not found!"));
|
|
error= ER_FOREIGN_SERVER_DOESNT_EXIST;
|
|
}
|
|
else
|
|
{
|
|
/* ok, so we can update since the record exists in the table */
|
|
store_record(table,record[1]);
|
|
store_server_fields(table, server);
|
|
if ((error=table->file->ha_update_row(table->record[1],
|
|
table->record[0])) &&
|
|
error != HA_ERR_RECORD_IS_THE_SAME)
|
|
{
|
|
DBUG_PRINT("info",("problems with ha_update_row %d", error));
|
|
goto end;
|
|
}
|
|
else
|
|
error= 0;
|
|
}
|
|
|
|
end:
|
|
DBUG_RETURN(error);
|
|
}
|
|
|
|
|
|
/*
|
|
|
|
SYNOPSIS
|
|
delete_server_record()
|
|
TABLE *table
|
|
char *server_name
|
|
int server_name_length
|
|
|
|
NOTES
|
|
|
|
RETURN VALUE
|
|
0 - no error
|
|
|
|
*/
|
|
|
|
static int
|
|
delete_server_record(TABLE *table,
|
|
char *server_name, int server_name_length)
|
|
{
|
|
int error;
|
|
DBUG_ENTER("delete_server_record");
|
|
table->use_all_columns();
|
|
|
|
/* set the field that's the PK to the value we're looking for */
|
|
table->field[0]->store(server_name, server_name_length, system_charset_info);
|
|
|
|
if ((error= table->file->index_read_idx_map(table->record[0], 0,
|
|
(uchar *)table->field[0]->ptr,
|
|
HA_WHOLE_KEY,
|
|
HA_READ_KEY_EXACT)))
|
|
{
|
|
if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
|
|
table->file->print_error(error, MYF(0));
|
|
DBUG_PRINT("info",("server not found!"));
|
|
error= ER_FOREIGN_SERVER_DOESNT_EXIST;
|
|
}
|
|
else
|
|
{
|
|
if ((error= table->file->ha_delete_row(table->record[0])))
|
|
table->file->print_error(error, MYF(0));
|
|
}
|
|
|
|
DBUG_RETURN(error);
|
|
}
|
|
|
|
/*
|
|
|
|
SYNOPSIS
|
|
create_server()
|
|
THD *thd
|
|
LEX_SERVER_OPTIONS *server_options
|
|
|
|
NOTES
|
|
|
|
RETURN VALUE
|
|
0 - no error
|
|
|
|
*/
|
|
|
|
int create_server(THD *thd, LEX_SERVER_OPTIONS *server_options)
|
|
{
|
|
int error= ER_FOREIGN_SERVER_EXISTS;
|
|
FOREIGN_SERVER *server;
|
|
|
|
DBUG_ENTER("create_server");
|
|
DBUG_PRINT("info", ("server_options->server_name %s",
|
|
server_options->server_name));
|
|
|
|
rw_wrlock(&THR_LOCK_servers);
|
|
|
|
/* hit the memory first */
|
|
if (hash_search(&servers_cache, (uchar*) server_options->server_name,
|
|
server_options->server_name_length))
|
|
goto end;
|
|
|
|
server= (FOREIGN_SERVER *)alloc_root(&mem,
|
|
sizeof(FOREIGN_SERVER));
|
|
|
|
prepare_server_struct_for_insert(server_options, server);
|
|
|
|
error= insert_server(thd, server);
|
|
|
|
DBUG_PRINT("info", ("error returned %d", error));
|
|
|
|
end:
|
|
rw_unlock(&THR_LOCK_servers);
|
|
DBUG_RETURN(error);
|
|
}
|
|
|
|
|
|
/*
|
|
|
|
SYNOPSIS
|
|
alter_server()
|
|
THD *thd
|
|
LEX_SERVER_OPTIONS *server_options
|
|
|
|
NOTES
|
|
|
|
RETURN VALUE
|
|
0 - no error
|
|
|
|
*/
|
|
|
|
int alter_server(THD *thd, LEX_SERVER_OPTIONS *server_options)
|
|
{
|
|
int error= ER_FOREIGN_SERVER_DOESNT_EXIST;
|
|
FOREIGN_SERVER *altered, *existing;
|
|
LEX_STRING name= { server_options->server_name,
|
|
server_options->server_name_length };
|
|
DBUG_ENTER("alter_server");
|
|
DBUG_PRINT("info", ("server_options->server_name %s",
|
|
server_options->server_name));
|
|
|
|
rw_wrlock(&THR_LOCK_servers);
|
|
|
|
if (!(existing= (FOREIGN_SERVER *) hash_search(&servers_cache,
|
|
(uchar*) name.str,
|
|
name.length)))
|
|
goto end;
|
|
|
|
altered= (FOREIGN_SERVER *)alloc_root(&mem,
|
|
sizeof(FOREIGN_SERVER));
|
|
|
|
prepare_server_struct_for_update(server_options, existing, altered);
|
|
|
|
error= update_server(thd, existing, altered);
|
|
|
|
/* close the servers table before we call closed_cached_connection_tables */
|
|
close_thread_tables(thd);
|
|
|
|
if (close_cached_connection_tables(thd, FALSE, &name))
|
|
{
|
|
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
|
|
ER_UNKNOWN_ERROR, "Server connection in use");
|
|
}
|
|
|
|
end:
|
|
DBUG_PRINT("info", ("error returned %d", error));
|
|
rw_unlock(&THR_LOCK_servers);
|
|
DBUG_RETURN(error);
|
|
}
|
|
|
|
|
|
/*
|
|
|
|
SYNOPSIS
|
|
prepare_server_struct_for_insert()
|
|
LEX_SERVER_OPTIONS *server_options
|
|
FOREIGN_SERVER *server
|
|
|
|
NOTES
|
|
|
|
RETURN VALUE
|
|
none
|
|
|
|
*/
|
|
|
|
static void
|
|
prepare_server_struct_for_insert(LEX_SERVER_OPTIONS *server_options,
|
|
FOREIGN_SERVER *server)
|
|
{
|
|
char *unset_ptr= (char*)"";
|
|
DBUG_ENTER("prepare_server_struct");
|
|
|
|
/* these two MUST be set */
|
|
server->server_name= strdup_root(&mem, server_options->server_name);
|
|
server->server_name_length= server_options->server_name_length;
|
|
|
|
server->host= server_options->host ?
|
|
strdup_root(&mem, server_options->host) : unset_ptr;
|
|
|
|
server->db= server_options->db ?
|
|
strdup_root(&mem, server_options->db) : unset_ptr;
|
|
|
|
server->username= server_options->username ?
|
|
strdup_root(&mem, server_options->username) : unset_ptr;
|
|
|
|
server->password= server_options->password ?
|
|
strdup_root(&mem, server_options->password) : unset_ptr;
|
|
|
|
/* set to 0 if not specified */
|
|
server->port= server_options->port > -1 ?
|
|
server_options->port : 0;
|
|
|
|
server->socket= server_options->socket ?
|
|
strdup_root(&mem, server_options->socket) : unset_ptr;
|
|
|
|
server->scheme= server_options->scheme ?
|
|
strdup_root(&mem, server_options->scheme) : unset_ptr;
|
|
|
|
server->owner= server_options->owner ?
|
|
strdup_root(&mem, server_options->owner) : unset_ptr;
|
|
|
|
DBUG_VOID_RETURN;
|
|
}
|
|
|
|
/*
|
|
|
|
SYNOPSIS
|
|
prepare_server_struct_for_update()
|
|
LEX_SERVER_OPTIONS *server_options
|
|
|
|
NOTES
|
|
|
|
RETURN VALUE
|
|
0 - no error
|
|
|
|
*/
|
|
|
|
static void
|
|
prepare_server_struct_for_update(LEX_SERVER_OPTIONS *server_options,
|
|
FOREIGN_SERVER *existing,
|
|
FOREIGN_SERVER *altered)
|
|
{
|
|
DBUG_ENTER("prepare_server_struct_for_update");
|
|
|
|
altered->server_name= strdup_root(&mem, server_options->server_name);
|
|
altered->server_name_length= server_options->server_name_length;
|
|
DBUG_PRINT("info", ("existing name %s altered name %s",
|
|
existing->server_name, altered->server_name));
|
|
|
|
/*
|
|
The logic here is this: is this value set AND is it different
|
|
than the existing value?
|
|
*/
|
|
altered->host=
|
|
(server_options->host && (strcmp(server_options->host, existing->host))) ?
|
|
strdup_root(&mem, server_options->host) : 0;
|
|
|
|
altered->db=
|
|
(server_options->db && (strcmp(server_options->db, existing->db))) ?
|
|
strdup_root(&mem, server_options->db) : 0;
|
|
|
|
altered->username=
|
|
(server_options->username &&
|
|
(strcmp(server_options->username, existing->username))) ?
|
|
strdup_root(&mem, server_options->username) : 0;
|
|
|
|
altered->password=
|
|
(server_options->password &&
|
|
(strcmp(server_options->password, existing->password))) ?
|
|
strdup_root(&mem, server_options->password) : 0;
|
|
|
|
/*
|
|
port is initialised to -1, so if unset, it will be -1
|
|
*/
|
|
altered->port= (server_options->port > -1 &&
|
|
server_options->port != existing->port) ?
|
|
server_options->port : -1;
|
|
|
|
altered->socket=
|
|
(server_options->socket &&
|
|
(strcmp(server_options->socket, existing->socket))) ?
|
|
strdup_root(&mem, server_options->socket) : 0;
|
|
|
|
altered->scheme=
|
|
(server_options->scheme &&
|
|
(strcmp(server_options->scheme, existing->scheme))) ?
|
|
strdup_root(&mem, server_options->scheme) : 0;
|
|
|
|
altered->owner=
|
|
(server_options->owner &&
|
|
(strcmp(server_options->owner, existing->owner))) ?
|
|
strdup_root(&mem, server_options->owner) : 0;
|
|
|
|
DBUG_VOID_RETURN;
|
|
}
|
|
|
|
/*
|
|
|
|
SYNOPSIS
|
|
servers_free()
|
|
bool end
|
|
|
|
NOTES
|
|
|
|
RETURN VALUE
|
|
void
|
|
|
|
*/
|
|
|
|
void servers_free(bool end)
|
|
{
|
|
DBUG_ENTER("servers_free");
|
|
if (!hash_inited(&servers_cache))
|
|
DBUG_VOID_RETURN;
|
|
if (!end)
|
|
{
|
|
free_root(&mem, MYF(MY_MARK_BLOCKS_FREE));
|
|
my_hash_reset(&servers_cache);
|
|
DBUG_VOID_RETURN;
|
|
}
|
|
rwlock_destroy(&THR_LOCK_servers);
|
|
free_root(&mem,MYF(0));
|
|
hash_free(&servers_cache);
|
|
DBUG_VOID_RETURN;
|
|
}
|
|
|
|
|
|
/*
|
|
SYNOPSIS
|
|
|
|
clone_server(MEM_ROOT *mem_root, FOREIGN_SERVER *orig, FOREIGN_SERVER *buff)
|
|
|
|
Create a clone of FOREIGN_SERVER. If the supplied mem_root is of
|
|
thd->mem_root then the copy is automatically disposed at end of statement.
|
|
|
|
NOTES
|
|
|
|
ARGS
|
|
MEM_ROOT pointer (strings are copied into this mem root)
|
|
FOREIGN_SERVER pointer (made a copy of)
|
|
FOREIGN_SERVER buffer (if not-NULL, this pointer is returned)
|
|
|
|
RETURN VALUE
|
|
FOREIGN_SEVER pointer (copy of one supplied FOREIGN_SERVER)
|
|
*/
|
|
|
|
static FOREIGN_SERVER *clone_server(MEM_ROOT *mem, const FOREIGN_SERVER *server,
|
|
FOREIGN_SERVER *buffer)
|
|
{
|
|
DBUG_ENTER("sql_server.cc:clone_server");
|
|
|
|
if (!buffer)
|
|
buffer= (FOREIGN_SERVER *) alloc_root(mem, sizeof(FOREIGN_SERVER));
|
|
|
|
buffer->server_name= strmake_root(mem, server->server_name,
|
|
server->server_name_length);
|
|
buffer->port= server->port;
|
|
buffer->server_name_length= server->server_name_length;
|
|
|
|
/* TODO: We need to examine which of these can really be NULL */
|
|
buffer->db= server->db ? strdup_root(mem, server->db) : NULL;
|
|
buffer->scheme= server->scheme ? strdup_root(mem, server->scheme) : NULL;
|
|
buffer->username= server->username? strdup_root(mem, server->username): NULL;
|
|
buffer->password= server->password? strdup_root(mem, server->password): NULL;
|
|
buffer->socket= server->socket ? strdup_root(mem, server->socket) : NULL;
|
|
buffer->owner= server->owner ? strdup_root(mem, server->owner) : NULL;
|
|
buffer->host= server->host ? strdup_root(mem, server->host) : NULL;
|
|
|
|
DBUG_RETURN(buffer);
|
|
}
|
|
|
|
|
|
/*
|
|
|
|
SYNOPSIS
|
|
get_server_by_name()
|
|
const char *server_name
|
|
|
|
NOTES
|
|
|
|
RETURN VALUE
|
|
FOREIGN_SERVER *
|
|
|
|
*/
|
|
|
|
FOREIGN_SERVER *get_server_by_name(MEM_ROOT *mem, const char *server_name,
|
|
FOREIGN_SERVER *buff)
|
|
{
|
|
uint server_name_length;
|
|
FOREIGN_SERVER *server;
|
|
DBUG_ENTER("get_server_by_name");
|
|
DBUG_PRINT("info", ("server_name %s", server_name));
|
|
|
|
server_name_length= strlen(server_name);
|
|
|
|
if (! server_name || !strlen(server_name))
|
|
{
|
|
DBUG_PRINT("info", ("server_name not defined!"));
|
|
DBUG_RETURN((FOREIGN_SERVER *)NULL);
|
|
}
|
|
|
|
DBUG_PRINT("info", ("locking servers_cache"));
|
|
rw_rdlock(&THR_LOCK_servers);
|
|
if (!(server= (FOREIGN_SERVER *) hash_search(&servers_cache,
|
|
(uchar*) server_name,
|
|
server_name_length)))
|
|
{
|
|
DBUG_PRINT("info", ("server_name %s length %d not found!",
|
|
server_name, server_name_length));
|
|
server= (FOREIGN_SERVER *) NULL;
|
|
}
|
|
/* otherwise, make copy of server */
|
|
else
|
|
server= clone_server(mem, server, buff);
|
|
|
|
DBUG_PRINT("info", ("unlocking servers_cache"));
|
|
rw_unlock(&THR_LOCK_servers);
|
|
DBUG_RETURN(server);
|
|
|
|
}
|