mariadb/sql/item_sum.cc

3434 lines
87 KiB
C++
Raw Normal View History

/* Copyright (c) 2000, 2010 Oracle and/or its affiliates. All rights reserved.
2000-07-31 21:29:14 +02:00
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.
2000-07-31 21:29:14 +02:00
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.
2000-07-31 21:29:14 +02:00
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
2007-10-11 19:29:09 +02:00
/**
@file
@brief
Sum functions (COUNT, MIN...)
*/
2000-07-31 21:29:14 +02:00
#ifdef USE_PRAGMA_IMPLEMENTATION
2000-07-31 21:29:14 +02:00
#pragma implementation // gcc: Class implementation
#endif
#include "sql_priv.h"
2005-03-01 21:19:19 +01:00
#include "sql_select.h"
/**
Calculate the affordable RAM limit for structures like TREE or Unique
used in Item_sum_*
*/
ulonglong Item_sum::ram_limitation(THD *thd)
{
return min(thd->variables.tmp_table_size,
thd->variables.max_heap_table_size);
}
2009-11-17 12:24:23 +01:00
2007-10-11 19:29:09 +02:00
/**
Prepare an aggregate function item for checking context conditions.
The function initializes the members of the Item_sum object created
for a set function that are used to check validity of the set function
occurrence.
If the set function is not allowed in any subquery where it occurs
an error is reported immediately.
2007-10-11 19:29:09 +02:00
@param thd reference to the thread context info
@note
This function is to be called for any item created for a set function
object when the traversal of trees built for expressions used in the query
is performed at the phase of context analysis. This function is to
be invoked at the descent of this traversal.
2007-10-11 19:29:09 +02:00
@retval
TRUE if an error is reported
@retval
FALSE otherwise
*/
bool Item_sum::init_sum_func_check(THD *thd)
{
if (!thd->lex->allow_sum_func)
{
my_message(ER_INVALID_GROUP_FUNC_USE, ER(ER_INVALID_GROUP_FUNC_USE),
MYF(0));
return TRUE;
}
/* Set a reference to the nesting set function if there is any */
in_sum_func= thd->lex->in_sum_func;
/* Save a pointer to object to be used in items for nested set functions */
thd->lex->in_sum_func= this;
nest_level= thd->lex->current_select->nest_level;
ref_by= 0;
aggr_level= -1;
aggr_sel= NULL;
max_arg_level= -1;
max_sum_func_level= -1;
outer_fields.empty();
return FALSE;
}
2007-10-11 19:29:09 +02:00
/**
Check constraints imposed on a usage of a set function.
The method verifies whether context conditions imposed on a usage
of any set function are met for this occurrence.
It checks whether the set function occurs in the position where it
can be aggregated and, when it happens to occur in argument of another
set function, the method checks that these two functions are aggregated in
different subqueries.
If the context conditions are not met the method reports an error.
If the set function is aggregated in some outer subquery the method
adds it to the chain of items for such set functions that is attached
to the the st_select_lex structure for this subquery.
A number of designated members of the object are used to check the
conditions. They are specified in the comment before the Item_sum
class declaration.
Additionally a bitmap variable called allow_sum_func is employed.
It is included into the thd->lex structure.
The bitmap contains 1 at n-th position if the set function happens
to occur under a construct of the n-th level subquery where usage
of set functions are allowed (i.e either in the SELECT list or
in the HAVING clause of the corresponding subquery)
Consider the query:
2007-10-11 19:29:09 +02:00
@code
SELECT SUM(t1.b) FROM t1 GROUP BY t1.a
HAVING t1.a IN (SELECT t2.c FROM t2 WHERE AVG(t1.b) > 20) AND
t1.a > (SELECT MIN(t2.d) FROM t2);
@endcode
allow_sum_func will contain:
2007-10-11 19:29:09 +02:00
- for SUM(t1.b) - 1 at the first position
- for AVG(t1.b) - 1 at the first position, 0 at the second position
- for MIN(t2.d) - 1 at the first position, 1 at the second position.
@param thd reference to the thread context info
@param ref location of the pointer to this item in the embedding expression
@note
This function is to be called for any item created for a set function
object when the traversal of trees built for expressions used in the query
is performed at the phase of context analysis. This function is to
be invoked at the ascent of this traversal.
2007-10-11 19:29:09 +02:00
@retval
TRUE if an error is reported
@retval
FALSE otherwise
*/
bool Item_sum::check_sum_func(THD *thd, Item **ref)
{
bool invalid= FALSE;
nesting_map allow_sum_func= thd->lex->allow_sum_func;
/*
The value of max_arg_level is updated if an argument of the set function
contains a column reference resolved against a subquery whose level is
greater than the current value of max_arg_level.
max_arg_level cannot be greater than nest level.
nest level is always >= 0
*/
if (nest_level == max_arg_level)
{
/*
The function must be aggregated in the current subquery,
If it is there under a construct where it is not allowed
we report an error.
*/
invalid= !(allow_sum_func & (1 << max_arg_level));
}
else if (max_arg_level >= 0 || !(allow_sum_func & (1 << nest_level)))
{
/*
The set function can be aggregated only in outer subqueries.
Try to find a subquery where it can be aggregated;
If we fail to find such a subquery report an error.
*/
if (register_sum_func(thd, ref))
return TRUE;
invalid= aggr_level < 0 && !(allow_sum_func & (1 << nest_level));
if (!invalid && thd->variables.sql_mode & MODE_ANSI)
invalid= aggr_level < 0 && max_arg_level < nest_level;
}
if (!invalid && aggr_level < 0)
{
aggr_level= nest_level;
aggr_sel= thd->lex->current_select;
}
/*
By this moment we either found a subquery where the set function is
to be aggregated and assigned a value that is >= 0 to aggr_level,
or set the value of 'invalid' to TRUE to report later an error.
*/
/*
Additionally we have to check whether possible nested set functions
are acceptable here: they are not, if the level of aggregation of
some of them is less than aggr_level.
*/
if (!invalid)
invalid= aggr_level <= max_sum_func_level;
if (invalid)
{
my_message(ER_INVALID_GROUP_FUNC_USE, ER(ER_INVALID_GROUP_FUNC_USE),
MYF(0));
return TRUE;
}
if (in_sum_func)
{
/*
If the set function is nested adjust the value of
max_sum_func_level for the nesting set function.
We take into account only enclosed set functions that are to be
aggregated on the same level or above of the nest level of
the enclosing set function.
But we must always pass up the max_sum_func_level because it is
the maximum nested level of all directly and indirectly enclosed
set functions. We must do that even for set functions that are
aggregated inside of their enclosing set function's nest level
because the enclosing function may contain another enclosing
function that is to be aggregated outside or on the same level
as its parent's nest level.
*/
if (in_sum_func->nest_level >= aggr_level)
set_if_bigger(in_sum_func->max_sum_func_level, aggr_level);
set_if_bigger(in_sum_func->max_sum_func_level, max_sum_func_level);
}
/*
Check that non-aggregated fields and sum functions aren't mixed in the
same select in the ONLY_FULL_GROUP_BY mode.
*/
if (outer_fields.elements)
{
Item_field *field;
/*
Here we compare the nesting level of the select to which an outer field
belongs to with the aggregation level of the sum function. All fields in
the outer_fields list are checked.
If the nesting level is equal to the aggregation level then the field is
aggregated by this sum function.
If the nesting level is less than the aggregation level then the field
belongs to an outer select. In this case if there is an embedding sum
function add current field to functions outer_fields list. If there is
no embedding function then the current field treated as non aggregated
and the select it belongs to is marked accordingly.
If the nesting level is greater than the aggregation level then it means
that this field was added by an inner sum function.
Consider an example:
select avg ( <-- we are here, checking outer.f1
select (
select sum(outer.f1 + inner.f1) from inner
) from outer)
from most_outer;
In this case we check that no aggregate functions are used in the
select the field belongs to. If there are some then an error is
raised.
*/
List_iterator<Item_field> of(outer_fields);
while ((field= of++))
{
SELECT_LEX *sel= field->cached_table->select_lex;
if (sel->nest_level < aggr_level)
{
if (in_sum_func)
{
/*
Let upper function decide whether this field is a non
aggregated one.
*/
in_sum_func->outer_fields.push_back(field);
}
else
sel->full_group_by_flag|= NON_AGG_FIELD_USED;
}
if (sel->nest_level > aggr_level &&
(sel->full_group_by_flag & SUM_FUNC_USED) &&
!sel->group_list.elements)
{
my_message(ER_MIX_OF_GROUP_FUNC_AND_FIELDS,
ER(ER_MIX_OF_GROUP_FUNC_AND_FIELDS), MYF(0));
return TRUE;
}
}
}
aggr_sel->full_group_by_flag|= SUM_FUNC_USED;
update_used_tables();
thd->lex->in_sum_func= in_sum_func;
return FALSE;
}
2007-10-11 19:29:09 +02:00
/**
Attach a set function to the subquery where it must be aggregated.
The function looks for an outer subquery where the set function must be
aggregated. If it finds such a subquery then aggr_level is set to
the nest level of this subquery and the item for the set function
is added to the list of set functions used in nested subqueries
inner_sum_func_list defined for each subquery. When the item is placed
there the field 'ref_by' is set to ref.
2007-10-11 19:29:09 +02:00
@note
Now we 'register' only set functions that are aggregated in outer
subqueries. Actually it makes sense to link all set function for
a subquery in one chain. It would simplify the process of 'splitting'
for set functions.
2007-10-11 19:29:09 +02:00
@param thd reference to the thread context info
@param ref location of the pointer to this item in the embedding expression
@retval
FALSE if the executes without failures (currently always)
2007-10-11 19:29:09 +02:00
@retval
TRUE otherwise
*/
bool Item_sum::register_sum_func(THD *thd, Item **ref)
{
SELECT_LEX *sl;
nesting_map allow_sum_func= thd->lex->allow_sum_func;
for (sl= thd->lex->current_select->master_unit()->outer_select() ;
sl && sl->nest_level > max_arg_level;
sl= sl->master_unit()->outer_select() )
{
if (aggr_level < 0 && (allow_sum_func & (1 << sl->nest_level)))
{
/* Found the most nested subquery where the function can be aggregated */
aggr_level= sl->nest_level;
aggr_sel= sl;
}
}
if (sl && (allow_sum_func & (1 << sl->nest_level)))
{
/*
We reached the subquery of level max_arg_level and checked
that the function can be aggregated here.
The set function will be aggregated in this subquery.
*/
aggr_level= sl->nest_level;
aggr_sel= sl;
}
if (aggr_level >= 0)
{
ref_by= ref;
/* Add the object to the list of registered objects assigned to aggr_sel */
if (!aggr_sel->inner_sum_func_list)
next= this;
else
{
next= aggr_sel->inner_sum_func_list->next;
aggr_sel->inner_sum_func_list->next= this;
}
aggr_sel->inner_sum_func_list= this;
aggr_sel->with_sum_func= 1;
/*
Mark Item_subselect(s) as containing aggregate function all the way up
to aggregate function's calculation context.
Note that we must not mark the Item of calculation context itself
because with_sum_func on the calculation context st_select_lex is
already set above.
with_sum_func being set for an Item means that this Item refers
(somewhere in it, e.g. one of its arguments if it's a function) directly
or through intermediate items to an aggregate function that is calculated
in a context "outside" of the Item (e.g. in the current or outer select).
with_sum_func being set for an st_select_lex means that this st_select_lex
has aggregate functions directly referenced (i.e. not through a sub-select).
*/
for (sl= thd->lex->current_select;
sl && sl != aggr_sel && sl->master_unit()->item;
sl= sl->master_unit()->outer_select() )
sl->master_unit()->item->with_sum_func= 1;
}
thd->lex->current_select->mark_as_dependent(aggr_sel);
return FALSE;
}
Item_sum::Item_sum(List<Item> &list) :arg_count(list.elements),
forced_const(FALSE)
2000-07-31 21:29:14 +02:00
{
if ((args=(Item**) sql_alloc(sizeof(Item*)*arg_count)))
{
uint i=0;
List_iterator_fast<Item> li(list);
2000-07-31 21:29:14 +02:00
Item *item;
while ((item=li++))
{
args[i++]= item;
}
}
if (!(orig_args= (Item **) sql_alloc(sizeof(Item *) * arg_count)))
{
args= NULL;
}
mark_as_sum_func();
2009-09-28 09:21:25 +02:00
init_aggregator();
2000-07-31 21:29:14 +02:00
list.empty(); // Fields are used
}
2007-10-11 19:29:09 +02:00
/**
Constructor used in processing select with temporary tebles.
*/
Item_sum::Item_sum(THD *thd, Item_sum *item):
Item_result_field(thd, item),
aggr_sel(item->aggr_sel),
nest_level(item->nest_level), aggr_level(item->aggr_level),
quick_group(item->quick_group),
arg_count(item->arg_count), orig_args(NULL),
used_tables_cache(item->used_tables_cache),
forced_const(item->forced_const)
{
if (arg_count <= 2)
{
args=tmp_args;
orig_args=tmp_orig_args;
}
else
{
if (!(args= (Item**) thd->alloc(sizeof(Item*)*arg_count)))
return;
if (!(orig_args= (Item**) thd->alloc(sizeof(Item*)*arg_count)))
return;
}
memcpy(args, item->args, sizeof(Item*)*arg_count);
memcpy(orig_args, item->orig_args, sizeof(Item*)*arg_count);
2009-09-28 09:21:25 +02:00
init_aggregator();
with_distinct= item->with_distinct;
if (item->aggr)
set_aggregator(item->aggr->Aggrtype());
}
void Item_sum::mark_as_sum_func()
{
SELECT_LEX *cur_select= current_thd->lex->current_select;
cur_select->n_sum_items++;
cur_select->with_sum_func= 1;
with_sum_func= 1;
}
2000-07-31 21:29:14 +02:00
void Item_sum::print(String *str, enum_query_type query_type)
2000-07-31 21:29:14 +02:00
{
/* orig_args is not filled with valid values until fix_fields() */
Item **pargs= fixed ? orig_args : args;
2000-07-31 21:29:14 +02:00
str->append(func_name());
for (uint i=0 ; i < arg_count ; i++)
{
if (i)
str->append(',');
pargs[i]->print(str, query_type);
2000-07-31 21:29:14 +02:00
}
str->append(')');
}
void Item_sum::fix_num_length_and_dec()
{
decimals=0;
for (uint i=0 ; i < arg_count ; i++)
set_if_bigger(decimals,args[i]->decimals);
max_length=float_length(decimals);
}
Item *Item_sum::get_tmp_table_item(THD *thd)
{
Item_sum* sum_item= (Item_sum *) copy_or_same(thd);
if (sum_item && sum_item->result_field) // If not a const sum func
{
Field *result_field_tmp= sum_item->result_field;
for (uint i=0 ; i < sum_item->arg_count ; i++)
{
Item *arg= sum_item->args[i];
if (!arg->const_item())
{
if (arg->type() == Item::FIELD_ITEM)
((Item_field*) arg)->field= result_field_tmp++;
else
sum_item->args[i]= new Item_field(result_field_tmp++);
}
}
}
return sum_item;
}
2000-07-31 21:29:14 +02:00
2005-02-08 23:50:45 +01:00
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes Changes that requires code changes in other code of other storage engines. (Note that all changes are very straightforward and one should find all issues by compiling a --debug build and fixing all compiler errors and all asserts in field.cc while running the test suite), - New optional handler function introduced: reset() This is called after every DML statement to make it easy for a handler to statement specific cleanups. (The only case it's not called is if force the file to be closed) - handler::extra(HA_EXTRA_RESET) is removed. Code that was there before should be moved to handler::reset() - table->read_set contains a bitmap over all columns that are needed in the query. read_row() and similar functions only needs to read these columns - table->write_set contains a bitmap over all columns that will be updated in the query. write_row() and update_row() only needs to update these columns. The above bitmaps should now be up to date in all context (including ALTER TABLE, filesort()). The handler is informed of any changes to the bitmap after fix_fields() by calling the virtual function handler::column_bitmaps_signal(). If the handler does caching of these bitmaps (instead of using table->read_set, table->write_set), it should redo the caching in this code. as the signal() may be sent several times, it's probably best to set a variable in the signal and redo the caching on read_row() / write_row() if the variable was set. - Removed the read_set and write_set bitmap objects from the handler class - Removed all column bit handling functions from the handler class. (Now one instead uses the normal bitmap functions in my_bitmap.c instead of handler dedicated bitmap functions) - field->query_id is removed. One should instead instead check table->read_set and table->write_set if a field is used in the query. - handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now instead use table->read_set to check for which columns to retrieve. - If a handler needs to call Field->val() or Field->store() on columns that are not used in the query, one should install a temporary all-columns-used map while doing so. For this, we provide the following functions: my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); field->val(); dbug_tmp_restore_column_map(table->read_set, old_map); and similar for the write map: my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set); field->val(); dbug_tmp_restore_column_map(table->write_set, old_map); If this is not done, you will sooner or later hit a DBUG_ASSERT in the field store() / val() functions. (For not DBUG binaries, the dbug_tmp_restore_column_map() and dbug_tmp_restore_column_map() are inline dummy functions and should be optimized away be the compiler). - If one needs to temporary set the column map for all binaries (and not just to avoid the DBUG_ASSERT() in the Field::store() / Field::val() methods) one should use the functions tmp_use_all_columns() and tmp_restore_column_map() instead of the above dbug_ variants. - All 'status' fields in the handler base class (like records, data_file_length etc) are now stored in a 'stats' struct. This makes it easier to know what status variables are provided by the base handler. This requires some trivial variable names in the extra() function. - New virtual function handler::records(). This is called to optimize COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true. (stats.records is not supposed to be an exact value. It's only has to be 'reasonable enough' for the optimizer to be able to choose a good optimization path). - Non virtual handler::init() function added for caching of virtual constants from engine. - Removed has_transactions() virtual method. Now one should instead return HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support transactions. - The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument that is to be used with 'new handler_name()' to allocate the handler in the right area. The xxxx_create_handler() function is also responsible for any initialization of the object before returning. For example, one should change: static handler *myisam_create_handler(TABLE_SHARE *table) { return new ha_myisam(table); } -> static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root) { return new (mem_root) ha_myisam(table); } - New optional virtual function: use_hidden_primary_key(). This is called in case of an update/delete when (table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined but we don't have a primary key. This allows the handler to take precisions in remembering any hidden primary key to able to update/delete any found row. The default handler marks all columns to be read. - handler::table_flags() now returns a ulonglong (to allow for more flags). - New/changed table_flags() - HA_HAS_RECORDS Set if ::records() is supported - HA_NO_TRANSACTIONS Set if engine doesn't support transactions - HA_PRIMARY_KEY_REQUIRED_FOR_DELETE Set if we should mark all primary key columns for read when reading rows as part of a DELETE statement. If there is no primary key, all columns are marked for read. - HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some cases (based on table->read_set) - HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION. - HA_DUPP_POS Renamed to HA_DUPLICATE_POS - HA_REQUIRES_KEY_COLUMNS_FOR_DELETE Set this if we should mark ALL key columns for read when when reading rows as part of a DELETE statement. In case of an update we will mark all keys for read for which key part changed value. - HA_STATS_RECORDS_IS_EXACT Set this if stats.records is exact. (This saves us some extra records() calls when optimizing COUNT(*)) - Removed table_flags() - HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if handler::records() gives an exact count() and HA_STATS_RECORDS_IS_EXACT if stats.records is exact. - HA_READ_RND_SAME Removed (no one supported this one) - Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk() - Renamed handler::dupp_pos to handler::dup_pos - Removed not used variable handler::sortkey Upper level handler changes: - ha_reset() now does some overall checks and calls ::reset() - ha_table_flags() added. This is a cached version of table_flags(). The cache is updated on engine creation time and updated on open. MySQL level changes (not obvious from the above): - DBUG_ASSERT() added to check that column usage matches what is set in the column usage bit maps. (This found a LOT of bugs in current column marking code). - In 5.1 before, all used columns was marked in read_set and only updated columns was marked in write_set. Now we only mark columns for which we need a value in read_set. - Column bitmaps are created in open_binary_frm() and open_table_from_share(). (Before this was in table.cc) - handler::table_flags() calls are replaced with handler::ha_table_flags() - For calling field->val() you must have the corresponding bit set in table->read_set. For calling field->store() you must have the corresponding bit set in table->write_set. (There are asserts in all store()/val() functions to catch wrong usage) - thd->set_query_id is renamed to thd->mark_used_columns and instead of setting this to an integer value, this has now the values: MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE Changed also all variables named 'set_query_id' to mark_used_columns. - In filesort() we now inform the handler of exactly which columns are needed doing the sort and choosing the rows. - The TABLE_SHARE object has a 'all_set' column bitmap one can use when one needs a column bitmap with all columns set. (This is used for table->use_all_columns() and other places) - The TABLE object has 3 column bitmaps: - def_read_set Default bitmap for columns to be read - def_write_set Default bitmap for columns to be written - tmp_set Can be used as a temporary bitmap when needed. The table object has also two pointer to bitmaps read_set and write_set that the handler should use to find out which columns are used in which way. - count() optimization now calls handler::records() instead of using handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true). - Added extra argument to Item::walk() to indicate if we should also traverse sub queries. - Added TABLE parameter to cp_buffer_from_ref() - Don't close tables created with CREATE ... SELECT but keep them in the table cache. (Faster usage of newly created tables). New interfaces: - table->clear_column_bitmaps() to initialize the bitmaps for tables at start of new statements. - table->column_bitmaps_set() to set up new column bitmaps and signal the handler about this. - table->column_bitmaps_set_no_signal() for some few cases where we need to setup new column bitmaps but don't signal the handler (as the handler has already been signaled about these before). Used for the momement only in opt_range.cc when doing ROR scans. - table->use_all_columns() to install a bitmap where all columns are marked as use in the read and the write set. - table->default_column_bitmaps() to install the normal read and write column bitmaps, but not signaling the handler about this. This is mainly used when creating TABLE instances. - table->mark_columns_needed_for_delete(), table->mark_columns_needed_for_delete() and table->mark_columns_needed_for_insert() to allow us to put additional columns in column usage maps if handler so requires. (The handler indicates what it neads in handler->table_flags()) - table->prepare_for_position() to allow us to tell handler that it needs to read primary key parts to be able to store them in future table->position() calls. (This replaces the table->file->ha_retrieve_all_pk function) - table->mark_auto_increment_column() to tell handler are going to update columns part of any auto_increment key. - table->mark_columns_used_by_index() to mark all columns that is part of an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow it to quickly know that it only needs to read colums that are part of the key. (The handler can also use the column map for detecting this, but simpler/faster handler can just monitor the extra() call). - table->mark_columns_used_by_index_no_reset() to in addition to other columns, also mark all columns that is used by the given key. - table->restore_column_maps_after_mark_index() to restore to default column maps after a call to table->mark_columns_used_by_index(). - New item function register_field_in_read_map(), for marking used columns in table->read_map. Used by filesort() to mark all used columns - Maintain in TABLE->merge_keys set of all keys that are used in query. (Simplices some optimization loops) - Maintain Field->part_of_key_not_clustered which is like Field->part_of_key but the field in the clustered key is not assumed to be part of all index. (used in opt_range.cc for faster loops) - dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map() tmp_use_all_columns() and tmp_restore_column_map() functions to temporally mark all columns as usable. The 'dbug_' version is primarily intended inside a handler when it wants to just call Field:store() & Field::val() functions, but don't need the column maps set for any other usage. (ie:: bitmap_is_set() is never called) - We can't use compare_records() to skip updates for handlers that returns a partial column set and the read_set doesn't cover all columns in the write set. The reason for this is that if we have a column marked only for write we can't in the MySQL level know if the value changed or not. The reason this worked before was that MySQL marked all to be written columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden bug'. - open_table_from_share() does not anymore setup temporary MEM_ROOT object as a thread specific variable for the handler. Instead we send the to-be-used MEMROOT to get_new_handler(). (Simpler, faster code) Bugs fixed: - Column marking was not done correctly in a lot of cases. (ALTER TABLE, when using triggers, auto_increment fields etc) (Could potentially result in wrong values inserted in table handlers relying on that the old column maps or field->set_query_id was correct) Especially when it comes to triggers, there may be cases where the old code would cause lost/wrong values for NDB and/or InnoDB tables. - Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags: OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG. This allowed me to remove some wrong warnings about: "Some non-transactional changed tables couldn't be rolled back" - Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset (thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose some warnings about "Some non-transactional changed tables couldn't be rolled back") - Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table() which could cause delete_table to report random failures. - Fixed core dumps for some tests when running with --debug - Added missing FN_LIBCHAR in mysql_rm_tmp_tables() (This has probably caused us to not properly remove temporary files after crash) - slow_logs was not properly initialized, which could maybe cause extra/lost entries in slow log. - If we get an duplicate row on insert, change column map to read and write all columns while retrying the operation. This is required by the definition of REPLACE and also ensures that fields that are only part of UPDATE are properly handled. This fixed a bug in NDB and REPLACE where REPLACE wrongly copied some column values from the replaced row. - For table handler that doesn't support NULL in keys, we would give an error when creating a primary key with NULL fields, even after the fields has been automaticly converted to NOT NULL. - Creating a primary key on a SPATIAL key, would fail if field was not declared as NOT NULL. Cleanups: - Removed not used condition argument to setup_tables - Removed not needed item function reset_query_id_processor(). - Field->add_index is removed. Now this is instead maintained in (field->flags & FIELD_IN_ADD_INDEX) - Field->fieldnr is removed (use field->field_index instead) - New argument to filesort() to indicate that it should return a set of row pointers (not used columns). This allowed me to remove some references to sql_command in filesort and should also enable us to return column results in some cases where we couldn't before. - Changed column bitmap handling in opt_range.cc to be aligned with TABLE bitmap, which allowed me to use bitmap functions instead of looping over all fields to create some needed bitmaps. (Faster and smaller code) - Broke up found too long lines - Moved some variable declaration at start of function for better code readability. - Removed some not used arguments from functions. (setup_fields(), mysql_prepare_insert_check_table()) - setup_fields() now takes an enum instead of an int for marking columns usage. - For internal temporary tables, use handler::write_row(), handler::delete_row() and handler::update_row() instead of handler::ha_xxxx() for faster execution. - Changed some constants to enum's and define's. - Using separate column read and write sets allows for easier checking of timestamp field was set by statement. - Remove calls to free_io_cache() as this is now done automaticly in ha_reset() - Don't build table->normalized_path as this is now identical to table->path (after bar's fixes to convert filenames) - Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to do comparision with the 'convert-dbug-for-diff' tool. Things left to do in 5.1: - We wrongly log failed CREATE TABLE ... SELECT in some cases when using row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result) Mats has promised to look into this. - Test that my fix for CREATE TABLE ... SELECT is indeed correct. (I added several test cases for this, but in this case it's better that someone else also tests this throughly). Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
bool Item_sum::walk (Item_processor processor, bool walk_subquery,
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
2007-05-10 11:59:39 +02:00
uchar *argument)
{
if (arg_count)
{
Item **arg,**arg_end;
for (arg= args, arg_end= args+arg_count; arg != arg_end; arg++)
{
This changeset is largely a handler cleanup changeset (WL#3281), but includes fixes and cleanups that was found necessary while testing the handler changes Changes that requires code changes in other code of other storage engines. (Note that all changes are very straightforward and one should find all issues by compiling a --debug build and fixing all compiler errors and all asserts in field.cc while running the test suite), - New optional handler function introduced: reset() This is called after every DML statement to make it easy for a handler to statement specific cleanups. (The only case it's not called is if force the file to be closed) - handler::extra(HA_EXTRA_RESET) is removed. Code that was there before should be moved to handler::reset() - table->read_set contains a bitmap over all columns that are needed in the query. read_row() and similar functions only needs to read these columns - table->write_set contains a bitmap over all columns that will be updated in the query. write_row() and update_row() only needs to update these columns. The above bitmaps should now be up to date in all context (including ALTER TABLE, filesort()). The handler is informed of any changes to the bitmap after fix_fields() by calling the virtual function handler::column_bitmaps_signal(). If the handler does caching of these bitmaps (instead of using table->read_set, table->write_set), it should redo the caching in this code. as the signal() may be sent several times, it's probably best to set a variable in the signal and redo the caching on read_row() / write_row() if the variable was set. - Removed the read_set and write_set bitmap objects from the handler class - Removed all column bit handling functions from the handler class. (Now one instead uses the normal bitmap functions in my_bitmap.c instead of handler dedicated bitmap functions) - field->query_id is removed. One should instead instead check table->read_set and table->write_set if a field is used in the query. - handler::extra(HA_EXTRA_RETRIVE_ALL_COLS) and handler::extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY) are removed. One should now instead use table->read_set to check for which columns to retrieve. - If a handler needs to call Field->val() or Field->store() on columns that are not used in the query, one should install a temporary all-columns-used map while doing so. For this, we provide the following functions: my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); field->val(); dbug_tmp_restore_column_map(table->read_set, old_map); and similar for the write map: my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set); field->val(); dbug_tmp_restore_column_map(table->write_set, old_map); If this is not done, you will sooner or later hit a DBUG_ASSERT in the field store() / val() functions. (For not DBUG binaries, the dbug_tmp_restore_column_map() and dbug_tmp_restore_column_map() are inline dummy functions and should be optimized away be the compiler). - If one needs to temporary set the column map for all binaries (and not just to avoid the DBUG_ASSERT() in the Field::store() / Field::val() methods) one should use the functions tmp_use_all_columns() and tmp_restore_column_map() instead of the above dbug_ variants. - All 'status' fields in the handler base class (like records, data_file_length etc) are now stored in a 'stats' struct. This makes it easier to know what status variables are provided by the base handler. This requires some trivial variable names in the extra() function. - New virtual function handler::records(). This is called to optimize COUNT(*) if (handler::table_flags() & HA_HAS_RECORDS()) is true. (stats.records is not supposed to be an exact value. It's only has to be 'reasonable enough' for the optimizer to be able to choose a good optimization path). - Non virtual handler::init() function added for caching of virtual constants from engine. - Removed has_transactions() virtual method. Now one should instead return HA_NO_TRANSACTIONS in table_flags() if the table handler DOES NOT support transactions. - The 'xxxx_create_handler()' function now has a MEM_ROOT_root argument that is to be used with 'new handler_name()' to allocate the handler in the right area. The xxxx_create_handler() function is also responsible for any initialization of the object before returning. For example, one should change: static handler *myisam_create_handler(TABLE_SHARE *table) { return new ha_myisam(table); } -> static handler *myisam_create_handler(TABLE_SHARE *table, MEM_ROOT *mem_root) { return new (mem_root) ha_myisam(table); } - New optional virtual function: use_hidden_primary_key(). This is called in case of an update/delete when (table_flags() and HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) is defined but we don't have a primary key. This allows the handler to take precisions in remembering any hidden primary key to able to update/delete any found row. The default handler marks all columns to be read. - handler::table_flags() now returns a ulonglong (to allow for more flags). - New/changed table_flags() - HA_HAS_RECORDS Set if ::records() is supported - HA_NO_TRANSACTIONS Set if engine doesn't support transactions - HA_PRIMARY_KEY_REQUIRED_FOR_DELETE Set if we should mark all primary key columns for read when reading rows as part of a DELETE statement. If there is no primary key, all columns are marked for read. - HA_PARTIAL_COLUMN_READ Set if engine will not read all columns in some cases (based on table->read_set) - HA_PRIMARY_KEY_ALLOW_RANDOM_ACCESS Renamed to HA_PRIMARY_KEY_REQUIRED_FOR_POSITION. - HA_DUPP_POS Renamed to HA_DUPLICATE_POS - HA_REQUIRES_KEY_COLUMNS_FOR_DELETE Set this if we should mark ALL key columns for read when when reading rows as part of a DELETE statement. In case of an update we will mark all keys for read for which key part changed value. - HA_STATS_RECORDS_IS_EXACT Set this if stats.records is exact. (This saves us some extra records() calls when optimizing COUNT(*)) - Removed table_flags() - HA_NOT_EXACT_COUNT Now one should instead use HA_HAS_RECORDS if handler::records() gives an exact count() and HA_STATS_RECORDS_IS_EXACT if stats.records is exact. - HA_READ_RND_SAME Removed (no one supported this one) - Removed not needed functions ha_retrieve_all_cols() and ha_retrieve_all_pk() - Renamed handler::dupp_pos to handler::dup_pos - Removed not used variable handler::sortkey Upper level handler changes: - ha_reset() now does some overall checks and calls ::reset() - ha_table_flags() added. This is a cached version of table_flags(). The cache is updated on engine creation time and updated on open. MySQL level changes (not obvious from the above): - DBUG_ASSERT() added to check that column usage matches what is set in the column usage bit maps. (This found a LOT of bugs in current column marking code). - In 5.1 before, all used columns was marked in read_set and only updated columns was marked in write_set. Now we only mark columns for which we need a value in read_set. - Column bitmaps are created in open_binary_frm() and open_table_from_share(). (Before this was in table.cc) - handler::table_flags() calls are replaced with handler::ha_table_flags() - For calling field->val() you must have the corresponding bit set in table->read_set. For calling field->store() you must have the corresponding bit set in table->write_set. (There are asserts in all store()/val() functions to catch wrong usage) - thd->set_query_id is renamed to thd->mark_used_columns and instead of setting this to an integer value, this has now the values: MARK_COLUMNS_NONE, MARK_COLUMNS_READ, MARK_COLUMNS_WRITE Changed also all variables named 'set_query_id' to mark_used_columns. - In filesort() we now inform the handler of exactly which columns are needed doing the sort and choosing the rows. - The TABLE_SHARE object has a 'all_set' column bitmap one can use when one needs a column bitmap with all columns set. (This is used for table->use_all_columns() and other places) - The TABLE object has 3 column bitmaps: - def_read_set Default bitmap for columns to be read - def_write_set Default bitmap for columns to be written - tmp_set Can be used as a temporary bitmap when needed. The table object has also two pointer to bitmaps read_set and write_set that the handler should use to find out which columns are used in which way. - count() optimization now calls handler::records() instead of using handler->stats.records (if (table_flags() & HA_HAS_RECORDS) is true). - Added extra argument to Item::walk() to indicate if we should also traverse sub queries. - Added TABLE parameter to cp_buffer_from_ref() - Don't close tables created with CREATE ... SELECT but keep them in the table cache. (Faster usage of newly created tables). New interfaces: - table->clear_column_bitmaps() to initialize the bitmaps for tables at start of new statements. - table->column_bitmaps_set() to set up new column bitmaps and signal the handler about this. - table->column_bitmaps_set_no_signal() for some few cases where we need to setup new column bitmaps but don't signal the handler (as the handler has already been signaled about these before). Used for the momement only in opt_range.cc when doing ROR scans. - table->use_all_columns() to install a bitmap where all columns are marked as use in the read and the write set. - table->default_column_bitmaps() to install the normal read and write column bitmaps, but not signaling the handler about this. This is mainly used when creating TABLE instances. - table->mark_columns_needed_for_delete(), table->mark_columns_needed_for_delete() and table->mark_columns_needed_for_insert() to allow us to put additional columns in column usage maps if handler so requires. (The handler indicates what it neads in handler->table_flags()) - table->prepare_for_position() to allow us to tell handler that it needs to read primary key parts to be able to store them in future table->position() calls. (This replaces the table->file->ha_retrieve_all_pk function) - table->mark_auto_increment_column() to tell handler are going to update columns part of any auto_increment key. - table->mark_columns_used_by_index() to mark all columns that is part of an index. It will also send extra(HA_EXTRA_KEYREAD) to handler to allow it to quickly know that it only needs to read colums that are part of the key. (The handler can also use the column map for detecting this, but simpler/faster handler can just monitor the extra() call). - table->mark_columns_used_by_index_no_reset() to in addition to other columns, also mark all columns that is used by the given key. - table->restore_column_maps_after_mark_index() to restore to default column maps after a call to table->mark_columns_used_by_index(). - New item function register_field_in_read_map(), for marking used columns in table->read_map. Used by filesort() to mark all used columns - Maintain in TABLE->merge_keys set of all keys that are used in query. (Simplices some optimization loops) - Maintain Field->part_of_key_not_clustered which is like Field->part_of_key but the field in the clustered key is not assumed to be part of all index. (used in opt_range.cc for faster loops) - dbug_tmp_use_all_columns(), dbug_tmp_restore_column_map() tmp_use_all_columns() and tmp_restore_column_map() functions to temporally mark all columns as usable. The 'dbug_' version is primarily intended inside a handler when it wants to just call Field:store() & Field::val() functions, but don't need the column maps set for any other usage. (ie:: bitmap_is_set() is never called) - We can't use compare_records() to skip updates for handlers that returns a partial column set and the read_set doesn't cover all columns in the write set. The reason for this is that if we have a column marked only for write we can't in the MySQL level know if the value changed or not. The reason this worked before was that MySQL marked all to be written columns as also to be read. The new 'optimal' bitmaps exposed this 'hidden bug'. - open_table_from_share() does not anymore setup temporary MEM_ROOT object as a thread specific variable for the handler. Instead we send the to-be-used MEMROOT to get_new_handler(). (Simpler, faster code) Bugs fixed: - Column marking was not done correctly in a lot of cases. (ALTER TABLE, when using triggers, auto_increment fields etc) (Could potentially result in wrong values inserted in table handlers relying on that the old column maps or field->set_query_id was correct) Especially when it comes to triggers, there may be cases where the old code would cause lost/wrong values for NDB and/or InnoDB tables. - Split thd->options flag OPTION_STATUS_NO_TRANS_UPDATE to two flags: OPTION_STATUS_NO_TRANS_UPDATE and OPTION_KEEP_LOG. This allowed me to remove some wrong warnings about: "Some non-transactional changed tables couldn't be rolled back" - Fixed handling of INSERT .. SELECT and CREATE ... SELECT that wrongly reset (thd->options & OPTION_STATUS_NO_TRANS_UPDATE) which caused us to loose some warnings about "Some non-transactional changed tables couldn't be rolled back") - Fixed use of uninitialized memory in ha_ndbcluster.cc::delete_table() which could cause delete_table to report random failures. - Fixed core dumps for some tests when running with --debug - Added missing FN_LIBCHAR in mysql_rm_tmp_tables() (This has probably caused us to not properly remove temporary files after crash) - slow_logs was not properly initialized, which could maybe cause extra/lost entries in slow log. - If we get an duplicate row on insert, change column map to read and write all columns while retrying the operation. This is required by the definition of REPLACE and also ensures that fields that are only part of UPDATE are properly handled. This fixed a bug in NDB and REPLACE where REPLACE wrongly copied some column values from the replaced row. - For table handler that doesn't support NULL in keys, we would give an error when creating a primary key with NULL fields, even after the fields has been automaticly converted to NOT NULL. - Creating a primary key on a SPATIAL key, would fail if field was not declared as NOT NULL. Cleanups: - Removed not used condition argument to setup_tables - Removed not needed item function reset_query_id_processor(). - Field->add_index is removed. Now this is instead maintained in (field->flags & FIELD_IN_ADD_INDEX) - Field->fieldnr is removed (use field->field_index instead) - New argument to filesort() to indicate that it should return a set of row pointers (not used columns). This allowed me to remove some references to sql_command in filesort and should also enable us to return column results in some cases where we couldn't before. - Changed column bitmap handling in opt_range.cc to be aligned with TABLE bitmap, which allowed me to use bitmap functions instead of looping over all fields to create some needed bitmaps. (Faster and smaller code) - Broke up found too long lines - Moved some variable declaration at start of function for better code readability. - Removed some not used arguments from functions. (setup_fields(), mysql_prepare_insert_check_table()) - setup_fields() now takes an enum instead of an int for marking columns usage. - For internal temporary tables, use handler::write_row(), handler::delete_row() and handler::update_row() instead of handler::ha_xxxx() for faster execution. - Changed some constants to enum's and define's. - Using separate column read and write sets allows for easier checking of timestamp field was set by statement. - Remove calls to free_io_cache() as this is now done automaticly in ha_reset() - Don't build table->normalized_path as this is now identical to table->path (after bar's fixes to convert filenames) - Fixed some missed DBUG_PRINT(.."%lx") to use "0x%lx" to make it easier to do comparision with the 'convert-dbug-for-diff' tool. Things left to do in 5.1: - We wrongly log failed CREATE TABLE ... SELECT in some cases when using row based logging (as shown by testcase binlog_row_mix_innodb_myisam.result) Mats has promised to look into this. - Test that my fix for CREATE TABLE ... SELECT is indeed correct. (I added several test cases for this, but in this case it's better that someone else also tests this throughly). Lars has promosed to do this.
2006-06-04 17:52:22 +02:00
if ((*arg)->walk(processor, walk_subquery, argument))
return 1;
}
}
return (this->*processor)(argument);
}
2005-02-08 23:50:45 +01:00
Field *Item_sum::create_tmp_field(bool group, TABLE *table,
uint convert_blob_length)
{
Field *field;
2005-02-08 23:50:45 +01:00
switch (result_type()) {
case REAL_RESULT:
field= new Field_double(max_length, maybe_null, name, decimals, TRUE);
break;
2005-02-08 23:50:45 +01:00
case INT_RESULT:
field= new Field_longlong(max_length, maybe_null, name, unsigned_flag);
break;
2005-02-08 23:50:45 +01:00
case STRING_RESULT:
if (max_length/collation.collation->mbmaxlen <= 255 ||
convert_blob_length > Field_varstring::MAX_SIZE ||
!convert_blob_length)
return make_string_field(table);
field= new Field_varstring(convert_blob_length, maybe_null,
name, table->s, collation.collation);
break;
2005-02-08 23:50:45 +01:00
case DECIMAL_RESULT:
field= Field_new_decimal::create_from_item(this);
break;
2005-02-08 23:50:45 +01:00
case ROW_RESULT:
default:
// This case should never be choosen
DBUG_ASSERT(0);
return 0;
}
if (field)
field->init(table);
return field;
2005-02-08 23:50:45 +01:00
}
void Item_sum::update_used_tables ()
{
if (!forced_const)
{
used_tables_cache= 0;
for (uint i=0 ; i < arg_count ; i++)
{
args[i]->update_used_tables();
used_tables_cache|= args[i]->used_tables();
}
used_tables_cache&= PSEUDO_TABLE_BITS;
/* the aggregate function is aggregated into its local context */
used_tables_cache |= (1 << aggr_sel->join->tables) - 1;
}
}
2009-09-28 09:21:25 +02:00
Item *Item_sum::set_arg(uint i, THD *thd, Item *new_val)
{
thd->change_item_tree(args + i, new_val);
return new_val;
}
2009-09-28 09:21:25 +02:00
int Item_sum::set_aggregator(Aggregator::Aggregator_type aggregator)
{
if (aggr)
{
/*
Dependent subselects may be executed multiple times, making
set_aggregator to be called multiple times. The aggregator type
will be the same, but it needs to be reset so that it is
reevaluated with the new dependent data.
*/
DBUG_ASSERT(aggregator == aggr->Aggrtype());
aggr->clear();
return FALSE;
}
2009-09-28 09:21:25 +02:00
switch (aggregator)
{
case Aggregator::DISTINCT_AGGREGATOR:
aggr= new Aggregator_distinct(this);
break;
case Aggregator::SIMPLE_AGGREGATOR:
aggr= new Aggregator_simple(this);
break;
};
return aggr ? FALSE : TRUE;
}
void Item_sum::cleanup()
{
if (aggr)
{
delete aggr;
aggr= NULL;
}
Item_result_field::cleanup();
forced_const= FALSE;
}
/**
Compare keys consisting of single field that cannot be compared as binary.
Used by the Unique class to compare keys. Will do correct comparisons
for all field types.
@param arg Pointer to the relevant Field class instance
@param key1 left key image
@param key2 right key image
@return comparison result
@retval < 0 if key1 < key2
@retval = 0 if key1 = key2
@retval > 0 if key1 > key2
*/
static int simple_str_key_cmp(void* arg, uchar* key1, uchar* key2)
{
Field *f= (Field*) arg;
return f->cmp(key1, key2);
}
/**
Correctly compare composite keys.
Used by the Unique class to compare keys. Will do correct comparisons
for composite keys with various field types.
@param arg Pointer to the relevant Aggregator_distinct instance
@param key1 left key image
@param key2 right key image
@return comparison result
@retval <0 if key1 < key2
@retval =0 if key1 = key2
@retval >0 if key1 > key2
*/
int Aggregator_distinct::composite_key_cmp(void* arg, uchar* key1, uchar* key2)
{
Aggregator_distinct *aggr= (Aggregator_distinct *) arg;
Field **field = aggr->table->field;
Field **field_end= field + aggr->table->s->fields;
uint32 *lengths=aggr->field_lengths;
for (; field < field_end; ++field)
{
Field* f = *field;
int len = *lengths++;
int res = f->cmp(key1, key2);
if (res)
return res;
key1 += len;
key2 += len;
}
return 0;
}
static enum enum_field_types
calc_tmp_field_type(enum enum_field_types table_field_type,
Item_result result_type)
{
/* Adjust tmp table type according to the chosen aggregation type */
switch (result_type) {
case STRING_RESULT:
case REAL_RESULT:
if (table_field_type != MYSQL_TYPE_FLOAT)
table_field_type= MYSQL_TYPE_DOUBLE;
break;
case INT_RESULT:
table_field_type= MYSQL_TYPE_LONGLONG;
/* fallthrough */
case DECIMAL_RESULT:
if (table_field_type != MYSQL_TYPE_LONGLONG)
table_field_type= MYSQL_TYPE_NEWDECIMAL;
break;
case ROW_RESULT:
default:
DBUG_ASSERT(0);
}
return table_field_type;
}
/***************************************************************************/
C_MODE_START
/* Declarations for auxilary C-callbacks */
static int simple_raw_key_cmp(void* arg, const void* key1, const void* key2)
{
return memcmp(key1, key2, *(uint *) arg);
}
static int item_sum_distinct_walk(void *element, element_count num_of_dups,
void *item)
{
return ((Aggregator_distinct*) (item))->unique_walk_function(element);
}
C_MODE_END
/***************************************************************************/
/**
Called before feeding the first row. Used to allocate/setup
the internal structures used for aggregation.
@param thd Thread descriptor
@return status
@retval FALSE success
@retval TRUE faliure
Prepares Aggregator_distinct to process the incoming stream.
Creates the temporary table and the Unique class if needed.
Called by Item_sum::aggregator_setup()
*/
bool Aggregator_distinct::setup(THD *thd)
{
endup_done= FALSE;
/*
Setup can be called twice for ROLLUP items. This is a bug.
Please add DBUG_ASSERT(tree == 0) here when it's fixed.
*/
if (tree || table || tmp_table_param)
return FALSE;
if (item_sum->setup(thd))
return TRUE;
if (item_sum->sum_func() == Item_sum::COUNT_FUNC ||
item_sum->sum_func() == Item_sum::COUNT_DISTINCT_FUNC)
{
List<Item> list;
SELECT_LEX *select_lex= thd->lex->current_select;
if (!(tmp_table_param= new TMP_TABLE_PARAM))
return TRUE;
/* Create a table with an unique key over all parameters */
for (uint i=0; i < item_sum->get_arg_count() ; i++)
{
Item *item=item_sum->get_arg(i);
if (list.push_back(item))
return TRUE; // End of memory
if (item->const_item() && item->is_null())
always_null= true;
2009-09-28 09:21:25 +02:00
}
if (always_null)
return FALSE;
count_field_types(select_lex, tmp_table_param, list, 0);
tmp_table_param->force_copy_fields= item_sum->has_force_copy_fields();
2009-09-28 09:21:25 +02:00
DBUG_ASSERT(table == 0);
/*
Make create_tmp_table() convert BIT columns to BIGINT.
This is needed because BIT fields store parts of their data in table's
null bits, and we don't have methods to compare two table records, which
is needed by Unique which is used when HEAP table is used.
*/
{
List_iterator_fast<Item> li(list);
Item *item;
while ((item= li++))
{
if (item->type() == Item::FIELD_ITEM &&
((Item_field*)item)->field->type() == FIELD_TYPE_BIT)
item->marker=4;
}
}
if (!(table= create_tmp_table(thd, tmp_table_param, list, (ORDER*) 0, 1,
0,
WL#4738 streamline/simplify @@variable creation process Bug#16565 mysqld --help --verbose does not order variablesBug#20413 sql_slave_skip_counter is not shown in show variables Bug#20415 Output of mysqld --help --verbose is incomplete Bug#25430 variable not found in SELECT @@global.ft_max_word_len; Bug#32902 plugin variables don't know their names Bug#34599 MySQLD Option and Variable Reference need to be consistent in formatting! Bug#34829 No default value for variable and setting default does not raise error Bug#34834 ? Is accepted as a valid sql mode Bug#34878 Few variables have default value according to documentation but error occurs Bug#34883 ft_boolean_syntax cant be assigned from user variable to global var. Bug#37187 `INFORMATION_SCHEMA`.`GLOBAL_VARIABLES`: inconsistent status Bug#40988 log_output_basic.test succeeded though syntactically false. Bug#41010 enum-style command-line options are not honoured (maria.maria-recover fails) Bug#42103 Setting key_buffer_size to a negative value may lead to very large allocations Bug#44691 Some plugins configured as MYSQL_PLUGIN_MANDATORY in can be disabled Bug#44797 plugins w/o command-line options have no disabling option in --help Bug#46314 string system variables don't support expressions Bug#46470 sys_vars.max_binlog_cache_size_basic_32 is broken Bug#46586 When using the plugin interface the type "set" for options caused a crash. Bug#47212 Crash in DBUG_PRINT in mysqltest.cc when trying to print octal number Bug#48758 mysqltest crashes on sys_vars.collation_server_basic in gcov builds Bug#49417 some complaints about mysqld --help --verbose output Bug#49540 DEFAULT value of binlog_format isn't the default value Bug#49640 ambiguous option '--skip-skip-myisam' (double skip prefix) Bug#49644 init_connect and \0 Bug#49645 init_slave and multi-byte characters Bug#49646 mysql --show-warnings crashes when server dies
2009-12-22 10:35:56 +01:00
(select_lex->options | thd->variables.option_bits),
HA_POS_ERROR, "")))
2009-09-28 09:21:25 +02:00
return TRUE;
table->file->extra(HA_EXTRA_NO_ROWS); // Don't update rows
table->no_rows=1;
if (table->s->db_type() == heap_hton)
{
/*
No blobs, otherwise it would have been MyISAM: set up a compare
function and its arguments to use with Unique.
*/
qsort_cmp2 compare_key;
void* cmp_arg;
Field **field= table->field;
Field **field_end= field + table->s->fields;
bool all_binary= TRUE;
for (tree_key_length= 0; field < field_end; ++field)
{
Field *f= *field;
enum enum_field_types type= f->type();
tree_key_length+= f->pack_length();
if ((type == MYSQL_TYPE_VARCHAR) ||
(!f->binary() && (type == MYSQL_TYPE_STRING ||
type == MYSQL_TYPE_VAR_STRING)))
{
all_binary= FALSE;
break;
}
}
if (all_binary)
{
cmp_arg= (void*) &tree_key_length;
compare_key= (qsort_cmp2) simple_raw_key_cmp;
}
else
{
if (table->s->fields == 1)
{
/*
If we have only one field, which is the most common use of
count(distinct), it is much faster to use a simpler key
compare method that can take advantage of not having to worry
about other fields.
*/
compare_key= (qsort_cmp2) simple_str_key_cmp;
cmp_arg= (void*) table->field[0];
/* tree_key_length has been set already */
}
else
{
uint32 *length;
compare_key= (qsort_cmp2) composite_key_cmp;
cmp_arg= (void*) this;
field_lengths= (uint32*) thd->alloc(table->s->fields * sizeof(uint32));
for (tree_key_length= 0, length= field_lengths, field= table->field;
field < field_end; ++field, ++length)
{
*length= (*field)->pack_length();
tree_key_length+= *length;
}
}
}
DBUG_ASSERT(tree == 0);
tree= new Unique(compare_key, cmp_arg, tree_key_length,
2009-11-17 12:24:23 +01:00
item_sum->ram_limitation(thd));
2009-09-28 09:21:25 +02:00
/*
The only time tree_key_length could be 0 is if someone does
count(distinct) on a char(0) field - stupid thing to do,
but this has to be handled - otherwise someone can crash
the server with a DoS attack
*/
if (! tree)
return TRUE;
}
return FALSE;
}
else
{
List<Create_field> field_list;
Create_field field_def; /* field definition */
Item *arg;
DBUG_ENTER("Aggregator_distinct::setup");
2009-09-28 09:21:25 +02:00
/* It's legal to call setup() more than once when in a subquery */
if (tree)
DBUG_RETURN(FALSE);
2009-09-28 09:21:25 +02:00
/*
Virtual table and the tree are created anew on each re-execution of
PS/SP. Hence all further allocations are performed in the runtime
mem_root.
*/
if (field_list.push_back(&field_def))
DBUG_RETURN(TRUE);
2009-09-28 09:21:25 +02:00
item_sum->null_value= item_sum->maybe_null= 1;
item_sum->quick_group= 0;
DBUG_ASSERT(item_sum->get_arg(0)->fixed);
arg= item_sum->get_arg(0);
2009-09-28 09:21:25 +02:00
if (arg->const_item())
{
(void) arg->val_int();
if (arg->null_value)
always_null= true;
2009-09-28 09:21:25 +02:00
}
if (always_null)
DBUG_RETURN(FALSE);
2009-09-28 09:21:25 +02:00
enum enum_field_types field_type;
field_type= calc_tmp_field_type(arg->field_type(),
arg->result_type());
field_def.init_for_tmp_table(field_type,
arg->max_length,
arg->decimals,
arg->maybe_null,
arg->unsigned_flag);
if (! (table= create_virtual_tmp_table(thd, field_list)))
DBUG_RETURN(TRUE);
2009-09-28 09:21:25 +02:00
/* XXX: check that the case of CHAR(0) works OK */
tree_key_length= table->s->reclength - table->s->null_bytes;
/*
Unique handles all unique elements in a tree until they can't fit
in. Then the tree is dumped to the temporary file. We can use
simple_raw_key_cmp because the table contains numbers only; decimals
are converted to binary representation as well.
*/
tree= new Unique(simple_raw_key_cmp, &tree_key_length, tree_key_length,
2009-11-17 12:24:23 +01:00
item_sum->ram_limitation(thd));
2009-09-28 09:21:25 +02:00
DBUG_RETURN(tree == 0);
}
}
/**
Invalidate calculated value and clear the distinct rows.
Frees space used by the internal data structures.
Removes the accumulated distinct rows. Invalidates the calculated result.
*/
void Aggregator_distinct::clear()
{
endup_done= FALSE;
item_sum->clear();
if (tree)
tree->reset();
/* tree and table can be both null only if always_null */
if (item_sum->sum_func() == Item_sum::COUNT_FUNC ||
item_sum->sum_func() == Item_sum::COUNT_DISTINCT_FUNC)
{
if (!tree && table)
{
table->file->extra(HA_EXTRA_NO_CACHE);
table->file->ha_delete_all_rows();
table->file->extra(HA_EXTRA_WRITE_CACHE);
}
}
else
{
item_sum->null_value= 1;
}
}
/**
Process incoming row.
Add it to Unique/temp hash table if it's unique. Skip the row if
not unique.
Prepare Aggregator_distinct to process the incoming stream.
Create the temporary table and the Unique class if needed.
Called by Item_sum::aggregator_add().
To actually get the result value in item_sum's buffers
Aggregator_distinct::endup() must be called.
@return status
@retval FALSE success
@retval TRUE failure
*/
bool Aggregator_distinct::add()
{
if (always_null)
return 0;
if (item_sum->sum_func() == Item_sum::COUNT_FUNC ||
item_sum->sum_func() == Item_sum::COUNT_DISTINCT_FUNC)
{
int error;
copy_fields(tmp_table_param);
2010-08-17 14:12:52 +02:00
if (copy_funcs(tmp_table_param->items_to_copy, table->in_use))
return TRUE;
2009-09-28 09:21:25 +02:00
for (Field **field=table->field ; *field ; field++)
if ((*field)->is_real_null(0))
return 0; // Don't count NULL
if (tree)
{
/*
The first few bytes of record (at least one) are just markers
for deleted and NULLs. We want to skip them since they will
bloat the tree without providing any valuable info. Besides,
key_length used to initialize the tree didn't include space for them.
*/
return tree->unique_add(table->record[0] + table->s->null_bytes);
}
if ((error= table->file->ha_write_row(table->record[0])) &&
table->file->is_fatal_error(error, HA_CHECK_DUP))
return TRUE;
return FALSE;
}
else
{
item_sum->get_arg(0)->save_in_field(table->field[0], FALSE);
if (table->field[0]->is_null())
return 0;
DBUG_ASSERT(tree);
item_sum->null_value= 0;
/*
'0' values are also stored in the tree. This doesn't matter
for SUM(DISTINCT), but is important for AVG(DISTINCT)
*/
return tree->unique_add(table->field[0]->ptr);
}
}
/**
Calculate the aggregate function value.
Since Distinct_aggregator::add() just collects the distinct rows,
we must go over the distinct rows and feed them to the aggregation
function before returning its value.
This is what endup () does. It also sets the result validity flag
endup_done to TRUE so it will not recalculate the aggregate value
again if the Item_sum hasn't been reset.
*/
void Aggregator_distinct::endup()
{
/* prevent consecutive recalculations */
if (endup_done)
return;
/* we are going to calculate the aggregate value afresh */
item_sum->clear();
/* The result will definitely be null : no more calculations needed */
if (always_null)
return;
if (item_sum->sum_func() == Item_sum::COUNT_FUNC ||
item_sum->sum_func() == Item_sum::COUNT_DISTINCT_FUNC)
{
DBUG_ASSERT(item_sum->fixed == 1);
Item_sum_count *sum= (Item_sum_count *)item_sum;
if (tree && tree->elements == 0)
{
/* everything fits in memory */
sum->count= (longlong) tree->elements_in_tree();
endup_done= TRUE;
}
if (!tree)
{
/* there were blobs */
table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
sum->count= table->file->stats.records;
endup_done= TRUE;
}
}
else
{
/*
We don't have a tree only if 'setup()' hasn't been called;
this is the case of sql_select.cc:return_zero_rows.
*/
if (tree)
table->field[0]->set_notnull();
}
if (tree && !endup_done)
{
/* go over the tree of distinct keys and calculate the aggregate value */
use_distinct_values= TRUE;
tree->walk(item_sum_distinct_walk, (void*) this);
use_distinct_values= FALSE;
}
/* prevent consecutive recalculations */
endup_done= TRUE;
}
2000-07-31 21:29:14 +02:00
String *
Item_sum_num::val_str(String *str)
{
return val_string_from_real(str);
2000-07-31 21:29:14 +02:00
}
2005-02-08 23:50:45 +01:00
my_decimal *Item_sum_num::val_decimal(my_decimal *decimal_value)
{
return val_decimal_from_real(decimal_value);
2005-02-08 23:50:45 +01:00
}
2000-07-31 21:29:14 +02:00
String *
Item_sum_int::val_str(String *str)
{
return val_string_from_int(str);
}
my_decimal *Item_sum_int::val_decimal(my_decimal *decimal_value)
{
return val_decimal_from_int(decimal_value);
2000-07-31 21:29:14 +02:00
}
bool
Item_sum_num::fix_fields(THD *thd, Item **ref)
2000-07-31 21:29:14 +02:00
{
DBUG_ASSERT(fixed == 0);
if (init_sum_func_check(thd))
return TRUE;
2000-07-31 21:29:14 +02:00
decimals=0;
maybe_null=0;
for (uint i=0 ; i < arg_count ; i++)
{
if (args[i]->fix_fields(thd, args + i) || args[i]->check_cols(1))
return TRUE;
2005-02-08 23:50:45 +01:00
set_if_bigger(decimals, args[i]->decimals);
2000-07-31 21:29:14 +02:00
maybe_null |= args[i]->maybe_null;
}
result_field=0;
max_length=float_length(decimals);
null_value=1;
fix_length_and_dec();
if (check_sum_func(thd, ref))
return TRUE;
memcpy (orig_args, args, sizeof (Item *) * arg_count);
fixed= 1;
return FALSE;
2000-07-31 21:29:14 +02:00
}
bool
Item_sum_hybrid::fix_fields(THD *thd, Item **ref)
2000-07-31 21:29:14 +02:00
{
DBUG_ASSERT(fixed == 0);
Item *item= args[0];
if (init_sum_func_check(thd))
return TRUE;
// 'item' can be changed during fix_fields
if ((!item->fixed && item->fix_fields(thd, args)) ||
(item= args[0])->check_cols(1))
return TRUE;
2005-02-08 23:50:45 +01:00
decimals=item->decimals;
switch (hybrid_type= item->result_type()) {
2005-02-08 23:50:45 +01:00
case INT_RESULT:
max_length= 20;
break;
case DECIMAL_RESULT:
max_length= item->max_length;
break;
case REAL_RESULT:
max_length= float_length(decimals);
break;
case STRING_RESULT:
max_length= item->max_length;
break;
case ROW_RESULT:
default:
DBUG_ASSERT(0);
};
setup_hybrid(args[0], NULL);
/* MIN/MAX can return NULL for empty set indepedent of the used column */
maybe_null= 1;
unsigned_flag=item->unsigned_flag;
2000-07-31 21:29:14 +02:00
result_field=0;
null_value=1;
fix_length_and_dec();
item= item->real_item();
if (item->type() == Item::FIELD_ITEM)
hybrid_field_type= ((Item_field*) item)->field->type();
else
hybrid_field_type= Item::field_type();
if (check_sum_func(thd, ref))
return TRUE;
orig_args[0]= args[0];
fixed= 1;
return FALSE;
2000-07-31 21:29:14 +02:00
}
/**
MIN/MAX function setup.
@param item argument of MIN/MAX function
@param value_arg calculated value of MIN/MAX function
@details
Setup cache/comparator of MIN/MAX functions. When called by the
copy_or_same function value_arg parameter contains calculated value
of the original MIN/MAX object and it is saved in this object's cache.
*/
void Item_sum_hybrid::setup_hybrid(Item *item, Item *value_arg)
{
value= Item_cache::get_cache(item);
value->setup(item);
Bug#49771: Incorrect MIN/MAX for date/time values. This bug is a design flaw of the fix for the bug#33546. It assumed that an item can be used only in one comparison context, but actually it isn't the case. Item_cache_datetime is used to store result for MIX/MAX aggregate functions. Because Arg_comparator always compares datetime values as INTs when possible the Item_cache_datetime most time caches only INT value. But since all datetime values has STRING result type MIN/MAX functions are asked for a STRING value when the result is being sent to a client. The Item_cache_datetime was designed to avoid conversions and get INT/STRING values from an underlying item, but at the moment the values is asked underlying item doesn't hold it anymore thus wrong result is returned. Beside that MIN/MAX aggregate functions was wrongly initializing cached result and this led to a wrong result. The Item::has_compatible_context helper function is added. It checks whether this and given items has the same comparison context or can be compared as DATETIME values by Arg_comparator. The equality propagation optimization is adjusted to take into account that items which being compared as DATETIME can have different comparison contexts. The Item_cache_datetime now converts cached INT value to a correct STRING DATETIME value by means of number_to_datetime & my_TIME_to_str functions. The Arg_comparator::set_cmp_context_for_datetime helper function is added. It sets comparison context of items being compared as DATETIMEs to INT if items will be compared as longlong. The Item_sum_hybrid::setup function now correctly initializes its result value. In order to avoid unnecessary conversions Item_sum_hybrid now states that it can provide correct longlong value if the item being aggregated can do it too.
2010-07-19 19:11:47 +02:00
value->store(value_arg);
cmp= new Arg_comparator();
cmp->set_cmp_func(this, args, (Item**)&value, FALSE);
collation.set(item->collation);
}
2005-03-01 21:19:19 +01:00
Field *Item_sum_hybrid::create_tmp_field(bool group, TABLE *table,
uint convert_blob_length)
{
Field *field;
2005-03-01 21:19:19 +01:00
if (args[0]->type() == Item::FIELD_ITEM)
{
field= ((Item_field*) args[0])->field;
2005-03-01 21:19:19 +01:00
2005-03-30 15:57:56 +02:00
if ((field= create_tmp_field_from_field(current_thd, field, name, table,
NULL, convert_blob_length)))
2005-03-01 21:19:19 +01:00
field->flags&= ~NOT_NULL_FLAG;
return field;
}
/*
DATE/TIME fields have STRING_RESULT result types.
In order to preserve field type, it's needed to handle DATE/TIME
fields creations separately.
*/
switch (args[0]->field_type()) {
case MYSQL_TYPE_DATE:
field= new Field_newdate(maybe_null, name, collation.collation);
break;
case MYSQL_TYPE_TIME:
field= new Field_time(maybe_null, name, collation.collation);
break;
case MYSQL_TYPE_TIMESTAMP:
case MYSQL_TYPE_DATETIME:
field= new Field_datetime(maybe_null, name, collation.collation);
break;
default:
return Item_sum::create_tmp_field(group, table, convert_blob_length);
}
if (field)
field->init(table);
return field;
2005-03-01 21:19:19 +01:00
}
2000-07-31 21:29:14 +02:00
/***********************************************************************
** reset and add of sum_func
***********************************************************************/
2007-10-11 19:29:09 +02:00
/**
@todo
check if the following assignments are really needed
*/
2005-02-08 23:50:45 +01:00
Item_sum_sum::Item_sum_sum(THD *thd, Item_sum_sum *item)
:Item_sum_num(thd, item), hybrid_type(item->hybrid_type),
curr_dec_buff(item->curr_dec_buff)
{
/* TODO: check if the following assignments are really needed */
2005-02-08 23:50:45 +01:00
if (hybrid_type == DECIMAL_RESULT)
{
my_decimal2decimal(item->dec_buffs, dec_buffs);
my_decimal2decimal(item->dec_buffs + 1, dec_buffs + 1);
}
else
sum= item->sum;
}
Item *Item_sum_sum::copy_or_same(THD* thd)
2000-07-31 21:29:14 +02:00
{
return new (thd->mem_root) Item_sum_sum(thd, this);
2000-07-31 21:29:14 +02:00
}
void Item_sum_sum::clear()
{
2005-02-08 23:50:45 +01:00
DBUG_ENTER("Item_sum_sum::clear");
null_value=1;
if (hybrid_type == DECIMAL_RESULT)
{
curr_dec_buff= 0;
my_decimal_set_zero(dec_buffs);
}
else
sum= 0.0;
DBUG_VOID_RETURN;
}
void Item_sum_sum::fix_length_and_dec()
{
DBUG_ENTER("Item_sum_sum::fix_length_and_dec");
maybe_null=null_value=1;
decimals= args[0]->decimals;
switch (args[0]->result_type()) {
2005-02-08 23:50:45 +01:00
case REAL_RESULT:
case STRING_RESULT:
hybrid_type= REAL_RESULT;
sum= 0.0;
break;
case INT_RESULT:
case DECIMAL_RESULT:
{
2005-02-08 23:50:45 +01:00
/* SUM result can't be longer than length(arg) + length(MAX_ROWS) */
int precision= args[0]->decimal_precision() + DECIMAL_LONGLONG_DIGITS;
max_length= my_decimal_precision_to_length_no_truncation(precision,
decimals,
unsigned_flag);
2005-02-08 23:50:45 +01:00
curr_dec_buff= 0;
hybrid_type= DECIMAL_RESULT;
my_decimal_set_zero(dec_buffs);
break;
}
2005-02-08 23:50:45 +01:00
case ROW_RESULT:
default:
DBUG_ASSERT(0);
}
DBUG_PRINT("info", ("Type: %s (%d, %d)",
(hybrid_type == REAL_RESULT ? "REAL_RESULT" :
hybrid_type == DECIMAL_RESULT ? "DECIMAL_RESULT" :
hybrid_type == INT_RESULT ? "INT_RESULT" :
"--ILLEGAL!!!--"),
max_length,
(int)decimals));
DBUG_VOID_RETURN;
}
2000-07-31 21:29:14 +02:00
bool Item_sum_sum::add()
{
2005-02-08 23:50:45 +01:00
DBUG_ENTER("Item_sum_sum::add");
2009-09-28 09:21:25 +02:00
bool arg_is_null;
2005-02-08 23:50:45 +01:00
if (hybrid_type == DECIMAL_RESULT)
{
2009-09-28 09:21:25 +02:00
my_decimal value, *val;
if (aggr->use_distinct_values)
2005-02-08 23:50:45 +01:00
{
2009-09-28 09:21:25 +02:00
/*
We are aggregating distinct rows. Get the value from the distinct
table pointer
*/
Aggregator_distinct *daggr= (Aggregator_distinct *)aggr;
val= daggr->table->field[0]->val_decimal (&value);
arg_is_null= daggr->table->field[0]->is_null();
2005-02-08 23:50:45 +01:00
}
2009-09-28 09:21:25 +02:00
else
{
/* non-distinct aggregation */
val= args[0]->val_decimal(&value);
arg_is_null= args[0]->null_value;
}
2009-09-28 09:21:25 +02:00
if (!arg_is_null)
{
my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs + (curr_dec_buff^1),
val, dec_buffs + curr_dec_buff);
curr_dec_buff^= 1;
null_value= 0;
}
}
else
2005-02-08 23:50:45 +01:00
{
2009-09-28 09:21:25 +02:00
double val;
if (aggr->use_distinct_values)
{
2009-09-28 09:21:25 +02:00
/*
We are aggregating distinct rows. Get the value from the distinct
table pointer
*/
Aggregator_distinct *daggr= (Aggregator_distinct *)aggr;
val= daggr->table->field[0]->val_real ();
arg_is_null= daggr->table->field[0]->is_null();
}
2009-09-28 09:21:25 +02:00
else
{
/* non-distinct aggregation */
val= args[0]->val_real();
arg_is_null= args[0]->null_value;
}
sum+= val;
if (!arg_is_null)
null_value= 0;
2005-02-08 23:50:45 +01:00
}
2009-09-28 09:21:25 +02:00
DBUG_RETURN(0);
}
2005-02-08 23:50:45 +01:00
2009-09-28 09:21:25 +02:00
longlong Item_sum_sum::val_int()
2005-02-08 23:50:45 +01:00
{
2009-09-28 09:21:25 +02:00
DBUG_ASSERT(fixed == 1);
if (aggr)
aggr->endup();
if (hybrid_type == DECIMAL_RESULT)
{
longlong result;
my_decimal2int(E_DEC_FATAL_ERROR, dec_buffs + curr_dec_buff, unsigned_flag,
&result);
return result;
}
return (longlong) rint(val_real());
2005-02-08 23:50:45 +01:00
}
2009-09-28 09:21:25 +02:00
double Item_sum_sum::val_real()
2005-02-08 23:50:45 +01:00
{
2009-09-28 09:21:25 +02:00
DBUG_ASSERT(fixed == 1);
if (aggr)
aggr->endup();
if (hybrid_type == DECIMAL_RESULT)
my_decimal2double(E_DEC_FATAL_ERROR, dec_buffs + curr_dec_buff, &sum);
return sum;
2005-02-08 23:50:45 +01:00
}
2009-09-28 09:21:25 +02:00
String *Item_sum_sum::val_str(String *str)
2005-02-08 23:50:45 +01:00
{
2009-09-28 09:21:25 +02:00
if (aggr)
aggr->endup();
if (hybrid_type == DECIMAL_RESULT)
return val_string_from_decimal(str);
return val_string_from_real(str);
}
2009-09-28 09:21:25 +02:00
my_decimal *Item_sum_sum::val_decimal(my_decimal *val)
{
2009-09-28 09:21:25 +02:00
if (aggr)
aggr->endup();
if (hybrid_type == DECIMAL_RESULT)
return (dec_buffs + curr_dec_buff);
return val_decimal_from_real(val);
2005-02-08 23:50:45 +01:00
}
2009-09-28 09:21:25 +02:00
/**
Aggregate a distinct row from the distinct hash table.
Called for each row into the hash table 'Aggregator_distinct::table'.
Includes the current distinct row into the calculation of the
aggregate value. Uses the Field classes to get the value from the row.
This function is used for AVG/SUM(DISTINCT). For COUNT(DISTINCT)
it's called only when there are no blob arguments and the data don't
fit into memory (so Unique makes persisted trees on disk).
@param element pointer to the row data.
@return status
@retval FALSE success
@retval TRUE failure
*/
bool Aggregator_distinct::unique_walk_function(void *element)
{
2009-09-28 09:21:25 +02:00
memcpy(table->field[0]->ptr, element, tree_key_length);
item_sum->add();
return 0;
}
2009-09-28 09:21:25 +02:00
Aggregator_distinct::~Aggregator_distinct()
{
2009-09-28 09:21:25 +02:00
if (tree)
{
delete tree;
tree= NULL;
}
if (table)
{
free_tmp_table(table->in_use, table);
table=NULL;
}
if (tmp_table_param)
{
2009-09-28 09:21:25 +02:00
delete tmp_table_param;
tmp_table_param= NULL;
}
}
2005-02-08 23:50:45 +01:00
Item *Item_sum_count::copy_or_same(THD* thd)
2000-07-31 21:29:14 +02:00
{
return new (thd->mem_root) Item_sum_count(thd, this);
2000-07-31 21:29:14 +02:00
}
void Item_sum_count::clear()
{
count= 0;
}
2000-07-31 21:29:14 +02:00
bool Item_sum_count::add()
{
if (!args[0]->maybe_null || !args[0]->is_null())
2000-07-31 21:29:14 +02:00
count++;
return 0;
}
longlong Item_sum_count::val_int()
{
DBUG_ASSERT(fixed == 1);
2009-09-28 09:21:25 +02:00
if (aggr)
aggr->endup();
2000-07-31 21:29:14 +02:00
return (longlong) count;
}
void Item_sum_count::cleanup()
{
DBUG_ENTER("Item_sum_count::cleanup");
count= 0;
Item_sum_int::cleanup();
DBUG_VOID_RETURN;
}
2000-07-31 21:29:14 +02:00
/*
Avgerage
2000-07-31 21:29:14 +02:00
*/
2005-02-08 23:50:45 +01:00
void Item_sum_avg::fix_length_and_dec()
{
Item_sum_sum::fix_length_and_dec();
maybe_null=null_value=1;
prec_increment= current_thd->variables.div_precincrement;
2005-02-08 23:50:45 +01:00
if (hybrid_type == DECIMAL_RESULT)
{
int precision= args[0]->decimal_precision() + prec_increment;
decimals= min(args[0]->decimals + prec_increment, DECIMAL_MAX_SCALE);
max_length= my_decimal_precision_to_length_no_truncation(precision,
decimals,
unsigned_flag);
f_precision= min(precision+DECIMAL_LONGLONG_DIGITS, DECIMAL_MAX_PRECISION);
f_scale= args[0]->decimals;
2005-02-08 23:50:45 +01:00
dec_bin_size= my_decimal_get_binary_size(f_precision, f_scale);
}
Bug #24791: Union with AVG-groups generates wrong results The problem in this bug is when we create temporary tables. When temporary tables are created for unions, there is some inferrence being carried out regarding the type of the column. Whenever this column type is inferred to be REAL (i.e. FLOAT or DOUBLE), MySQL will always try to maintain exact precision, and if that is not possible (there are hardware limits, since FLOAT and DOUBLE are stored as approximate values) will switch to using approximate values. The problem here is that at this point the information about number of significant digits is not available. Furthermore, the number of significant digits should be increased for the AVG function, however, this was not properly handled. There are 4 parts to the problem: #1: DOUBLE and FLOAT fields don't display their proper display lengths in max_display_length(). This is hard-coded as 53 for DOUBLE and 24 for FLOAT. Now changed to instead return the field_length. #2: Type holders for temporary tables do not preserve the max_length of the Item's from which they are created, and is instead reverted to the 53 and 24 from above. This causes *all* fields to get non-fixed significant digits. #3: AVG function does not update max_length (display length) when updating number of decimals. #4: The function that switches to non-fixed number of significant digits should use DBL_DIG + 2 or FLT_DIG + 2 as cut-off values (Since fixed precision does not use the 'e' notation) Of these points, #1 is the controversial one, but this change is preferred and has been cleared with Monty. The function causes quite a few unit tests to blow up and they had to b changed, but each one is annotated and motivated. We frequently see the magical 53 and 24 give way to more relevant numbers.
2007-03-22 10:56:47 +01:00
else {
decimals= min(args[0]->decimals + prec_increment, NOT_FIXED_DEC);
Bug #24791: Union with AVG-groups generates wrong results The problem in this bug is when we create temporary tables. When temporary tables are created for unions, there is some inferrence being carried out regarding the type of the column. Whenever this column type is inferred to be REAL (i.e. FLOAT or DOUBLE), MySQL will always try to maintain exact precision, and if that is not possible (there are hardware limits, since FLOAT and DOUBLE are stored as approximate values) will switch to using approximate values. The problem here is that at this point the information about number of significant digits is not available. Furthermore, the number of significant digits should be increased for the AVG function, however, this was not properly handled. There are 4 parts to the problem: #1: DOUBLE and FLOAT fields don't display their proper display lengths in max_display_length(). This is hard-coded as 53 for DOUBLE and 24 for FLOAT. Now changed to instead return the field_length. #2: Type holders for temporary tables do not preserve the max_length of the Item's from which they are created, and is instead reverted to the 53 and 24 from above. This causes *all* fields to get non-fixed significant digits. #3: AVG function does not update max_length (display length) when updating number of decimals. #4: The function that switches to non-fixed number of significant digits should use DBL_DIG + 2 or FLT_DIG + 2 as cut-off values (Since fixed precision does not use the 'e' notation) Of these points, #1 is the controversial one, but this change is preferred and has been cleared with Monty. The function causes quite a few unit tests to blow up and they had to b changed, but each one is annotated and motivated. We frequently see the magical 53 and 24 give way to more relevant numbers.
2007-03-22 10:56:47 +01:00
max_length= args[0]->max_length + prec_increment;
}
2005-02-08 23:50:45 +01:00
}
2000-07-31 21:29:14 +02:00
Item *Item_sum_avg::copy_or_same(THD* thd)
2000-07-31 21:29:14 +02:00
{
return new (thd->mem_root) Item_sum_avg(thd, this);
2000-07-31 21:29:14 +02:00
}
2005-02-08 23:50:45 +01:00
Field *Item_sum_avg::create_tmp_field(bool group, TABLE *table,
uint convert_blob_len)
{
Field *field;
if (group)
2005-02-08 23:50:45 +01:00
{
/*
We must store both value and counter in the temporary table in one field.
The easiest way is to do this is to store both value in a string
and unpack on access.
*/
field= new Field_string(((hybrid_type == DECIMAL_RESULT) ?
dec_bin_size : sizeof(double)) + sizeof(longlong),
0, name, &my_charset_bin);
2005-02-08 23:50:45 +01:00
}
else if (hybrid_type == DECIMAL_RESULT)
field= Field_new_decimal::create_from_item(this);
else
field= new Field_double(max_length, maybe_null, name, decimals, TRUE);
if (field)
field->init(table);
return field;
2005-02-08 23:50:45 +01:00
}
void Item_sum_avg::clear()
{
2005-02-08 23:50:45 +01:00
Item_sum_sum::clear();
count=0;
}
2000-07-31 21:29:14 +02:00
bool Item_sum_avg::add()
{
2005-02-08 23:50:45 +01:00
if (Item_sum_sum::add())
return TRUE;
2000-07-31 21:29:14 +02:00
if (!args[0]->null_value)
count++;
2005-02-08 23:50:45 +01:00
return FALSE;
2000-07-31 21:29:14 +02:00
}
2004-11-11 19:39:35 +01:00
double Item_sum_avg::val_real()
2000-07-31 21:29:14 +02:00
{
DBUG_ASSERT(fixed == 1);
2009-09-28 09:21:25 +02:00
if (aggr)
aggr->endup();
2000-07-31 21:29:14 +02:00
if (!count)
{
null_value=1;
return 0.0;
}
2005-02-08 23:50:45 +01:00
return Item_sum_sum::val_real() / ulonglong2double(count);
}
my_decimal *Item_sum_avg::val_decimal(my_decimal *val)
{
my_decimal sum_buff, cnt;
const my_decimal *sum_dec;
2005-02-08 23:50:45 +01:00
DBUG_ASSERT(fixed == 1);
2009-09-28 09:21:25 +02:00
if (aggr)
aggr->endup();
2005-02-08 23:50:45 +01:00
if (!count)
{
null_value=1;
return NULL;
}
/*
For non-DECIMAL hybrid_type the division will be done in
Item_sum_avg::val_real().
*/
if (hybrid_type != DECIMAL_RESULT)
return val_decimal_from_real(val);
sum_dec= dec_buffs + curr_dec_buff;
2005-02-08 23:50:45 +01:00
int2my_decimal(E_DEC_FATAL_ERROR, count, 0, &cnt);
my_decimal_div(E_DEC_FATAL_ERROR, val, sum_dec, &cnt, prec_increment);
2005-02-08 23:50:45 +01:00
return val;
}
String *Item_sum_avg::val_str(String *str)
{
2009-09-28 09:21:25 +02:00
if (aggr)
aggr->endup();
2005-02-08 23:50:45 +01:00
if (hybrid_type == DECIMAL_RESULT)
return val_string_from_decimal(str);
return val_string_from_real(str);
2000-07-31 21:29:14 +02:00
}
/*
Standard deviation
2000-07-31 21:29:14 +02:00
*/
2004-11-11 19:39:35 +01:00
double Item_sum_std::val_real()
2000-07-31 21:29:14 +02:00
{
DBUG_ASSERT(fixed == 1);
double nr= Item_sum_variance::val_real();
DBUG_ASSERT(nr >= 0.0);
return sqrt(nr);
2000-07-31 21:29:14 +02:00
}
Item *Item_sum_std::copy_or_same(THD* thd)
{
return new (thd->mem_root) Item_sum_std(thd, this);
}
2002-12-14 00:36:59 +01:00
/*
Variance
2002-12-14 00:36:59 +01:00
*/
2005-02-08 23:50:45 +01:00
/**
Variance implementation for floating-point implementations, without
catastrophic cancellation, from Knuth's _TAoCP_, 3rd ed, volume 2, pg232.
This alters the value at m, s, and increments count.
*/
/*
These two functions are used by the Item_sum_variance and the
Item_variance_field classes, which are unrelated, and each need to calculate
variance. The difference between the two classes is that the first is used
for a mundane SELECT, while the latter is used in a GROUPing SELECT.
*/
static void variance_fp_recurrence_next(double *m, double *s, ulonglong *count, double nr)
2005-02-08 23:50:45 +01:00
{
*count += 1;
if (*count == 1)
2005-02-08 23:50:45 +01:00
{
*m= nr;
*s= 0;
2005-02-08 23:50:45 +01:00
}
else
{
double m_kminusone= *m;
*m= m_kminusone + (nr - m_kminusone) / (double) *count;
*s= *s + (nr - m_kminusone) * (nr - *m);
2005-02-08 23:50:45 +01:00
}
}
static double variance_fp_recurrence_result(double s, ulonglong count, bool is_sample_variance)
{
if (count == 1)
return 0.0;
if (is_sample_variance)
return s / (count - 1);
/* else, is a population variance */
return s / count;
}
Item_sum_variance::Item_sum_variance(THD *thd, Item_sum_variance *item):
Item_sum_num(thd, item), hybrid_type(item->hybrid_type),
count(item->count), sample(item->sample),
prec_increment(item->prec_increment)
{
recurrence_m= item->recurrence_m;
recurrence_s= item->recurrence_s;
}
2005-02-08 23:50:45 +01:00
void Item_sum_variance::fix_length_and_dec()
{
DBUG_ENTER("Item_sum_variance::fix_length_and_dec");
maybe_null= null_value= 1;
prec_increment= current_thd->variables.div_precincrement;
/*
According to the SQL2003 standard (Part 2, Foundations; sec 10.9,
aggregate function; paragraph 7h of Syntax Rules), "the declared
type of the result is an implementation-defined aproximate numeric
type.
*/
hybrid_type= REAL_RESULT;
switch (args[0]->result_type()) {
2005-02-08 23:50:45 +01:00
case REAL_RESULT:
case STRING_RESULT:
decimals= min(args[0]->decimals + 4, NOT_FIXED_DEC);
2005-02-08 23:50:45 +01:00
break;
case INT_RESULT:
case DECIMAL_RESULT:
{
int precision= args[0]->decimal_precision()*2 + prec_increment;
decimals= min(args[0]->decimals + prec_increment, DECIMAL_MAX_SCALE);
max_length= my_decimal_precision_to_length_no_truncation(precision,
decimals,
unsigned_flag);
2005-02-08 23:50:45 +01:00
break;
}
2005-02-08 23:50:45 +01:00
case ROW_RESULT:
default:
DBUG_ASSERT(0);
}
DBUG_PRINT("info", ("Type: REAL_RESULT (%d, %d)", max_length, (int)decimals));
2005-02-08 23:50:45 +01:00
DBUG_VOID_RETURN;
}
Item *Item_sum_variance::copy_or_same(THD* thd)
{
return new (thd->mem_root) Item_sum_variance(thd, this);
}
/**
Create a new field to match the type of value we're expected to yield.
If we're grouping, then we need some space to serialize variables into, to
pass around.
*/
2005-02-08 23:50:45 +01:00
Field *Item_sum_variance::create_tmp_field(bool group, TABLE *table,
uint convert_blob_len)
{
Field *field;
if (group)
2005-02-08 23:50:45 +01:00
{
/*
We must store both value and counter in the temporary table in one field.
The easiest way is to do this is to store both value in a string
and unpack on access.
*/
field= new Field_string(sizeof(double)*2 + sizeof(longlong), 0, name, &my_charset_bin);
2005-02-08 23:50:45 +01:00
}
else
field= new Field_double(max_length, maybe_null, name, decimals, TRUE);
if (field != NULL)
field->init(table);
return field;
2005-02-08 23:50:45 +01:00
}
void Item_sum_variance::clear()
2002-12-14 00:36:59 +01:00
{
count= 0;
2002-12-14 00:36:59 +01:00
}
bool Item_sum_variance::add()
2000-07-31 21:29:14 +02:00
{
/*
Why use a temporary variable? We don't know if it is null until we
evaluate it, which has the side-effect of setting null_value .
*/
double nr= args[0]->val_real();
if (!args[0]->null_value)
variance_fp_recurrence_next(&recurrence_m, &recurrence_s, &count, nr);
2000-07-31 21:29:14 +02:00
return 0;
}
2004-11-11 19:39:35 +01:00
double Item_sum_variance::val_real()
2000-07-31 21:29:14 +02:00
{
DBUG_ASSERT(fixed == 1);
/*
'sample' is a 1/0 boolean value. If it is 1/true, id est this is a sample
variance call, then we should set nullness when the count of the items
is one or zero. If it's zero, i.e. a population variance, then we only
set nullness when the count is zero.
Another way to read it is that 'sample' is the numerical threshhold, at and
below which a 'count' number of items is called NULL.
*/
DBUG_ASSERT((sample == 0) || (sample == 1));
if (count <= sample)
2000-07-31 21:29:14 +02:00
{
null_value=1;
return 0.0;
}
2005-02-08 23:50:45 +01:00
null_value=0;
return variance_fp_recurrence_result(recurrence_s, count, sample);
2005-02-08 23:50:45 +01:00
}
my_decimal *Item_sum_variance::val_decimal(my_decimal *dec_buf)
{
DBUG_ASSERT(fixed == 1);
return val_decimal_from_real(dec_buf);
2005-02-08 23:50:45 +01:00
}
2005-02-08 23:50:45 +01:00
void Item_sum_variance::reset_field()
{
double nr;
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
2007-05-10 11:59:39 +02:00
uchar *res= result_field->ptr;
nr= args[0]->val_real(); /* sets null_value as side-effect */
2000-07-31 21:29:14 +02:00
if (args[0]->null_value)
bzero(res,sizeof(double)*2+sizeof(longlong));
else
{
/* Serialize format is (double)m, (double)s, (longlong)count */
ulonglong tmp_count;
double tmp_s;
float8store(res, nr); /* recurrence variable m */
tmp_s= 0.0;
float8store(res + sizeof(double), tmp_s);
tmp_count= 1;
int8store(res + sizeof(double)*2, tmp_count);
2000-07-31 21:29:14 +02:00
}
}
2003-08-29 12:44:35 +02:00
void Item_sum_variance::update_field()
2000-07-31 21:29:14 +02:00
{
ulonglong field_count;
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
2007-05-10 11:59:39 +02:00
uchar *res=result_field->ptr;
double nr= args[0]->val_real(); /* sets null_value as side-effect */
if (args[0]->null_value)
2005-02-08 23:50:45 +01:00
return;
2000-07-31 21:29:14 +02:00
/* Serialize format is (double)m, (double)s, (longlong)count */
double field_recurrence_m, field_recurrence_s;
float8get(field_recurrence_m, res);
float8get(field_recurrence_s, res + sizeof(double));
field_count=sint8korr(res+sizeof(double)*2);
2000-07-31 21:29:14 +02:00
variance_fp_recurrence_next(&field_recurrence_m, &field_recurrence_s, &field_count, nr);
float8store(res, field_recurrence_m);
float8store(res + sizeof(double), field_recurrence_s);
res+= sizeof(double)*2;
int8store(res,field_count);
2000-07-31 21:29:14 +02:00
}
2000-07-31 21:29:14 +02:00
/* min & max */
void Item_sum_hybrid::clear()
{
Bug#49771: Incorrect MIN/MAX for date/time values. This bug is a design flaw of the fix for the bug#33546. It assumed that an item can be used only in one comparison context, but actually it isn't the case. Item_cache_datetime is used to store result for MIX/MAX aggregate functions. Because Arg_comparator always compares datetime values as INTs when possible the Item_cache_datetime most time caches only INT value. But since all datetime values has STRING result type MIN/MAX functions are asked for a STRING value when the result is being sent to a client. The Item_cache_datetime was designed to avoid conversions and get INT/STRING values from an underlying item, but at the moment the values is asked underlying item doesn't hold it anymore thus wrong result is returned. Beside that MIN/MAX aggregate functions was wrongly initializing cached result and this led to a wrong result. The Item::has_compatible_context helper function is added. It checks whether this and given items has the same comparison context or can be compared as DATETIME values by Arg_comparator. The equality propagation optimization is adjusted to take into account that items which being compared as DATETIME can have different comparison contexts. The Item_cache_datetime now converts cached INT value to a correct STRING DATETIME value by means of number_to_datetime & my_TIME_to_str functions. The Arg_comparator::set_cmp_context_for_datetime helper function is added. It sets comparison context of items being compared as DATETIMEs to INT if items will be compared as longlong. The Item_sum_hybrid::setup function now correctly initializes its result value. In order to avoid unnecessary conversions Item_sum_hybrid now states that it can provide correct longlong value if the item being aggregated can do it too.
2010-07-19 19:11:47 +02:00
value->clear();
null_value= 1;
}
2004-11-11 19:39:35 +01:00
double Item_sum_hybrid::val_real()
2000-07-31 21:29:14 +02:00
{
DBUG_ASSERT(fixed == 1);
2000-07-31 21:29:14 +02:00
if (null_value)
return 0.0;
return value->val_real();
}
longlong Item_sum_hybrid::val_int()
{
DBUG_ASSERT(fixed == 1);
if (null_value)
return 0;
return value->val_int();
2005-02-08 23:50:45 +01:00
}
my_decimal *Item_sum_hybrid::val_decimal(my_decimal *val)
{
DBUG_ASSERT(fixed == 1);
if (null_value)
return 0;
return value->val_decimal(val);
2000-07-31 21:29:14 +02:00
}
String *
Item_sum_hybrid::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
2000-07-31 21:29:14 +02:00
if (null_value)
return 0;
return value->val_str(str);
2000-07-31 21:29:14 +02:00
}
void Item_sum_hybrid::cleanup()
{
DBUG_ENTER("Item_sum_hybrid::cleanup");
Item_sum::cleanup();
forced_const= FALSE;
if (cmp)
delete cmp;
cmp= 0;
/*
2004-12-07 20:18:15 +01:00
by default it is TRUE to avoid TRUE reporting by
Item_func_not_all/Item_func_nop_all if this item was never called.
no_rows_in_result() set it to FALSE if was not results found.
2004-12-07 20:18:15 +01:00
If some results found it will be left unchanged.
*/
was_values= TRUE;
DBUG_VOID_RETURN;
}
void Item_sum_hybrid::no_rows_in_result()
{
was_values= FALSE;
clear();
}
Item *Item_sum_min::copy_or_same(THD* thd)
{
Item_sum_min *item= new (thd->mem_root) Item_sum_min(thd, this);
item->setup_hybrid(args[0], value);
return item;
}
2000-07-31 21:29:14 +02:00
bool Item_sum_min::add()
{
/* args[0] < value */
int res= cmp->compare();
if (!args[0]->null_value &&
(null_value || res < 0))
2000-07-31 21:29:14 +02:00
{
value->store(args[0]);
2009-11-24 16:26:13 +01:00
value->cache_value();
null_value= 0;
}
return 0;
}
Item *Item_sum_max::copy_or_same(THD* thd)
{
Item_sum_max *item= new (thd->mem_root) Item_sum_max(thd, this);
item->setup_hybrid(args[0], value);
return item;
}
bool Item_sum_max::add()
{
/* args[0] > value */
int res= cmp->compare();
if (!args[0]->null_value &&
(null_value || res > 0))
2000-07-31 21:29:14 +02:00
{
value->store(args[0]);
2009-11-24 16:26:13 +01:00
value->cache_value();
null_value= 0;
}
2000-07-31 21:29:14 +02:00
return 0;
}
/* bit_or and bit_and */
longlong Item_sum_bit::val_int()
{
DBUG_ASSERT(fixed == 1);
2000-07-31 21:29:14 +02:00
return (longlong) bits;
}
void Item_sum_bit::clear()
{
bits= reset_bits;
}
Item *Item_sum_or::copy_or_same(THD* thd)
2000-07-31 21:29:14 +02:00
{
return new (thd->mem_root) Item_sum_or(thd, this);
2000-07-31 21:29:14 +02:00
}
2000-07-31 21:29:14 +02:00
bool Item_sum_or::add()
{
ulonglong value= (ulonglong) args[0]->val_int();
if (!args[0]->null_value)
bits|=value;
return 0;
}
Item *Item_sum_xor::copy_or_same(THD* thd)
{
return new (thd->mem_root) Item_sum_xor(thd, this);
}
bool Item_sum_xor::add()
{
ulonglong value= (ulonglong) args[0]->val_int();
if (!args[0]->null_value)
bits^=value;
return 0;
}
Item *Item_sum_and::copy_or_same(THD* thd)
{
return new (thd->mem_root) Item_sum_and(thd, this);
}
2000-07-31 21:29:14 +02:00
bool Item_sum_and::add()
{
ulonglong value= (ulonglong) args[0]->val_int();
if (!args[0]->null_value)
bits&=value;
return 0;
}
/************************************************************************
** reset result of a Item_sum with is saved in a tmp_table
*************************************************************************/
void Item_sum_num::reset_field()
{
2004-11-11 19:39:35 +01:00
double nr= args[0]->val_real();
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
2007-05-10 11:59:39 +02:00
uchar *res=result_field->ptr;
2000-07-31 21:29:14 +02:00
if (maybe_null)
{
if (args[0]->null_value)
{
nr=0.0;
result_field->set_null();
}
else
result_field->set_notnull();
}
float8store(res,nr);
}
void Item_sum_hybrid::reset_field()
{
switch(hybrid_type) {
2005-02-08 23:50:45 +01:00
case STRING_RESULT:
2000-07-31 21:29:14 +02:00
{
char buff[MAX_FIELD_WIDTH];
String tmp(buff,sizeof(buff),result_field->charset()),*res;
2000-07-31 21:29:14 +02:00
res=args[0]->val_str(&tmp);
if (args[0]->null_value)
{
result_field->set_null();
result_field->reset();
}
else
{
result_field->set_notnull();
result_field->store(res->ptr(),res->length(),tmp.charset());
2000-07-31 21:29:14 +02:00
}
2005-02-08 23:50:45 +01:00
break;
2000-07-31 21:29:14 +02:00
}
2005-02-08 23:50:45 +01:00
case INT_RESULT:
2000-07-31 21:29:14 +02:00
{
longlong nr=args[0]->val_int();
if (maybe_null)
{
if (args[0]->null_value)
{
nr=0;
result_field->set_null();
}
else
result_field->set_notnull();
}
result_field->store(nr, unsigned_flag);
2005-02-08 23:50:45 +01:00
break;
2000-07-31 21:29:14 +02:00
}
2005-02-08 23:50:45 +01:00
case REAL_RESULT:
2000-07-31 21:29:14 +02:00
{
2004-11-11 19:39:35 +01:00
double nr= args[0]->val_real();
2000-07-31 21:29:14 +02:00
if (maybe_null)
{
if (args[0]->null_value)
{
nr=0.0;
result_field->set_null();
}
else
result_field->set_notnull();
}
result_field->store(nr);
2005-02-08 23:50:45 +01:00
break;
}
case DECIMAL_RESULT:
{
my_decimal value_buff, *arg_dec= args[0]->val_decimal(&value_buff);
2005-02-08 23:50:45 +01:00
if (maybe_null)
{
if (args[0]->null_value)
result_field->set_null();
else
result_field->set_notnull();
}
/*
We must store zero in the field as we will use the field value in
add()
*/
if (!arg_dec) // Null
arg_dec= &decimal_zero;
result_field->store_decimal(arg_dec);
2005-02-08 23:50:45 +01:00
break;
}
case ROW_RESULT:
default:
DBUG_ASSERT(0);
2000-07-31 21:29:14 +02:00
}
}
void Item_sum_sum::reset_field()
{
2009-09-28 09:21:25 +02:00
DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR);
2005-02-08 23:50:45 +01:00
if (hybrid_type == DECIMAL_RESULT)
{
my_decimal value, *arg_val= args[0]->val_decimal(&value);
if (!arg_val) // Null
arg_val= &decimal_zero;
result_field->store_decimal(arg_val);
2005-02-08 23:50:45 +01:00
}
else
{
DBUG_ASSERT(hybrid_type == REAL_RESULT);
double nr= args[0]->val_real(); // Nulls also return 0
float8store(result_field->ptr, nr);
}
if (args[0]->null_value)
result_field->set_null();
else
result_field->set_notnull();
2000-07-31 21:29:14 +02:00
}
void Item_sum_count::reset_field()
{
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
2007-05-10 11:59:39 +02:00
uchar *res=result_field->ptr;
2000-07-31 21:29:14 +02:00
longlong nr=0;
2009-09-28 09:21:25 +02:00
DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR);
2000-07-31 21:29:14 +02:00
if (!args[0]->maybe_null || !args[0]->is_null())
2000-07-31 21:29:14 +02:00
nr=1;
int8store(res,nr);
}
void Item_sum_avg::reset_field()
{
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
2007-05-10 11:59:39 +02:00
uchar *res=result_field->ptr;
2009-09-28 09:21:25 +02:00
DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR);
2005-02-08 23:50:45 +01:00
if (hybrid_type == DECIMAL_RESULT)
{
longlong tmp;
2005-02-08 23:50:45 +01:00
my_decimal value, *arg_dec= args[0]->val_decimal(&value);
if (args[0]->null_value)
{
arg_dec= &decimal_zero;
tmp= 0;
2005-02-08 23:50:45 +01:00
}
else
tmp= 1;
my_decimal2binary(E_DEC_FATAL_ERROR, arg_dec, res, f_precision, f_scale);
res+= dec_bin_size;
int8store(res, tmp);
2005-02-08 23:50:45 +01:00
}
2000-07-31 21:29:14 +02:00
else
{
2005-02-08 23:50:45 +01:00
double nr= args[0]->val_real();
if (args[0]->null_value)
bzero(res,sizeof(double)+sizeof(longlong));
else
{
longlong tmp= 1;
2005-02-08 23:50:45 +01:00
float8store(res,nr);
res+=sizeof(double);
int8store(res,tmp);
}
2000-07-31 21:29:14 +02:00
}
}
2000-07-31 21:29:14 +02:00
void Item_sum_bit::reset_field()
{
reset();
int8store(result_field->ptr, bits);
}
void Item_sum_bit::update_field()
2000-07-31 21:29:14 +02:00
{
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
2007-05-10 11:59:39 +02:00
uchar *res=result_field->ptr;
bits= uint8korr(res);
add();
int8store(res, bits);
2000-07-31 21:29:14 +02:00
}
2007-10-11 19:29:09 +02:00
/**
calc next value and merge it with field_value.
2000-07-31 21:29:14 +02:00
*/
void Item_sum_sum::update_field()
2000-07-31 21:29:14 +02:00
{
2009-09-28 09:21:25 +02:00
DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR);
2005-02-08 23:50:45 +01:00
if (hybrid_type == DECIMAL_RESULT)
{
2005-02-08 23:50:45 +01:00
my_decimal value, *arg_val= args[0]->val_decimal(&value);
if (!args[0]->null_value)
{
if (!result_field->is_null())
{
my_decimal field_value,
*field_val= result_field->val_decimal(&field_value);
my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs, arg_val, field_val);
result_field->store_decimal(dec_buffs);
}
else
{
result_field->store_decimal(arg_val);
result_field->set_notnull();
}
}
}
else
{
double old_nr,nr;
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
2007-05-10 11:59:39 +02:00
uchar *res=result_field->ptr;
2005-02-08 23:50:45 +01:00
float8get(old_nr,res);
nr= args[0]->val_real();
if (!args[0]->null_value)
{
old_nr+=nr;
result_field->set_notnull();
}
float8store(res,old_nr);
}
2000-07-31 21:29:14 +02:00
}
void Item_sum_count::update_field()
2000-07-31 21:29:14 +02:00
{
longlong nr;
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
2007-05-10 11:59:39 +02:00
uchar *res=result_field->ptr;
2000-07-31 21:29:14 +02:00
nr=sint8korr(res);
if (!args[0]->maybe_null || !args[0]->is_null())
2000-07-31 21:29:14 +02:00
nr++;
int8store(res,nr);
}
void Item_sum_avg::update_field()
2000-07-31 21:29:14 +02:00
{
longlong field_count;
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
2007-05-10 11:59:39 +02:00
uchar *res=result_field->ptr;
2009-09-28 09:21:25 +02:00
DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR);
2005-02-08 23:50:45 +01:00
if (hybrid_type == DECIMAL_RESULT)
{
my_decimal value, *arg_val= args[0]->val_decimal(&value);
if (!args[0]->null_value)
{
binary2my_decimal(E_DEC_FATAL_ERROR, res,
dec_buffs + 1, f_precision, f_scale);
field_count= sint8korr(res + dec_bin_size);
my_decimal_add(E_DEC_FATAL_ERROR, dec_buffs, arg_val, dec_buffs + 1);
my_decimal2binary(E_DEC_FATAL_ERROR, dec_buffs,
res, f_precision, f_scale);
res+= dec_bin_size;
field_count++;
2005-02-08 23:50:45 +01:00
int8store(res, field_count);
}
}
else
{
double nr;
2000-07-31 21:29:14 +02:00
2005-02-08 23:50:45 +01:00
nr= args[0]->val_real();
if (!args[0]->null_value)
{
double old_nr;
float8get(old_nr, res);
field_count= sint8korr(res + sizeof(double));
2005-02-08 23:50:45 +01:00
old_nr+= nr;
float8store(res,old_nr);
res+= sizeof(double);
2005-02-08 23:50:45 +01:00
field_count++;
int8store(res, field_count);
2005-02-08 23:50:45 +01:00
}
2000-07-31 21:29:14 +02:00
}
}
void Item_sum_hybrid::update_field()
2000-07-31 21:29:14 +02:00
{
switch (hybrid_type) {
2005-02-08 23:50:45 +01:00
case STRING_RESULT:
min_max_update_str_field();
2005-02-08 23:50:45 +01:00
break;
case INT_RESULT:
min_max_update_int_field();
2005-02-08 23:50:45 +01:00
break;
case DECIMAL_RESULT:
min_max_update_decimal_field();
break;
default:
min_max_update_real_field();
2005-02-08 23:50:45 +01:00
}
2000-07-31 21:29:14 +02:00
}
void
Item_sum_hybrid::min_max_update_str_field()
2000-07-31 21:29:14 +02:00
{
DBUG_ASSERT(cmp);
String *res_str=args[0]->val_str(&cmp->value1);
2000-07-31 21:29:14 +02:00
if (!args[0]->null_value)
2000-07-31 21:29:14 +02:00
{
result_field->val_str(&cmp->value2);
2000-07-31 21:29:14 +02:00
if (result_field->is_null() ||
(cmp_sign * sortcmp(res_str,&cmp->value2,collation.collation)) < 0)
result_field->store(res_str->ptr(),res_str->length(),res_str->charset());
2000-07-31 21:29:14 +02:00
result_field->set_notnull();
}
}
void
Item_sum_hybrid::min_max_update_real_field()
2000-07-31 21:29:14 +02:00
{
double nr,old_nr;
old_nr=result_field->val_real();
2004-11-11 19:39:35 +01:00
nr= args[0]->val_real();
2000-07-31 21:29:14 +02:00
if (!args[0]->null_value)
{
if (result_field->is_null(0) ||
2000-07-31 21:29:14 +02:00
(cmp_sign > 0 ? old_nr > nr : old_nr < nr))
old_nr=nr;
result_field->set_notnull();
}
else if (result_field->is_null(0))
2000-07-31 21:29:14 +02:00
result_field->set_null();
result_field->store(old_nr);
}
void
Item_sum_hybrid::min_max_update_int_field()
2000-07-31 21:29:14 +02:00
{
longlong nr,old_nr;
old_nr=result_field->val_int();
nr=args[0]->val_int();
if (!args[0]->null_value)
{
if (result_field->is_null(0))
2000-07-31 21:29:14 +02:00
old_nr=nr;
else
{
bool res=(unsigned_flag ?
(ulonglong) old_nr > (ulonglong) nr :
old_nr > nr);
/* (cmp_sign > 0 && res) || (!(cmp_sign > 0) && !res) */
2001-10-03 16:02:14 +02:00
if ((cmp_sign > 0) ^ (!res))
old_nr=nr;
}
2000-07-31 21:29:14 +02:00
result_field->set_notnull();
}
else if (result_field->is_null(0))
2000-07-31 21:29:14 +02:00
result_field->set_null();
result_field->store(old_nr, unsigned_flag);
2000-07-31 21:29:14 +02:00
}
2007-10-11 19:29:09 +02:00
/**
@todo
optimize: do not get result_field in case of args[0] is NULL
*/
2005-02-08 23:50:45 +01:00
void
Item_sum_hybrid::min_max_update_decimal_field()
{
/* TODO: optimize: do not get result_field in case of args[0] is NULL */
my_decimal old_val, nr_val;
const my_decimal *old_nr= result_field->val_decimal(&old_val);
const my_decimal *nr= args[0]->val_decimal(&nr_val);
if (!args[0]->null_value)
{
if (result_field->is_null(0))
old_nr=nr;
else
{
bool res= my_decimal_cmp(old_nr, nr) > 0;
/* (cmp_sign > 0 && res) || (!(cmp_sign > 0) && !res) */
if ((cmp_sign > 0) ^ (!res))
old_nr=nr;
}
result_field->set_notnull();
}
else if (result_field->is_null(0))
result_field->set_null();
result_field->store_decimal(old_nr);
}
Item_avg_field::Item_avg_field(Item_result res_type, Item_sum_avg *item)
2000-07-31 21:29:14 +02:00
{
name=item->name;
decimals=item->decimals;
max_length= item->max_length;
unsigned_flag= item->unsigned_flag;
2000-07-31 21:29:14 +02:00
field=item->result_field;
maybe_null=1;
2005-02-08 23:50:45 +01:00
hybrid_type= res_type;
prec_increment= item->prec_increment;
2005-02-08 23:50:45 +01:00
if (hybrid_type == DECIMAL_RESULT)
{
f_scale= item->f_scale;
f_precision= item->f_precision;
dec_bin_size= item->dec_bin_size;
}
2000-07-31 21:29:14 +02:00
}
2004-11-11 19:39:35 +01:00
double Item_avg_field::val_real()
2000-07-31 21:29:14 +02:00
{
// fix_fields() never calls for this Item
double nr;
longlong count;
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
2007-05-10 11:59:39 +02:00
uchar *res;
2005-02-08 23:50:45 +01:00
if (hybrid_type == DECIMAL_RESULT)
return val_real_from_decimal();
2005-02-08 23:50:45 +01:00
float8get(nr,field->ptr);
res= (field->ptr+sizeof(double));
count= sint8korr(res);
if ((null_value= !count))
return 0.0;
return nr/(double) count;
2005-02-08 23:50:45 +01:00
}
2005-02-08 23:50:45 +01:00
longlong Item_avg_field::val_int()
{
return (longlong) rint(val_real());
2005-02-08 23:50:45 +01:00
}
my_decimal *Item_avg_field::val_decimal(my_decimal *dec_buf)
2005-02-08 23:50:45 +01:00
{
// fix_fields() never calls for this Item
if (hybrid_type == REAL_RESULT)
return val_decimal_from_real(dec_buf);
2005-02-08 23:50:45 +01:00
longlong count= sint8korr(field->ptr + dec_bin_size);
if ((null_value= !count))
return 0;
2005-02-08 23:50:45 +01:00
my_decimal dec_count, dec_field;
binary2my_decimal(E_DEC_FATAL_ERROR,
field->ptr, &dec_field, f_precision, f_scale);
int2my_decimal(E_DEC_FATAL_ERROR, count, 0, &dec_count);
my_decimal_div(E_DEC_FATAL_ERROR, dec_buf,
&dec_field, &dec_count, prec_increment);
return dec_buf;
2000-07-31 21:29:14 +02:00
}
2005-02-08 23:50:45 +01:00
2000-07-31 21:29:14 +02:00
String *Item_avg_field::val_str(String *str)
{
// fix_fields() never calls for this Item
2005-02-08 23:50:45 +01:00
if (hybrid_type == DECIMAL_RESULT)
return val_string_from_decimal(str);
return val_string_from_real(str);
2000-07-31 21:29:14 +02:00
}
2000-07-31 21:29:14 +02:00
Item_std_field::Item_std_field(Item_sum_std *item)
2002-12-14 00:36:59 +01:00
: Item_variance_field(item)
{
}
2004-11-11 19:39:35 +01:00
double Item_std_field::val_real()
2002-12-14 00:36:59 +01:00
{
double nr;
// fix_fields() never calls for this Item
nr= Item_variance_field::val_real();
DBUG_ASSERT(nr >= 0.0);
return sqrt(nr);
2002-12-14 00:36:59 +01:00
}
my_decimal *Item_std_field::val_decimal(my_decimal *dec_buf)
{
/*
We can't call val_decimal_from_real() for DECIMAL_RESULT as
Item_variance_field::val_real() would cause an infinite loop
*/
my_decimal tmp_dec, *dec;
double nr;
if (hybrid_type == REAL_RESULT)
return val_decimal_from_real(dec_buf);
dec= Item_variance_field::val_decimal(dec_buf);
if (!dec)
return 0;
my_decimal2double(E_DEC_FATAL_ERROR, dec, &nr);
DBUG_ASSERT(nr >= 0.0);
nr= sqrt(nr);
double2my_decimal(E_DEC_FATAL_ERROR, nr, &tmp_dec);
my_decimal_round(E_DEC_FATAL_ERROR, &tmp_dec, decimals, FALSE, dec_buf);
return dec_buf;
}
2002-12-14 00:36:59 +01:00
Item_variance_field::Item_variance_field(Item_sum_variance *item)
2000-07-31 21:29:14 +02:00
{
name=item->name;
decimals=item->decimals;
max_length=item->max_length;
unsigned_flag= item->unsigned_flag;
2000-07-31 21:29:14 +02:00
field=item->result_field;
maybe_null=1;
sample= item->sample;
prec_increment= item->prec_increment;
2005-02-08 23:50:45 +01:00
if ((hybrid_type= item->hybrid_type) == DECIMAL_RESULT)
{
f_scale0= item->f_scale0;
f_precision0= item->f_precision0;
dec_bin_size0= item->dec_bin_size0;
f_scale1= item->f_scale1;
f_precision1= item->f_precision1;
dec_bin_size1= item->dec_bin_size1;
}
2000-07-31 21:29:14 +02:00
}
2004-11-11 19:39:35 +01:00
double Item_variance_field::val_real()
2000-07-31 21:29:14 +02:00
{
// fix_fields() never calls for this Item
2005-02-08 23:50:45 +01:00
if (hybrid_type == DECIMAL_RESULT)
return val_real_from_decimal();
double recurrence_s;
ulonglong count;
float8get(recurrence_s, (field->ptr + sizeof(double)));
2000-07-31 21:29:14 +02:00
count=sint8korr(field->ptr+sizeof(double)*2);
if ((null_value= (count <= sample)))
2000-07-31 21:29:14 +02:00
return 0.0;
return variance_fp_recurrence_result(recurrence_s, count, sample);
2005-02-08 23:50:45 +01:00
}
2000-07-31 21:29:14 +02:00
/****************************************************************************
** Functions to handle dynamic loadable aggregates
** Original source by: Alexis Mikhailov <root@medinf.chuvashia.su>
** Adapted for UDAs by: Andreas F. Bobak <bobak@relog.ch>.
** Rewritten by: Monty.
****************************************************************************/
#ifdef HAVE_DLOPEN
void Item_udf_sum::clear()
2000-07-31 21:29:14 +02:00
{
DBUG_ENTER("Item_udf_sum::clear");
udf.clear();
DBUG_VOID_RETURN;
2000-07-31 21:29:14 +02:00
}
bool Item_udf_sum::add()
{
DBUG_ENTER("Item_udf_sum::add");
2000-07-31 21:29:14 +02:00
udf.add(&null_value);
DBUG_RETURN(0);
}
void Item_udf_sum::cleanup()
{
/*
udf_handler::cleanup() nicely handles case when we have not
original item but one created by copy_or_same() method.
*/
udf.cleanup();
Item_sum::cleanup();
}
void Item_udf_sum::print(String *str, enum_query_type query_type)
{
str->append(func_name());
str->append('(');
for (uint i=0 ; i < arg_count ; i++)
{
if (i)
str->append(',');
args[i]->print(str, query_type);
}
str->append(')');
}
Item *Item_sum_udf_float::copy_or_same(THD* thd)
{
return new (thd->mem_root) Item_sum_udf_float(thd, this);
}
2004-11-11 19:39:35 +01:00
double Item_sum_udf_float::val_real()
2000-07-31 21:29:14 +02:00
{
DBUG_ASSERT(fixed == 1);
2000-07-31 21:29:14 +02:00
DBUG_ENTER("Item_sum_udf_float::val");
DBUG_PRINT("info",("result_type: %d arg_count: %d",
args[0]->result_type(), arg_count));
DBUG_RETURN(udf.val(&null_value));
}
2000-07-31 21:29:14 +02:00
String *Item_sum_udf_float::val_str(String *str)
{
return val_string_from_real(str);
2000-07-31 21:29:14 +02:00
}
my_decimal *Item_sum_udf_float::val_decimal(my_decimal *dec)
{
return val_decimal_from_real(dec);
}
2005-02-08 23:50:45 +01:00
String *Item_sum_udf_decimal::val_str(String *str)
{
return val_string_from_decimal(str);
2005-02-08 23:50:45 +01:00
}
double Item_sum_udf_decimal::val_real()
{
return val_real_from_decimal();
2005-02-08 23:50:45 +01:00
}
longlong Item_sum_udf_decimal::val_int()
{
return val_int_from_decimal();
2005-02-08 23:50:45 +01:00
}
my_decimal *Item_sum_udf_decimal::val_decimal(my_decimal *dec_buf)
{
DBUG_ASSERT(fixed == 1);
DBUG_ENTER("Item_func_udf_decimal::val_decimal");
DBUG_PRINT("info",("result_type: %d arg_count: %d",
args[0]->result_type(), arg_count));
DBUG_RETURN(udf.val_decimal(&null_value, dec_buf));
}
Item *Item_sum_udf_decimal::copy_or_same(THD* thd)
{
return new (thd->mem_root) Item_sum_udf_decimal(thd, this);
}
Item *Item_sum_udf_int::copy_or_same(THD* thd)
{
return new (thd->mem_root) Item_sum_udf_int(thd, this);
}
2000-07-31 21:29:14 +02:00
longlong Item_sum_udf_int::val_int()
{
DBUG_ASSERT(fixed == 1);
2000-07-31 21:29:14 +02:00
DBUG_ENTER("Item_sum_udf_int::val_int");
DBUG_PRINT("info",("result_type: %d arg_count: %d",
args[0]->result_type(), arg_count));
DBUG_RETURN(udf.val_int(&null_value));
}
2000-07-31 21:29:14 +02:00
String *Item_sum_udf_int::val_str(String *str)
{
return val_string_from_int(str);
2000-07-31 21:29:14 +02:00
}
my_decimal *Item_sum_udf_int::val_decimal(my_decimal *dec)
{
return val_decimal_from_int(dec);
}
2007-10-11 19:29:09 +02:00
/** Default max_length is max argument length. */
2000-07-31 21:29:14 +02:00
void Item_sum_udf_str::fix_length_and_dec()
{
DBUG_ENTER("Item_sum_udf_str::fix_length_and_dec");
max_length=0;
for (uint i = 0; i < arg_count; i++)
set_if_bigger(max_length,args[i]->max_length);
DBUG_VOID_RETURN;
}
Item *Item_sum_udf_str::copy_or_same(THD* thd)
{
return new (thd->mem_root) Item_sum_udf_str(thd, this);
}
my_decimal *Item_sum_udf_str::val_decimal(my_decimal *dec)
{
return val_decimal_from_string(dec);
}
2000-07-31 21:29:14 +02:00
String *Item_sum_udf_str::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
2000-07-31 21:29:14 +02:00
DBUG_ENTER("Item_sum_udf_str::str");
String *res=udf.val_str(str,&str_value);
null_value = !res;
DBUG_RETURN(res);
}
#endif /* HAVE_DLOPEN */
/*****************************************************************************
GROUP_CONCAT function
SQL SYNTAX:
2005-03-17 22:41:03 +01:00
GROUP_CONCAT([DISTINCT] expr,... [ORDER BY col [ASC|DESC],...]
[SEPARATOR str_const])
concat of values from "group by" operation
BUGS
Blobs doesn't work with DISTINCT or ORDER BY
*****************************************************************************/
/**
Compares the values for fields in expr list of GROUP_CONCAT.
@note
GROUP_CONCAT([DISTINCT] expr [,expr ...]
[ORDER BY {unsigned_integer | col_name | expr}
[ASC | DESC] [,col_name ...]]
[SEPARATOR str_val])
@return
@retval -1 : key1 < key2
@retval 0 : key1 = key2
@retval 1 : key1 > key2
*/
extern "C"
int group_concat_key_cmp_with_distinct(void* arg, const void* key1,
const void* key2)
{
Item_func_group_concat *item_func= (Item_func_group_concat*)arg;
TABLE *table= item_func->table;
for (uint i= 0; i < item_func->arg_count_field; i++)
{
Item *item= item_func->args[i];
/*
If field_item is a const item then either get_tp_table_field returns 0
or it is an item over a const table.
*/
if (item->const_item())
continue;
/*
We have to use get_tmp_table_field() instead of
real_item()->get_tmp_table_field() because we want the field in
the temporary table, not the original field
*/
Field *field= item->get_tmp_table_field();
int res;
uint offset= field->offset(field->table->record[0])-table->s->null_bytes;
if((res= field->cmp((uchar*)key1 + offset, (uchar*)key2 + offset)))
return res;
}
return 0;
}
2007-10-11 19:29:09 +02:00
/**
function of sort for syntax: GROUP_CONCAT(expr,... ORDER BY col,... )
*/
extern "C"
int group_concat_key_cmp_with_order(void* arg, const void* key1,
const void* key2)
{
Item_func_group_concat* grp_item= (Item_func_group_concat*) arg;
ORDER **order_item, **end;
2005-03-17 22:41:03 +01:00
TABLE *table= grp_item->table;
for (order_item= grp_item->order, end=order_item+ grp_item->arg_count_order;
order_item < end;
order_item++)
{
Item *item= *(*order_item)->item;
/*
We have to use get_tmp_table_field() instead of
real_item()->get_tmp_table_field() because we want the field in
the temporary table, not the original field
*/
Field *field= item->get_tmp_table_field();
/*
If item is a const item then either get_tp_table_field returns 0
or it is an item over a const table.
*/
if (field && !item->const_item())
{
int res;
uint offset= (field->offset(field->table->record[0]) -
table->s->null_bytes);
if ((res= field->cmp((uchar*)key1 + offset, (uchar*)key2 + offset)))
return (*order_item)->asc ? res : -res;
}
}
/*
We can't return 0 because in that case the tree class would remove this
item as double value. This would cause problems for case-changes and
2005-03-17 22:41:03 +01:00
if the returned values are not the same we do the sort on.
*/
return 1;
}
2007-10-11 19:29:09 +02:00
/**
Append data from current leaf to item->result.
*/
extern "C"
int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)),
void* item_arg)
{
Item_func_group_concat *item= (Item_func_group_concat *) item_arg;
2005-03-17 22:41:03 +01:00
TABLE *table= item->table;
2005-06-07 04:43:59 +02:00
String tmp((char *)table->record[1], table->s->reclength,
default_charset_info);
String tmp2;
uchar *key= (uchar *) key_arg;
String *result= &item->result;
2005-03-17 22:41:03 +01:00
Item **arg= item->args, **arg_end= item->args + item->arg_count_field;
uint old_length= result->length();
if (item->no_appended)
item->no_appended= FALSE;
else
result->append(*item->separator);
tmp.length(0);
2005-03-17 22:41:03 +01:00
for (; arg < arg_end; arg++)
{
2005-03-17 22:41:03 +01:00
String *res;
if (! (*arg)->const_item())
{
/*
We have to use get_tmp_table_field() instead of
real_item()->get_tmp_table_field() because we want the field in
the temporary table, not the original field
2005-03-17 22:41:03 +01:00
We also can't use table->field array to access the fields
because it contains both order and arg list fields.
*/
2005-03-17 22:41:03 +01:00
Field *field= (*arg)->get_tmp_table_field();
uint offset= (field->offset(field->table->record[0]) -
table->s->null_bytes);
2005-03-17 22:41:03 +01:00
DBUG_ASSERT(offset < table->s->reclength);
WL#3817: Simplify string / memory area types and make things more consistent (first part) The following type conversions was done: - Changed byte to uchar - Changed gptr to uchar* - Change my_string to char * - Change my_size_t to size_t - Change size_s to size_t Removed declaration of byte, gptr, my_string, my_size_t and size_s. Following function parameter changes was done: - All string functions in mysys/strings was changed to use size_t instead of uint for string lengths. - All read()/write() functions changed to use size_t (including vio). - All protocoll functions changed to use size_t instead of uint - Functions that used a pointer to a string length was changed to use size_t* - Changed malloc(), free() and related functions from using gptr to use void * as this requires fewer casts in the code and is more in line with how the standard functions work. - Added extra length argument to dirname_part() to return the length of the created string. - Changed (at least) following functions to take uchar* as argument: - db_dump() - my_net_write() - net_write_command() - net_store_data() - DBUG_DUMP() - decimal2bin() & bin2decimal() - Changed my_compress() and my_uncompress() to use size_t. Changed one argument to my_uncompress() from a pointer to a value as we only return one value (makes function easier to use). - Changed type of 'pack_data' argument to packfrm() to avoid casts. - Changed in readfrm() and writefrom(), ha_discover and handler::discover() the type for argument 'frmdata' to uchar** to avoid casts. - Changed most Field functions to use uchar* instead of char* (reduced a lot of casts). - Changed field->val_xxx(xxx, new_ptr) to take const pointers. Other changes: - Removed a lot of not needed casts - Added a few new cast required by other changes - Added some cast to my_multi_malloc() arguments for safety (as string lengths needs to be uint, not size_t). - Fixed all calls to hash-get-key functions to use size_t*. (Needed to be done explicitely as this conflict was often hided by casting the function to hash_get_key). - Changed some buffers to memory regions to uchar* to avoid casts. - Changed some string lengths from uint to size_t. - Changed field->ptr to be uchar* instead of char*. This allowed us to get rid of a lot of casts. - Some changes from true -> TRUE, false -> FALSE, unsigned char -> uchar - Include zlib.h in some files as we needed declaration of crc32() - Changed MY_FILE_ERROR to be (size_t) -1. - Changed many variables to hold the result of my_read() / my_write() to be size_t. This was needed to properly detect errors (which are returned as (size_t) -1). - Removed some very old VMS code - Changed packfrm()/unpackfrm() to not be depending on uint size (portability fix) - Removed windows specific code to restore cursor position as this causes slowdown on windows and we should not mix read() and pread() calls anyway as this is not thread safe. Updated function comment to reflect this. Changed function that depended on original behavior of my_pwrite() to itself restore the cursor position (one such case). - Added some missing checking of return value of malloc(). - Changed definition of MOD_PAD_CHAR_TO_FULL_LENGTH to avoid 'long' overflow. - Changed type of table_def::m_size from my_size_t to ulong to reflect that m_size is the number of elements in the array, not a string/memory length. - Moved THD::max_row_length() to table.cc (as it's not depending on THD). Inlined max_row_length_blob() into this function. - More function comments - Fixed some compiler warnings when compiled without partitions. - Removed setting of LEX_STRING() arguments in declaration (portability fix). - Some trivial indentation/variable name changes. - Some trivial code simplifications: - Replaced some calls to alloc_root + memcpy to use strmake_root()/strdup_root(). - Changed some calls from memdup() to strmake() (Safety fix) - Simpler loops in client-simple.c
2007-05-10 11:59:39 +02:00
res= field->val_str(&tmp, key + offset);
}
2005-03-17 22:41:03 +01:00
else
res= (*arg)->val_str(&tmp);
if (res)
result->append(*res);
}
item->row_count++;
2005-03-17 22:41:03 +01:00
/* stop if length of result more than max_length */
if (result->length() > item->max_length)
{
int well_formed_error;
CHARSET_INFO *cs= item->collation.collation;
2006-11-08 19:08:50 +01:00
const char *ptr= result->ptr();
uint add_length;
/*
It's ok to use item->result.length() as the fourth argument
as this is never used to limit the length of the data.
Cut is done with the third argument.
*/
add_length= cs->cset->well_formed_len(cs,
ptr + old_length,
2006-11-08 19:08:50 +01:00
ptr + item->max_length,
result->length(),
&well_formed_error);
2006-11-08 19:08:50 +01:00
result->length(old_length + add_length);
item->warning_for_row= TRUE;
push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_CUT_VALUE_GROUP_CONCAT, ER(ER_CUT_VALUE_GROUP_CONCAT),
item->row_count);
return 1;
}
return 0;
}
2007-10-11 19:29:09 +02:00
/**
Constructor of Item_func_group_concat.
@param distinct_arg distinct
@param select_list list of expression for show values
@param order_list list of sort columns
@param separator_arg string value of separator.
*/
2005-03-17 22:41:03 +01:00
Item_func_group_concat::
Item_func_group_concat(Name_resolution_context *context_arg,
bool distinct_arg, List<Item> *select_list,
SQL_I_List<ORDER> *order_list, String *separator_arg)
:tmp_table_param(0), separator(separator_arg), tree(0),
unique_filter(NULL), table(0),
order(0), context(context_arg),
2005-03-17 22:41:03 +01:00
arg_count_order(order_list ? order_list->elements : 0),
arg_count_field(select_list->elements),
row_count(0),
2005-03-17 22:41:03 +01:00
distinct(distinct_arg),
warning_for_row(FALSE),
force_copy_fields(0), original(0)
{
Item *item_select;
Item **arg_ptr;
2005-03-17 22:41:03 +01:00
quick_group= FALSE;
arg_count= arg_count_field + arg_count_order;
2005-03-17 22:41:03 +01:00
/*
We need to allocate:
args - arg_count_field+arg_count_order
(for possible order items in temporare tables)
order - arg_count_order
*/
if (!(args= (Item**) sql_alloc(sizeof(Item*) * arg_count +
2005-03-17 22:41:03 +01:00
sizeof(ORDER*)*arg_count_order)))
return;
if (!(orig_args= (Item **) sql_alloc(sizeof(Item *) * arg_count)))
{
args= NULL;
return;
}
order= (ORDER**)(args + arg_count);
/* fill args items of show and sort */
2005-03-17 22:41:03 +01:00
List_iterator_fast<Item> li(*select_list);
for (arg_ptr=args ; (item_select= li++) ; arg_ptr++)
*arg_ptr= item_select;
2005-03-17 22:41:03 +01:00
if (arg_count_order)
{
ORDER **order_ptr= order;
for (ORDER *order_item= order_list->first;
2005-03-17 22:41:03 +01:00
order_item != NULL;
order_item= order_item->next)
{
(*order_ptr++)= order_item;
*arg_ptr= *order_item->item;
order_item->item= arg_ptr++;
}
}
}
2005-03-17 22:41:03 +01:00
Item_func_group_concat::Item_func_group_concat(THD *thd,
2005-03-17 22:41:03 +01:00
Item_func_group_concat *item)
:Item_sum(thd, item),
tmp_table_param(item->tmp_table_param),
separator(item->separator),
tree(item->tree),
unique_filter(item->unique_filter),
table(item->table),
context(item->context),
arg_count_order(item->arg_count_order),
arg_count_field(item->arg_count_field),
row_count(item->row_count),
2005-03-17 22:41:03 +01:00
distinct(item->distinct),
warning_for_row(item->warning_for_row),
always_null(item->always_null),
force_copy_fields(item->force_copy_fields),
original(item)
{
quick_group= item->quick_group;
result.set_charset(collation.collation);
Bug #54476: crash when group_concat and 'with rollup' in prepared statements Using GROUP_CONCAT() together with the WITH ROLLUP modifier could crash the server. The reason was a combination of several facts: 1. The Item_func_group_concat class stores pointers to ORDER objects representing the columns in the ORDER BY clause of GROUP_CONCAT(). 2. find_order_in_list() called from Item_func_group_concat::setup() modifies the ORDER objects so that their 'item' member points to the arguments list allocated in the Item_func_group_concat constructor. 3. In some cases (e.g. in JOIN::rollup_make_fields) a copy of the original Item_func_group_concat object could be created by using the Item_func_group_concat::Item_func_group_concat(THD *thd, Item_func_group_concat *item) copy constructor. The latter essentially creates a shallow copy of the source object. Memory for the arguments array is allocated on thd->mem_root, but the pointers for arguments and ORDER are copied verbatim. What happens in the test case is that when executing the query for the first time, after a copy of the original Item_func_group_concat object has been created by JOIN::rollup_make_fields(), find_order_in_list() is called for this new object. It then resolves ORDER BY by modifying the ORDER objects so that they point to elements of the arguments array which is local to the cloned object. When thd->mem_root is freed upon completing the execution, pointers in the ORDER objects become invalid. Those ORDER objects, however, are also shared with the original Item_func_group_concat object which is preserved between executions of a prepared statement. So the first call to find_order_in_list() for the original object on the second execution tries to dereference an invalid pointer. The solution is to create copies of the ORDER objects when copying Item_func_group_concat to not leave any stale pointers in other instances with different lifecycles.
2010-07-23 13:52:54 +02:00
/*
Since the ORDER structures pointed to by the elements of the 'order' array
may be modified in find_order_in_list() called from
Item_func_group_concat::setup(), create a copy of those structures so that
such modifications done in this object would not have any effect on the
object being copied.
*/
ORDER *tmp;
if (!(order= (ORDER **) thd->alloc(sizeof(ORDER *) * arg_count_order +
sizeof(ORDER) * arg_count_order)))
return;
tmp= (ORDER *)(order + arg_count_order);
for (uint i= 0; i < arg_count_order; i++, tmp++)
{
memcpy(tmp, item->order[i], sizeof(ORDER));
order[i]= tmp;
}
}
void Item_func_group_concat::cleanup()
{
DBUG_ENTER("Item_func_group_concat::cleanup");
Item_sum::cleanup();
/*
Free table and tree if they belong to this item (if item have not pointer
to original item from which was made copy => it own its objects )
*/
if (!original)
{
2005-03-17 22:41:03 +01:00
delete tmp_table_param;
tmp_table_param= 0;
if (table)
{
2005-03-17 22:41:03 +01:00
THD *thd= table->in_use;
free_tmp_table(thd, table);
table= 0;
2005-03-17 22:41:03 +01:00
if (tree)
{
delete_tree(tree);
tree= 0;
}
if (unique_filter)
{
delete unique_filter;
unique_filter= NULL;
}
}
DBUG_ASSERT(tree == 0);
}
DBUG_VOID_RETURN;
}
Item *Item_func_group_concat::copy_or_same(THD* thd)
{
return new (thd->mem_root) Item_func_group_concat(thd, this);
}
void Item_func_group_concat::clear()
{
result.length(0);
result.copy();
null_value= TRUE;
warning_for_row= FALSE;
no_appended= TRUE;
2005-03-17 22:41:03 +01:00
if (tree)
reset_tree(tree);
if (unique_filter)
unique_filter->reset();
2005-03-17 22:41:03 +01:00
/* No need to reset the table as we never call write_row */
}
bool Item_func_group_concat::add()
{
2003-06-03 13:02:51 +02:00
if (always_null)
return 0;
copy_fields(tmp_table_param);
if (copy_funcs(tmp_table_param->items_to_copy, table->in_use))
return TRUE;
for (uint i= 0; i < arg_count_field; i++)
{
Item *show_item= args[i];
if (!show_item->const_item())
{
2005-06-07 04:43:59 +02:00
Field *f= show_item->get_tmp_table_field();
if (f->is_null_in_record((const uchar*) table->record[0]))
2005-03-17 22:41:03 +01:00
return 0; // Skip row if it contains null
}
}
null_value= FALSE;
bool row_eligible= TRUE;
if (distinct)
{
/* Filter out duplicate rows. */
uint count= unique_filter->elements_in_tree();
unique_filter->unique_add(table->record[0] + table->s->null_bytes);
if (count == unique_filter->elements_in_tree())
row_eligible= FALSE;
}
TREE_ELEMENT *el= 0; // Only for safety
if (row_eligible && tree)
{
2005-03-17 22:41:03 +01:00
el= tree_insert(tree, table->record[0] + table->s->null_bytes, 0,
tree->custom_arg);
/* check if there was enough memory to insert the row */
if (!el)
return 1;
}
/*
If the row is not a duplicate (el->count == 1)
we can dump the row here in case of GROUP_CONCAT(DISTINCT...)
instead of doing tree traverse later.
*/
if (row_eligible && !warning_for_row &&
2005-03-17 22:41:03 +01:00
(!tree || (el->count == 1 && distinct && !arg_count_order)))
dump_leaf_key(table->record[0] + table->s->null_bytes, 1, this);
return 0;
}
bool
Item_func_group_concat::fix_fields(THD *thd, Item **ref)
{
2005-03-17 22:41:03 +01:00
uint i; /* for loop variable */
DBUG_ASSERT(fixed == 0);
if (init_sum_func_check(thd))
return TRUE;
2005-03-17 22:41:03 +01:00
maybe_null= 1;
/*
Fix fields for select list and ORDER clause
*/
2005-03-17 22:41:03 +01:00
for (i=0 ; i < arg_count ; i++)
{
2005-02-08 23:50:45 +01:00
if ((!args[i]->fixed &&
args[i]->fix_fields(thd, args + i)) ||
args[i]->check_cols(1))
return TRUE;
}
WL#2649 Number-to-string conversions added: include/ctype_numconv.inc mysql-test/include/ctype_numconv.inc mysql-test/r/ctype_binary.result mysql-test/t/ctype_binary.test Adding tests modified: mysql-test/r/bigint.result mysql-test/r/case.result mysql-test/r/create.result mysql-test/r/ctype_cp1251.result mysql-test/r/ctype_latin1.result mysql-test/r/ctype_ucs.result mysql-test/r/func_gconcat.result mysql-test/r/func_str.result mysql-test/r/metadata.result mysql-test/r/ps_1general.result mysql-test/r/ps_2myisam.result mysql-test/r/ps_3innodb.result mysql-test/r/ps_4heap.result mysql-test/r/ps_5merge.result mysql-test/r/show_check.result mysql-test/r/type_datetime.result mysql-test/r/type_ranges.result mysql-test/r/union.result mysql-test/suite/ndb/r/ps_7ndb.result mysql-test/t/ctype_cp1251.test mysql-test/t/ctype_latin1.test mysql-test/t/ctype_ucs.test mysql-test/t/func_str.test Fixing tests @ sql/field.cc - Return str result using my_charset_numeric. - Using real multi-byte aware str_to_XXX functions to handle tricky charset values propely (e.g. UCS2) @ sql/field.h - Changing derivation of non-string field types to DERIVATION_NUMERIC. - Changing binary() for numeric/datetime fields to always return TRUE even if charset is not my_charset_bin. We need this to keep ha_base_keytype() return HA_KEYTYPE_BINARY. - Adding BINARY_FLAG into some fields, because it's not being set automatically anymore with "my_charset_bin to my_charset_numeric" change. - Changing derivation for numeric/datetime datatypes to a weaker value, to make "SELECT concat('string', field)" use character set of the string literal for the result of the function. @ sql/item.cc - Implementing generic val_str_ascii(). - Using max_char_length() instead of direct read of max_length to make "tricky" charsets like UCS2 work. NOTE: in the future we'll possibly remove all direct reads of max_length - Fixing Item_num::safe_charset_converter(). Previously it alligned binary string to character string (for example by adding leading 0x00 when doing binary->UCS2 conversion). Now it just converts from my_charset_numbner to "tocs". - Using val_str_ascii() in Item::get_time() to make UCS2 arguments work. - Other misc changes @ sql/item.h - Changing MY_COLL_CMP_CONV and MY_COLL_ALLOW_CONV to bit operations instead of hard-coded bit masks. - Addding new method DTCollation.set_numeric(). - Adding new methods to Item. - Adding helper functions to make code look nicer: agg_item_charsets_for_string_result() agg_item_charsets_for_comparison() - Changing charset for Item_num-derived items from my_charset_bin to my_charset_numeric (which is an alias for latin1). @ sql/item_cmpfunc.cc - Using new helper functions - Other misc changes @ sql/item_cmpfunc.h - Fixing strcmp() to return max_length=2. Previously it returned 1, which was wrong, because it did not fit '-1'. @ sql/item_func.cc - Using new helper functions - Other minor changes @ sql/item_func.h - Removing unused functions - Adding helper functions agg_arg_charsets_for_string_result() agg_arg_charsets_for_comparison() - Adding set_numeric() into constructors of numeric items. - Using fix_length_and_charset() and fix_char_length() instead of direct write to max_length. @ sql/item_geofunc.cc - Changing class for Item_func_geometry_type and Item_func_as_wkt from Item_str_func to Item_str_ascii_func, to make them return UCS2 result properly (when character_set_connection=ucs2). @ sql/item_geofunc.h - Changing class for Item_func_geometry_type and Item_func_as_wkt from Item_str_func to Item_str_ascii_func, to make them return UCS2 result properly (when @@character_set_connection=ucs2). @ sql/item_strfunc.cc - Implementing Item_str_func::val_str(). - Renaming val_str to val_str_ascii for some items, to make them work with UCS2 properly. - Using new helper functions - All single-argument functions that expect string result now call this method: agg_arg_charsets_for_string_result(collation, args, 1); This enables character set conversion to @@character_set_connection in case of pure numeric input. @ sql/item_strfunc.h - Introducing Item_str_ascii_func - for functions which return pure ASCII data, for performance purposes, as well as for the cases when the old implementation of val_str() was heavily 8-bit oriented and implementing a UCS2-aware version is tricky. @ sql/item_sum.cc - Using new helper functions. @ sql/item_timefunc.cc - Using my_charset_numeric instead of my_charset_bin. - Using fix_char_length(), fix_length_and_charset() and fix_length_and_charset_datetime() instead of direct write to max_length. - Using tricky-charset aware function str_to_time_with_warn() @ sql/item_timefunc.h - Using new helper functions for charset and length initialization. - Changing base class for Item_func_get_format() to make it return UCS2 properly (when character_set_connection=ucs2). @ sql/item_xmlfunc.cc - Using new helper function @ sql/my_decimal.cc - Adding a new DECIMAL to CHAR converter with real multibyte support (e.g. UCS2) @ sql/mysql_priv.h - Introducing a new derivation level for numeric/datetime data types. - Adding macros for my_charset_numeric and MY_REPERTOIRE_NUMERIC. - Adding prototypes for str_set_decimal() - Adding prototypes for character-set aware str_to_xxx() functions. @ sql/protocol.cc - Changing charsetnr to "binary" client-side metadata for numeric/datetime data types. @ sql/time.cc - Adding to_ascii() helper function, to convert a string in any character set to ascii representation. In the future can be extended to understand digits written in various non-Latin word scripts. - Adding real multy-byte character set aware versions for str_to_XXXX, to make these these type of queries work correct: INSERT INTO t1 SET datetime_column=ucs2_expression; @ strings/ctype-ucs2.c - endptr was not calculated correctly. INSERTing of UCS2 values into numeric columns returned warnings about truncated wrong data.
2010-02-11 05:17:25 +01:00
/* skip charset aggregation for order columns */
if (agg_item_charsets_for_string_result(collation, func_name(),
args, arg_count - arg_count_order))
return 1;
result.set_charset(collation.collation);
result_field= 0;
null_value= 1;
2005-03-17 22:41:03 +01:00
max_length= thd->variables.group_concat_max_len;
uint32 offset;
if (separator->needs_conversion(separator->length(), separator->charset(),
collation.collation, &offset))
{
uint32 buflen= collation.collation->mbmaxlen * separator->length();
uint errors, conv_length;
char *buf;
String *new_separator;
2007-06-22 15:32:06 +02:00
if (!(buf= (char*) thd->stmt_arena->alloc(buflen)) ||
!(new_separator= new(thd->stmt_arena->mem_root)
String(buf, buflen, collation.collation)))
return TRUE;
conv_length= copy_and_convert(buf, buflen, collation.collation,
separator->ptr(), separator->length(),
separator->charset(), &errors);
new_separator->length(conv_length);
separator= new_separator;
}
if (check_sum_func(thd, ref))
return TRUE;
memcpy (orig_args, args, sizeof (Item *) * arg_count);
fixed= 1;
return FALSE;
}
bool Item_func_group_concat::setup(THD *thd)
{
List<Item> list;
2003-08-26 11:51:09 +02:00
SELECT_LEX *select_lex= thd->lex->current_select;
DBUG_ENTER("Item_func_group_concat::setup");
/*
2005-03-17 22:41:03 +01:00
Currently setup() can be called twice. Please add
assertion here when this is fixed.
2004-07-21 00:45:08 +02:00
*/
2005-03-17 22:41:03 +01:00
if (table || tree)
DBUG_RETURN(FALSE);
if (!(tmp_table_param= new TMP_TABLE_PARAM))
DBUG_RETURN(TRUE);
/* We'll convert all blobs to varchar fields in the temporary table */
2006-11-08 19:08:50 +01:00
tmp_table_param->convert_blob_length= max_length *
collation.collation->mbmaxlen;
2005-03-17 22:41:03 +01:00
/* Push all not constant fields to the list and create a temp table */
2003-06-03 13:02:51 +02:00
always_null= 0;
for (uint i= 0; i < arg_count_field; i++)
{
Item *item= args[i];
if (list.push_back(item))
2005-03-17 22:41:03 +01:00
DBUG_RETURN(TRUE);
if (item->const_item())
{
if (item->is_null())
2005-03-17 22:41:03 +01:00
{
always_null= 1;
DBUG_RETURN(FALSE);
2005-03-17 22:41:03 +01:00
}
}
}
2004-07-21 00:45:08 +02:00
List<Item> all_fields(list);
2005-03-17 22:41:03 +01:00
/*
Try to find every ORDER expression in the list of GROUP_CONCAT
arguments. If an expression is not found, prepend it to
"all_fields". The resulting field list is used as input to create
tmp table columns.
*/
if (arg_count_order &&
setup_order(thd, args, context->table_list, list, all_fields, *order))
2005-03-17 22:41:03 +01:00
DBUG_RETURN(TRUE);
2004-07-21 00:45:08 +02:00
count_field_types(select_lex, tmp_table_param, all_fields, 0);
Fixed bug#15560: GROUP_CONCAT wasn't ready for WITH ROLLUP queries The GROUP_CONCAT uses its own temporary table. When ROLLUP is present it creates the second copy of Item_func_group_concat. This copy receives the same list of arguments that original group_concat does. When the copy is set up the result_fields of functions from the argument list are reset to the temporary table of this copy. As a result of this action data from functions flow directly to the ROLLUP copy and the original group_concat functions shows wrong result. Since queries with COUNT(DISTINCT ...) use temporary tables to store the results the COUNT function they are also affected by this bug. The idea of the fix is to copy content of the result_field for the function under GROUP_CONCAT/COUNT from the first temporary table to the second one, rather than setting result_field to point to the second temporary table. To achieve this goal force_copy_fields flag is added to Item_func_group_concat and Item_sum_count_distinct classes. This flag is initialized to 0 and set to 1 into the make_unique() member function of both classes. To the TMP_TABLE_PARAM structure is modified to include the similar flag as well. The create_tmp_table() function passes that flag to create_tmp_field(). When the flag is set the create_tmp_field() function will set result_field as a source field and will not reset that result field to newly created field for Item_func_result_field and its descendants. Due to this there will be created copy func to copy data from old result_field to newly created field.
2006-03-29 21:30:34 +02:00
tmp_table_param->force_copy_fields= force_copy_fields;
2005-03-17 22:41:03 +01:00
DBUG_ASSERT(table == 0);
2007-05-21 10:27:33 +02:00
if (arg_count_order > 0 || distinct)
{
/*
Currently we have to force conversion of BLOB values to VARCHAR's
if we are to store them in TREE objects used for ORDER BY and
DISTINCT. This leads to truncation if the BLOB's size exceeds
Field_varstring::MAX_SIZE.
*/
2007-05-21 10:27:33 +02:00
set_if_smaller(tmp_table_param->convert_blob_length,
Field_varstring::MAX_SIZE);
/*
Force the create_tmp_table() to convert BIT columns to INT
as we cannot compare two table records containg BIT fields
stored in the the tree used for distinct/order by.
Moreover we don't even save in the tree record null bits
where BIT fields store parts of their data.
*/
List_iterator_fast<Item> li(all_fields);
Item *item;
while ((item= li++))
{
if (item->type() == Item::FIELD_ITEM &&
((Item_field*) item)->field->type() == FIELD_TYPE_BIT)
item->marker= 4;
}
}
/*
2005-03-17 22:41:03 +01:00
We have to create a temporary table to get descriptions of fields
(types, sizes and so on).
Note that in the table, we first have the ORDER BY fields, then the
field list.
*/
2005-03-17 22:41:03 +01:00
if (!(table= create_tmp_table(thd, tmp_table_param, all_fields,
(ORDER*) 0, 0, TRUE,
WL#4738 streamline/simplify @@variable creation process Bug#16565 mysqld --help --verbose does not order variablesBug#20413 sql_slave_skip_counter is not shown in show variables Bug#20415 Output of mysqld --help --verbose is incomplete Bug#25430 variable not found in SELECT @@global.ft_max_word_len; Bug#32902 plugin variables don't know their names Bug#34599 MySQLD Option and Variable Reference need to be consistent in formatting! Bug#34829 No default value for variable and setting default does not raise error Bug#34834 ? Is accepted as a valid sql mode Bug#34878 Few variables have default value according to documentation but error occurs Bug#34883 ft_boolean_syntax cant be assigned from user variable to global var. Bug#37187 `INFORMATION_SCHEMA`.`GLOBAL_VARIABLES`: inconsistent status Bug#40988 log_output_basic.test succeeded though syntactically false. Bug#41010 enum-style command-line options are not honoured (maria.maria-recover fails) Bug#42103 Setting key_buffer_size to a negative value may lead to very large allocations Bug#44691 Some plugins configured as MYSQL_PLUGIN_MANDATORY in can be disabled Bug#44797 plugins w/o command-line options have no disabling option in --help Bug#46314 string system variables don't support expressions Bug#46470 sys_vars.max_binlog_cache_size_basic_32 is broken Bug#46586 When using the plugin interface the type "set" for options caused a crash. Bug#47212 Crash in DBUG_PRINT in mysqltest.cc when trying to print octal number Bug#48758 mysqltest crashes on sys_vars.collation_server_basic in gcov builds Bug#49417 some complaints about mysqld --help --verbose output Bug#49540 DEFAULT value of binlog_format isn't the default value Bug#49640 ambiguous option '--skip-skip-myisam' (double skip prefix) Bug#49644 init_connect and \0 Bug#49645 init_slave and multi-byte characters Bug#49646 mysql --show-warnings crashes when server dies
2009-12-22 10:35:56 +01:00
(select_lex->options | thd->variables.option_bits),
2005-03-17 22:41:03 +01:00
HA_POS_ERROR, (char*) "")))
DBUG_RETURN(TRUE);
table->file->extra(HA_EXTRA_NO_ROWS);
table->no_rows= 1;
/*
Need sorting or uniqueness: init tree and choose a function to sort.
Don't reserve space for NULLs: if any of gconcat arguments is NULL,
the row is not added to the result.
*/
uint tree_key_length= table->s->reclength - table->s->null_bytes;
if (arg_count_order)
{
2005-03-17 22:41:03 +01:00
tree= &tree_base;
/*
Create a tree for sorting. The tree is used to sort (according to the
syntax of this function). If there is no ORDER BY clause, we don't
create this tree.
*/
init_tree(tree, (uint) min(thd->variables.max_heap_table_size,
thd->variables.sortbuff_size/16), 0,
tree_key_length,
group_concat_key_cmp_with_order , 0, NULL, (void*) this);
}
2005-03-17 22:41:03 +01:00
if (distinct)
unique_filter= new Unique(group_concat_key_cmp_with_distinct,
(void*)this,
tree_key_length,
ram_limitation(thd));
2005-03-17 22:41:03 +01:00
DBUG_RETURN(FALSE);
}
/* This is used by rollup to create a separate usable copy of the function */
void Item_func_group_concat::make_unique()
{
2005-03-17 22:41:03 +01:00
tmp_table_param= 0;
table=0;
original= 0;
Fixed bug#15560: GROUP_CONCAT wasn't ready for WITH ROLLUP queries The GROUP_CONCAT uses its own temporary table. When ROLLUP is present it creates the second copy of Item_func_group_concat. This copy receives the same list of arguments that original group_concat does. When the copy is set up the result_fields of functions from the argument list are reset to the temporary table of this copy. As a result of this action data from functions flow directly to the ROLLUP copy and the original group_concat functions shows wrong result. Since queries with COUNT(DISTINCT ...) use temporary tables to store the results the COUNT function they are also affected by this bug. The idea of the fix is to copy content of the result_field for the function under GROUP_CONCAT/COUNT from the first temporary table to the second one, rather than setting result_field to point to the second temporary table. To achieve this goal force_copy_fields flag is added to Item_func_group_concat and Item_sum_count_distinct classes. This flag is initialized to 0 and set to 1 into the make_unique() member function of both classes. To the TMP_TABLE_PARAM structure is modified to include the similar flag as well. The create_tmp_table() function passes that flag to create_tmp_field(). When the flag is set the create_tmp_field() function will set result_field as a source field and will not reset that result field to newly created field for Item_func_result_field and its descendants. Due to this there will be created copy func to copy data from old result_field to newly created field.
2006-03-29 21:30:34 +02:00
force_copy_fields= 1;
2005-03-17 22:41:03 +01:00
tree= 0;
}
String* Item_func_group_concat::val_str(String* str)
{
DBUG_ASSERT(fixed == 1);
if (null_value)
return 0;
if (no_appended && tree)
/* Tree is used for sorting as in ORDER BY */
tree_walk(tree, &dump_leaf_key, this, left_root_right);
return &result;
}
void Item_func_group_concat::print(String *str, enum_query_type query_type)
{
/* orig_args is not filled with valid values until fix_fields() */
Item **pargs= fixed ? orig_args : args;
str->append(STRING_WITH_LEN("group_concat("));
if (distinct)
str->append(STRING_WITH_LEN("distinct "));
for (uint i= 0; i < arg_count_field; i++)
{
if (i)
str->append(',');
2010-02-19 20:06:47 +01:00
pargs[i]->print(str, query_type);
}
if (arg_count_order)
{
str->append(STRING_WITH_LEN(" order by "));
for (uint i= 0 ; i < arg_count_order ; i++)
{
if (i)
2005-03-17 22:41:03 +01:00
str->append(',');
pargs[i + arg_count_field]->print(str, query_type);
if (order[i]->asc)
str->append(STRING_WITH_LEN(" ASC"));
else
str->append(STRING_WITH_LEN(" DESC"));
}
}
str->append(STRING_WITH_LEN(" separator \'"));
str->append(*separator);
str->append(STRING_WITH_LEN("\')"));
}
Item_func_group_concat::~Item_func_group_concat()
{
if (!original && unique_filter)
delete unique_filter;
}