mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-30 04:26:45 +03:00 
			
		
		
		
	The reason for the failure was a bug in an include file on debian that causes 'struct stat' to have different sized depending on the environment. This patch fixes so that we always include my_global.h or my_config.h before we include any other files. Other things: - Removed #include <my_global.h> in some include files; Better to always do this at the top level to have as few "always-include-this-file-first' files as possible. - Removed usage of some include files that where already included by my_global.h or by other files. client/mysql_plugin.c: Use my_global.h first client/mysqlslap.c: Remove duplicated include files extra/comp_err.c: Remove duplicated include files include/m_string.h: Remove duplicated include files include/maria.h: Remove duplicated include files libmysqld/emb_qcache.cc: Use my_global.h first plugin/semisync/semisync.h: Use my_pthread.h first sql/datadict.cc: Use my_global.h first sql/debug_sync.cc: Use my_global.h first sql/derror.cc: Use my_global.h first sql/des_key_file.cc: Use my_global.h first sql/discover.cc: Use my_global.h first sql/event_data_objects.cc: Use my_global.h first sql/event_db_repository.cc: Use my_global.h first sql/event_parse_data.cc: Use my_global.h first sql/event_queue.cc: Use my_global.h first sql/event_scheduler.cc: Use my_global.h first sql/events.cc: Use my_global.h first sql/field.cc: Use my_global.h first Remove duplicated include files sql/field_conv.cc: Use my_global.h first sql/filesort.cc: Use my_global.h first Remove duplicated include files sql/gstream.cc: Use my_global.h first sql/ha_ndbcluster.cc: Use my_global.h first sql/ha_ndbcluster_binlog.cc: Use my_global.h first sql/ha_ndbcluster_cond.cc: Use my_global.h first sql/ha_partition.cc: Use my_global.h first sql/handler.cc: Use my_global.h first sql/hash_filo.cc: Use my_global.h first sql/hostname.cc: Use my_global.h first sql/init.cc: Use my_global.h first sql/item.cc: Use my_global.h first sql/item_buff.cc: Use my_global.h first sql/item_cmpfunc.cc: Use my_global.h first sql/item_create.cc: Use my_global.h first sql/item_geofunc.cc: Use my_global.h first sql/item_inetfunc.cc: Use my_global.h first sql/item_row.cc: Use my_global.h first sql/item_strfunc.cc: Use my_global.h first sql/item_subselect.cc: Use my_global.h first sql/item_sum.cc: Use my_global.h first sql/item_timefunc.cc: Use my_global.h first sql/item_xmlfunc.cc: Use my_global.h first sql/key.cc: Use my_global.h first sql/lock.cc: Use my_global.h first sql/log.cc: Use my_global.h first sql/log_event.cc: Use my_global.h first sql/log_event_old.cc: Use my_global.h first sql/mf_iocache.cc: Use my_global.h first sql/mysql_install_db.cc: Remove duplicated include files sql/mysqld.cc: Remove duplicated include files sql/net_serv.cc: Remove duplicated include files sql/opt_range.cc: Use my_global.h first sql/opt_subselect.cc: Use my_global.h first sql/opt_sum.cc: Use my_global.h first sql/parse_file.cc: Use my_global.h first sql/partition_info.cc: Use my_global.h first sql/procedure.cc: Use my_global.h first sql/protocol.cc: Use my_global.h first sql/records.cc: Use my_global.h first sql/records.h: Don't include my_global.h Better to do this at the upper level sql/repl_failsafe.cc: Use my_global.h first sql/rpl_filter.cc: Use my_global.h first sql/rpl_gtid.cc: Use my_global.h first sql/rpl_handler.cc: Use my_global.h first sql/rpl_injector.cc: Use my_global.h first sql/rpl_record.cc: Use my_global.h first sql/rpl_record_old.cc: Use my_global.h first sql/rpl_reporting.cc: Use my_global.h first sql/rpl_rli.cc: Use my_global.h first sql/rpl_tblmap.cc: Use my_global.h first sql/rpl_utility.cc: Use my_global.h first sql/set_var.cc: Added comment sql/slave.cc: Use my_global.h first sql/sp.cc: Use my_global.h first sql/sp_cache.cc: Use my_global.h first sql/sp_head.cc: Use my_global.h first sql/sp_pcontext.cc: Use my_global.h first sql/sp_rcontext.cc: Use my_global.h first sql/spatial.cc: Use my_global.h first sql/sql_acl.cc: Use my_global.h first sql/sql_admin.cc: Use my_global.h first sql/sql_analyse.cc: Use my_global.h first sql/sql_audit.cc: Use my_global.h first sql/sql_base.cc: Use my_global.h first sql/sql_binlog.cc: Use my_global.h first sql/sql_bootstrap.cc: Use my_global.h first Use my_global.h first sql/sql_cache.cc: Use my_global.h first sql/sql_class.cc: Use my_global.h first sql/sql_client.cc: Use my_global.h first sql/sql_connect.cc: Use my_global.h first sql/sql_crypt.cc: Use my_global.h first sql/sql_cursor.cc: Use my_global.h first sql/sql_db.cc: Use my_global.h first sql/sql_delete.cc: Use my_global.h first sql/sql_derived.cc: Use my_global.h first sql/sql_do.cc: Use my_global.h first sql/sql_error.cc: Use my_global.h first sql/sql_explain.cc: Use my_global.h first sql/sql_expression_cache.cc: Use my_global.h first sql/sql_handler.cc: Use my_global.h first sql/sql_help.cc: Use my_global.h first sql/sql_insert.cc: Use my_global.h first sql/sql_lex.cc: Use my_global.h first sql/sql_load.cc: Use my_global.h first sql/sql_locale.cc: Use my_global.h first sql/sql_manager.cc: Use my_global.h first sql/sql_parse.cc: Use my_global.h first sql/sql_partition.cc: Use my_global.h first sql/sql_plugin.cc: Added comment sql/sql_prepare.cc: Use my_global.h first sql/sql_priv.h: Added error if we use this before including my_global.h This check is here becasue so many files includes sql_priv.h first. sql/sql_profile.cc: Use my_global.h first sql/sql_reload.cc: Use my_global.h first sql/sql_rename.cc: Use my_global.h first sql/sql_repl.cc: Use my_global.h first sql/sql_select.cc: Use my_global.h first sql/sql_servers.cc: Use my_global.h first sql/sql_show.cc: Added comment sql/sql_signal.cc: Use my_global.h first sql/sql_statistics.cc: Use my_global.h first sql/sql_table.cc: Use my_global.h first sql/sql_tablespace.cc: Use my_global.h first sql/sql_test.cc: Use my_global.h first sql/sql_time.cc: Use my_global.h first sql/sql_trigger.cc: Use my_global.h first sql/sql_udf.cc: Use my_global.h first sql/sql_union.cc: Use my_global.h first sql/sql_update.cc: Use my_global.h first sql/sql_view.cc: Use my_global.h first sql/sys_vars.cc: Added comment sql/table.cc: Use my_global.h first sql/thr_malloc.cc: Use my_global.h first sql/transaction.cc: Use my_global.h first sql/uniques.cc: Use my_global.h first sql/unireg.cc: Use my_global.h first sql/unireg.h: Removed inclusion of my_global.h storage/archive/ha_archive.cc: Added comment storage/blackhole/ha_blackhole.cc: Use my_global.h first storage/csv/ha_tina.cc: Use my_global.h first storage/csv/transparent_file.cc: Use my_global.h first storage/federated/ha_federated.cc: Use my_global.h first storage/federatedx/federatedx_io.cc: Use my_global.h first storage/federatedx/federatedx_io_mysql.cc: Use my_global.h first storage/federatedx/federatedx_io_null.cc: Use my_global.h first storage/federatedx/federatedx_txn.cc: Use my_global.h first storage/heap/ha_heap.cc: Use my_global.h first storage/innobase/handler/handler0alter.cc: Use my_global.h first storage/maria/ha_maria.cc: Use my_global.h first storage/maria/unittest/ma_maria_log_cleanup.c: Remove duplicated include files storage/maria/unittest/test_file.c: Added comment storage/myisam/ha_myisam.cc: Move sql_plugin.h first as this includes my_global.h storage/myisammrg/ha_myisammrg.cc: Use my_global.h first storage/oqgraph/oqgraph_thunk.cc: Use my_config.h and my_global.h first One could not include my_global.h before oqgraph_thunk.h (don't know why) storage/spider/ha_spider.cc: Use my_global.h first storage/spider/hs_client/config.cpp: Use my_global.h first storage/spider/hs_client/escape.cpp: Use my_global.h first storage/spider/hs_client/fatal.cpp: Use my_global.h first storage/spider/hs_client/hstcpcli.cpp: Use my_global.h first storage/spider/hs_client/socket.cpp: Use my_global.h first storage/spider/hs_client/string_util.cpp: Use my_global.h first storage/spider/spd_conn.cc: Use my_global.h first storage/spider/spd_copy_tables.cc: Use my_global.h first storage/spider/spd_db_conn.cc: Use my_global.h first storage/spider/spd_db_handlersocket.cc: Use my_global.h first storage/spider/spd_db_mysql.cc: Use my_global.h first storage/spider/spd_db_oracle.cc: Use my_global.h first storage/spider/spd_direct_sql.cc: Use my_global.h first storage/spider/spd_i_s.cc: Use my_global.h first storage/spider/spd_malloc.cc: Use my_global.h first storage/spider/spd_param.cc: Use my_global.h first storage/spider/spd_ping_table.cc: Use my_global.h first storage/spider/spd_sys_table.cc: Use my_global.h first storage/spider/spd_table.cc: Use my_global.h first storage/spider/spd_trx.cc: Use my_global.h first storage/xtradb/handler/handler0alter.cc: Use my_global.h first storage/xtradb/handler/i_s.cc: Use my_global.h first
		
			
				
	
	
		
			1243 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1243 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|    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-1301, 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 <my_global.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);
 | |
| }
 | |
| 
 | |
| 
 | |
| Procedure *
 | |
| proc_analyse_init(THD *thd, ORDER *param, select_result *result,
 | |
| 		  List<Item> &field_list)
 | |
| {
 | |
|   char *proc_name = (*param->item)->name;
 | |
|   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 (!(*param->item)->fixed && (*param->item)->fix_fields(thd, param->item))
 | |
|     {
 | |
|       DBUG_PRINT("info", ("fix_fields() for the first parameter failed"));
 | |
|       goto err;
 | |
|     }
 | |
|     if ((*param->item)->type() != Item::INT_ITEM ||
 | |
| 	(*param->item)->val_real() < 0)
 | |
|     {
 | |
|       my_error(ER_WRONG_PARAMETERS_TO_PROCEDURE, MYF(0), proc_name);
 | |
|       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 (!(*param->item)->fixed && (*param->item)->fix_fields(thd, param->item))
 | |
|     {
 | |
|       DBUG_PRINT("info", ("fix_fields() for the second parameter failed"));
 | |
|       goto err;
 | |
|     }
 | |
|     if ((*param->item)->type() != Item::INT_ITEM ||
 | |
| 	(*param->item)->val_real() < 0)
 | |
|     {
 | |
|       my_error(ER_WRONG_PARAMETERS_TO_PROCEDURE, MYF(0), proc_name);
 | |
|       goto err;
 | |
|     }
 | |
|     pc->max_treemem = (uint) (*param->item)->val_int();
 | |
|   }
 | |
|   else if ((*param->item)->type() != Item::INT_ITEM ||
 | |
| 	   (*param->item)->val_real() < 0)
 | |
|   {
 | |
|     my_error(ER_WRONG_PARAMETERS_TO_PROCEDURE, MYF(0), proc_name);
 | |
|     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**)sql_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
 | |
| 
 | |
| 
 | |
| void free_string(String *s)
 | |
| {
 | |
|   s->free();
 | |
| }
 | |
| 
 | |
| 
 | |
| 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);
 | |
|       }
 | |
|       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);
 | |
| 	}
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   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()) == NOT_FIXED_DEC)
 | |
|   {
 | |
|     length= sprintf(buff, "%g", num);
 | |
|     if (rint(num) != num)
 | |
|       max_notzero_dec_len = 1;
 | |
|   }
 | |
|   else
 | |
|   {
 | |
| #ifdef HAVE_SNPRINTF
 | |
|     buff[sizeof(buff)-1]=0;			// Safety
 | |
|     snprintf(buff, sizeof(buff)-1, "%-.*f", (int) decs, num);
 | |
|     length = (uint) strlen(buff);
 | |
| #else
 | |
|     length= sprintf(buff, "%-.*f", (int) decs, num);
 | |
| #endif
 | |
| 
 | |
|     // 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);
 | |
|     }
 | |
|     /*
 | |
|       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);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   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 */
 | |
|   my_decimal dec_buf, *dec= item->val_decimal(&dec_buf);
 | |
|   my_decimal rounded;
 | |
|   uint length;
 | |
|   TREE_ELEMENT *element;
 | |
| 
 | |
|   if (item->null_value)
 | |
|   {
 | |
|     nulls++;
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   my_decimal_round(E_DEC_FATAL_ERROR, dec, item->decimals, FALSE,&rounded);
 | |
|   dec= &rounded;
 | |
| 
 | |
|   length= my_decimal_string_length(dec);
 | |
| 
 | |
|   if (decimal_is_zero(dec))
 | |
|     empty++;
 | |
| 
 | |
|   if (room_in_tree)
 | |
|   {
 | |
|     uchar buf[DECIMAL_MAX_FIELD_SIZE];
 | |
|     my_decimal2binary(E_DEC_FATAL_ERROR, dec, 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);
 | |
|     }
 | |
|     /*
 | |
|       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);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   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 (my_decimal_cmp(dec, &min_arg) < 0)
 | |
|     {
 | |
|       min_arg= *dec;
 | |
|     }
 | |
|     if (my_decimal_cmp(dec, &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);
 | |
|     }
 | |
|     /*
 | |
|       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);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   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);
 | |
|     }
 | |
|     /*
 | |
|       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);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   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);
 | |
| 
 | |
|   for (; f != f_end; f++)
 | |
|   {
 | |
|     func_items[0]->set((*f)->item->full_name());
 | |
|     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)
 | |
|       sprintf(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)
 | |
| 	sprintf(buff, "FLOAT(%d,%d)", (num_info.integers + num_info.decimals), num_info.decimals);
 | |
|       else
 | |
| 	sprintf(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))
 | |
|       sprintf(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))
 | |
|       sprintf(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))
 | |
|       sprintf(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))
 | |
|       sprintf(buff, "INT(%d)", num_info.integers);
 | |
|     else
 | |
|       sprintf(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))
 | |
|     {
 | |
|       sprintf(buff, "CHAR(%d)", (int) max_length);
 | |
|       answer->append(buff, (uint) strlen(buff));
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       sprintf(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 == NOT_FIXED_DEC) ?
 | |
| 				 0 : (item->decimals + 1));
 | |
| 
 | |
|     if (min_arg >= -128 && max_arg <= (min_arg >= 0 ? 255 : 127))
 | |
|       sprintf(buff, "TINYINT(%d)", len);
 | |
|     else if (min_arg >= INT_MIN16 && max_arg <= (min_arg >= 0 ?
 | |
| 						 UINT_MAX16 : INT_MAX16))
 | |
|       sprintf(buff, "SMALLINT(%d)", len);
 | |
|     else if (min_arg >= INT_MIN24 && max_arg <= (min_arg >= 0 ?
 | |
| 						 UINT_MAX24 : INT_MAX24))
 | |
|       sprintf(buff, "MEDIUMINT(%d)", len);
 | |
|     else if (min_arg >= INT_MIN32 && max_arg <= (min_arg >= 0 ?
 | |
| 						 UINT_MAX32 : INT_MAX32))
 | |
|       sprintf(buff, "INT(%d)", len);
 | |
|     else
 | |
|       sprintf(buff, "BIGINT(%d)", len);
 | |
|     answer->append(buff, (uint) strlen(buff));
 | |
|     if (min_arg >= 0)
 | |
|       answer->append(STRING_WITH_LEN(" UNSIGNED"));
 | |
|   }
 | |
|   else if (item->decimals == NOT_FIXED_DEC)
 | |
|   {
 | |
|     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)
 | |
|       sprintf(buff, "FLOAT(%d,%d)", (int) max_length - (item->decimals + 1) + max_notzero_dec_len,
 | |
| 	      max_notzero_dec_len);
 | |
|     else
 | |
|       sprintf(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))
 | |
|     sprintf(buff, "TINYINT(%d)", (int) max_length);
 | |
|   else if (min_arg >= INT_MIN16 && max_arg <= (min_arg >= 0 ?
 | |
| 					       UINT_MAX16 : INT_MAX16))
 | |
|     sprintf(buff, "SMALLINT(%d)", (int) max_length);
 | |
|   else if (min_arg >= INT_MIN24 && max_arg <= (min_arg >= 0 ?
 | |
| 					       UINT_MAX24 : INT_MAX24))
 | |
|     sprintf(buff, "MEDIUMINT(%d)", (int) max_length);
 | |
|   else if (min_arg >= INT_MIN32 && max_arg <= (min_arg >= 0 ?
 | |
| 					       UINT_MAX32 : INT_MAX32))
 | |
|     sprintf(buff, "INT(%d)", (int) max_length);
 | |
|   else
 | |
|     sprintf(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)
 | |
|     sprintf(buff, "TINYINT(%d) UNSIGNED", (int) max_length);
 | |
|    else if (max_arg <= ((2 * INT_MAX16) + 1))
 | |
|      sprintf(buff, "SMALLINT(%d) UNSIGNED", (int) max_length);
 | |
|   else if (max_arg <= ((2 * INT_MAX24) + 1))
 | |
|     sprintf(buff, "MEDIUMINT(%d) UNSIGNED", (int) max_length);
 | |
|   else if (max_arg < (((ulonglong) 1) << 32))
 | |
|     sprintf(buff, "INT(%d) UNSIGNED", (int) max_length);
 | |
|   else
 | |
|     sprintf(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= (my_decimal_cmp(&zero, &min_arg) >= 0);
 | |
| 
 | |
|   length= sprintf(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)
 | |
| {
 | |
|   my_decimal2string(E_DEC_FATAL_ERROR, &min_arg, 0, 0, '0', str);
 | |
|   return str;
 | |
| }
 | |
| 
 | |
| 
 | |
| String *field_decimal::get_max_arg(String *str)
 | |
| {
 | |
|   my_decimal2string(E_DEC_FATAL_ERROR, &max_arg, 0, 0, '0', str);
 | |
|   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 */
 | |
|   my_decimal_round(E_DEC_FATAL_ERROR, &avg_val,
 | |
|                    MY_MIN(sum[cur_sum].frac + prec_increment, DECIMAL_MAX_SCALE),
 | |
|                    FALSE,&rounded_avg);
 | |
|   my_decimal2string(E_DEC_FATAL_ERROR, &rounded_avg, 0, 0, '0', s);
 | |
|   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;
 | |
|   double std_sqr;
 | |
|   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);
 | |
|   my_decimal2double(E_DEC_FATAL_ERROR, &tmp, &std_sqr);
 | |
|   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;
 | |
|   binary2my_decimal(E_DEC_FATAL_ERROR, element, &dec,
 | |
|                     info->item->max_length, info->item->decimals);
 | |
|   
 | |
|   info->str->append('\'');
 | |
|   my_decimal2string(E_DEC_FATAL_ERROR, &dec, 0, 0, '0', &s);
 | |
|   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(List<Item> &field_list)
 | |
| {
 | |
|   field_list.empty();
 | |
| 
 | |
|   func_items[0] = new Item_proc_string("Field_name", 255);
 | |
|   func_items[1] = new Item_proc_string("Min_value", 255);
 | |
|   func_items[1]->maybe_null = 1;
 | |
|   func_items[2] = new Item_proc_string("Max_value", 255);
 | |
|   func_items[2]->maybe_null = 1;
 | |
|   func_items[3] = new Item_proc_int("Min_length");
 | |
|   func_items[4] = new Item_proc_int("Max_length");
 | |
|   func_items[5] = new Item_proc_int("Empties_or_zeros");
 | |
|   func_items[6] = new Item_proc_int("Nulls");
 | |
|   func_items[7] = new Item_proc_string("Avg_value_or_avg_length", 255);
 | |
|   func_items[8] = new Item_proc_string("Std", 255);
 | |
|   func_items[8]->maybe_null = 1;
 | |
|   func_items[9] = new Item_proc_string("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]);
 | |
|   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_ulonlong */
 | |
| 
 |