~drizzle-trunk/drizzle/development

584.4.1 by Monty Taylor
Split out DTCollation.
1
/* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2
 *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
3
 *
4
 *  Copyright (C) 2008 Sun Microsystems
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; version 2 of the License.
9
 *
10
 *  This program is distributed in the hope that it will be useful,
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 *  GNU General Public License for more details.
14
 *
15
 *  You should have received a copy of the GNU General Public License
16
 *  along with this program; if not, write to the Free Software
17
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
 */
19
20
#include <drizzled/server_includes.h>
584.4.7 by Monty Taylor
Removed a big bank of includes from item.h.
21
#include <drizzled/dtcollation.h>
22
584.4.1 by Monty Taylor
Split out DTCollation.
23
#include <drizzled/definitions.h>
24
#include <mysys/my_sys.h>
25
#include <mystrings/m_ctype.h>
26
#include <drizzled/error.h>
584.4.7 by Monty Taylor
Removed a big bank of includes from item.h.
27
#include <drizzled/functions/str/conv_charset.h>
584.1.15 by Monty Taylor
The mega-patch from hell. Renamed sql_class to session (since that's what it is) and removed it and field and table from common_includes.
28
#include <drizzled/session.h>
584.4.1 by Monty Taylor
Split out DTCollation.
29
30
31
static bool
32
left_is_superset(const DTCollation &left, const DTCollation &right)
33
{
34
  /* Allow convert to Unicode */
35
  if (left.collation->state & MY_CS_UNICODE &&
36
      (left.derivation < right.derivation ||
37
       (left.derivation == right.derivation &&
38
        !(right.collation->state & MY_CS_UNICODE))))
39
    return true;
40
  /* Allow convert from ASCII */
41
  if (right.repertoire == MY_REPERTOIRE_ASCII &&
42
      (left.derivation < right.derivation ||
43
       (left.derivation == right.derivation &&
44
        !(left.repertoire == MY_REPERTOIRE_ASCII))))
45
    return true;
46
  /* Disallow conversion otherwise */
47
  return false;
48
}
49
50
51
DTCollation::DTCollation()
52
{
53
  collation= &my_charset_bin;
54
  derivation= DERIVATION_NONE;
55
  repertoire= MY_REPERTOIRE_UNICODE30;
56
}
57
58
59
DTCollation::DTCollation(const CHARSET_INFO * const collation_arg,
60
                         Derivation derivation_arg)
61
{
62
  collation= collation_arg;
63
  derivation= derivation_arg;
64
  set_repertoire_from_charset(collation_arg);
65
}
66
void DTCollation::set_repertoire_from_charset(const CHARSET_INFO * const cs)
67
{
68
  repertoire= cs->state & MY_CS_PUREASCII ?
69
    MY_REPERTOIRE_ASCII : MY_REPERTOIRE_UNICODE30;
70
}
71
72
73
void DTCollation::set(DTCollation &dt)
74
{
75
  collation= dt.collation;
76
  derivation= dt.derivation;
77
  repertoire= dt.repertoire;
78
}
79
80
81
void DTCollation::set(const CHARSET_INFO * const collation_arg,
82
                      Derivation derivation_arg)
83
{
84
  collation= collation_arg;
85
  derivation= derivation_arg;
86
  set_repertoire_from_charset(collation_arg);
87
}
88
89
90
void DTCollation::set(const CHARSET_INFO * const collation_arg,
91
                      Derivation derivation_arg,
92
                      uint32_t repertoire_arg)
93
{
94
  collation= collation_arg;
95
  derivation= derivation_arg;
96
  repertoire= repertoire_arg;
97
}
98
99
100
void DTCollation::set(const CHARSET_INFO * const collation_arg)
101
{
102
  collation= collation_arg;
103
  set_repertoire_from_charset(collation_arg);
104
}
105
106
107
void DTCollation::set(Derivation derivation_arg)
108
{
109
  derivation= derivation_arg;
110
}
111
112
113
bool DTCollation::aggregate(DTCollation &dt, uint32_t flags)
114
{
115
  if (!my_charset_same(collation, dt.collation))
116
  {
117
    /*
118
      We do allow to use binary strings (like BLOBS)
119
      together with character strings.
120
      Binaries have more precedence than a character
121
      string of the same derivation.
122
    */
123
    if (collation == &my_charset_bin)
124
    {
125
      if (derivation <= dt.derivation)
126
        ; // Do nothing
127
      else
128
      {
129
        set(dt);
130
      }
131
    }
132
    else if (dt.collation == &my_charset_bin)
133
    {
134
      if (dt.derivation <= derivation)
135
      {
136
        set(dt);
137
      }
138
      else
139
      {
140
        // Do nothing
141
      }
142
    }
143
    else if ((flags & MY_COLL_ALLOW_SUPERSET_CONV) &&
144
             left_is_superset(*this, dt))
145
    {
146
      // Do nothing
147
    }
148
    else if ((flags & MY_COLL_ALLOW_SUPERSET_CONV) &&
149
             left_is_superset(dt, *this))
150
    {
151
      set(dt);
152
    }
153
    else if ((flags & MY_COLL_ALLOW_COERCIBLE_CONV) &&
154
             derivation < dt.derivation &&
155
             dt.derivation >= DERIVATION_SYSCONST)
156
    {
157
      // Do nothing;
158
    }
159
    else if ((flags & MY_COLL_ALLOW_COERCIBLE_CONV) &&
160
             dt.derivation < derivation &&
161
             derivation >= DERIVATION_SYSCONST)
162
    {
163
      set(dt);
164
    }
165
    else
166
    {
167
      // Cannot apply conversion
168
      set(0, DERIVATION_NONE, 0);
169
      return 1;
170
    }
171
  }
172
  else if (derivation < dt.derivation)
173
  {
174
    // Do nothing
175
  }
176
  else if (dt.derivation < derivation)
177
  {
178
    set(dt);
179
  }
180
  else
181
  {
182
    if (collation == dt.collation)
183
    {
184
      // Do nothing
185
    }
186
    else
187
    {
188
      if (derivation == DERIVATION_EXPLICIT)
189
      {
190
        set(0, DERIVATION_NONE, 0);
191
        return 1;
192
      }
193
      if (collation->state & MY_CS_BINSORT)
194
        return 0;
195
      if (dt.collation->state & MY_CS_BINSORT)
196
      {
197
        set(dt);
198
        return 0;
199
      }
200
      const CHARSET_INFO * const bin= get_charset_by_csname(collation->csname,
201
                                                            MY_CS_BINSORT,
202
                                                            MYF(0));
203
      set(bin, DERIVATION_NONE);
204
    }
205
  }
206
  repertoire|= dt.repertoire;
207
  return 0;
208
}
209
210
211
bool DTCollation::set(DTCollation &dt1, DTCollation &dt2, uint32_t flags)
212
{ set(dt1); return aggregate(dt2, flags); }
213
214
215
const char *DTCollation::derivation_name() const
216
{
217
  switch(derivation)
218
  {
219
  case DERIVATION_IGNORABLE: return "IGNORABLE";
220
  case DERIVATION_COERCIBLE: return "COERCIBLE";
221
  case DERIVATION_IMPLICIT:  return "IMPLICIT";
222
  case DERIVATION_SYSCONST:  return "SYSCONST";
223
  case DERIVATION_EXPLICIT:  return "EXPLICIT";
224
  case DERIVATION_NONE:      return "NONE";
225
  default: return "UNKNOWN";
226
  }
227
}
228
229
230
bool agg_item_collations(DTCollation &c, const char *fname,
231
                         Item **av, uint32_t count,
232
                         uint32_t flags, int item_sep)
233
{
234
  uint32_t i;
235
  Item **arg;
236
  c.set(av[0]->collation);
237
  for (i= 1, arg= &av[item_sep]; i < count; i++, arg++)
238
  {
239
    if (c.aggregate((*arg)->collation, flags))
240
    {
241
      my_coll_agg_error(av, count, fname, item_sep);
242
      return true;
243
    }
244
  }
245
  if ((flags & MY_COLL_DISALLOW_NONE) &&
246
      c.derivation == DERIVATION_NONE)
247
  {
248
    my_coll_agg_error(av, count, fname, item_sep);
249
    return true;
250
  }
251
  return false;
252
}
253
254
255
bool agg_item_collations_for_comparison(DTCollation &c, const char *fname,
256
                                        Item **av, uint32_t count,
257
                                        uint32_t flags)
258
{
259
  return (agg_item_collations(c, fname, av, count,
260
                              flags | MY_COLL_DISALLOW_NONE, 1));
261
}
262
263
264
bool agg_item_charsets(DTCollation &coll, const char *fname,
265
                       Item **args, uint32_t nargs, uint32_t flags,
266
                       int item_sep)
267
{
268
  Item **arg, *safe_args[2];
269
270
  memset(safe_args, 0, sizeof(safe_args));
271
272
  if (agg_item_collations(coll, fname, args, nargs, flags, item_sep))
273
    return true;
274
275
  /*
276
    For better error reporting: save the first and the second argument.
277
    We need this only if the the number of args is 3 or 2:
278
    - for a longer argument list, "Illegal mix of collations"
279
      doesn't display each argument's characteristics.
280
    - if nargs is 1, then this error cannot happen.
281
  */
282
  if (nargs >=2 && nargs <= 3)
283
  {
284
    safe_args[0]= args[0];
285
    safe_args[1]= args[item_sep];
286
  }
287
288
  Session *session= current_session;
289
  bool res= false;
290
  uint32_t i;
291
292
  for (i= 0, arg= args; i < nargs; i++, arg+= item_sep)
293
  {
294
    Item* conv;
295
    uint32_t dummy_offset;
296
    if (!String::needs_conversion(0, (*arg)->collation.collation,
297
                                  coll.collation,
298
                                  &dummy_offset))
299
      continue;
300
301
    if (!(conv= (*arg)->safe_charset_converter(coll.collation)) &&
302
        ((*arg)->collation.repertoire == MY_REPERTOIRE_ASCII))
303
      conv= new Item_func_conv_charset(*arg, coll.collation, 1);
304
305
    if (!conv)
306
    {
307
      if (nargs >=2 && nargs <= 3)
308
      {
309
        /* restore the original arguments for better error message */
310
        args[0]= safe_args[0];
311
        args[item_sep]= safe_args[1];
312
      }
313
      my_coll_agg_error(args, nargs, fname, item_sep);
314
      res= true;
315
      break; // we cannot return here, we need to restore "arena".
316
    }
317
    if ((*arg)->type() == Item::FIELD_ITEM)
318
      ((Item_field *)(*arg))->no_const_subst= 1;
319
    /*
320
      If in statement prepare, then we create a converter for two
321
      constant items, do it once and then reuse it.
322
      If we're in execution of a prepared statement, arena is NULL,
323
      and the conv was created in runtime memory. This can be
324
      the case only if the argument is a parameter marker ('?'),
325
      because for all true constants the charset converter has already
326
      been created in prepare. In this case register the change for
327
      rollback.
328
    */
329
    session->change_item_tree(arg, conv);
330
    /*
331
      We do not check conv->fixed, because Item_func_conv_charset which can
332
      be return by safe_charset_converter can't be fixed at creation
333
    */
334
    conv->fix_fields(session, arg);
335
  }
336
337
  return res;
338
}
339
340
341
void my_coll_agg_error(DTCollation &c1,
342
                       DTCollation &c2, const char *fname)
343
{
344
  my_error(ER_CANT_AGGREGATE_2COLLATIONS,MYF(0),
345
           c1.collation->name,c1.derivation_name(),
346
           c2.collation->name,c2.derivation_name(),
347
           fname);
348
}
349
350
351
void my_coll_agg_error(DTCollation &c1,
352
                       DTCollation &c2,
353
                       DTCollation &c3,
354
                       const char *fname)
355
{
356
  my_error(ER_CANT_AGGREGATE_3COLLATIONS,MYF(0),
357
           c1.collation->name,c1.derivation_name(),
358
           c2.collation->name,c2.derivation_name(),
359
           c3.collation->name,c3.derivation_name(),
360
           fname);
361
}
362
363
364
void my_coll_agg_error(Item** args, uint32_t count, const char *fname,
365
                       int item_sep)
366
{
367
  if (count == 2)
368
    my_coll_agg_error(args[0]->collation, args[item_sep]->collation, fname);
369
  else if (count == 3)
370
    my_coll_agg_error(args[0]->collation, args[item_sep]->collation,
371
                      args[2*item_sep]->collation, fname);
372
  else
373
    my_error(ER_CANT_AGGREGATE_NCOLLATIONS,MYF(0),fname);
374
}
375