~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to drizzled/field/fstring.cc

Merged build changes from Antony.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
25
#include <drizzled/field/fstring.h>
 
26
 
 
27
#define LONGLONG_TO_STRING_CONVERSION_BUFFER_SIZE 128
 
28
#define DECIMAL_TO_STRING_CONVERSION_BUFFER_SIZE 128
 
29
 
 
30
/****************************************************************************
 
31
** string type
 
32
** A string may be varchar or binary
 
33
****************************************************************************/
 
34
 
 
35
/* Copy a string and fill with space */
 
36
int Field_string::store(const char *from,uint length,CHARSET_INFO *cs)
 
37
{
 
38
  uint copy_length;
 
39
  const char *well_formed_error_pos;
 
40
  const char *cannot_convert_error_pos;
 
41
  const char *from_end_pos;
 
42
 
 
43
  /* See the comment for Field_long::store(long long) */
 
44
  assert(table->in_use == current_thd);
 
45
 
 
46
  copy_length= well_formed_copy_nchars(field_charset,
 
47
                                       (char*) ptr, field_length,
 
48
                                       cs, from, length,
 
49
                                       field_length / field_charset->mbmaxlen,
 
50
                                       &well_formed_error_pos,
 
51
                                       &cannot_convert_error_pos,
 
52
                                       &from_end_pos);
 
53
 
 
54
  /* Append spaces if the string was shorter than the field. */
 
55
  if (copy_length < field_length)
 
56
    field_charset->cset->fill(field_charset,(char*) ptr+copy_length,
 
57
                              field_length-copy_length,
 
58
                              field_charset->pad_char);
 
59
 
 
60
  if (check_string_copy_error(this, well_formed_error_pos,
 
61
                              cannot_convert_error_pos, from + length, cs))
 
62
    return 2;
 
63
 
 
64
  return report_if_important_data(from_end_pos, from + length);
 
65
}
 
66
 
 
67
 
 
68
int Field_string::store(int64_t nr, bool unsigned_val)
 
69
{
 
70
  char buff[64];
 
71
  int  l;
 
72
  CHARSET_INFO *cs=charset();
 
73
  l= (cs->cset->int64_t10_to_str)(cs,buff,sizeof(buff),
 
74
                                   unsigned_val ? 10 : -10, nr);
 
75
  return Field_string::store(buff,(uint)l,cs);
 
76
}
 
77
 
 
78
 
 
79
double Field_string::val_real(void)
 
80
{
 
81
  int error;
 
82
  char *end;
 
83
  CHARSET_INFO *cs= charset();
 
84
  double result;
 
85
  
 
86
  result=  my_strntod(cs,(char*) ptr,field_length,&end,&error);
 
87
  if (!table->in_use->no_errors &&
 
88
      (error || (field_length != (uint32_t)(end - (char*) ptr) && 
 
89
                 !check_if_only_end_space(cs, end,
 
90
                                          (char*) ptr + field_length))))
 
91
  {
 
92
    char buf[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE];
 
93
    String tmp(buf, sizeof(buf), cs);
 
94
    tmp.copy((char*) ptr, field_length, cs);
 
95
    push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
 
96
                        ER_TRUNCATED_WRONG_VALUE, 
 
97
                        ER(ER_TRUNCATED_WRONG_VALUE),
 
98
                        "DOUBLE", tmp.c_ptr());
 
99
  }
 
100
  return result;
 
101
}
 
102
 
 
103
 
 
104
int64_t Field_string::val_int(void)
 
105
{
 
106
  int error;
 
107
  char *end;
 
108
  CHARSET_INFO *cs= charset();
 
109
  int64_t result;
 
110
 
 
111
  result= my_strntoll(cs, (char*) ptr,field_length,10,&end,&error);
 
112
  if (!table->in_use->no_errors &&
 
113
      (error || (field_length != (uint32_t)(end - (char*) ptr) && 
 
114
                 !check_if_only_end_space(cs, end,
 
115
                                          (char*) ptr + field_length))))
 
116
  {
 
117
    char buf[LONGLONG_TO_STRING_CONVERSION_BUFFER_SIZE];
 
118
    String tmp(buf, sizeof(buf), cs);
 
119
    tmp.copy((char*) ptr, field_length, cs);
 
120
    push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
 
121
                        ER_TRUNCATED_WRONG_VALUE, 
 
122
                        ER(ER_TRUNCATED_WRONG_VALUE),
 
123
                        "INTEGER", tmp.c_ptr());
 
124
  }
 
125
  return result;
 
126
}
 
127
 
 
128
 
 
129
String *Field_string::val_str(String *val_buffer __attribute__((unused)),
 
130
                              String *val_ptr)
 
131
{
 
132
  /* See the comment for Field_long::store(long long) */
 
133
  assert(table->in_use == current_thd);
 
134
  uint length;
 
135
  if (table->in_use->variables.sql_mode &
 
136
      MODE_PAD_CHAR_TO_FULL_LENGTH)
 
137
    length= my_charpos(field_charset, ptr, ptr + field_length, field_length);
 
138
  else
 
139
    length= field_charset->cset->lengthsp(field_charset, (const char*) ptr,
 
140
                                          field_length);
 
141
  val_ptr->set((const char*) ptr, length, field_charset);
 
142
  return val_ptr;
 
143
}
 
144
 
 
145
 
 
146
my_decimal *Field_string::val_decimal(my_decimal *decimal_value)
 
147
{
 
148
  int err= str2my_decimal(E_DEC_FATAL_ERROR, (char*) ptr, field_length,
 
149
                          charset(), decimal_value);
 
150
  if (!table->in_use->no_errors && err)
 
151
  {
 
152
    char buf[DECIMAL_TO_STRING_CONVERSION_BUFFER_SIZE];
 
153
    CHARSET_INFO *cs= charset();
 
154
    String tmp(buf, sizeof(buf), cs);
 
155
    tmp.copy((char*) ptr, field_length, cs);
 
156
    push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
 
157
                        ER_TRUNCATED_WRONG_VALUE, 
 
158
                        ER(ER_TRUNCATED_WRONG_VALUE),
 
159
                        "DECIMAL", tmp.c_ptr());
 
160
  }
 
161
 
 
162
  return decimal_value;
 
163
}
 
164
 
 
165
 
 
166
int Field_string::cmp(const uchar *a_ptr, const uchar *b_ptr)
 
167
{
 
168
  uint a_len, b_len;
 
169
 
 
170
  if (field_charset->mbmaxlen != 1)
 
171
  {
 
172
    uint char_len= field_length/field_charset->mbmaxlen;
 
173
    a_len= my_charpos(field_charset, a_ptr, a_ptr + field_length, char_len);
 
174
    b_len= my_charpos(field_charset, b_ptr, b_ptr + field_length, char_len);
 
175
  }
 
176
  else
 
177
    a_len= b_len= field_length;
 
178
  /*
 
179
    We have to remove end space to be able to compare multi-byte-characters
 
180
    like in latin_de 'ae' and 0xe4
 
181
  */
 
182
  return field_charset->coll->strnncollsp(field_charset,
 
183
                                          a_ptr, a_len,
 
184
                                          b_ptr, b_len,
 
185
                                          0);
 
186
}
 
187
 
 
188
 
 
189
void Field_string::sort_string(uchar *to,uint length)
 
190
{
 
191
  uint tmp= my_strnxfrm(field_charset,
 
192
                                 to, length,
 
193
                                 ptr, field_length);
 
194
  assert(tmp == length);
 
195
}
 
196
 
 
197
 
 
198
void Field_string::sql_type(String &res) const
 
199
{
 
200
  THD *thd= table->in_use;
 
201
  CHARSET_INFO *cs=res.charset();
 
202
  ulong length;
 
203
 
 
204
  length= cs->cset->snprintf(cs,(char*) res.ptr(),
 
205
                             res.alloced_length(), "%s(%d)",
 
206
                             ((type() == DRIZZLE_TYPE_VAR_STRING &&
 
207
                               !thd->variables.new_mode) ?
 
208
                              (has_charset() ? "varchar" : "varbinary") :
 
209
                              (has_charset() ? "char" : "binary")),
 
210
                             (int) field_length / charset()->mbmaxlen);
 
211
  res.length(length);
 
212
  if ((thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) &&
 
213
      has_charset() && (charset()->state & MY_CS_BINSORT))
 
214
    res.append(STRING_WITH_LEN(" binary"));
 
215
}
 
216
 
 
217
 
 
218
uchar *Field_string::pack(uchar *to, const uchar *from,
 
219
                          uint max_length,
 
220
                          bool low_byte_first __attribute__((unused)))
 
221
{
 
222
  uint length=      min(field_length,max_length);
 
223
  uint local_char_length= max_length/field_charset->mbmaxlen;
 
224
  if (length > local_char_length)
 
225
    local_char_length= my_charpos(field_charset, from, from+length,
 
226
                                  local_char_length);
 
227
  set_if_smaller(length, local_char_length);
 
228
  while (length && from[length-1] == field_charset->pad_char)
 
229
    length--;
 
230
 
 
231
  // Length always stored little-endian
 
232
  *to++= (uchar) length;
 
233
  if (field_length > 255)
 
234
    *to++= (uchar) (length >> 8);
 
235
 
 
236
  // Store the actual bytes of the string
 
237
  memcpy(to, from, length);
 
238
  return to+length;
 
239
}
 
240
 
 
241
 
 
242
/**
 
243
   Unpack a string field from row data.
 
244
 
 
245
   This method is used to unpack a string field from a master whose size 
 
246
   of the field is less than that of the slave. Note that there can be a
 
247
   variety of field types represented with this class. Certain types like
 
248
   ENUM or SET are processed differently. Hence, the upper byte of the 
 
249
   @c param_data argument contains the result of field->real_type() from
 
250
   the master.
 
251
 
 
252
   @param   to         Destination of the data
 
253
   @param   from       Source of the data
 
254
   @param   param_data Real type (upper) and length (lower) values
 
255
 
 
256
   @return  New pointer into memory based on from + length of the data
 
257
*/
 
258
const uchar *
 
259
Field_string::unpack(uchar *to,
 
260
                     const uchar *from,
 
261
                     uint param_data,
 
262
                     bool low_byte_first __attribute__((unused)))
 
263
{
 
264
  uint from_length=
 
265
    param_data ? min(param_data & 0x00ff, field_length) : field_length;
 
266
  uint length;
 
267
 
 
268
  if (from_length > 255)
 
269
  {
 
270
    length= uint2korr(from);
 
271
    from+= 2;
 
272
  }
 
273
  else
 
274
    length= (uint) *from++;
 
275
 
 
276
  memcpy(to, from, length);
 
277
  // Pad the string with the pad character of the fields charset
 
278
  memset(to + length, field_charset->pad_char, field_length - length);
 
279
  return from+length;
 
280
}
 
281
 
 
282
 
 
283
/**
 
284
   Save the field metadata for string fields.
 
285
 
 
286
   Saves the real type in the first byte and the field length in the 
 
287
   second byte of the field metadata array at index of *metadata_ptr and
 
288
   *(metadata_ptr + 1).
 
289
 
 
290
   @param   metadata_ptr   First byte of field metadata
 
291
 
 
292
   @returns number of bytes written to metadata_ptr
 
293
*/
 
294
int Field_string::do_save_field_metadata(uchar *metadata_ptr)
 
295
{
 
296
  *metadata_ptr= real_type();
 
297
  *(metadata_ptr + 1)= field_length;
 
298
  return 2;
 
299
}
 
300
 
 
301
 
 
302
/*
 
303
  Compare two packed keys
 
304
 
 
305
  SYNOPSIS
 
306
    pack_cmp()
 
307
     a                  New key
 
308
     b                  Original key
 
309
     length             Key length
 
310
     insert_or_update   1 if this is an insert or update
 
311
 
 
312
  RETURN
 
313
    < 0   a < b
 
314
    0     a = b
 
315
    > 0   a > b
 
316
*/
 
317
 
 
318
int Field_string::pack_cmp(const uchar *a, const uchar *b, uint length,
 
319
                           my_bool insert_or_update)
 
320
{
 
321
  uint a_length, b_length;
 
322
  if (length > 255)
 
323
  {
 
324
    a_length= uint2korr(a);
 
325
    b_length= uint2korr(b);
 
326
    a+= 2;
 
327
    b+= 2;
 
328
  }
 
329
  else
 
330
  {
 
331
    a_length= (uint) *a++;
 
332
    b_length= (uint) *b++;
 
333
  }
 
334
  return field_charset->coll->strnncollsp(field_charset,
 
335
                                          a, a_length,
 
336
                                          b, b_length,
 
337
                                          insert_or_update);
 
338
}
 
339
 
 
340
 
 
341
/**
 
342
  Compare a packed key against row.
 
343
 
 
344
  @param key                    Original key
 
345
  @param length         Key length. (May be less than field length)
 
346
  @param insert_or_update       1 if this is an insert or update
 
347
 
 
348
  @return
 
349
    < 0   row < key
 
350
  @return
 
351
    0     row = key
 
352
  @return
 
353
    > 0   row > key
 
354
*/
 
355
 
 
356
int Field_string::pack_cmp(const uchar *key, uint length,
 
357
                           my_bool insert_or_update)
 
358
{
 
359
  uint row_length, local_key_length;
 
360
  uchar *end;
 
361
  if (length > 255)
 
362
  {
 
363
    local_key_length= uint2korr(key);
 
364
    key+= 2;
 
365
  }
 
366
  else
 
367
    local_key_length= (uint) *key++;
 
368
  
 
369
  /* Only use 'length' of key, not field_length */
 
370
  end= ptr + length;
 
371
  while (end > ptr && end[-1] == ' ')
 
372
    end--;
 
373
  row_length= (uint) (end - ptr);
 
374
 
 
375
  return field_charset->coll->strnncollsp(field_charset,
 
376
                                          ptr, row_length,
 
377
                                          key, local_key_length,
 
378
                                          insert_or_update);
 
379
}
 
380
 
 
381
 
 
382
uint Field_string::packed_col_length(const uchar *data_ptr, uint length)
 
383
{
 
384
  if (length > 255)
 
385
    return uint2korr(data_ptr)+2;
 
386
  return (uint) *data_ptr + 1;
 
387
}
 
388
 
 
389
 
 
390
uint Field_string::max_packed_col_length(uint max_length)
 
391
{
 
392
  return (max_length > 255 ? 2 : 1)+max_length;
 
393
}
 
394
 
 
395
 
 
396
uint Field_string::get_key_image(uchar *buff,
 
397
                                 uint length,
 
398
                                 imagetype type_arg __attribute__((unused)))
 
399
{
 
400
  uint bytes = my_charpos(field_charset, (char*) ptr,
 
401
                          (char*) ptr + field_length,
 
402
                          length / field_charset->mbmaxlen);
 
403
  memcpy(buff, ptr, bytes);
 
404
  if (bytes < length)
 
405
    field_charset->cset->fill(field_charset, (char*) buff + bytes,
 
406
                              length - bytes, field_charset->pad_char);
 
407
  return bytes;
 
408
}
 
409
 
 
410
 
 
411
Field *Field_string::new_field(MEM_ROOT *root, struct st_table *new_table,
 
412
                               bool keep_type)
 
413
{
 
414
  Field *field;
 
415
  if (type() != DRIZZLE_TYPE_VAR_STRING || keep_type)
 
416
    field= Field::new_field(root, new_table, keep_type);
 
417
  else if ((field= new Field_varstring(field_length, maybe_null(), field_name,
 
418
                                       new_table->s, charset())))
 
419
  {
 
420
    /*
 
421
      Old VARCHAR field which should be modified to a VARCHAR on copy
 
422
      This is done to ensure that ALTER TABLE will convert old VARCHAR fields
 
423
      to now VARCHAR fields.
 
424
    */
 
425
    field->init(new_table);
 
426
    /*
 
427
      Normally orig_table is different from table only if field was created
 
428
      via ::new_field.  Here we alter the type of field, so ::new_field is
 
429
      not applicable. But we still need to preserve the original field
 
430
      metadata for the client-server protocol.
 
431
    */
 
432
    field->orig_table= orig_table;
 
433
  }
 
434
  return field;
 
435
}
 
436
 
 
437