~drizzle-trunk/drizzle/development

173.1.2 by Toru Maesaka
forgot to bzr-add new files in the previous push
1
/* - mode: c; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2
 *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
3
 *
4
 *  Copyright (C) 2008 MySQL
5
 *
6
 *  This program is free software; you can redistribute it and/or modify
7
 *  it under the terms of the GNU General Public License as published by
8
 *  the Free Software Foundation; either version 2 of the License, or
9
 *  (at your option) any later version.
10
 *
11
 *  This program is distributed in the hope that it will be useful,
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 *  GNU General Public License for more details.
15
 *
16
 *  You should have received a copy of the GNU General Public License
17
 *  along with this program; if not, write to the Free Software
18
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19
 */
20
21
#ifdef USE_PRAGMA_IMPLEMENTATION
22
#pragma implementation				// gcc: Class implementation
23
#endif
24
214 by Brian Aker
Rename of fields (fix issue with string and decimal .h clashing).
25
#include <drizzled/field/blob.h>
173.1.2 by Toru Maesaka
forgot to bzr-add new files in the previous push
26
27
#define BLOB_PACK_LENGTH_TO_MAX_LENGH(arg) \
28
((ulong) ((1LL << min(arg, 4) * 8) - 1LL))
29
30
/****************************************************************************
31
** blob type
32
** A blob is saved as a length and a pointer. The length is stored in the
33
** packlength slot and may be from 1-4.
34
****************************************************************************/
35
36
Field_blob::Field_blob(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg,
37
		       enum utype unireg_check_arg, const char *field_name_arg,
38
                       TABLE_SHARE *share, uint blob_pack_length,
39
		       CHARSET_INFO *cs)
40
  :Field_longstr(ptr_arg, BLOB_PACK_LENGTH_TO_MAX_LENGH(blob_pack_length),
41
                 null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg,
42
                 cs),
43
   packlength(blob_pack_length)
44
{
45
  flags|= BLOB_FLAG;
46
  share->blob_fields++;
47
  /* TODO: why do not fill table->s->blob_field array here? */
48
}
49
50
51
void Field_blob::store_length(uchar *i_ptr,
52
                              uint i_packlength,
53
                              uint32_t i_number,
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
54
                              bool low_byte_first __attribute__((unused)))
173.1.2 by Toru Maesaka
forgot to bzr-add new files in the previous push
55
{
56
  switch (i_packlength) {
57
  case 1:
58
    i_ptr[0]= (uchar) i_number;
59
    break;
60
  case 2:
61
#ifdef WORDS_BIGENDIAN
62
    if (low_byte_first)
63
    {
64
      int2store(i_ptr,(unsigned short) i_number);
65
    }
66
    else
67
#endif
68
      shortstore(i_ptr,(unsigned short) i_number);
69
    break;
70
  case 3:
71
    int3store(i_ptr,i_number);
72
    break;
73
  case 4:
74
#ifdef WORDS_BIGENDIAN
75
    if (low_byte_first)
76
    {
77
      int4store(i_ptr,i_number);
78
    }
79
    else
80
#endif
81
      longstore(i_ptr,i_number);
82
  }
83
}
84
85
86
uint32_t Field_blob::get_length(const uchar *pos,
87
                              uint packlength_arg,
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
88
                              bool low_byte_first __attribute__((unused)))
173.1.2 by Toru Maesaka
forgot to bzr-add new files in the previous push
89
{
90
  switch (packlength_arg) {
91
  case 1:
92
    return (uint32_t) pos[0];
93
  case 2:
94
    {
206 by Brian Aker
Removed final uint dead types.
95
      uint16_t tmp;
173.1.2 by Toru Maesaka
forgot to bzr-add new files in the previous push
96
#ifdef WORDS_BIGENDIAN
97
      if (low_byte_first)
98
	tmp=sint2korr(pos);
99
      else
100
#endif
101
	shortget(tmp,pos);
102
      return (uint32_t) tmp;
103
    }
104
  case 3:
105
    return (uint32_t) uint3korr(pos);
106
  case 4:
107
    {
108
      uint32_t tmp;
109
#ifdef WORDS_BIGENDIAN
110
      if (low_byte_first)
111
	tmp=uint4korr(pos);
112
      else
113
#endif
114
	longget(tmp,pos);
115
      return (uint32_t) tmp;
116
    }
117
  }
118
  return 0;					// Impossible
119
}
120
121
122
/**
123
  Put a blob length field into a record buffer.
124
125
  Depending on the maximum length of a blob, its length field is
126
  put into 1 to 4 bytes. This is a property of the blob object,
127
  described by 'packlength'.
128
129
  @param pos                 Pointer into the record buffer.
130
  @param length              The length value to put.
131
*/
132
133
void Field_blob::put_length(uchar *pos, uint32_t length)
134
{
135
  switch (packlength) {
136
  case 1:
137
    *pos= (char) length;
138
    break;
139
  case 2:
140
    int2store(pos, length);
141
    break;
142
  case 3:
143
    int3store(pos, length);
144
    break;
145
  case 4:
146
    int4store(pos, length);
147
    break;
148
  }
149
}
150
151
152
int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
153
{
154
  uint copy_length, new_length;
155
  const char *well_formed_error_pos;
156
  const char *cannot_convert_error_pos;
157
  const char *from_end_pos, *tmp;
158
  char buff[STRING_BUFFER_USUAL_SIZE];
159
  String tmpstr(buff,sizeof(buff), &my_charset_bin);
160
161
  if (!length)
162
  {
163
    bzero(ptr,Field_blob::pack_length());
164
    return 0;
165
  }
166
167
  if (from == value.ptr())
168
  {
169
    uint32_t dummy_offset;
170
    if (!String::needs_conversion(length, cs, field_charset, &dummy_offset))
171
    {
172
      Field_blob::store_length(length);
173
      bmove(ptr+packlength,(char*) &from,sizeof(char*));
174
      return 0;
175
    }
176
    if (tmpstr.copy(from, length, cs))
177
      goto oom_error;
178
    from= tmpstr.ptr();
179
  }
180
181
  new_length= min(max_data_length(), field_charset->mbmaxlen * length);
182
  if (value.alloc(new_length))
183
    goto oom_error;
184
185
186
  if (f_is_hex_escape(flags))
187
  {
188
    copy_length= my_copy_with_hex_escaping(field_charset,
189
                                           (char*) value.ptr(), new_length,
190
                                            from, length);
191
    Field_blob::store_length(copy_length);
192
    tmp= value.ptr();
193
    bmove(ptr + packlength, (uchar*) &tmp, sizeof(char*));
194
    return 0;
195
  }
196
  /*
197
    "length" is OK as "nchars" argument to well_formed_copy_nchars as this
198
    is never used to limit the length of the data. The cut of long data
199
    is done with the new_length value.
200
  */
201
  copy_length= well_formed_copy_nchars(field_charset,
202
                                       (char*) value.ptr(), new_length,
203
                                       cs, from, length,
204
                                       length,
205
                                       &well_formed_error_pos,
206
                                       &cannot_convert_error_pos,
207
                                       &from_end_pos);
208
209
  Field_blob::store_length(copy_length);
210
  tmp= value.ptr();
211
  bmove(ptr+packlength,(uchar*) &tmp,sizeof(char*));
212
213
  if (check_string_copy_error(this, well_formed_error_pos,
214
                              cannot_convert_error_pos, from + length, cs))
215
    return 2;
216
217
  return report_if_important_data(from_end_pos, from + length);
218
219
oom_error:
220
  /* Fatal OOM error */
221
  bzero(ptr,Field_blob::pack_length());
222
  return -1; 
223
}
224
225
226
int Field_blob::store(double nr)
227
{
228
  CHARSET_INFO *cs=charset();
229
  value.set_real(nr, NOT_FIXED_DEC, cs);
230
  return Field_blob::store(value.ptr(),(uint) value.length(), cs);
231
}
232
233
234
int Field_blob::store(int64_t nr, bool unsigned_val)
235
{
236
  CHARSET_INFO *cs=charset();
237
  value.set_int(nr, unsigned_val, cs);
238
  return Field_blob::store(value.ptr(), (uint) value.length(), cs);
239
}
240
241
242
double Field_blob::val_real(void)
243
{
244
  int not_used;
245
  char *end_not_used, *blob;
246
  uint32_t length;
247
  CHARSET_INFO *cs;
248
249
  memcpy_fixed(&blob,ptr+packlength,sizeof(char*));
250
  if (!blob)
251
    return 0.0;
252
  length= get_length(ptr);
253
  cs= charset();
254
  return my_strntod(cs, blob, length, &end_not_used, &not_used);
255
}
256
257
258
int64_t Field_blob::val_int(void)
259
{
260
  int not_used;
261
  char *blob;
262
  memcpy_fixed(&blob,ptr+packlength,sizeof(char*));
263
  if (!blob)
264
    return 0;
265
  uint32_t length=get_length(ptr);
266
  return my_strntoll(charset(),blob,length,10,NULL,&not_used);
267
}
268
269
String *Field_blob::val_str(String *val_buffer __attribute__((unused)),
270
			    String *val_ptr)
271
{
272
  char *blob;
273
  memcpy_fixed(&blob,ptr+packlength,sizeof(char*));
274
  if (!blob)
275
    val_ptr->set("",0,charset());	// A bit safer than ->length(0)
276
  else
277
    val_ptr->set((const char*) blob,get_length(ptr),charset());
278
  return val_ptr;
279
}
280
281
282
my_decimal *Field_blob::val_decimal(my_decimal *decimal_value)
283
{
284
  const char *blob;
285
  size_t length;
286
  memcpy_fixed(&blob, ptr+packlength, sizeof(const uchar*));
287
  if (!blob)
288
  {
289
    blob= "";
290
    length= 0;
291
  }
292
  else
293
    length= get_length(ptr);
294
295
  str2my_decimal(E_DEC_FATAL_ERROR, blob, length, charset(),
296
                 decimal_value);
297
  return decimal_value;
298
}
299
300
301
int Field_blob::cmp(const uchar *a,uint32_t a_length, const uchar *b,
302
		    uint32_t b_length)
303
{
304
  return field_charset->coll->strnncollsp(field_charset, 
305
                                          a, a_length, b, b_length,
306
                                          0);
307
}
308
309
310
int Field_blob::cmp_max(const uchar *a_ptr, const uchar *b_ptr,
311
                        uint max_length)
312
{
313
  uchar *blob1,*blob2;
314
  memcpy_fixed(&blob1,a_ptr+packlength,sizeof(char*));
315
  memcpy_fixed(&blob2,b_ptr+packlength,sizeof(char*));
316
  uint a_len= get_length(a_ptr), b_len= get_length(b_ptr);
317
  set_if_smaller(a_len, max_length);
318
  set_if_smaller(b_len, max_length);
319
  return Field_blob::cmp(blob1,a_len,blob2,b_len);
320
}
321
322
323
int Field_blob::cmp_binary(const uchar *a_ptr, const uchar *b_ptr,
324
			   uint32_t max_length)
325
{
326
  char *a,*b;
327
  uint diff;
328
  uint32_t a_length,b_length;
329
  memcpy_fixed(&a,a_ptr+packlength,sizeof(char*));
330
  memcpy_fixed(&b,b_ptr+packlength,sizeof(char*));
331
  a_length=get_length(a_ptr);
332
  if (a_length > max_length)
333
    a_length=max_length;
334
  b_length=get_length(b_ptr);
335
  if (b_length > max_length)
336
    b_length=max_length;
337
  diff=memcmp(a,b,min(a_length,b_length));
338
  return diff ? diff : (int) (a_length - b_length);
339
}
340
341
342
/* The following is used only when comparing a key */
343
344
uint Field_blob::get_key_image(uchar *buff,
345
                               uint length,
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
346
                               imagetype type_arg __attribute__((unused)))
173.1.2 by Toru Maesaka
forgot to bzr-add new files in the previous push
347
{
348
  uint32_t blob_length= get_length(ptr);
349
  uchar *blob;
350
351
  get_ptr(&blob);
352
  uint local_char_length= length / field_charset->mbmaxlen;
353
  local_char_length= my_charpos(field_charset, blob, blob + blob_length,
354
                          local_char_length);
355
  set_if_smaller(blob_length, local_char_length);
356
357
  if ((uint32_t) length > blob_length)
358
  {
359
    /*
360
      Must clear this as we do a memcmp in opt_range.cc to detect
361
      identical keys
362
    */
363
    bzero(buff+HA_KEY_BLOB_LENGTH+blob_length, (length-blob_length));
364
    length=(uint) blob_length;
365
  }
366
  int2store(buff,length);
367
  memcpy(buff+HA_KEY_BLOB_LENGTH, blob, length);
368
  return HA_KEY_BLOB_LENGTH+length;
369
}
370
371
372
void Field_blob::set_key_image(const uchar *buff,uint length)
373
{
374
  length= uint2korr(buff);
375
  (void) Field_blob::store((const char*) buff+HA_KEY_BLOB_LENGTH, length,
376
                           field_charset);
377
}
378
379
380
int Field_blob::key_cmp(const uchar *key_ptr, uint max_key_length)
381
{
382
  uchar *blob1;
383
  uint blob_length=get_length(ptr);
384
  memcpy_fixed(&blob1,ptr+packlength,sizeof(char*));
385
  CHARSET_INFO *cs= charset();
386
  uint local_char_length= max_key_length / cs->mbmaxlen;
387
  local_char_length= my_charpos(cs, blob1, blob1+blob_length,
388
                                local_char_length);
389
  set_if_smaller(blob_length, local_char_length);
390
  return Field_blob::cmp(blob1, blob_length,
391
			 key_ptr+HA_KEY_BLOB_LENGTH,
392
			 uint2korr(key_ptr));
393
}
394
395
int Field_blob::key_cmp(const uchar *a,const uchar *b)
396
{
397
  return Field_blob::cmp(a+HA_KEY_BLOB_LENGTH, uint2korr(a),
398
			 b+HA_KEY_BLOB_LENGTH, uint2korr(b));
399
}
400
401
402
/**
403
   Save the field metadata for blob fields.
404
405
   Saves the pack length in the first byte of the field metadata array
406
   at index of *metadata_ptr.
407
408
   @param   metadata_ptr   First byte of field metadata
409
410
   @returns number of bytes written to metadata_ptr
411
*/
412
int Field_blob::do_save_field_metadata(uchar *metadata_ptr)
413
{
414
  *metadata_ptr= pack_length_no_ptr();
415
  return 1;
416
}
417
418
419
uint32_t Field_blob::sort_length() const
420
{
421
  return (uint32_t) (current_thd->variables.max_sort_length + 
422
                   (field_charset == &my_charset_bin ? 0 : packlength));
423
}
424
425
426
void Field_blob::sort_string(uchar *to,uint length)
427
{
428
  uchar *blob;
429
  uint blob_length=get_length();
430
431
  if (!blob_length)
432
    bzero(to,length);
433
  else
434
  {
435
    if (field_charset == &my_charset_bin)
436
    {
437
      uchar *pos;
438
439
      /*
440
        Store length of blob last in blob to shorter blobs before longer blobs
441
      */
442
      length-= packlength;
443
      pos= to+length;
444
445
      switch (packlength) {
446
      case 1:
447
        *pos= (char) blob_length;
448
        break;
449
      case 2:
450
        mi_int2store(pos, blob_length);
451
        break;
452
      case 3:
453
        mi_int3store(pos, blob_length);
454
        break;
455
      case 4:
456
        mi_int4store(pos, blob_length);
457
        break;
458
      }
459
    }
460
    memcpy_fixed(&blob,ptr+packlength,sizeof(char*));
461
    
462
    blob_length=my_strnxfrm(field_charset,
463
                            to, length, blob, blob_length);
464
    assert(blob_length == length);
465
  }
466
}
467
468
469
void Field_blob::sql_type(String &res) const
470
{
471
  const char *str;
472
  uint length;
473
  switch (packlength) {
474
  default: str="tiny"; length=4; break;
475
  case 2:  str="";     length=0; break;
476
  case 3:  str="medium"; length= 6; break;
477
  case 4:  str="long";  length=4; break;
478
  }
479
  res.set_ascii(str,length);
480
  if (charset() == &my_charset_bin)
481
    res.append(STRING_WITH_LEN("blob"));
482
  else
483
  {
484
    res.append(STRING_WITH_LEN("text"));
485
  }
486
}
487
488
uchar *Field_blob::pack(uchar *to, const uchar *from,
489
                        uint max_length, bool low_byte_first)
490
{
491
  uchar *save= ptr;
492
  ptr= (uchar*) from;
493
  uint32_t length=get_length();			// Length of from string
494
495
  /*
496
    Store max length, which will occupy packlength bytes. If the max
497
    length given is smaller than the actual length of the blob, we
498
    just store the initial bytes of the blob.
499
  */
500
  store_length(to, packlength, min(length, max_length), low_byte_first);
501
502
  /*
503
    Store the actual blob data, which will occupy 'length' bytes.
504
   */
505
  if (length > 0)
506
  {
507
    get_ptr((uchar**) &from);
508
    memcpy(to+packlength, from,length);
509
  }
510
  ptr=save;					// Restore org row pointer
511
  return(to+packlength+length);
512
}
513
514
515
/**
516
   Unpack a blob field from row data.
517
518
   This method is used to unpack a blob field from a master whose size of 
519
   the field is less than that of the slave. Note: This method is included
520
   to satisfy inheritance rules, but is not needed for blob fields. It
521
   simply is used as a pass-through to the original unpack() method for
522
   blob fields.
523
524
   @param   to         Destination of the data
525
   @param   from       Source of the data
526
   @param   param_data @c true if base types should be stored in little-
527
                       endian format, @c false if native format should
528
                       be used.
529
530
   @return  New pointer into memory based on from + length of the data
531
*/
212.1.3 by Monty Taylor
Renamed __attribute__((__unused__)) to __attribute__((unused)).
532
const uchar *Field_blob::unpack(uchar *to __attribute__((unused)),
173.1.2 by Toru Maesaka
forgot to bzr-add new files in the previous push
533
                                const uchar *from,
534
                                uint param_data,
535
                                bool low_byte_first)
536
{
537
  uint const master_packlength=
538
    param_data > 0 ? param_data & 0xFF : packlength;
539
  uint32_t const length= get_length(from, master_packlength, low_byte_first);
540
  bitmap_set_bit(table->write_set, field_index);
541
  store(reinterpret_cast<const char*>(from) + master_packlength,
542
        length, field_charset);
543
  return(from + master_packlength + length);
544
}
545
546
/* Keys for blobs are like keys on varchars */
547
548
int Field_blob::pack_cmp(const uchar *a, const uchar *b, uint key_length_arg,
549
                         my_bool insert_or_update)
550
{
551
  uint a_length, b_length;
552
  if (key_length_arg > 255)
553
  {
554
    a_length=uint2korr(a); a+=2;
555
    b_length=uint2korr(b); b+=2;
556
  }
557
  else
558
  {
559
    a_length= (uint) *a++;
560
    b_length= (uint) *b++;
561
  }
562
  return field_charset->coll->strnncollsp(field_charset,
563
                                          a, a_length,
564
                                          b, b_length,
565
                                          insert_or_update);
566
}
567
568
569
int Field_blob::pack_cmp(const uchar *b, uint key_length_arg,
570
                         my_bool insert_or_update)
571
{
572
  uchar *a;
573
  uint a_length, b_length;
574
  memcpy_fixed(&a,ptr+packlength,sizeof(char*));
575
  if (!a)
576
    return key_length_arg > 0 ? -1 : 0;
577
578
  a_length= get_length(ptr);
579
  if (key_length_arg > 255)
580
  {
581
    b_length= uint2korr(b); b+=2;
582
  }
583
  else
584
    b_length= (uint) *b++;
585
  return field_charset->coll->strnncollsp(field_charset,
586
                                          a, a_length,
587
                                          b, b_length,
588
                                          insert_or_update);
589
}
590
591
/** Create a packed key that will be used for storage from a MySQL row. */
592
593
uchar *
594
Field_blob::pack_key(uchar *to, const uchar *from, uint max_length,
595
                     bool low_byte_first __attribute__((unused)))
596
{
597
  uchar *save= ptr;
598
  ptr= (uchar*) from;
599
  uint32_t length=get_length();        // Length of from string
600
  uint local_char_length= ((field_charset->mbmaxlen > 1) ?
601
                           max_length/field_charset->mbmaxlen : max_length);
602
  if (length)
603
    get_ptr((uchar**) &from);
604
  if (length > local_char_length)
605
    local_char_length= my_charpos(field_charset, from, from+length,
606
                                  local_char_length);
607
  set_if_smaller(length, local_char_length);
608
  *to++= (uchar) length;
609
  if (max_length > 255)				// 2 byte length
610
    *to++= (uchar) (length >> 8);
611
  memcpy(to, from, length);
612
  ptr=save;					// Restore org row pointer
613
  return to+length;
614
}
615
616
617
/**
618
  Unpack a blob key into a record buffer.
619
620
  A blob key has a maximum size of 64K-1.
621
  In its packed form, the length field is one or two bytes long,
622
  depending on 'max_length'.
623
  Depending on the maximum length of a blob, its length field is
624
  put into 1 to 4 bytes. This is a property of the blob object,
625
  described by 'packlength'.
626
  Blobs are internally stored apart from the record buffer, which
627
  contains a pointer to the blob buffer.
628
629
630
  @param to                          Pointer into the record buffer.
631
  @param from                        Pointer to the packed key.
632
  @param max_length                  Key length limit from key description.
633
634
  @return
635
    Pointer into 'from' past the last byte copied from packed key.
636
*/
637
638
const uchar *
639
Field_blob::unpack_key(uchar *to, const uchar *from, uint max_length,
640
                       bool low_byte_first __attribute__((unused)))
641
{
642
  /* get length of the blob key */
643
  uint32_t length= *from++;
644
  if (max_length > 255)
645
    length+= *from++ << 8;
646
647
  /* put the length into the record buffer */
648
  put_length(to, length);
649
650
  /* put the address of the blob buffer or NULL */
651
  if (length)
652
    memcpy_fixed(to + packlength, &from, sizeof(from));
653
  else
654
    bzero(to + packlength, sizeof(from));
655
656
  /* point to first byte of next field in 'from' */
657
  return from + length;
658
}
659
660
661
/** Create a packed key that will be used for storage from a MySQL key. */
662
663
uchar *
664
Field_blob::pack_key_from_key_image(uchar *to, const uchar *from, uint max_length,
665
                                    bool low_byte_first __attribute__((unused)))
666
{
667
  uint length=uint2korr(from);
668
  if (length > max_length)
669
    length=max_length;
670
  *to++= (char) (length & 255);
671
  if (max_length > 255)
672
    *to++= (char) (length >> 8);
673
  if (length)
674
    memcpy(to, from+HA_KEY_BLOB_LENGTH, length);
675
  return to+length;
676
}
677
678
679
uint Field_blob::packed_col_length(const uchar *data_ptr, uint length)
680
{
681
  if (length > 255)
682
    return uint2korr(data_ptr)+2;
683
  return (uint) *data_ptr + 1;
684
}
685
686
687
uint Field_blob::max_packed_col_length(uint max_length)
688
{
689
  return (max_length > 255 ? 2 : 1)+max_length;
690
}
691
692
693
uint Field_blob::is_equal(Create_field *new_field)
694
{
695
  if (compare_str_field_flags(new_field, flags))
696
    return 0;
697
698
  return ((new_field->sql_type == get_blob_type_from_length(max_data_length()))
699
          && new_field->charset == field_charset &&
700
          ((Field_blob *)new_field->field)->max_data_length() ==
701
          max_data_length());
702
}
703
704
705
/**
706
  maximum possible display length for blob.
707
708
  @return
709
    length
710
*/
711
712
uint32_t Field_blob::max_display_length()
713
{
714
  switch (packlength)
715
  {
716
  case 1:
717
    return 255 * field_charset->mbmaxlen;
718
  case 2:
719
    return 65535 * field_charset->mbmaxlen;
720
  case 3:
721
    return 16777215 * field_charset->mbmaxlen;
722
  case 4:
723
    return (uint32_t) 4294967295U;
724
  default:
725
    assert(0); // we should never go here
726
    return 0;
727
  }
728
}
729