Skip to content

Commit 32ddafd

Browse files
Merge pull request ClickHouse#79085 from ClickHouse/implicit-table
Implicit table in `clickhouse-local`
2 parents 7453e77 + cd16fd1 commit 32ddafd

16 files changed

+130
-32
lines changed

programs/local/LocalServer.cpp

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -418,12 +418,19 @@ void LocalServer::cleanup()
418418
}
419419

420420

421-
std::string LocalServer::getInitialCreateTableQuery()
421+
std::pair<std::string, std::string> LocalServer::getInitialCreateTableQuery()
422422
{
423-
if (!getClientConfiguration().has("table-structure") && !getClientConfiguration().has("table-file") && !getClientConfiguration().has("table-data-format") && (!isRegularFile(STDIN_FILENO) || queries.empty()))
423+
/// The input data can be specified explicitly with any of the `file`, `structure`, `input-format` command line arguments,
424+
/// or it can be implicitly specified in stdin - then the structure and format is autodetected.
425+
/// But if queries were not specified in the command line, they might me in stdin, and this means that stdin is not input data.
426+
427+
if (!getClientConfiguration().has("table-structure")
428+
&& !getClientConfiguration().has("table-file")
429+
&& !getClientConfiguration().has("table-data-format")
430+
&& (queries.empty() || !isFileDescriptorSuitableForInput(stdin_fd))) /// In we know that there is data in stdin, we can auto-detect the format.
424431
return {};
425432

426-
auto table_name = backQuoteIfNeed(getClientConfiguration().getString("table-name", "table"));
433+
auto table_name = getClientConfiguration().getString("table-name", "table");
427434
auto table_structure = getClientConfiguration().getString("table-structure", "auto");
428435

429436
String table_file;
@@ -442,15 +449,24 @@ std::string LocalServer::getInitialCreateTableQuery()
442449
table_file = quoteString(file_name);
443450
}
444451

445-
String data_format = backQuoteIfNeed(default_input_format);
452+
String data_format;
453+
454+
if (default_input_format == "auto" && getClientConfiguration().has("table-structure"))
455+
data_format = "TabSeparated"; /// Compatibility with older versions when format inference was not available.
456+
else
457+
data_format = backQuoteIfNeed(default_input_format);
446458

447459
if (table_structure == "auto")
448460
table_structure = "";
449461
else
450462
table_structure = "(" + table_structure + ")";
451463

452-
return fmt::format("CREATE TEMPORARY TABLE {} {} ENGINE = File({}, {}, {});",
453-
table_name, table_structure, data_format, table_file, compression);
464+
return
465+
{
466+
table_name,
467+
fmt::format("CREATE TEMPORARY TABLE {} {} ENGINE = File({}, {}, {});",
468+
backQuote(table_name), table_structure, data_format, table_file, compression)
469+
};
454470
}
455471

456472

@@ -626,10 +642,13 @@ try
626642
std::cerr << std::endl;
627643
}
628644

645+
auto [table_name, initial_query] = getInitialCreateTableQuery();
646+
if (!table_name.empty())
647+
client_context->setSetting("implicit_table_at_top_level", table_name);
648+
629649
connect();
630650

631-
String initial_query = getInitialCreateTableQuery();
632-
if (!initial_query.empty())
651+
if (!table_name.empty())
633652
processQueryText(initial_query);
634653

635654
#if USE_FUZZING_MODE
@@ -868,7 +887,7 @@ void LocalServer::processConfig()
868887
#endif
869888

870889
/// NOTE: it is important to apply any overrides before
871-
/// setDefaultProfiles() calls since it will copy current context (i.e.
890+
/// `setDefaultProfiles` calls since it will copy current context (i.e.
872891
/// there is separate context for Buffer tables).
873892
adjustSettings();
874893
applySettingsOverridesForLocal(global_context);

programs/local/LocalServer.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,10 @@ class LocalServer : public ClientApplicationBase, public Loggers
5353
private:
5454
/** Composes CREATE subquery based on passed arguments (--structure --file --table and --input-format)
5555
* This query will be executed first, before queries passed through --query argument
56-
* Returns empty string if it cannot compose that query.
56+
* Returns a pair of the table name and the corresponding create table statement.
57+
* Returns empty strings if it cannot compose that query.
5758
*/
58-
std::string getInitialCreateTableQuery();
59+
std::pair<std::string, std::string> getInitialCreateTableQuery();
5960

6061
void tryInitPath();
6162
void setupUsers();

src/Analyzer/QueryTreeBuilder.cpp

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
#include <Common/quoteString.h>
55

66
#include <DataTypes/FieldToDataType.h>
7-
#include <Parsers/ParserSelectWithUnionQuery.h>
87
#include <Parsers/ASTSelectWithUnionQuery.h>
98
#include <Parsers/ASTSelectIntersectExceptQuery.h>
109
#include <Parsers/ASTExpressionList.h>
@@ -47,12 +46,12 @@
4746

4847
#include <Databases/IDatabase.h>
4948

50-
#include <Interpreters/StorageID.h>
5149
#include <Interpreters/Context.h>
5250

5351

5452
namespace DB
5553
{
54+
5655
namespace Setting
5756
{
5857
extern const SettingsBool allow_experimental_variant_type;
@@ -62,6 +61,7 @@ namespace Setting
6261
extern const SettingsUInt64 limit;
6362
extern const SettingsUInt64 offset;
6463
extern const SettingsBool use_variant_as_common_type;
64+
extern const SettingsString implicit_table_at_top_level;
6565
}
6666

6767

@@ -123,7 +123,7 @@ class QueryTreeBuilder
123123

124124
QueryTreeNodePtr buildWindow(const ASTPtr & window_definition, const ContextPtr & context) const;
125125

126-
QueryTreeNodePtr buildJoinTree(const ASTSelectQuery & select_query, const ContextPtr & context) const;
126+
QueryTreeNodePtr buildJoinTree(bool is_subquery, const ASTSelectQuery & select_query, const ContextPtr & context) const;
127127

128128
ColumnTransformersNodes buildColumnTransformers(const ASTPtr & matcher_expression, const ContextPtr & context) const;
129129

@@ -239,7 +239,8 @@ QueryTreeNodePtr QueryTreeBuilder::buildSelectIntersectExceptQuery(const ASTPtr
239239
return union_node;
240240
}
241241

242-
QueryTreeNodePtr QueryTreeBuilder::buildSelectExpression(const ASTPtr & select_query,
242+
QueryTreeNodePtr QueryTreeBuilder::buildSelectExpression(
243+
const ASTPtr & select_query,
243244
bool is_subquery,
244245
const std::string & cte_name,
245246
const ASTPtr & aliases,
@@ -313,7 +314,7 @@ QueryTreeNodePtr QueryTreeBuilder::buildSelectExpression(const ASTPtr & select_q
313314

314315
auto current_context = current_query_tree->getContext();
315316

316-
current_query_tree->getJoinTree() = buildJoinTree(select_query_typed, current_context);
317+
current_query_tree->getJoinTree() = buildJoinTree(is_subquery, select_query_typed, current_context);
317318

318319
auto select_with_list = select_query_typed.with();
319320
if (select_with_list)
@@ -853,16 +854,23 @@ std::shared_ptr<TableFunctionNode> QueryTreeBuilder::buildTableFunction(const AS
853854
return node;
854855
}
855856

856-
QueryTreeNodePtr QueryTreeBuilder::buildJoinTree(const ASTSelectQuery & select_query, const ContextPtr & context) const
857+
QueryTreeNodePtr QueryTreeBuilder::buildJoinTree(bool is_subquery, const ASTSelectQuery & select_query, const ContextPtr & context) const
857858
{
858859
const auto & tables_in_select_query = select_query.tables();
859860
if (!tables_in_select_query)
860861
{
861-
/** If no table is specified in SELECT query we substitute system.one table.
862-
* SELECT * FROM system.one;
862+
/** If no table is specified in SELECT query,
863+
* if 'implicit_table_at_top_level' is set, we substitute it as a table,
864+
* otherwise, we substitute the system.one table: SELECT * FROM system.one;
863865
*/
864-
Identifier storage_identifier("system.one");
865-
return std::make_shared<IdentifierNode>(storage_identifier);
866+
if (!is_subquery)
867+
{
868+
String implicit_table = context->getSettingsRef()[Setting::implicit_table_at_top_level];
869+
if (!implicit_table.empty())
870+
return std::make_shared<IdentifierNode>(Identifier(implicit_table));
871+
}
872+
873+
return std::make_shared<IdentifierNode>(Identifier("system.one"));
866874
}
867875

868876
auto & tables = tables_in_select_query->as<ASTTablesInSelectQuery &>();

src/Analyzer/Resolve/QueryAnalyzer.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ namespace Setting
113113
extern const SettingsBool allow_suspicious_types_in_order_by;
114114
extern const SettingsBool allow_not_comparable_types_in_order_by;
115115
extern const SettingsBool use_concurrency_control;
116+
extern const SettingsString implicit_table_at_top_level;
116117
}
117118

118119

@@ -581,6 +582,7 @@ void QueryAnalyzer::evaluateScalarSubqueryIfNeeded(QueryTreeNodePtr & node, Iden
581582
Settings subquery_settings = context->getSettingsCopy();
582583
subquery_settings[Setting::max_result_rows] = 1;
583584
subquery_settings[Setting::extremes] = false;
585+
subquery_settings[Setting::implicit_table_at_top_level] = "";
584586
/// When execute `INSERT INTO t WITH ... SELECT ...`, it may lead to `Unknown columns`
585587
/// exception with this settings enabled(https://github.com/ClickHouse/ClickHouse/issues/52494).
586588
subquery_settings[Setting::use_structure_from_insertion_table_in_table_functions] = false;

src/Client/ClientBase.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -877,10 +877,11 @@ void ClientBase::initClientContext()
877877
client_context->setQueryParameters(query_parameters);
878878
}
879879

880-
bool ClientBase::isRegularFile(int fd)
880+
bool ClientBase::isFileDescriptorSuitableForInput(int fd)
881881
{
882882
struct stat file_stat;
883-
return fstat(fd, &file_stat) == 0 && S_ISREG(file_stat.st_mode);
883+
return fstat(fd, &file_stat) == 0
884+
&& (S_ISREG(file_stat.st_mode) || S_ISLNK(file_stat.st_mode));
884885
}
885886

886887
void ClientBase::setDefaultFormatsAndCompressionFromConfiguration()
@@ -900,7 +901,7 @@ void ClientBase::setDefaultFormatsAndCompressionFromConfiguration()
900901
default_output_format = "Vertical";
901902
is_default_format = false;
902903
}
903-
else if (isRegularFile(stdout_fd))
904+
else if (isFileDescriptorSuitableForInput(stdout_fd))
904905
{
905906
std::optional<String> format_from_file_name = FormatFactory::instance().tryGetFormatFromFileDescriptor(stdout_fd);
906907
if (format_from_file_name)
@@ -936,15 +937,15 @@ void ClientBase::setDefaultFormatsAndCompressionFromConfiguration()
936937
if (format_from_file_name)
937938
default_input_format = *format_from_file_name;
938939
else
939-
default_input_format = "TSV";
940+
default_input_format = "auto";
940941
}
941942
else
942943
{
943944
std::optional<String> format_from_file_name = FormatFactory::instance().tryGetFormatFromFileDescriptor(stdin_fd);
944945
if (format_from_file_name)
945946
default_input_format = *format_from_file_name;
946947
else
947-
default_input_format = "TSV";
948+
default_input_format = "auto";
948949

949950
std::optional<String> file_name = tryGetFileNameFromFileDescriptor(stdin_fd);
950951
if (file_name)

src/Client/ClientBase.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ class ClientBase
269269
static bool isSyncInsertWithData(const ASTInsertQuery & insert_query, const ContextPtr & context);
270270
bool processMultiQueryFromFile(const String & file_name);
271271

272-
static bool isRegularFile(int fd);
272+
static bool isFileDescriptorSuitableForInput(int fd);
273273

274274
/// Adjust some settings after command line options and config had been processed.
275275
void adjustSettings();

src/Core/Settings.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6401,6 +6401,19 @@ Enable pushing user roles from originator to other nodes while performing a quer
64016401
)", 0) \
64026402
DECLARE(Bool, shared_merge_tree_sync_parts_on_partition_operations, true, R"(
64036403
Automatically synchronize set of data parts after MOVE|REPLACE|ATTACH partition operations in SMT tables. Cloud only
6404+
)", 0) \
6405+
DECLARE(String, implicit_table_at_top_level, "", R"(
6406+
If not empty, queries without FROM at the top level will read from this table instead of system.one.
6407+
6408+
This is used in clickhouse-local for input data processing.
6409+
The setting could be set explicitly by a user but is not intended for this type of usage.
6410+
6411+
Subqueries are not affected by this setting (neither scalar, FROM, or IN subqueries).
6412+
SELECTs at the top level of UNION, INTERSECT, EXCEPT chains are treated uniformly and affected by this setting, regardless of their grouping in parentheses.
6413+
It is unspecified how this setting affects views and distributed queries.
6414+
6415+
The setting accepts a table name (then the table is resolved from the current database) or a qualified name in the form of 'database.table'.
6416+
Both database and table names have to be unquoted - only simple identifiers are allowed.
64046417
)", 0) \
64056418
\
64066419
DECLARE(Bool, allow_experimental_variant_type, true, R"(

src/Core/SettingsChangesHistory.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ const VersionToSettingsChangesMap & getSettingsChangesHistory()
6969
addSettingsChanges(settings_changes_history, "25.5",
7070
{
7171
{"secondary_indices_enable_bulk_filtering", false, true, "A new algorithm for filtering by data skipping indices"},
72+
{"implicit_table_at_top_level", "", "", "A new setting, used in clickhouse-local"},
7273
});
7374
addSettingsChanges(settings_changes_history, "25.4",
7475
{

src/Interpreters/ExecuteScalarSubqueriesVisitor.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,12 @@
55
#include <Core/Settings.h>
66
#include <DataTypes/DataTypeNullable.h>
77
#include <DataTypes/DataTypeTuple.h>
8-
#include <IO/WriteBufferFromString.h>
98
#include <IO/WriteHelpers.h>
109
#include <Interpreters/Context.h>
1110
#include <Interpreters/InterpreterSelectWithUnionQuery.h>
1211
#include <Interpreters/ProcessorsProfileLog.h>
1312
#include <Interpreters/addTypeConversionToAST.h>
1413
#include <Interpreters/misc.h>
15-
#include <Parsers/ASTExpressionList.h>
1614
#include <Parsers/ASTFunction.h>
1715
#include <Parsers/ASTLiteral.h>
1816
#include <Parsers/ASTSelectQuery.h>
@@ -38,6 +36,7 @@ namespace Setting
3836
extern const SettingsBool extremes;
3937
extern const SettingsUInt64 max_result_rows;
4038
extern const SettingsBool use_concurrency_control;
39+
extern const SettingsString implicit_table_at_top_level;
4140
}
4241

4342
namespace ErrorCodes
@@ -96,6 +95,7 @@ static auto getQueryInterpreter(const ASTSubquery & subquery, ExecuteScalarSubqu
9695
Settings subquery_settings = data.getContext()->getSettingsCopy();
9796
subquery_settings[Setting::max_result_rows] = 1;
9897
subquery_settings[Setting::extremes] = false;
98+
subquery_settings[Setting::implicit_table_at_top_level] = "";
9999
subquery_context->setSettings(subquery_settings);
100100

101101
if (subquery_context->hasQueryContext())

src/Interpreters/InterpreterSelectQuery.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ namespace Setting
193193
extern const SettingsUInt64 max_bytes_to_transfer;
194194
extern const SettingsUInt64 max_rows_to_transfer;
195195
extern const SettingsOverflowMode transfer_overflow_mode;
196+
extern const SettingsString implicit_table_at_top_level;
196197
}
197198

198199
namespace ServerSetting
@@ -355,6 +356,7 @@ ContextPtr getSubqueryContext(const ContextPtr & context)
355356
Settings subquery_settings = context->getSettingsCopy();
356357
subquery_settings[Setting::max_result_rows] = 0;
357358
subquery_settings[Setting::max_result_bytes] = 0;
359+
subquery_settings[Setting::implicit_table_at_top_level] = "";
358360
/// The calculation of extremes does not make sense and is not necessary (if you do it, then the extremes of the subquery can be taken for whole query).
359361
subquery_settings[Setting::extremes] = false;
360362
subquery_context->setSettings(subquery_settings);

0 commit comments

Comments
 (0)