diff options
Diffstat (limited to '')
-rw-r--r-- | sql/discover.cc | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/sql/discover.cc b/sql/discover.cc new file mode 100644 index 00000000..4267f97c --- /dev/null +++ b/sql/discover.cc @@ -0,0 +1,273 @@ +/* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2009, 2020, MariaDB Corporation. + + 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + + +/** + @file + + @brief + Functions for discover of frm file from handler +*/ + +#include "mariadb.h" +#include "sql_priv.h" +#include "unireg.h" +#include "discover.h" +#include <my_dir.h> + +/** + Read the contents of a .frm file. + + frmdata and len are set to 0 on error. + + @param name path to table-file "db/name" + @param frmdata frm data + @param len length of the read frmdata + + @retval + 0 ok + @retval + 1 Could not open file + @retval + 2 Could not stat file + @retval + 3 Could not allocate data for read. Could not read file +*/ + +int readfrm(const char *name, const uchar **frmdata, size_t *len) +{ + int error; + char index_file[FN_REFLEN]; + File file; + size_t read_len; + uchar *read_data; + MY_STAT state; + DBUG_ENTER("readfrm"); + DBUG_PRINT("enter",("name: '%s'",name)); + + *frmdata= NULL; // In case of errors + *len= 0; + error= 1; + if ((file= mysql_file_open(key_file_frm, + fn_format(index_file, name, "", reg_ext, + MY_UNPACK_FILENAME|MY_APPEND_EXT), + O_RDONLY | O_SHARE, + MYF(0))) < 0) + goto err_end; + + // Get length of file + error= 2; + if (mysql_file_fstat(file, &state, MYF(0))) + goto err; + read_len= (size_t)MY_MIN(FRM_MAX_SIZE, state.st_size); // safety + + // Read whole frm file + error= 3; + if (!(read_data= (uchar*)my_malloc(key_memory_frm_string, read_len, + MYF(MY_WME)))) + goto err; + if (mysql_file_read(file, read_data, read_len, MYF(MY_NABP))) + { + my_free(read_data); + goto err; + } + + // Setup return data + *frmdata= (uchar*) read_data; + *len= read_len; + error= 0; + + err: + (void) mysql_file_close(file, MYF(MY_WME)); + + err_end: /* Here when no file */ + DBUG_RETURN (error); +} /* readfrm */ + + +/* + Write the content of a frm data pointer to a frm or par file. + + @param path full path to table-file "db/name.frm" or .par + @param db Database name. Only used for my_error() + @param table Table name. Only used for my_error() + @param data data to write to file + @param len length of the data + + @retval + 0 ok + @retval + <> 0 Could not write file. In this case the file is not created +*/ + +int writefile(const char *path, const char *db, const char *table, + bool tmp_table, const uchar *data, size_t len) +{ + int error; + int create_flags= O_RDWR | O_TRUNC; + DBUG_ENTER("writefile"); + DBUG_PRINT("enter",("name: '%s' len: %lu ",path, (ulong) len)); + + if (tmp_table) + create_flags|= O_EXCL | O_NOFOLLOW; + + File file= mysql_file_create(key_file_frm, path, + CREATE_MODE, create_flags, MYF(0)); + + if (unlikely((error= file < 0))) + { + if (my_errno == ENOENT) + my_error(ER_BAD_DB_ERROR, MYF(0), db); + else + my_error(ER_CANT_CREATE_TABLE, MYF(0), db, table, my_errno); + } + else + { + error= (int)mysql_file_write(file, data, len, MYF(MY_WME | MY_NABP)); + + if (!error && !tmp_table && opt_sync_frm) + error= mysql_file_sync(file, MYF(MY_WME)) || + my_sync_dir_by_file(path, MYF(MY_WME)); + + error|= mysql_file_close(file, MYF(MY_WME)); + if (error) + my_delete(path, MYF(0)); + } + DBUG_RETURN(error); +} /* writefile */ + + +static inline void advance(FILEINFO* &from, FILEINFO* &to, + FILEINFO* cur, bool &skip) +{ + if (skip) // if not copying + from= cur; // just advance the start pointer + else // if copying + if (to == from) // but to the same place, not shifting the data + from= to= cur; // advance both pointers + else // otherwise + while (from < cur) // have to copy [from...cur) to [to...) + *to++ = *from++; + skip= false; +} + +/** + Go through the directory listing looking for files with a specified + extension and add them to the result list + + @details + This function may be called many times on the same directory listing + but with different extensions. To avoid discovering the same table twice, + whenever a table file is discovered, all files with the same name + (independently from the extensions) are removed from the list. + + Example: the list contained + { "db.opt", "t1.MYD", "t1.MYI", "t1.frm", "t2.ARZ", "t3.ARZ", "t3.frm" } + on discovering all ".frm" files, tables "t1" and "t3" will be found, + and list will become + { "db.opt", "t2.ARZ" } + and now ".ARZ" discovery can discover the table "t2" + + @note + This function assumes that the directory listing is sorted alphabetically. + + @note Partitioning makes this more complicated. A partitioned table t1 might + have files, like t1.frm, t1#P#part1.ibd, t1#P#foo.ibd, etc. + That means we need to compare file names only up to the first '#' or '.' + whichever comes first. +*/ +int extension_based_table_discovery(MY_DIR *dirp, const char *ext_meta, + handlerton::discovered_list *result) +{ + CHARSET_INFO *cs= character_set_filesystem; + size_t ext_meta_len= strlen(ext_meta); + FILEINFO *from, *to, *cur, *end; + bool skip= false; + + from= to= cur= dirp->dir_entry; + end= cur + dirp->number_of_files; + while (cur < end) + { + char *octothorp= strchr(cur->name + 1, '#'); + char *ext= strchr(octothorp ? octothorp : cur->name, FN_EXTCHAR); + + if (ext) + { + size_t len= (octothorp ? octothorp : ext) - cur->name; + if (from != cur && + (strlen(from->name) <= len || + cs->strnncoll(from->name, len, cur->name, len) || + (from->name[len] != FN_EXTCHAR && from->name[len] != '#'))) + advance(from, to, cur, skip); + + if (cs->strnncoll(ext, strlen(ext), + ext_meta, ext_meta_len) == 0) + { + *ext = 0; + if (result->add_file(cur->name)) + return 1; + *ext = FN_EXTCHAR; + skip= true; // table discovered, skip all files with the same name + } + } + else + { + advance(from, to, cur, skip); + from++; + } + + cur++; + } + advance(from, to, cur, skip); + dirp->number_of_files= (uint)(to - dirp->dir_entry); + return 0; +} + +/** + Simple, not reusable file-based table discovery + + @details + simplified version of extension_based_table_discovery(), that does not + modify the list of files. It cannot be called many times for the same + directory listing, otherwise it'll produce duplicate results. +*/ +int ext_table_discovery_simple(MY_DIR *dirp, + handlerton::discovered_list *result) +{ + CHARSET_INFO *cs= character_set_filesystem; + FILEINFO *cur, *end; + + cur= dirp->dir_entry; + end= cur + dirp->number_of_files; + while (cur < end) + { + char *ext= strrchr(cur->name, FN_EXTCHAR); + + if (ext) + { + if (cs->strnncoll(ext, strlen(ext), + reg_ext, reg_ext_length) == 0) + { + *ext = 0; + if (result->add_file(cur->name)) + return 1; + } + } + cur++; + } + return 0; +} + |