MDEV-23908: Implement SELECT ... OFFSET ... FETCH ...

This commit implements the standard SQL extension
OFFSET start { ROW | ROWS }
[FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } { ONLY | WITH TIES }]

To achieve this a reserved keyword OFFSET is introduced.

The general logic for WITH TIES implies:
1. The number of rows a query returns is no longer known during optimize
   phase. Adjust optimizations to no longer consider this.
2. During end_send make use of an "order Cached_item"to compare if the
   ORDER BY columns changed. Keep returning rows until there is a
   change. This happens only after we reached the row limit.
3. Within end_send_group, the order by clause was eliminated. It is
   still possible to keep the optimization of using end_send_group for
   producing the final result set.
This commit is contained in:
Vicențiu Ciorbaru 2021-03-28 21:41:50 +03:00
parent 2d595319bf
commit 299b935320
19 changed files with 2621 additions and 81 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -29,7 +29,7 @@ Warning 1280 Name 'TzId' ignored for PRIMARY key.
CREATE TABLE time_zone_transition ( Time_zone_id int unsigned NOT NULL, Transition_time bigint signed NOT NULL, Transition_type_id int unsigned NOT NULL, PRIMARY KEY TzIdTranTime (Time_zone_id, Transition_time) ) engine=MyISAM CHARACTER SET utf8 comment='Time zone transitions';
Warnings:
Warning 1280 Name 'TzIdTranTime' ignored for PRIMARY key.
CREATE TABLE time_zone_transition_type ( Time_zone_id int unsigned NOT NULL, Transition_type_id int unsigned NOT NULL, Offset int signed DEFAULT 0 NOT NULL, Is_DST tinyint unsigned DEFAULT 0 NOT NULL, Abbreviation char(8) DEFAULT '' NOT NULL, PRIMARY KEY TzIdTrTId (Time_zone_id, Transition_type_id) ) engine=MyISAM CHARACTER SET utf8 comment='Time zone transition types';
CREATE TABLE time_zone_transition_type ( Time_zone_id int unsigned NOT NULL, Transition_type_id int unsigned NOT NULL, `Offset` int signed DEFAULT 0 NOT NULL, Is_DST tinyint unsigned DEFAULT 0 NOT NULL, Abbreviation char(8) DEFAULT '' NOT NULL, PRIMARY KEY TzIdTrTId (Time_zone_id, Transition_type_id) ) engine=MyISAM CHARACTER SET utf8 comment='Time zone transition types';
Warnings:
Warning 1280 Name 'TzIdTrTId' ignored for PRIMARY key.
CREATE TABLE time_zone_leap_second ( Transition_time bigint signed NOT NULL, Correction int signed NOT NULL, PRIMARY KEY TranTime (Transition_time) ) engine=MyISAM CHARACTER SET utf8 comment='Leap seconds information for time zones';

View File

@ -42,7 +42,7 @@ CREATE TABLE help_keyword ( help_keyword_id int unsigned not null, name varchar(
CREATE TABLE time_zone_name ( Name char(64) NOT NULL, Time_zone_id int unsigned NOT NULL, PRIMARY KEY Name (Name) ) engine=MyISAM CHARACTER SET utf8 comment='Time zone names';
CREATE TABLE time_zone ( Time_zone_id int unsigned NOT NULL auto_increment, Use_leap_seconds enum('Y','N') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, PRIMARY KEY TzId (Time_zone_id) ) engine=MyISAM CHARACTER SET utf8 comment='Time zones';
CREATE TABLE time_zone_transition ( Time_zone_id int unsigned NOT NULL, Transition_time bigint signed NOT NULL, Transition_type_id int unsigned NOT NULL, PRIMARY KEY TzIdTranTime (Time_zone_id, Transition_time) ) engine=MyISAM CHARACTER SET utf8 comment='Time zone transitions';
CREATE TABLE time_zone_transition_type ( Time_zone_id int unsigned NOT NULL, Transition_type_id int unsigned NOT NULL, Offset int signed DEFAULT 0 NOT NULL, Is_DST tinyint unsigned DEFAULT 0 NOT NULL, Abbreviation char(8) DEFAULT '' NOT NULL, PRIMARY KEY TzIdTrTId (Time_zone_id, Transition_type_id) ) engine=MyISAM CHARACTER SET utf8 comment='Time zone transition types';
CREATE TABLE time_zone_transition_type ( Time_zone_id int unsigned NOT NULL, Transition_type_id int unsigned NOT NULL, `Offset` int signed DEFAULT 0 NOT NULL, Is_DST tinyint unsigned DEFAULT 0 NOT NULL, Abbreviation char(8) DEFAULT '' NOT NULL, PRIMARY KEY TzIdTrTId (Time_zone_id, Transition_type_id) ) engine=MyISAM CHARACTER SET utf8 comment='Time zone transition types';
CREATE TABLE time_zone_leap_second ( Transition_time bigint signed NOT NULL, Correction int signed NOT NULL, PRIMARY KEY TranTime (Transition_time) ) engine=MyISAM CHARACTER SET utf8 comment='Leap seconds information for time zones';
# Run the mysql_fix_privilege_tables.sql using "mysql --force"

View File

@ -29,7 +29,7 @@ Warning 1280 Name 'TzId' ignored for PRIMARY key.
CREATE TABLE time_zone_transition ( Time_zone_id int unsigned NOT NULL, Transition_time bigint signed NOT NULL, Transition_type_id int unsigned NOT NULL, PRIMARY KEY TzIdTranTime (Time_zone_id, Transition_time) ) engine=MyISAM CHARACTER SET utf8 comment='Time zone transitions';
Warnings:
Warning 1280 Name 'TzIdTranTime' ignored for PRIMARY key.
CREATE TABLE time_zone_transition_type ( Time_zone_id int unsigned NOT NULL, Transition_type_id int unsigned NOT NULL, Offset int signed DEFAULT 0 NOT NULL, Is_DST tinyint unsigned DEFAULT 0 NOT NULL, Abbreviation char(8) DEFAULT '' NOT NULL, PRIMARY KEY TzIdTrTId (Time_zone_id, Transition_type_id) ) engine=MyISAM CHARACTER SET utf8 comment='Time zone transition types';
CREATE TABLE time_zone_transition_type ( Time_zone_id int unsigned NOT NULL, Transition_type_id int unsigned NOT NULL, `Offset` int signed DEFAULT 0 NOT NULL, Is_DST tinyint unsigned DEFAULT 0 NOT NULL, Abbreviation char(8) DEFAULT '' NOT NULL, PRIMARY KEY TzIdTrTId (Time_zone_id, Transition_type_id) ) engine=MyISAM CHARACTER SET utf8 comment='Time zone transition types';
Warnings:
Warning 1280 Name 'TzIdTrTId' ignored for PRIMARY key.
CREATE TABLE time_zone_leap_second ( Transition_time bigint signed NOT NULL, Correction int signed NOT NULL, PRIMARY KEY TranTime (Transition_time) ) engine=MyISAM CHARACTER SET utf8 comment='Leap seconds information for time zones';

View File

@ -42,7 +42,7 @@ CREATE TABLE help_keyword ( help_keyword_id int unsigned not null, name char(64)
CREATE TABLE time_zone_name ( Name char(64) NOT NULL, Time_zone_id int unsigned NOT NULL, PRIMARY KEY Name (Name) ) engine=MyISAM CHARACTER SET utf8 comment='Time zone names';
CREATE TABLE time_zone ( Time_zone_id int unsigned NOT NULL auto_increment, Use_leap_seconds enum('Y','N') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, PRIMARY KEY TzId (Time_zone_id) ) engine=MyISAM CHARACTER SET utf8 comment='Time zones';
CREATE TABLE time_zone_transition ( Time_zone_id int unsigned NOT NULL, Transition_time bigint signed NOT NULL, Transition_type_id int unsigned NOT NULL, PRIMARY KEY TzIdTranTime (Time_zone_id, Transition_time) ) engine=MyISAM CHARACTER SET utf8 comment='Time zone transitions';
CREATE TABLE time_zone_transition_type ( Time_zone_id int unsigned NOT NULL, Transition_type_id int unsigned NOT NULL, Offset int signed DEFAULT 0 NOT NULL, Is_DST tinyint unsigned DEFAULT 0 NOT NULL, Abbreviation char(8) DEFAULT '' NOT NULL, PRIMARY KEY TzIdTrTId (Time_zone_id, Transition_type_id) ) engine=MyISAM CHARACTER SET utf8 comment='Time zone transition types';
CREATE TABLE time_zone_transition_type ( Time_zone_id int unsigned NOT NULL, Transition_type_id int unsigned NOT NULL, `Offset` int signed DEFAULT 0 NOT NULL, Is_DST tinyint unsigned DEFAULT 0 NOT NULL, Abbreviation char(8) DEFAULT '' NOT NULL, PRIMARY KEY TzIdTrTId (Time_zone_id, Transition_type_id) ) engine=MyISAM CHARACTER SET utf8 comment='Time zone transition types';
CREATE TABLE time_zone_leap_second ( Transition_time bigint signed NOT NULL, Correction int signed NOT NULL, PRIMARY KEY TranTime (Transition_time) ) engine=MyISAM CHARACTER SET utf8 comment='Leap seconds information for time zones';
CREATE TABLE proc ( db char(64) collate utf8_bin DEFAULT '' NOT NULL, name char(64) DEFAULT '' NOT NULL, type enum('FUNCTION','PROCEDURE') NOT NULL, specific_name char(64) DEFAULT '' NOT NULL, language enum('SQL') DEFAULT 'SQL' NOT NULL, sql_data_access enum('CONTAINS_SQL', 'NO_SQL', 'READS_SQL_DATA', 'MODIFIES_SQL_DATA' ) DEFAULT 'CONTAINS_SQL' NOT NULL, is_deterministic enum('YES','NO') DEFAULT 'NO' NOT NULL, security_type enum('INVOKER','DEFINER') DEFAULT 'DEFINER' NOT NULL, param_list blob DEFAULT '' NOT NULL, returns char(64) DEFAULT '' NOT NULL, body longblob DEFAULT '' NOT NULL, definer char(77) collate utf8_bin DEFAULT '' NOT NULL, created timestamp, modified timestamp, sql_mode set( 'REAL_AS_FLOAT', 'PIPES_AS_CONCAT', 'ANSI_QUOTES', 'IGNORE_SPACE', 'NOT_USED', 'ONLY_FULL_GROUP_BY', 'NO_UNSIGNED_SUBTRACTION', 'NO_DIR_IN_CREATE', 'POSTGRESQL', 'ORACLE', 'MSSQL', 'DB2', 'MAXDB', 'NO_KEY_OPTIONS', 'NO_TABLE_OPTIONS', 'NO_FIELD_OPTIONS', 'MYSQL323', 'MYSQL40', 'ANSI', 'NO_AUTO_VALUE_ON_ZERO', 'NO_BACKSLASH_ESCAPES', 'STRICT_TRANS_TABLES', 'STRICT_ALL_TABLES', 'NO_ZERO_IN_DATE', 'NO_ZERO_DATE', 'INVALID_DATES', 'ERROR_FOR_DIVISION_BY_ZERO', 'TRADITIONAL', 'NO_AUTO_CREATE_USER', 'HIGH_NOT_PRECEDENCE' ) DEFAULT '' NOT NULL, comment char(64) collate utf8_bin DEFAULT '' NOT NULL, PRIMARY KEY (db,name,type) ) engine=MyISAM character set utf8 comment='Stored Procedures';
CREATE TABLE procs_priv ( Host char(60) binary DEFAULT '' NOT NULL, Db char(64) binary DEFAULT '' NOT NULL, User char(16) binary DEFAULT '' NOT NULL, Routine_name char(64) binary DEFAULT '' NOT NULL, Routine_type enum('FUNCTION','PROCEDURE') NOT NULL, Grantor char(77) DEFAULT '' NOT NULL, Proc_priv set('Execute','Alter Routine','Grant') COLLATE utf8_general_ci DEFAULT '' NOT NULL, Timestamp timestamp, PRIMARY KEY (Host,Db,User,Routine_name,Routine_type), KEY Grantor (Grantor) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Procedure privileges';

View File

@ -14,7 +14,7 @@ CREATE TABLE IF NOT EXISTS help_keyword ( help_keyword_id int unsigned not null,
CREATE TABLE IF NOT EXISTS time_zone_name ( Name char(64) NOT NULL, Time_zone_id int unsigned NOT NULL, PRIMARY KEY (Name) ) engine=MyISAM CHARACTER SET utf8 comment='Time zone names';
CREATE TABLE IF NOT EXISTS time_zone ( Time_zone_id int unsigned NOT NULL auto_increment, Use_leap_seconds enum('Y','N') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, PRIMARY KEY (Time_zone_id) ) engine=MyISAM CHARACTER SET utf8 comment='Time zones';
CREATE TABLE IF NOT EXISTS time_zone_transition ( Time_zone_id int unsigned NOT NULL, Transition_time bigint signed NOT NULL, Transition_type_id int unsigned NOT NULL, PRIMARY KEY (Time_zone_id, Transition_time) ) engine=MyISAM CHARACTER SET utf8 comment='Time zone transitions';
CREATE TABLE IF NOT EXISTS time_zone_transition_type ( Time_zone_id int unsigned NOT NULL, Transition_type_id int unsigned NOT NULL, Offset int signed DEFAULT 0 NOT NULL, Is_DST tinyint unsigned DEFAULT 0 NOT NULL, Abbreviation char(8) DEFAULT '' NOT NULL, PRIMARY KEY (Time_zone_id, Transition_type_id) ) engine=MyISAM CHARACTER SET utf8 comment='Time zone transition types';
CREATE TABLE IF NOT EXISTS time_zone_transition_type ( Time_zone_id int unsigned NOT NULL, Transition_type_id int unsigned NOT NULL, `Offset` int signed DEFAULT 0 NOT NULL, Is_DST tinyint unsigned DEFAULT 0 NOT NULL, Abbreviation char(8) DEFAULT '' NOT NULL, PRIMARY KEY (Time_zone_id, Transition_type_id) ) engine=MyISAM CHARACTER SET utf8 comment='Time zone transition types';
CREATE TABLE IF NOT EXISTS time_zone_leap_second ( Transition_time bigint signed NOT NULL, Correction int signed NOT NULL, PRIMARY KEY (Transition_time) ) engine=MyISAM CHARACTER SET utf8 comment='Leap seconds information for time zones';
CREATE TABLE IF NOT EXISTS proc ( db char(64) collate utf8_bin DEFAULT '' NOT NULL, name char(64) DEFAULT '' NOT NULL, type enum('FUNCTION','PROCEDURE') NOT NULL, specific_name char(64) DEFAULT '' NOT NULL, language enum('SQL') DEFAULT 'SQL' NOT NULL, sql_data_access enum('CONTAINS_SQL', 'NO_SQL', 'READS_SQL_DATA', 'MODIFIES_SQL_DATA' ) DEFAULT 'CONTAINS_SQL' NOT NULL, is_deterministic enum('YES','NO') DEFAULT 'NO' NOT NULL, security_type enum('INVOKER','DEFINER') DEFAULT 'DEFINER' NOT NULL, param_list blob NOT NULL, returns char(64) DEFAULT '' NOT NULL, body longblob NOT NULL, definer char(77) collate utf8_bin DEFAULT '' NOT NULL, created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, modified timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', sql_mode set( 'REAL_AS_FLOAT', 'PIPES_AS_CONCAT', 'ANSI_QUOTES', 'IGNORE_SPACE', 'NOT_USED', 'ONLY_FULL_GROUP_BY', 'NO_UNSIGNED_SUBTRACTION', 'NO_DIR_IN_CREATE', 'POSTGRESQL', 'ORACLE', 'MSSQL', 'DB2', 'MAXDB', 'NO_KEY_OPTIONS', 'NO_TABLE_OPTIONS', 'NO_FIELD_OPTIONS', 'MYSQL323', 'MYSQL40', 'ANSI', 'NO_AUTO_VALUE_ON_ZERO', 'NO_BACKSLASH_ESCAPES', 'STRICT_TRANS_TABLES', 'STRICT_ALL_TABLES', 'NO_ZERO_IN_DATE', 'NO_ZERO_DATE', 'INVALID_DATES', 'ERROR_FOR_DIVISION_BY_ZERO', 'TRADITIONAL', 'NO_AUTO_CREATE_USER', 'HIGH_NOT_PRECEDENCE' ) DEFAULT '' NOT NULL, comment char(64) collate utf8_bin DEFAULT '' NOT NULL, PRIMARY KEY (db,name,type) ) engine=MyISAM character set utf8 comment='Stored Procedures';
CREATE TABLE IF NOT EXISTS procs_priv ( Host char(60) binary DEFAULT '' NOT NULL, Db char(64) binary DEFAULT '' NOT NULL, User char(16) binary DEFAULT '' NOT NULL, Routine_name char(64) binary DEFAULT '' NOT NULL, Routine_type enum('FUNCTION','PROCEDURE') NOT NULL, Grantor char(77) DEFAULT '' NOT NULL, Proc_priv set('Execute','Alter Routine','Grant') COLLATE utf8_general_ci DEFAULT '' NOT NULL, Timestamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (Host,Db,User,Routine_name,Routine_type), KEY Grantor (Grantor) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Procedure privileges';

View File

@ -37,7 +37,7 @@ CREATE TABLE IF NOT EXISTS help_keyword ( help_keyword_id int unsigned not null,
CREATE TABLE IF NOT EXISTS time_zone_name ( Name char(64) NOT NULL, Time_zone_id int unsigned NOT NULL, PRIMARY KEY (Name) ) engine=MyISAM CHARACTER SET utf8 comment='Time zone names';
CREATE TABLE IF NOT EXISTS time_zone ( Time_zone_id int unsigned NOT NULL auto_increment, Use_leap_seconds enum('Y','N') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, PRIMARY KEY (Time_zone_id) ) engine=MyISAM CHARACTER SET utf8 comment='Time zones';
CREATE TABLE IF NOT EXISTS time_zone_transition ( Time_zone_id int unsigned NOT NULL, Transition_time bigint signed NOT NULL, Transition_type_id int unsigned NOT NULL, PRIMARY KEY (Time_zone_id, Transition_time) ) engine=MyISAM CHARACTER SET utf8 comment='Time zone transitions';
CREATE TABLE IF NOT EXISTS time_zone_transition_type ( Time_zone_id int unsigned NOT NULL, Transition_type_id int unsigned NOT NULL, Offset int signed DEFAULT 0 NOT NULL, Is_DST tinyint unsigned DEFAULT 0 NOT NULL, Abbreviation char(8) DEFAULT '' NOT NULL, PRIMARY KEY (Time_zone_id, Transition_type_id) ) engine=MyISAM CHARACTER SET utf8 comment='Time zone transition types';
CREATE TABLE IF NOT EXISTS time_zone_transition_type ( Time_zone_id int unsigned NOT NULL, Transition_type_id int unsigned NOT NULL, `Offset` int signed DEFAULT 0 NOT NULL, Is_DST tinyint unsigned DEFAULT 0 NOT NULL, Abbreviation char(8) DEFAULT '' NOT NULL, PRIMARY KEY (Time_zone_id, Transition_type_id) ) engine=MyISAM CHARACTER SET utf8 comment='Time zone transition types';
CREATE TABLE IF NOT EXISTS time_zone_leap_second ( Transition_time bigint signed NOT NULL, Correction int signed NOT NULL, PRIMARY KEY (Transition_time) ) engine=MyISAM CHARACTER SET utf8 comment='Leap seconds information for time zones';
CREATE TABLE IF NOT EXISTS proc ( db char(64) collate utf8_bin DEFAULT '' NOT NULL, name char(64) DEFAULT '' NOT NULL, type enum('FUNCTION','PROCEDURE') NOT NULL, specific_name char(64) DEFAULT '' NOT NULL, language enum('SQL') DEFAULT 'SQL' NOT NULL, sql_data_access enum('CONTAINS_SQL', 'NO_SQL', 'READS_SQL_DATA', 'MODIFIES_SQL_DATA' ) DEFAULT 'CONTAINS_SQL' NOT NULL, is_deterministic enum('YES','NO') DEFAULT 'NO' NOT NULL, security_type enum('INVOKER','DEFINER') DEFAULT 'DEFINER' NOT NULL, param_list blob NOT NULL, returns char(64) DEFAULT '' NOT NULL, body longblob NOT NULL, definer char(77) collate utf8_bin DEFAULT '' NOT NULL, created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, modified timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', sql_mode set( 'REAL_AS_FLOAT', 'PIPES_AS_CONCAT', 'ANSI_QUOTES', 'IGNORE_SPACE', 'NOT_USED', 'ONLY_FULL_GROUP_BY', 'NO_UNSIGNED_SUBTRACTION', 'NO_DIR_IN_CREATE', 'POSTGRESQL', 'ORACLE', 'MSSQL', 'DB2', 'MAXDB', 'NO_KEY_OPTIONS', 'NO_TABLE_OPTIONS', 'NO_FIELD_OPTIONS', 'MYSQL323', 'MYSQL40', 'ANSI', 'NO_AUTO_VALUE_ON_ZERO', 'NO_BACKSLASH_ESCAPES', 'STRICT_TRANS_TABLES', 'STRICT_ALL_TABLES', 'NO_ZERO_IN_DATE', 'NO_ZERO_DATE', 'INVALID_DATES', 'ERROR_FOR_DIVISION_BY_ZERO', 'TRADITIONAL', 'NO_AUTO_CREATE_USER', 'HIGH_NOT_PRECEDENCE' ) DEFAULT '' NOT NULL, comment char(64) collate utf8_bin DEFAULT '' NOT NULL, PRIMARY KEY (db,name,type) ) engine=MyISAM character set utf8 comment='Stored Procedures';
CREATE TABLE IF NOT EXISTS procs_priv ( Host char(60) binary DEFAULT '' NOT NULL, Db char(64) binary DEFAULT '' NOT NULL, User char(16) binary DEFAULT '' NOT NULL, Routine_name char(64) binary DEFAULT '' NOT NULL, Routine_type enum('FUNCTION','PROCEDURE') NOT NULL, Grantor char(77) DEFAULT '' NOT NULL, Proc_priv set('Execute','Alter Routine','Grant') COLLATE utf8_general_ci DEFAULT '' NOT NULL, Timestamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (Host,Db,User,Routine_name,Routine_type), KEY Grantor (Grantor) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Procedure privileges';

View File

@ -144,7 +144,7 @@ CREATE TABLE IF NOT EXISTS time_zone ( Time_zone_id int unsigned NOT NULL auto
CREATE TABLE IF NOT EXISTS time_zone_transition ( Time_zone_id int unsigned NOT NULL, Transition_time bigint signed NOT NULL, Transition_type_id int unsigned NOT NULL, PRIMARY KEY /*TzIdTranTime*/ (Time_zone_id, Transition_time) ) engine=Aria transactional=1 CHARACTER SET utf8 comment='Time zone transitions';
CREATE TABLE IF NOT EXISTS time_zone_transition_type ( Time_zone_id int unsigned NOT NULL, Transition_type_id int unsigned NOT NULL, Offset int signed DEFAULT 0 NOT NULL, Is_DST tinyint unsigned DEFAULT 0 NOT NULL, Abbreviation char(8) DEFAULT '' NOT NULL, PRIMARY KEY /*TzIdTrTId*/ (Time_zone_id, Transition_type_id) ) engine=Aria transactional=1 CHARACTER SET utf8 comment='Time zone transition types';
CREATE TABLE IF NOT EXISTS time_zone_transition_type ( Time_zone_id int unsigned NOT NULL, Transition_type_id int unsigned NOT NULL, `Offset` int signed DEFAULT 0 NOT NULL, Is_DST tinyint unsigned DEFAULT 0 NOT NULL, Abbreviation char(8) DEFAULT '' NOT NULL, PRIMARY KEY /*TzIdTrTId*/ (Time_zone_id, Transition_type_id) ) engine=Aria transactional=1 CHARACTER SET utf8 comment='Time zone transition types';
CREATE TABLE IF NOT EXISTS time_zone_leap_second ( Transition_time bigint signed NOT NULL, Correction int signed NOT NULL, PRIMARY KEY /*TranTime*/ (Transition_time) ) engine=Aria transactional=1 CHARACTER SET utf8 comment='Leap seconds information for time zones';

File diff suppressed because one or more lines are too long

View File

@ -7983,3 +7983,5 @@ ER_JSON_TABLE_SCALAR_EXPECTED
eng "Can't store an array or an object in the scalar column '%s' of JSON_TABLE '%s'."
ER_JSON_TABLE_MULTIPLE_MATCHES
eng "Can't store multiple matches of the path in the column '%s' of JSON_TABLE '%s'."
ER_WITH_TIES_NEEDS_ORDER
eng "FETCH ... WITH TIES requires ORDER BY clause to be present"

View File

@ -3776,13 +3776,34 @@ void st_select_lex::print_limit(THD *thd,
if (limit_params.explicit_limit &&
limit_params.select_limit)
{
str->append(STRING_WITH_LEN(" limit "));
if (limit_params.offset_limit)
/*
[OFFSET n]
FETCH FIRST n ROWS WITH TIES
For FETCH FIRST n ROWS ONLY we fall back to the "limit" specification
as it's identical.
*/
if (limit_params.with_ties)
{
limit_params.offset_limit->print(str, query_type);
str->append(',');
if (limit_params.offset_limit)
{
str->append(STRING_WITH_LEN(" offset "));
limit_params.offset_limit->print(str, query_type);
}
str->append(STRING_WITH_LEN(" fetch first "));
limit_params.select_limit->print(str, query_type);
str->append(STRING_WITH_LEN(" rows with ties"));
}
else
{
str->append(STRING_WITH_LEN(" limit "));
if (limit_params.offset_limit)
{
limit_params.offset_limit->print(str, query_type);
str->append(',');
}
limit_params.select_limit->print(str, query_type);
}
limit_params.select_limit->print(str, query_type);
}
}
@ -4195,7 +4216,7 @@ void st_select_lex_unit::set_limit(st_select_lex *sl)
{
DBUG_ASSERT(!thd->stmt_arena->is_stmt_prepare());
lim.set_limit(sl->get_limit(), sl->get_offset());
lim.set_limit(sl->get_limit(), sl->get_offset(), sl->limit_params.with_ties);
}

View File

@ -23,20 +23,23 @@
class Select_limit_counters
{
ha_rows select_limit_cnt, offset_limit_cnt;
bool with_ties;
public:
Select_limit_counters():
select_limit_cnt(0), offset_limit_cnt(0)
select_limit_cnt(0), offset_limit_cnt(0), with_ties(false)
{};
Select_limit_counters(Select_limit_counters &orig):
Select_limit_counters(const Select_limit_counters &orig):
select_limit_cnt(orig.select_limit_cnt),
offset_limit_cnt(orig.offset_limit_cnt)
offset_limit_cnt(orig.offset_limit_cnt),
with_ties(orig.with_ties)
{};
void set_limit(ha_rows limit, ha_rows offset)
void set_limit(ha_rows limit, ha_rows offset, bool with_ties_arg)
{
offset_limit_cnt= offset;
select_limit_cnt= limit;
with_ties= with_ties_arg;
/*
Guard against an overflow condition, where limit + offset exceede
ha_rows value range. This case covers unreasonably large parameter
@ -53,6 +56,7 @@ class Select_limit_counters
{
offset_limit_cnt= 0;
select_limit_cnt= 1;
with_ties= false;
}
bool is_unlimited() const
@ -67,7 +71,7 @@ class Select_limit_counters
/* Reset the limit entirely. */
void clear()
{ select_limit_cnt= HA_POS_ERROR; offset_limit_cnt= 0; }
{ select_limit_cnt= HA_POS_ERROR; offset_limit_cnt= 0; with_ties= false;}
bool check_offset(ha_rows sent) const
{
@ -79,6 +83,8 @@ class Select_limit_counters
{ return select_limit_cnt; }
ha_rows get_offset_limit() const
{ return offset_limit_cnt; }
bool is_with_ties() const
{ return with_ties; }
};
#endif // INCLUDES_MARIADB_SQL_LIMIT_H

View File

@ -260,7 +260,8 @@ static TABLE *get_sort_by_table(ORDER *a,ORDER *b,List<TABLE_LIST> &tables,
static void calc_group_buffer(JOIN *join, ORDER *group);
static bool make_group_fields(JOIN *main_join, JOIN *curr_join);
static bool alloc_group_fields(JOIN *join, ORDER *group);
static bool alloc_order_fields(JOIN *join, ORDER *group);
static bool alloc_order_fields(JOIN *join, ORDER *group,
uint max_number_of_elements);
// Create list for using with tempory table
static bool change_to_use_tmp_fields(THD *thd, Ref_ptr_array ref_pointer_array,
List<Item> &new_list1,
@ -1393,9 +1394,23 @@ JOIN::prepare(TABLE_LIST *tables_init, COND *conds_init, uint og_num,
if (order)
{
bool real_order= FALSE;
ORDER *ord;
for (ord= order; ord; ord= ord->next)
bool requires_sorting= FALSE;
/*
WITH TIES forces the results to be sorted, even if it's not sanely
sortable.
*/
if (select_lex->limit_params.with_ties)
requires_sorting= true;
/*
Go through each ORDER BY item and perform the following:
1. Detect if none of the items contain meaningful data, which means we
can drop the sorting altogether.
2. Split any columns with aggregation functions or window functions into
their base components and store them as separate fields.
(see split_sum_func) for more details.
*/
for (ORDER *ord= order; ord; ord= ord->next)
{
Item *item= *ord->item;
/*
@ -1404,7 +1419,7 @@ JOIN::prepare(TABLE_LIST *tables_init, COND *conds_init, uint og_num,
zero length NOT NULL string functions there.
Such tuples don't contain any data to sort.
*/
if (!real_order &&
if (!requires_sorting &&
/* Not a zero length NOT NULL field */
((item->type() != Item::FIELD_ITEM ||
((Item_field *) item)->field->maybe_null() ||
@ -1414,15 +1429,26 @@ JOIN::prepare(TABLE_LIST *tables_init, COND *conds_init, uint og_num,
item->maybe_null ||
item->result_type() != STRING_RESULT ||
item->max_length)))
real_order= TRUE;
requires_sorting= TRUE;
if ((item->with_sum_func() && item->type() != Item::SUM_FUNC_ITEM) ||
item->with_window_func)
item->split_sum_func(thd, ref_ptrs, all_fields, SPLIT_SUM_SELECT);
}
if (!real_order)
/* Drop the ORDER BY clause if none of the columns contain any data that
can produce a meaningful sorted set. */
if (!requires_sorting)
order= NULL;
}
else
{
/* The current select does not have an ORDER BY */
if (select_lex->limit_params.with_ties)
{
my_error(ER_WITH_TIES_NEEDS_ORDER, MYF(0));
DBUG_RETURN(-1);
}
}
if (having && having->with_sum_func())
having->split_sum_func2(thd, ref_ptrs, all_fields,
@ -2615,6 +2641,19 @@ int JOIN::optimize_stage2()
if (!order && org_order)
skip_sort_order= 1;
}
/*
For FETCH ... WITH TIES save how many items order by had, after we've
removed constant items that have no relevance on the final sorting.
*/
if (unit->lim.is_with_ties())
{
DBUG_ASSERT(with_ties_order_count == 0);
for (ORDER *it= order; it; it= it->next)
with_ties_order_count+= 1;
}
/*
Check if we can optimize away GROUP BY/DISTINCT.
We can do that if there are no aggregate functions, the
@ -2802,7 +2841,7 @@ int JOIN::optimize_stage2()
if (test_if_subpart(group_list, order) ||
(!group_list && tmp_table_param.sum_func_count))
{
order=0;
order= 0;
if (is_indexed_agg_distinct(this, NULL))
sort_and_group= 0;
}
@ -3738,6 +3777,9 @@ bool JOIN::make_aggr_tables_info()
sort_tab->filesort->limit=
(has_group_by || (join_tab + top_join_tab_count > curr_tab + 1)) ?
select_limit : unit->lim.get_select_limit();
if (unit->lim.is_with_ties())
sort_tab->filesort->limit= HA_POS_ERROR;
}
if (!only_const_tables() &&
!join_tab[const_tables].filesort &&
@ -3774,6 +3816,18 @@ bool JOIN::make_aggr_tables_info()
if (select_lex->custom_agg_func_used())
status_var_increment(thd->status_var.feature_custom_aggregate_functions);
/*
Allocate Cached_items of ORDER BY for FETCH FIRST .. WITH TIES.
The order list might have been modified prior to this, but we are
only interested in the initial order by columns, after all const
elements are removed.
*/
if (unit->lim.is_with_ties())
{
if (alloc_order_fields(this, order, with_ties_order_count))
DBUG_RETURN(true);
}
fields= curr_fields_list;
// Reset before execution
set_items_ref_array(items0);
@ -21982,6 +22036,19 @@ end_send(JOIN *join, JOIN_TAB *join_tab, bool end_of_records)
DBUG_RETURN(NESTED_LOOP_ERROR);
DBUG_RETURN(NESTED_LOOP_OK);
}
if (join->send_records >= join->unit->lim.get_select_limit() &&
join->unit->lim.is_with_ties())
{
/*
Stop sending rows if the order fields corresponding to WITH TIES
have changed.
*/
int idx= test_if_item_cache_changed(join->order_fields);
if (idx >= 0)
join->do_send_rows= false;
}
if (join->do_send_rows)
{
int error;
@ -21998,27 +22065,36 @@ end_send(JOIN *join, JOIN_TAB *join_tab, bool end_of_records)
}
++join->send_records;
if (join->send_records >= join->unit->lim.get_select_limit() &&
!join->do_send_rows)
if (join->send_records >= join->unit->lim.get_select_limit())
{
/*
If we have used Priority Queue for optimizing order by with limit,
then stop here, there are no more records to consume.
When this optimization is used, end_send is called on the next
join_tab.
*/
if (join->order &&
join->select_options & OPTION_FOUND_ROWS &&
join_tab > join->join_tab &&
(join_tab - 1)->filesort && (join_tab - 1)->filesort->using_pq)
if (!join->do_send_rows)
{
DBUG_PRINT("info", ("filesort NESTED_LOOP_QUERY_LIMIT"));
DBUG_RETURN(NESTED_LOOP_QUERY_LIMIT);
/*
If we have used Priority Queue for optimizing order by with limit,
then stop here, there are no more records to consume.
When this optimization is used, end_send is called on the next
join_tab.
*/
if (join->order &&
join->select_options & OPTION_FOUND_ROWS &&
join_tab > join->join_tab &&
(join_tab - 1)->filesort && (join_tab - 1)->filesort->using_pq)
{
DBUG_PRINT("info", ("filesort NESTED_LOOP_QUERY_LIMIT"));
DBUG_RETURN(NESTED_LOOP_QUERY_LIMIT);
}
DBUG_RETURN(NESTED_LOOP_OK);
}
/* For WITH TIES we keep sending rows until a group has changed. */
if (join->unit->lim.is_with_ties())
{
/* Prepare the order_fields comparison for with ties. */
if (join->send_records == join->unit->lim.get_select_limit())
(void) test_if_group_changed(join->order_fields);
/* One more loop, to check if the next row matches with_ties or not. */
DBUG_RETURN(NESTED_LOOP_OK);
}
}
if (join->send_records >= join->unit->lim.get_select_limit() &&
join->do_send_rows)
{
if (join->select_options & OPTION_FOUND_ROWS)
{
JOIN_TAB *jt=join->join_tab;
@ -22095,6 +22171,7 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab, bool end_of_records)
{
if (join->procedure)
join->procedure->end_group();
/* Test if there was a group change. */
if (idx < (int) join->send_group_parts)
{
int error=0;
@ -22113,6 +22190,7 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab, bool end_of_records)
}
else
{
/* Reset all sum functions on group change. */
if (!join->first_record)
{
List_iterator_fast<Item> it(*join->fields);
@ -22152,21 +22230,27 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab, bool end_of_records)
DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */
if (end_of_records)
DBUG_RETURN(NESTED_LOOP_OK);
if (join->send_records >= join->unit->lim.get_select_limit() &&
join->do_send_rows)
{
if (!(join->select_options & OPTION_FOUND_ROWS))
DBUG_RETURN(NESTED_LOOP_QUERY_LIMIT); // Abort nicely
join->do_send_rows=0;
join->unit->lim.set_unlimited();
if (join->send_records >= join->unit->lim.get_select_limit() &&
join->do_send_rows)
{
/* WITH TIES can be computed during end_send_group if
the order by is a subset of group by and we had an index
available to compute group by order directly. */
if (!join->unit->lim.is_with_ties() ||
idx < (int)join->with_ties_order_count)
{
if (!(join->select_options & OPTION_FOUND_ROWS))
DBUG_RETURN(NESTED_LOOP_QUERY_LIMIT); // Abort nicely
join->do_send_rows= 0;
join->unit->lim.set_unlimited();
}
}
else if (join->send_records >= join->fetch_limit)
{
/*
There is a server side cursor and all rows
for this fetch request are sent.
*/
/*
Preventing code duplication. When finished with the group reset
the group functions and copy_fields. We fall through. bug #11904
*/
@ -25221,6 +25305,19 @@ make_group_fields(JOIN *main_join, JOIN *curr_join)
return (0);
}
static bool
fill_cached_item_list(THD *thd, List<Cached_item> *list, ORDER *order,
uint max_number_of_elements = UINT_MAX)
{
for (; order && max_number_of_elements ;
order= order->next, max_number_of_elements--)
{
Cached_item *tmp= new_Cached_item(thd, *order->item, true);
if (!tmp || list->push_front(tmp))
return true;
}
return false;
}
/**
Get a list of buffers for saving last group.
@ -25229,21 +25326,20 @@ make_group_fields(JOIN *main_join, JOIN *curr_join)
*/
static bool
alloc_group_fields(JOIN *join,ORDER *group)
alloc_group_fields(JOIN *join, ORDER *group)
{
if (group)
{
for (; group ; group=group->next)
{
Cached_item *tmp=new_Cached_item(join->thd, *group->item, TRUE);
if (!tmp || join->group_fields.push_front(tmp))
return TRUE;
}
}
if (fill_cached_item_list(join->thd, &join->group_fields, group))
return true;
join->sort_and_group=1; /* Mark for do_select */
return FALSE;
return false;
}
static bool
alloc_order_fields(JOIN *join, ORDER *order, uint max_number_of_elements)
{
return fill_cached_item_list(join->thd, &join->order_fields, order,
max_number_of_elements);
}
/*

View File

@ -1177,6 +1177,16 @@ public:
uint top_join_tab_count;
uint aggr_tables; ///< Number of post-join tmp tables
uint send_group_parts;
/*
This represents the number of items in ORDER BY *after* removing
all const items. This is computed before other optimizations take place,
such as removal of ORDER BY when it is a prefix of GROUP BY, for example:
GROUP BY a, b ORDER BY a
This is used when deciding to send rows, by examining the correct number
of items in the group_fields list when ORDER BY was previously eliminated.
*/
uint with_ties_order_count;
/*
True if the query has GROUP BY.
(that is, if group_by != NULL. when DISTINCT is converted into GROUP BY, it
@ -1306,6 +1316,10 @@ public:
*/
double join_record_count;
List<Item> *fields;
/* Used only for FETCH ... WITH TIES to identify peers. */
List<Cached_item> order_fields;
/* Used during GROUP BY operations to identify when a group has changed. */
List<Cached_item> group_fields, group_fields_cache;
THD *thd;
Item_sum **sum_funcs, ***sum_funcs_end;
@ -1609,6 +1623,8 @@ public:
sjm_lookup_tables= 0;
sjm_scan_tables= 0;
is_orig_degenerated= false;
with_ties_order_count= 0;
}
/* True if the plan guarantees that it will be returned zero or one row */

View File

@ -347,7 +347,7 @@ static_assert(sizeof(YYSTYPE) == sizeof(void*)*2+8, "%union size check");
bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
%}
%pure-parser /* We have threads */
%define api.pure /* We have threads */
%parse-param { THD *thd }
%lex-param { THD *thd }
/*
@ -1625,6 +1625,7 @@ End SQL_MODE_ORACLE_SPECIFIC */
opt_lock_wait_timeout_new
%type <select_limit> opt_limit_clause limit_clause limit_options
fetch_first_clause
%type <order_limit_lock>
query_expression_tail
@ -1731,6 +1732,7 @@ End SQL_MODE_ORACLE_SPECIFIC */
%type <num> view_algorithm view_check_option
%type <view_suid> view_suid opt_view_suid
%type <num> only_or_with_ties
%type <plsql_cursor_attr> plsql_cursor_attr
%type <sp_suid> sp_suid
@ -12484,8 +12486,77 @@ limit_clause:
$$.clear();
Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
}
| fetch_first_clause
{
$$= $1;
if (!$$.select_limit ||
!$$.select_limit->basic_const_item() ||
$$.select_limit->val_int() > 0)
Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT);
}
;
fetch_first_clause:
FETCH_SYM first_or_next row_or_rows only_or_with_ties
{
Item *one= new (thd->mem_root) Item_int(thd, (int32) 1);
if (unlikely(one == NULL))
MYSQL_YYABORT;
$$.select_limit= one;
$$.offset_limit= 0;
$$.explicit_limit= true;
$$.with_ties= $4;
}
| OFFSET_SYM limit_option
FETCH_SYM first_or_next row_or_rows only_or_with_ties
{
Item *one= new (thd->mem_root) Item_int(thd, (int32) 1);
if (unlikely(one == NULL))
MYSQL_YYABORT;
$$.select_limit= one;
$$.offset_limit= $2;
$$.explicit_limit= true;
$$.with_ties= $6;
}
| FETCH_SYM first_or_next limit_option row_or_rows only_or_with_ties
{
$$.select_limit= $3;
$$.offset_limit= 0;
$$.explicit_limit= true;
$$.with_ties= $5;
}
| OFFSET_SYM limit_option
FETCH_SYM first_or_next limit_option row_or_rows only_or_with_ties
{
$$.select_limit= $5;
$$.offset_limit= $2;
$$.explicit_limit= true;
$$.with_ties= $7;
}
| OFFSET_SYM limit_option
{
$$.select_limit= 0;
$$.offset_limit= $2;
$$.explicit_limit= true;
$$.with_ties= false;
}
;
first_or_next:
FIRST_SYM
| NEXT_SYM
;
row_or_rows:
ROW_SYM
| ROWS_SYM
;
only_or_with_ties:
ONLY_SYM { $$= 0; }
| WITH TIES_SYM { $$= 1; }
;
opt_global_limit_clause:
opt_limit_clause
{
@ -12497,20 +12568,23 @@ limit_options:
limit_option
{
$$.select_limit= $1;
$$.offset_limit= 0;
$$.explicit_limit= 1;
$$.offset_limit= NULL;
$$.explicit_limit= true;
$$.with_ties= false;
}
| limit_option ',' limit_option
{
$$.select_limit= $3;
$$.offset_limit= $1;
$$.explicit_limit= 1;
$$.explicit_limit= true;
$$.with_ties= false;
}
| limit_option OFFSET_SYM limit_option
{
$$.select_limit= $1;
$$.offset_limit= $3;
$$.explicit_limit= 1;
$$.explicit_limit= true;
$$.with_ties= false;
}
;
@ -12633,6 +12707,7 @@ opt_procedure_or_into:
}
;
order_or_limit:
order_clause opt_limit_clause
{
@ -15554,6 +15629,7 @@ keyword_sysvar_name:
| WINDOW_SYM
| EXCEPTION_ORACLE_SYM
| IGNORED_SYM
| OFFSET_SYM
;
keyword_set_usual_case:
@ -15569,6 +15645,7 @@ keyword_set_usual_case:
| WINDOW_SYM
| EXCEPTION_ORACLE_SYM
| IGNORED_SYM
| OFFSET_SYM
;
non_reserved_keyword_udt:
@ -15579,6 +15656,7 @@ non_reserved_keyword_udt:
| keyword_sp_block_section
| keyword_sysvar_type
| keyword_sp_var_and_label
| OFFSET_SYM
;
/*
@ -15965,7 +16043,6 @@ keyword_sp_var_and_label:
| NONE_SYM
| NOTFOUND_SYM
| OF_SYM
| OFFSET_SYM
| OLD_PASSWORD_SYM
| ONE_SYM
| ONLINE_SYM

View File

@ -826,11 +826,13 @@ class Lex_select_limit
public:
/* explicit LIMIT clause was used */
bool explicit_limit;
bool with_ties;
Item *select_limit, *offset_limit;
void clear()
{
explicit_limit= FALSE; // No explicit limit given by user
with_ties= FALSE; // No use of WITH TIES operator
select_limit= NULL; // denotes the default limit = HA_POS_ERROR
offset_limit= NULL; // denotes the default offset = 0
}

View File

@ -34,7 +34,7 @@ END//
CREATE PROCEDURE test.dbf_header(in fname VARCHAR(1024)) DETERMINISTIC
BEGIN
DECLARE content BLOB;
DECLARE offset INT;
DECLARE `offset` INT;
DECLARE fieldno INT;
SELECT '--------';
SELECT LOAD_FILE(fname) INTO content;
@ -45,11 +45,11 @@ SELECT CONV(HEX(REVERSE(SUBSTRING(content,9,2))),16,10) AS FirstRecPos;
SELECT CONV(HEX(REVERSE(SUBSTRING(content,11,2))),16,10) AS RecLength;
SELECT HEX(REVERSE(SUBSTRING(content,29,2))) AS TableFlags;
SELECT HEX(REVERSE(SUBSTRING(content,30,1))) AS CodePageMark;
SET offset=33;
SET `offset`=33;
SET fieldno=0;
WHILE SUBSTR(content, offset, 1) <> 0x0D AND offset + 32 < LENGTH(content) DO
CALL dbf_field(fieldno, SUBSTRING(content, offset, 32));
SET offset=offset + 32;
WHILE SUBSTR(content, `offset`, 1) <> 0x0D AND `offset` + 32 < LENGTH(content) DO
CALL dbf_field(fieldno, SUBSTRING(content, `offset`, 32));
SET `offset`=`offset` + 32;
SET fieldno=fieldno + 1;
END WHILE;
SELECT '--------';

View File

@ -34,7 +34,7 @@ END//
CREATE PROCEDURE test.dbf_header(in fname VARCHAR(1024)) DETERMINISTIC
BEGIN
DECLARE content BLOB;
DECLARE offset INT;
DECLARE `offset` INT;
DECLARE fieldno INT;
SELECT '--------';
SELECT LOAD_FILE(fname) INTO content;
@ -45,11 +45,11 @@ BEGIN
SELECT CONV(HEX(REVERSE(SUBSTRING(content,11,2))),16,10) AS RecLength;
SELECT HEX(REVERSE(SUBSTRING(content,29,2))) AS TableFlags;
SELECT HEX(REVERSE(SUBSTRING(content,30,1))) AS CodePageMark;
SET offset=33;
SET `offset`=33;
SET fieldno=0;
WHILE SUBSTR(content, offset, 1) <> 0x0D AND offset + 32 < LENGTH(content) DO
CALL dbf_field(fieldno, SUBSTRING(content, offset, 32));
SET offset=offset + 32;
WHILE SUBSTR(content, `offset`, 1) <> 0x0D AND `offset` + 32 < LENGTH(content) DO
CALL dbf_field(fieldno, SUBSTRING(content, `offset`, 32));
SET `offset`=`offset` + 32;
SET fieldno=fieldno + 1;
END WHILE;
SELECT '--------';