| 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-1301 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 [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 | memset(&create_info, 0, sizeof(create_info)); |
| 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 | |