Commit 8c2bb705 authored by Alexey Botchkov's avatar Alexey Botchkov

MDEV-495 backport --ignore-db-dir.

The feature was backported from MySQL 5.6.
Some code was added to make commands as
        SELECT * FROM ignored_db.t1;
        CALL ignored_db.proc();
        USE ignored_db;
to take that option into account.

per-file comments:
  mysql-test/r/ignore_db_dirs_basic.result
        test result added.
  mysql-test/t/ignore_db_dirs_basic-master.opt
        options for the test,
        actually the set of --ignore-db-dir lines.
  mysql-test/t/ignore_db_dirs_basic.test
        test for the feature.
        Same test from 5.6 was taken as a basis,
        then tests for SELECT, CALL etc were added.

per-file comments:
  sql/mysql_priv.h
MDEV-495 backport --ignore-db-dir.
        interface for db_name_is_in_ignore_list() added.
  sql/mysqld.cc
MDEV-495 backport --ignore-db-dir.
        --ignore-db-dir handling.
  sql/set_var.cc
MDEV-495 backport --ignore-db-dir.
        the @@ignore_db_dirs variable added.
  sql/sql_show.cc
MDEV-495 backport --ignore-db-dir.
        check if the directory is ignored.
  sql/sql_show.h
MDEV-495 backport --ignore-db-dir.
        interface added for opt_ignored_db_dirs.
  sql/table.cc
MDEV-495 backport --ignore-db-dir.
        check if the directory is ignored.
parent 2f5f360f
select @@ignore_db_dirs;
@@ignore_db_dirs
e,lost+found,.mysqlgui,ignored_db
# Check that SHOW DATABASES ignores all directories from
# @@ignore_db_dirs and all directories with names starting
# with '.'
SHOW DATABASES;
Database
information_schema
#mysql50#.otherdir
mtr
mysql
test
USE ignored_db;
ERROR 42000: Incorrect database name 'ignored_db'
SELECT * FROM ignored_db.t1;
ERROR 42000: Incorrect database name 'ignored_db'
CALL ignored_db.p1();
ERROR 42000: Incorrect database name 'ignored_db'
SELECT COUNT(*) FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME='ignored_db';
COUNT(*)
1
CREATE DATABASE ignored_db;
ERROR 42000: Incorrect database name 'ignored_db'
CREATE DATABASE `lost+found`;
USE `lost+found`;
CREATE TABLE t1(id INT);
INSERT INTO t1 VALUES (1), (2);
SELECT * FROM `lost+found`.t1;
id
1
2
SHOW DATABASES;
Database
information_schema
#mysql50#.otherdir
lost+found
mtr
mysql
test
DROP DATABASE `lost+found`;
SET @@global.ignore_db_dirs = 'aha';
ERROR HY000: Variable 'ignore_db_dirs' is a read only variable
SET @@local.ignore_db_dirs = 'aha';
ERROR HY000: Variable 'ignore_db_dirs' is a read only variable
SET @@ignore_db_dirs = 'aha';
ERROR HY000: Variable 'ignore_db_dirs' is a read only variable
--ignore-db-dir=a
--ignore-db-dir=b
--ignore-db-dir=c
--ignore-db-dir=
--ignore-db-dir=d
--ignore-db-dir x
--ignore-db-dir=
--ignore-db-dir=e
--ignore-db-dir=lost+found
--ignore-db-dir=.mysqlgui
--ignore-db-dir=ignored_db
select @@ignore_db_dirs;
let $MYSQLD_DATADIR= `select @@datadir`;
mkdir $MYSQLD_DATADIR/.mysqlgui;
mkdir $MYSQLD_DATADIR/.otherdir;
mkdir $MYSQLD_DATADIR/lost+found;
mkdir $MYSQLD_DATADIR/ignored_db;
--echo # Check that SHOW DATABASES ignores all directories from
--echo # @@ignore_db_dirs and all directories with names starting
--echo # with '.'
SHOW DATABASES;
--error ER_WRONG_DB_NAME
USE ignored_db;
--error ER_WRONG_DB_NAME
SELECT * FROM ignored_db.t1;
--error ER_WRONG_DB_NAME
CALL ignored_db.p1();
SELECT COUNT(*) FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME='ignored_db';
--error ER_WRONG_DB_NAME
CREATE DATABASE ignored_db;
CREATE DATABASE `lost+found`;
USE `lost+found`;
CREATE TABLE t1(id INT);
INSERT INTO t1 VALUES (1), (2);
SELECT * FROM `lost+found`.t1;
SHOW DATABASES;
DROP DATABASE `lost+found`;
rmdir $MYSQLD_DATADIR/.mysqlgui;
rmdir $MYSQLD_DATADIR/.otherdir;
rmdir $MYSQLD_DATADIR/lost+found;
rmdir $MYSQLD_DATADIR/ignored_db;
--error ER_INCORRECT_GLOBAL_LOCAL_VAR
SET @@global.ignore_db_dirs = 'aha';
--error ER_INCORRECT_GLOBAL_LOCAL_VAR
SET @@local.ignore_db_dirs = 'aha';
--error ER_INCORRECT_GLOBAL_LOCAL_VAR
SET @@ignore_db_dirs = 'aha';
......@@ -2834,6 +2834,8 @@ bool load_collation(MEM_ROOT *mem_root,
CHARSET_INFO *dflt_cl,
CHARSET_INFO **cl);
bool db_name_is_in_ignore_db_dirs_list(const char *dbase);
#endif /* MYSQL_SERVER */
extern "C" int test_if_data_home_dir(const char *dir);
......
......@@ -33,6 +33,7 @@
#include <waiting_threads.h>
#include "debug_sync.h"
#include "log_event.h"
#include "sql_show.h"
#include "../storage/myisam/ha_myisam.h"
......@@ -1447,6 +1448,7 @@ void clean_up(bool print_message)
#endif
my_tz_free();
my_database_names_free();
ignore_db_dirs_free();
#ifndef NO_EMBEDDED_ACCESS_CHECKS
servers_free(1);
acl_free(1);
......@@ -3332,6 +3334,9 @@ static int init_common_variables(const char *conf_file_name, int argc,
mysql_init_variables())
return 1;
if (ignore_db_dirs_init())
return 1;
#ifdef HAVE_TZNAME
{
struct tm tm_tmp;
......@@ -3677,6 +3682,12 @@ You should consider changing lower_case_table_names to 1 or 2",
files_charset_info :
&my_charset_bin);
if (ignore_db_dirs_process_additions())
{
sql_print_error("An error occurred while storing ignore_db_dirs to a hash.");
return 1;
}
return 0;
}
......@@ -5999,7 +6010,8 @@ enum options_mysqld
OPT_MAX_LONG_DATA_SIZE,
OPT_MASTER_VERIFY_CHECKSUM,
OPT_SLAVE_SQL_VERIFY_CHECKSUM,
OPT_QUERY_CACHE_STRIP_COMMENTS
OPT_QUERY_CACHE_STRIP_COMMENTS,
OPT_IGNORE_DB_DIRECTORY
};
......@@ -6288,6 +6300,11 @@ struct my_option my_long_options[] =
each time the SQL thread starts.",
&opt_init_slave, &opt_init_slave, 0, GET_STR_ALLOC,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"ignore-db-dir", OPT_IGNORE_DB_DIRECTORY,
"Specifies a directory to add to the ignore list when collecting "
"database names from the datadir. Put a blank argument to reset "
"the list accumulated so far.", 0, 0, 0, GET_STR, REQUIRED_ARG,
0, 0, 0, 0, 0, 0},
{"language", 'L',
"Client error messages in given language. May be given as a full path.",
&language_ptr, &language_ptr, 0, GET_STR, REQUIRED_ARG,
......@@ -9286,6 +9303,22 @@ mysqld_get_one_option(int optid,
case OPT_MAX_LONG_DATA_SIZE:
max_long_data_size_used= true;
break;
case OPT_IGNORE_DB_DIRECTORY:
if (*argument == 0)
ignore_db_dirs_reset();
else
{
if (push_ignored_db_dir(argument))
{
sql_print_error("Can't start server: "
"cannot process --ignore-db-dir=%.*s",
FN_REFLEN, argument);
return 1;
}
}
break;
}
return 0;
}
......
......@@ -67,6 +67,7 @@
#include <my_dir.h>
#include <waiting_threads.h>
#include "events.h"
#include "sql_show.h" // opt_ignore_db_dirs
/* WITH_NDBCLUSTER_STORAGE_ENGINE */
#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
......@@ -1013,6 +1014,8 @@ static sys_var_readonly sys_in_transaction(&vars, "in_transaction",
OPT_SESSION, SHOW_BOOL,
in_transaction);
static sys_var_const_str_ptr sys_ignore_db_dirs(&vars, "ignore_db_dirs",
&opt_ignore_db_dirs);
bool sys_var::check(THD *thd, set_var *var)
......
......@@ -379,6 +379,284 @@ bool mysqld_show_privileges(THD *thd)
}
/** Hash of LEX_STRINGs used to search for ignored db directories. */
static HASH ignore_db_dirs_hash;
/**
An array of LEX_STRING pointers to collect the options at
option parsing time.
*/
static DYNAMIC_ARRAY ignore_db_dirs_array;
/**
A value for the read only system variable to show a list of
ignored directories.
*/
char *opt_ignore_db_dirs= NULL;
/**
This flag is ON if:
- the list of ignored directories is not empty
- and some of the ignored directory names
need no tablename-to-filename conversion.
Otherwise, if the name of the directory contains
unconditional characters like '+' or '.', they
never can match the database directory name. So the
db_name_is_in_ignore_db_dirs_list() can just return at once.
*/
static bool skip_ignored_dir_check= TRUE;
/**
Sets up the data structures for collection of directories at option
processing time.
We need to collect the directories in an array first, because
we need the character sets initialized before setting up the hash.
@return state
@retval TRUE failed
@retval FALSE success
*/
bool
ignore_db_dirs_init()
{
return my_init_dynamic_array(&ignore_db_dirs_array, sizeof(LEX_STRING *),
0, 0);
}
/**
Retrieves the key (the string itself) from the LEX_STRING hash members.
Needed by hash_init().
@param data the data element from the hash
@param out len_ret Placeholder to return the length of the key
@param unused
@return a pointer to the key
*/
static uchar *
db_dirs_hash_get_key(const uchar *data, size_t *len_ret,
my_bool __attribute__((unused)))
{
LEX_STRING *e= (LEX_STRING *) data;
*len_ret= e->length;
return (uchar *) e->str;
}
/**
Wrap a directory name into a LEX_STRING and push it to the array.
Called at option processing time for each --ignore-db-dir option.
@param path the name of the directory to push
@return state
@retval TRUE failed
@retval FALSE success
*/
bool
push_ignored_db_dir(char *path)
{
LEX_STRING *new_elt;
char *new_elt_buffer;
size_t path_len= strlen(path);
if (!path_len || path_len >= FN_REFLEN)
return true;
// No need to normalize, it's only a directory name, not a path.
if (!my_multi_malloc(0,
&new_elt, sizeof(LEX_STRING),
&new_elt_buffer, path_len + 1,
NullS))
return true;
new_elt->str= new_elt_buffer;
memcpy(new_elt_buffer, path, path_len);
new_elt_buffer[path_len]= 0;
new_elt->length= path_len;
return insert_dynamic(&ignore_db_dirs_array, (uchar*) &new_elt);
}
/**
Clean up the directory ignore options accumulated so far.
Called at option processing time for each --ignore-db-dir option
with an empty argument.
*/
void
ignore_db_dirs_reset()
{
LEX_STRING **elt;
while (NULL!= (elt= (LEX_STRING **) pop_dynamic(&ignore_db_dirs_array)))
if (elt && *elt)
my_free(*elt, MYF(0));
}
/**
Free the directory ignore option variables.
Called at server shutdown.
*/
void
ignore_db_dirs_free()
{
if (opt_ignore_db_dirs)
{
my_free(opt_ignore_db_dirs, MYF(0));
opt_ignore_db_dirs= NULL;
}
ignore_db_dirs_reset();
delete_dynamic(&ignore_db_dirs_array);
my_hash_free(&ignore_db_dirs_hash);
}
/**
Initialize the ignore db directories hash and status variable from
the options collected in the array.
Called when option processing is over and the server's in-memory
structures are fully initialized.
@return state
@retval TRUE failed
@retval FALSE success
*/
static void dispose_db_dir(void *ptr)
{
my_free(ptr, MYF(0));
}
bool
ignore_db_dirs_process_additions()
{
ulong i;
size_t len;
char *ptr;
LEX_STRING *dir;
DBUG_ASSERT(opt_ignore_db_dirs == NULL);
skip_ignored_dir_check= TRUE;
if (my_hash_init(&ignore_db_dirs_hash,
lower_case_table_names ?
character_set_filesystem : &my_charset_bin,
0, 0, 0, db_dirs_hash_get_key,
dispose_db_dir,
HASH_UNIQUE))
return true;
/* len starts from 1 because of the terminating zero. */
len= 1;
for (i= 0; i < ignore_db_dirs_array.elements; i++)
{
get_dynamic(&ignore_db_dirs_array, (uchar *) &dir, i);
len+= dir->length + 1; // +1 for the comma
if (skip_ignored_dir_check)
{
char buff[FN_REFLEN];
uint buff_len;
buff_len= tablename_to_filename(dir->str, buff, sizeof(buff));
skip_ignored_dir_check= strcmp(dir->str, buff) != 0;
}
}
/* No delimiter for the last directory. */
if (len > 1)
len--;
/* +1 the terminating zero */
ptr= opt_ignore_db_dirs= (char *) my_malloc(len + 1, MYF(0));
if (!ptr)
return true;
/* Make sure we have an empty string to start with. */
*ptr= 0;
for (i= 0; i < ignore_db_dirs_array.elements; i++)
{
get_dynamic(&ignore_db_dirs_array, (uchar *) &dir, i);
if (my_hash_insert(&ignore_db_dirs_hash, (uchar *) dir))
return true;
ptr= strnmov(ptr, dir->str, dir->length);
if (i + 1 < ignore_db_dirs_array.elements)
ptr= strmov(ptr, ",");
/*
Set the transferred array element to NULL to avoid double free
in case of error.
*/
dir= NULL;
set_dynamic(&ignore_db_dirs_array, (uchar *) &dir, i);
}
/* make sure the string is terminated */
DBUG_ASSERT(ptr - opt_ignore_db_dirs <= (ptrdiff_t) len);
*ptr= 0;
/*
It's OK to empty the array here as the allocated elements are
referenced through the hash now.
*/
reset_dynamic(&ignore_db_dirs_array);
return false;
}
/**
Check if a directory name is in the hash of ignored directories.
@return search result
@retval TRUE found
@retval FALSE not found
*/
static inline bool
is_in_ignore_db_dirs_list(const char *directory)
{
return ignore_db_dirs_hash.records &&
NULL != my_hash_search(&ignore_db_dirs_hash, (const uchar *) directory,
strlen(directory));
}
/**
Check if a database name is in the hash of ignored directories.
@return search result
@retval TRUE found
@retval FALSE not found
*/
bool
db_name_is_in_ignore_db_dirs_list(const char *directory)
{
char buff[FN_REFLEN];
uint buff_len;
if (skip_ignored_dir_check)
return 0;
buff_len= tablename_to_filename(directory, buff, sizeof(buff));
return my_hash_search(&ignore_db_dirs_hash, (uchar *) buff, buff_len)!=NULL;
}
/***************************************************************************
List all column types
***************************************************************************/
......@@ -552,6 +830,9 @@ find_files(THD *thd, List<LEX_STRING> *files, const char *db,
if (!MY_S_ISDIR(file->mystat->st_mode))
continue;
if (is_in_ignore_db_dirs_list(file->name))
continue;
file_name_len= filename_to_tablename(file->name, uname, sizeof(uname));
if (wild)
{
......
......@@ -41,4 +41,13 @@ int view_store_create_info(THD *thd, TABLE_LIST *table, String *buff);
int copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table);
/* Handle the ignored database directories list for SHOW/I_S. */
bool ignore_db_dirs_init();
void ignore_db_dirs_free();
void ignore_db_dirs_reset();
bool ignore_db_dirs_process_additions();
bool push_ignored_db_dir(char *path);
extern char *opt_ignore_db_dirs;
#endif /* SQL_SHOW_H */
......@@ -3197,6 +3197,9 @@ bool check_db_name(LEX_STRING *org_name)
if (lower_case_table_names && name != any_db)
my_casedn_str(files_charset_info, name);
if (db_name_is_in_ignore_db_dirs_list(name))
return 1;
return check_table_name(name, name_length, check_for_path_chars);
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment