1 /* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. 2 3 This program is free software; you can redistribute it and/or modify 4 it under the terms of the GNU General Public License as published by 5 the Free Software Foundation; version 2 of the License. 6 7 This program is distributed in the hope that it will be useful, 8 but WITHOUT ANY WARRANTY; without even the implied warranty of 9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 GNU General Public License for more details. 11 12 You should have received a copy of the GNU General Public License 13 along with this program; if not, write to the Free Software 14 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ 15 16 17 /** 18 @file 19 20 @brief 21 Functions for discover of frm file from handler 22 */ 23 24 #include "mariadb.h" 25 #include "sql_priv.h" 26 #include "unireg.h" 27 #include "discover.h" 28 #include <my_dir.h> 29 30 /** 31 Read the contents of a .frm file. 32 33 frmdata and len are set to 0 on error. 34 35 @param name path to table-file "db/name" 36 @param frmdata frm data 37 @param len length of the read frmdata 38 39 @retval 40 0 ok 41 @retval 42 1 Could not open file 43 @retval 44 2 Could not stat file 45 @retval 46 3 Could not allocate data for read. Could not read file 47 */ 48 49 int readfrm(const char *name, const uchar **frmdata, size_t *len) 50 { 51 int error; 52 char index_file[FN_REFLEN]; 53 File file; 54 size_t read_len; 55 uchar *read_data; 56 MY_STAT state; 57 DBUG_ENTER("readfrm"); 58 DBUG_PRINT("enter",("name: '%s'",name)); 59 60 *frmdata= NULL; // In case of errors 61 *len= 0; 62 error= 1; 63 if ((file= mysql_file_open(key_file_frm, 64 fn_format(index_file, name, "", reg_ext, 65 MY_UNPACK_FILENAME|MY_APPEND_EXT), 66 O_RDONLY | O_SHARE, 67 MYF(0))) < 0) 68 goto err_end; 69 70 // Get length of file 71 error= 2; 72 if (mysql_file_fstat(file, &state, MYF(0))) 73 goto err; 74 read_len= (size_t)MY_MIN(FRM_MAX_SIZE, state.st_size); // safety 75 76 // Read whole frm file 77 error= 3; 78 if (!(read_data= (uchar*)my_malloc(read_len, MYF(MY_WME)))) 79 goto err; 80 if (mysql_file_read(file, read_data, read_len, MYF(MY_NABP))) 81 { 82 my_free(read_data); 83 goto err; 84 } 85 86 // Setup return data 87 *frmdata= (uchar*) read_data; 88 *len= read_len; 89 error= 0; 90 91 err: 92 (void) mysql_file_close(file, MYF(MY_WME)); 93 94 err_end: /* Here when no file */ 95 DBUG_RETURN (error); 96 } /* readfrm */ 97 98 99 /* 100 Write the content of a frm data pointer 101 to a frm file. 102 103 @param path path to table-file "db/name" 104 @param frmdata frm data 105 @param len length of the frmdata 106 107 @retval 108 0 ok 109 @retval 110 2 Could not write file 111 */ 112 113 int writefrm(const char *path, const char *db, const char *table, 114 bool tmp_table, const uchar *frmdata, size_t len) 115 { 116 char file_name[FN_REFLEN+1]; 117 int error; 118 int create_flags= O_RDWR | O_TRUNC; 119 DBUG_ENTER("writefrm"); 120 DBUG_PRINT("enter",("name: '%s' len: %lu ",path, (ulong) len)); 121 122 if (tmp_table) 123 create_flags|= O_EXCL | O_NOFOLLOW; 124 125 strxnmov(file_name, sizeof(file_name)-1, path, reg_ext, NullS); 126 127 File file= mysql_file_create(key_file_frm, file_name, 128 CREATE_MODE, create_flags, MYF(0)); 129 130 if (unlikely((error= file < 0))) 131 { 132 if (my_errno == ENOENT) 133 my_error(ER_BAD_DB_ERROR, MYF(0), db); 134 else 135 my_error(ER_CANT_CREATE_TABLE, MYF(0), db, table, my_errno); 136 } 137 else 138 { 139 error= (int)mysql_file_write(file, frmdata, len, MYF(MY_WME | MY_NABP)); 140 141 if (!error && !tmp_table && opt_sync_frm) 142 error= mysql_file_sync(file, MYF(MY_WME)) || 143 my_sync_dir_by_file(file_name, MYF(MY_WME)); 144 145 error|= mysql_file_close(file, MYF(MY_WME)); 146 } 147 DBUG_RETURN(error); 148 } /* writefrm */ 149 150 static inline void advance(FILEINFO* &from, FILEINFO* &to, 151 FILEINFO* cur, bool &skip) 152 { 153 if (skip) // if not copying 154 from= cur; // just advance the start pointer 155 else // if copying 156 if (to == from) // but to the same place (not shifting the data) 157 from= to= cur; // advance both pointers 158 else // otherwise 159 while (from < cur) // have to copy [from...cur) to [to...) 160 *to++ = *from++; 161 skip= false; 162 } 163 164 /** 165 Go through the directory listing looking for files with a specified 166 extension and add them to the result list 167 168 @details 169 This function may be called many times on the same directory listing 170 but with different extensions. To avoid discovering the same table twice, 171 whenever a table file is discovered, all files with the same name 172 (independently from the extensions) are removed from the list. 173 174 Example: the list contained 175 { "db.opt", "t1.MYD", "t1.MYI", "t1.frm", "t2.ARZ", "t3.ARZ", "t3.frm" } 176 on discovering all ".frm" files, tables "t1" and "t3" will be found, 177 and list will become 178 { "db.opt", "t2.ARZ" } 179 and now ".ARZ" discovery can discover the table "t2" 180 181 @note 182 This function assumes that the directory listing is sorted alphabetically. 183 184 @note Partitioning makes this more complicated. A partitioned table t1 might 185 have files, like t1.frm, t1#P#part1.ibd, t1#P#foo.ibd, etc. 186 That means we need to compare file names only up to the first '#' or '.' 187 whichever comes first. 188 */ 189 int extension_based_table_discovery(MY_DIR *dirp, const char *ext_meta, 190 handlerton::discovered_list *result) 191 { 192 CHARSET_INFO *cs= character_set_filesystem; 193 size_t ext_meta_len= strlen(ext_meta); 194 FILEINFO *from, *to, *cur, *end; 195 bool skip= false; 196 197 from= to= cur= dirp->dir_entry; 198 end= cur + dirp->number_of_files; 199 while (cur < end) 200 { 201 char *octothorp= strchr(cur->name + 1, '#'); 202 char *ext= strchr(octothorp ? octothorp : cur->name, FN_EXTCHAR); 203 204 if (ext) 205 { 206 size_t len= (octothorp ? octothorp : ext) - cur->name; 207 if (from != cur && 208 (strlen(from->name) <= len || 209 my_strnncoll(cs, (uchar*)from->name, len, (uchar*)cur->name, len) || 210 (from->name[len] != FN_EXTCHAR && from->name[len] != '#'))) 211 advance(from, to, cur, skip); 212 213 if (my_strnncoll(cs, (uchar*)ext, strlen(ext), 214 (uchar*)ext_meta, ext_meta_len) == 0) 215 { 216 *ext = 0; 217 if (result->add_file(cur->name)) 218 return 1; 219 *ext = FN_EXTCHAR; 220 skip= true; // table discovered, skip all files with the same name 221 } 222 } 223 else 224 { 225 advance(from, to, cur, skip); 226 from++; 227 } 228 229 cur++; 230 } 231 advance(from, to, cur, skip); 232 dirp->number_of_files= (uint)(to - dirp->dir_entry); 233 return 0; 234 } 235 236 /** 237 Simple, not reusable file-based table discovery 238 239 @details 240 simplified version of extension_based_table_discovery(), that does not 241 modify the list of files. It cannot be called many times for the same 242 directory listing, otherwise it'll produce duplicate results. 243 */ 244 int ext_table_discovery_simple(MY_DIR *dirp, 245 handlerton::discovered_list *result) 246 { 247 CHARSET_INFO *cs= character_set_filesystem; 248 FILEINFO *cur, *end; 249 250 cur= dirp->dir_entry; 251 end= cur + dirp->number_of_files; 252 while (cur < end) 253 { 254 char *ext= strrchr(cur->name, FN_EXTCHAR); 255 256 if (ext) 257 { 258 if (my_strnncoll(cs, (uchar*)ext, strlen(ext), 259 (uchar*)reg_ext, reg_ext_length) == 0) 260 { 261 *ext = 0; 262 if (result->add_file(cur->name)) 263 return 1; 264 } 265 } 266 cur++; 267 } 268 return 0; 269 } 270 271