| 1 | /* |
| 2 | Copyright (c) 2000, 2011, Oracle and/or its affiliates. |
| 3 | Copyright (c) 2009, 2018, MariaDB Corporation |
| 4 | |
| 5 | This program is free software; you can redistribute it and/or modify |
| 6 | it under the terms of the GNU General Public License as published by |
| 7 | the Free Software Foundation; version 2 of the License. |
| 8 | |
| 9 | This program is distributed in the hope that it will be useful, |
| 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | GNU General Public License for more details. |
| 13 | |
| 14 | You should have received a copy of the GNU General Public License |
| 15 | along with this program; if not, write to the Free Software |
| 16 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| 17 | */ |
| 18 | |
| 19 | /* |
| 20 | Functions to create a unireg form-file from a FIELD and a fieldname-fieldinfo |
| 21 | struct. |
| 22 | In the following functions FIELD * is an ordinary field-structure with |
| 23 | the following exeptions: |
| 24 | sc_length,typepos,row,kol,dtype,regnr and field need not to be set. |
| 25 | str is a (long) to record position where 0 is the first position. |
| 26 | */ |
| 27 | |
| 28 | #include "mariadb.h" |
| 29 | #include "sql_priv.h" |
| 30 | #include "unireg.h" |
| 31 | #include "sql_partition.h" // struct partition_info |
| 32 | #include "sql_class.h" // THD, Internal_error_handler |
| 33 | #include "create_options.h" |
| 34 | #include "discover.h" |
| 35 | #include <m_ctype.h> |
| 36 | |
| 37 | #define FCOMP 17 /* Bytes for a packed field */ |
| 38 | |
| 39 | /* threshold for safe_alloca */ |
| 40 | #define ALLOCA_THRESHOLD 2048 |
| 41 | |
| 42 | static uint pack_keys(uchar *,uint, KEY *, ulong); |
| 43 | static bool pack_header(THD *, uchar *, List<Create_field> &, HA_CREATE_INFO *, |
| 44 | ulong, handler *); |
| 45 | static bool pack_vcols(String *, List<Create_field> &, List<Virtual_column_info> *); |
| 46 | static uint get_interval_id(uint *,List<Create_field> &, Create_field *); |
| 47 | static bool pack_fields(uchar **, List<Create_field> &, HA_CREATE_INFO*, |
| 48 | ulong); |
| 49 | static size_t packed_fields_length(List<Create_field> &); |
| 50 | static bool make_empty_rec(THD *, uchar *, uint, List<Create_field> &, uint, |
| 51 | ulong); |
| 52 | |
| 53 | /* |
| 54 | write the length as |
| 55 | if ( 0 < length <= 255) one byte |
| 56 | if (256 < length <= 65535) zero byte, then two bytes, low-endian |
| 57 | */ |
| 58 | static uchar *(uchar *pos, size_t len) |
| 59 | { |
| 60 | if (len <= 255) |
| 61 | *pos++= (uchar)len; |
| 62 | else |
| 63 | { |
| 64 | /* |
| 65 | At the moment we support options_len up to 64K. |
| 66 | We can easily extend it in the future, if the need arises. |
| 67 | */ |
| 68 | DBUG_ASSERT(len <= 65535); |
| 69 | int2store(pos + 1, len); |
| 70 | pos+= 3; |
| 71 | } |
| 72 | return pos; |
| 73 | } |
| 74 | |
| 75 | static uchar *(uchar *pos, enum extra2_frm_value_type type, |
| 76 | const LEX_CSTRING *str) |
| 77 | { |
| 78 | *pos++ = type; |
| 79 | pos= extra2_write_len(pos, str->length); |
| 80 | memcpy(pos, str->str, str->length); |
| 81 | return pos + str->length; |
| 82 | } |
| 83 | |
| 84 | static uchar *(uchar *pos, enum extra2_frm_value_type type, |
| 85 | LEX_CUSTRING *str) |
| 86 | { |
| 87 | return extra2_write(pos, type, reinterpret_cast<LEX_CSTRING *>(str)); |
| 88 | } |
| 89 | |
| 90 | static uchar *(uchar *pos, |
| 91 | List<Create_field> &create_fields) |
| 92 | { |
| 93 | List_iterator<Create_field> it(create_fields); |
| 94 | *pos++= EXTRA2_FIELD_FLAGS; |
| 95 | /* |
| 96 | always first 2 for field visibility |
| 97 | */ |
| 98 | pos= extra2_write_len(pos, create_fields.elements); |
| 99 | while (Create_field *cf= it++) |
| 100 | { |
| 101 | uchar flags= cf->invisible; |
| 102 | if (cf->flags & VERS_UPDATE_UNVERSIONED_FLAG) |
| 103 | flags|= VERS_OPTIMIZED_UPDATE; |
| 104 | *pos++= flags; |
| 105 | } |
| 106 | return pos; |
| 107 | } |
| 108 | |
| 109 | static const bool ROW_START = true; |
| 110 | static const bool ROW_END = false; |
| 111 | |
| 112 | static inline |
| 113 | uint16 |
| 114 | vers_get_field(HA_CREATE_INFO *create_info, List<Create_field> &create_fields, bool row_start) |
| 115 | { |
| 116 | DBUG_ASSERT(create_info->versioned()); |
| 117 | |
| 118 | List_iterator<Create_field> it(create_fields); |
| 119 | Create_field *sql_field = NULL; |
| 120 | |
| 121 | const Lex_ident row_field= row_start ? create_info->vers_info.as_row.start |
| 122 | : create_info->vers_info.as_row.end; |
| 123 | DBUG_ASSERT(row_field); |
| 124 | |
| 125 | for (unsigned field_no = 0; (sql_field = it++); ++field_no) |
| 126 | { |
| 127 | if (row_field.streq(sql_field->field_name)) |
| 128 | { |
| 129 | DBUG_ASSERT(field_no <= uint16(~0U)); |
| 130 | return uint16(field_no); |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | DBUG_ASSERT(0); /* Not Reachable */ |
| 135 | return 0; |
| 136 | } |
| 137 | |
| 138 | static inline |
| 139 | bool (List<Create_field> &create_fields) |
| 140 | { |
| 141 | List_iterator<Create_field> it(create_fields); |
| 142 | while (Create_field *f= it++) |
| 143 | { |
| 144 | if (f->invisible) |
| 145 | return true; |
| 146 | if (f->flags & VERS_UPDATE_UNVERSIONED_FLAG) |
| 147 | return true; |
| 148 | } |
| 149 | return false; |
| 150 | } |
| 151 | |
| 152 | /** |
| 153 | Create a frm (table definition) file |
| 154 | |
| 155 | @param thd Thread handler |
| 156 | @param table Name of table |
| 157 | @param create_info create info parameters |
| 158 | @param create_fields Fields to create |
| 159 | @param keys number of keys to create |
| 160 | @param key_info Keys to create |
| 161 | @param db_file Handler to use. |
| 162 | |
| 163 | @return the generated frm image as a LEX_CUSTRING, |
| 164 | or null LEX_CUSTRING (str==0) in case of an error. |
| 165 | */ |
| 166 | |
| 167 | LEX_CUSTRING build_frm_image(THD *thd, const LEX_CSTRING *table, |
| 168 | HA_CREATE_INFO *create_info, |
| 169 | List<Create_field> &create_fields, |
| 170 | uint keys, KEY *key_info, handler *db_file) |
| 171 | { |
| 172 | LEX_CSTRING str_db_type; |
| 173 | uint reclength, key_info_length, i; |
| 174 | ulong key_buff_length; |
| 175 | size_t filepos; |
| 176 | ulong data_offset; |
| 177 | uint options_len; |
| 178 | uint = 0; |
| 179 | uchar fileinfo[FRM_HEADER_SIZE],forminfo[FRM_FORMINFO_SIZE]; |
| 180 | const partition_info *part_info= IF_PARTITIONING(thd->work_part_info, 0); |
| 181 | bool error; |
| 182 | uchar *frm_ptr, *pos; |
| 183 | LEX_CUSTRING frm= {0,0}; |
| 184 | StringBuffer<MAX_FIELD_WIDTH> vcols; |
| 185 | DBUG_ENTER("build_frm_image" ); |
| 186 | |
| 187 | /* If fixed row records, we need one bit to check for deleted rows */ |
| 188 | if (!(create_info->table_options & HA_OPTION_PACK_RECORD)) |
| 189 | create_info->null_bits++; |
| 190 | data_offset= (create_info->null_bits + 7) / 8; |
| 191 | |
| 192 | sql_mode_t save_sql_mode= thd->variables.sql_mode; |
| 193 | thd->variables.sql_mode &= ~MODE_ANSI_QUOTES; |
| 194 | error= pack_vcols(&vcols, create_fields, create_info->check_constraint_list); |
| 195 | thd->variables.sql_mode= save_sql_mode; |
| 196 | |
| 197 | if (unlikely(error)) |
| 198 | DBUG_RETURN(frm); |
| 199 | |
| 200 | if (vcols.length()) |
| 201 | create_info->expression_length= vcols.length() + FRM_VCOL_NEW_BASE_SIZE; |
| 202 | |
| 203 | error= pack_header(thd, forminfo, create_fields, create_info, |
| 204 | (ulong)data_offset, db_file); |
| 205 | if (unlikely(error)) |
| 206 | DBUG_RETURN(frm); |
| 207 | |
| 208 | reclength= uint2korr(forminfo+266); |
| 209 | |
| 210 | /* Calculate extra data segment length */ |
| 211 | str_db_type= *hton_name(create_info->db_type); |
| 212 | /* str_db_type */ |
| 213 | create_info->extra_size= (uint)(2 + str_db_type.length + |
| 214 | 2 + create_info->connect_string.length); |
| 215 | /* |
| 216 | Partition: |
| 217 | Length of partition info = 4 byte |
| 218 | Potential NULL byte at end of partition info string = 1 byte |
| 219 | Indicator if auto-partitioned table = 1 byte |
| 220 | => Total 6 byte |
| 221 | */ |
| 222 | create_info->extra_size+= 6; |
| 223 | if (part_info) |
| 224 | create_info->extra_size+= (uint)part_info->part_info_len; |
| 225 | |
| 226 | for (i= 0; i < keys; i++) |
| 227 | { |
| 228 | if (key_info[i].parser_name) |
| 229 | create_info->extra_size+= (uint)key_info[i].parser_name->length + 1; |
| 230 | } |
| 231 | |
| 232 | options_len= engine_table_options_frm_length(create_info->option_list, |
| 233 | create_fields, |
| 234 | keys, key_info); |
| 235 | #ifdef HAVE_SPATIAL |
| 236 | gis_extra2_len= gis_field_options_image(NULL, create_fields); |
| 237 | #endif /*HAVE_SPATIAL*/ |
| 238 | DBUG_PRINT("info" , ("Options length: %u" , options_len)); |
| 239 | |
| 240 | if (validate_comment_length(thd, &create_info->comment, TABLE_COMMENT_MAXLEN, |
| 241 | ER_TOO_LONG_TABLE_COMMENT, table->str)) |
| 242 | DBUG_RETURN(frm); |
| 243 | /* |
| 244 | If table comment is longer than TABLE_COMMENT_INLINE_MAXLEN bytes, |
| 245 | store the comment in an extra segment (up to TABLE_COMMENT_MAXLEN bytes). |
| 246 | Pre 5.5, the limit was 60 characters, with no extra segment-handling. |
| 247 | */ |
| 248 | if (create_info->comment.length > TABLE_COMMENT_INLINE_MAXLEN) |
| 249 | { |
| 250 | forminfo[46]=255; |
| 251 | create_info->extra_size+= 2 + (uint)create_info->comment.length; |
| 252 | } |
| 253 | else |
| 254 | { |
| 255 | strmake((char*) forminfo+47, create_info->comment.str ? |
| 256 | create_info->comment.str : "" , create_info->comment.length); |
| 257 | forminfo[46]=(uchar) create_info->comment.length; |
| 258 | } |
| 259 | |
| 260 | if (!create_info->tabledef_version.str) |
| 261 | { |
| 262 | uchar *to= (uchar*) thd->alloc(MY_UUID_SIZE); |
| 263 | if (unlikely(!to)) |
| 264 | DBUG_RETURN(frm); |
| 265 | my_uuid(to); |
| 266 | create_info->tabledef_version.str= to; |
| 267 | create_info->tabledef_version.length= MY_UUID_SIZE; |
| 268 | } |
| 269 | DBUG_ASSERT(create_info->tabledef_version.length > 0); |
| 270 | DBUG_ASSERT(create_info->tabledef_version.length <= 255); |
| 271 | |
| 272 | prepare_frm_header(thd, reclength, fileinfo, create_info, keys, key_info); |
| 273 | |
| 274 | /* one byte for a type, one or three for a length */ |
| 275 | size_t = 1 + 1 + create_info->tabledef_version.length; |
| 276 | if (options_len) |
| 277 | extra2_size+= 1 + (options_len > 255 ? 3 : 1) + options_len; |
| 278 | |
| 279 | if (part_info) |
| 280 | extra2_size+= 1 + 1 + hton_name(part_info->default_engine_type)->length; |
| 281 | |
| 282 | if (gis_extra2_len) |
| 283 | extra2_size+= 1 + (gis_extra2_len > 255 ? 3 : 1) + gis_extra2_len; |
| 284 | |
| 285 | if (create_info->versioned()) |
| 286 | { |
| 287 | extra2_size+= 1 + 1 + 2 * sizeof(uint16); |
| 288 | } |
| 289 | |
| 290 | bool = has_extra2_field_flags(create_fields); |
| 291 | if (has_extra2_field_flags_) |
| 292 | { |
| 293 | extra2_size+= 1 + (create_fields.elements > 255 ? 3 : 1) + |
| 294 | create_fields.elements; |
| 295 | } |
| 296 | |
| 297 | key_buff_length= uint4korr(fileinfo+47); |
| 298 | |
| 299 | frm.length= FRM_HEADER_SIZE; // fileinfo; |
| 300 | frm.length+= extra2_size + 4; // mariadb extra2 frm segment |
| 301 | |
| 302 | int2store(fileinfo+4, extra2_size); |
| 303 | int2store(fileinfo+6, frm.length); // Position to key information |
| 304 | frm.length+= key_buff_length; |
| 305 | frm.length+= reclength; // row with default values |
| 306 | frm.length+= create_info->extra_size; |
| 307 | |
| 308 | filepos= frm.length; |
| 309 | frm.length+= FRM_FORMINFO_SIZE; // forminfo |
| 310 | frm.length+= packed_fields_length(create_fields); |
| 311 | frm.length+= create_info->expression_length; |
| 312 | |
| 313 | if (frm.length > FRM_MAX_SIZE || |
| 314 | create_info->expression_length > UINT_MAX32) |
| 315 | { |
| 316 | my_error(ER_TABLE_DEFINITION_TOO_BIG, MYF(0), table->str); |
| 317 | DBUG_RETURN(frm); |
| 318 | } |
| 319 | |
| 320 | frm_ptr= (uchar*) my_malloc(frm.length, MYF(MY_WME | MY_ZEROFILL | |
| 321 | MY_THREAD_SPECIFIC)); |
| 322 | if (!frm_ptr) |
| 323 | DBUG_RETURN(frm); |
| 324 | |
| 325 | /* write the extra2 segment */ |
| 326 | pos = frm_ptr + 64; |
| 327 | compile_time_assert(EXTRA2_TABLEDEF_VERSION != '/'); |
| 328 | pos= extra2_write(pos, EXTRA2_TABLEDEF_VERSION, |
| 329 | &create_info->tabledef_version); |
| 330 | |
| 331 | if (part_info) |
| 332 | pos= extra2_write(pos, EXTRA2_DEFAULT_PART_ENGINE, |
| 333 | hton_name(part_info->default_engine_type)); |
| 334 | |
| 335 | if (options_len) |
| 336 | { |
| 337 | *pos++= EXTRA2_ENGINE_TABLEOPTS; |
| 338 | pos= extra2_write_len(pos, options_len); |
| 339 | pos= engine_table_options_frm_image(pos, create_info->option_list, |
| 340 | create_fields, keys, key_info); |
| 341 | } |
| 342 | |
| 343 | #ifdef HAVE_SPATIAL |
| 344 | if (gis_extra2_len) |
| 345 | { |
| 346 | *pos= EXTRA2_GIS; |
| 347 | pos= extra2_write_len(pos+1, gis_extra2_len); |
| 348 | pos+= gis_field_options_image(pos, create_fields); |
| 349 | } |
| 350 | #endif /*HAVE_SPATIAL*/ |
| 351 | |
| 352 | if (create_info->versioned()) |
| 353 | { |
| 354 | *pos++= EXTRA2_PERIOD_FOR_SYSTEM_TIME; |
| 355 | *pos++= 2 * sizeof(uint16); |
| 356 | int2store(pos, vers_get_field(create_info, create_fields, ROW_START)); |
| 357 | pos+= sizeof(uint16); |
| 358 | int2store(pos, vers_get_field(create_info, create_fields, ROW_END)); |
| 359 | pos+= sizeof(uint16); |
| 360 | } |
| 361 | |
| 362 | if (has_extra2_field_flags_) |
| 363 | pos= extra2_write_field_properties(pos, create_fields); |
| 364 | |
| 365 | int4store(pos, filepos); // end of the extra2 segment |
| 366 | pos+= 4; |
| 367 | |
| 368 | DBUG_ASSERT(pos == frm_ptr + uint2korr(fileinfo+6)); |
| 369 | key_info_length= pack_keys(pos, keys, key_info, data_offset); |
| 370 | if (key_info_length > UINT_MAX16) |
| 371 | { |
| 372 | my_printf_error(ER_CANT_CREATE_TABLE, |
| 373 | "Cannot create table %`s: index information is too long. " |
| 374 | "Decrease number of indexes or use shorter index names or shorter comments." , |
| 375 | MYF(0), table->str); |
| 376 | goto err; |
| 377 | } |
| 378 | |
| 379 | int2store(forminfo+2, frm.length - filepos); |
| 380 | int4store(fileinfo+10, frm.length); |
| 381 | fileinfo[26]= (uchar) MY_TEST((create_info->max_rows == 1) && |
| 382 | (create_info->min_rows == 1) && (keys == 0)); |
| 383 | int2store(fileinfo+28,key_info_length); |
| 384 | |
| 385 | if (part_info) |
| 386 | { |
| 387 | fileinfo[61]= (uchar) ha_legacy_type(part_info->default_engine_type); |
| 388 | DBUG_PRINT("info" , ("part_db_type = %d" , fileinfo[61])); |
| 389 | } |
| 390 | |
| 391 | memcpy(frm_ptr, fileinfo, FRM_HEADER_SIZE); |
| 392 | |
| 393 | pos+= key_buff_length; |
| 394 | if (make_empty_rec(thd, pos, create_info->table_options, create_fields, |
| 395 | reclength, data_offset)) |
| 396 | goto err; |
| 397 | |
| 398 | pos+= reclength; |
| 399 | int2store(pos, create_info->connect_string.length); |
| 400 | pos+= 2; |
| 401 | memcpy(pos, create_info->connect_string.str, create_info->connect_string.length); |
| 402 | pos+= create_info->connect_string.length; |
| 403 | int2store(pos, str_db_type.length); |
| 404 | pos+= 2; |
| 405 | memcpy(pos, str_db_type.str, str_db_type.length); |
| 406 | pos+= str_db_type.length; |
| 407 | |
| 408 | if (part_info) |
| 409 | { |
| 410 | char auto_partitioned= part_info->is_auto_partitioned ? 1 : 0; |
| 411 | int4store(pos, part_info->part_info_len); |
| 412 | pos+= 4; |
| 413 | memcpy(pos, part_info->part_info_string, part_info->part_info_len + 1); |
| 414 | pos+= part_info->part_info_len + 1; |
| 415 | *pos++= auto_partitioned; |
| 416 | } |
| 417 | else |
| 418 | { |
| 419 | pos+= 6; |
| 420 | } |
| 421 | |
| 422 | for (i= 0; i < keys; i++) |
| 423 | { |
| 424 | if (key_info[i].parser_name) |
| 425 | { |
| 426 | memcpy(pos, key_info[i].parser_name->str, key_info[i].parser_name->length + 1); |
| 427 | pos+= key_info[i].parser_name->length + 1; |
| 428 | } |
| 429 | } |
| 430 | if (forminfo[46] == (uchar)255) // New style MySQL 5.5 table comment |
| 431 | { |
| 432 | int2store(pos, create_info->comment.length); |
| 433 | pos+=2; |
| 434 | memcpy(pos, create_info->comment.str, create_info->comment.length); |
| 435 | pos+= create_info->comment.length; |
| 436 | } |
| 437 | |
| 438 | memcpy(frm_ptr + filepos, forminfo, 288); |
| 439 | pos= frm_ptr + filepos + 288; |
| 440 | if (pack_fields(&pos, create_fields, create_info, data_offset)) |
| 441 | goto err; |
| 442 | |
| 443 | if (vcols.length()) |
| 444 | { |
| 445 | /* Store header for packed fields (extra space for future) */ |
| 446 | bzero(pos, FRM_VCOL_NEW_BASE_SIZE); |
| 447 | pos+= FRM_VCOL_NEW_BASE_SIZE; |
| 448 | memcpy(pos, vcols.ptr(), vcols.length()); |
| 449 | pos+= vcols.length(); |
| 450 | } |
| 451 | |
| 452 | { |
| 453 | /* |
| 454 | Restore all UCS2 intervals. |
| 455 | HEX representation of them is not needed anymore. |
| 456 | */ |
| 457 | List_iterator<Create_field> it(create_fields); |
| 458 | Create_field *field; |
| 459 | while ((field=it++)) |
| 460 | { |
| 461 | if (field->save_interval) |
| 462 | { |
| 463 | field->interval= field->save_interval; |
| 464 | field->save_interval= 0; |
| 465 | } |
| 466 | } |
| 467 | } |
| 468 | |
| 469 | frm.str= frm_ptr; |
| 470 | DBUG_RETURN(frm); |
| 471 | |
| 472 | err: |
| 473 | my_free(frm_ptr); |
| 474 | DBUG_RETURN(frm); |
| 475 | } |
| 476 | |
| 477 | |
| 478 | /** |
| 479 | Create a frm (table definition) file and the tables |
| 480 | |
| 481 | @param thd Thread handler |
| 482 | @param frm Binary frm image of the table to create |
| 483 | @param path Name of file (including database, without .frm) |
| 484 | @param db Data base name |
| 485 | @param table_name Table name |
| 486 | @param create_info create info parameters |
| 487 | @param file Handler to use or NULL if only frm needs to be created |
| 488 | |
| 489 | @retval 0 ok |
| 490 | @retval 1 error |
| 491 | */ |
| 492 | |
| 493 | int rea_create_table(THD *thd, LEX_CUSTRING *frm, |
| 494 | const char *path, const char *db, const char *table_name, |
| 495 | HA_CREATE_INFO *create_info, handler *file, |
| 496 | bool no_ha_create_table) |
| 497 | { |
| 498 | DBUG_ENTER("rea_create_table" ); |
| 499 | |
| 500 | if (no_ha_create_table) |
| 501 | { |
| 502 | if (writefrm(path, db, table_name, true, frm->str, frm->length)) |
| 503 | goto err_frm; |
| 504 | } |
| 505 | |
| 506 | if (thd->variables.keep_files_on_create) |
| 507 | create_info->options|= HA_CREATE_KEEP_FILES; |
| 508 | |
| 509 | if (file->ha_create_partitioning_metadata(path, NULL, CHF_CREATE_FLAG)) |
| 510 | goto err_part; |
| 511 | |
| 512 | if (!no_ha_create_table) |
| 513 | { |
| 514 | if (ha_create_table(thd, path, db, table_name, create_info, frm)) |
| 515 | goto err_part; |
| 516 | } |
| 517 | |
| 518 | DBUG_RETURN(0); |
| 519 | |
| 520 | err_part: |
| 521 | file->ha_create_partitioning_metadata(path, NULL, CHF_DELETE_FLAG); |
| 522 | err_frm: |
| 523 | deletefrm(path); |
| 524 | DBUG_RETURN(1); |
| 525 | } /* rea_create_table */ |
| 526 | |
| 527 | |
| 528 | /* Pack keyinfo and keynames to keybuff for save in form-file. */ |
| 529 | |
| 530 | static uint pack_keys(uchar *keybuff, uint key_count, KEY *keyinfo, |
| 531 | ulong data_offset) |
| 532 | { |
| 533 | uint key_parts,length; |
| 534 | uchar *pos, *keyname_pos; |
| 535 | KEY *key,*end; |
| 536 | KEY_PART_INFO *key_part,*key_part_end; |
| 537 | DBUG_ENTER("pack_keys" ); |
| 538 | |
| 539 | pos=keybuff+6; |
| 540 | key_parts=0; |
| 541 | for (key=keyinfo,end=keyinfo+key_count ; key != end ; key++) |
| 542 | { |
| 543 | int2store(pos, (key->flags ^ HA_NOSAME)); |
| 544 | int2store(pos+2,key->key_length); |
| 545 | pos[4]= (uchar) key->user_defined_key_parts; |
| 546 | pos[5]= (uchar) key->algorithm; |
| 547 | int2store(pos+6, key->block_size); |
| 548 | pos+=8; |
| 549 | key_parts+=key->user_defined_key_parts; |
| 550 | DBUG_PRINT("loop" , ("flags: %lu key_parts: %d key_part: %p" , |
| 551 | key->flags, key->user_defined_key_parts, |
| 552 | key->key_part)); |
| 553 | for (key_part=key->key_part,key_part_end=key_part+key->user_defined_key_parts ; |
| 554 | key_part != key_part_end ; |
| 555 | key_part++) |
| 556 | |
| 557 | { |
| 558 | uint offset; |
| 559 | DBUG_PRINT("loop" ,("field: %d startpos: %lu length: %d" , |
| 560 | key_part->fieldnr, key_part->offset + data_offset, |
| 561 | key_part->length)); |
| 562 | int2store(pos,key_part->fieldnr+1+FIELD_NAME_USED); |
| 563 | offset= (uint) (key_part->offset+data_offset+1); |
| 564 | int2store(pos+2, offset); |
| 565 | pos[4]=0; // Sort order |
| 566 | int2store(pos+5,key_part->key_type); |
| 567 | int2store(pos+7,key_part->length); |
| 568 | pos+=9; |
| 569 | } |
| 570 | } |
| 571 | /* Save keynames */ |
| 572 | keyname_pos=pos; |
| 573 | *pos++=(uchar) NAMES_SEP_CHAR; |
| 574 | for (key=keyinfo ; key != end ; key++) |
| 575 | { |
| 576 | uchar *tmp=(uchar*) strmov((char*) pos,key->name.str); |
| 577 | *tmp++= (uchar) NAMES_SEP_CHAR; |
| 578 | *tmp=0; |
| 579 | pos=tmp; |
| 580 | } |
| 581 | *(pos++)=0; |
| 582 | for (key=keyinfo,end=keyinfo+key_count ; key != end ; key++) |
| 583 | { |
| 584 | if (key->flags & HA_USES_COMMENT) |
| 585 | { |
| 586 | int2store(pos, key->comment.length); |
| 587 | uchar *tmp= (uchar*)strnmov((char*) pos+2,key->comment.str, |
| 588 | key->comment.length); |
| 589 | pos= tmp; |
| 590 | } |
| 591 | } |
| 592 | |
| 593 | if (key_count > 127 || key_parts > 127) |
| 594 | { |
| 595 | keybuff[0]= (key_count & 0x7f) | 0x80; |
| 596 | keybuff[1]= key_count >> 7; |
| 597 | int2store(keybuff+2,key_parts); |
| 598 | } |
| 599 | else |
| 600 | { |
| 601 | keybuff[0]=(uchar) key_count; |
| 602 | keybuff[1]=(uchar) key_parts; |
| 603 | keybuff[2]= keybuff[3]= 0; |
| 604 | } |
| 605 | length=(uint) (pos-keyname_pos); |
| 606 | int2store(keybuff+4,length); |
| 607 | DBUG_RETURN((uint) (pos-keybuff)); |
| 608 | } /* pack_keys */ |
| 609 | |
| 610 | |
| 611 | /** |
| 612 | Pack the expression (for GENERATED ALWAYS AS, DEFAULT, CHECK) |
| 613 | |
| 614 | The data is stored as: |
| 615 | 1 byte type (enum_vcol_info_type) |
| 616 | 2 bytes field_number |
| 617 | 2 bytes length of expression |
| 618 | 1 byte length of name |
| 619 | name |
| 620 | next bytes column expression (text data) |
| 621 | |
| 622 | @return 0 ok |
| 623 | @return 1 error (out of memory or wrong characters in expression) |
| 624 | */ |
| 625 | |
| 626 | static bool pack_expression(String *buf, Virtual_column_info *vcol, |
| 627 | uint field_nr, enum_vcol_info_type type) |
| 628 | { |
| 629 | if (buf->reserve(FRM_VCOL_NEW_HEADER_SIZE + vcol->name.length)) |
| 630 | return 1; |
| 631 | |
| 632 | buf->q_append((char) type); |
| 633 | buf->q_append2b(field_nr); |
| 634 | size_t len_off= buf->length(); |
| 635 | buf->q_append2b(0); // to be added later |
| 636 | buf->q_append((char)vcol->name.length); |
| 637 | buf->q_append(&vcol->name); |
| 638 | size_t expr_start= buf->length(); |
| 639 | vcol->print(buf); |
| 640 | size_t expr_len= buf->length() - expr_start; |
| 641 | if (expr_len >= 65536) |
| 642 | { |
| 643 | my_error(ER_EXPRESSION_IS_TOO_BIG, MYF(0), vcol_type_name(type)); |
| 644 | return 1; |
| 645 | } |
| 646 | int2store(buf->ptr() + len_off, expr_len); |
| 647 | return 0; |
| 648 | } |
| 649 | |
| 650 | |
| 651 | static bool pack_vcols(String *buf, List<Create_field> &create_fields, |
| 652 | List<Virtual_column_info> *check_constraint_list) |
| 653 | { |
| 654 | List_iterator<Create_field> it(create_fields); |
| 655 | Create_field *field; |
| 656 | |
| 657 | for (uint field_nr=0; (field= it++); field_nr++) |
| 658 | { |
| 659 | if (field->vcol_info) |
| 660 | if (pack_expression(buf, field->vcol_info, field_nr, |
| 661 | field->vcol_info->stored_in_db |
| 662 | ? VCOL_GENERATED_STORED : VCOL_GENERATED_VIRTUAL)) |
| 663 | return 1; |
| 664 | if (field->has_default_expression() && !field->has_default_now_unireg_check()) |
| 665 | if (pack_expression(buf, field->default_value, field_nr, VCOL_DEFAULT)) |
| 666 | return 1; |
| 667 | if (field->check_constraint) |
| 668 | if (pack_expression(buf, field->check_constraint, field_nr, |
| 669 | VCOL_CHECK_FIELD)) |
| 670 | return 1; |
| 671 | } |
| 672 | |
| 673 | List_iterator<Virtual_column_info> cit(*check_constraint_list); |
| 674 | Virtual_column_info *check; |
| 675 | while ((check= cit++)) |
| 676 | if (pack_expression(buf, check, UINT_MAX32, VCOL_CHECK_TABLE)) |
| 677 | return 1; |
| 678 | return 0; |
| 679 | } |
| 680 | |
| 681 | |
| 682 | /* Make formheader */ |
| 683 | |
| 684 | static bool pack_header(THD *thd, uchar *forminfo, |
| 685 | List<Create_field> &create_fields, |
| 686 | HA_CREATE_INFO *create_info, ulong data_offset, |
| 687 | handler *file) |
| 688 | { |
| 689 | uint int_count,int_length, int_parts; |
| 690 | uint time_stamp_pos,null_fields; |
| 691 | uint table_options= create_info->table_options; |
| 692 | size_t length, reclength, totlength, n_length, com_length; |
| 693 | DBUG_ENTER("pack_header" ); |
| 694 | |
| 695 | if (create_fields.elements > MAX_FIELDS) |
| 696 | { |
| 697 | my_message(ER_TOO_MANY_FIELDS, ER_THD(thd, ER_TOO_MANY_FIELDS), MYF(0)); |
| 698 | DBUG_RETURN(1); |
| 699 | } |
| 700 | |
| 701 | totlength= 0L; |
| 702 | reclength= data_offset; |
| 703 | int_count=int_parts=int_length=time_stamp_pos=null_fields=0; |
| 704 | com_length= 0; |
| 705 | n_length=2L; |
| 706 | create_info->field_check_constraints= 0; |
| 707 | |
| 708 | /* Check fields */ |
| 709 | List_iterator<Create_field> it(create_fields); |
| 710 | Create_field *field; |
| 711 | while ((field=it++)) |
| 712 | { |
| 713 | if (validate_comment_length(thd, &field->comment, COLUMN_COMMENT_MAXLEN, |
| 714 | ER_TOO_LONG_FIELD_COMMENT, |
| 715 | field->field_name.str)) |
| 716 | DBUG_RETURN(1); |
| 717 | |
| 718 | totlength+= (size_t)field->length; |
| 719 | com_length+= field->comment.length; |
| 720 | /* |
| 721 | We mark first TIMESTAMP field with NOW() in DEFAULT or ON UPDATE |
| 722 | as auto-update field. |
| 723 | */ |
| 724 | if (field->real_field_type() == MYSQL_TYPE_TIMESTAMP && |
| 725 | MTYP_TYPENR(field->unireg_check) != Field::NONE && |
| 726 | !time_stamp_pos) |
| 727 | time_stamp_pos= (uint) field->offset+ (uint) data_offset + 1; |
| 728 | length=field->pack_length; |
| 729 | if ((uint) field->offset+ (uint) data_offset+ length > reclength) |
| 730 | reclength=(uint) (field->offset+ data_offset + length); |
| 731 | n_length+= field->field_name.length + 1; |
| 732 | field->interval_id=0; |
| 733 | field->save_interval= 0; |
| 734 | if (field->interval) |
| 735 | { |
| 736 | uint old_int_count=int_count; |
| 737 | |
| 738 | if (field->charset->mbminlen > 1) |
| 739 | { |
| 740 | /* |
| 741 | Escape UCS2 intervals using HEX notation to avoid |
| 742 | problems with delimiters between enum elements. |
| 743 | As the original representation is still needed in |
| 744 | the function make_empty_rec to create a record of |
| 745 | filled with default values it is saved in save_interval |
| 746 | The HEX representation is created from this copy. |
| 747 | */ |
| 748 | field->save_interval= field->interval; |
| 749 | field->interval= (TYPELIB*) thd->alloc(sizeof(TYPELIB)); |
| 750 | *field->interval= *field->save_interval; |
| 751 | field->interval->type_names= |
| 752 | (const char **) thd->alloc(sizeof(char*) * |
| 753 | (field->interval->count+1)); |
| 754 | field->interval->type_names[field->interval->count]= 0; |
| 755 | field->interval->type_lengths= |
| 756 | (uint *) thd->alloc(sizeof(uint) * field->interval->count); |
| 757 | |
| 758 | for (uint pos= 0; pos < field->interval->count; pos++) |
| 759 | { |
| 760 | char *dst; |
| 761 | const char *src= field->save_interval->type_names[pos]; |
| 762 | size_t hex_length; |
| 763 | length= field->save_interval->type_lengths[pos]; |
| 764 | hex_length= length * 2; |
| 765 | field->interval->type_lengths[pos]= (uint)hex_length; |
| 766 | field->interval->type_names[pos]= dst= |
| 767 | (char*) thd->alloc(hex_length + 1); |
| 768 | octet2hex(dst, src, length); |
| 769 | } |
| 770 | } |
| 771 | |
| 772 | field->interval_id=get_interval_id(&int_count,create_fields,field); |
| 773 | if (old_int_count != int_count) |
| 774 | { |
| 775 | for (const char **pos=field->interval->type_names ; *pos ; pos++) |
| 776 | int_length+=(uint) strlen(*pos)+1; // field + suffix prefix |
| 777 | int_parts+=field->interval->count+1; |
| 778 | } |
| 779 | } |
| 780 | if (f_maybe_null(field->pack_flag)) |
| 781 | null_fields++; |
| 782 | if (field->check_constraint) |
| 783 | create_info->field_check_constraints++; |
| 784 | } |
| 785 | int_length+=int_count*2; // 255 prefix + 0 suffix |
| 786 | |
| 787 | /* Save values in forminfo */ |
| 788 | if (reclength > (ulong) file->max_record_length()) |
| 789 | { |
| 790 | my_error(ER_TOO_BIG_ROWSIZE, MYF(0), static_cast<long>(file->max_record_length())); |
| 791 | DBUG_RETURN(1); |
| 792 | } |
| 793 | |
| 794 | /* Hack to avoid bugs with small static rows in MySQL */ |
| 795 | reclength= MY_MAX(file->min_record_length(table_options), reclength); |
| 796 | length= n_length + create_fields.elements*FCOMP + FRM_FORMINFO_SIZE + |
| 797 | int_length + com_length + create_info->expression_length; |
| 798 | if (length > 65535L || int_count > 255) |
| 799 | { |
| 800 | my_message(ER_TOO_MANY_FIELDS, "Table definition is too large" , MYF(0)); |
| 801 | DBUG_RETURN(1); |
| 802 | } |
| 803 | |
| 804 | bzero((char*)forminfo,FRM_FORMINFO_SIZE); |
| 805 | int2store(forminfo,length); |
| 806 | int2store(forminfo+258,create_fields.elements); |
| 807 | // bytes 260-261 are unused |
| 808 | int2store(forminfo+262,totlength); |
| 809 | // bytes 264-265 are unused |
| 810 | int2store(forminfo+266,reclength); |
| 811 | int2store(forminfo+268,n_length); |
| 812 | int2store(forminfo+270,int_count); |
| 813 | int2store(forminfo+272,int_parts); |
| 814 | int2store(forminfo+274,int_length); |
| 815 | int2store(forminfo+276,time_stamp_pos); |
| 816 | int2store(forminfo+278,80); /* Columns needed */ |
| 817 | int2store(forminfo+280,22); /* Rows needed */ |
| 818 | int2store(forminfo+282,null_fields); |
| 819 | int2store(forminfo+284,com_length); |
| 820 | int2store(forminfo+286,create_info->expression_length); |
| 821 | DBUG_RETURN(0); |
| 822 | } /* pack_header */ |
| 823 | |
| 824 | |
| 825 | /* get each unique interval each own id */ |
| 826 | static uint get_interval_id(uint *int_count,List<Create_field> &create_fields, |
| 827 | Create_field *last_field) |
| 828 | { |
| 829 | List_iterator<Create_field> it(create_fields); |
| 830 | Create_field *field; |
| 831 | TYPELIB *interval=last_field->interval; |
| 832 | |
| 833 | while ((field=it++) != last_field) |
| 834 | { |
| 835 | if (field->interval_id && field->interval->count == interval->count) |
| 836 | { |
| 837 | const char **a,**b; |
| 838 | for (a=field->interval->type_names, b=interval->type_names ; |
| 839 | *a && !strcmp(*a,*b); |
| 840 | a++,b++) ; |
| 841 | |
| 842 | if (! *a) |
| 843 | { |
| 844 | return field->interval_id; // Re-use last interval |
| 845 | } |
| 846 | } |
| 847 | } |
| 848 | return ++*int_count; // New unique interval |
| 849 | } |
| 850 | |
| 851 | |
| 852 | static size_t packed_fields_length(List<Create_field> &create_fields) |
| 853 | { |
| 854 | Create_field *field; |
| 855 | size_t length= 0; |
| 856 | DBUG_ENTER("packed_fields_length" ); |
| 857 | |
| 858 | List_iterator<Create_field> it(create_fields); |
| 859 | uint int_count=0; |
| 860 | while ((field=it++)) |
| 861 | { |
| 862 | if (field->interval_id > int_count) |
| 863 | { |
| 864 | int_count= field->interval_id; |
| 865 | length++; |
| 866 | for (int i=0; field->interval->type_names[i]; i++) |
| 867 | { |
| 868 | length+= field->interval->type_lengths[i]; |
| 869 | length++; |
| 870 | } |
| 871 | length++; |
| 872 | } |
| 873 | |
| 874 | length+= FCOMP; |
| 875 | length+= field->field_name.length + 1; |
| 876 | length+= field->comment.length; |
| 877 | } |
| 878 | length+= 2; |
| 879 | DBUG_RETURN(length); |
| 880 | } |
| 881 | |
| 882 | /* Save fields, fieldnames and intervals */ |
| 883 | |
| 884 | static bool pack_fields(uchar **buff_arg, List<Create_field> &create_fields, |
| 885 | HA_CREATE_INFO *create_info, |
| 886 | ulong data_offset) |
| 887 | { |
| 888 | uchar *buff= *buff_arg; |
| 889 | uint int_count; |
| 890 | size_t = 0; |
| 891 | Create_field *field; |
| 892 | DBUG_ENTER("pack_fields" ); |
| 893 | |
| 894 | /* Write field info */ |
| 895 | List_iterator<Create_field> it(create_fields); |
| 896 | int_count=0; |
| 897 | while ((field=it++)) |
| 898 | { |
| 899 | uint recpos; |
| 900 | int2store(buff+3, field->length); |
| 901 | /* The +1 is here becasue the col offset in .frm file have offset 1 */ |
| 902 | recpos= field->offset+1 + (uint) data_offset; |
| 903 | int3store(buff+5,recpos); |
| 904 | int2store(buff+8,field->pack_flag); |
| 905 | buff[10]= (uchar) field->unireg_check; |
| 906 | buff[12]= (uchar) field->interval_id; |
| 907 | buff[13]= (uchar) field->real_field_type(); |
| 908 | if (field->real_field_type() == MYSQL_TYPE_GEOMETRY) |
| 909 | { |
| 910 | buff[11]= 0; |
| 911 | buff[14]= (uchar) field->geom_type; |
| 912 | #ifndef HAVE_SPATIAL |
| 913 | DBUG_ASSERT(0); // Should newer happen |
| 914 | #endif |
| 915 | } |
| 916 | else if (field->charset) |
| 917 | { |
| 918 | buff[11]= (uchar) (field->charset->number >> 8); |
| 919 | buff[14]= (uchar) field->charset->number; |
| 920 | } |
| 921 | else |
| 922 | { |
| 923 | buff[11]= buff[14]= 0; // Numerical |
| 924 | } |
| 925 | |
| 926 | int2store(buff+15, field->comment.length); |
| 927 | comment_length+= field->comment.length; |
| 928 | set_if_bigger(int_count,field->interval_id); |
| 929 | buff+= FCOMP; |
| 930 | } |
| 931 | |
| 932 | /* Write fieldnames */ |
| 933 | *buff++= NAMES_SEP_CHAR; |
| 934 | it.rewind(); |
| 935 | while ((field=it++)) |
| 936 | { |
| 937 | buff= (uchar*)strmov((char*) buff, field->field_name.str); |
| 938 | *buff++=NAMES_SEP_CHAR; |
| 939 | } |
| 940 | *buff++= 0; |
| 941 | |
| 942 | /* Write intervals */ |
| 943 | if (int_count) |
| 944 | { |
| 945 | it.rewind(); |
| 946 | int_count=0; |
| 947 | while ((field=it++)) |
| 948 | { |
| 949 | if (field->interval_id > int_count) |
| 950 | { |
| 951 | unsigned char sep= 0; |
| 952 | unsigned char occ[256]; |
| 953 | uint i; |
| 954 | unsigned char *val= NULL; |
| 955 | |
| 956 | bzero(occ, sizeof(occ)); |
| 957 | |
| 958 | for (i=0; (val= (unsigned char*) field->interval->type_names[i]); i++) |
| 959 | for (uint j = 0; j < field->interval->type_lengths[i]; j++) |
| 960 | occ[(unsigned int) (val[j])]= 1; |
| 961 | |
| 962 | if (!occ[(unsigned char)NAMES_SEP_CHAR]) |
| 963 | sep= (unsigned char) NAMES_SEP_CHAR; |
| 964 | else if (!occ[(unsigned int)',']) |
| 965 | sep= ','; |
| 966 | else |
| 967 | { |
| 968 | for (uint i=1; i<256; i++) |
| 969 | { |
| 970 | if(!occ[i]) |
| 971 | { |
| 972 | sep= i; |
| 973 | break; |
| 974 | } |
| 975 | } |
| 976 | |
| 977 | if (!sep) |
| 978 | { |
| 979 | /* disaster, enum uses all characters, none left as separator */ |
| 980 | my_message(ER_WRONG_FIELD_TERMINATORS, |
| 981 | ER(ER_WRONG_FIELD_TERMINATORS), |
| 982 | MYF(0)); |
| 983 | DBUG_RETURN(1); |
| 984 | } |
| 985 | } |
| 986 | |
| 987 | int_count= field->interval_id; |
| 988 | *buff++= sep; |
| 989 | for (int i=0; field->interval->type_names[i]; i++) |
| 990 | { |
| 991 | memcpy(buff, field->interval->type_names[i], field->interval->type_lengths[i]); |
| 992 | buff+= field->interval->type_lengths[i]; |
| 993 | *buff++= sep; |
| 994 | } |
| 995 | *buff++= 0; |
| 996 | } |
| 997 | } |
| 998 | } |
| 999 | if (comment_length) |
| 1000 | { |
| 1001 | it.rewind(); |
| 1002 | while ((field=it++)) |
| 1003 | { |
| 1004 | memcpy(buff, field->comment.str, field->comment.length); |
| 1005 | buff+= field->comment.length; |
| 1006 | } |
| 1007 | } |
| 1008 | *buff_arg= buff; |
| 1009 | DBUG_RETURN(0); |
| 1010 | } |
| 1011 | |
| 1012 | /* save an empty record on start of formfile */ |
| 1013 | |
| 1014 | static bool make_empty_rec(THD *thd, uchar *buff, uint table_options, |
| 1015 | List<Create_field> &create_fields, |
| 1016 | uint reclength, ulong data_offset) |
| 1017 | { |
| 1018 | int error= 0; |
| 1019 | uint null_count; |
| 1020 | uchar *null_pos; |
| 1021 | TABLE table; |
| 1022 | TABLE_SHARE share; |
| 1023 | Create_field *field; |
| 1024 | enum_check_fields old_count_cuted_fields= thd->count_cuted_fields; |
| 1025 | DBUG_ENTER("make_empty_rec" ); |
| 1026 | |
| 1027 | /* We need a table to generate columns for default values */ |
| 1028 | bzero((char*) &table, sizeof(table)); |
| 1029 | bzero((char*) &share, sizeof(share)); |
| 1030 | table.s= &share; |
| 1031 | |
| 1032 | table.in_use= thd; |
| 1033 | |
| 1034 | null_count=0; |
| 1035 | if (!(table_options & HA_OPTION_PACK_RECORD)) |
| 1036 | { |
| 1037 | null_count++; // Need one bit for delete mark |
| 1038 | *buff|= 1; |
| 1039 | } |
| 1040 | null_pos= buff; |
| 1041 | |
| 1042 | List_iterator<Create_field> it(create_fields); |
| 1043 | thd->count_cuted_fields= CHECK_FIELD_WARN; // To find wrong default values |
| 1044 | while ((field=it++)) |
| 1045 | { |
| 1046 | /* regfield don't have to be deleted as it's allocated on THD::mem_root */ |
| 1047 | Field *regfield= make_field(&share, thd->mem_root, |
| 1048 | buff+field->offset + data_offset, |
| 1049 | (uint32)field->length, |
| 1050 | null_pos + null_count / 8, |
| 1051 | null_count & 7, |
| 1052 | field->pack_flag, |
| 1053 | field->type_handler(), |
| 1054 | field->charset, |
| 1055 | field->geom_type, field->srid, |
| 1056 | field->unireg_check, |
| 1057 | field->save_interval ? field->save_interval |
| 1058 | : field->interval, |
| 1059 | &field->field_name, |
| 1060 | field->flags); |
| 1061 | if (!regfield) |
| 1062 | { |
| 1063 | error= 1; |
| 1064 | goto err; // End of memory |
| 1065 | } |
| 1066 | |
| 1067 | /* save_in_field() will access regfield->table->in_use */ |
| 1068 | regfield->init(&table); |
| 1069 | |
| 1070 | if (!(field->flags & NOT_NULL_FLAG)) |
| 1071 | { |
| 1072 | *regfield->null_ptr|= regfield->null_bit; |
| 1073 | null_count++; |
| 1074 | } |
| 1075 | |
| 1076 | if (field->real_field_type() == MYSQL_TYPE_BIT && |
| 1077 | !f_bit_as_char(field->pack_flag)) |
| 1078 | null_count+= field->length & 7; |
| 1079 | |
| 1080 | if (field->default_value && !field->default_value->flags && |
| 1081 | (!(field->flags & BLOB_FLAG) || |
| 1082 | field->real_field_type() == MYSQL_TYPE_GEOMETRY)) |
| 1083 | { |
| 1084 | Item *expr= field->default_value->expr; |
| 1085 | |
| 1086 | int res= !expr->fixed && // may be already fixed if ALTER TABLE |
| 1087 | expr->fix_fields(thd, &expr); |
| 1088 | if (!res) |
| 1089 | res= expr->save_in_field(regfield, 1); |
| 1090 | if (!res && (field->flags & BLOB_FLAG)) |
| 1091 | regfield->reset(); |
| 1092 | |
| 1093 | /* If not ok or warning of level 'note' */ |
| 1094 | if (res != 0 && res != 3) |
| 1095 | { |
| 1096 | my_error(ER_INVALID_DEFAULT, MYF(0), regfield->field_name.str); |
| 1097 | error= 1; |
| 1098 | delete regfield; //To avoid memory leak |
| 1099 | goto err; |
| 1100 | } |
| 1101 | delete regfield; //To avoid memory leak |
| 1102 | } |
| 1103 | else if (regfield->real_type() == MYSQL_TYPE_ENUM && |
| 1104 | (field->flags & NOT_NULL_FLAG)) |
| 1105 | { |
| 1106 | regfield->set_notnull(); |
| 1107 | regfield->store((longlong) 1, TRUE); |
| 1108 | } |
| 1109 | else |
| 1110 | regfield->reset(); |
| 1111 | } |
| 1112 | DBUG_ASSERT(data_offset == ((null_count + 7) / 8)); |
| 1113 | |
| 1114 | /* |
| 1115 | We need to set the unused bits to 1. If the number of bits is a multiple |
| 1116 | of 8 there are no unused bits. |
| 1117 | */ |
| 1118 | if (null_count & 7) |
| 1119 | *(null_pos + null_count / 8)|= ~(((uchar) 1 << (null_count & 7)) - 1); |
| 1120 | |
| 1121 | err: |
| 1122 | thd->count_cuted_fields= old_count_cuted_fields; |
| 1123 | DBUG_RETURN(error); |
| 1124 | } /* make_empty_rec */ |
| 1125 | |