merge
client/mysql.cc: Auto merged libmysql/libmysql.c: Auto merged sql/ha_ndbcluster.cc: Auto merged sql/item.cc: Auto merged sql/item.h: Auto merged sql/item_func.cc: Auto merged sql/item_subselect.cc: Auto merged sql/item_sum.cc: Auto merged sql/mysql_priv.h: Auto merged sql/opt_range.cc: Auto merged sql/sql_base.cc: Auto merged sql/sql_bitmap.h: Auto merged sql/sql_class.h: Auto merged sql/sql_insert.cc: Auto merged sql/sql_parse.cc: Auto merged sql/sql_select.cc: Auto merged
This commit is contained in:
commit
4eb26350cb
@ -1657,9 +1657,10 @@ int mysql_real_query_for_lazy(const char *buf, int length)
|
|||||||
{
|
{
|
||||||
for (uint retry=0;; retry++)
|
for (uint retry=0;; retry++)
|
||||||
{
|
{
|
||||||
|
int error;
|
||||||
if (!mysql_real_query(&mysql,buf,length))
|
if (!mysql_real_query(&mysql,buf,length))
|
||||||
return 0;
|
return 0;
|
||||||
int error= put_error(&mysql);
|
error= put_error(&mysql);
|
||||||
if (mysql_errno(&mysql) != CR_SERVER_GONE_ERROR || retry > 1 ||
|
if (mysql_errno(&mysql) != CR_SERVER_GONE_ERROR || retry > 1 ||
|
||||||
!opt_reconnect)
|
!opt_reconnect)
|
||||||
return error;
|
return error;
|
||||||
@ -2314,22 +2315,23 @@ print_table_data_vertically(MYSQL_RES *result)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* print_warnings should be called right after executing a statement */
|
/* print_warnings should be called right after executing a statement */
|
||||||
static void
|
|
||||||
print_warnings()
|
static void print_warnings()
|
||||||
{
|
{
|
||||||
char query[30];
|
const char *query;
|
||||||
MYSQL_RES *result;
|
MYSQL_RES *result;
|
||||||
MYSQL_ROW cur;
|
MYSQL_ROW cur;
|
||||||
|
my_ulonglong num_rows;
|
||||||
|
|
||||||
/* Get the warnings */
|
/* Get the warnings */
|
||||||
strmov(query,"show warnings");
|
query= "show warnings";
|
||||||
mysql_real_query_for_lazy(query, strlen(query));
|
mysql_real_query_for_lazy(query, strlen(query));
|
||||||
mysql_store_result_for_lazy(&result);
|
mysql_store_result_for_lazy(&result);
|
||||||
|
|
||||||
/* Bail out when no warnings */
|
/* Bail out when no warnings */
|
||||||
my_ulonglong num_rows = mysql_num_rows(result);
|
if (!(num_rows= mysql_num_rows(result)))
|
||||||
if (num_rows == 0)
|
|
||||||
{
|
{
|
||||||
mysql_free_result(result);
|
mysql_free_result(result);
|
||||||
return;
|
return;
|
||||||
@ -2343,13 +2345,12 @@ print_warnings()
|
|||||||
mysql_free_result(result);
|
mysql_free_result(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char
|
|
||||||
*array_value(const char **array, char key)
|
static const char *array_value(const char **array, char key)
|
||||||
{
|
{
|
||||||
int x;
|
for (; *array; array+= 2)
|
||||||
for (x= 0; array[x]; x+= 2)
|
if (**array == key)
|
||||||
if (*array[x] == key)
|
return array[1];
|
||||||
return array[x + 1];
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1738,7 +1738,7 @@ myodbc_remove_escape(MYSQL *mysql,char *name)
|
|||||||
|
|
||||||
/* Default number of rows fetched per one COM_FETCH command. */
|
/* Default number of rows fetched per one COM_FETCH command. */
|
||||||
|
|
||||||
#define DEFAULT_PREFETCH_ROWS 1UL
|
#define DEFAULT_PREFETCH_ROWS (ulong) 1
|
||||||
|
|
||||||
/*
|
/*
|
||||||
These functions are called by function pointer MYSQL_STMT::read_row_func.
|
These functions are called by function pointer MYSQL_STMT::read_row_func.
|
||||||
|
@ -887,7 +887,7 @@ report_stats () {
|
|||||||
|
|
||||||
found_error=0
|
found_error=0
|
||||||
# Find errors
|
# Find errors
|
||||||
for i in "^Warning:" "^Error:" "^==.* at 0x"
|
for i in "^Warning:" "^Error:" "^==.* at 0x" "InnoDB: Warning"
|
||||||
do
|
do
|
||||||
if $GREP "$i" $MY_LOG_DIR/warnings.tmp >> $MY_LOG_DIR/warnings
|
if $GREP "$i" $MY_LOG_DIR/warnings.tmp >> $MY_LOG_DIR/warnings
|
||||||
then
|
then
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
drop table if exists t1,t11,t12,t2;
|
drop table if exists t1,t2,t3,t11,t12;
|
||||||
CREATE TABLE t1 (a tinyint(3), b tinyint(5));
|
CREATE TABLE t1 (a tinyint(3), b tinyint(5));
|
||||||
INSERT INTO t1 VALUES (1,1);
|
INSERT INTO t1 VALUES (1,1);
|
||||||
INSERT LOW_PRIORITY INTO t1 VALUES (1,2);
|
INSERT LOW_PRIORITY INTO t1 VALUES (1,2);
|
||||||
@ -172,3 +172,23 @@ a
|
|||||||
0
|
0
|
||||||
2
|
2
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
|
CREATE TABLE t1 (a int not null,b int not null);
|
||||||
|
CREATE TABLE t2 (a int not null, b int not null, primary key (a,b));
|
||||||
|
CREATE TABLE t3 (a int not null, b int not null, primary key (a,b));
|
||||||
|
insert into t1 values (1,1),(2,1),(1,3);
|
||||||
|
insert into t2 values (1,1),(2,2),(3,3);
|
||||||
|
insert into t3 values (1,1),(2,1),(1,3);
|
||||||
|
select * from t1,t2,t3 where t1.a=t2.a AND t2.b=t3.a and t1.b=t3.b;
|
||||||
|
a b a b a b
|
||||||
|
1 1 1 1 1 1
|
||||||
|
2 1 2 2 2 1
|
||||||
|
1 3 1 1 1 3
|
||||||
|
explain select * from t1,t2,t3 where t1.a=t2.a AND t2.b=t3.a and t1.b=t3.b;
|
||||||
|
id select_type table type possible_keys key key_len ref rows Extra
|
||||||
|
1 SIMPLE t1 ALL NULL NULL NULL NULL 3
|
||||||
|
1 SIMPLE t2 index PRIMARY PRIMARY 8 NULL 3 Using where; Using index
|
||||||
|
1 SIMPLE t3 index PRIMARY PRIMARY 8 NULL 3 Using where; Using index
|
||||||
|
delete t2.*,t3.* from t1,t2,t3 where t1.a=t2.a AND t2.b=t3.a and t1.b=t3.b;
|
||||||
|
select * from t3;
|
||||||
|
a b
|
||||||
|
drop table t1,t2,t3;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
DROP TABLE IF EXISTS t0,t1,t2;
|
||||||
show variables where variable_name like "skip_show_database";
|
show variables where variable_name like "skip_show_database";
|
||||||
Variable_name Value
|
Variable_name Value
|
||||||
skip_show_database OFF
|
skip_show_database OFF
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
--disable_warnings
|
--disable_warnings
|
||||||
drop table if exists t1,t11,t12,t2;
|
drop table if exists t1,t2,t3,t11,t12;
|
||||||
--enable_warnings
|
--enable_warnings
|
||||||
CREATE TABLE t1 (a tinyint(3), b tinyint(5));
|
CREATE TABLE t1 (a tinyint(3), b tinyint(5));
|
||||||
INSERT INTO t1 VALUES (1,1);
|
INSERT INTO t1 VALUES (1,1);
|
||||||
@ -152,3 +152,20 @@ INSERT INTO t1 VALUES (0),(1),(2);
|
|||||||
DELETE FROM t1 WHERE t1.a > 0 ORDER BY t1.a LIMIT 1;
|
DELETE FROM t1 WHERE t1.a > 0 ORDER BY t1.a LIMIT 1;
|
||||||
SELECT * FROM t1;
|
SELECT * FROM t1;
|
||||||
DROP TABLE t1;
|
DROP TABLE t1;
|
||||||
|
|
||||||
|
#
|
||||||
|
# Test of multi-delete where we are not scanning the first table
|
||||||
|
#
|
||||||
|
|
||||||
|
CREATE TABLE t1 (a int not null,b int not null);
|
||||||
|
CREATE TABLE t2 (a int not null, b int not null, primary key (a,b));
|
||||||
|
CREATE TABLE t3 (a int not null, b int not null, primary key (a,b));
|
||||||
|
insert into t1 values (1,1),(2,1),(1,3);
|
||||||
|
insert into t2 values (1,1),(2,2),(3,3);
|
||||||
|
insert into t3 values (1,1),(2,1),(1,3);
|
||||||
|
select * from t1,t2,t3 where t1.a=t2.a AND t2.b=t3.a and t1.b=t3.b;
|
||||||
|
explain select * from t1,t2,t3 where t1.a=t2.a AND t2.b=t3.a and t1.b=t3.b;
|
||||||
|
delete t2.*,t3.* from t1,t2,t3 where t1.a=t2.a AND t2.b=t3.a and t1.b=t3.b;
|
||||||
|
# This should be empty
|
||||||
|
select * from t3;
|
||||||
|
drop table t1,t2,t3;
|
||||||
|
@ -4,6 +4,11 @@
|
|||||||
# Test for information_schema.schemata &
|
# Test for information_schema.schemata &
|
||||||
# show databases
|
# show databases
|
||||||
|
|
||||||
|
--disable_warnings
|
||||||
|
DROP TABLE IF EXISTS t0,t1,t2;
|
||||||
|
--enable_warnings
|
||||||
|
|
||||||
|
|
||||||
show variables where variable_name like "skip_show_database";
|
show variables where variable_name like "skip_show_database";
|
||||||
grant select, update, execute on test.* to mysqltest_2@localhost;
|
grant select, update, execute on test.* to mysqltest_2@localhost;
|
||||||
grant select, update on test.* to mysqltest_1@localhost;
|
grant select, update on test.* to mysqltest_1@localhost;
|
||||||
|
@ -911,7 +911,9 @@ DROP TABLE t1;
|
|||||||
# Bug #10465
|
# Bug #10465
|
||||||
#
|
#
|
||||||
|
|
||||||
|
--disable_warnings
|
||||||
CREATE TABLE t1 (GRADE DECIMAL(4) NOT NULL, PRIMARY KEY (GRADE)) ENGINE=INNODB;
|
CREATE TABLE t1 (GRADE DECIMAL(4) NOT NULL, PRIMARY KEY (GRADE)) ENGINE=INNODB;
|
||||||
|
--enable_warnings
|
||||||
INSERT INTO t1 (GRADE) VALUES (151),(252),(343);
|
INSERT INTO t1 (GRADE) VALUES (151),(252),(343);
|
||||||
SELECT GRADE FROM t1 WHERE GRADE > 160 AND GRADE < 300;
|
SELECT GRADE FROM t1 WHERE GRADE > 160 AND GRADE < 300;
|
||||||
SELECT GRADE FROM t1 WHERE GRADE= 151;
|
SELECT GRADE FROM t1 WHERE GRADE= 151;
|
||||||
|
@ -39,10 +39,11 @@
|
|||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
This function prepares memory root for further use, sets initial size of
|
This function prepares memory root for further use, sets initial size of
|
||||||
chunk for memory allocation and pre-allocates first block if specified.
|
chunk for memory allocation and pre-allocates first block if specified.
|
||||||
Altough error can happen during execution of this function if pre_alloc_size
|
Altough error can happen during execution of this function if
|
||||||
is non-0 it won't be reported. Instead it will be reported as error in first
|
pre_alloc_size is non-0 it won't be reported. Instead it will be
|
||||||
alloc_root() on this memory root.
|
reported as error in first alloc_root() on this memory root.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void init_alloc_root(MEM_ROOT *mem_root, uint block_size,
|
void init_alloc_root(MEM_ROOT *mem_root, uint block_size,
|
||||||
uint pre_alloc_size __attribute__((unused)))
|
uint pre_alloc_size __attribute__((unused)))
|
||||||
{
|
{
|
||||||
@ -71,6 +72,7 @@ void init_alloc_root(MEM_ROOT *mem_root, uint block_size,
|
|||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
reset_root_defaults()
|
reset_root_defaults()
|
||||||
|
@ -684,8 +684,10 @@ void thr_unlock(THR_LOCK_DATA *data)
|
|||||||
lock->read.last=data->prev;
|
lock->read.last=data->prev;
|
||||||
else if (lock_type == TL_WRITE_DELAYED && data->cond)
|
else if (lock_type == TL_WRITE_DELAYED && data->cond)
|
||||||
{
|
{
|
||||||
/* This only happens in extreme circumstances when a
|
/*
|
||||||
write delayed lock that is waiting for a lock */
|
This only happens in extreme circumstances when a
|
||||||
|
write delayed lock that is waiting for a lock
|
||||||
|
*/
|
||||||
lock->write_wait.last=data->prev; /* Put it on wait queue */
|
lock->write_wait.last=data->prev; /* Put it on wait queue */
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
7056
sql-bench/limits/mysql-4.1.cfg
Normal file
7056
sql-bench/limits/mysql-4.1.cfg
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -2248,8 +2248,7 @@ bool Item_param::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
|
|||||||
SELECT_LEX_UNIT::item set only for subqueries, so test of it presence
|
SELECT_LEX_UNIT::item set only for subqueries, so test of it presence
|
||||||
can be barrier to stop before derived table SELECT or very outer SELECT
|
can be barrier to stop before derived table SELECT or very outer SELECT
|
||||||
*/
|
*/
|
||||||
for(;
|
for (; cursel->master_unit()->item;
|
||||||
cursel->master_unit()->item;
|
|
||||||
cursel= cursel->outer_select())
|
cursel= cursel->outer_select())
|
||||||
{
|
{
|
||||||
Item_subselect *subselect_item= cursel->master_unit()->item;
|
Item_subselect *subselect_item= cursel->master_unit()->item;
|
||||||
@ -2530,8 +2529,7 @@ void mark_select_range_as_dependent(THD *thd,
|
|||||||
resolving)
|
resolving)
|
||||||
*/
|
*/
|
||||||
SELECT_LEX *previous_select= current_sel;
|
SELECT_LEX *previous_select= current_sel;
|
||||||
for(;
|
for (; previous_select->outer_select() != last_select;
|
||||||
previous_select->outer_select() != last_select;
|
|
||||||
previous_select= previous_select->outer_select())
|
previous_select= previous_select->outer_select())
|
||||||
{
|
{
|
||||||
Item_subselect *prev_subselect_item=
|
Item_subselect *prev_subselect_item=
|
||||||
|
@ -284,7 +284,9 @@ public:
|
|||||||
Item(THD *thd, Item *item);
|
Item(THD *thd, Item *item);
|
||||||
virtual ~Item()
|
virtual ~Item()
|
||||||
{
|
{
|
||||||
|
#ifdef EXTRA_DEBUG
|
||||||
name=0;
|
name=0;
|
||||||
|
#endif
|
||||||
} /*lint -e1509 */
|
} /*lint -e1509 */
|
||||||
void set_name(const char *str,uint length, CHARSET_INFO *cs);
|
void set_name(const char *str,uint length, CHARSET_INFO *cs);
|
||||||
void rename(char *new_name);
|
void rename(char *new_name);
|
||||||
|
@ -213,15 +213,16 @@ void Item_func::set_arguments(List<Item> &list)
|
|||||||
{
|
{
|
||||||
allowed_arg_cols= 1;
|
allowed_arg_cols= 1;
|
||||||
arg_count=list.elements;
|
arg_count=list.elements;
|
||||||
if ((args=(Item**) sql_alloc(sizeof(Item*)*arg_count)))
|
args= tmp_arg; // If 2 arguments
|
||||||
|
if (arg_count <= 2 || (args=(Item**) sql_alloc(sizeof(Item*)*arg_count)))
|
||||||
{
|
{
|
||||||
uint i=0;
|
|
||||||
List_iterator_fast<Item> li(list);
|
List_iterator_fast<Item> li(list);
|
||||||
Item *item;
|
Item *item;
|
||||||
|
Item **save_args= args;
|
||||||
|
|
||||||
while ((item=li++))
|
while ((item=li++))
|
||||||
{
|
{
|
||||||
args[i++]= item;
|
*(save_args++)= item;
|
||||||
with_sum_func|=item->with_sum_func;
|
with_sum_func|=item->with_sum_func;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -772,9 +772,8 @@ Item_in_subselect::single_value_transformer(JOIN *join,
|
|||||||
Comp_creator *func)
|
Comp_creator *func)
|
||||||
{
|
{
|
||||||
Item_subselect::trans_res result= RES_ERROR;
|
Item_subselect::trans_res result= RES_ERROR;
|
||||||
DBUG_ENTER("Item_in_subselect::single_value_transformer");
|
|
||||||
|
|
||||||
SELECT_LEX *select_lex= join->select_lex;
|
SELECT_LEX *select_lex= join->select_lex;
|
||||||
|
DBUG_ENTER("Item_in_subselect::single_value_transformer");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Check that the right part of the subselect contains no more than one
|
Check that the right part of the subselect contains no more than one
|
||||||
|
@ -242,8 +242,12 @@ Item_sum_hybrid::Item_sum_hybrid(THD *thd, Item_sum_hybrid *item)
|
|||||||
case REAL_RESULT:
|
case REAL_RESULT:
|
||||||
sum= item->sum;
|
sum= item->sum;
|
||||||
break;
|
break;
|
||||||
case STRING_RESULT: // This can happen with ROLLUP. Note that the value is already
|
case STRING_RESULT:
|
||||||
break; // copied at function call.
|
/*
|
||||||
|
This can happen with ROLLUP. Note that the value is already
|
||||||
|
copied at function call.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
case ROW_RESULT:
|
case ROW_RESULT:
|
||||||
default:
|
default:
|
||||||
DBUG_ASSERT(0);
|
DBUG_ASSERT(0);
|
||||||
|
@ -94,7 +94,7 @@ extern CHARSET_INFO *national_charset_info, *table_alias_charset;
|
|||||||
#define MAX_FIELDS_BEFORE_HASH 32
|
#define MAX_FIELDS_BEFORE_HASH 32
|
||||||
#define USER_VARS_HASH_SIZE 16
|
#define USER_VARS_HASH_SIZE 16
|
||||||
#define STACK_MIN_SIZE 8192 // Abort if less stack during eval.
|
#define STACK_MIN_SIZE 8192 // Abort if less stack during eval.
|
||||||
#define STACK_BUFF_ALLOC 64 // For stack overrun checks
|
#define STACK_BUFF_ALLOC 256 // For stack overrun checks
|
||||||
#ifndef MYSQLD_NET_RETRY_COUNT
|
#ifndef MYSQLD_NET_RETRY_COUNT
|
||||||
#define MYSQLD_NET_RETRY_COUNT 10 // Abort read after this many int.
|
#define MYSQLD_NET_RETRY_COUNT 10 // Abort read after this many int.
|
||||||
#endif
|
#endif
|
||||||
|
@ -398,7 +398,7 @@ TRP_GROUP_MIN_MAX *get_best_group_min_max(PARAM *param, SEL_TREE *tree);
|
|||||||
static int get_index_merge_params(PARAM *param, key_map& needed_reg,
|
static int get_index_merge_params(PARAM *param, key_map& needed_reg,
|
||||||
SEL_IMERGE *imerge, double *read_time,
|
SEL_IMERGE *imerge, double *read_time,
|
||||||
ha_rows* imerge_rows);
|
ha_rows* imerge_rows);
|
||||||
inline double get_index_only_read_time(const PARAM* param, ha_rows records,
|
static double get_index_only_read_time(const PARAM* param, ha_rows records,
|
||||||
int keynr);
|
int keynr);
|
||||||
|
|
||||||
#ifndef DBUG_OFF
|
#ifndef DBUG_OFF
|
||||||
@ -1117,6 +1117,7 @@ int QUICK_ROR_UNION_SELECT::init()
|
|||||||
val1 First merged select
|
val1 First merged select
|
||||||
val2 Second merged select
|
val2 Second merged select
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int QUICK_ROR_UNION_SELECT::queue_cmp(void *arg, byte *val1, byte *val2)
|
int QUICK_ROR_UNION_SELECT::queue_cmp(void *arg, byte *val1, byte *val2)
|
||||||
{
|
{
|
||||||
QUICK_ROR_UNION_SELECT *self= (QUICK_ROR_UNION_SELECT*)arg;
|
QUICK_ROR_UNION_SELECT *self= (QUICK_ROR_UNION_SELECT*)arg;
|
||||||
@ -1748,18 +1749,20 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use,
|
|||||||
double best_read_time= read_time;
|
double best_read_time= read_time;
|
||||||
|
|
||||||
if (cond)
|
if (cond)
|
||||||
tree= get_mm_tree(¶m,cond);
|
{
|
||||||
|
if ((tree= get_mm_tree(¶m,cond)))
|
||||||
if (tree && tree->type == SEL_TREE::IMPOSSIBLE)
|
{
|
||||||
|
if (tree->type == SEL_TREE::IMPOSSIBLE)
|
||||||
{
|
{
|
||||||
records=0L; /* Return -1 from this function. */
|
records=0L; /* Return -1 from this function. */
|
||||||
read_time= (double) HA_POS_ERROR;
|
read_time= (double) HA_POS_ERROR;
|
||||||
goto free_mem;
|
goto free_mem;
|
||||||
}
|
}
|
||||||
else if (tree && tree->type != SEL_TREE::KEY &&
|
if (tree->type != SEL_TREE::KEY &&
|
||||||
tree->type != SEL_TREE::KEY_SMALLER)
|
tree->type != SEL_TREE::KEY_SMALLER)
|
||||||
goto free_mem;
|
goto free_mem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Try to construct a QUICK_GROUP_MIN_MAX_SELECT.
|
Try to construct a QUICK_GROUP_MIN_MAX_SELECT.
|
||||||
@ -2250,7 +2253,7 @@ skip_to_ror_scan:
|
|||||||
clustered index)
|
clustered index)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
inline double get_index_only_read_time(const PARAM* param, ha_rows records,
|
static double get_index_only_read_time(const PARAM* param, ha_rows records,
|
||||||
int keynr)
|
int keynr)
|
||||||
{
|
{
|
||||||
double read_time;
|
double read_time;
|
||||||
@ -2296,6 +2299,7 @@ typedef struct st_ror_scan_info
|
|||||||
param Parameter from test_quick_select function
|
param Parameter from test_quick_select function
|
||||||
idx Index of key in param->keys
|
idx Index of key in param->keys
|
||||||
sel_arg Set of intervals for a given key
|
sel_arg Set of intervals for a given key
|
||||||
|
|
||||||
RETURN
|
RETURN
|
||||||
NULL - out of memory
|
NULL - out of memory
|
||||||
ROR scan structure containing a scan for {idx, sel_arg}
|
ROR scan structure containing a scan for {idx, sel_arg}
|
||||||
@ -2308,14 +2312,15 @@ ROR_SCAN_INFO *make_ror_scan(const PARAM *param, int idx, SEL_ARG *sel_arg)
|
|||||||
uchar *bitmap_buf;
|
uchar *bitmap_buf;
|
||||||
uint keynr;
|
uint keynr;
|
||||||
DBUG_ENTER("make_ror_scan");
|
DBUG_ENTER("make_ror_scan");
|
||||||
|
|
||||||
if (!(ror_scan= (ROR_SCAN_INFO*)alloc_root(param->mem_root,
|
if (!(ror_scan= (ROR_SCAN_INFO*)alloc_root(param->mem_root,
|
||||||
sizeof(ROR_SCAN_INFO))))
|
sizeof(ROR_SCAN_INFO))))
|
||||||
DBUG_RETURN(NULL);
|
DBUG_RETURN(NULL);
|
||||||
|
|
||||||
ror_scan->idx= idx;
|
ror_scan->idx= idx;
|
||||||
ror_scan->keynr= keynr= param->real_keynr[idx];
|
ror_scan->keynr= keynr= param->real_keynr[idx];
|
||||||
ror_scan->key_rec_length= param->table->key_info[keynr].key_length +
|
ror_scan->key_rec_length= (param->table->key_info[keynr].key_length +
|
||||||
param->table->file->ref_length;
|
param->table->file->ref_length);
|
||||||
ror_scan->sel_arg= sel_arg;
|
ror_scan->sel_arg= sel_arg;
|
||||||
ror_scan->records= param->table->quick_rows[keynr];
|
ror_scan->records= param->table->quick_rows[keynr];
|
||||||
|
|
||||||
@ -2331,15 +2336,11 @@ ROR_SCAN_INFO *make_ror_scan(const PARAM *param, int idx, SEL_ARG *sel_arg)
|
|||||||
KEY_PART_INFO *key_part= param->table->key_info[keynr].key_part;
|
KEY_PART_INFO *key_part= param->table->key_info[keynr].key_part;
|
||||||
KEY_PART_INFO *key_part_end= key_part +
|
KEY_PART_INFO *key_part_end= key_part +
|
||||||
param->table->key_info[keynr].key_parts;
|
param->table->key_info[keynr].key_parts;
|
||||||
uint n_used_covered= 0;
|
|
||||||
for (;key_part != key_part_end; ++key_part)
|
for (;key_part != key_part_end; ++key_part)
|
||||||
{
|
{
|
||||||
if (bitmap_is_set(¶m->needed_fields, key_part->fieldnr))
|
if (bitmap_is_set(¶m->needed_fields, key_part->fieldnr))
|
||||||
{
|
|
||||||
n_used_covered++;
|
|
||||||
bitmap_set_bit(&ror_scan->covered_fields, key_part->fieldnr);
|
bitmap_set_bit(&ror_scan->covered_fields, key_part->fieldnr);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
ror_scan->index_read_cost=
|
ror_scan->index_read_cost=
|
||||||
get_index_only_read_time(param, param->table->quick_rows[ror_scan->keynr],
|
get_index_only_read_time(param, param->table->quick_rows[ror_scan->keynr],
|
||||||
ror_scan->keynr);
|
ror_scan->keynr);
|
||||||
@ -2359,6 +2360,7 @@ ROR_SCAN_INFO *make_ror_scan(const PARAM *param, int idx, SEL_ARG *sel_arg)
|
|||||||
0 a = b
|
0 a = b
|
||||||
1 a > b
|
1 a > b
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int cmp_ror_scan_info(ROR_SCAN_INFO** a, ROR_SCAN_INFO** b)
|
static int cmp_ror_scan_info(ROR_SCAN_INFO** a, ROR_SCAN_INFO** b)
|
||||||
{
|
{
|
||||||
double val1= rows2double((*a)->records) * (*a)->key_rec_length;
|
double val1= rows2double((*a)->records) * (*a)->key_rec_length;
|
||||||
@ -2382,6 +2384,7 @@ static int cmp_ror_scan_info(ROR_SCAN_INFO** a, ROR_SCAN_INFO** b)
|
|||||||
0 a = b
|
0 a = b
|
||||||
1 a > b
|
1 a > b
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int cmp_ror_scan_info_covering(ROR_SCAN_INFO** a, ROR_SCAN_INFO** b)
|
static int cmp_ror_scan_info_covering(ROR_SCAN_INFO** a, ROR_SCAN_INFO** b)
|
||||||
{
|
{
|
||||||
if ((*a)->used_fields_covered > (*b)->used_fields_covered)
|
if ((*a)->used_fields_covered > (*b)->used_fields_covered)
|
||||||
@ -2399,6 +2402,7 @@ static int cmp_ror_scan_info_covering(ROR_SCAN_INFO** a, ROR_SCAN_INFO** b)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Auxiliary structure for incremental ROR-intersection creation */
|
/* Auxiliary structure for incremental ROR-intersection creation */
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
@ -2464,6 +2468,8 @@ void ror_intersect_cpy(ROR_INTERSECT_INFO *dst, const ROR_INTERSECT_INFO *src)
|
|||||||
dst->index_scan_costs= src->index_scan_costs;
|
dst->index_scan_costs= src->index_scan_costs;
|
||||||
dst->total_cost= src->total_cost;
|
dst->total_cost= src->total_cost;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Get selectivity of a ROR scan wrt ROR-intersection.
|
Get selectivity of a ROR scan wrt ROR-intersection.
|
||||||
|
|
||||||
@ -2481,7 +2487,7 @@ void ror_intersect_cpy(ROR_INTERSECT_INFO *dst, const ROR_INTERSECT_INFO *src)
|
|||||||
|
|
||||||
where k_ij may be the same as any k_pq (i.e. keys may have common parts).
|
where k_ij may be the same as any k_pq (i.e. keys may have common parts).
|
||||||
|
|
||||||
A full row is retrieved iff entire cond holds.
|
A full row is retrieved if entire condition holds.
|
||||||
|
|
||||||
The recursive procedure for finding P(cond) is as follows:
|
The recursive procedure for finding P(cond) is as follows:
|
||||||
|
|
||||||
@ -2539,7 +2545,7 @@ void ror_intersect_cpy(ROR_INTERSECT_INFO *dst, const ROR_INTERSECT_INFO *src)
|
|||||||
( this is result of application of option b) of the recursion step for
|
( this is result of application of option b) of the recursion step for
|
||||||
parts of a single key).
|
parts of a single key).
|
||||||
Since it is reasonable to expect that most of the fields are not marked
|
Since it is reasonable to expect that most of the fields are not marked
|
||||||
as fixed, we calcualate (3) as
|
as fixed, we calculate (3) as
|
||||||
|
|
||||||
n_{i1} n_{i_2}
|
n_{i1} n_{i_2}
|
||||||
(3) = n_{max_key_part} / ( --------- * --------- * .... )
|
(3) = n_{max_key_part} / ( --------- * --------- * .... )
|
||||||
@ -2573,33 +2579,32 @@ static double ror_scan_selectivity(const ROR_INTERSECT_INFO *info,
|
|||||||
max_range.key= (byte*) key_val;
|
max_range.key= (byte*) key_val;
|
||||||
max_range.flag= HA_READ_AFTER_KEY;
|
max_range.flag= HA_READ_AFTER_KEY;
|
||||||
ha_rows prev_records= info->param->table->file->records;
|
ha_rows prev_records= info->param->table->file->records;
|
||||||
int i;
|
|
||||||
DBUG_ENTER("ror_intersect_selectivity");
|
DBUG_ENTER("ror_intersect_selectivity");
|
||||||
for(i= 0, sel_arg= scan->sel_arg; sel_arg;
|
|
||||||
i++, sel_arg= sel_arg->next_key_part)
|
for (sel_arg= scan->sel_arg; sel_arg;
|
||||||
|
sel_arg= sel_arg->next_key_part)
|
||||||
{
|
{
|
||||||
DBUG_PRINT("info",("sel_arg step"));
|
DBUG_PRINT("info",("sel_arg step"));
|
||||||
cur_covered= test(bitmap_is_set(&info->covered_fields,
|
cur_covered= test(bitmap_is_set(&info->covered_fields,
|
||||||
(key_part + i)->fieldnr));
|
key_part[sel_arg->part].fieldnr));
|
||||||
if (cur_covered != prev_covered)
|
if (cur_covered != prev_covered)
|
||||||
{
|
{
|
||||||
/* create (part1val, ..., part{n-1}val) tuple. */
|
/* create (part1val, ..., part{n-1}val) tuple. */
|
||||||
{
|
ha_rows records;
|
||||||
if (!tuple_arg)
|
if (!tuple_arg)
|
||||||
{
|
{
|
||||||
tuple_arg= scan->sel_arg;
|
tuple_arg= scan->sel_arg;
|
||||||
|
/* Here we use the length of the first key part */
|
||||||
tuple_arg->store_min(key_part->length, &key_ptr, 0);
|
tuple_arg->store_min(key_part->length, &key_ptr, 0);
|
||||||
}
|
}
|
||||||
while (tuple_arg->next_key_part != sel_arg)
|
while (tuple_arg->next_key_part != sel_arg)
|
||||||
{
|
{
|
||||||
tuple_arg= tuple_arg->next_key_part;
|
tuple_arg= tuple_arg->next_key_part;
|
||||||
tuple_arg->store_min(key_part->length, &key_ptr, 0);
|
tuple_arg->store_min(key_part[tuple_arg->part].length, &key_ptr, 0);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
ha_rows records;
|
|
||||||
min_range.length= max_range.length= ((char*) key_ptr - (char*) key_val);
|
min_range.length= max_range.length= ((char*) key_ptr - (char*) key_val);
|
||||||
records= info->param->table->file->
|
records= (info->param->table->file->
|
||||||
records_in_range(scan->keynr, &min_range, &max_range);
|
records_in_range(scan->keynr, &min_range, &max_range));
|
||||||
if (cur_covered)
|
if (cur_covered)
|
||||||
{
|
{
|
||||||
/* uncovered -> covered */
|
/* uncovered -> covered */
|
||||||
@ -2627,6 +2632,7 @@ static double ror_scan_selectivity(const ROR_INTERSECT_INFO *info,
|
|||||||
DBUG_RETURN(selectivity_mult);
|
DBUG_RETURN(selectivity_mult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Check if adding a ROR scan to a ROR-intersection reduces its cost of
|
Check if adding a ROR scan to a ROR-intersection reduces its cost of
|
||||||
ROR-intersection and if yes, update parameters of ROR-intersection,
|
ROR-intersection and if yes, update parameters of ROR-intersection,
|
||||||
@ -4198,7 +4204,7 @@ key_and(SEL_ARG *key1,SEL_ARG *key2,uint clone_flag)
|
|||||||
clone_flag=swap_clone_flag(clone_flag);
|
clone_flag=swap_clone_flag(clone_flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If one of the key is MAYBE_KEY then the found region may be smaller
|
/* If one of the key is MAYBE_KEY then the found region may be smaller */
|
||||||
if (key2->type == SEL_ARG::MAYBE_KEY)
|
if (key2->type == SEL_ARG::MAYBE_KEY)
|
||||||
{
|
{
|
||||||
if (key1->use_count > 1)
|
if (key1->use_count > 1)
|
||||||
@ -7325,8 +7331,8 @@ check_group_min_max_predicates(COND *cond, Item_field *min_max_arg_item,
|
|||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
Test conditions (NGA1, NGA2) from get_best_group_min_max(). Namely,
|
Test conditions (NGA1, NGA2) from get_best_group_min_max(). Namely,
|
||||||
for each keypart field NGF_i not in GROUP-BY, check that there is a constant
|
for each keypart field NGF_i not in GROUP-BY, check that there is a
|
||||||
equality predicate among conds with the form (NGF_i = const_ci) or
|
constant equality predicate among conds with the form (NGF_i = const_ci) or
|
||||||
(const_ci = NGF_i).
|
(const_ci = NGF_i).
|
||||||
Thus all the NGF_i attributes must fill the 'gap' between the last group-by
|
Thus all the NGF_i attributes must fill the 'gap' between the last group-by
|
||||||
attribute and the MIN/MAX attribute in the index (if present). If these
|
attribute and the MIN/MAX attribute in the index (if present). If these
|
||||||
@ -7959,7 +7965,7 @@ void QUICK_GROUP_MIN_MAX_SELECT::update_key_stat()
|
|||||||
max_used_key_length= real_prefix_len;
|
max_used_key_length= real_prefix_len;
|
||||||
if (min_max_ranges.elements > 0)
|
if (min_max_ranges.elements > 0)
|
||||||
{
|
{
|
||||||
QUICK_RANGE *cur_range= 0;
|
QUICK_RANGE *cur_range;
|
||||||
if (have_min)
|
if (have_min)
|
||||||
{ /* Check if the right-most range has a lower boundary. */
|
{ /* Check if the right-most range has a lower boundary. */
|
||||||
get_dynamic(&min_max_ranges, (gptr)&cur_range,
|
get_dynamic(&min_max_ranges, (gptr)&cur_range,
|
||||||
@ -7967,7 +7973,7 @@ void QUICK_GROUP_MIN_MAX_SELECT::update_key_stat()
|
|||||||
if (!(cur_range->flag & NO_MIN_RANGE))
|
if (!(cur_range->flag & NO_MIN_RANGE))
|
||||||
{
|
{
|
||||||
max_used_key_length+= min_max_arg_len;
|
max_used_key_length+= min_max_arg_len;
|
||||||
++used_key_parts;
|
used_key_parts++;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -7977,7 +7983,7 @@ void QUICK_GROUP_MIN_MAX_SELECT::update_key_stat()
|
|||||||
if (!(cur_range->flag & NO_MAX_RANGE))
|
if (!(cur_range->flag & NO_MAX_RANGE))
|
||||||
{
|
{
|
||||||
max_used_key_length+= min_max_arg_len;
|
max_used_key_length+= min_max_arg_len;
|
||||||
++used_key_parts;
|
used_key_parts++;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -7994,7 +8000,7 @@ void QUICK_GROUP_MIN_MAX_SELECT::update_key_stat()
|
|||||||
usable key length.
|
usable key length.
|
||||||
*/
|
*/
|
||||||
max_used_key_length+= min_max_arg_len;
|
max_used_key_length+= min_max_arg_len;
|
||||||
++used_key_parts;
|
used_key_parts++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1904,15 +1904,14 @@ sp_instr_copen::execute(THD *thd, uint *nextp)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
sp_lex_keeper *lex_keeper= c->pre_open(thd);
|
sp_lex_keeper *lex_keeper= c->pre_open(thd);
|
||||||
|
if (!lex_keeper) // cursor already open or OOM
|
||||||
if (!lex_keeper)
|
|
||||||
{
|
{
|
||||||
res= -1;
|
res= -1;
|
||||||
*nextp= m_ip+1;
|
*nextp= m_ip+1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
res= lex_keeper->reset_lex_and_exec_core(thd, nextp, FALSE, this);
|
res= lex_keeper->reset_lex_and_exec_core(thd, nextp, FALSE, this);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Work around the fact that errors in selects are not returned properly
|
Work around the fact that errors in selects are not returned properly
|
||||||
(but instead converted into a warning), so if a condition handler
|
(but instead converted into a warning), so if a condition handler
|
||||||
@ -1925,12 +1924,13 @@ sp_instr_copen::execute(THD *thd, uint *nextp)
|
|||||||
if (thd->spcont->found_handler(&dummy1, &dummy2))
|
if (thd->spcont->found_handler(&dummy1, &dummy2))
|
||||||
res= -1;
|
res= -1;
|
||||||
}
|
}
|
||||||
c->post_open(thd, (lex_keeper && !res ? TRUE : FALSE));
|
c->post_open(thd, res ? FALSE : TRUE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DBUG_RETURN(res);
|
DBUG_RETURN(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
sp_instr_copen::exec_core(THD *thd, uint *nextp)
|
sp_instr_copen::exec_core(THD *thd, uint *nextp)
|
||||||
{
|
{
|
||||||
|
@ -168,8 +168,22 @@ sp_rcontext::pop_cursors(uint count)
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// We have split this in two to make it easy for sp_instr_copen
|
/*
|
||||||
// to reuse the sp_instr::exec_stmt() code.
|
pre_open cursor
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
pre_open()
|
||||||
|
THD Thread handler
|
||||||
|
|
||||||
|
NOTES
|
||||||
|
We have to open cursor in two steps to make it easy for sp_instr_copen
|
||||||
|
to reuse the sp_instr::exec_stmt() code.
|
||||||
|
If this function returns 0, post_open should not be called
|
||||||
|
|
||||||
|
RETURN
|
||||||
|
0 ERROR
|
||||||
|
*/
|
||||||
|
|
||||||
sp_lex_keeper*
|
sp_lex_keeper*
|
||||||
sp_cursor::pre_open(THD *thd)
|
sp_cursor::pre_open(THD *thd)
|
||||||
{
|
{
|
||||||
@ -179,31 +193,30 @@ sp_cursor::pre_open(THD *thd)
|
|||||||
MYF(0));
|
MYF(0));
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bzero((char *)&m_mem_root, sizeof(m_mem_root));
|
|
||||||
init_alloc_root(&m_mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
|
init_alloc_root(&m_mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
|
||||||
if ((m_prot= new Protocol_cursor(thd, &m_mem_root)) == NULL)
|
if ((m_prot= new Protocol_cursor(thd, &m_mem_root)) == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
m_oprot= thd->protocol; // Save the original protocol
|
/* Save for execution. Will be restored in post_open */
|
||||||
thd->protocol= m_prot;
|
m_oprot= thd->protocol;
|
||||||
|
|
||||||
m_nseof= thd->net.no_send_eof;
|
m_nseof= thd->net.no_send_eof;
|
||||||
|
|
||||||
|
/* Change protocol for execution */
|
||||||
|
thd->protocol= m_prot;
|
||||||
thd->net.no_send_eof= TRUE;
|
thd->net.no_send_eof= TRUE;
|
||||||
return m_lex_keeper;
|
return m_lex_keeper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
sp_cursor::post_open(THD *thd, my_bool was_opened)
|
sp_cursor::post_open(THD *thd, my_bool was_opened)
|
||||||
{
|
{
|
||||||
thd->net.no_send_eof= m_nseof; // Restore the originals
|
thd->net.no_send_eof= m_nseof; // Restore the originals
|
||||||
thd->protocol= m_oprot;
|
thd->protocol= m_oprot;
|
||||||
if (was_opened)
|
if ((m_isopen= was_opened))
|
||||||
{
|
|
||||||
m_isopen= was_opened;
|
|
||||||
m_current_row= m_prot->data;
|
m_current_row= m_prot->data;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
int
|
int
|
||||||
sp_cursor::close(THD *thd)
|
sp_cursor::close(THD *thd)
|
||||||
@ -217,6 +230,7 @@ sp_cursor::close(THD *thd)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
sp_cursor::destroy()
|
sp_cursor::destroy()
|
||||||
{
|
{
|
||||||
@ -225,7 +239,6 @@ sp_cursor::destroy()
|
|||||||
delete m_prot;
|
delete m_prot;
|
||||||
m_prot= NULL;
|
m_prot= NULL;
|
||||||
free_root(&m_mem_root, MYF(0));
|
free_root(&m_mem_root, MYF(0));
|
||||||
bzero((char *)&m_mem_root, sizeof(m_mem_root));
|
|
||||||
}
|
}
|
||||||
m_isopen= FALSE;
|
m_isopen= FALSE;
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,7 @@ public:
|
|||||||
if (sizeof(buffer) >= 8)
|
if (sizeof(buffer) >= 8)
|
||||||
return uint8korr(buffer);
|
return uint8korr(buffer);
|
||||||
DBUG_ASSERT(sizeof(buffer) >= 4);
|
DBUG_ASSERT(sizeof(buffer) >= 4);
|
||||||
uint4korr(buffer);
|
return (ulonglong) uint4korr(buffer);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1855,7 +1855,8 @@ class multi_delete :public select_result_interceptor
|
|||||||
ha_rows deleted, found;
|
ha_rows deleted, found;
|
||||||
uint num_of_tables;
|
uint num_of_tables;
|
||||||
int error;
|
int error;
|
||||||
bool do_delete, transactional_tables, normal_tables;
|
bool do_delete, transactional_tables, normal_tables, delete_while_scanning;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
multi_delete(THD *thd, TABLE_LIST *dt, uint num_of_tables);
|
multi_delete(THD *thd, TABLE_LIST *dt, uint num_of_tables);
|
||||||
~multi_delete();
|
~multi_delete();
|
||||||
@ -1863,7 +1864,7 @@ public:
|
|||||||
bool send_data(List<Item> &items);
|
bool send_data(List<Item> &items);
|
||||||
bool initialize_tables (JOIN *join);
|
bool initialize_tables (JOIN *join);
|
||||||
void send_error(uint errcode,const char *err);
|
void send_error(uint errcode,const char *err);
|
||||||
int do_deletes (bool from_send_error);
|
int do_deletes();
|
||||||
bool send_eof();
|
bool send_eof();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -410,7 +410,7 @@ multi_delete::multi_delete(THD *thd_arg, TABLE_LIST *dt,
|
|||||||
num_of_tables(num_of_tables_arg), error(0),
|
num_of_tables(num_of_tables_arg), error(0),
|
||||||
do_delete(0), transactional_tables(0), normal_tables(0)
|
do_delete(0), transactional_tables(0), normal_tables(0)
|
||||||
{
|
{
|
||||||
tempfiles = (Unique **) sql_calloc(sizeof(Unique *) * (num_of_tables-1));
|
tempfiles= (Unique **) sql_calloc(sizeof(Unique *) * num_of_tables);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -440,6 +440,7 @@ multi_delete::initialize_tables(JOIN *join)
|
|||||||
tables_to_delete_from|= walk->table->map;
|
tables_to_delete_from|= walk->table->map;
|
||||||
|
|
||||||
walk= delete_tables;
|
walk= delete_tables;
|
||||||
|
delete_while_scanning= 1;
|
||||||
for (JOIN_TAB *tab=join->join_tab, *end=join->join_tab+join->tables;
|
for (JOIN_TAB *tab=join->join_tab, *end=join->join_tab+join->tables;
|
||||||
tab < end;
|
tab < end;
|
||||||
tab++)
|
tab++)
|
||||||
@ -459,10 +460,25 @@ multi_delete::initialize_tables(JOIN *join)
|
|||||||
else
|
else
|
||||||
normal_tables= 1;
|
normal_tables= 1;
|
||||||
}
|
}
|
||||||
|
else if ((tab->type != JT_SYSTEM && tab->type != JT_CONST) &&
|
||||||
|
walk == delete_tables)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
We are not deleting from the table we are scanning. In this
|
||||||
|
case send_data() shouldn't delete any rows a we may touch
|
||||||
|
the rows in the deleted table many times
|
||||||
|
*/
|
||||||
|
delete_while_scanning= 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
walk= delete_tables;
|
walk= delete_tables;
|
||||||
tempfiles_ptr= tempfiles;
|
tempfiles_ptr= tempfiles;
|
||||||
for (walk= walk->next_local ;walk ;walk= walk->next_local)
|
if (delete_while_scanning)
|
||||||
|
{
|
||||||
|
table_being_deleted= delete_tables;
|
||||||
|
walk= walk->next_local;
|
||||||
|
}
|
||||||
|
for (;walk ;walk= walk->next_local)
|
||||||
{
|
{
|
||||||
TABLE *table=walk->table;
|
TABLE *table=walk->table;
|
||||||
*tempfiles_ptr++= new Unique (refpos_order_cmp,
|
*tempfiles_ptr++= new Unique (refpos_order_cmp,
|
||||||
@ -481,12 +497,12 @@ multi_delete::~multi_delete()
|
|||||||
table_being_deleted;
|
table_being_deleted;
|
||||||
table_being_deleted= table_being_deleted->next_local)
|
table_being_deleted= table_being_deleted->next_local)
|
||||||
{
|
{
|
||||||
TABLE *t=table_being_deleted->table;
|
TABLE *table= table_being_deleted->table;
|
||||||
free_io_cache(t); // Alloced by unique
|
free_io_cache(table); // Alloced by unique
|
||||||
t->no_keyread=0;
|
table->no_keyread=0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint counter= 0; counter < num_of_tables-1; counter++)
|
for (uint counter= 0; counter < num_of_tables; counter++)
|
||||||
{
|
{
|
||||||
if (tempfiles[counter])
|
if (tempfiles[counter])
|
||||||
delete tempfiles[counter];
|
delete tempfiles[counter];
|
||||||
@ -496,14 +512,15 @@ multi_delete::~multi_delete()
|
|||||||
|
|
||||||
bool multi_delete::send_data(List<Item> &values)
|
bool multi_delete::send_data(List<Item> &values)
|
||||||
{
|
{
|
||||||
int secure_counter= -1;
|
int secure_counter= delete_while_scanning ? -1 : 0;
|
||||||
|
TABLE_LIST *del_table;
|
||||||
DBUG_ENTER("multi_delete::send_data");
|
DBUG_ENTER("multi_delete::send_data");
|
||||||
|
|
||||||
for (table_being_deleted= delete_tables;
|
for (del_table= delete_tables;
|
||||||
table_being_deleted;
|
del_table;
|
||||||
table_being_deleted= table_being_deleted->next_local, secure_counter++)
|
del_table= del_table->next_local, secure_counter++)
|
||||||
{
|
{
|
||||||
TABLE *table=table_being_deleted->table;
|
TABLE *table= del_table->table;
|
||||||
|
|
||||||
/* Check if we are using outer join and we didn't find the row */
|
/* Check if we are using outer join and we didn't find the row */
|
||||||
if (table->status & (STATUS_NULL_ROW | STATUS_DELETED))
|
if (table->status & (STATUS_NULL_ROW | STATUS_DELETED))
|
||||||
@ -514,7 +531,8 @@ bool multi_delete::send_data(List<Item> &values)
|
|||||||
|
|
||||||
if (secure_counter < 0)
|
if (secure_counter < 0)
|
||||||
{
|
{
|
||||||
/* If this is the table we are scanning */
|
/* We are scanning the current table */
|
||||||
|
DBUG_ASSERT(del_table == table_being_deleted);
|
||||||
if (table->triggers &&
|
if (table->triggers &&
|
||||||
table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
|
table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
|
||||||
TRG_ACTION_BEFORE, FALSE))
|
TRG_ACTION_BEFORE, FALSE))
|
||||||
@ -528,8 +546,7 @@ bool multi_delete::send_data(List<Item> &values)
|
|||||||
TRG_ACTION_AFTER, FALSE))
|
TRG_ACTION_AFTER, FALSE))
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
}
|
}
|
||||||
else if (!table_being_deleted->next_local ||
|
else
|
||||||
table_being_deleted->table->file->has_transactions())
|
|
||||||
{
|
{
|
||||||
table->file->print_error(error,MYF(0));
|
table->file->print_error(error,MYF(0));
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
@ -540,7 +557,7 @@ bool multi_delete::send_data(List<Item> &values)
|
|||||||
error=tempfiles[secure_counter]->unique_add((char*) table->file->ref);
|
error=tempfiles[secure_counter]->unique_add((char*) table->file->ref);
|
||||||
if (error)
|
if (error)
|
||||||
{
|
{
|
||||||
error=-1;
|
error= 1; // Fatal error
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -563,22 +580,24 @@ void multi_delete::send_error(uint errcode,const char *err)
|
|||||||
/* Something already deleted so we have to invalidate cache */
|
/* Something already deleted so we have to invalidate cache */
|
||||||
query_cache_invalidate3(thd, delete_tables, 1);
|
query_cache_invalidate3(thd, delete_tables, 1);
|
||||||
|
|
||||||
/* Below can happen when thread is killed early ... */
|
|
||||||
if (!table_being_deleted)
|
|
||||||
table_being_deleted=delete_tables;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
If rows from the first table only has been deleted and it is
|
If rows from the first table only has been deleted and it is
|
||||||
transactional, just do rollback.
|
transactional, just do rollback.
|
||||||
The same if all tables are transactional, regardless of where we are.
|
The same if all tables are transactional, regardless of where we are.
|
||||||
In all other cases do attempt deletes ...
|
In all other cases do attempt deletes ...
|
||||||
*/
|
*/
|
||||||
if ((table_being_deleted->table->file->has_transactions() &&
|
if ((table_being_deleted == delete_tables &&
|
||||||
table_being_deleted == delete_tables) || !normal_tables)
|
table_being_deleted->table->file->has_transactions()) ||
|
||||||
|
!normal_tables)
|
||||||
ha_rollback_stmt(thd);
|
ha_rollback_stmt(thd);
|
||||||
else if (do_delete)
|
else if (do_delete)
|
||||||
{
|
{
|
||||||
VOID(do_deletes(1));
|
/*
|
||||||
|
We have to execute the recorded do_deletes() and write info into the
|
||||||
|
error log
|
||||||
|
*/
|
||||||
|
error= 1;
|
||||||
|
send_eof();
|
||||||
}
|
}
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
@ -591,27 +610,20 @@ void multi_delete::send_error(uint errcode,const char *err)
|
|||||||
1 error
|
1 error
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int multi_delete::do_deletes(bool from_send_error)
|
int multi_delete::do_deletes()
|
||||||
{
|
{
|
||||||
int local_error= 0, counter= 0;
|
int local_error= 0, counter= 0;
|
||||||
DBUG_ENTER("do_deletes");
|
DBUG_ENTER("do_deletes");
|
||||||
|
DBUG_ASSERT(do_delete);
|
||||||
|
|
||||||
if (from_send_error)
|
do_delete= 0; // Mark called
|
||||||
{
|
|
||||||
/* Found out table number for 'table_being_deleted*/
|
|
||||||
for (TABLE_LIST *aux= delete_tables;
|
|
||||||
aux != table_being_deleted;
|
|
||||||
aux= aux->next_local)
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
table_being_deleted = delete_tables;
|
|
||||||
|
|
||||||
do_delete= 0;
|
|
||||||
if (!found)
|
if (!found)
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
for (table_being_deleted= table_being_deleted->next_local;
|
|
||||||
table_being_deleted;
|
table_being_deleted= (delete_while_scanning ? delete_tables->next_local :
|
||||||
|
delete_tables);
|
||||||
|
|
||||||
|
for (; table_being_deleted;
|
||||||
table_being_deleted= table_being_deleted->next_local, counter++)
|
table_being_deleted= table_being_deleted->next_local, counter++)
|
||||||
{
|
{
|
||||||
TABLE *table = table_being_deleted->table;
|
TABLE *table = table_being_deleted->table;
|
||||||
@ -673,7 +685,7 @@ bool multi_delete::send_eof()
|
|||||||
thd->proc_info="deleting from reference tables";
|
thd->proc_info="deleting from reference tables";
|
||||||
|
|
||||||
/* Does deletes for the last n - 1 tables, returns 0 if ok */
|
/* Does deletes for the last n - 1 tables, returns 0 if ok */
|
||||||
int local_error= do_deletes(0); // returns 0 if success
|
int local_error= do_deletes(); // returns 0 if success
|
||||||
|
|
||||||
/* reset used flags */
|
/* reset used flags */
|
||||||
thd->proc_info="end";
|
thd->proc_info="end";
|
||||||
|
@ -128,7 +128,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
|
|||||||
/* it is join view => we need to find table for update */
|
/* it is join view => we need to find table for update */
|
||||||
List_iterator_fast<Item> it(fields);
|
List_iterator_fast<Item> it(fields);
|
||||||
Item *item;
|
Item *item;
|
||||||
TABLE_LIST *tbl= 0;
|
TABLE_LIST *tbl= 0; // reset for call to check_single_table()
|
||||||
table_map map= 0;
|
table_map map= 0;
|
||||||
|
|
||||||
while ((item= it++))
|
while ((item= it++))
|
||||||
@ -1012,6 +1012,7 @@ ok_or_after_trg_err:
|
|||||||
|
|
||||||
err:
|
err:
|
||||||
info->last_errno= error;
|
info->last_errno= error;
|
||||||
|
thd->lex->current_select->no_error= 0; // Give error
|
||||||
table->file->print_error(error,MYF(0));
|
table->file->print_error(error,MYF(0));
|
||||||
|
|
||||||
before_trg_err:
|
before_trg_err:
|
||||||
|
@ -4537,7 +4537,8 @@ unsent_create_error:
|
|||||||
send_ok(thd);
|
send_ok(thd);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (thd->transaction.xa_state == XA_IDLE && thd->lex->xa_opt == XA_ONE_PHASE)
|
if (thd->transaction.xa_state == XA_IDLE &&
|
||||||
|
thd->lex->xa_opt == XA_ONE_PHASE)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
if ((r= ha_commit(thd)))
|
if ((r= ha_commit(thd)))
|
||||||
@ -4545,8 +4546,8 @@ unsent_create_error:
|
|||||||
else
|
else
|
||||||
send_ok(thd);
|
send_ok(thd);
|
||||||
}
|
}
|
||||||
else
|
else if (thd->transaction.xa_state == XA_PREPARED &&
|
||||||
if (thd->transaction.xa_state == XA_PREPARED && thd->lex->xa_opt == XA_NONE)
|
thd->lex->xa_opt == XA_NONE)
|
||||||
{
|
{
|
||||||
if (wait_if_global_read_lock(thd, 0, 0))
|
if (wait_if_global_read_lock(thd, 0, 0))
|
||||||
{
|
{
|
||||||
|
@ -957,14 +957,16 @@ JOIN::optimize()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
DBUG_EXECUTE("info",TEST_join(this););
|
DBUG_EXECUTE("info",TEST_join(this););
|
||||||
|
|
||||||
|
if (const_tables != tables)
|
||||||
|
{
|
||||||
/*
|
/*
|
||||||
Because filesort always does a full table scan or a quick range scan
|
Because filesort always does a full table scan or a quick range scan
|
||||||
we must add the removed reference to the select for the table.
|
we must add the removed reference to the select for the table.
|
||||||
We only need to do this when we have a simple_order or simple_group
|
We only need to do this when we have a simple_order or simple_group
|
||||||
as in other cases the join is done before the sort.
|
as in other cases the join is done before the sort.
|
||||||
*/
|
*/
|
||||||
if (const_tables != tables &&
|
if ((order || group_list) &&
|
||||||
(order || group_list) &&
|
|
||||||
join_tab[const_tables].type != JT_ALL &&
|
join_tab[const_tables].type != JT_ALL &&
|
||||||
join_tab[const_tables].type != JT_FT &&
|
join_tab[const_tables].type != JT_FT &&
|
||||||
join_tab[const_tables].type != JT_REF_OR_NULL &&
|
join_tab[const_tables].type != JT_REF_OR_NULL &&
|
||||||
@ -975,7 +977,7 @@ JOIN::optimize()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!(select_options & SELECT_BIG_RESULT) &&
|
if (!(select_options & SELECT_BIG_RESULT) &&
|
||||||
((group_list && const_tables != tables &&
|
((group_list &&
|
||||||
(!simple_group ||
|
(!simple_group ||
|
||||||
!test_if_skip_sort_order(&join_tab[const_tables], group_list,
|
!test_if_skip_sort_order(&join_tab[const_tables], group_list,
|
||||||
unit->select_limit_cnt, 0))) ||
|
unit->select_limit_cnt, 0))) ||
|
||||||
@ -984,6 +986,7 @@ JOIN::optimize()
|
|||||||
{
|
{
|
||||||
need_tmp=1; simple_order=simple_group=0; // Force tmp table without sort
|
need_tmp=1; simple_order=simple_group=0; // Force tmp table without sort
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tmp_having= having;
|
tmp_having= having;
|
||||||
if (select_options & SELECT_DESCRIBE)
|
if (select_options & SELECT_DESCRIBE)
|
||||||
@ -5381,6 +5384,8 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
|
|||||||
JOIN_TAB *first_inner_tab= tab->first_inner;
|
JOIN_TAB *first_inner_tab= tab->first_inner;
|
||||||
table_map current_map= tab->table->map;
|
table_map current_map= tab->table->map;
|
||||||
bool use_quick_range=0;
|
bool use_quick_range=0;
|
||||||
|
COND *tmp;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Following force including random expression in last table condition.
|
Following force including random expression in last table condition.
|
||||||
It solve problem with select like SELECT * FROM t1 WHERE rand() > 0.5
|
It solve problem with select like SELECT * FROM t1 WHERE rand() > 0.5
|
||||||
@ -5402,7 +5407,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
|
|||||||
join->best_positions[i].records_read= rows2double(tab->quick->records);
|
join->best_positions[i].records_read= rows2double(tab->quick->records);
|
||||||
}
|
}
|
||||||
|
|
||||||
COND *tmp= NULL;
|
tmp= NULL;
|
||||||
if (cond)
|
if (cond)
|
||||||
tmp= make_cond_for_table(cond,used_tables,current_map);
|
tmp= make_cond_for_table(cond,used_tables,current_map);
|
||||||
if (cond && !tmp && tab->quick)
|
if (cond && !tmp && tab->quick)
|
||||||
|
@ -1265,7 +1265,7 @@ static bool show_status_array(THD *thd, const char *wild,
|
|||||||
name_buffer, wild)))
|
name_buffer, wild)))
|
||||||
{
|
{
|
||||||
char *value=variables->value;
|
char *value=variables->value;
|
||||||
const char *pos, *end;
|
const char *pos, *end; // We assign a lot of const's
|
||||||
long nr;
|
long nr;
|
||||||
if (show_type == SHOW_SYS)
|
if (show_type == SHOW_SYS)
|
||||||
{
|
{
|
||||||
@ -1336,8 +1336,8 @@ static bool show_status_array(THD *thd, const char *wild,
|
|||||||
case SHOW_SLAVE_RETRIED_TRANS:
|
case SHOW_SLAVE_RETRIED_TRANS:
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
TODO: in 5.1 with multimaster, have one such counter per line in SHOW
|
TODO: in 5.1 with multimaster, have one such counter per line in
|
||||||
SLAVE STATUS, and have the sum over all lines here.
|
SHOW SLAVE STATUS, and have the sum over all lines here.
|
||||||
*/
|
*/
|
||||||
pthread_mutex_lock(&LOCK_active_mi);
|
pthread_mutex_lock(&LOCK_active_mi);
|
||||||
pthread_mutex_lock(&active_mi->rli.data_lock);
|
pthread_mutex_lock(&active_mi->rli.data_lock);
|
||||||
@ -1359,7 +1359,11 @@ static bool show_status_array(THD *thd, const char *wild,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (int i= 1; i < MAX_SLAVE_ERROR; i++)
|
/* 10 is enough assuming errors are max 4 digits */
|
||||||
|
int i;
|
||||||
|
for (i= 1;
|
||||||
|
i < MAX_SLAVE_ERROR && (uint) (end-buff) < sizeof(buff)-10;
|
||||||
|
i++)
|
||||||
{
|
{
|
||||||
if (bitmap_is_set(bitmap, i))
|
if (bitmap_is_set(bitmap, i))
|
||||||
{
|
{
|
||||||
@ -1369,6 +1373,8 @@ static bool show_status_array(THD *thd, const char *wild,
|
|||||||
}
|
}
|
||||||
if (end != buff)
|
if (end != buff)
|
||||||
end--; // Remove last ','
|
end--; // Remove last ','
|
||||||
|
if (i < MAX_SLAVE_ERROR)
|
||||||
|
end= strmov((char*) end, "..."); // Couldn't show all errors
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2164,15 +2164,11 @@ bool st_table_list::check_single_table(st_table_list **table, table_map map,
|
|||||||
{
|
{
|
||||||
if (*table)
|
if (*table)
|
||||||
return TRUE;
|
return TRUE;
|
||||||
else
|
|
||||||
{
|
|
||||||
*table= tbl;
|
*table= tbl;
|
||||||
tbl->check_option= view->check_option;
|
tbl->check_option= view->check_option;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
else if (tbl->check_single_table(table, map, view))
|
||||||
else
|
|
||||||
if (tbl->check_single_table(table, map, view))
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
@ -436,10 +436,7 @@ typedef struct st_table_list
|
|||||||
bool skip_temporary; /* this table shouldn't be temporary */
|
bool skip_temporary; /* this table shouldn't be temporary */
|
||||||
/* TRUE if this merged view contain auto_increment field */
|
/* TRUE if this merged view contain auto_increment field */
|
||||||
bool contain_auto_increment;
|
bool contain_auto_increment;
|
||||||
#if 0
|
|
||||||
#else
|
|
||||||
bool multitable_view; /* TRUE iff this is multitable view */
|
bool multitable_view; /* TRUE iff this is multitable view */
|
||||||
#endif
|
|
||||||
/* FRMTYPE_ERROR if any type is acceptable */
|
/* FRMTYPE_ERROR if any type is acceptable */
|
||||||
enum frm_type_enum required_type;
|
enum frm_type_enum required_type;
|
||||||
char timestamp_buffer[20]; /* buffer for timestamp (19+1) */
|
char timestamp_buffer[20]; /* buffer for timestamp (19+1) */
|
||||||
|
@ -1549,15 +1549,19 @@ decimal_round(decimal_t *from, decimal_t *to, int scale,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
while (unlikely(*buf1 == 0) && buf1 >= to->buf)
|
for (;;)
|
||||||
buf1--;
|
{
|
||||||
if (buf1 < to->buf)
|
if (likely(*buf1))
|
||||||
|
break;
|
||||||
|
if (buf1-- == to->buf)
|
||||||
{
|
{
|
||||||
decimal_make_zero(to);
|
decimal_make_zero(to);
|
||||||
return E_DEC_OK;
|
return E_DEC_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (scale<0) scale=0;
|
}
|
||||||
|
if (scale<0)
|
||||||
|
scale=0;
|
||||||
|
|
||||||
done:
|
done:
|
||||||
to->frac=scale;
|
to->frac=scale;
|
||||||
@ -1727,11 +1731,14 @@ static int do_sub(decimal_t *from1, decimal_t *from2, decimal_t *to)
|
|||||||
while (buf1 <=end1 && buf2 <= end2 && *buf1 == *buf2)
|
while (buf1 <=end1 && buf2 <= end2 && *buf1 == *buf2)
|
||||||
buf1++, buf2++;
|
buf1++, buf2++;
|
||||||
if (buf1 <= end1)
|
if (buf1 <= end1)
|
||||||
|
{
|
||||||
if (buf2 <= end2)
|
if (buf2 <= end2)
|
||||||
carry= *buf2 > *buf1;
|
carry= *buf2 > *buf1;
|
||||||
else
|
else
|
||||||
carry= 0;
|
carry= 0;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
if (buf2 <= end2)
|
if (buf2 <= end2)
|
||||||
carry=1;
|
carry=1;
|
||||||
else /* short-circuit everything: from1 == from2 */
|
else /* short-circuit everything: from1 == from2 */
|
||||||
@ -1742,6 +1749,7 @@ static int do_sub(decimal_t *from1, decimal_t *from2, decimal_t *to)
|
|||||||
return E_DEC_OK;
|
return E_DEC_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (to == 0) /* decimal_cmp() */
|
if (to == 0) /* decimal_cmp() */
|
||||||
return carry == from1->sign ? 1 : -1;
|
return carry == from1->sign ? 1 : -1;
|
||||||
@ -1937,10 +1945,18 @@ int decimal_mul(decimal_t *from1, decimal_t *from2, decimal_t *to)
|
|||||||
{
|
{
|
||||||
dec1 *buf= to->buf;
|
dec1 *buf= to->buf;
|
||||||
dec1 *end= to->buf + intg0 + frac0;
|
dec1 *end= to->buf + intg0 + frac0;
|
||||||
for (; (buf<end) && !*buf; buf++);
|
DBUG_ASSERT(buf != end);
|
||||||
if (buf == end)
|
for (;;)
|
||||||
/* So we got decimal zero */
|
{
|
||||||
|
if (*buf)
|
||||||
|
break;
|
||||||
|
if (++buf == end)
|
||||||
|
{
|
||||||
|
/* We got decimal zero */
|
||||||
decimal_make_zero(to);
|
decimal_make_zero(to);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user