1 /* Copyright (c) 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 #include "mariadb.h" 17 #include "datadict.h" 18 #include "sql_priv.h" 19 #include "sql_class.h" 20 #include "sql_table.h" 21 #include "ha_sequence.h" 22 23 static int read_string(File file, uchar**to, size_t length) 24 { 25 DBUG_ENTER("read_string"); 26 27 /* This can't use MY_THREAD_SPECIFIC as it's used on server start */ 28 if (!(*to= (uchar*) my_malloc(length+1,MYF(MY_WME))) || 29 mysql_file_read(file, *to, length, MYF(MY_NABP))) 30 { 31 my_free(*to); 32 *to= 0; 33 DBUG_RETURN(1); 34 } 35 *((char*) *to+length)= '\0'; // C-style safety 36 DBUG_RETURN (0); 37 } 38 39 40 /** 41 Check type of .frm if we are not going to parse it. 42 43 @param[in] thd The current session. 44 @param[in] path path to FRM file. 45 @param[in/out] engine_name table engine name (length < NAME_CHAR_LEN) 46 47 engine_name is a LEX_CSTRING, where engine_name->str must point to 48 a buffer of at least NAME_CHAR_LEN+1 bytes. 49 If engine_name is 0, then the function will only test if the file is a 50 view or not 51 52 @param[out] is_sequence 1 if table is a SEQUENCE, 0 otherwise 53 54 @retval TABLE_TYPE_UNKNOWN error - file can't be opened 55 @retval TABLE_TYPE_NORMAL table 56 @retval TABLE_TYPE_SEQUENCE sequence table 57 @retval TABLE_TYPE_VIEW view 58 */ 59 60 Table_type dd_frm_type(THD *thd, char *path, LEX_CSTRING *engine_name, 61 bool *is_sequence) 62 { 63 File file; 64 uchar header[40]; //"TYPE=VIEW\n" it is 10 characters 65 size_t error; 66 Table_type type= TABLE_TYPE_UNKNOWN; 67 uchar dbt; 68 DBUG_ENTER("dd_frm_type"); 69 70 *is_sequence= 0; 71 72 if ((file= mysql_file_open(key_file_frm, path, O_RDONLY | O_SHARE, MYF(0))) 73 < 0) 74 DBUG_RETURN(TABLE_TYPE_UNKNOWN); 75 76 /* 77 We return TABLE_TYPE_NORMAL if we can open the .frm file. This allows us 78 to drop a bad .frm file with DROP TABLE 79 */ 80 type= TABLE_TYPE_NORMAL; 81 82 /* 83 Initialize engine name in case we are not able to find it out 84 The cast is safe, as engine_name->str points to a usable buffer. 85 */ 86 if (engine_name) 87 { 88 engine_name->length= 0; 89 ((char*) (engine_name->str))[0]= 0; 90 } 91 92 if (unlikely((error= mysql_file_read(file, (uchar*) header, sizeof(header), MYF(MY_NABP))))) 93 goto err; 94 95 if (unlikely((!strncmp((char*) header, "TYPE=VIEW\n", 10)))) 96 { 97 type= TABLE_TYPE_VIEW; 98 goto err; 99 } 100 101 /* engine_name is 0 if we only want to know if table is view or not */ 102 if (!engine_name) 103 goto err; 104 105 if (!is_binary_frm_header(header)) 106 goto err; 107 108 dbt= header[3]; 109 110 if (((header[39] >> 4) & 3) == HA_CHOICE_YES) 111 { 112 DBUG_PRINT("info", ("Sequence found")); 113 *is_sequence= 1; 114 } 115 116 /* cannot use ha_resolve_by_legacy_type without a THD */ 117 if (thd && dbt < DB_TYPE_FIRST_DYNAMIC) 118 { 119 handlerton *ht= ha_resolve_by_legacy_type(thd, (enum legacy_db_type)dbt); 120 if (ht) 121 { 122 *engine_name= hton2plugin[ht->slot]->name; 123 goto err; 124 } 125 } 126 127 /* read the true engine name */ 128 { 129 MY_STAT state; 130 uchar *frm_image= 0; 131 uint n_length; 132 133 if (mysql_file_fstat(file, &state, MYF(MY_WME))) 134 goto err; 135 136 if (mysql_file_seek(file, 0, SEEK_SET, MYF(MY_WME))) 137 goto err; 138 139 if (read_string(file, &frm_image, (size_t)state.st_size)) 140 goto err; 141 142 if ((n_length= uint4korr(frm_image+55))) 143 { 144 uint record_offset= uint2korr(frm_image+6)+ 145 ((uint2korr(frm_image+14) == 0xffff ? 146 uint4korr(frm_image+47) : uint2korr(frm_image+14))); 147 uint reclength= uint2korr(frm_image+16); 148 149 uchar *next_chunk= frm_image + record_offset + reclength; 150 uchar *buff_end= next_chunk + n_length; 151 uint connect_string_length= uint2korr(next_chunk); 152 next_chunk+= connect_string_length + 2; 153 if (next_chunk + 2 < buff_end) 154 { 155 uint len= uint2korr(next_chunk); 156 if (len <= NAME_CHAR_LEN) 157 { 158 /* 159 The following cast is safe as the caller has allocated buffer 160 and it's up to this function to generate the name. 161 */ 162 strmake((char*) engine_name->str, (char*)next_chunk + 2, 163 engine_name->length= len); 164 } 165 } 166 } 167 168 my_free(frm_image); 169 } 170 171 /* Probably a table. */ 172 err: 173 mysql_file_close(file, MYF(MY_WME)); 174 DBUG_RETURN(type); 175 } 176 177 178 /* 179 Regenerate a metadata locked table. 180 181 @param thd Thread context. 182 @param db Name of the database to which the table belongs to. 183 @param name Table name. 184 @param path For temporary tables only - path to table files. 185 Otherwise NULL (the path is calculated from db and table names). 186 187 @retval FALSE Success. 188 @retval TRUE Error. 189 */ 190 191 bool dd_recreate_table(THD *thd, const char *db, const char *table_name, 192 const char *path) 193 { 194 bool error= TRUE; 195 HA_CREATE_INFO create_info; 196 char path_buf[FN_REFLEN + 1]; 197 DBUG_ENTER("dd_recreate_table"); 198 199 create_info.init(); 200 201 if (path) 202 create_info.options|= HA_LEX_CREATE_TMP_TABLE; 203 else 204 { 205 build_table_filename(path_buf, sizeof(path_buf) - 1, 206 db, table_name, "", 0); 207 path= path_buf; 208 209 /* There should be a exclusive metadata lock on the table. */ 210 DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, table_name, 211 MDL_EXCLUSIVE)); 212 } 213 214 /* Attempt to reconstruct the table. */ 215 error= ha_create_table(thd, path, db, table_name, &create_info, NULL); 216 217 DBUG_RETURN(error); 218 } 219 220