diff options
Diffstat (limited to 'sql/sql_analyse.cc')
-rw-r--r-- | sql/sql_analyse.cc | 1240 |
1 files changed, 1240 insertions, 0 deletions
diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc new file mode 100644 index 00000000..4c853689 --- /dev/null +++ b/sql/sql_analyse.cc @@ -0,0 +1,1240 @@ +/* + Copyright (c) 2000, 2013, Oracle and/or its affiliates. + + 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; version 2 of the License. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ + + +/* Analyse database */ + +/* TODO: - Check if any character fields can be of any date type +** (date, datetime, year, time, timestamp, newdate) +** - Check if any number field should be a timestamp +** - type set is out of optimization yet +*/ + +#ifdef USE_PRAGMA_IMPLEMENTATION +#pragma implementation // gcc: Class implementation +#endif + +#define MYSQL_LEX 1 + +#include "mariadb.h" +#include "sql_priv.h" +#include "procedure.h" +#include "sql_analyse.h" +#include <m_ctype.h> + +#define MAX_TREEMEM 8192 +#define MAX_TREE_ELEMENTS 256 + +int sortcmp2(void* cmp_arg __attribute__((unused)), + const String *a,const String *b) +{ + return sortcmp(a,b,a->charset()); +} + +int compare_double2(void* cmp_arg __attribute__((unused)), + const double *s, const double *t) +{ + return compare_double(s,t); +} + +int compare_longlong2(void* cmp_arg __attribute__((unused)), + const longlong *s, const longlong *t) +{ + return compare_longlong(s,t); +} + +int compare_ulonglong2(void* cmp_arg __attribute__((unused)), + const ulonglong *s, const ulonglong *t) +{ + return compare_ulonglong(s,t); +} + +int compare_decimal2(int* len, const char *s, const char *t) +{ + return memcmp(s, t, *len); +} + + +static bool +prepare_param(THD *thd, Item **item, const char *proc_name, uint pos) +{ + if ((*item)->fix_fields_if_needed(thd, item)) + { + DBUG_PRINT("info", ("fix_fields() for the parameter %u failed", pos)); + return true; + } + if ((*item)->type_handler()->result_type() != INT_RESULT || + !(*item)->basic_const_item() || + (*item)->val_real() < 0) + { + my_error(ER_WRONG_PARAMETERS_TO_PROCEDURE, MYF(0), proc_name); + return true; + } + return false; +} + + +Procedure * +proc_analyse_init(THD *thd, ORDER *param, select_result *result, + List<Item> &field_list) +{ + const char *proc_name = (*param->item)->name.str; + analyse *pc = new analyse(result); + field_info **f_info; + DBUG_ENTER("proc_analyse_init"); + + if (!pc) + DBUG_RETURN(0); + + if (!(param = param->next)) + { + pc->max_tree_elements = MAX_TREE_ELEMENTS; + pc->max_treemem = MAX_TREEMEM; + } + else if (param->next) + { + // first parameter + if (prepare_param(thd, param->item, proc_name, 0)) + goto err; + pc->max_tree_elements = (uint) (*param->item)->val_int(); + param = param->next; + if (param->next) // no third parameter possible + { + my_error(ER_WRONG_PARAMCOUNT_TO_PROCEDURE, MYF(0), proc_name); + goto err; + } + // second parameter + if (prepare_param(thd, param->item, proc_name, 1)) + goto err; + pc->max_treemem = (uint) (*param->item)->val_int(); + } + else if (prepare_param(thd, param->item, proc_name, 0)) + goto err; + // if only one parameter was given, it will be the value of max_tree_elements + else + { + pc->max_tree_elements = (uint) (*param->item)->val_int(); + pc->max_treemem = MAX_TREEMEM; + } + + if (!(pc->f_info= + (field_info**) thd->alloc(sizeof(field_info*) * field_list.elements))) + goto err; + pc->f_end = pc->f_info + field_list.elements; + pc->fields = field_list; + + { + List_iterator_fast<Item> it(pc->fields); + f_info = pc->f_info; + + Item *item; + while ((item = it++)) + { + field_info *new_field; + switch (item->result_type()) { + case INT_RESULT: + // Check if fieldtype is ulonglong + if (item->type() == Item::FIELD_ITEM && + ((Item_field*) item)->field->type() == MYSQL_TYPE_LONGLONG && + ((Field_longlong*) ((Item_field*) item)->field)->unsigned_flag) + new_field= new field_ulonglong(item, pc); + else + new_field= new field_longlong(item, pc); + break; + case REAL_RESULT: + new_field= new field_real(item, pc); + break; + case DECIMAL_RESULT: + new_field= new field_decimal(item, pc); + break; + case STRING_RESULT: + new_field= new field_str(item, pc); + break; + default: + goto err; + } + *f_info++= new_field; + } + } + DBUG_RETURN(pc); +err: + delete pc; + DBUG_RETURN(0); +} + + +/* + Return 1 if number, else return 0 + store info about found number in info + NOTE:It is expected, that elements of 'info' are all zero! +*/ + +bool test_if_number(NUM_INFO *info, const char *str, uint str_len) +{ + const char *begin, *end= str + str_len; + DBUG_ENTER("test_if_number"); + + /* + MySQL removes any endspaces of a string, so we must take care only of + spaces in front of a string + */ + for (; str != end && my_isspace(system_charset_info, *str); str++) ; + if (str == end) + DBUG_RETURN(0); + + if (*str == '-') + { + info->negative = 1; + if (++str == end || *str == '0') // converting -0 to a number + DBUG_RETURN(0); // might lose information + } + else + info->negative = 0; + begin = str; + for (; str != end && my_isdigit(system_charset_info,*str); str++) + { + if (!info->integers && *str == '0' && (str + 1) != end && + my_isdigit(system_charset_info,*(str + 1))) + info->zerofill = 1; // could be a postnumber for example + info->integers++; + } + if (str == end && info->integers) + { + char *endpos= (char*) end; + int error; + info->ullval= (ulonglong) my_strtoll10(begin, &endpos, &error); + if (info->integers == 1) + DBUG_RETURN(0); // single number can't be zerofill + info->maybe_zerofill = 1; + DBUG_RETURN(1); // a zerofill number, or an integer + } + if (*str == '.' || *str == 'e' || *str == 'E') + { + if (info->zerofill) // can't be zerofill anymore + DBUG_RETURN(0); + if ((str + 1) == end) // number was something like '123[.eE]' + { + char *endpos= (char*) str; + int error; + info->ullval= (ulonglong) my_strtoll10(begin, &endpos, &error); + DBUG_RETURN(1); + } + if (*str == 'e' || *str == 'E') // number may be something like '1e+50' + { + str++; + if (*str != '-' && *str != '+') + DBUG_RETURN(0); + for (str++; str != end && my_isdigit(system_charset_info,*str); str++) ; + if (str == end) + { + info->is_float = 1; // we can't use variable decimals here + DBUG_RETURN(1); + } + DBUG_RETURN(0); + } + for (str++; *(end - 1) == '0'; end--) // jump over zeros at the end + ; + if (str == end) // number was something like '123.000' + { + char *endpos= (char*) str; + int error; + info->ullval= (ulonglong) my_strtoll10(begin, &endpos, &error); + DBUG_RETURN(1); + } + for (; str != end && my_isdigit(system_charset_info,*str); str++) + info->decimals++; + if (str == end) + { + info->dval = my_atof(begin); + DBUG_RETURN(1); + } + } + DBUG_RETURN(0); +} + + +/* + Stores the biggest and the smallest value from current 'info' + to ev_num_info + If info contains an ulonglong number, which is bigger than + biggest positive number able to be stored in a longlong variable + and is marked as negative, function will return 0, else 1. +*/ + +bool get_ev_num_info(EV_NUM_INFO *ev_info, NUM_INFO *info, const char *num) +{ + if (info->negative) + { + if (((longlong) info->ullval) < 0) + return 0; // Impossible to store as a negative number + ev_info->llval = -(longlong) MY_MAX((ulonglong) -ev_info->llval, + info->ullval); + ev_info->min_dval = (double) -MY_MAX(-ev_info->min_dval, info->dval); + } + else // ulonglong is as big as bigint in MySQL + { + if ((check_ulonglong(num, info->integers) == DECIMAL_NUM)) + return 0; + ev_info->ullval = (ulonglong) MY_MAX(ev_info->ullval, info->ullval); + ev_info->max_dval = (double) MY_MAX(ev_info->max_dval, info->dval); + } + return 1; +} // get_ev_num_info + + +int free_string(void* str, TREE_FREE, void*) +{ + ((String*)str)->free(); + return 0; +} + + +void field_str::add() +{ + char buff[MAX_FIELD_WIDTH], *ptr; + String s(buff, sizeof(buff),&my_charset_bin), *res; + ulong length; + + if (!(res = item->val_str(&s))) + { + nulls++; + return; + } + + if (!(length = res->length())) + empty++; + else + { + ptr = (char*) res->ptr(); + if (*(ptr + (length - 1)) == ' ') + must_be_blob = 1; + } + + if (can_be_still_num) + { + bzero((char*) &num_info, sizeof(num_info)); + if (!test_if_number(&num_info, res->ptr(), (uint) length)) + can_be_still_num = 0; + if (!found) + { + bzero((char*) &ev_num_info, sizeof(ev_num_info)); + was_zero_fill = num_info.zerofill; + } + else if (num_info.zerofill != was_zero_fill && !was_maybe_zerofill) + can_be_still_num = 0; // one more check needed, when length is counted + if (can_be_still_num) + can_be_still_num = get_ev_num_info(&ev_num_info, &num_info, res->ptr()); + was_maybe_zerofill = num_info.maybe_zerofill; + } + + /* Update min and max arguments */ + if (!found) + { + found = 1; + min_arg.copy(*res); + max_arg.copy(*res); + min_length = max_length = length; sum=length; + } + else if (length) + { + sum += length; + if (length < min_length) + min_length = length; + if (length > max_length) + max_length = length; + + if (sortcmp(res, &min_arg,item->collation.collation) < 0) + min_arg.copy(*res); + if (sortcmp(res, &max_arg,item->collation.collation) > 0) + max_arg.copy(*res); + } + + if (room_in_tree) + { + if (res != &s) + s.copy(*res); + if (!tree_search(&tree, (void*) &s, tree.custom_arg)) // If not in tree + { + s.copy(); // slow, when SAFE_MALLOC is in use + if (!tree_insert(&tree, (void*) &s, 0, tree.custom_arg)) + { + room_in_tree = 0; // Remove tree, out of RAM ? + delete_tree(&tree, 0); + } + else + { + bzero((char*) &s, sizeof(s)); // Let tree handle free of this + if ((treemem += length) > pc->max_treemem) + { + room_in_tree = 0; // Remove tree, too big tree + delete_tree(&tree, 0); + } + } + } + } + + if ((num_info.zerofill && (max_length != min_length)) || + (was_zero_fill && (max_length != min_length))) + can_be_still_num = 0; // zerofilled numbers must be of same length +} // field_str::add + + +void field_real::add() +{ + char buff[MAX_FIELD_WIDTH], *ptr, *end; + double num= item->val_real(); + uint length, zero_count, decs; + TREE_ELEMENT *element; + + if (item->null_value) + { + nulls++; + return; + } + if (num == 0.0) + empty++; + + if ((decs = decimals()) >= FLOATING_POINT_DECIMALS) + { + length= snprintf(buff, sizeof(buff), "%g", num); + if (rint(num) != num) + max_notzero_dec_len = 1; + } + else + { + buff[sizeof(buff)-1]=0; // Safety + snprintf(buff, sizeof(buff)-1, "%-.*f", (int) decs, num); + length = (uint) strlen(buff); + + // We never need to check further than this + end = buff + length - 1 - decs + max_notzero_dec_len; + + zero_count = 0; + for (ptr = buff + length - 1; ptr > end && *ptr == '0'; ptr--) + zero_count++; + + if ((decs - zero_count > max_notzero_dec_len)) + max_notzero_dec_len = decs - zero_count; + } + + if (room_in_tree) + { + if (!(element = tree_insert(&tree, (void*) &num, 0, tree.custom_arg))) + { + room_in_tree = 0; // Remove tree, out of RAM ? + delete_tree(&tree, 0); + } + /* + if element->count == 1, this element can be found only once from tree + if element->count == 2, or more, this element is already in tree + */ + else if (element->count == 1 && (tree_elements++) >= pc->max_tree_elements) + { + room_in_tree = 0; // Remove tree, too many elements + delete_tree(&tree, 0); + } + } + + if (!found) + { + found = 1; + min_arg = max_arg = sum = num; + sum_sqr = num * num; + min_length = max_length = length; + } + else if (num != 0.0) + { + sum += num; + sum_sqr += num * num; + if (length < min_length) + min_length = length; + if (length > max_length) + max_length = length; + if (compare_double(&num, &min_arg) < 0) + min_arg = num; + if (compare_double(&num, &max_arg) > 0) + max_arg = num; + } +} // field_real::add + + +void field_decimal::add() +{ + /*TODO - remove rounding stuff after decimal_div returns proper frac */ + VDec vdec(item); + uint length; + TREE_ELEMENT *element; + + if (vdec.is_null()) + { + nulls++; + return; + } + + my_decimal dec; + vdec.round_to(&dec, item->decimals, HALF_UP); + + length= my_decimal_string_length(&dec); + + if (decimal_is_zero(&dec)) + empty++; + + if (room_in_tree) + { + uchar buf[DECIMAL_MAX_FIELD_SIZE]; + dec.to_binary(buf, item->max_length, item->decimals); + if (!(element = tree_insert(&tree, (void*)buf, 0, tree.custom_arg))) + { + room_in_tree = 0; // Remove tree, out of RAM ? + delete_tree(&tree, 0); + } + /* + if element->count == 1, this element can be found only once from tree + if element->count == 2, or more, this element is already in tree + */ + else if (element->count == 1 && (tree_elements++) >= pc->max_tree_elements) + { + room_in_tree = 0; // Remove tree, too many elements + delete_tree(&tree, 0); + } + } + + if (!found) + { + found = 1; + min_arg = max_arg = sum[0] = dec; + my_decimal_mul(E_DEC_FATAL_ERROR, sum_sqr, &dec, &dec); + cur_sum= 0; + min_length = max_length = length; + } + else if (!decimal_is_zero(&dec)) + { + int next_cur_sum= cur_sum ^ 1; + my_decimal sqr_buf; + + my_decimal_add(E_DEC_FATAL_ERROR, sum+next_cur_sum, sum+cur_sum, &dec); + my_decimal_mul(E_DEC_FATAL_ERROR, &sqr_buf, &dec, &dec); + my_decimal_add(E_DEC_FATAL_ERROR, + sum_sqr+next_cur_sum, sum_sqr+cur_sum, &sqr_buf); + cur_sum= next_cur_sum; + if (length < min_length) + min_length = length; + if (length > max_length) + max_length = length; + if (dec.cmp(&min_arg) < 0) + { + min_arg= dec; + } + if (dec.cmp(&max_arg) > 0) + { + max_arg= dec; + } + } +} + + +void field_longlong::add() +{ + char buff[MAX_FIELD_WIDTH]; + longlong num = item->val_int(); + uint length = (uint) (longlong10_to_str(num, buff, -10) - buff); + TREE_ELEMENT *element; + + if (item->null_value) + { + nulls++; + return; + } + if (num == 0) + empty++; + + if (room_in_tree) + { + if (!(element = tree_insert(&tree, (void*) &num, 0, tree.custom_arg))) + { + room_in_tree = 0; // Remove tree, out of RAM ? + delete_tree(&tree, 0); + } + /* + if element->count == 1, this element can be found only once from tree + if element->count == 2, or more, this element is already in tree + */ + else if (element->count == 1 && (tree_elements++) >= pc->max_tree_elements) + { + room_in_tree = 0; // Remove tree, too many elements + delete_tree(&tree, 0); + } + } + + if (!found) + { + found = 1; + min_arg = max_arg = sum = num; + sum_sqr = num * num; + min_length = max_length = length; + } + else if (num != 0) + { + sum += num; + sum_sqr += num * num; + if (length < min_length) + min_length = length; + if (length > max_length) + max_length = length; + if (compare_longlong(&num, &min_arg) < 0) + min_arg = num; + if (compare_longlong(&num, &max_arg) > 0) + max_arg = num; + } +} // field_longlong::add + + +void field_ulonglong::add() +{ + char buff[MAX_FIELD_WIDTH]; + longlong num = item->val_int(); + uint length = (uint) (longlong10_to_str(num, buff, 10) - buff); + TREE_ELEMENT *element; + + if (item->null_value) + { + nulls++; + return; + } + if (num == 0) + empty++; + + if (room_in_tree) + { + if (!(element = tree_insert(&tree, (void*) &num, 0, tree.custom_arg))) + { + room_in_tree = 0; // Remove tree, out of RAM ? + delete_tree(&tree, 0); + } + /* + if element->count == 1, this element can be found only once from tree + if element->count == 2, or more, this element is already in tree + */ + else if (element->count == 1 && (tree_elements++) >= pc->max_tree_elements) + { + room_in_tree = 0; // Remove tree, too many elements + delete_tree(&tree, 0); + } + } + + if (!found) + { + found = 1; + min_arg = max_arg = sum = num; + sum_sqr = num * num; + min_length = max_length = length; + } + else if (num != 0) + { + sum += num; + sum_sqr += num * num; + if (length < min_length) + min_length = length; + if (length > max_length) + max_length = length; + if (compare_ulonglong((ulonglong*) &num, &min_arg) < 0) + min_arg = num; + if (compare_ulonglong((ulonglong*) &num, &max_arg) > 0) + max_arg = num; + } +} // field_ulonglong::add + + +int analyse::send_row(List<Item> & /* field_list */) +{ + field_info **f = f_info; + + rows++; + + for (;f != f_end; f++) + { + (*f)->add(); + } + return 0; +} // analyse::send_row + + +int analyse::end_of_records() +{ + field_info **f = f_info; + char buff[MAX_FIELD_WIDTH]; + String *res, s_min(buff, sizeof(buff),&my_charset_bin), + s_max(buff, sizeof(buff),&my_charset_bin), + ans(buff, sizeof(buff),&my_charset_bin); + StringBuffer<NAME_LEN> name; + + for (; f != f_end; f++) + { + /* + We have to make a copy of full_name() as it stores it's value in str_value, + which is reset by save_str_in_field + */ + LEX_CSTRING col_name= (*f)->item->full_name_cstring(); + name.set_buffer_if_not_allocated(&my_charset_bin); + name.copy(col_name.str, col_name.length, &my_charset_bin); + func_items[0]->set((char*) name.ptr(), name.length(), &my_charset_bin); + + if (!(*f)->found) + { + func_items[1]->null_value = 1; + func_items[2]->null_value = 1; + } + else + { + func_items[1]->null_value = 0; + res = (*f)->get_min_arg(&s_min); + func_items[1]->set(res->ptr(), res->length(), res->charset()); + func_items[2]->null_value = 0; + res = (*f)->get_max_arg(&s_max); + func_items[2]->set(res->ptr(), res->length(), res->charset()); + } + func_items[3]->set((longlong) (*f)->min_length); + func_items[4]->set((longlong) (*f)->max_length); + func_items[5]->set((longlong) (*f)->empty); + func_items[6]->set((longlong) (*f)->nulls); + res = (*f)->avg(&s_max, rows); + func_items[7]->set(res->ptr(), res->length(), res->charset()); + func_items[8]->null_value = 0; + res = (*f)->std(&s_max, rows); + if (!res) + func_items[8]->null_value = 1; + else + func_items[8]->set(res->ptr(), res->length(), res->charset()); + /* + count the dots, quotas, etc. in (ENUM("a","b","c"...)) + If tree has been removed, don't suggest ENUM. + treemem is used to measure the size of tree for strings, + tree_elements is used to count the elements + max_treemem tells how long the string starting from ENUM("... and + ending to ..") shall at maximum be. If case is about numbers, + max_tree_elements will tell the length of the above, now + every number is considered as length 1 + */ + if (((*f)->treemem || (*f)->tree_elements) && + (*f)->tree.elements_in_tree && + (((*f)->treemem ? max_treemem : max_tree_elements) > + (((*f)->treemem ? (*f)->treemem : (*f)->tree_elements) + + ((*f)->tree.elements_in_tree * 3 - 1 + 6)))) + { + char tmp[331]; //331, because one double prec. num. can be this long + String tmp_str(tmp, sizeof(tmp),&my_charset_bin); + TREE_INFO tree_info; + + tree_info.str = &tmp_str; + tree_info.found = 0; + tree_info.item = (*f)->item; + + tmp_str.set(STRING_WITH_LEN("ENUM("),&my_charset_bin); + tree_walk(&(*f)->tree, (*f)->collect_enum(), (char*) &tree_info, + left_root_right); + tmp_str.append(')'); + + if (!(*f)->nulls) + tmp_str.append(STRING_WITH_LEN(" NOT NULL")); + output_str_length = tmp_str.length(); + func_items[9]->set(tmp_str.ptr(), tmp_str.length(), tmp_str.charset()); + if (result->send_data(result_fields) > 0) + return -1; + continue; + } + + ans.length(0); + if (!(*f)->treemem && !(*f)->tree_elements) + ans.append(STRING_WITH_LEN("CHAR(0)")); + else if ((*f)->item->type() == Item::FIELD_ITEM) + { + switch (((Item_field*) (*f)->item)->field->real_type()) + { + case MYSQL_TYPE_TIMESTAMP: + ans.append(STRING_WITH_LEN("TIMESTAMP")); + break; + case MYSQL_TYPE_DATETIME: + ans.append(STRING_WITH_LEN("DATETIME")); + break; + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_NEWDATE: + ans.append(STRING_WITH_LEN("DATE")); + break; + case MYSQL_TYPE_SET: + ans.append(STRING_WITH_LEN("SET")); + break; + case MYSQL_TYPE_YEAR: + ans.append(STRING_WITH_LEN("YEAR")); + break; + case MYSQL_TYPE_TIME: + ans.append(STRING_WITH_LEN("TIME")); + break; + case MYSQL_TYPE_DECIMAL: + ans.append(STRING_WITH_LEN("DECIMAL")); + // if item is FIELD_ITEM, it _must_be_ Field_num in this case + if (((Field_num*) ((Item_field*) (*f)->item)->field)->zerofill) + ans.append(STRING_WITH_LEN(" ZEROFILL")); + break; + default: + (*f)->get_opt_type(&ans, rows); + break; + } + } + if (!(*f)->nulls) + ans.append(STRING_WITH_LEN(" NOT NULL")); + func_items[9]->set(ans.ptr(), ans.length(), ans.charset()); + if (result->send_data(result_fields) > 0) + return -1; + } + return 0; +} // analyse::end_of_records + + +void field_str::get_opt_type(String *answer, ha_rows total_rows) +{ + char buff[MAX_FIELD_WIDTH]; + + if (can_be_still_num) + { + if (num_info.is_float) + snprintf(buff, sizeof(buff), "DOUBLE"); // number was like 1e+50... TODO: + else if (num_info.decimals) // DOUBLE(%d,%d) sometime + { + if (num_info.dval > -FLT_MAX && num_info.dval < FLT_MAX) + snprintf(buff, sizeof(buff), "FLOAT(%d,%d)", (num_info.integers + num_info.decimals), num_info.decimals); + else + snprintf(buff, sizeof(buff), "DOUBLE(%d,%d)", (num_info.integers + num_info.decimals), num_info.decimals); + } + else if (ev_num_info.llval >= -128 && + ev_num_info.ullval <= + (ulonglong) (ev_num_info.llval >= 0 ? 255 : 127)) + snprintf(buff, sizeof(buff), "TINYINT(%d)", num_info.integers); + else if (ev_num_info.llval >= INT_MIN16 && + ev_num_info.ullval <= (ulonglong) (ev_num_info.llval >= 0 ? + UINT_MAX16 : INT_MAX16)) + snprintf(buff, sizeof(buff), "SMALLINT(%d)", num_info.integers); + else if (ev_num_info.llval >= INT_MIN24 && + ev_num_info.ullval <= (ulonglong) (ev_num_info.llval >= 0 ? + UINT_MAX24 : INT_MAX24)) + snprintf(buff, sizeof(buff), "MEDIUMINT(%d)", num_info.integers); + else if (ev_num_info.llval >= INT_MIN32 && + ev_num_info.ullval <= (ulonglong) (ev_num_info.llval >= 0 ? + UINT_MAX32 : INT_MAX32)) + snprintf(buff, sizeof(buff), "INT(%d)", num_info.integers); + else + snprintf(buff, sizeof(buff), "BIGINT(%d)", num_info.integers); + answer->append(buff, (uint) strlen(buff)); + if (ev_num_info.llval >= 0 && ev_num_info.min_dval >= 0) + answer->append(STRING_WITH_LEN(" UNSIGNED")); + if (num_info.zerofill) + answer->append(STRING_WITH_LEN(" ZEROFILL")); + } + else if (max_length < 256) + { + if (must_be_blob) + { + if (item->collation.collation == &my_charset_bin) + answer->append(STRING_WITH_LEN("TINYBLOB")); + else + answer->append(STRING_WITH_LEN("TINYTEXT")); + } + else if ((max_length * (total_rows - nulls)) < (sum + total_rows)) + { + snprintf(buff, sizeof(buff), "CHAR(%d)", (int) max_length); + answer->append(buff, (uint) strlen(buff)); + } + else + { + snprintf(buff, sizeof(buff), "VARCHAR(%d)", (int) max_length); + answer->append(buff, (uint) strlen(buff)); + } + } + else if (max_length < (1L << 16)) + { + if (item->collation.collation == &my_charset_bin) + answer->append(STRING_WITH_LEN("BLOB")); + else + answer->append(STRING_WITH_LEN("TEXT")); + } + else if (max_length < (1L << 24)) + { + if (item->collation.collation == &my_charset_bin) + answer->append(STRING_WITH_LEN("MEDIUMBLOB")); + else + answer->append(STRING_WITH_LEN("MEDIUMTEXT")); + } + else + { + if (item->collation.collation == &my_charset_bin) + answer->append(STRING_WITH_LEN("LONGBLOB")); + else + answer->append(STRING_WITH_LEN("LONGTEXT")); + } +} // field_str::get_opt_type + + +void field_real::get_opt_type(String *answer, + ha_rows total_rows __attribute__((unused))) +{ + char buff[MAX_FIELD_WIDTH]; + + if (!max_notzero_dec_len) + { + int len= (int) max_length - ((item->decimals >= FLOATING_POINT_DECIMALS) ? + 0 : (item->decimals + 1)); + + if (min_arg >= -128 && max_arg <= (min_arg >= 0 ? 255 : 127)) + snprintf(buff, sizeof(buff), "TINYINT(%d)", len); + else if (min_arg >= INT_MIN16 && max_arg <= (min_arg >= 0 ? + UINT_MAX16 : INT_MAX16)) + snprintf(buff, sizeof(buff), "SMALLINT(%d)", len); + else if (min_arg >= INT_MIN24 && max_arg <= (min_arg >= 0 ? + UINT_MAX24 : INT_MAX24)) + snprintf(buff, sizeof(buff), "MEDIUMINT(%d)", len); + else if (min_arg >= INT_MIN32 && max_arg <= (min_arg >= 0 ? + UINT_MAX32 : INT_MAX32)) + snprintf(buff, sizeof(buff), "INT(%d)", len); + else + snprintf(buff, sizeof(buff), "BIGINT(%d)", len); + answer->append(buff, (uint) strlen(buff)); + if (min_arg >= 0) + answer->append(STRING_WITH_LEN(" UNSIGNED")); + } + else if (item->decimals >= FLOATING_POINT_DECIMALS) + { + if (min_arg >= -FLT_MAX && max_arg <= FLT_MAX) + answer->append(STRING_WITH_LEN("FLOAT")); + else + answer->append(STRING_WITH_LEN("DOUBLE")); + } + else + { + if (min_arg >= -FLT_MAX && max_arg <= FLT_MAX) + snprintf(buff, sizeof(buff), "FLOAT(%d,%d)", (int) max_length - (item->decimals + 1) + max_notzero_dec_len, + max_notzero_dec_len); + else + snprintf(buff, sizeof(buff), "DOUBLE(%d,%d)", (int) max_length - (item->decimals + 1) + max_notzero_dec_len, + max_notzero_dec_len); + answer->append(buff, (uint) strlen(buff)); + } + // if item is FIELD_ITEM, it _must_be_ Field_num in this class + if (item->type() == Item::FIELD_ITEM && + // a single number shouldn't be zerofill + (max_length - (item->decimals + 1)) != 1 && + ((Field_num*) ((Item_field*) item)->field)->zerofill) + answer->append(STRING_WITH_LEN(" ZEROFILL")); +} // field_real::get_opt_type + + +void field_longlong::get_opt_type(String *answer, + ha_rows total_rows __attribute__((unused))) +{ + char buff[MAX_FIELD_WIDTH]; + + if (min_arg >= -128 && max_arg <= (min_arg >= 0 ? 255 : 127)) + snprintf(buff, sizeof(buff), "TINYINT(%d)", (int) max_length); + else if (min_arg >= INT_MIN16 && max_arg <= (min_arg >= 0 ? + UINT_MAX16 : INT_MAX16)) + snprintf(buff, sizeof(buff), "SMALLINT(%d)", (int) max_length); + else if (min_arg >= INT_MIN24 && max_arg <= (min_arg >= 0 ? + UINT_MAX24 : INT_MAX24)) + snprintf(buff, sizeof(buff), "MEDIUMINT(%d)", (int) max_length); + else if (min_arg >= INT_MIN32 && max_arg <= (min_arg >= 0 ? + UINT_MAX32 : INT_MAX32)) + snprintf(buff, sizeof(buff), "INT(%d)", (int) max_length); + else + snprintf(buff, sizeof(buff), "BIGINT(%d)", (int) max_length); + answer->append(buff, (uint) strlen(buff)); + if (min_arg >= 0) + answer->append(STRING_WITH_LEN(" UNSIGNED")); + + // if item is FIELD_ITEM, it _must_be_ Field_num in this class + if ((item->type() == Item::FIELD_ITEM) && + // a single number shouldn't be zerofill + max_length != 1 && + ((Field_num*) ((Item_field*) item)->field)->zerofill) + answer->append(STRING_WITH_LEN(" ZEROFILL")); +} // field_longlong::get_opt_type + + +void field_ulonglong::get_opt_type(String *answer, + ha_rows total_rows __attribute__((unused))) +{ + char buff[MAX_FIELD_WIDTH]; + + if (max_arg < 256) + snprintf(buff, sizeof(buff), "TINYINT(%d) UNSIGNED", (int) max_length); + else if (max_arg <= ((2 * INT_MAX16) + 1)) + snprintf(buff, sizeof(buff), "SMALLINT(%d) UNSIGNED", (int) max_length); + else if (max_arg <= ((2 * INT_MAX24) + 1)) + snprintf(buff, sizeof(buff), "MEDIUMINT(%d) UNSIGNED", (int) max_length); + else if (max_arg < (((ulonglong) 1) << 32)) + snprintf(buff, sizeof(buff), "INT(%d) UNSIGNED", (int) max_length); + else + snprintf(buff, sizeof(buff), "BIGINT(%d) UNSIGNED", (int) max_length); + // if item is FIELD_ITEM, it _must_be_ Field_num in this class + answer->append(buff, (uint) strlen(buff)); + if (item->type() == Item::FIELD_ITEM && + // a single number shouldn't be zerofill + max_length != 1 && + ((Field_num*) ((Item_field*) item)->field)->zerofill) + answer->append(STRING_WITH_LEN(" ZEROFILL")); +} //field_ulonglong::get_opt_type + + +void field_decimal::get_opt_type(String *answer, + ha_rows total_rows __attribute__((unused))) +{ + my_decimal zero; + char buff[MAX_FIELD_WIDTH]; + uint length; + + my_decimal_set_zero(&zero); + my_bool is_unsigned= (zero.cmp(&min_arg) >= 0); + + length= snprintf(buff, sizeof(buff), "DECIMAL(%d, %d)", + (int) (max_length - (item->decimals ? 1 : 0)), + item->decimals); + if (is_unsigned) + length= (uint) (strmov(buff+length, " UNSIGNED")- buff); + answer->append(buff, length); +} + + +String *field_decimal::get_min_arg(String *str) +{ + min_arg.to_string_native(str, 0, 0, '0'); + return str; +} + + +String *field_decimal::get_max_arg(String *str) +{ + max_arg.to_string_native(str, 0, 0, '0'); + return str; +} + + +String *field_decimal::avg(String *s, ha_rows rows) +{ + if (!(rows - nulls)) + { + s->set_real((double) 0.0, 1,my_thd_charset); + return s; + } + my_decimal num, avg_val, rounded_avg; + int prec_increment= current_thd->variables.div_precincrement; + + int2my_decimal(E_DEC_FATAL_ERROR, rows - nulls, FALSE, &num); + my_decimal_div(E_DEC_FATAL_ERROR, &avg_val, sum+cur_sum, &num, prec_increment); + /* TODO remove this after decimal_div returns proper frac */ + avg_val.round_to(&rounded_avg, + MY_MIN(sum[cur_sum].frac + prec_increment, DECIMAL_MAX_SCALE), + HALF_UP); + rounded_avg.to_string_native(s, 0, 0, '0'); + return s; +} + + +String *field_decimal::std(String *s, ha_rows rows) +{ + if (!(rows - nulls)) + { + s->set_real((double) 0.0, 1,my_thd_charset); + return s; + } + my_decimal num, tmp, sum2, sum2d; + int prec_increment= current_thd->variables.div_precincrement; + + int2my_decimal(E_DEC_FATAL_ERROR, rows - nulls, FALSE, &num); + my_decimal_mul(E_DEC_FATAL_ERROR, &sum2, sum+cur_sum, sum+cur_sum); + my_decimal_div(E_DEC_FATAL_ERROR, &tmp, &sum2, &num, prec_increment); + my_decimal_sub(E_DEC_FATAL_ERROR, &sum2, sum_sqr+cur_sum, &tmp); + my_decimal_div(E_DEC_FATAL_ERROR, &tmp, &sum2, &num, prec_increment); + double std_sqr= tmp.to_double(); + s->set_real(((double) std_sqr <= 0.0 ? 0.0 : sqrt(std_sqr)), + MY_MIN(item->decimals + prec_increment, NOT_FIXED_DEC), my_thd_charset); + + return s; +} + + +int collect_string(String *element, + element_count count __attribute__((unused)), + TREE_INFO *info) +{ + if (info->found) + info->str->append(','); + else + info->found = 1; + info->str->append('\''); + if (info->str->append_for_single_quote(element)) + return 1; + info->str->append('\''); + return 0; +} // collect_string + + +int collect_real(double *element, element_count count __attribute__((unused)), + TREE_INFO *info) +{ + char buff[MAX_FIELD_WIDTH]; + String s(buff, sizeof(buff),current_thd->charset()); + + if (info->found) + info->str->append(','); + else + info->found = 1; + info->str->append('\''); + s.set_real(*element, info->item->decimals, current_thd->charset()); + info->str->append(s); + info->str->append('\''); + return 0; +} // collect_real + + +int collect_decimal(uchar *element, element_count count, + TREE_INFO *info) +{ + char buff[DECIMAL_MAX_STR_LENGTH]; + String s(buff, sizeof(buff),&my_charset_bin); + + if (info->found) + info->str->append(','); + else + info->found = 1; + my_decimal dec(element, info->item->max_length, info->item->decimals); + info->str->append('\''); + dec.to_string_native(&s, 0, 0, '0'); + info->str->append(s); + info->str->append('\''); + return 0; +} + + +int collect_longlong(longlong *element, + element_count count __attribute__((unused)), + TREE_INFO *info) +{ + char buff[MAX_FIELD_WIDTH]; + String s(buff, sizeof(buff),&my_charset_bin); + + if (info->found) + info->str->append(','); + else + info->found = 1; + info->str->append('\''); + s.set(*element, current_thd->charset()); + info->str->append(s); + info->str->append('\''); + return 0; +} // collect_longlong + + +int collect_ulonglong(ulonglong *element, + element_count count __attribute__((unused)), + TREE_INFO *info) +{ + char buff[MAX_FIELD_WIDTH]; + String s(buff, sizeof(buff),&my_charset_bin); + + if (info->found) + info->str->append(','); + else + info->found = 1; + info->str->append('\''); + s.set(*element, current_thd->charset()); + info->str->append(s); + info->str->append('\''); + return 0; +} // collect_ulonglong + + +bool analyse::change_columns(THD *thd, List<Item> &field_list) +{ + MEM_ROOT *mem_root= thd->mem_root; + field_list.empty(); + + func_items[0]= new (mem_root) Item_proc_string(thd, "Field_name", 255); + func_items[1]= new (mem_root) Item_proc_string(thd, "Min_value", 255); + func_items[1]->set_maybe_null(); + func_items[2]= new (mem_root) Item_proc_string(thd, "Max_value", 255); + func_items[2]->set_maybe_null(); + func_items[3]= new (mem_root) Item_proc_int(thd, "Min_length"); + func_items[4]= new (mem_root) Item_proc_int(thd, "Max_length"); + func_items[5]= new (mem_root) Item_proc_int(thd, "Empties_or_zeros"); + func_items[6]= new (mem_root) Item_proc_int(thd, "Nulls"); + func_items[7]= new (mem_root) Item_proc_string(thd, "Avg_value_or_avg_length", 255); + func_items[8]= new (mem_root) Item_proc_string(thd, "Std", 255); + func_items[8]->set_maybe_null(); + func_items[9]= new (mem_root) Item_proc_string(thd, "Optimal_fieldtype", + MY_MAX(64, + output_str_length)); + + for (uint i = 0; i < array_elements(func_items); i++) + field_list.push_back(func_items[i], thd->mem_root); + result_fields = field_list; + return 0; +} // analyse::change_columns + +int compare_double(const double *s, const double *t) +{ + return ((*s < *t) ? -1 : *s > *t ? 1 : 0); +} /* compare_double */ + +int compare_longlong(const longlong *s, const longlong *t) +{ + return ((*s < *t) ? -1 : *s > *t ? 1 : 0); +} /* compare_longlong */ + + int compare_ulonglong(const ulonglong *s, const ulonglong *t) +{ + return ((*s < *t) ? -1 : *s > *t ? 1 : 0); +} /* compare_ulonglong */ + + +uint check_ulonglong(const char *str, uint length) +{ + const char *long_str = "2147483647", *ulonglong_str = "18446744073709551615"; + const uint long_len = 10, ulonglong_len = 20; + + while (*str == '0' && length) + { + str++; length--; + } + if (length < long_len) + return NUM; + + uint smaller, bigger; + const char *cmp; + + if (length == long_len) + { + cmp = long_str; + smaller = NUM; + bigger = LONG_NUM; + } + else if (length > ulonglong_len) + return DECIMAL_NUM; + else + { + cmp = ulonglong_str; + smaller = LONG_NUM; + bigger = DECIMAL_NUM; + } + while (*cmp && *cmp++ == *str++) ; + return ((uchar) str[-1] <= (uchar) cmp[-1]) ? smaller : bigger; +} /* check_ulonglong */ |