From 047963922c0c89c76f82cd14eb05ec56c19a91e9 Mon Sep 17 00:00:00 2001 From: Sergei Petrunia Date: Fri, 23 Sep 2016 14:18:29 +0300 Subject: MDEV-9736: Window functions: multiple cursors to read filesort result Add support for having multiple IO_CACHEs with type=READ_CACHE to share the file they are reading from. Each IO_CACHE keeps its own in-memory buffer. When doing a read or seek operation on the file, it notifies other IO_CACHEs that the file position has been changed. Make Rowid_seq_cursor use cloned IO_CACHE when reading filesort result. --- mysys/mf_iocache.c | 148 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 134 insertions(+), 14 deletions(-) (limited to 'mysys/mf_iocache.c') diff --git a/mysys/mf_iocache.c b/mysys/mf_iocache.c index 635e544d367..77581a51d75 100644 --- a/mysys/mf_iocache.c +++ b/mysys/mf_iocache.c @@ -193,6 +193,7 @@ int init_io_cache(IO_CACHE *info, File file, size_t cachesize, info->alloced_buffer = 0; info->buffer=0; info->seek_not_done= 0; + info->next_file_user= NULL; if (file >= 0) { @@ -328,6 +329,101 @@ int init_io_cache(IO_CACHE *info, File file, size_t cachesize, DBUG_RETURN(0); } /* init_io_cache */ + + +/* + Initialize the slave IO_CACHE to read the same file (and data) + as master does. + + One can create multiple slaves from a single master. Every slave and master + will have independent file positions. + + The master must be a non-shared READ_CACHE. + It is assumed that no more reads are done after a master and/or a slave + has been freed (this limitation can be easily lifted). +*/ + +int init_slave_io_cache(IO_CACHE *master, IO_CACHE *slave) +{ + uchar *slave_buf; + DBUG_ASSERT(master->type == READ_CACHE); + DBUG_ASSERT(!master->share); + DBUG_ASSERT(master->alloced_buffer); + + if (!(slave_buf= (uchar*)my_malloc(master->buffer_length, MYF(0)))) + { + return 1; + } + memcpy(slave, master, sizeof(IO_CACHE)); + slave->buffer= slave_buf; + + memcpy(slave->buffer, master->buffer, master->buffer_length); + slave->read_pos= slave->buffer + (master->read_pos - master->buffer); + slave->read_end= slave->buffer + (master->read_end - master->buffer); + + DBUG_ASSERT(master->current_pos == &master->read_pos); + slave->current_pos= &slave->read_pos; + DBUG_ASSERT(master->current_end == &master->read_end); + slave->current_end= &slave->read_end; + + if (master->next_file_user) + { + IO_CACHE *p; + for (p= master->next_file_user; + p->next_file_user !=master; + p= p->next_file_user) + {} + + p->next_file_user= slave; + slave->next_file_user= master; + } + else + { + slave->next_file_user= master; + master->next_file_user= slave; + } + return 0; +} + + +void end_slave_io_cache(IO_CACHE *cache) +{ + my_free(cache->buffer); +} + +/* + Seek a read io cache to a given offset +*/ +void seek_io_cache(IO_CACHE *cache, my_off_t needed_offset) +{ + my_off_t cached_data_start= cache->pos_in_file; + my_off_t cached_data_end= cache->pos_in_file + (cache->read_pos - + cache->buffer); + if (needed_offset >= cached_data_start && + needed_offset < cached_data_end) + { + /* + The offset we're seeking to is in the buffer. + Move buffer's read position accordingly + */ + cache->read_pos= cache->buffer + (needed_offset - cached_data_start); + } + else + { + if (needed_offset > cache->end_of_file) + needed_offset= cache->end_of_file; + /* + The offset we're seeking to is not in the buffer. + - Set the buffer to be exhausted. + - Make the next read to a mysql_file_seek() call to the required + offset (but still use aligned reads). + */ + cache->read_pos= cache->read_end; + cache->seek_not_done= 1; + cache->pos_in_file= (needed_offset / IO_SIZE) * IO_SIZE; + } +} + /* Wait until current request is ready */ #ifdef HAVE_AIOWAIT @@ -583,6 +679,17 @@ int _my_b_cache_read(IO_CACHE *info, uchar *Buffer, size_t Count) { /* No error, reset seek_not_done flag. */ info->seek_not_done= 0; + + if (info->next_file_user) + { + IO_CACHE *c; + for (c= info->next_file_user; + c!= info; + c= c->next_file_user) + { + c->seek_not_done= 1; + } + } } else { @@ -671,22 +778,35 @@ int _my_b_cache_read(IO_CACHE *info, uchar *Buffer, size_t Count) DBUG_RETURN(0); /* EOF */ } } - else if ((length= mysql_file_read(info->file,info->buffer, max_length, + else + { + if (info->next_file_user) + { + IO_CACHE *c; + for (c= info->next_file_user; + c!= info; + c= c->next_file_user) + { + c->seek_not_done= 1; + } + } + if ((length= mysql_file_read(info->file,info->buffer, max_length, info->myflags)) < Count || length == (size_t) -1) - { - /* - We got an read error, or less than requested (end of file). - If not a read error, copy, what we got. - */ - if (length != (size_t) -1) - memcpy(Buffer, info->buffer, length); - info->pos_in_file= pos_in_file; - /* For a read error, return -1, otherwise, what we got in total. */ - info->error= length == (size_t) -1 ? -1 : (int) (length+left_length); - info->read_pos=info->read_end=info->buffer; - info->seek_not_done=1; - DBUG_RETURN(1); + { + /* + We got an read error, or less than requested (end of file). + If not a read error, copy, what we got. + */ + if (length != (size_t) -1) + memcpy(Buffer, info->buffer, length); + info->pos_in_file= pos_in_file; + /* For a read error, return -1, otherwise, what we got in total. */ + info->error= length == (size_t) -1 ? -1 : (int) (length+left_length); + info->read_pos=info->read_end=info->buffer; + info->seek_not_done=1; + DBUG_RETURN(1); + } } /* Count is the remaining number of bytes requested. -- cgit v1.2.1