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