Drizzled Public API Documentation

charset.cc
00001 /* Copyright (C) 2000 MySQL AB
00002 
00003    This program is free software; you can redistribute it and/or modify
00004    it under the terms of the GNU General Public License as published by
00005    the Free Software Foundation; version 2 of the License.
00006 
00007    This program is distributed in the hope that it will be useful,
00008    but WITHOUT ANY WARRANTY; without even the implied warranty of
00009    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00010    GNU General Public License for more details.
00011 
00012    You should have received a copy of the GNU General Public License
00013    along with this program; if not, write to the Free Software
00014    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */
00015 
00016 #include <config.h>
00017 
00018 #include <drizzled/charset.h>
00019 #include <drizzled/error.h>
00020 #include <drizzled/internal/m_string.h>
00021 #include <drizzled/configmake.h>
00022 #include <vector>
00023 
00024 #include <drizzled/visibility.h>
00025 
00026 using namespace std;
00027 
00028 namespace drizzled {
00029 
00030 /*
00031   We collect memory in this vector that we free on delete.
00032 */
00033 static vector<unsigned char*> memory_vector;
00034 
00035 extern charset_info_st my_charset_utf8mb4_icelandic_uca_ci;
00036 extern charset_info_st my_charset_utf8mb4_latvian_uca_ci;
00037 extern charset_info_st my_charset_utf8mb4_romanian_uca_ci;
00038 extern charset_info_st my_charset_utf8mb4_slovenian_uca_ci;
00039 extern charset_info_st my_charset_utf8mb4_polish_uca_ci;
00040 extern charset_info_st my_charset_utf8mb4_estonian_uca_ci;
00041 extern charset_info_st my_charset_utf8mb4_spanish_uca_ci;
00042 extern charset_info_st my_charset_utf8mb4_swedish_uca_ci;
00043 extern charset_info_st my_charset_utf8mb4_turkish_uca_ci;
00044 extern charset_info_st my_charset_utf8mb4_czech_uca_ci;
00045 extern charset_info_st my_charset_utf8mb4_danish_uca_ci;
00046 extern charset_info_st my_charset_utf8mb4_lithuanian_uca_ci;
00047 extern charset_info_st my_charset_utf8mb4_slovak_uca_ci;
00048 extern charset_info_st my_charset_utf8mb4_spanish2_uca_ci;
00049 extern charset_info_st my_charset_utf8mb4_roman_uca_ci;
00050 extern charset_info_st my_charset_utf8mb4_persian_uca_ci;
00051 extern charset_info_st my_charset_utf8mb4_esperanto_uca_ci;
00052 extern charset_info_st my_charset_utf8mb4_hungarian_uca_ci;
00053 extern charset_info_st my_charset_utf8mb4_sinhala_uca_ci;
00054 
00055 /*
00056   The code below implements this functionality:
00057 
00058     - Initializing charset related structures
00059     - Loading dynamic charsets
00060     - Searching for a proper charset_info_st
00061       using charset name, collation name or collation ID
00062     - Setting server default character set
00063 */
00064 
00065 bool my_charset_same(const charset_info_st *cs1, const charset_info_st *cs2)
00066 {
00067   return cs1 == cs2 || not strcmp(cs1->csname, cs2->csname);
00068 }
00069 
00070 static uint get_collation_number_internal(const char *name)
00071 {
00072   for (charset_info_st **cs= all_charsets; cs < all_charsets + array_elements(all_charsets) - 1; cs++)
00073   {
00074     if (cs[0] && cs[0]->name && not my_charset_utf8_general_ci.strcasecmp(cs[0]->name, name))
00075     {
00076       return cs[0]->number;
00077     }
00078   }
00079   return 0;
00080 }
00081 
00082 static unsigned char* cs_alloc(size_t size)
00083 {
00084   memory_vector.push_back(new unsigned char[size]);
00085   return memory_vector.back();
00086 }
00087 
00088 static void init_state_maps(charset_info_st *cs)
00089 {
00090   cs->state_map= cs_alloc(256);
00091   cs->ident_map= cs_alloc(256);
00092 
00093   unsigned char *state_map= cs->state_map;
00094   unsigned char *ident_map= cs->ident_map;
00095 
00096   /* Fill state_map with states to get a faster parser */
00097   for (int i= 0; i < 256; i++)
00098   {
00099     if (cs->isalpha(i))
00100       state_map[i]= MY_LEX_IDENT;
00101     else if (cs->isdigit(i))
00102       state_map[i]= MY_LEX_NUMBER_IDENT;
00103     else if (my_mbcharlen(cs, i) > 1)
00104       state_map[i]= MY_LEX_IDENT;
00105     else if (cs->isspace(i))
00106       state_map[i]= MY_LEX_SKIP;
00107     else
00108       state_map[i]= MY_LEX_CHAR;
00109   }
00110   state_map['_']=state_map['$']= MY_LEX_IDENT;
00111   state_map['\'']= MY_LEX_STRING;
00112   state_map['.']= MY_LEX_REAL_OR_POINT;
00113   state_map['>']=state_map['=']=state_map['!']=  MY_LEX_CMP_OP;
00114   state_map['<']=  MY_LEX_LONG_CMP_OP;
00115   state_map['&']=state_map['|']= MY_LEX_BOOL;
00116   state_map['#']= MY_LEX_COMMENT;
00117   state_map[';']= MY_LEX_SEMICOLON;
00118   state_map[':']= MY_LEX_SET_VAR;
00119   state_map[0]= MY_LEX_EOL;
00120   state_map['\\']=  MY_LEX_ESCAPE;
00121   state_map['/']=  MY_LEX_LONG_COMMENT;
00122   state_map['*']=  MY_LEX_END_LONG_COMMENT;
00123   state_map['@']=  MY_LEX_USER_END;
00124   state_map['`']=  MY_LEX_USER_VARIABLE_DELIMITER;
00125   state_map['"']=  MY_LEX_STRING_OR_DELIMITER;
00126 
00127   /*
00128     Create a second map to make it faster to find identifiers
00129   */
00130   for (int i= 0; i < 256; i++)
00131   {
00132     ident_map[i]= state_map[i] == MY_LEX_IDENT || state_map[i] == MY_LEX_NUMBER_IDENT;
00133   }
00134 
00135   /* Special handling of hex and binary strings */
00136   state_map['x']= state_map['X']=  MY_LEX_IDENT_OR_HEX;
00137   state_map['b']= state_map['B']=  MY_LEX_IDENT_OR_BIN;
00138 }
00139 
00140 static bool charset_initialized= false;
00141 
00142 DRIZZLED_API charset_info_st *all_charsets[256];
00143 const DRIZZLED_API charset_info_st *default_charset_info = &my_charset_utf8_general_ci;
00144 
00145 static void add_compiled_collation(charset_info_st * cs)
00146 {
00147   all_charsets[cs->number]= cs;
00148   cs->state|= MY_CS_AVAILABLE;
00149 }
00150 
00151 static void init_compiled_charsets()
00152 {
00153   add_compiled_collation(&my_charset_bin);
00154 
00155   add_compiled_collation(&my_charset_utf8mb4_general_ci);
00156   add_compiled_collation(&my_charset_utf8mb4_bin);
00157   add_compiled_collation(&my_charset_utf8mb4_unicode_ci);
00158   add_compiled_collation(&my_charset_utf8mb4_icelandic_uca_ci);
00159   add_compiled_collation(&my_charset_utf8mb4_latvian_uca_ci);
00160   add_compiled_collation(&my_charset_utf8mb4_romanian_uca_ci);
00161   add_compiled_collation(&my_charset_utf8mb4_slovenian_uca_ci);
00162   add_compiled_collation(&my_charset_utf8mb4_polish_uca_ci);
00163   add_compiled_collation(&my_charset_utf8mb4_estonian_uca_ci);
00164   add_compiled_collation(&my_charset_utf8mb4_spanish_uca_ci);
00165   add_compiled_collation(&my_charset_utf8mb4_swedish_uca_ci);
00166   add_compiled_collation(&my_charset_utf8mb4_turkish_uca_ci);
00167   add_compiled_collation(&my_charset_utf8mb4_czech_uca_ci);
00168   add_compiled_collation(&my_charset_utf8mb4_danish_uca_ci);
00169   add_compiled_collation(&my_charset_utf8mb4_lithuanian_uca_ci);
00170   add_compiled_collation(&my_charset_utf8mb4_slovak_uca_ci);
00171   add_compiled_collation(&my_charset_utf8mb4_spanish2_uca_ci);
00172   add_compiled_collation(&my_charset_utf8mb4_roman_uca_ci);
00173   add_compiled_collation(&my_charset_utf8mb4_persian_uca_ci);
00174   add_compiled_collation(&my_charset_utf8mb4_esperanto_uca_ci);
00175   add_compiled_collation(&my_charset_utf8mb4_hungarian_uca_ci);
00176   add_compiled_collation(&my_charset_utf8mb4_sinhala_uca_ci);
00177 }
00178 
00179 static void init_available_charsets()
00180 {
00181   /*
00182     We have to use charset_initialized to not lock on THR_LOCK_charset
00183     inside get_internal_charset...
00184   */
00185   if (charset_initialized)
00186     return;
00187   memset(&all_charsets, 0, sizeof(all_charsets));
00188   init_compiled_charsets();
00189 
00190   /* Copy compiled charsets */
00191   for (charset_info_st**cs= all_charsets;
00192     cs < all_charsets+array_elements(all_charsets)-1;
00193     cs++)
00194   {
00195     if (*cs && cs[0]->ctype)
00196       init_state_maps(*cs);
00197   }
00198 
00199   charset_initialized= true;
00200 }
00201 
00202 void free_charsets()
00203 {
00204   charset_initialized= false;
00205 
00206   while (not memory_vector.empty())
00207   {
00208     delete[] memory_vector.back();
00209     memory_vector.pop_back();
00210   }
00211 }
00212 
00213 uint32_t get_collation_number(const char *name)
00214 {
00215   init_available_charsets();
00216   return get_collation_number_internal(name);
00217 }
00218 
00219 uint32_t get_charset_number(const char *charset_name, uint32_t cs_flags)
00220 {
00221   init_available_charsets();
00222 
00223   for (charset_info_st** cs= all_charsets; cs < all_charsets + array_elements(all_charsets) - 1; cs++)
00224   {
00225     if (cs[0] && cs[0]->csname && (cs[0]->state & cs_flags) && not my_charset_utf8_general_ci.strcasecmp(cs[0]->csname, charset_name))
00226       return cs[0]->number;
00227   }
00228   return 0;
00229 }
00230 
00231 const char *get_charset_name(uint32_t charset_number)
00232 {
00233   init_available_charsets();
00234   const charset_info_st* cs= all_charsets[charset_number];
00235   return cs && cs->number == charset_number && cs->name ? cs->name : "?";
00236 }
00237 
00238 static const charset_info_st *get_internal_charset(uint32_t cs_number)
00239 {
00240   charset_info_st* cs= all_charsets[cs_number];
00241   /*
00242     To make things thread safe we are not allowing other threads to interfere
00243     while we may changing the cs_info_table
00244   */
00245   if (not cs)
00246     return NULL;
00247   assert(not (not (cs->state & MY_CS_COMPILED) && not (cs->state & MY_CS_LOADED)));
00248   if (not (cs->state & MY_CS_AVAILABLE))
00249     return NULL;
00250   if (not (cs->state & MY_CS_READY))
00251   {
00252     if (cs->coll->init && cs->coll->init(*cs, cs_alloc))
00253       return NULL;
00254     cs->state|= MY_CS_READY;
00255   }
00256   return cs;
00257 }
00258 
00259 const charset_info_st *get_charset(uint32_t cs_number)
00260 {
00261   if (cs_number == default_charset_info->number)
00262     return default_charset_info;
00263 
00264   init_available_charsets();  /* If it isn't initialized */
00265 
00266   if (!cs_number || cs_number >= array_elements(all_charsets)-1)
00267     return NULL;
00268 
00269   return get_internal_charset(cs_number);
00270 }
00271 
00272 const charset_info_st *get_charset_by_name(const char *cs_name)
00273 {
00274   init_available_charsets();  /* If it isn't initialized */
00275   uint32_t cs_number= get_collation_number(cs_name);
00276   return cs_number ? get_internal_charset(cs_number) : NULL;
00277 }
00278 
00279 const charset_info_st *get_charset_by_csname(const char *cs_name, uint32_t cs_flags)
00280 {
00281   init_available_charsets();  /* If it isn't initialized */
00282   uint32_t cs_number= get_charset_number(cs_name, cs_flags);
00283   return cs_number ? get_internal_charset(cs_number) : NULL;
00284 }
00285 
00286 
00287 /*
00288   Escape apostrophes by doubling them up
00289 
00290   SYNOPSIS
00291     escape_quotes_for_drizzle()
00292     charset_info        Charset of the strings
00293     to                  Buffer for escaped string
00294     to_length           Length of destination buffer, or 0
00295     from                The string to escape
00296     length              The length of the string to escape
00297 
00298   DESCRIPTION
00299     This escapes the contents of a string by doubling up any apostrophes that
00300     it contains. This is used when the NO_BACKSLASH_ESCAPES SQL_MODE is in
00301     effect on the server.
00302 
00303   NOTE
00304     To be consistent with escape_string_for_mysql(), to_length may be 0 to
00305     mean "big enough"
00306 
00307   RETURN VALUES
00308     UINT32_MAX  The escaped string did not fit in the to buffer
00309     >=0         The length of the escaped string
00310 */
00311 
00312 size_t escape_quotes_for_drizzle(const charset_info_st *charset_info,
00313                                  char *to, size_t to_length,
00314                                  const char *from, size_t length)
00315 {
00316   const char *to_start= to;
00317   const char *end, *to_end=to_start + (to_length ? to_length-1 : 2*length);
00318   bool overflow= false;
00319   bool use_mb_flag= use_mb(charset_info);
00320   for (end= from + length; from < end; from++)
00321   {
00322     int tmp_length;
00323     if (use_mb_flag && (tmp_length= my_ismbchar(charset_info, from, end)))
00324     {
00325       if (to + tmp_length > to_end)
00326       {
00327         overflow= true;
00328         break;
00329       }
00330       while (tmp_length--)
00331   *to++= *from++;
00332       from--;
00333       continue;
00334     }
00335     /*
00336       We don't have the same issue here with a non-multi-byte character being
00337       turned into a multi-byte character by the addition of an escaping
00338       character, because we are only escaping the ' character with itself.
00339      */
00340     if (*from == '\'')
00341     {
00342       if (to + 2 > to_end)
00343       {
00344         overflow= true;
00345         break;
00346       }
00347       *to++= '\'';
00348       *to++= '\'';
00349     }
00350     else
00351     {
00352       if (to + 1 > to_end)
00353       {
00354         overflow= true;
00355         break;
00356       }
00357       *to++= *from;
00358     }
00359   }
00360   *to= 0;
00361   return overflow ? UINT32_MAX : to - to_start;
00362 }
00363 
00364 } /* namespace drizzled */