2016-10-18 14:18:37 +00:00
# include <DB/Storages/StorageFile.h>
2016-11-11 17:01:02 +00:00
# include <DB/Interpreters/Context.h>
2016-10-18 14:18:37 +00:00
# include <DB/IO/ReadBufferFromFile.h>
# include <DB/IO/WriteBufferFromFile.h>
2016-10-25 13:49:07 +00:00
# include <DB/IO/WriteHelpers.h>
2016-11-11 17:01:02 +00:00
# include <DB/DataStreams/FormatFactory.h>
2016-10-18 14:18:37 +00:00
# include <DB/DataStreams/IProfilingBlockInputStream.h>
# include <DB/DataStreams/IBlockOutputStream.h>
# include <DB/Common/escapeForFileName.h>
2016-10-25 13:49:07 +00:00
# include <fcntl.h>
2016-10-18 14:18:37 +00:00
namespace DB
{
2016-10-28 17:38:32 +00:00
namespace ErrorCodes
{
extern const int CANNOT_WRITE_TO_FILE_DESCRIPTOR ;
extern const int CANNOT_SEEK_THROUGH_FILE ;
2016-11-11 17:01:02 +00:00
extern const int DATABASE_ACCESS_DENIED ;
2016-10-28 17:38:32 +00:00
} ;
2016-10-18 14:18:37 +00:00
static std : : string getTablePath ( const std : : string & db_dir_path , const std : : string & table_name , const std : : string & format_name )
{
2016-11-11 17:01:02 +00:00
return db_dir_path + escapeForFileName ( table_name ) + " /data. " + escapeForFileName ( format_name ) ;
}
static void checkCreationIsAllowed ( Context & context_global )
{
if ( context_global . getApplicationType ( ) = = Context : : ApplicationType : : SERVER )
throw Exception ( " Using file descriptor or user specified path as source of storage isn't allowed for server daemons " , ErrorCodes : : DATABASE_ACCESS_DENIED ) ;
2016-10-18 14:18:37 +00:00
}
StorageFile : : StorageFile (
const std : : string & table_path_ ,
int table_fd_ ,
const std : : string & db_dir_path ,
const std : : string & table_name_ ,
const std : : string & format_name_ ,
const NamesAndTypesListPtr & columns_ ,
const NamesAndTypesList & materialized_columns_ ,
const NamesAndTypesList & alias_columns_ ,
const ColumnDefaults & column_defaults_ ,
Context & context_ )
: IStorage ( materialized_columns_ , alias_columns_ , column_defaults_ ) ,
table_name ( table_name_ ) , format_name ( format_name_ ) , columns ( columns_ ) , context_global ( context_ ) , table_fd ( table_fd_ )
{
2016-11-11 17:01:02 +00:00
if ( table_fd < 0 ) /// Will use file
2016-10-18 14:18:37 +00:00
{
2016-10-28 17:38:32 +00:00
use_table_fd = false ;
2016-11-11 17:01:02 +00:00
if ( ! table_path_ . empty ( ) ) /// Is user's file
2016-10-18 14:18:37 +00:00
{
2016-11-11 17:01:02 +00:00
checkCreationIsAllowed ( context_global ) ;
2016-10-18 14:18:37 +00:00
path = Poco : : Path ( table_path_ ) . absolute ( ) . toString ( ) ;
is_db_table = false ;
}
2016-11-11 17:01:02 +00:00
else /// Is DB's file
2016-10-18 14:18:37 +00:00
{
path = getTablePath ( db_dir_path , table_name , format_name ) ;
is_db_table = true ;
Poco : : File ( Poco : : Path ( path ) . parent ( ) ) . createDirectories ( ) ;
}
}
2016-11-11 17:01:02 +00:00
else /// Will use FD
2016-10-18 14:18:37 +00:00
{
2016-11-11 17:01:02 +00:00
checkCreationIsAllowed ( context_global ) ;
2016-10-18 14:18:37 +00:00
is_db_table = false ;
2016-10-28 17:38:32 +00:00
use_table_fd = true ;
2016-11-11 17:01:02 +00:00
/// Save initial offset, it will be used for repeating SELECTs
/// If FD isn't seekable (lseek returns -1), then the second and subsequent SELECTs will fail.
2016-10-28 17:38:32 +00:00
table_fd_init_offset = lseek ( table_fd , 0 , SEEK_CUR ) ;
2016-10-18 14:18:37 +00:00
}
}
class StorageFileBlockInputStream : public IProfilingBlockInputStream
{
public :
StorageFileBlockInputStream ( StorageFile & storage_ , const Context & context , size_t max_block_size )
2016-11-11 17:01:02 +00:00
: storage ( storage_ ) , lock ( storage . rwlock , storage . use_table_fd )
2016-10-18 14:18:37 +00:00
{
2016-10-28 17:38:32 +00:00
if ( storage . use_table_fd )
{
/// We could use common ReadBuffer and WriteBuffer in storage to leverage cache
/// and add ability to seek unseekable files, but cache sync isn't supported.
if ( storage . table_fd_was_used ) /// We need seek to initial position
{
if ( storage . table_fd_init_offset < 0 )
throw Exception ( " File descriptor isn't seekable, inside " + storage . getName ( ) , ErrorCodes : : CANNOT_SEEK_THROUGH_FILE ) ;
/// ReadBuffer's seek() doesn't make sence, since cache is empty
if ( lseek ( storage . table_fd , storage . table_fd_init_offset , SEEK_SET ) < 0 )
2016-11-11 17:01:02 +00:00
throwFromErrno ( " Cannot seek file descriptor, inside " + storage . getName ( ) , ErrorCodes : : CANNOT_SEEK_THROUGH_FILE ) ;
2016-10-28 17:38:32 +00:00
}
storage . table_fd_was_used = true ;
read_buf = std : : make_unique < ReadBufferFromFileDescriptor > ( storage . table_fd ) ;
}
else
{
read_buf = std : : make_unique < ReadBufferFromFile > ( storage . path ) ;
}
2016-10-18 14:18:37 +00:00
2016-11-11 17:01:02 +00:00
reader = FormatFactory ( ) . getInput ( storage . format_name , * read_buf , storage . getSampleBlock ( ) , context , max_block_size ) ;
2016-10-18 14:18:37 +00:00
}
String getName ( ) const override
{
return storage . getName ( ) ;
}
String getID ( ) const override
{
std : : stringstream res_stream ;
2016-11-11 17:01:02 +00:00
res_stream < < " File( " < < storage . format_name < < " , " ;
if ( ! storage . path . empty ( ) )
res_stream < < storage . path ;
else
res_stream < < storage . table_fd ;
res_stream < < " ) " ;
2016-10-18 14:18:37 +00:00
return res_stream . str ( ) ;
}
Block readImpl ( ) override
{
2016-11-11 17:01:02 +00:00
return reader - > read ( ) ;
2016-10-18 14:18:37 +00:00
}
void readPrefixImpl ( ) override
{
2016-11-11 17:01:02 +00:00
reader - > readPrefix ( ) ;
2016-10-18 14:18:37 +00:00
}
void readSuffixImpl ( ) override
{
2016-11-11 17:01:02 +00:00
reader - > readSuffix ( ) ;
2016-10-18 14:18:37 +00:00
}
private :
StorageFile & storage ;
2016-10-28 17:38:32 +00:00
Poco : : ScopedRWLock lock ;
2016-10-18 14:18:37 +00:00
Block sample_block ;
2016-10-28 17:38:32 +00:00
std : : unique_ptr < ReadBufferFromFileDescriptor > read_buf ;
2016-11-11 17:01:02 +00:00
BlockInputStreamPtr reader ;
2016-10-18 14:18:37 +00:00
} ;
BlockInputStreams StorageFile : : read (
const Names & column_names ,
ASTPtr query ,
const Context & context ,
const Settings & settings ,
QueryProcessingStage : : Enum & processed_stage ,
size_t max_block_size ,
unsigned threads )
{
return BlockInputStreams ( 1 , std : : make_shared < StorageFileBlockInputStream > ( * this , context , max_block_size ) ) ;
}
class StorageFileBlockOutputStream : public IBlockOutputStream
{
public :
StorageFileBlockOutputStream ( StorageFile & storage_ )
2016-11-11 17:01:02 +00:00
: storage ( storage_ ) , lock ( storage . rwlock )
2016-10-18 14:18:37 +00:00
{
2016-10-28 17:38:32 +00:00
if ( storage . use_table_fd )
{
2016-11-11 17:01:02 +00:00
/** NOTE: Using real file binded to FD may be misleading:
* SELECT * ; INSERT insert_data ; SELECT * ; last SELECT returns initil_fd_data + insert_data
* INSERT data ; SELECT * ; last SELECT returns only insert_data
*/
2016-10-28 17:38:32 +00:00
storage . table_fd_was_used = true ;
write_buf = std : : make_unique < WriteBufferFromFileDescriptor > ( storage . table_fd ) ;
}
else
{
write_buf = std : : make_unique < WriteBufferFromFile > ( storage . path , DBMS_DEFAULT_BUFFER_SIZE , O_WRONLY | O_APPEND | O_CREAT ) ;
}
2016-10-18 14:18:37 +00:00
2016-11-11 17:01:02 +00:00
writer = FormatFactory ( ) . getOutput ( storage . format_name , * write_buf , storage . getSampleBlock ( ) , storage . context_global ) ;
2016-10-18 14:18:37 +00:00
}
void write ( const Block & block ) override
{
2016-11-11 17:01:02 +00:00
writer - > write ( block ) ;
2016-10-18 14:18:37 +00:00
}
void writePrefix ( ) override
{
2016-11-11 17:01:02 +00:00
writer - > writePrefix ( ) ;
2016-10-18 14:18:37 +00:00
}
void writeSuffix ( ) override
{
2016-11-11 17:01:02 +00:00
writer - > writeSuffix ( ) ;
2016-10-18 14:18:37 +00:00
}
void flush ( ) override
{
2016-11-11 17:01:02 +00:00
writer - > flush ( ) ;
2016-10-18 14:18:37 +00:00
}
private :
StorageFile & storage ;
Poco : : ScopedWriteRWLock lock ;
2016-10-28 17:38:32 +00:00
std : : unique_ptr < WriteBufferFromFileDescriptor > write_buf ;
2016-11-11 17:01:02 +00:00
BlockOutputStreamPtr writer ;
2016-10-18 14:18:37 +00:00
} ;
BlockOutputStreamPtr StorageFile : : write (
ASTPtr query ,
const Settings & settings )
{
return std : : make_shared < StorageFileBlockOutputStream > ( * this ) ;
}
void StorageFile : : drop ( )
{
2016-10-25 13:49:07 +00:00
/// Extra actions are not required.
2016-10-18 14:18:37 +00:00
}
void StorageFile : : rename ( const String & new_path_to_db , const String & new_database_name , const String & new_table_name )
{
if ( ! is_db_table )
2016-11-11 17:01:02 +00:00
throw Exception ( " Can't rename table ' " + table_name + " ' binded to user-defined file (or FD) " , ErrorCodes::DATABASE_ACCESS_DENIED) ;
2016-10-18 14:18:37 +00:00
Poco : : ScopedWriteRWLock lock ( rwlock ) ;
std : : string path_new = getTablePath ( new_path_to_db , new_table_name , format_name ) ;
Poco : : File ( Poco : : Path ( path_new ) . parent ( ) ) . createDirectories ( ) ;
Poco : : File ( path ) . renameTo ( path_new ) ;
path = std : : move ( path_new ) ;
}
}