datadict.cc 7.96 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
14
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA */
15

16
#include "mariadb.h"
17 18 19 20
#include "datadict.h"
#include "sql_priv.h"
#include "sql_class.h"
#include "sql_table.h"
21
#include "ha_sequence.h"
22

23 24 25 26
static int read_string(File file, uchar**to, size_t length)
{
  DBUG_ENTER("read_string");

27
  /* This can't use MY_THREAD_SPECIFIC as it's used on server start */
28
  if (!(*to= (uchar*) my_malloc(PSI_INSTRUMENT_ME, length+1,MYF(MY_WME))) ||
29 30 31 32 33 34 35 36 37 38
      mysql_file_read(file, *to, length, MYF(MY_NABP)))
  {
     my_free(*to);
    *to= 0;
    DBUG_RETURN(1);
  }
  *((char*) *to+length)= '\0'; // C-style safety
  DBUG_RETURN (0);
}

39 40 41 42

/**
  Check type of .frm if we are not going to parse it.

43 44 45 46
  @param[in]  thd               The current session.
  @param[in]  path              path to FRM file.
  @param[in/out] engine_name    table engine name (length < NAME_CHAR_LEN)

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 50
  If engine_name is 0, then the function will only test if the file is a
  view or not
51

52
  @retval  TABLE_TYPE_UNKNOWN   error  - file can't be opened
53
  @retval  TABLE_TYPE_NORMAL    table
54 55
  @retval  TABLE_TYPE_SEQUENCE  sequence table
  @retval  TABLE_TYPE_VIEW      view
56 57
*/

58 59 60
Table_type dd_frm_type(THD *thd, char *path, LEX_CSTRING *engine_name,
                       LEX_CSTRING *partition_engine_name,
                       LEX_CUSTRING *table_version)
61 62
{
  File file;
63
  uchar header[64+ MY_UUID_SIZE + 2];     // Header and uuid
64
  size_t error;
65
  Table_type type= TABLE_TYPE_UNKNOWN;
66
  uchar dbt;
67 68
  DBUG_ENTER("dd_frm_type");

69 70
  file= mysql_file_open(key_file_frm, path, O_RDONLY | O_SHARE, MYF(0));
  if (file < 0)
71
    DBUG_RETURN(TABLE_TYPE_UNKNOWN);
72

73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
  /*
    We return TABLE_TYPE_NORMAL if we can open the .frm file. This allows us
    to drop a bad .frm file with DROP TABLE
  */
  type= TABLE_TYPE_NORMAL;

  /*
    Initialize engine name in case we are not able to find it out
    The cast is safe, as engine_name->str points to a usable buffer.
   */
  if (engine_name)
  {
    engine_name->length= 0;
    ((char*) (engine_name->str))[0]= 0;
  }
88 89 90 91 92 93 94 95 96 97 98 99
  if (partition_engine_name)
  {
    partition_engine_name->length= 0;
    partition_engine_name->str= 0;
  }
  if (table_version)
  {
    table_version->length= 0;
    table_version->str= 0;                      // Allocated if needed
  }
  if (unlikely((error= mysql_file_read(file, (uchar*) header, sizeof(header),
                                       MYF(MY_NABP)))))
100
    goto err;
101

102
  if (unlikely((!strncmp((char*) header, "TYPE=VIEW\n", 10))))
103
  {
104
    type= TABLE_TYPE_VIEW;
105 106 107
    goto err;
  }

108 109
  /* engine_name is 0 if we only want to know if table is view or not */
  if (!engine_name)
110
    goto err;
111

112 113 114
  if (!is_binary_frm_header(header))
    goto err;

115 116
  dbt= header[3];

117 118 119
  if (((header[39] >> 4) & 3) == HA_CHOICE_YES)
  {
    DBUG_PRINT("info", ("Sequence found"));
120
    type= TABLE_TYPE_SEQUENCE;
121 122
  }

123 124 125 126 127 128 129 130
  if (table_version)
  {
    /* Read the table version (if it is a 'new' frm file) */
    if (header[64] == EXTRA2_TABLEDEF_VERSION && header[65] == MY_UUID_SIZE)
      if ((table_version->str= (uchar*) thd->memdup(header + 66, MY_UUID_SIZE)))
        table_version->length= MY_UUID_SIZE;
  }

131 132 133
  /* cannot use ha_resolve_by_legacy_type without a THD */
  if (thd && dbt < DB_TYPE_FIRST_DYNAMIC)
  {
134
    handlerton *ht= ha_resolve_by_legacy_type(thd, (legacy_db_type) dbt);
135 136 137
    if (ht)
    {
      *engine_name= hton2plugin[ht->slot]->name;
138 139 140 141 142 143 144 145 146 147 148 149
#ifdef WITH_PARTITION_STORAGE_ENGINE
      if (partition_engine_name && dbt == DB_TYPE_PARTITION_DB)
      {
        handlerton *p_ht;
        legacy_db_type new_dbt= (legacy_db_type) header[61];
        if (new_dbt >= DB_TYPE_FIRST_DYNAMIC)
          goto cont;
        if (!(p_ht= ha_resolve_by_legacy_type(thd, new_dbt)))
          goto err;
        *partition_engine_name= *hton_name(p_ht);
      }
#endif // WITH_PARTITION_STORAGE_ENGINE
150 151 152
      goto err;
    }
  }
153

154 155 156
#ifdef WITH_PARTITION_STORAGE_ENGINE
cont:
#endif
157
  /* read the true engine name */
158
  {
159
    MY_STAT state;
160 161 162 163 164 165
    uchar *frm_image= 0;
    uint n_length;

    if (mysql_file_fstat(file, &state, MYF(MY_WME)))
      goto err;

166 167
    MSAN_STAT_WORKAROUND(&state);

168 169 170
    if (mysql_file_seek(file, 0, SEEK_SET, MYF(MY_WME)))
      goto err;

171
    if (read_string(file, &frm_image, (size_t)state.st_size))
172 173
      goto err;

174 175
    /* The test for !engine_name->length is only true for partition engine */
    if (!engine_name->length && (n_length= uint4korr(frm_image+55)))
176
    {
177
      uint record_offset= uint2korr(frm_image+6)+
178
                      ((uint2korr(frm_image+14) == 0xffff ?
179
                        uint4korr(frm_image+47) : uint2korr(frm_image+14)));
180 181 182 183 184 185 186 187
      uint reclength= uint2korr(frm_image+16);

      uchar *next_chunk= frm_image + record_offset + reclength;
      uchar *buff_end= next_chunk + n_length;
      uint connect_string_length= uint2korr(next_chunk);
      next_chunk+= connect_string_length + 2;
      if (next_chunk + 2 < buff_end)
      {
188 189
        uint len= uint2korr(next_chunk);
        if (len <= NAME_CHAR_LEN)
190 191 192 193 194 195
        {
          /*
            The following cast is safe as the caller has allocated buffer
            and it's up to this function to generate the name.
          */
          strmake((char*) engine_name->str, (char*)next_chunk + 2,
196
                  engine_name->length= len);
197
        }
198 199 200
      }
    }

201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
#ifdef WITH_PARTITION_STORAGE_ENGINE
    if (partition_engine_name && dbt == DB_TYPE_PARTITION_DB)
    {
      uint len;
      const uchar *extra2;
      /* Length of the MariaDB extra2 segment in the form file. */
      len = uint2korr(frm_image+4);
      extra2= frm_image + 64;
      if (*extra2 != '/')   // old frm had '/' there
      {
        const uchar *e2end= extra2 + len;
        while (extra2 + 3 <= e2end)
        {
          uchar type= *extra2++;
          size_t length= *extra2++;
          if (!length)
          {
            if (extra2 + 2 >= e2end)
              break;
            length= uint2korr(extra2);
            extra2+= 2;
            if (length < 256)
              break;
          }
          if (extra2 + length > e2end)
            break;
          if (type == EXTRA2_DEFAULT_PART_ENGINE)
          {
            partition_engine_name->str= thd->strmake((char*)extra2, length);
            partition_engine_name->length= length;
            break;
          }
          extra2+= length;
        }
      }
    }
#endif // WITH_PARTITION_STORAGE_ENGINE
238 239 240
    my_free(frm_image);
  }

241
  /* Probably a table. */
242 243 244
err:
  mysql_file_close(file, MYF(MY_WME));
  DBUG_RETURN(type);
245 246 247 248 249 250 251 252 253 254 255 256 257 258
}


/*
  Regenerate a metadata locked table.

  @param  thd   Thread context.
  @param  db    Name of the database to which the table belongs to.
  @param  name  Table name.

  @retval  FALSE  Success.
  @retval  TRUE   Error.
*/

259
bool dd_recreate_table(THD *thd, const char *db, const char *table_name)
260 261
{
  HA_CREATE_INFO create_info;
Sergei Golubchik's avatar
Sergei Golubchik committed
262
  char path_buf[FN_REFLEN + 1];
263 264
  DBUG_ENTER("dd_recreate_table");

265 266 267
  /* There should be a exclusive metadata lock on the table. */
  DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, table_name,
                                             MDL_EXCLUSIVE));
268
  create_info.init();
269 270
  build_table_filename(path_buf, sizeof(path_buf) - 1,
                       db, table_name, "", 0);
271
  /* Attempt to reconstruct the table. */
272
  DBUG_RETURN(ha_create_table(thd, path_buf, db, table_name, &create_info, 0, 0));
273 274
}