summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorunknown <sergefp@mysql.com>2005-10-18 14:08:50 +0400
committerunknown <sergefp@mysql.com>2005-10-18 14:08:50 +0400
commit7d81acb5e91b0e94b169bfb0a09f430f28496d59 (patch)
treed6d7b767e37101864068ce6dc37173555bab7642 /sql
parent3867a5db376cf225403116c26eaa8c73e4c9b359 (diff)
parent8ff8fac53dc7423d1fbe229dfb2f112236b7cf2a (diff)
downloadmariadb-git-7d81acb5e91b0e94b169bfb0a09f430f28496d59.tar.gz
Merge spetrunia@bk-internal.mysql.com:/home/bk/mysql-4.1
into mysql.com:/home/psergey/mysql-4.1-bug12915-r2 sql/sql_update.cc: Auto merged
Diffstat (limited to 'sql')
-rw-r--r--sql/mysql_priv.h2
-rw-r--r--sql/opt_range.cc90
-rw-r--r--sql/opt_range.h2
-rw-r--r--sql/records.cc108
-rw-r--r--sql/sql_delete.cc46
-rw-r--r--sql/sql_update.cc32
-rw-r--r--sql/structs.h1
7 files changed, 259 insertions, 22 deletions
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 32c5861028e..5961d57e83f 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -1105,6 +1105,8 @@ void change_byte(byte *,uint,char,char);
void init_read_record(READ_RECORD *info, THD *thd, TABLE *reg_form,
SQL_SELECT *select,
int use_record_cache, bool print_errors);
+void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table,
+ bool print_error, uint idx);
void end_read_record(READ_RECORD *info);
ha_rows filesort(THD *thd, TABLE *form,struct st_sort_field *sortorder,
uint s_length, SQL_SELECT *select,
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index 5cb330100f8..71f937f90c6 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -582,6 +582,96 @@ SEL_ARG *SEL_ARG::clone_tree()
return root;
}
+
+/*
+ Find the best index to retrieve first N records in given order
+
+ SYNOPSIS
+ get_index_for_order()
+ table Table to be accessed
+ order Required ordering
+ limit Number of records that will be retrieved
+
+ DESCRIPTION
+ Find the best index that allows to retrieve first #limit records in the
+ given order cheaper then one would retrieve them using full table scan.
+
+ IMPLEMENTATION
+ Run through all table indexes and find the shortest index that allows
+ records to be retrieved in given order. We look for the shortest index
+ as we will have fewer index pages to read with it.
+
+ This function is used only by UPDATE/DELETE, so we take into account how
+ the UPDATE/DELETE code will work:
+ * index can only be scanned in forward direction
+ * HA_EXTRA_KEYREAD will not be used
+ Perhaps these assumptions could be relaxed
+
+ RETURN
+ index number
+ MAX_KEY if no such index was found.
+*/
+
+uint get_index_for_order(TABLE *table, ORDER *order, ha_rows limit)
+{
+ uint idx;
+ uint match_key= MAX_KEY, match_key_len= MAX_KEY_LENGTH + 1;
+ ORDER *ord;
+
+ for (ord= order; ord; ord= ord->next)
+ if (!ord->asc)
+ return MAX_KEY;
+
+ for (idx= 0; idx < table->keys; idx++)
+ {
+ if (!(table->keys_in_use_for_query.is_set(idx)))
+ continue;
+ KEY_PART_INFO *keyinfo= table->key_info[idx].key_part;
+ uint partno= 0;
+
+ /*
+ The below check is sufficient considering we now have either BTREE
+ indexes (records are returned in order for any index prefix) or HASH
+ indexes (records are not returned in order for any index prefix).
+ */
+ if (!(table->file->index_flags(idx, 0, 1) & HA_READ_ORDER))
+ continue;
+ for (ord= order; ord; ord= ord->next, partno++)
+ {
+ Item *item= order->item[0];
+ if (!(item->type() == Item::FIELD_ITEM &&
+ ((Item_field*)item)->field->eq(keyinfo[partno].field)))
+ break;
+ }
+
+ if (!ord && table->key_info[idx].key_length < match_key_len)
+ {
+ /*
+ Ok, the ordering is compatible and this key is shorter then
+ previous match (we want shorter keys as we'll have to read fewer
+ index pages for the same number of records)
+ */
+ match_key= idx;
+ match_key_len= table->key_info[idx].key_length;
+ }
+ }
+
+ if (match_key != MAX_KEY)
+ {
+ /*
+ Found an index that allows records to be retrieved in the requested
+ order. Now we'll check if using the index is cheaper then doing a table
+ scan.
+ */
+ double full_scan_time= table->file->scan_time();
+ double index_scan_time= table->file->read_time(match_key, 1, limit);
+ if (index_scan_time > full_scan_time)
+ match_key= MAX_KEY;
+ }
+ return match_key;
+}
+
+
/*
Test if a key can be used in different ranges
diff --git a/sql/opt_range.h b/sql/opt_range.h
index 87e0315a09e..15f0bf02b34 100644
--- a/sql/opt_range.h
+++ b/sql/opt_range.h
@@ -167,4 +167,6 @@ public:
QUICK_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table,
struct st_table_ref *ref);
+uint get_index_for_order(TABLE *table, ORDER *order, ha_rows limit);
+
#endif
diff --git a/sql/records.cc b/sql/records.cc
index e5a0d102b10..9150024d4b8 100644
--- a/sql/records.cc
+++ b/sql/records.cc
@@ -29,7 +29,59 @@ static int rr_from_cache(READ_RECORD *info);
static int init_rr_cache(READ_RECORD *info);
static int rr_cmp(uchar *a,uchar *b);
- /* init struct for read with info->read_record */
+static int rr_index(READ_RECORD *info);
+
+
+
+/*
+ Initialize READ_RECORD structure to perform full index scan
+
+ SYNOPSIS
+ init_read_record_idx()
+ info READ_RECORD structure to initialize.
+ thd Thread handle
+ table Table to be accessed
+ print_error If true, call table->file->print_error() if an error
+ occurs (except for end-of-records error)
+ idx index to scan
+
+ DESCRIPTION
+ Initialize READ_RECORD structure to perform full index scan (in forward
+ direction) using read_record.read_record() interface.
+
+ This function has been added at late stage and is used only by
+ UPDATE/DELETE. Other statements perform index scans using
+ join_read_first/next functions.
+*/
+
+void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table,
+ bool print_error, uint idx)
+{
+ bzero((char*) info,sizeof(*info));
+ info->thd=thd;
+ info->table=table;
+ info->file= table->file;
+ info->forms= &info->table; /* Only one table */
+
+ info->record= table->record[0];
+ info->ref_length= table->file->ref_length;
+
+ info->select=NULL;
+ info->print_error=print_error;
+ info->ignore_not_found_rows= 0;
+ table->status=0; /* And it's always found */
+
+ if (!table->file->inited)
+ {
+ table->file->ha_index_init(idx);
+ table->file->extra(HA_EXTRA_RETRIEVE_PRIMARY_KEY);
+ }
+ info->read_record= rr_index;
+ info->first= TRUE;
+}
+
+
+/* init struct for read with info->read_record */
void init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
SQL_SELECT *select,
@@ -182,6 +234,60 @@ static int rr_quick(READ_RECORD *info)
}
+/*
+ A READ_RECORD::read_record implementation that reads index sequentially
+
+ SYNOPSIS
+ rr_index()
+ info Scan info
+
+ DESCRIPTION
+ Read the next index record (in forward direction) and translate return
+ value.
+
+ RETURN
+ 0 Ok
+ -1 End of records
+ 1 Error
+*/
+
+static int rr_index(READ_RECORD *info)
+{
+ int tmp;
+ while (1)
+ {
+ if (info->first)
+ {
+ info->first= FALSE;
+ tmp= info->file->index_first(info->record);
+ }
+ else
+ tmp= info->file->index_next(info->record);
+
+ if (!tmp)
+ break;
+ if (info->thd->killed)
+ {
+ my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ return 1;
+ }
+ if (tmp != HA_ERR_RECORD_DELETED)
+ {
+ if (tmp == HA_ERR_END_OF_FILE)
+ tmp= -1;
+ else
+ {
+ if (info->print_error)
+ info->table->file->print_error(tmp,MYF(0));
+ if (tmp < 0) // Fix negative BDB errno
+ tmp=1;
+ }
+ break;
+ }
+ }
+ return tmp;
+}
+
static int rr_sequential(READ_RECORD *info)
{
int tmp;
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 1dd52a2ba74..079a301818c 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -37,6 +37,7 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
bool using_limit=limit != HA_POS_ERROR;
bool transactional_table, log_delayed, safe_update, const_cond;
ha_rows deleted;
+ uint usable_index= MAX_KEY;
DBUG_ENTER("mysql_delete");
if ((open_and_lock_tables(thd, table_list)))
@@ -119,30 +120,47 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
tables.table = table;
tables.alias = table_list->alias;
- table->sort.io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE),
- MYF(MY_FAE | MY_ZEROFILL));
if (thd->lex->select_lex.setup_ref_array(thd, order->elements) ||
setup_order(thd, thd->lex->select_lex.ref_pointer_array, &tables,
- fields, all_fields, (ORDER*) order->first) ||
- !(sortorder=make_unireg_sortorder((ORDER*) order->first, &length)) ||
+ fields, all_fields, (ORDER*) order->first))
+ {
+ delete select;
+ free_underlaid_joins(thd, &thd->lex->select_lex);
+ DBUG_RETURN(-1); // This will force out message
+ }
+
+ if (!select && limit != HA_POS_ERROR)
+ usable_index= get_index_for_order(table, (ORDER*)(order->first), limit);
+
+ if (usable_index == MAX_KEY)
+ {
+ table->sort.io_cache= (IO_CACHE *) my_malloc(sizeof(IO_CACHE),
+ MYF(MY_FAE | MY_ZEROFILL));
+
+ if ( !(sortorder=make_unireg_sortorder((ORDER*) order->first, &length)) ||
(table->sort.found_records = filesort(thd, table, sortorder, length,
select, HA_POS_ERROR,
&examined_rows))
== HA_POS_ERROR)
- {
+ {
+ delete select;
+ free_underlaid_joins(thd, &thd->lex->select_lex);
+ DBUG_RETURN(-1); // This will force out message
+ }
+ /*
+ Filesort has already found and selected the rows we want to delete,
+ so we don't need the where clause
+ */
delete select;
- free_underlaid_joins(thd, &thd->lex->select_lex);
- DBUG_RETURN(-1); // This will force out message
+ select= 0;
}
- /*
- Filesort has already found and selected the rows we want to delete,
- so we don't need the where clause
- */
- delete select;
- select= 0;
}
- init_read_record(&info,thd,table,select,1,1);
+ if (usable_index==MAX_KEY)
+ init_read_record(&info,thd,table,select,1,1);
+ else
+ init_read_record_idx(&info, thd, table, 1, usable_index);
+
deleted=0L;
init_ftfuncs(thd, &thd->lex->select_lex, 1);
thd->proc_info="updating";
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index ca40ed554dd..c30749677d6 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -61,7 +61,8 @@ int mysql_update(THD *thd,
bool safe_update= thd->options & OPTION_SAFE_UPDATES;
bool used_key_is_modified, transactional_table, log_delayed;
int error=0;
- uint used_index;
+ uint used_index= MAX_KEY;
+ bool need_sort= TRUE;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
uint want_privilege;
#endif
@@ -145,6 +146,11 @@ int mysql_update(THD *thd,
send_ok(thd); // No matching records
DBUG_RETURN(0);
}
+ if (!select && limit != HA_POS_ERROR)
+ {
+ if (MAX_KEY != (used_index= get_index_for_order(table, order, limit)))
+ need_sort= FALSE;
+ }
/* If running in safe sql mode, don't allow updates without keys */
if (table->quick_keys.is_clear_all())
{
@@ -157,6 +163,7 @@ int mysql_update(THD *thd,
}
}
init_ftfuncs(thd, &thd->lex->select_lex, 1);
+
/* Check if we are modifying a key that we are used to search with */
if (select && select->quick)
{
@@ -164,13 +171,15 @@ int mysql_update(THD *thd,
used_key_is_modified= (!select->quick->unique_key_range() &&
check_if_key_used(table, used_index, fields));
}
+ else if (used_index != MAX_KEY)
+ {
+ used_key_is_modified= check_if_key_used(table, used_index, fields);
+ }
else if ((used_index=table->file->key_used_on_scan) < MAX_KEY)
used_key_is_modified=check_if_key_used(table, used_index, fields);
else
- {
used_key_is_modified=0;
- used_index= MAX_KEY;
- }
+
if (used_key_is_modified || order)
{
/*
@@ -181,10 +190,11 @@ int mysql_update(THD *thd,
if (used_index < MAX_KEY && old_used_keys.is_set(used_index))
{
table->key_read=1;
- table->file->extra(HA_EXTRA_KEYREAD);
+ table->file->extra(HA_EXTRA_KEYREAD); //todo: psergey: check
}
- if (order)
+ /* note: can actually avoid sorting below.. */
+ if (order && need_sort)
{
/*
Doing an ORDER BY; Let filesort find and sort the rows we are going
@@ -225,7 +235,11 @@ int mysql_update(THD *thd,
DISK_BUFFER_SIZE, MYF(MY_WME)))
goto err;
- init_read_record(&info,thd,table,select,0,1);
+ if (used_index == MAX_KEY)
+ init_read_record(&info,thd,table,select,0,1);
+ else
+ init_read_record_idx(&info, thd, table, 1, used_index);
+
thd->proc_info="Searching rows for update";
uint tmp_limit= limit;
@@ -251,6 +265,10 @@ int mysql_update(THD *thd,
error= 1; // Aborted
limit= tmp_limit;
end_read_record(&info);
+
+ /* if we got here we must not use index in the main update loop below */
+ used_index= MAX_KEY;
+
/* Change select to use tempfile */
if (select)
{
diff --git a/sql/structs.h b/sql/structs.h
index 081ada88bf7..4a88a17cdd0 100644
--- a/sql/structs.h
+++ b/sql/structs.h
@@ -132,6 +132,7 @@ typedef struct st_read_record { /* Parameter to read_record */
byte *cache,*cache_pos,*cache_end,*read_positions;
IO_CACHE *io_cache;
bool print_error, ignore_not_found_rows;
+ bool first; /* used only with rr_index_read */
} READ_RECORD;