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