2014-10-26 00:01:36 +00:00
# include <DB/Interpreters/InterpreterSelectQuery.h>
2014-10-27 04:18:13 +00:00
# include <DB/Interpreters/InterpreterInsertQuery.h>
2014-10-26 00:01:36 +00:00
# include <DB/Interpreters/InterpreterAlterQuery.h>
2015-01-18 08:25:56 +00:00
# include <DB/DataStreams/IProfilingBlockInputStream.h>
2016-05-13 21:08:19 +00:00
# include <DB/Databases/IDatabase.h>
2014-10-26 00:01:36 +00:00
# include <DB/Storages/StorageBuffer.h>
2014-10-27 04:18:13 +00:00
# include <DB/Parsers/ASTInsertQuery.h>
2015-11-08 00:28:12 +00:00
# include <DB/Parsers/ASTIdentifier.h>
2015-09-24 18:54:21 +00:00
# include <DB/Common/setThreadName.h>
2014-10-26 00:01:36 +00:00
# include <Poco/Ext/ThreadNumber.h>
2015-10-05 00:33:43 +00:00
# include <ext/range.hpp>
2014-10-27 04:18:13 +00:00
2014-10-26 00:01:36 +00:00
namespace DB
{
2016-01-11 21:46:36 +00:00
namespace ErrorCodes
{
extern const int INFINITE_LOOP ;
extern const int BLOCKS_HAS_DIFFERENT_STRUCTURE ;
}
2014-10-26 00:01:36 +00:00
2015-06-09 19:43:06 +00:00
StoragePtr StorageBuffer : : create ( const std : : string & name_ , NamesAndTypesListPtr columns_ ,
const NamesAndTypesList & materialized_columns_ ,
const NamesAndTypesList & alias_columns_ ,
const ColumnDefaults & column_defaults_ ,
Context & context_ ,
2014-10-26 00:01:36 +00:00
size_t num_shards_ , const Thresholds & min_thresholds_ , const Thresholds & max_thresholds_ ,
const String & destination_database_ , const String & destination_table_ )
{
2016-08-26 21:25:05 +00:00
return make_shared (
2015-06-09 19:43:06 +00:00
name_ , columns_ , materialized_columns_ , alias_columns_ , column_defaults_ ,
2016-08-26 21:25:05 +00:00
context_ , num_shards_ , min_thresholds_ , max_thresholds_ , destination_database_ , destination_table_ ) ; ;
2014-10-26 00:01:36 +00:00
}
2015-06-09 19:43:06 +00:00
StorageBuffer : : StorageBuffer ( const std : : string & name_ , NamesAndTypesListPtr columns_ ,
const NamesAndTypesList & materialized_columns_ ,
const NamesAndTypesList & alias_columns_ ,
const ColumnDefaults & column_defaults_ ,
Context & context_ ,
2014-10-26 00:01:36 +00:00
size_t num_shards_ , const Thresholds & min_thresholds_ , const Thresholds & max_thresholds_ ,
const String & destination_database_ , const String & destination_table_ )
2015-06-09 19:43:06 +00:00
: IStorage { materialized_columns_ , alias_columns_ , column_defaults_ } ,
name ( name_ ) , columns ( columns_ ) , context ( context_ ) ,
2014-10-26 00:01:36 +00:00
num_shards ( num_shards_ ) , buffers ( num_shards_ ) ,
min_thresholds ( min_thresholds_ ) , max_thresholds ( max_thresholds_ ) ,
destination_database ( destination_database_ ) , destination_table ( destination_table_ ) ,
no_destination ( destination_database . empty ( ) & & destination_table . empty ( ) ) ,
log ( & Logger : : get ( " StorageBuffer ( " + name + " ) " ) ) ,
2014-12-03 13:01:00 +00:00
flush_thread ( & StorageBuffer : : flushThread , this )
2014-10-26 00:01:36 +00:00
{
}
/// Читает из одного буфера (из одного блока) под е г о mutex-ом.
class BufferBlockInputStream : public IProfilingBlockInputStream
{
public :
BufferBlockInputStream ( const Names & column_names_ , StorageBuffer : : Buffer & buffer_ )
: column_names ( column_names_ . begin ( ) , column_names_ . end ( ) ) , buffer ( buffer_ ) { }
2015-06-08 20:22:02 +00:00
String getName ( ) const { return " Buffer " ; }
2014-10-26 00:01:36 +00:00
String getID ( ) const
{
std : : stringstream res ;
res < < " Buffer( " < < & buffer ;
for ( const auto & name : column_names )
res < < " , " < < name ;
res < < " ) " ;
return res . str ( ) ;
}
protected :
Block readImpl ( )
{
2014-10-26 00:12:39 +00:00
Block res ;
if ( has_been_read )
return res ;
has_been_read = true ;
2014-10-26 00:01:36 +00:00
std : : lock_guard < std : : mutex > lock ( buffer . mutex ) ;
2016-03-21 23:04:50 +00:00
if ( ! buffer . data . rowsInFirstColumn ( ) )
2014-10-26 00:01:36 +00:00
return res ;
2015-02-13 20:37:30 +00:00
for ( const auto & name : column_names )
2014-10-26 00:01:36 +00:00
{
2015-02-13 20:37:30 +00:00
auto & col = buffer . data . getByName ( name ) ;
2015-07-17 01:27:35 +00:00
res . insert ( ColumnWithTypeAndName ( col . column - > clone ( ) , col . type , name ) ) ;
2014-10-26 00:01:36 +00:00
}
return res ;
}
private :
NameSet column_names ;
StorageBuffer : : Buffer & buffer ;
2014-10-26 00:12:39 +00:00
bool has_been_read = false ;
2014-10-26 00:01:36 +00:00
} ;
BlockInputStreams StorageBuffer : : read (
const Names & column_names ,
ASTPtr query ,
2014-12-17 11:53:17 +00:00
const Context & context ,
2014-10-26 00:01:36 +00:00
const Settings & settings ,
QueryProcessingStage : : Enum & processed_stage ,
size_t max_block_size ,
unsigned threads )
{
processed_stage = QueryProcessingStage : : FetchColumns ;
BlockInputStreams streams_from_dst ;
if ( ! no_destination )
2015-02-27 20:39:34 +00:00
{
auto destination = context . getTable ( destination_database , destination_table ) ;
if ( destination . get ( ) = = this )
throw Exception ( " Destination table is myself. Read will cause infinite loop. " , ErrorCodes : : INFINITE_LOOP ) ;
2016-03-04 02:54:26 +00:00
/** Отключаем оптимизацию "перенос в PREWHERE",
* т а к к а к Buffer н е п о д д е р ж и в а е т PREWHERE .
*/
Settings modified_settings = settings ;
modified_settings . optimize_move_to_prewhere = false ;
streams_from_dst = destination - > read ( column_names , query , context , modified_settings , processed_stage , max_block_size , threads ) ;
2015-02-27 20:39:34 +00:00
}
2014-10-26 00:01:36 +00:00
BlockInputStreams streams_from_buffers ;
streams_from_buffers . reserve ( num_shards ) ;
for ( auto & buf : buffers )
2016-05-28 12:22:22 +00:00
streams_from_buffers . push_back ( std : : make_shared < BufferBlockInputStream > ( column_names , buf ) ) ;
2014-10-26 00:01:36 +00:00
/** Если источники из таблицы были обработаны до какой-то не начальной стадии выполнения запроса,
* т о т о г д а и с т о ч н и к и и з б у ф е р о в н а д о т о ж е о б е р н у т ь в к о н в е й е р о б р а б о т к и д о т о й ж е с т а д и и .
*/
if ( processed_stage > QueryProcessingStage : : FetchColumns )
for ( auto & stream : streams_from_buffers )
2015-06-18 02:11:05 +00:00
stream = InterpreterSelectQuery ( query , context , processed_stage , 0 , stream ) . execute ( ) . in ;
2014-10-26 00:01:36 +00:00
streams_from_dst . insert ( streams_from_dst . end ( ) , streams_from_buffers . begin ( ) , streams_from_buffers . end ( ) ) ;
return streams_from_dst ;
}
2014-10-27 04:18:13 +00:00
static void appendBlock ( const Block & from , Block & to )
{
2015-12-09 06:04:00 +00:00
if ( ! to )
throw Exception ( " Cannot append to empty block " , ErrorCodes : : LOGICAL_ERROR ) ;
2014-10-27 04:18:13 +00:00
size_t rows = from . rows ( ) ;
for ( size_t column_no = 0 , columns = to . columns ( ) ; column_no < columns ; + + column_no )
{
const IColumn & col_from = * from . getByPosition ( column_no ) . column . get ( ) ;
IColumn & col_to = * to . getByPosition ( column_no ) . column . get ( ) ;
if ( col_from . getName ( ) ! = col_to . getName ( ) )
throw Exception ( " Cannot append block to another: different type of columns at index " + toString ( column_no )
+ " . Block 1: " + from . dumpStructure ( ) + " . Block 2: " + to . dumpStructure ( ) , ErrorCodes : : BLOCKS_HAS_DIFFERENT_STRUCTURE ) ;
2015-11-30 20:20:37 +00:00
col_to . insertRangeFrom ( col_from , 0 , rows ) ;
2014-10-27 04:18:13 +00:00
}
}
2014-10-26 00:01:36 +00:00
class BufferBlockOutputStream : public IBlockOutputStream
{
public :
BufferBlockOutputStream ( StorageBuffer & storage_ ) : storage ( storage_ ) { }
void write ( const Block & block )
{
if ( ! block )
return ;
size_t rows = block . rowsInFirstColumn ( ) ;
if ( ! rows )
return ;
2014-10-27 04:18:13 +00:00
StoragePtr destination ;
if ( ! storage . no_destination )
{
destination = storage . context . tryGetTable ( storage . destination_database , storage . destination_table ) ;
2015-05-18 20:28:40 +00:00
if ( destination )
2014-10-27 04:18:13 +00:00
{
2015-05-18 20:28:40 +00:00
if ( destination . get ( ) = = & storage )
throw Exception ( " Destination table is myself. Write will cause infinite loop. " , ErrorCodes : : INFINITE_LOOP ) ;
/// Проверяем структуру таблицы.
try
{
destination - > check ( block , true ) ;
}
catch ( Exception & e )
{
e . addMessage ( " (when looking at destination table " + storage . destination_database + " . " + storage . destination_table + " ) " ) ;
throw ;
}
2014-10-27 04:18:13 +00:00
}
}
2014-10-26 00:01:36 +00:00
size_t bytes = block . bytes ( ) ;
/// Если блок уже превышает максимальные ограничения, то пишем минуя буфер.
if ( rows > storage . max_thresholds . rows | | bytes > storage . max_thresholds . bytes )
{
2014-10-27 04:18:13 +00:00
if ( ! storage . no_destination )
{
LOG_TRACE ( storage . log , " Writing block with " < < rows < < " rows, " < < bytes < < " bytes directly. " ) ;
storage . writeBlockToDestination ( block , destination ) ;
}
2014-10-26 00:01:36 +00:00
return ;
}
/// Распределяем нагрузку по шардам по номеру потока.
const auto start_shard_num = Poco : : ThreadNumber : : get ( ) % storage . num_shards ;
/// Перебираем буферы по кругу, пытаясь заблокировать mutex. Н е более одного круга.
auto shard_num = start_shard_num ;
size_t try_no = 0 ;
for ( ; try_no ! = storage . num_shards ; + + try_no )
{
std : : unique_lock < std : : mutex > lock ( storage . buffers [ shard_num ] . mutex , std : : try_to_lock_t ( ) ) ;
if ( lock . owns_lock ( ) )
{
insertIntoBuffer ( block , storage . buffers [ shard_num ] , std : : move ( lock ) ) ;
break ;
}
+ + shard_num ;
if ( shard_num = = storage . num_shards )
shard_num = 0 ;
}
/// Если так и не удалось ничего сразу заблокировать, то будем ждать на mutex-е .
if ( try_no = = storage . num_shards )
insertIntoBuffer ( block , storage . buffers [ start_shard_num ] , std : : unique_lock < std : : mutex > ( storage . buffers [ start_shard_num ] . mutex ) ) ;
}
private :
StorageBuffer & storage ;
void insertIntoBuffer ( const Block & block , StorageBuffer : : Buffer & buffer , std : : unique_lock < std : : mutex > & & lock )
{
2015-12-09 06:55:49 +00:00
time_t current_time = time ( 0 ) ;
2015-02-13 20:37:30 +00:00
/// Сортируем столбцы в блоке. Это нужно, чтобы было проще потом конкатенировать блоки.
Block sorted_block = block . sortColumns ( ) ;
2014-10-26 00:01:36 +00:00
if ( ! buffer . data )
{
2015-02-13 20:37:30 +00:00
buffer . data = sorted_block . cloneEmpty ( ) ;
2014-10-26 00:01:36 +00:00
}
2015-12-09 06:55:49 +00:00
else if ( storage . checkThresholds ( buffer , current_time , sorted_block . rowsInFirstColumn ( ) , sorted_block . bytes ( ) ) )
2014-10-26 00:01:36 +00:00
{
2015-12-09 06:04:00 +00:00
/** Если после вставки в буфер, ограничения будут превышены, то будем сбрасывать буфер.
* Э т о т а к ж е з а щ и щ а е т о т н е о г р а н и ч е н н о г о п о т р е б л е н и я о п е р а т и в к и , т а к к а к в с л у ч а е н е в о з м о ж н о с т и з а п и с а т ь в т а б л и ц у ,
* б у д е т в ы к и н у т о и с к л ю ч е н и е , а н о в ы е д а н н ы е н е б у д у т д о б а в л е н ы в б у ф е р .
*/
2014-10-26 00:01:36 +00:00
lock . unlock ( ) ;
2015-09-01 21:48:38 +00:00
storage . flushBuffer ( buffer , false ) ;
lock . lock ( ) ;
2014-10-26 00:01:36 +00:00
}
2015-09-01 21:48:38 +00:00
2015-12-09 06:10:13 +00:00
if ( ! buffer . first_write_time )
2015-12-09 06:55:49 +00:00
buffer . first_write_time = current_time ;
2015-12-09 06:10:13 +00:00
2015-09-01 21:48:38 +00:00
appendBlock ( sorted_block , buffer . data ) ;
2014-10-26 00:01:36 +00:00
}
} ;
2015-09-10 20:43:42 +00:00
BlockOutputStreamPtr StorageBuffer : : write ( ASTPtr query , const Settings & settings )
2014-10-26 00:01:36 +00:00
{
2016-05-28 12:22:22 +00:00
return std : : make_shared < BufferBlockOutputStream > ( * this ) ;
2014-10-26 00:01:36 +00:00
}
void StorageBuffer : : shutdown ( )
{
shutdown_event . set ( ) ;
if ( flush_thread . joinable ( ) )
flush_thread . join ( ) ;
2015-09-02 17:47:29 +00:00
try
{
2016-05-16 18:43:38 +00:00
optimize ( { } , { } , context . getSettings ( ) ) ;
2015-09-02 17:47:29 +00:00
}
catch ( . . . )
{
tryLogCurrentException ( __PRETTY_FUNCTION__ ) ;
}
2014-10-26 00:21:06 +00:00
}
2016-05-16 18:43:38 +00:00
bool StorageBuffer : : optimize ( const String & partition , bool final , const Settings & settings )
2014-10-26 00:21:06 +00:00
{
2016-05-16 18:43:38 +00:00
if ( ! partition . empty ( ) )
throw Exception ( " Partition cannot be specified when optimizing table of type Buffer " , ErrorCodes : : NOT_IMPLEMENTED ) ;
if ( final )
throw Exception ( " FINAL cannot be specified when optimizing table of type Buffer " , ErrorCodes : : NOT_IMPLEMENTED ) ;
2014-10-26 00:21:06 +00:00
2016-05-16 18:43:38 +00:00
flushAllBuffers ( false ) ;
2014-10-26 00:21:06 +00:00
return true ;
2014-10-26 00:01:36 +00:00
}
2015-12-09 06:55:49 +00:00
bool StorageBuffer : : checkThresholds ( const Buffer & buffer , time_t current_time , size_t additional_rows , size_t additional_bytes ) const
2014-10-26 00:01:36 +00:00
{
time_t time_passed = 0 ;
if ( buffer . first_write_time )
time_passed = current_time - buffer . first_write_time ;
size_t rows = buffer . data . rowsInFirstColumn ( ) + additional_rows ;
size_t bytes = buffer . data . bytes ( ) + additional_bytes ;
2015-12-09 06:55:49 +00:00
return checkThresholdsImpl ( rows , bytes , time_passed ) ;
}
bool StorageBuffer : : checkThresholdsImpl ( size_t rows , size_t bytes , time_t time_passed ) const
{
2015-12-09 06:16:24 +00:00
return
2014-10-26 00:01:36 +00:00
( time_passed > min_thresholds . time & & rows > min_thresholds . rows & & bytes > min_thresholds . bytes )
| | ( time_passed > max_thresholds . time | | rows > max_thresholds . rows | | bytes > max_thresholds . bytes ) ;
}
2014-12-03 13:28:17 +00:00
void StorageBuffer : : flushAllBuffers ( const bool check_thresholds )
{
for ( auto & buf : buffers )
flushBuffer ( buf , check_thresholds ) ;
}
2014-10-26 00:01:36 +00:00
void StorageBuffer : : flushBuffer ( Buffer & buffer , bool check_thresholds )
{
2015-12-11 02:19:32 +00:00
Block block_to_write ;
2015-12-09 06:55:49 +00:00
time_t current_time = time ( 0 ) ;
2014-10-26 00:01:36 +00:00
2015-12-09 06:16:24 +00:00
size_t rows = 0 ;
size_t bytes = 0 ;
time_t time_passed = 0 ;
2014-10-27 04:18:13 +00:00
/** Довольно много проблем из-за того, что хотим блокировать буфер лишь на короткое время.
* П о д б л о к и р о в к о й , п о л у ч а е м и з б у ф е р а б л о к , и з а м е н я е м в н ё м б л о к н а н о в ы й п у с т о й .
* З а т е м п ы т а е м с я з а п и с а т ь п о л у ч е н н ы й б л о к в п о д ч и н ё н н у ю т а б л и ц у .
* Е с л и э т о г о н е п о л у ч и л о с ь - к л а д ё м д а н н ы е о б р а т н о в б у ф е р .
* З а м е ч а н и е : м о ж е т б ы т ь , с т о и т и з б а в и т ь с я о т т а к о й с л о ж н о с т и .
*/
2014-10-26 00:01:36 +00:00
{
std : : lock_guard < std : : mutex > lock ( buffer . mutex ) ;
2015-12-11 02:19:32 +00:00
block_to_write = buffer . data . cloneEmpty ( ) ;
2015-12-09 06:16:24 +00:00
rows = buffer . data . rowsInFirstColumn ( ) ;
bytes = buffer . data . bytes ( ) ;
if ( buffer . first_write_time )
time_passed = current_time - buffer . first_write_time ;
2015-09-01 21:48:38 +00:00
if ( check_thresholds )
{
2015-12-09 06:55:49 +00:00
if ( ! checkThresholdsImpl ( rows , bytes , time_passed ) )
2015-09-01 21:48:38 +00:00
return ;
}
else
{
2015-12-09 06:16:24 +00:00
if ( rows = = 0 )
2015-09-01 21:48:38 +00:00
return ;
}
2014-10-26 00:01:36 +00:00
buffer . data . swap ( block_to_write ) ;
buffer . first_write_time = 0 ;
}
2015-12-09 06:16:24 +00:00
LOG_TRACE ( log , " Flushing buffer with " < < rows < < " rows, " < < bytes < < " bytes, age " < < time_passed < < " seconds. " ) ;
2014-10-27 04:18:13 +00:00
if ( no_destination )
return ;
try
2014-10-26 00:01:36 +00:00
{
2014-10-27 04:18:13 +00:00
writeBlockToDestination ( block_to_write , context . tryGetTable ( destination_database , destination_table ) ) ;
}
catch ( . . . )
{
/// Возвращаем блок на место в буфер.
std : : lock_guard < std : : mutex > lock ( buffer . mutex ) ;
if ( buffer . data )
{
/** Так как структура таблицы не изменилась, можно склеить два блока.
* З а м е ч а н и е : о с т а ё т с я п р о б л е м а - и з - з а т о г о , ч т о в р а з н ы х п о п ы т к а х в с т а в л я ю т с я р а з н ы е б л о к и ,
* т е р я е т с я и д е м п о т е н т н о с т ь в с т а в к и в ReplicatedMergeTree .
*/
2015-09-01 21:48:38 +00:00
appendBlock ( buffer . data , block_to_write ) ;
2014-10-27 04:18:13 +00:00
}
2015-09-01 21:48:38 +00:00
buffer . data . swap ( block_to_write ) ;
2014-10-27 04:18:13 +00:00
if ( ! buffer . first_write_time )
buffer . first_write_time = current_time ;
/// Через некоторое время будет следующая попытка записать.
throw ;
}
}
2014-10-26 00:01:36 +00:00
2014-10-27 04:18:13 +00:00
void StorageBuffer : : writeBlockToDestination ( const Block & block , StoragePtr table )
{
if ( no_destination | | ! block )
2014-10-26 00:01:36 +00:00
return ;
2014-10-27 04:18:13 +00:00
if ( ! table )
{
LOG_ERROR ( log , " Destination table " < < destination_database < < " . " < < destination_table < < " doesn't exist. Block of data is discarded. " ) ;
return ;
}
2016-05-28 15:42:22 +00:00
auto insert = std : : make_shared < ASTInsertQuery > ( ) ;
2014-10-27 04:18:13 +00:00
insert - > database = destination_database ;
insert - > table = destination_table ;
/** Будем вставлять столбцы, являющиеся пересечением множества столбцов таблицы-буфера и подчинённой таблицы.
* Э т о п о з в о л и т п о д д е р ж а т ь ч а с т ь с л у ч а е в ( н о н е в с е ) , к о г д а с т р у к т у р а т а б л и ц ы н е с о в п а д а е т .
*/
Block structure_of_destination_table = table - > getSampleBlock ( ) ;
Names columns_intersection ;
columns_intersection . reserve ( block . columns ( ) ) ;
for ( size_t i : ext : : range ( 0 , structure_of_destination_table . columns ( ) ) )
{
auto dst_col = structure_of_destination_table . unsafeGetByPosition ( i ) ;
if ( block . has ( dst_col . name ) )
{
if ( block . getByName ( dst_col . name ) . type - > getName ( ) ! = dst_col . type - > getName ( ) )
{
LOG_ERROR ( log , " Destination table " < < destination_database < < " . " < < destination_table
< < " have different type of column " < < dst_col . name < < " . Block of data is discarded. " ) ;
return ;
}
columns_intersection . push_back ( dst_col . name ) ;
}
2014-10-26 00:01:36 +00:00
}
2014-10-27 04:18:13 +00:00
if ( columns_intersection . empty ( ) )
2014-10-26 00:01:36 +00:00
{
2014-10-27 04:18:13 +00:00
LOG_ERROR ( log , " Destination table " < < destination_database < < " . " < < destination_table < < " have no common columns with block in buffer. Block of data is discarded. " ) ;
return ;
2014-10-26 00:01:36 +00:00
}
2014-10-27 04:18:13 +00:00
if ( columns_intersection . size ( ) ! = block . columns ( ) )
LOG_WARNING ( log , " Not all columns from block in buffer exist in destination table "
< < destination_database < < " . " < < destination_table < < " . Some columns are discarded. " ) ;
2016-05-28 15:42:22 +00:00
auto list_of_columns = std : : make_shared < ASTExpressionList > ( ) ;
2014-10-27 04:18:13 +00:00
insert - > columns = list_of_columns ;
list_of_columns - > children . reserve ( columns_intersection . size ( ) ) ;
for ( const String & column : columns_intersection )
2016-05-28 15:42:22 +00:00
list_of_columns - > children . push_back ( std : : make_shared < ASTIdentifier > ( StringRange ( ) , column , ASTIdentifier : : Column ) ) ;
2014-10-27 04:18:13 +00:00
2016-05-28 15:42:22 +00:00
InterpreterInsertQuery interpreter { insert , context } ;
2014-10-27 04:18:13 +00:00
auto block_io = interpreter . execute ( ) ;
block_io . out - > writePrefix ( ) ;
block_io . out - > write ( block ) ;
block_io . out - > writeSuffix ( ) ;
2014-10-26 00:01:36 +00:00
}
void StorageBuffer : : flushThread ( )
{
2015-09-24 18:54:21 +00:00
setThreadName ( " BufferFlush " ) ;
2014-10-26 00:01:36 +00:00
do
{
try
{
2014-12-03 13:28:17 +00:00
flushAllBuffers ( true ) ;
2014-10-26 00:01:36 +00:00
}
catch ( . . . )
{
tryLogCurrentException ( __PRETTY_FUNCTION__ ) ;
}
} while ( ! shutdown_event . tryWait ( 1000 ) ) ;
}
2016-01-28 01:00:27 +00:00
void StorageBuffer : : alter ( const AlterCommands & params , const String & database_name , const String & table_name , const Context & context )
2014-10-26 00:01:36 +00:00
{
2016-05-05 18:28:46 +00:00
for ( const auto & param : params )
if ( param . type = = AlterCommand : : MODIFY_PRIMARY_KEY )
throw Exception ( " Storage engine " + getName ( ) + " doesn't support primary key. " , ErrorCodes : : NOT_IMPLEMENTED ) ;
2014-10-26 00:01:36 +00:00
auto lock = lockStructureForAlter ( ) ;
2014-10-27 04:18:13 +00:00
/// Чтобы не осталось блоков старой структуры.
2016-05-16 18:43:38 +00:00
optimize ( { } , { } , context . getSettings ( ) ) ;
2014-10-27 04:18:13 +00:00
2014-11-12 10:37:47 +00:00
params . apply ( * columns , materialized_columns , alias_columns , column_defaults ) ;
2016-05-13 21:08:19 +00:00
context . getDatabase ( database_name ) - > alterTable (
context , table_name ,
* columns , materialized_columns , alias_columns , column_defaults , { } ) ;
2014-10-26 00:01:36 +00:00
}
}