/* Copyright (C) 1979-1996 TcX AB & Monty Program KB & Detron HB This software is distributed with NO WARRANTY OF ANY KIND. No author or distributor accepts any responsibility for the consequences of using it, or for whether it serves any particular purpose or works at all, unless he or she says so in writing. Refer to the Free Public License (the "License") for full details. Every copy of this file must include a copy of the License, normally in a plain ASCII text file named PUBLIC. The License grants you the right to copy, modify and redistribute this file, but only under certain conditions described in the License. Among other things, the License requires that the copyright notice and this notice be preserved on all copies. */ /* This file defines all string functions ** Warning: Some string functions doesn't always put and end-null on a String ** (This shouldn't be neaded) */ #ifdef __GNUC__ #pragma implementation // gcc: Class implementation #endif #include "mysql_priv.h" #include #ifdef HAVE_CRYPT_H #include #endif String empty_string(""); uint nr_of_decimals(const char *str) { if ((str=strchr(str,'.'))) { const char *start= ++str; for ( ; isdigit(*str) ; str++) ; return (uint) (str-start); } return 0; } double Item_str_func::val() { String *res; res=str(&str_value); return res ? atof(res->c_ptr()) : 0.0; } longlong Item_str_func::val_int() { String *res; res=str(&str_value); return res ? strtoll(res->c_ptr(),NULL,10) : (longlong) 0; } /* ** Concatinate args with the following premissess ** If only one arg which is ok, return value of arg ** Don't reallocate str() if not absolute necessary. */ String *Item_func_concat::str(String *str) { String *res,*res2,*use_as_buff; uint i; null_value=0; if (!(res=args[0]->str(str))) goto null; use_as_buff= &tmp_value; for (i=1 ; i < arg_count ; i++) { if (res->length() == 0) { if (!(res=args[i]->str(str))) goto null; } else { if (!(res2=args[i]->str(use_as_buff))) goto null; if (res2->length() == 0) continue; if (res->length()+res2->length() > 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()) { str->copy(*res); str->append(*res2); res=str; } else if (res == &tmp_value) { res->append(*res2); // Must be a blob } else if (res2 == &tmp_value) { // This can happend only 1 time tmp_value.replace(0,0,*res); 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 real seldom tmp_value.copy(*res); // Move res to start of tmp tmp_value.replace(0,0,*res); res= &tmp_value; use_as_buff=str; // Put next arg here } else { // Two big const strings if (tmp_value.alloc(max_length)) return &empty_string; /* purecov: inspected */ tmp_value.copy(*res); tmp_value.append(*res2); res= &tmp_value; 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; } } String *Item_func_reverse::str(String *str) { String *res = args[0]->str(str); char *ptr,*end; if ((null_value=args[0]->null_value)) return 0; res=copy_if_not_alloced(str,res,res->length()); ptr = (char *) res->ptr(); end=ptr+res->length()-1; for (; ptr < end ; ptr++,end--) { char 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 str() if not neaded */ String *Item_func_replace::str(String *str) { String *res,*res2,*res3; int offset; uint from_length,to_length; bool alloced=0; null_value=0; res=args[0]->str(str); if (args[0]->null_value) goto null; res2=args[1]->str(&tmp_value); if (args[1]->null_value) goto null; if (res2->length() == 0) return res; if ((offset=res->strstr(*res2)) < 0) return res; if (!(res3=args[2]->str(&tmp_value2))) goto null; from_length= res2->length(); to_length= res3->length(); do { if (res->length()-from_length + to_length > 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::str(String *str) { String *res,*res2; uint start,length; null_value=0; res=args[0]->str(str); res2=args[3]->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 */ if (start > res->length()+1) return res; // Wrong param; skipp insert if (length > res->length()-start) length=res->length()-start; if (res->length() - length + res2->length() > 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::str(String *str) { String *res; if (!(res=args[0]->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::str(String *str) { String *res; if (!(res=args[0]->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::str(String *str) { String *res =args[0]->str(str); long length =(long) args[1]->val_int(); if ((null_value=args[0]->null_value)) return 0; if (length <= 0) return &empty_string; 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 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::str(String *str) { String *res =args[0]->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 */ 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::str(String *str) { String *res = args[0]->str(str); int32 start = (int32) args[1]->val_int(); 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 */ if (start <= 0 || (uint) start > res->length() || length <= 0) return &empty_string; start--; 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::str(String *str) { String *res =args[0]->str(str); String *delimeter =args[1]->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 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) { 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::str(String *str) { String *res =args[0]->str(str); if ((null_value=args[0]->null_value)) return 0; /* purecov: inspected */ char buff[MAX_FIELD_WIDTH]; String tmp(buff,sizeof(buff)); String *remove=args[1]->str(&tmp); uint remove_length; LINT_INIT(remove_length); if (!remove || (remove_length=remove->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)[0]; while (ptr != end && *ptr == chr) ptr++; } else { const char *r_ptr=remove->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::str(String *str) { String *res =args[0]->str(str); if ((null_value=args[0]->null_value)) return 0; /* purecov: inspected */ char buff[MAX_FIELD_WIDTH]; String tmp(buff,sizeof(buff)); String *remove=args[1]->str(&tmp); uint remove_length; LINT_INIT(remove_length); if (!remove || (remove_length=remove->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)[0]; while (ptr != end && end[-1] == chr) end--; } else { const char *r_ptr=remove->ptr(); 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-ptr)); return &tmp_value; } String *Item_func_trim::str(String *str) { String *res =args[0]->str(str); if ((null_value=args[0]->null_value)) return 0; /* purecov: inspected */ char buff[MAX_FIELD_WIDTH]; String tmp(buff,sizeof(buff)); String *remove=args[1]->str(&tmp); uint remove_length; LINT_INIT(remove_length); if (!remove || (remove_length=remove->length()) == 0 || remove_length > res->length()) return res; char *ptr=(char*) res->ptr(); char *end=ptr+res->length(); const char *r_ptr=remove->ptr(); while (ptr+remove_length < end && !memcmp(ptr,r_ptr,remove_length)) ptr+=remove_length; 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::str(String *str) { String *res =args[0]->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); return str; } #define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.') String *Item_func_encrypt::str(String *str) { String *res =args[0]->str(str); #ifdef HAVE_CRYPT char salt[2]; 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(timestamp & 0x3f); salt[1] = bin_to_ascii((timestamp >> 5) & 0x3f); } else { // obtain salt from the first two bytes String *salt_str=args[1]->str(&tmp_value); if ((null_value= (args[1]->null_value || salt_str->length() < 2))) return 0; salt[0]=(*salt_str)[0]; salt[1]=(*salt_str)[1]; } str->set(crypt(res->c_ptr(),salt),13); 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::str(String *str) { String *res; if (!(res=args[0]->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::str(String *str) { String *res; if (!(res=args[0]->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::str(String *str) { if (!current_thd->db) str->length(0); else str->set((const char*) current_thd->db,strlen(current_thd->db)); return str; } String *Item_func_user::str(String *str) { THD *thd=current_thd; str->copy((const char*) thd->user,strlen(thd->user)); str->append('@'); str->append(thd->host ? thd->host : thd->ip ? thd->ip : ""); 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 skipp to next char else return 0 */ extern "C" { extern char *soundex_map; // In mysys/static.c } static char get_scode(char *ptr) { uchar ch=toupper(*ptr); if (ch < 'A' || ch > 'Z') { // Thread extended alfa (country spec) return '0'; // as vokal } return(soundex_map[ch-'A']); } String *Item_func_soundex::str(String *str) { String *res =args[0]->str(str); char last_ch,ch; if ((null_value=args[0]->null_value)) return 0; /* purecov: inspected */ if (str_value.alloc(max(res->length(),4))) return str; /* purecov: inspected */ char *to= (char *) str_value.ptr(); char *from= (char *) res->ptr(), *end=from+res->length(); while (from != end && isspace(*from)) // Skipp pre-space from++; /* purecov: inspected */ if (from == end) return &empty_string; // No alpha characters. *to++ = toupper(*from); // Copy first letter last_ch = get_scode(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 (!isalpha(*from)) continue; ch=get_scode(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*) str_value.ptr()+4 ; to < end ; to++) *to = '0'; *to=0; // end string str_value.length((uint) (to-str_value.ptr())); return &str_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::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); 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 used_tables_cache|=item->used_tables(); } void Item_func_elt::update_used_tables() { Item_func::update_used_tables(); item->update_used_tables(); used_tables_cache|=item->used_tables(); } 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::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]->str(str); } void Item_func_make_set::fix_length_and_dec() { max_length=arg_count-1; decimals=0; for (uint i=1 ; i < arg_count ; i++) max_length+=args[i]->max_length; used_tables_cache|=item->used_tables(); } void Item_func_make_set::update_used_tables() { Item_func::update_used_tables(); item->update_used_tables(); used_tables_cache|=item->used_tables(); } String *Item_func_make_set::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)->str(str); if (res) // Skipp nulls { if (!first_found) { // First argument first_found=1; if (res != str) result=res; // Use original string else { tmp_str.copy(*res); // Don't use 'str' result= &tmp_str; } } else { if (result != &tmp_str) { // Copy data to tmp_str tmp_str.alloc(result->length()+res->length()+1); tmp_str.copy(*result); result= &tmp_str; } tmp_str.append(','); tmp_str.append(*res); } } } } return result; } String *Item_func_char::str(String *str) { str->length(0); for (uint i=0 ; i < arg_count ; i++) { char num= (char) args[i]->val_int(); if (!args[i]->null_value) str->append(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) { str->copy(*res); str->length(length); return str; } else { if (tmp_value->alloc(length)) return 0; 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::str(String *str) { uint length,tot_length; char *to; long count= (long) args[1]->val_int(); String *res =args[0]->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(); if (length > max_allowed_packet/count)// Safe length check 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::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]->str(str); String *rpad = args[2]->str(str); if (args[0]->null_value || args[1]->null_value) goto err; null_value=0; if (count <= (int32) (res_length=res->length())) return (res); // String to pad is big enough length_pad= rpad->length(); if ((ulong) count > 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::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]->str(str); String *lpad= args[2]->str(str); if (args[0]->null_value || args[1]->null_value) goto err; null_value=0; if (count <= (res_length=res->length())) return (res); // String to pad is big enough length_pad= lpad->length(); if (count > 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::str(String *str) { String *res= args[0]->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); res->copy(ans,(uint32) (ptr-ans)); return res; }