2016-10-25 12:14:27 +00:00
# include "LocalServer.h"
2017-03-14 18:39:06 +00:00
2016-10-25 12:14:27 +00:00
# include <Poco/Util/XMLConfiguration.h>
2016-10-31 14:31:26 +00:00
# include <Poco/Util/HelpFormatter.h>
# include <Poco/Util/OptionCallback.h>
2016-12-13 18:51:19 +00:00
# include <Poco/String.h>
2017-04-01 09:19:00 +00:00
# include <Databases/DatabaseOrdinary.h>
# include <Storages/System/attachSystemTables.h>
# include <Interpreters/Context.h>
# include <Interpreters/ProcessList.h>
# include <Interpreters/executeQuery.h>
# include <Interpreters/loadMetadata.h>
# include <Common/Exception.h>
# include <Common/Macros.h>
# include <Common/ConfigProcessor.h>
# include <Common/escapeForFileName.h>
# include <IO/ReadBufferFromString.h>
2017-07-31 21:39:24 +00:00
# include <IO/WriteBufferFromString.h>
2017-04-01 09:19:00 +00:00
# include <IO/WriteBufferFromFileDescriptor.h>
# include <Parsers/parseQuery.h>
# include <Parsers/IAST.h>
2016-10-25 12:14:27 +00:00
# include <common/ErrorHandlers.h>
2016-10-31 19:54:49 +00:00
# include <common/ApplicationServerExt.h>
2016-12-13 18:51:19 +00:00
# include "StatusFile.h"
2017-04-21 17:47:27 +00:00
# include <Functions/registerFunctions.h>
2017-05-05 20:39:25 +00:00
# include <AggregateFunctions/registerAggregateFunctions.h>
2017-06-10 09:04:31 +00:00
# include <TableFunctions/registerTableFunctions.h>
2016-12-13 18:51:19 +00:00
2016-10-25 12:14:27 +00:00
namespace DB
{
2016-11-11 17:01:02 +00:00
namespace ErrorCodes
{
2017-04-01 07:20:54 +00:00
extern const int SYNTAX_ERROR ;
extern const int CANNOT_LOAD_CONFIG ;
2016-11-11 17:01:02 +00:00
}
LocalServer : : LocalServer ( ) = default ;
2016-12-13 18:51:19 +00:00
LocalServer : : ~ LocalServer ( )
{
2017-04-01 07:20:54 +00:00
if ( context )
context - > shutdown ( ) ; /// required for properly exception handling
2016-12-13 18:51:19 +00:00
}
2016-11-11 17:01:02 +00:00
2016-10-25 12:14:27 +00:00
void LocalServer : : initialize ( Poco : : Util : : Application & self )
{
2017-04-01 07:20:54 +00:00
Poco : : Util : : Application : : initialize ( self ) ;
2016-10-25 12:14:27 +00:00
}
2016-12-13 18:51:19 +00:00
2016-10-25 12:14:27 +00:00
void LocalServer : : defineOptions ( Poco : : Util : : OptionSet & _options )
{
2017-04-01 07:20:54 +00:00
Poco : : Util : : Application : : defineOptions ( _options ) ;
_options . addOption (
Poco : : Util : : Option ( " config-file " , " " , " Load configuration from a given file " )
. required ( false )
. repeatable ( false )
. argument ( " [config.xml] " )
. binding ( " config-file " ) ) ;
/// Arguments that define first query creating initial table:
/// (If structure argument is omitted then initial query is not generated)
_options . addOption (
Poco : : Util : : Option ( " structure " , " S " , " Structe of initial table(list columns names with their types) " )
. required ( false )
. repeatable ( false )
. argument ( " [name Type] " )
. binding ( " table-structure " ) ) ;
_options . addOption (
Poco : : Util : : Option ( " table " , " N " , " Name of intial table " )
. required ( false )
. repeatable ( false )
. argument ( " [table] " )
. binding ( " table-name " ) ) ;
_options . addOption (
Poco : : Util : : Option ( " file " , " f " , " Path to file with data of initial table (stdin if not specified) " )
. required ( false )
. repeatable ( false )
. argument ( " stdin " )
. binding ( " table-file " ) ) ;
_options . addOption (
Poco : : Util : : Option ( " input-format " , " if " , " Input format of intial table data " )
. required ( false )
. repeatable ( false )
. argument ( " [TSV] " )
. binding ( " table-data-format " ) ) ;
/// List of queries to execute
_options . addOption (
Poco : : Util : : Option ( " query " , " q " , " Queries to execute " )
. required ( false )
. repeatable ( false )
. argument ( " <query> " , true )
. binding ( " query " ) ) ;
/// Default Output format
_options . addOption (
Poco : : Util : : Option ( " output-format " , " of " , " Default output format " )
. required ( false )
. repeatable ( false )
. argument ( " [TSV] " , true )
. binding ( " output-format " ) ) ;
/// Alias for previous one, required for clickhouse-client compability
_options . addOption (
Poco : : Util : : Option ( " format " , " " , " Default ouput format " )
. required ( false )
. repeatable ( false )
. argument ( " [TSV] " , true )
. binding ( " format " ) ) ;
_options . addOption (
Poco : : Util : : Option ( " stacktrace " , " " , " Print stack traces of exceptions " )
. required ( false )
. repeatable ( false )
. binding ( " stacktrace " ) ) ;
_options . addOption (
Poco : : Util : : Option ( " verbose " , " " , " Print info about execution of queries " )
. required ( false )
. repeatable ( false )
. noArgument ( )
. binding ( " verbose " ) ) ;
_options . addOption (
Poco : : Util : : Option ( " help " , " " , " Display help information " )
. required ( false )
. repeatable ( false )
. noArgument ( )
. binding ( " help " )
. callback ( Poco : : Util : : OptionCallback < LocalServer > ( this , & LocalServer : : handleHelp ) ) ) ;
/// These arrays prevent "variable tracking size limit exceeded" compiler notice.
static const char * settings_names [ ] = {
2016-12-20 23:01:09 +00:00
# define DECLARE_SETTING(TYPE, NAME, DEFAULT) #NAME,
2017-04-01 07:20:54 +00:00
APPLY_FOR_SETTINGS ( DECLARE_SETTING )
2016-10-31 19:54:49 +00:00
# undef DECLARE_SETTING
2017-04-01 07:20:54 +00:00
nullptr } ;
2016-10-31 19:54:49 +00:00
2017-04-01 07:20:54 +00:00
static const char * limits_names [ ] = {
2016-12-20 23:01:09 +00:00
# define DECLARE_SETTING(TYPE, NAME, DEFAULT) #NAME,
2017-04-01 07:20:54 +00:00
APPLY_FOR_LIMITS ( DECLARE_SETTING )
2016-10-31 19:54:49 +00:00
# undef DECLARE_SETTING
2017-04-01 07:20:54 +00:00
nullptr } ;
2016-12-20 23:01:09 +00:00
2017-04-01 07:20:54 +00:00
for ( const char * * name = settings_names ; * name ; + + name )
_options . addOption ( Poco : : Util : : Option ( * name , " " , " Settings.h " ) . required ( false ) . argument ( " <value> " )
. repeatable ( false ) . binding ( * name ) ) ;
2016-12-20 23:01:09 +00:00
2017-04-01 07:20:54 +00:00
for ( const char * * name = limits_names ; * name ; + + name )
_options . addOption ( Poco : : Util : : Option ( * name , " " , " Limits.h " ) . required ( false ) . argument ( " <value> " )
. repeatable ( false ) . binding ( * name ) ) ;
2016-10-31 19:54:49 +00:00
}
void LocalServer : : applyOptions ( )
{
2017-04-01 07:20:54 +00:00
context - > setDefaultFormat ( config ( ) . getString ( " output-format " , config ( ) . getString ( " format " , " TSV " ) ) ) ;
2016-10-31 19:54:49 +00:00
2017-04-01 07:20:54 +00:00
/// settings and limits could be specified in config file, but passed settings has higher priority
2016-10-31 19:54:49 +00:00
# define EXTRACT_SETTING(TYPE, NAME, DEFAULT) \
2017-04-01 07:20:54 +00:00
if ( config ( ) . has ( # NAME ) & & ! context - > getSettingsRef ( ) . NAME . changed ) \
context - > setSetting ( # NAME , config ( ) . getString ( # NAME ) ) ;
APPLY_FOR_SETTINGS ( EXTRACT_SETTING )
2016-10-31 19:54:49 +00:00
# undef EXTRACT_SETTING
# define EXTRACT_LIMIT(TYPE, NAME, DEFAULT) \
2017-04-01 07:20:54 +00:00
if ( config ( ) . has ( # NAME ) & & ! context - > getSettingsRef ( ) . limits . NAME . changed ) \
context - > setSetting ( # NAME , config ( ) . getString ( # NAME ) ) ;
APPLY_FOR_LIMITS ( EXTRACT_LIMIT )
2016-10-31 19:54:49 +00:00
# undef EXTRACT_LIMIT
2016-10-31 14:31:26 +00:00
}
void LocalServer : : displayHelp ( )
{
2017-04-01 07:20:54 +00:00
Poco : : Util : : HelpFormatter helpFormatter ( options ( ) ) ;
helpFormatter . setCommand ( commandName ( ) ) ;
helpFormatter . setUsage ( " [initial table definition] [--query <query>] " ) ;
helpFormatter . setHeader ( " \n "
" clickhouse-local allows to execute SQL queries on your data files via single command line call. \n "
" To do so, intially you need to define your data source and its format. \n "
" After you can execute your SQL queries in the usual manner. \n "
" There are two ways to define initial table keeping your data: \n "
" either just in first query like this: \n "
" CREATE TABLE <table> (<structure>) ENGINE = File(<input-format>, <file>); \n "
" either through corresponding command line parameters. "
) ;
helpFormatter . setWidth ( 132 ) ; /// 80 is ugly due to wide settings params
helpFormatter . format ( std : : cerr ) ;
std : : cerr < < " Example printing memory used by each Unix user: \n "
" ps aux | tail -n +2 | awk '{ printf( \" %s \\ t%s \\ n \" , $1, $4) }' | "
" clickhouse-local -S \" user String, mem Float64 \" -q \" SELECT user, round(sum(mem), 2) as memTotal FROM table GROUP BY user ORDER BY memTotal DESC FORMAT Pretty \" \n " ;
2016-10-25 12:14:27 +00:00
}
2016-10-31 14:31:26 +00:00
void LocalServer : : handleHelp ( const std : : string & name , const std : : string & value )
{
2017-04-01 07:20:54 +00:00
displayHelp ( ) ;
stopOptionsProcessing ( ) ;
2016-10-31 14:31:26 +00:00
}
2016-12-13 18:51:19 +00:00
/// If path is specified and not empty, will try to setup server environment and load existing metadata
void LocalServer : : tryInitPath ( )
{
2017-04-01 07:20:54 +00:00
if ( ! config ( ) . has ( " path " ) | | ( path = config ( ) . getString ( " path " ) ) . empty ( ) )
return ;
2016-12-13 18:51:19 +00:00
2017-04-01 07:20:54 +00:00
Poco : : trimInPlace ( path ) ;
if ( path . empty ( ) )
return ;
if ( path . back ( ) ! = ' / ' )
path + = ' / ' ;
2016-12-13 18:51:19 +00:00
2017-04-01 07:20:54 +00:00
context - > setPath ( path ) ;
2016-12-13 18:51:19 +00:00
2017-04-01 07:20:54 +00:00
StatusFile status { path + " status " } ;
2016-12-13 18:51:19 +00:00
}
2016-10-25 12:14:27 +00:00
int LocalServer : : main ( const std : : vector < std : : string > & args )
2016-12-13 18:51:19 +00:00
try
2016-10-25 12:14:27 +00:00
{
2017-04-01 07:20:54 +00:00
Logger * log = & logger ( ) ;
if ( ! config ( ) . has ( " query " ) & & ! config ( ) . has ( " table-structure " ) ) /// Nothing to process
{
if ( ! config ( ) . hasOption ( " help " ) )
{
std : : cerr < < " There are no queries to process. " < < std : : endl ;
displayHelp ( ) ;
}
return Application : : EXIT_OK ;
}
/// Load config files if exists
if ( config ( ) . has ( " config-file " ) | | Poco : : File ( " config.xml " ) . exists ( ) )
{
ConfigurationPtr processed_config = ConfigProcessor ( false , true )
. loadConfig ( config ( ) . getString ( " config-file " , " config.xml " ) )
. configuration ;
config ( ) . add ( processed_config . duplicate ( ) , PRIO_DEFAULT , false ) ;
}
2017-06-19 20:31:23 +00:00
context = std : : make_unique < Context > ( Context : : createGlobal ( ) ) ;
2017-04-01 07:20:54 +00:00
context - > setGlobalContext ( * context ) ;
2017-07-11 20:12:15 +00:00
context - > setApplicationType ( Context : : ApplicationType : : LOCAL ) ;
2017-04-01 07:20:54 +00:00
tryInitPath ( ) ;
applyOptions ( ) ;
/// Skip temp path installation
/// We will terminate process on error
static KillingErrorHandler error_handler ;
Poco : : ErrorHandler : : set ( & error_handler ) ;
/// Don't initilaize DateLUT
2017-04-21 17:47:27 +00:00
registerFunctions ( ) ;
2017-05-05 20:39:25 +00:00
registerAggregateFunctions ( ) ;
2017-06-10 09:04:31 +00:00
registerTableFunctions ( ) ;
2017-04-21 17:47:27 +00:00
2017-04-01 07:20:54 +00:00
/// Maybe useless
if ( config ( ) . has ( " macros " ) )
context - > setMacros ( Macros ( config ( ) , " macros " ) ) ;
/// Skip networking
setupUsers ( ) ;
/// Limit on total number of concurrently executing queries.
/// Threre are no need for concurrent threads, override max_concurrent_queries.
context - > getProcessList ( ) . setMaxSize ( 0 ) ;
/// Size of cache for uncompressed blocks. Zero means disabled.
2017-04-12 16:37:19 +00:00
size_t uncompressed_cache_size = config ( ) . getUInt64 ( " uncompressed_cache_size " , 0 ) ;
2017-04-01 07:20:54 +00:00
if ( uncompressed_cache_size )
context - > setUncompressedCache ( uncompressed_cache_size ) ;
/// Size of cache for marks (index of MergeTree family of tables). It is necessary.
/// Specify default value for mark_cache_size explicitly!
2017-04-12 16:37:19 +00:00
size_t mark_cache_size = config ( ) . getUInt64 ( " mark_cache_size " , 5368709120 ) ;
2017-04-01 07:20:54 +00:00
if ( mark_cache_size )
context - > setMarkCache ( mark_cache_size ) ;
/// Load global settings from default profile.
String default_profile_name = config ( ) . getString ( " default_profile " , " default " ) ;
context - > setDefaultProfileName ( default_profile_name ) ;
context - > setSetting ( " profile " , default_profile_name ) ;
/** Init dummy default DB
* NOTE : We force using isolated default database to avoid conflicts with default database from server enviroment
* Otherwise , metadata of temporary File ( format , EXPLICIT_PATH ) tables will pollute metadata / directory ;
* if such tables will not be dropped , clickhouse - server can not load them due to security reasons .
*/
const std : : string default_database = " _local " ;
context - > addDatabase ( default_database , std : : make_shared < DatabaseMemory > ( default_database ) ) ;
context - > setCurrentDatabase ( default_database ) ;
if ( ! path . empty ( ) )
{
LOG_DEBUG ( log , " Loading metadata from " < < path ) ;
2017-06-18 05:43:29 +00:00
loadMetadataSystem ( * context ) ;
attachSystemTables ( ) ;
2017-04-01 07:20:54 +00:00
loadMetadata ( * context ) ;
LOG_DEBUG ( log , " Loaded metadata. " ) ;
}
2017-06-15 20:08:26 +00:00
else
{
attachSystemTables ( ) ;
}
2017-04-01 07:20:54 +00:00
processQueries ( ) ;
context - > shutdown ( ) ;
context . reset ( ) ;
return Application : : EXIT_OK ;
2016-10-25 12:14:27 +00:00
}
2016-12-13 18:51:19 +00:00
catch ( const Exception & e )
{
2017-04-01 07:20:54 +00:00
bool print_stack_trace = config ( ) . has ( " stacktrace " ) ;
2016-12-13 18:51:19 +00:00
2017-04-01 07:20:54 +00:00
std : : string text = e . displayText ( ) ;
2016-12-13 18:51:19 +00:00
2017-04-01 07:20:54 +00:00
auto embedded_stack_trace_pos = text . find ( " Stack trace " ) ;
if ( std : : string : : npos ! = embedded_stack_trace_pos & & ! print_stack_trace )
text . resize ( embedded_stack_trace_pos ) ;
2016-12-13 18:51:19 +00:00
2017-04-01 07:20:54 +00:00
std : : cerr < < " Code: " < < e . code ( ) < < " . " < < text < < std : : endl < < std : : endl ;
2016-12-13 18:51:19 +00:00
2017-04-01 07:20:54 +00:00
if ( print_stack_trace & & std : : string : : npos = = embedded_stack_trace_pos )
{
std : : cerr < < " Stack trace: " < < std : : endl < < e . getStackTrace ( ) . toString ( ) ;
}
2016-12-13 18:51:19 +00:00
2017-04-01 07:20:54 +00:00
/// If exception code isn't zero, we should return non-zero return code anyway.
return e . code ( ) ? e . code ( ) : - 1 ;
2016-12-13 18:51:19 +00:00
}
2016-10-25 12:14:27 +00:00
2016-10-28 17:38:32 +00:00
2016-11-11 17:01:02 +00:00
inline String getQuotedString ( const String & s )
{
2017-07-31 21:39:24 +00:00
WriteBufferFromOwnString buf ;
2017-04-01 07:20:54 +00:00
writeQuotedString ( s , buf ) ;
2017-07-31 21:39:24 +00:00
return buf . str ( ) ;
2016-11-11 17:01:02 +00:00
}
2016-10-28 17:38:32 +00:00
std : : string LocalServer : : getInitialCreateTableQuery ( )
{
2017-04-01 07:20:54 +00:00
if ( ! config ( ) . has ( " table-structure " ) )
return { } ;
auto table_name = backQuoteIfNeed ( config ( ) . getString ( " table-name " , " table " ) ) ;
auto table_structure = config ( ) . getString ( " table-structure " ) ;
auto data_format = backQuoteIfNeed ( config ( ) . getString ( " table-data-format " , " TSV " ) ) ;
String table_file ;
if ( ! config ( ) . has ( " table-file " ) | | config ( ) . getString ( " table-file " ) = = " - " ) /// Use Unix tools stdin naming convention
table_file = " stdin " ;
else /// Use regular file
table_file = getQuotedString ( config ( ) . getString ( " table-file " ) ) ;
return
" CREATE TABLE " + table_name +
" ( " + table_structure + " ) " +
" ENGINE = "
" File( " + data_format + " , " + table_file + " ) "
" ; " ;
2016-10-28 17:38:32 +00:00
}
void LocalServer : : attachSystemTables ( )
{
2017-04-01 07:20:54 +00:00
DatabasePtr system_database = context - > tryGetDatabase ( " system " ) ;
if ( ! system_database )
{
/// TODO: add attachTableDelayed into DatabaseMemory to speedup loading
system_database = std : : make_shared < DatabaseMemory > ( " system " ) ;
context - > addDatabase ( " system " , system_database ) ;
}
2017-06-18 05:43:29 +00:00
attachSystemTablesLocal ( * system_database ) ;
2016-10-28 17:38:32 +00:00
}
2016-10-25 12:14:27 +00:00
void LocalServer : : processQueries ( )
{
2017-04-01 07:20:54 +00:00
Logger * log = & logger ( ) ;
2016-10-25 12:14:27 +00:00
2017-04-01 07:20:54 +00:00
String initial_create_query = getInitialCreateTableQuery ( ) ;
String queries_str = initial_create_query + config ( ) . getString ( " query " ) ;
2016-10-31 14:31:26 +00:00
2017-04-01 07:20:54 +00:00
bool verbose = config ( ) . hasOption ( " verbose " ) ;
2016-10-28 17:38:32 +00:00
2017-04-01 07:20:54 +00:00
std : : vector < String > queries ;
auto parse_res = splitMultipartQuery ( queries_str , queries ) ;
2016-10-25 12:14:27 +00:00
2017-04-01 07:20:54 +00:00
if ( ! parse_res . second )
throw Exception ( " Cannot parse and execute the following part of query: " + String ( parse_res . first ) , ErrorCodes : : SYNTAX_ERROR ) ;
2016-11-11 17:01:02 +00:00
2017-04-01 07:20:54 +00:00
context - > setUser ( " default " , " " , Poco : : Net : : SocketAddress { } , " " ) ;
2016-10-25 12:14:27 +00:00
2017-04-01 07:20:54 +00:00
for ( const auto & query : queries )
{
ReadBufferFromString read_buf ( query ) ;
WriteBufferFromFileDescriptor write_buf ( STDOUT_FILENO ) ;
2016-10-25 12:14:27 +00:00
2017-04-01 07:20:54 +00:00
if ( verbose )
LOG_INFO ( log , " Executing query: " < < query ) ;
2016-10-31 14:31:26 +00:00
2017-04-01 07:20:54 +00:00
executeQuery ( read_buf , write_buf , /* allow_into_outfile = */ true , * context , { } ) ;
}
2016-10-25 12:14:27 +00:00
}
2016-11-11 17:01:02 +00:00
static const char * minimal_default_user_xml =
" <yandex> "
2017-04-01 07:20:54 +00:00
" <profiles> "
" <default></default> "
" </profiles> "
" <users> "
" <default> "
" <password></password> "
" <networks> "
" <ip>::/0</ip> "
" </networks> "
" <profile>default</profile> "
" <quota>default</quota> "
" </default> "
" </users> "
" <quotas> "
" <default></default> "
" </quotas> "
2016-11-11 17:01:02 +00:00
" </yandex> " ;
2016-10-25 12:14:27 +00:00
void LocalServer : : setupUsers ( )
{
2017-04-01 07:20:54 +00:00
ConfigurationPtr users_config ;
if ( config ( ) . has ( " users_config " ) | | config ( ) . has ( " config-file " ) | | Poco : : File ( " config.xml " ) . exists ( ) )
{
auto users_config_path = config ( ) . getString ( " users_config " , config ( ) . getString ( " config-file " , " config.xml " ) ) ;
users_config = ConfigProcessor ( ) . loadConfig ( users_config_path ) . configuration ;
}
else
{
std : : stringstream default_user_stream ;
default_user_stream < < minimal_default_user_xml ;
Poco : : XML : : InputSource default_user_source ( default_user_stream ) ;
users_config = ConfigurationPtr ( new Poco : : Util : : XMLConfiguration ( & default_user_source ) ) ;
}
if ( users_config )
context - > setUsersConfig ( users_config ) ;
else
throw Exception ( " Can't load config for users " , ErrorCodes : : CANNOT_LOAD_CONFIG ) ;
2016-10-31 14:31:26 +00:00
}
2016-10-25 12:14:27 +00:00
}
2016-10-31 19:54:49 +00:00
2017-03-24 15:05:54 +00:00
YANDEX_APP_MAIN_FUNC ( DB : : LocalServer , mainEntryClickHouseLocal ) ;