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 | |