/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* This file defines all string functions ** Warning: Some string functions doesn't always put and end-null on a String ** (This shouldn't be needed) */ #ifdef __GNUC__ #pragma implementation // gcc: Class implementation #endif #include "mysql_priv.h" #include "sql_acl.h" #include <m_ctype.h> #ifdef HAVE_CRYPT_H #include <crypt.h> #endif #ifdef HAVE_OPENSSL #include <openssl/des.h> #endif /* HAVE_OPENSSL */ #include "md5.h" #include "sha1.h" #include "my_aes.h" String empty_string("",default_charset_info); uint nr_of_decimals(const char *str) { if ((str=strchr(str,'.'))) { const char *start= ++str; for (; my_isdigit(system_charset_info,*str) ; str++) ; return (uint) (str-start); } return 0; } double Item_str_func::val() { String *res; res=val_str(&str_value); return res ? atof(res->c_ptr()) : 0.0; } longlong Item_str_func::val_int() { String *res; res=val_str(&str_value); return res ? strtoll(res->c_ptr(),NULL,10) : (longlong) 0; } String *Item_func_md5::val_str(String *str) { String * sptr= args[0]->val_str(str); if (sptr) { my_MD5_CTX context; unsigned char digest[16]; null_value=0; my_MD5Init (&context); my_MD5Update (&context,(unsigned char *) sptr->ptr(), sptr->length()); my_MD5Final (digest, &context); if (str->alloc(32)) // Ensure that memory is free { null_value=1; return 0; } sprintf((char *) str->ptr(), "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7], digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15]); str->length((uint) 32); return str; } null_value=1; return 0; } void Item_func_md5::fix_length_and_dec() { max_length=32; } String *Item_func_sha::val_str(String *str) { String * sptr= args[0]->val_str(str); if (sptr) /* If we got value different from NULL */ { SHA1_CONTEXT context; /* Context used to generate SHA1 hash */ /* Temporary buffer to store 160bit digest */ uint8 digest[SHA1_HASH_SIZE]; sha1_reset(&context); /* We do not have to check for error here */ /* No need to check error as the only case would be too long message */ sha1_input(&context,(const unsigned char *) sptr->ptr(), sptr->length()); /* Ensure that memory is free and we got result */ if (!( str->alloc(SHA1_HASH_SIZE*2) || (sha1_result(&context,digest)))) { sprintf((char *) str->ptr(), "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\ %02x%02x%02x%02x%02x%02x%02x%02x", digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7], digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15], digest[16], digest[17], digest[18], digest[19]); str->length((uint) SHA1_HASH_SIZE*2); null_value=0; return str; } } null_value=1; return 0; } void Item_func_sha::fix_length_and_dec() { max_length=SHA1_HASH_SIZE*2; // size of hex representation of hash } /* Implementation of AES encryption routines */ String *Item_func_aes_encrypt::val_str(String *str) { char key_buff[80]; String tmp_key_value(key_buff, sizeof(key_buff), system_charset_info); String *sptr= args[0]->val_str(str); // String to encrypt String *key= args[1]->val_str(&tmp_key_value); // key int aes_length; if (sptr && key) // we need both arguments to be not NULL { null_value=0; aes_length=my_aes_get_size(sptr->length()); // Calculate result length if (!str_value.alloc(aes_length)) // Ensure that memory is free { // finally encrypt directly to allocated buffer. if (my_aes_encrypt(sptr->ptr(),sptr->length(), (char*) str_value.ptr(), key->ptr(), key->length()) == aes_length) { // We got the expected result length str_value.length((uint) aes_length); return &str_value; } } } null_value=1; return 0; } void Item_func_aes_encrypt::fix_length_and_dec() { max_length=my_aes_get_size(args[0]->max_length); } String *Item_func_aes_decrypt::val_str(String *str) { char key_buff[80]; String tmp_key_value(key_buff, sizeof(key_buff), system_charset_info); String *sptr, *key; DBUG_ENTER("Item_func_aes_decrypt::val_str"); sptr= args[0]->val_str(str); // String to decrypt key= args[1]->val_str(&tmp_key_value); // Key if (sptr && key) // Need to have both arguments not NULL { null_value=0; if (!str_value.alloc(sptr->length())) // Ensure that memory is free { // finally decrypt directly to allocated buffer. int length; length=my_aes_decrypt(sptr->ptr(), sptr->length(), (char*) str_value.ptr(), key->ptr(), key->length()); if (length >= 0) // if we got correct data data { str_value.length((uint) length); DBUG_RETURN(&str_value); } } } // Bad parameters. No memory or bad data will all go here null_value=1; DBUG_RETURN(0); } void Item_func_aes_decrypt::fix_length_and_dec() { max_length=args[0]->max_length; } /* Concatenate args with the following premises: If only one arg (which is ok), return value of arg Don't reallocate val_str() if not absolute necessary. */ String *Item_func_concat::val_str(String *str) { String *res,*res2,*use_as_buff; uint i; null_value=0; if (!(res=args[0]->val_str(str))) goto null; use_as_buff= &tmp_value; for (i=1 ; i < arg_count ; i++) { if (args[i]->binary()) set_charset(my_charset_bin); if (res->length() == 0) { if (!(res=args[i]->val_str(str))) goto null; } else { if (!(res2=args[i]->val_str(use_as_buff))) goto null; if (res2->length() == 0) continue; if (res->length()+res2->length() > current_thd->variables.max_allowed_packet) goto null; // Error check if (res->alloced_length() >= res->length()+res2->length()) { // Use old buffer res->append(*res2); } else if (str->alloced_length() >= res->length()+res2->length()) { if (str == res2) str->replace(0,0,*res); else { str->copy(*res); str->append(*res2); } res=str; res->set_charset(charset()); } else if (res == &tmp_value) { if (res->append(*res2)) // Must be a blob goto null; } else if (res2 == &tmp_value) { // This can happend only 1 time if (tmp_value.replace(0,0,*res)) goto null; res= &tmp_value; res->set_charset(charset()); use_as_buff=str; // Put next arg here } else if (tmp_value.is_alloced() && res2->ptr() >= tmp_value.ptr() && res2->ptr() <= tmp_value.ptr() + tmp_value.alloced_length()) { /* This happens really seldom: In this case res2 is sub string of tmp_value. We will now work in place in tmp_value to set it to res | res2 */ /* Chop the last characters in tmp_value that isn't in res2 */ tmp_value.length((uint32) (res2->ptr() - tmp_value.ptr()) + res2->length()); /* Place res2 at start of tmp_value, remove chars before res2 */ if (tmp_value.replace(0,(uint32) (res2->ptr() - tmp_value.ptr()), *res)) goto null; res= &tmp_value; res->set_charset(charset()); use_as_buff=str; // Put next arg here } else { // Two big const strings if (tmp_value.alloc(max_length) || tmp_value.copy(*res) || tmp_value.append(*res2)) goto null; res= &tmp_value; res->set_charset(charset()); use_as_buff=str; } } } return res; null: null_value=1; return 0; } void Item_func_concat::fix_length_and_dec() { max_length=0; for (uint i=0 ; i < arg_count ; i++) max_length+=args[i]->max_length; if (max_length > MAX_BLOB_WIDTH) { max_length=MAX_BLOB_WIDTH; maybe_null=1; } } /* Function des_encrypt() by tonu@spam.ee & monty Works only if compiled with OpenSSL library support. This returns a binary string where first character is CHAR(128 | key-number). If one uses a string key key_number is 127. Encryption result is longer than original by formula: new_length= org_length + (8-(org_length % 8))+1 */ String *Item_func_des_encrypt::val_str(String *str) { #ifdef HAVE_OPENSSL des_cblock ivec; struct st_des_keyblock keyblock; struct st_des_keyschedule keyschedule; const char *append_str="********"; uint key_number, res_length, tail; String *res= args[0]->val_str(str); if ((null_value=args[0]->null_value)) return 0; if ((res_length=res->length()) == 0) return &empty_string; if (arg_count == 1) { /* Protect against someone doing FLUSH DES_KEY_FILE */ VOID(pthread_mutex_lock(&LOCK_des_key_file)); keyschedule= des_keyschedule[key_number=des_default_key]; VOID(pthread_mutex_unlock(&LOCK_des_key_file)); } else if (args[1]->result_type() == INT_RESULT) { key_number= (uint) args[1]->val_int(); if (key_number > 9) goto error; VOID(pthread_mutex_lock(&LOCK_des_key_file)); keyschedule= des_keyschedule[key_number]; VOID(pthread_mutex_unlock(&LOCK_des_key_file)); } else { String *keystr=args[1]->val_str(&tmp_value); if (!keystr) goto error; key_number=127; // User key string /* We make good 24-byte (168 bit) key from given plaintext key with MD5 */ bzero((char*) &ivec,sizeof(ivec)); EVP_BytesToKey(EVP_des_ede3_cbc(),EVP_md5(),NULL, (uchar*) keystr->ptr(), (int) keystr->length(), 1, (uchar*) &keyblock,ivec); des_set_key_unchecked(&keyblock.key1,keyschedule.ks1); des_set_key_unchecked(&keyblock.key2,keyschedule.ks2); des_set_key_unchecked(&keyblock.key3,keyschedule.ks3); } /* The problem: DES algorithm requires original data to be in 8-bytes chunks. Missing bytes get filled with '*'s and result of encryption can be up to 8 bytes longer than original string. When decrypted, we do not know the size of original string :( We add one byte with value 0x1..0x8 as the last byte of the padded string marking change of string length. */ tail= (8-(res_length) % 8); // 1..8 marking extra length res_length+=tail; if (tail && res->append(append_str, tail) || tmp_value.alloc(res_length+1)) goto error; (*res)[res_length-1]=tail; // save extra length tmp_value.length(res_length+1); tmp_value[0]=(char) (128 | key_number); // Real encryption bzero((char*) &ivec,sizeof(ivec)); des_ede3_cbc_encrypt((const uchar*) (res->ptr()), (uchar*) (tmp_value.ptr()+1), res_length, keyschedule.ks1, keyschedule.ks2, keyschedule.ks3, &ivec, TRUE); return &tmp_value; error: #endif /* HAVE_OPENSSL */ null_value=1; return 0; } String *Item_func_des_decrypt::val_str(String *str) { #ifdef HAVE_OPENSSL des_key_schedule ks1, ks2, ks3; des_cblock ivec; struct st_des_keyblock keyblock; struct st_des_keyschedule keyschedule; String *res= args[0]->val_str(str); uint length=res->length(),tail; if ((null_value=args[0]->null_value)) return 0; length=res->length(); if (length < 9 || (length % 8) != 1 || !((*res)[0] & 128)) return res; // Skip decryption if not encrypted if (arg_count == 1) // If automatic uncompression { uint key_number=(uint) (*res)[0] & 127; // Check if automatic key and that we have privilege to uncompress using it if (!(current_thd->master_access & SUPER_ACL) || key_number > 9) goto error; VOID(pthread_mutex_lock(&LOCK_des_key_file)); keyschedule= des_keyschedule[key_number]; VOID(pthread_mutex_unlock(&LOCK_des_key_file)); } else { // We make good 24-byte (168 bit) key from given plaintext key with MD5 String *keystr=args[1]->val_str(&tmp_value); if (!keystr) goto error; bzero((char*) &ivec,sizeof(ivec)); EVP_BytesToKey(EVP_des_ede3_cbc(),EVP_md5(),NULL, (uchar*) keystr->ptr(),(int) keystr->length(), 1,(uchar*) &keyblock,ivec); // Here we set all 64-bit keys (56 effective) one by one des_set_key_unchecked(&keyblock.key1,keyschedule.ks1); des_set_key_unchecked(&keyblock.key2,keyschedule.ks2); des_set_key_unchecked(&keyblock.key3,keyschedule.ks3); } if (tmp_value.alloc(length-1)) goto error; bzero((char*) &ivec,sizeof(ivec)); des_ede3_cbc_encrypt((const uchar*) res->ptr()+1, (uchar*) (tmp_value.ptr()), length-1, keyschedule.ks1, keyschedule.ks2, keyschedule.ks3, &ivec, FALSE); /* Restore old length of key */ if ((tail=(uint) (uchar) tmp_value[length-2]) > 8) goto error; // Wrong key tmp_value.length(length-1-tail); return &tmp_value; error: #endif /* HAVE_OPENSSL */ null_value=1; return 0; } /* concat with separator. First arg is the separator concat_ws takes at least two arguments. */ String *Item_func_concat_ws::val_str(String *str) { char tmp_str_buff[10]; String tmp_sep_str(tmp_str_buff, sizeof(tmp_str_buff),default_charset_info), *sep_str, *res, *res2,*use_as_buff; uint i; null_value=0; if (!(sep_str= separator->val_str(&tmp_sep_str))) goto null; use_as_buff= &tmp_value; str->length(0); // QQ; Should be removed res=str; // Skip until non-null and non-empty argument is found. // If not, return the empty string for (i=0; i < arg_count; i++) if ((res= args[i]->val_str(str)) && res->length()) break; if (i == arg_count) return &empty_string; for (i++; i < arg_count ; i++) { if (!(res2= args[i]->val_str(use_as_buff)) || !res2->length()) continue; // Skip NULL and empty string if (res->length() + sep_str->length() + res2->length() > current_thd->variables.max_allowed_packet) goto null; // Error check if (res->alloced_length() >= res->length() + sep_str->length() + res2->length()) { // Use old buffer res->append(*sep_str); // res->length() > 0 always res->append(*res2); } else if (str->alloced_length() >= res->length() + sep_str->length() + res2->length()) { /* We have room in str; We can't get any errors here */ if (str == res2) { // This is quote uncommon! str->replace(0,0,*sep_str); str->replace(0,0,*res); } else { str->copy(*res); str->append(*sep_str); str->append(*res2); } res=str; use_as_buff= &tmp_value; } else if (res == &tmp_value) { if (res->append(*sep_str) || res->append(*res2)) goto null; // Must be a blob } else if (res2 == &tmp_value) { // This can happend only 1 time if (tmp_value.replace(0,0,*sep_str) || tmp_value.replace(0,0,*res)) goto null; res= &tmp_value; use_as_buff=str; // Put next arg here } else if (tmp_value.is_alloced() && res2->ptr() >= tmp_value.ptr() && res2->ptr() < tmp_value.ptr() + tmp_value.alloced_length()) { /* This happens really seldom: In this case res2 is sub string of tmp_value. We will now work in place in tmp_value to set it to res | sep_str | res2 */ /* Chop the last characters in tmp_value that isn't in res2 */ tmp_value.length((uint32) (res2->ptr() - tmp_value.ptr()) + res2->length()); /* Place res2 at start of tmp_value, remove chars before res2 */ if (tmp_value.replace(0,(uint32) (res2->ptr() - tmp_value.ptr()), *res) || tmp_value.replace(res->length(),0, *sep_str)) goto null; res= &tmp_value; use_as_buff=str; // Put next arg here } else { // Two big const strings if (tmp_value.alloc(max_length) || tmp_value.copy(*res) || tmp_value.append(*sep_str) || tmp_value.append(*res2)) goto null; res= &tmp_value; use_as_buff=str; } } return res; null: null_value=1; return 0; } void Item_func_concat_ws::fix_length_and_dec() { max_length=separator->max_length*(arg_count-1); for (uint i=0 ; i < arg_count ; i++) max_length+=args[i]->max_length; if (max_length > MAX_BLOB_WIDTH) { max_length=MAX_BLOB_WIDTH; maybe_null=1; } used_tables_cache|=separator->used_tables(); const_item_cache&=separator->const_item(); } void Item_func_concat_ws::update_used_tables() { Item_func::update_used_tables(); separator->update_used_tables(); used_tables_cache|=separator->used_tables(); const_item_cache&=separator->const_item(); } String *Item_func_reverse::val_str(String *str) { String *res = args[0]->val_str(str); char *ptr,*end; if ((null_value=args[0]->null_value)) return 0; /* An empty string is a special case as the string pointer may be null */ if (!res->length()) return &empty_string; res=copy_if_not_alloced(str,res,res->length()); ptr = (char *) res->ptr(); end=ptr+res->length(); #ifdef USE_MB if (use_mb(res->charset()) && !binary()) { String tmpstr; tmpstr.copy(*res); char *tmp = (char *) tmpstr.ptr() + tmpstr.length(); register uint32 l; while (ptr < end) { if ((l=my_ismbchar(res->charset(), ptr,end))) tmp-=l, memcpy(tmp,ptr,l), ptr+=l; else *--tmp=*ptr++; } memcpy((char *) res->ptr(),(char *) tmpstr.ptr(), res->length()); } else #endif /* USE_MB */ { char tmp; while (ptr < end) { tmp=*ptr; *ptr++=*--end; *end=tmp; } } return res; } void Item_func_reverse::fix_length_and_dec() { max_length = args[0]->max_length; } /* ** Replace all occurences of string2 in string1 with string3. ** Don't reallocate val_str() if not needed */ /* TODO: Fix that this works with binary strings when using USE_MB */ String *Item_func_replace::val_str(String *str) { String *res,*res2,*res3; int offset; uint from_length,to_length; bool alloced=0; #ifdef USE_MB const char *ptr,*end,*strend,*search,*search_end; register uint32 l; bool binary_str; #endif null_value=0; res=args[0]->val_str(str); if (args[0]->null_value) goto null; res2=args[1]->val_str(&tmp_value); if (args[1]->null_value) goto null; #ifdef USE_MB binary_str = (args[0]->binary() || args[1]->binary() || !use_mb(res->charset())); #endif if (res2->length() == 0) return res; #ifndef USE_MB if ((offset=res->strstr(*res2)) < 0) return res; #else offset=0; if (binary_str && (offset=res->strstr(*res2)) < 0) return res; #endif if (!(res3=args[2]->val_str(&tmp_value2))) goto null; from_length= res2->length(); to_length= res3->length(); #ifdef USE_MB if (!binary_str) { search=res2->ptr(); search_end=search+from_length; redo: ptr=res->ptr()+offset; strend=res->ptr()+res->length(); end=strend-from_length+1; while (ptr < end) { if (*ptr == *search) { register char *i,*j; i=(char*) ptr+1; j=(char*) search+1; while (j != search_end) if (*i++ != *j++) goto skipp; offset= (int) (ptr-res->ptr()); if (res->length()-from_length + to_length > current_thd->variables.max_allowed_packet) goto null; if (!alloced) { alloced=1; res=copy_if_not_alloced(str,res,res->length()+to_length); } res->replace((uint) offset,from_length,*res3); offset+=(int) to_length; goto redo; } skipp: if ((l=my_ismbchar(res->charset(), ptr,strend))) ptr+=l; else ++ptr; } } else #endif /* USE_MB */ do { if (res->length()-from_length + to_length > current_thd->variables.max_allowed_packet) goto null; if (!alloced) { alloced=1; res=copy_if_not_alloced(str,res,res->length()+to_length); } res->replace((uint) offset,from_length,*res3); offset+=(int) to_length; } while ((offset=res->strstr(*res2,(uint) offset)) >= 0); return res; null: null_value=1; return 0; } void Item_func_replace::fix_length_and_dec() { max_length=args[0]->max_length; int diff=(int) (args[2]->max_length - args[1]->max_length); if (diff > 0 && args[1]->max_length) { // Calculate of maxreplaces max_length= max_length/args[1]->max_length; max_length= (max_length+1)*(uint) diff; } if (max_length > MAX_BLOB_WIDTH) { max_length=MAX_BLOB_WIDTH; maybe_null=1; } } String *Item_func_insert::val_str(String *str) { String *res,*res2; uint start,length; null_value=0; res=args[0]->val_str(str); res2=args[3]->val_str(&tmp_value); start=(uint) args[1]->val_int()-1; length=(uint) args[2]->val_int(); if (args[0]->null_value || args[1]->null_value || args[2]->null_value || args[3]->null_value) goto null; /* purecov: inspected */ #ifdef USE_MB if (use_mb(res->charset()) && !args[0]->binary()) { start=res->charpos(start); length=res->charpos(length,start); } #endif if (start > res->length()+1) return res; // Wrong param; skip insert if (length > res->length()-start) length=res->length()-start; if (res->length() - length + res2->length() > current_thd->variables.max_allowed_packet) goto null; // OOM check res=copy_if_not_alloced(str,res,res->length()); res->replace(start,length,*res2); return res; null: null_value=1; return 0; } void Item_func_insert::fix_length_and_dec() { max_length=args[0]->max_length+args[3]->max_length; if (max_length > MAX_BLOB_WIDTH) { max_length=MAX_BLOB_WIDTH; maybe_null=1; } } String *Item_func_lcase::val_str(String *str) { String *res; if (!(res=args[0]->val_str(str))) { null_value=1; /* purecov: inspected */ return 0; /* purecov: inspected */ } null_value=0; res=copy_if_not_alloced(str,res,res->length()); res->casedn(); return res; } String *Item_func_ucase::val_str(String *str) { String *res; if (!(res=args[0]->val_str(str))) { null_value=1; /* purecov: inspected */ return 0; /* purecov: inspected */ } null_value=0; res=copy_if_not_alloced(str,res,res->length()); res->caseup(); return res; } String *Item_func_left::val_str(String *str) { String *res =args[0]->val_str(str); long length =(long) args[1]->val_int(); if ((null_value=args[0]->null_value)) return 0; if (length <= 0) return &empty_string; #ifdef USE_MB if (use_mb(res->charset()) && !binary()) length = res->charpos(length); #endif if (res->length() > (ulong) length) { // Safe even if const arg if (!res->alloced_length()) { // Don't change const str str_value= *res; // Not malloced string set_charset(res->charset()); res= &str_value; } res->length((uint) length); } return res; } void Item_str_func::left_right_max_length() { max_length=args[0]->max_length; if (args[1]->const_item()) { int length=(int) args[1]->val_int(); if (length <= 0) max_length=0; else set_if_smaller(max_length,(uint) length); } } void Item_func_left::fix_length_and_dec() { left_right_max_length(); } String *Item_func_right::val_str(String *str) { String *res =args[0]->val_str(str); long length =(long) args[1]->val_int(); if ((null_value=args[0]->null_value)) return 0; /* purecov: inspected */ if (length <= 0) return &empty_string; /* purecov: inspected */ if (res->length() <= (uint) length) return res; /* purecov: inspected */ #ifdef USE_MB if (use_mb(res->charset()) && !binary()) { uint start=res->numchars()-(uint) length; if (start<=0) return res; start=res->charpos(start); tmp_value.set(*res,start,res->length()-start); } else #endif { tmp_value.set(*res,(res->length()- (uint) length),(uint) length); } return &tmp_value; } void Item_func_right::fix_length_and_dec() { left_right_max_length(); } String *Item_func_substr::val_str(String *str) { String *res = args[0]->val_str(str); int32 start = (int32) args[1]->val_int()-1; int32 length = arg_count == 3 ? (int32) args[2]->val_int() : INT_MAX32; int32 tmp_length; if ((null_value=(args[0]->null_value || args[1]->null_value || (arg_count == 3 && args[2]->null_value)))) return 0; /* purecov: inspected */ #ifdef USE_MB if (use_mb(res->charset()) && !binary()) { start=res->charpos(start); length=res->charpos(length,start); } #endif if (start < 0 || (uint) start+1 > res->length() || length <= 0) return &empty_string; tmp_length=(int32) res->length()-start; length=min(length,tmp_length); if (!start && res->length() == (uint) length) return res; tmp_value.set(*res,(uint) start,(uint) length); return &tmp_value; } void Item_func_substr::fix_length_and_dec() { max_length=args[0]->max_length; if (args[1]->const_item()) { int32 start=(int32) args[1]->val_int()-1; if (start < 0 || start >= (int32) max_length) max_length=0; /* purecov: inspected */ else max_length-= (uint) start; } if (arg_count == 3 && args[2]->const_item()) { int32 length= (int32) args[2]->val_int(); if (length <= 0) max_length=0; /* purecov: inspected */ else set_if_smaller(max_length,(uint) length); } } String *Item_func_substr_index::val_str(String *str) { String *res =args[0]->val_str(str); String *delimeter =args[1]->val_str(&tmp_value); int32 count = (int32) args[2]->val_int(); uint offset; if (args[0]->null_value || args[1]->null_value || args[2]->null_value) { // string and/or delim are null null_value=1; return 0; } null_value=0; uint delimeter_length=delimeter->length(); if (!res->length() || !delimeter_length || !count) return &empty_string; // Wrong parameters #ifdef USE_MB if (use_mb(res->charset()) && !binary()) { const char *ptr=res->ptr(); const char *strend = ptr+res->length(); const char *end=strend-delimeter_length+1; const char *search=delimeter->ptr(); const char *search_end=search+delimeter_length; int32 n=0,c=count,pass; register uint32 l; for (pass=(count>0);pass<2;++pass) { while (ptr < end) { if (*ptr == *search) { register char *i,*j; i=(char*) ptr+1; j=(char*) search+1; while (j != search_end) if (*i++ != *j++) goto skipp; if (pass==0) ++n; else if (!--c) break; ptr+=delimeter_length; continue; } skipp: if ((l=my_ismbchar(res->charset(), ptr,strend))) ptr+=l; else ++ptr; } /* either not found or got total number when count<0 */ if (pass == 0) /* count<0 */ { c+=n+1; if (c<=0) return res; /* not found, return original string */ ptr=res->ptr(); } else { if (c) return res; /* Not found, return original string */ if (count>0) /* return left part */ { tmp_value.set(*res,0,(ulong) (ptr-res->ptr())); } else /* return right part */ { ptr+=delimeter_length; tmp_value.set(*res,(ulong) (ptr-res->ptr()), (ulong) (strend-ptr)); } } } } else #endif /* USE_MB */ { if (count > 0) { // start counting from the beginning for (offset=0 ;; offset+=delimeter_length) { if ((int) (offset=res->strstr(*delimeter,offset)) < 0) return res; // Didn't find, return org string if (!--count) { tmp_value.set(*res,0,offset); break; } } } else { // Start counting at end for (offset=res->length() ; ; offset-=delimeter_length-1) { if ((int) (offset=res->strrstr(*delimeter,offset)) < 0) return res; // Didn't find, return org string if (!++count) { offset+=delimeter_length; tmp_value.set(*res,offset,res->length()- offset); break; } } } } return (&tmp_value); } /* ** The trim functions are extension to ANSI SQL because they trim substrings ** They ltrim() and rtrim() functions are optimized for 1 byte strings ** They also return the original string if possible, else they return ** a substring that points at the original string. */ String *Item_func_ltrim::val_str(String *str) { String *res =args[0]->val_str(str); if ((null_value=args[0]->null_value)) return 0; /* purecov: inspected */ char buff[MAX_FIELD_WIDTH]; String tmp(buff,sizeof(buff),res->charset()); String *remove_str=args[1]->val_str(&tmp); uint remove_length; LINT_INIT(remove_length); if (!remove_str || (remove_length=remove_str->length()) == 0 || remove_length > res->length()) return res; char *ptr=(char*) res->ptr(); char *end=ptr+res->length(); if (remove_length == 1) { char chr=(*remove_str)[0]; while (ptr != end && *ptr == chr) ptr++; } else { const char *r_ptr=remove_str->ptr(); end-=remove_length; while (ptr < end && !memcmp(ptr,r_ptr,remove_length)) ptr+=remove_length; end+=remove_length; } if (ptr == res->ptr()) return res; tmp_value.set(*res,(uint) (ptr - res->ptr()),(uint) (end-ptr)); return &tmp_value; } String *Item_func_rtrim::val_str(String *str) { String *res =args[0]->val_str(str); if ((null_value=args[0]->null_value)) return 0; /* purecov: inspected */ char buff[MAX_FIELD_WIDTH]; String tmp(buff,sizeof(buff),res->charset()); String *remove_str=args[1]->val_str(&tmp); uint remove_length; LINT_INIT(remove_length); if (!remove_str || (remove_length=remove_str->length()) == 0 || remove_length > res->length()) return res; char *ptr=(char*) res->ptr(); char *end=ptr+res->length(); #ifdef USE_MB char *p=ptr; register uint32 l; #endif if (remove_length == 1) { char chr=(*remove_str)[0]; #ifdef USE_MB if (use_mb(res->charset()) && !binary()) { while (ptr < end) { if ((l=my_ismbchar(res->charset(), ptr,end))) ptr+=l,p=ptr; else ++ptr; } ptr=p; } #endif while (ptr != end && end[-1] == chr) end--; } else { const char *r_ptr=remove_str->ptr(); #ifdef USE_MB if (use_mb(res->charset()) && !binary()) { loop: while (ptr + remove_length < end) { if ((l=my_ismbchar(res->charset(), ptr,end))) ptr+=l; else ++ptr; } if (ptr + remove_length == end && !memcmp(ptr,r_ptr,remove_length)) { end-=remove_length; ptr=p; goto loop; } } else #endif /* USE_MB */ { while (ptr + remove_length < end && !memcmp(end-remove_length,r_ptr,remove_length)) end-=remove_length; } } if (end == res->ptr()+res->length()) return res; tmp_value.set(*res,0,(uint) (end-res->ptr())); return &tmp_value; } String *Item_func_trim::val_str(String *str) { String *res =args[0]->val_str(str); if ((null_value=args[0]->null_value)) return 0; /* purecov: inspected */ char buff[MAX_FIELD_WIDTH]; String tmp(buff,sizeof(buff),res->charset()); String *remove_str=args[1]->val_str(&tmp); uint remove_length; LINT_INIT(remove_length); if (!remove_str || (remove_length=remove_str->length()) == 0 || remove_length > res->length()) return res; char *ptr=(char*) res->ptr(); char *end=ptr+res->length(); const char *r_ptr=remove_str->ptr(); while (ptr+remove_length <= end && !memcmp(ptr,r_ptr,remove_length)) ptr+=remove_length; #ifdef USE_MB if (use_mb(res->charset()) && !binary()) { char *p=ptr; register uint32 l; loop: while (ptr + remove_length < end) { if ((l=my_ismbchar(res->charset(), ptr,end))) ptr+=l; else ++ptr; } if (ptr + remove_length == end && !memcmp(ptr,r_ptr,remove_length)) { end-=remove_length; ptr=p; goto loop; } ptr=p; } else #endif /* USE_MB */ { while (ptr + remove_length <= end && !memcmp(end-remove_length,r_ptr,remove_length)) end-=remove_length; } if (ptr == res->ptr() && end == ptr+res->length()) return res; tmp_value.set(*res,(uint) (ptr - res->ptr()),(uint) (end-ptr)); return &tmp_value; } String *Item_func_password::val_str(String *str) { String *res =args[0]->val_str(str); if ((null_value=args[0]->null_value)) return 0; if (res->length() == 0) return &empty_string; make_scrambled_password(tmp_value,res->c_ptr()); str->set(tmp_value,16,res->charset()); return str; } #define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.') String *Item_func_encrypt::val_str(String *str) { String *res =args[0]->val_str(str); #ifdef HAVE_CRYPT char salt[3],*salt_ptr; if ((null_value=args[0]->null_value)) return 0; if (res->length() == 0) return &empty_string; if (arg_count == 1) { // generate random salt time_t timestamp=current_thd->query_start(); salt[0] = bin_to_ascii( (ulong) timestamp & 0x3f); salt[1] = bin_to_ascii(( (ulong) timestamp >> 5) & 0x3f); salt[2] = 0; salt_ptr=salt; } else { // obtain salt from the first two bytes String *salt_str=args[1]->val_str(&tmp_value); if ((null_value= (args[1]->null_value || salt_str->length() < 2))) return 0; salt_ptr= salt_str->c_ptr(); } pthread_mutex_lock(&LOCK_crypt); char *tmp=crypt(res->c_ptr(),salt_ptr); str->set(tmp,(uint) strlen(tmp),res->charset()); str->copy(); pthread_mutex_unlock(&LOCK_crypt); return str; #else null_value=1; return 0; #endif /* HAVE_CRYPT */ } void Item_func_encode::fix_length_and_dec() { max_length=args[0]->max_length; maybe_null=args[0]->maybe_null; } String *Item_func_encode::val_str(String *str) { String *res; if (!(res=args[0]->val_str(str))) { null_value=1; /* purecov: inspected */ return 0; /* purecov: inspected */ } null_value=0; res=copy_if_not_alloced(str,res,res->length()); sql_crypt.init(); sql_crypt.encode((char*) res->ptr(),res->length()); return res; } String *Item_func_decode::val_str(String *str) { String *res; if (!(res=args[0]->val_str(str))) { null_value=1; /* purecov: inspected */ return 0; /* purecov: inspected */ } null_value=0; res=copy_if_not_alloced(str,res,res->length()); sql_crypt.init(); sql_crypt.decode((char*) res->ptr(),res->length()); return res; } String *Item_func_database::val_str(String *str) { if (!current_thd->db) str->length(0); else str->set((const char*) current_thd->db,(uint) strlen(current_thd->db), default_charset_info); return str; } String *Item_func_user::val_str(String *str) { THD *thd=current_thd; if (str->copy((const char*) thd->user,(uint) strlen(thd->user), system_charset_info) || str->append('@') || str->append(thd->host ? thd->host : thd->ip ? thd->ip : "")) return &empty_string; return str; } void Item_func_soundex::fix_length_and_dec() { max_length=args[0]->max_length; set_if_bigger(max_length,4); } /* If alpha, map input letter to soundex code. If not alpha and remove_garbage is set then skip to next char else return 0 */ extern "C" { extern const char *soundex_map; // In mysys/static.c } static char get_scode(CHARSET_INFO *cs,char *ptr) { uchar ch=my_toupper(cs,*ptr); if (ch < 'A' || ch > 'Z') { // Thread extended alfa (country spec) return '0'; // as vokal } return(soundex_map[ch-'A']); } String *Item_func_soundex::val_str(String *str) { String *res =args[0]->val_str(str); char last_ch,ch; if ((null_value=args[0]->null_value)) return 0; /* purecov: inspected */ if (tmp_value.alloc(max(res->length(),4))) return str; /* purecov: inspected */ char *to= (char *) tmp_value.ptr(); char *from= (char *) res->ptr(), *end=from+res->length(); while (from != end && my_isspace(str->charset(),*from)) // Skip pre-space from++; /* purecov: inspected */ if (from == end) return &empty_string; // No alpha characters. *to++ = my_toupper(str->charset(),*from);// Copy first letter last_ch = get_scode(str->charset(),from);// code of the first letter // for the first 'double-letter check. // Loop on input letters until // end of input (null) or output // letter code count = 3 for (from++ ; from < end ; from++) { if (!my_isalpha(str->charset(),*from)) continue; ch=get_scode(str->charset(),from); if ((ch != '0') && (ch != last_ch)) // if not skipped or double { *to++ = ch; // letter, copy to output last_ch = ch; // save code of last input letter } // for next double-letter check } for (end=(char*) tmp_value.ptr()+4 ; to < end ; to++) *to = '0'; *to=0; // end string tmp_value.length((uint) (to-tmp_value.ptr())); return &tmp_value; } /* ** Change a number to format '3,333,333,333.000' ** This should be 'internationalized' sometimes. */ Item_func_format::Item_func_format(Item *org,int dec) :Item_str_func(org) { decimals=(uint) set_zone(dec,0,30); } String *Item_func_format::val_str(String *str) { double nr =args[0]->val(); uint32 diff,length,str_length; uint dec; if ((null_value=args[0]->null_value)) return 0; /* purecov: inspected */ dec= decimals ? decimals+1 : 0; str->set(nr,decimals,thd_charset()); str_length=str->length(); if (nr < 0) str_length--; // Don't count sign length=str->length()+(diff=(str_length- dec-1)/3); if (diff) { char *tmp,*pos; str=copy_if_not_alloced(&tmp_str,str,length); str->length(length); tmp=(char*) str->ptr()+length - dec-1; for (pos=(char*) str->ptr()+length ; pos != tmp; pos--) pos[0]=pos[- (int) diff]; while (diff) { pos[0]=pos[-(int) diff]; pos--; pos[0]=pos[-(int) diff]; pos--; pos[0]=pos[-(int) diff]; pos--; pos[0]=','; pos--; diff--; } } return str; } void Item_func_elt::fix_length_and_dec() { max_length=0; decimals=0; for (uint i=1 ; i < arg_count ; i++) { set_if_bigger(max_length,args[i]->max_length); set_if_bigger(decimals,args[i]->decimals); } maybe_null=1; // NULL if wrong first arg with_sum_func= with_sum_func || item->with_sum_func; used_tables_cache|=item->used_tables(); const_item_cache&=item->const_item(); } void Item_func_elt::update_used_tables() { Item_func::update_used_tables(); item->update_used_tables(); used_tables_cache|=item->used_tables(); const_item_cache&=item->const_item(); } double Item_func_elt::val() { uint tmp; if ((tmp=(uint) item->val_int()) == 0 || tmp > arg_count) { null_value=1; return 0.0; } null_value=0; return args[tmp-1]->val(); } longlong Item_func_elt::val_int() { uint tmp; if ((tmp=(uint) item->val_int()) == 0 || tmp > arg_count) { null_value=1; return 0; } null_value=0; return args[tmp-1]->val_int(); } String *Item_func_elt::val_str(String *str) { uint tmp; if ((tmp=(uint) item->val_int()) == 0 || tmp > arg_count) { null_value=1; return NULL; } null_value=0; return args[tmp-1]->val_str(str); } void Item_func_make_set::fix_length_and_dec() { max_length=arg_count-1; for (uint i=1 ; i < arg_count ; i++) max_length+=args[i]->max_length; used_tables_cache|=item->used_tables(); const_item_cache&=item->const_item(); } void Item_func_make_set::update_used_tables() { Item_func::update_used_tables(); item->update_used_tables(); used_tables_cache|=item->used_tables(); const_item_cache&=item->const_item(); } String *Item_func_make_set::val_str(String *str) { ulonglong bits; bool first_found=0; Item **ptr=args; String *result=&empty_string; bits=item->val_int(); if ((null_value=item->null_value)) return NULL; if (arg_count < 64) bits &= ((ulonglong) 1 << arg_count)-1; for (; bits; bits >>= 1, ptr++) { if (bits & 1) { String *res= (*ptr)->val_str(str); if (res) // Skip nulls { if (!first_found) { // First argument first_found=1; if (res != str) result=res; // Use original string else { if (tmp_str.copy(*res)) // Don't use 'str' return &empty_string; result= &tmp_str; } } else { if (result != &tmp_str) { // Copy data to tmp_str if (tmp_str.alloc(result->length()+res->length()+1) || tmp_str.copy(*result)) return &empty_string; result= &tmp_str; } if (tmp_str.append(',') || tmp_str.append(*res)) return &empty_string; } } } } return result; } String *Item_func_char::val_str(String *str) { str->length(0); for (uint i=0 ; i < arg_count ; i++) { int32 num=(int32) args[i]->val_int(); if (!args[i]->null_value) #ifdef USE_MB if (use_mb(charset())) { if (num&0xFF000000L) { str->append((char)(num>>24)); goto b2; } else if (num&0xFF0000L) { b2: str->append((char)(num>>16)); goto b1; } else if (num&0xFF00L) { b1: str->append((char)(num>>8)); } } #endif str->append((char)num); } str->realloc(str->length()); // Add end 0 (for Purify) return str; } inline String* alloc_buffer(String *res,String *str,String *tmp_value, ulong length) { if (res->alloced_length() < length) { if (str->alloced_length() >= length) { (void) str->copy(*res); str->length(length); return str; } else { if (tmp_value->alloc(length)) return 0; (void) tmp_value->copy(*res); tmp_value->length(length); return tmp_value; } } res->length(length); return res; } void Item_func_repeat::fix_length_and_dec() { if (args[1]->const_item()) { max_length=(long) (args[0]->max_length * args[1]->val_int()); if (max_length >= MAX_BLOB_WIDTH) { max_length=MAX_BLOB_WIDTH; maybe_null=1; } } else { max_length=MAX_BLOB_WIDTH; maybe_null=1; } } /* ** Item_func_repeat::str is carefully written to avoid reallocs ** as much as possible at the cost of a local buffer */ String *Item_func_repeat::val_str(String *str) { uint length,tot_length; char *to; long count= (long) args[1]->val_int(); String *res =args[0]->val_str(str); if (args[0]->null_value || args[1]->null_value) goto err; // string and/or delim are null null_value=0; if (count <= 0) // For nicer SQL code return &empty_string; if (count == 1) // To avoid reallocs return res; length=res->length(); // Safe length check if (length > current_thd->variables.max_allowed_packet/count) goto err; // Probably an error tot_length= length*(uint) count; if (!(res= alloc_buffer(res,str,&tmp_value,tot_length))) goto err; to=(char*) res->ptr()+length; while (--count) { memcpy(to,res->ptr(),length); to+=length; } return (res); err: null_value=1; return 0; } void Item_func_rpad::fix_length_and_dec() { if (args[1]->const_item()) { uint32 length= (uint32) args[1]->val_int(); max_length=max(args[0]->max_length,length); if (max_length >= MAX_BLOB_WIDTH) { max_length=MAX_BLOB_WIDTH; maybe_null=1; } } else { max_length=MAX_BLOB_WIDTH; maybe_null=1; } } String *Item_func_rpad::val_str(String *str) { uint32 res_length,length_pad; char *to; const char *ptr_pad; int32 count= (int32) args[1]->val_int(); String *res =args[0]->val_str(str); String *rpad = args[2]->val_str(str); if (!res || args[1]->null_value || !rpad) goto err; null_value=0; if (count <= (int32) (res_length=res->length())) { // String to pad is big enough res->length(count); // Shorten result if longer return (res); } length_pad= rpad->length(); if ((ulong) count > current_thd->variables.max_allowed_packet || args[2]->null_value || !length_pad) goto err; if (!(res= alloc_buffer(res,str,&tmp_value,count))) goto err; to= (char*) res->ptr()+res_length; ptr_pad=rpad->ptr(); for (count-= res_length; (uint32) count > length_pad; count-= length_pad) { memcpy(to,ptr_pad,length_pad); to+= length_pad; } memcpy(to,ptr_pad,(size_t) count); return (res); err: null_value=1; return 0; } void Item_func_lpad::fix_length_and_dec() { if (args[1]->const_item()) { uint32 length= (uint32) args[1]->val_int(); max_length=max(args[0]->max_length,length); if (max_length >= MAX_BLOB_WIDTH) { max_length=MAX_BLOB_WIDTH; maybe_null=1; } } else { max_length=MAX_BLOB_WIDTH; maybe_null=1; } } String *Item_func_lpad::val_str(String *str) { uint32 res_length,length_pad; char *to; const char *ptr_pad; ulong count= (long) args[1]->val_int(); String *res= args[0]->val_str(str); String *lpad= args[2]->val_str(str); if (!res || args[1]->null_value || !lpad) goto err; null_value=0; if (count <= (res_length=res->length())) { // String to pad is big enough res->length(count); // Shorten result if longer return (res); } length_pad= lpad->length(); if (count > current_thd->variables.max_allowed_packet || args[2]->null_value || !length_pad) goto err; if (res->alloced_length() < count) { if (str->alloced_length() >= count) { memcpy((char*) str->ptr()+(count-res_length),res->ptr(),res_length); res=str; } else { if (tmp_value.alloc(count)) goto err; memcpy((char*) tmp_value.ptr()+(count-res_length),res->ptr(),res_length); res=&tmp_value; } } else bmove_upp((char*) res->ptr()+count,res->ptr()+res_length,res_length); res->length(count); to= (char*) res->ptr(); ptr_pad= lpad->ptr(); for (count-= res_length; count > length_pad; count-= length_pad) { memcpy(to,ptr_pad,length_pad); to+= length_pad; } memcpy(to,ptr_pad,(size_t) count); return (res); err: null_value=1; return 0; } String *Item_func_conv::val_str(String *str) { String *res= args[0]->val_str(str); char *endptr,ans[65],*ptr; longlong dec; int from_base= (int) args[1]->val_int(); int to_base= (int) args[2]->val_int(); if (args[0]->null_value || args[1]->null_value || args[2]->null_value || abs(to_base) > 36 || abs(to_base) < 2 || abs(from_base) > 36 || abs(from_base) < 2 || !(res->length())) { null_value=1; return 0; } null_value=0; if (from_base < 0) dec= strtoll(res->c_ptr(),&endptr,-from_base); else dec= (longlong) strtoull(res->c_ptr(),&endptr,from_base); ptr= longlong2str(dec,ans,to_base); if (str->copy(ans,(uint32) (ptr-ans), thd_charset())) return &empty_string; return str; } String *Item_func_conv_charset::val_str(String *str) { my_wc_t wc; int cnvres; const uchar *s, *se; uchar *d, *d0, *de; uint32 dmaxlen; String *arg= args[0]->val_str(str); CHARSET_INFO *from,*to; if (!arg) { null_value=1; return 0; } null_value=0; from=arg->charset(); to=conv_charset; s=(const uchar*)arg->ptr(); se=s+arg->length(); dmaxlen=arg->length()*(to->mbmaxlen?to->mbmaxlen:1)+1; str->alloc(dmaxlen); d0=d=(unsigned char*)str->ptr(); de=d+dmaxlen; while( s < se && d < de){ cnvres=from->mb_wc(from,&wc,s,se); if (cnvres>0) { s+=cnvres; } else if (cnvres==MY_CS_ILSEQ) { s++; wc='?'; } else break; outp: cnvres=to->wc_mb(to,wc,d,de); if (cnvres>0) { d+=cnvres; } else if (cnvres==MY_CS_ILUNI && wc!='?') { wc='?'; goto outp; } else break; }; str->length((uint32) (d-d0)); str->set_charset(to); return str; } void Item_func_conv_charset::fix_length_and_dec() { max_length = args[0]->max_length*(conv_charset->mbmaxlen?conv_charset->mbmaxlen:1); set_charset(conv_charset); } String *Item_func_conv_charset3::val_str(String *str) { my_wc_t wc; int cnvres; const uchar *s, *se; uchar *d, *d0, *de; uint32 dmaxlen; String *arg= args[0]->val_str(str); String *to_cs= args[1]->val_str(str); String *from_cs= args[2]->val_str(str); CHARSET_INFO *from_charset; CHARSET_INFO *to_charset; if (!arg || args[0]->null_value || !to_cs || args[1]->null_value || !from_cs || args[2]->null_value || !(from_charset=get_charset_by_name(from_cs->ptr(), MYF(MY_WME))) || !(to_charset=get_charset_by_name(to_cs->ptr(), MYF(MY_WME)))) { null_value=1; return 0; } s=(const uchar*)arg->ptr(); se=s+arg->length(); dmaxlen=arg->length()*(to_charset->mbmaxlen?to_charset->mbmaxlen:1)+1; str->alloc(dmaxlen); d0=d=(unsigned char*)str->ptr(); de=d+dmaxlen; while( s < se && d < de){ cnvres=from_charset->mb_wc(from_charset,&wc,s,se); if (cnvres>0) { s+=cnvres; } else if (cnvres==MY_CS_ILSEQ) { s++; wc='?'; } else break; outp: cnvres=to_charset->wc_mb(to_charset,wc,d,de); if (cnvres>0) { d+=cnvres; } else if (cnvres==MY_CS_ILUNI && wc!='?') { wc='?'; goto outp; } else break; }; str->length((uint32) (d-d0)); str->set_charset(to_charset); return str; } bool Item_func_conv_charset::fix_fields(THD *thd,struct st_table_list *tables, Item **ref) { char buff[STACK_BUFF_ALLOC]; // Max argument in function used_tables_cache=0; const_item_cache=1; if (thd && check_stack_overrun(thd,buff)) return 0; // Fatal error if flag is set! if (args[0]->fix_fields(thd, tables, args)) return 1; maybe_null=args[0]->maybe_null; const_item_cache=args[0]->const_item(); set_charset(conv_charset); fix_length_and_dec(); return 0; } void Item_func_conv_charset3::fix_length_and_dec() { max_length = args[0]->max_length; } String *Item_func_set_collation::val_str(String *str) { str=args[0]->val_str(str); if ((null_value=args[0]->null_value)) return 0; str->set_charset(set_collation); return str; } bool Item_func_set_collation::fix_fields(THD *thd,struct st_table_list *tables, Item **ref) { char buff[STACK_BUFF_ALLOC]; // Max argument in function used_tables_cache=0; const_item_cache=1; if (thd && check_stack_overrun(thd,buff)) return 0; // Fatal error if flag is set! if (args[0]->fix_fields(thd, tables, args)) return 1; maybe_null=args[0]->maybe_null; set_charset(set_collation); with_sum_func= with_sum_func || args[0]->with_sum_func; used_tables_cache=args[0]->used_tables(); const_item_cache=args[0]->const_item(); fix_length_and_dec(); return 0; } bool Item_func_set_collation::eq(const Item *item, bool binary_cmp) const { /* Assume we don't have rtti */ if (this == item) return 1; if (item->type() != FUNC_ITEM) return 0; Item_func *item_func=(Item_func*) item; if (arg_count != item_func->arg_count || func_name() != item_func->func_name()) return 0; Item_func_set_collation *item_func_sc=(Item_func_set_collation*) item; if (set_collation != item_func_sc->set_collation) return 0; for (uint i=0; i < arg_count ; i++) if (!args[i]->eq(item_func_sc->args[i], binary_cmp)) return 0; return 1; } String *Item_func_charset::val_str(String *str) { String *res = args[0]->val_str(str); if ((null_value=(args[0]->null_value || !res->charset()))) return 0; str->copy(res->charset()->name,strlen(res->charset()->name),default_charset_info); return str; } String *Item_func_hex::val_str(String *str) { if (args[0]->result_type() != STRING_RESULT) { /* Return hex of unsigned longlong value */ longlong dec= args[0]->val_int(); char ans[65],*ptr; if ((null_value= args[0]->null_value)) return 0; ptr= longlong2str(dec,ans,16); if (str->copy(ans,(uint32) (ptr-ans),default_charset_info)) return &empty_string; // End of memory return str; } /* Convert given string to a hex string, character by character */ String *res= args[0]->val_str(str); const char *from, *end; char *to; if (!res || tmp_value.alloc(res->length()*2)) { null_value=1; return 0; } null_value=0; tmp_value.length(res->length()*2); for (from=res->ptr(), end=from+res->length(), to= (char*) tmp_value.ptr(); from != end ; from++, to+=2) { uint tmp=(uint) (uchar) *from; to[0]=_dig_vec[tmp >> 4]; to[1]=_dig_vec[tmp & 15]; } return &tmp_value; } #include <my_dir.h> // For my_stat String *Item_load_file::val_str(String *str) { String *file_name; File file; MY_STAT stat_info; DBUG_ENTER("load_file"); if (!(file_name= args[0]->val_str(str)) || !(current_thd->master_access & FILE_ACL) || !my_stat(file_name->c_ptr(), &stat_info, MYF(MY_WME))) goto err; if (!(stat_info.st_mode & S_IROTH)) { /* my_error(ER_TEXTFILE_NOT_READABLE, MYF(0), file_name->c_ptr()); */ goto err; } if (stat_info.st_size > (long) current_thd->variables.max_allowed_packet) { /* my_error(ER_TOO_LONG_STRING, MYF(0), file_name->c_ptr()); */ goto err; } if (tmp_value.alloc(stat_info.st_size)) goto err; if ((file = my_open(file_name->c_ptr(), O_RDONLY, MYF(0))) < 0) goto err; if (my_read(file, (byte*) tmp_value.ptr(), stat_info.st_size, MYF(MY_NABP))) { my_close(file, MYF(0)); goto err; } tmp_value.length(stat_info.st_size); my_close(file, MYF(0)); null_value = 0; return &tmp_value; err: null_value = 1; DBUG_RETURN(0); } String* Item_func_export_set::val_str(String* str) { ulonglong the_set = (ulonglong) args[0]->val_int(); String yes_buf, *yes; yes = args[1]->val_str(&yes_buf); String no_buf, *no; no = args[2]->val_str(&no_buf); String *sep = NULL, sep_buf ; uint num_set_values = 64; ulonglong mask = 0x1; str->length(0); /* Check if some argument is a NULL value */ if (args[0]->null_value || args[1]->null_value || args[2]->null_value) { null_value=1; return 0; } switch(arg_count) { case 5: num_set_values = (uint) args[4]->val_int(); if (num_set_values > 64) num_set_values=64; if (args[4]->null_value) { null_value=1; return 0; } /* Fall through */ case 4: if (!(sep = args[3]->val_str(&sep_buf))) // Only true if NULL { null_value=1; return 0; } break; case 3: sep_buf.set(",", 1, default_charset_info); sep = &sep_buf; } null_value=0; for (uint i = 0; i < num_set_values; i++, mask = (mask << 1)) { if (the_set & mask) str->append(*yes); else str->append(*no); if (i != num_set_values - 1) str->append(*sep); } return str; } void Item_func_export_set::fix_length_and_dec() { uint length=max(args[1]->max_length,args[2]->max_length); uint sep_length=(arg_count > 3 ? args[3]->max_length : 1); max_length=length*64+sep_length*63; } String* Item_func_inet_ntoa::val_str(String* str) { uchar buf[8], *p; ulonglong n = (ulonglong) args[0]->val_int(); char num[4]; /* We do not know if args[0] is NULL until we have called some val function on it if args[0] is not a constant! Also return null if n > 255.255.255.255 */ if ((null_value= (args[0]->null_value || n > (ulonglong) LL(4294967295)))) return 0; // Null value str->length(0); int4store(buf,n); /* Now we can assume little endian. */ num[3]='.'; for (p=buf+4 ; p-- > buf ; ) { uint c = *p; uint n1,n2; // Try to avoid divisions n1= c / 100; // 100 digits c-= n1*100; n2= c / 10; // 10 digits c-=n2*10; // last digit num[0]=(char) n1+'0'; num[1]=(char) n2+'0'; num[2]=(char) c+'0'; uint length=(n1 ? 4 : n2 ? 3 : 2); // Remove pre-zero (void) str->append(num+4-length,length); } str->length(str->length()-1); // Remove last '.'; return str; } /* QUOTE() function returns argument string in single quotes suitable for using in a SQL statement. DESCRIPTION Adds a \ before all characters that needs to be escaped in a SQL string. We also escape '^Z' (END-OF-FILE in windows) to avoid probelms when running commands from a file in windows. This function is very useful when you want to generate SQL statements RETURN VALUES str Quoted string NULL Argument to QUOTE() was NULL or out of memory. */ #define get_esc_bit(mask, num) (1 & (*((mask) + ((num) >> 3))) >> ((num) & 7)) String *Item_func_quote::val_str(String *str) { /* Bit mask that has 1 for set for the position of the following characters: 0, \, ' and ^Z */ static uchar escmask[32]= { 0x01, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; char *from, *to, *end, *start; String *arg= args[0]->val_str(str); uint arg_length, new_length; if (!arg) // Null argument goto null; arg_length= arg->length(); new_length= arg_length+2; /* for beginning and ending ' signs */ for (from= (char*) arg->ptr(), end= from + arg_length; from < end; from++) new_length+= get_esc_bit(escmask, *from); /* We have to use realloc() instead of alloc() as we want to keep the old result in str */ if (str->realloc(new_length)) goto null; /* As 'arg' and 'str' may be the same string, we must replace characters from the end to the beginning */ to= (char*) str->ptr() + new_length - 1; *to--= '\''; for (start= (char*) arg->ptr() ; end-- != start; to--) { /* We can't use the bitmask here as we want to replace \O and ^Z with 0 and Z */ switch (*end) { case 0: *to--= '0'; *to= '\\'; break; case '\032': *to--= 'Z'; *to= '\\'; break; case '\'': case '\\': *to--= *end; *to= '\\'; break; default: *to= *end; break; } } *to= '\''; str->length(new_length); return str; null: null_value= 1; return 0; } /******************************************************* General functions for spatial objects ********************************************************/ String *Item_func_geometry_from_text::val_str(String *str) { Geometry geom; String *wkt = args[0]->val_str(str); GTextReadStream trs(wkt->ptr(), wkt->length()); str->length(0); if ((null_value=(args[0]->null_value || geom.create_from_wkt(&trs, str, 0)))) return 0; return str; } void Item_func_geometry_from_text::fix_length_and_dec() { max_length=MAX_BLOB_WIDTH; } String *Item_func_as_text::val_str(String *str) { String *wkt = args[0]->val_str(str); Geometry geom; if ((null_value=(args[0]->null_value || geom.create_from_wkb(wkt->ptr(),wkt->length())))) return 0; str->length(0); if ((null_value=geom.as_wkt(str))) return 0; return str; } void Item_func_as_text::fix_length_and_dec() { max_length=MAX_BLOB_WIDTH; } String *Item_func_geometry_type::val_str(String *str) { String *wkt = args[0]->val_str(str); Geometry geom; if ((null_value=(args[0]->null_value || geom.create_from_wkb(wkt->ptr(),wkt->length())))) return 0; str->copy(geom.get_class_info()->m_name, strlen(geom.get_class_info()->m_name), default_charset_info); return str; } String *Item_func_envelope::val_str(String *str) { String *wkb = args[0]->val_str(str); Geometry geom; null_value = args[0]->null_value || geom.create_from_wkb(wkb->ptr(),wkb->length()) || geom.envelope(str); return null_value ? 0 : str; } String *Item_func_centroid::val_str(String *str) { String *wkb = args[0]->val_str(str); Geometry geom; null_value = args[0]->null_value || geom.create_from_wkb(wkb->ptr(),wkb->length()) || !GEOM_METHOD_PRESENT(geom,centroid) || geom.centroid(str); return null_value ? 0: str; } /*********************************************** Spatial decomposition functions ***********************************************/ String *Item_func_spatial_decomp::val_str(String *str) { String *wkb = args[0]->val_str(str); Geometry geom; if ((null_value = (args[0]->null_value || geom.create_from_wkb(wkb->ptr(),wkb->length())))) return 0; null_value=1; switch(decomp_func) { case SP_STARTPOINT: if (!GEOM_METHOD_PRESENT(geom,start_point) || geom.start_point(str)) goto ret; break; case SP_ENDPOINT: if (!GEOM_METHOD_PRESENT(geom,end_point) || geom.end_point(str)) goto ret; break; case SP_EXTERIORRING: if (!GEOM_METHOD_PRESENT(geom,exterior_ring) || geom.exterior_ring(str)) goto ret; break; default: goto ret; } null_value=0; ret: return null_value ? 0 : str; } String *Item_func_spatial_decomp_n::val_str(String *str) { String *wkb = args[0]->val_str(str); long n = (long) args[1]->val_int(); Geometry geom; if ((null_value = (args[0]->null_value || args[1]->null_value || geom.create_from_wkb(wkb->ptr(),wkb->length()) ))) return 0; null_value=1; switch(decomp_func_n) { case SP_POINTN: if (!GEOM_METHOD_PRESENT(geom,point_n) || geom.point_n(n,str)) goto ret; break; case SP_GEOMETRYN: if (!GEOM_METHOD_PRESENT(geom,geometry_n) || geom.geometry_n(n,str)) goto ret; break; case SP_INTERIORRINGN: if (!GEOM_METHOD_PRESENT(geom,interior_ring_n) || geom.interior_ring_n(n,str)) goto ret; break; default: goto ret; } null_value=0; ret: return null_value ? 0 : str; } /*********************************************** Functions to concatinate various spatial objects ************************************************/ /* * Concatinate doubles into Point */ String *Item_func_point::val_str(String *str) { if ( (null_value = (args[0]->null_value || args[1]->null_value || str->realloc(1+4+8+8)))) return 0; str->length(0); str->q_append((char)Geometry::wkbNDR); str->q_append((uint32)Geometry::wkbPoint); str->q_append((double)args[0]->val()); str->q_append((double)args[1]->val()); return str; } /* Concatinates various items into various collections with checkings for valid wkb type of items. For example, MultiPoint can be a collection of Points only. coll_type contains wkb type of target collection. item_type contains a valid wkb type of items. In the case when coll_type is wkbGeometryCollection, we do not check wkb type of items, any is valid. */ String *Item_func_spatial_collection::val_str(String *str) { uint i; null_value=1; str->length(0); if(str->reserve(9,512)) return 0; str->q_append((char)Geometry::wkbNDR); str->q_append((uint32)coll_type); str->q_append((uint32)arg_count); for (i = 0; i < arg_count; ++i) { if (args[i]->null_value) goto ret; String *res = args[i]->val_str(str); if ( coll_type == Geometry::wkbGeometryCollection ) { /* In the case of GeometryCollection we don't need any checkings for item types, so just copy them into target collection */ if ((null_value=(str->reserve(res->length(),512)))) goto ret; str->q_append(res->ptr(),res->length()); } else { enum Geometry::wkbType wkb_type; uint32 len=res->length(); const char *data=res->ptr()+1; /* In the case of named collection we must to check that items are of specific type, let's do this checking now */ if (len < 5) goto ret; wkb_type= (Geometry::wkbType) uint4korr(data); data+=4; len-=5; if (wkb_type != item_type) goto ret; switch (coll_type) { case Geometry::wkbMultiPoint: case Geometry::wkbMultiLineString: case Geometry::wkbMultiPolygon: if (len < WKB_HEADER_SIZE) goto ret; data+=WKB_HEADER_SIZE; len-=WKB_HEADER_SIZE; if (str->reserve(len,512)) goto ret; str->q_append(data,len); break; case Geometry::wkbLineString: if (str->reserve(POINT_DATA_SIZE,512)) goto ret; str->q_append(data,POINT_DATA_SIZE); break; case Geometry::wkbPolygon: { uint32 n_points; double x1, y1, x2, y2; if (len < WKB_HEADER_SIZE + 4 + 8 + 8) goto ret; data+=WKB_HEADER_SIZE; len-=WKB_HEADER_SIZE; uint32 llen=len; const char *ldata=data; n_points=uint4korr(data); data+=4; float8get(x1,data); data+=8; float8get(y1,data); data+=8; len-= 4 + 8 + 8; if (len < n_points * POINT_DATA_SIZE) goto ret; data+=(n_points-2) * POINT_DATA_SIZE; float8get(x2,data); float8get(y2,data+8); if ((x1 != x2) || (y1 != y2)) goto ret; if (str->reserve(llen,512)) goto ret; str->q_append(ldata, llen); } break; default: goto ret; } } } if (str->length() > current_thd->variables.max_allowed_packet) goto ret; null_value = 0; ret: return null_value ? 0 : str; }