Merge remote-tracking branch 'upstream/master' into fix25

This commit is contained in:
proller 2019-08-01 09:06:29 +00:00
commit ec981749b1
52 changed files with 932 additions and 241 deletions

View File

@ -9,10 +9,11 @@
#include <IO/ReadHelpers.h>
#include <IO/WriteBufferFromFile.h>
#include <boost/filesystem.hpp>
#include <filesystem>
#include "executeQuery.h"
namespace DB
{
@ -48,7 +49,7 @@ void waitQuery(Connection & connection)
}
}
namespace fs = boost::filesystem;
namespace fs = std::filesystem;
PerformanceTest::PerformanceTest(
const XMLConfigurationPtr & config_,

View File

@ -3,10 +3,11 @@
#include <IO/ReadBufferFromFile.h>
#include <IO/ReadHelpers.h>
#include <IO/WriteBufferFromFile.h>
#include <boost/filesystem.hpp>
#include "applySubstitutions.h"
#include <filesystem>
#include <iostream>
namespace DB
{
namespace ErrorCodes
@ -39,7 +40,7 @@ void extractSettings(
}
namespace fs = boost::filesystem;
namespace fs = std::filesystem;
PerformanceTestInfo::PerformanceTestInfo(
XMLConfigurationPtr config,

View File

@ -4,11 +4,11 @@
#include <regex>
#include <thread>
#include <memory>
#include <filesystem>
#include <port/unistd.h>
#include <sys/stat.h>
#include <boost/filesystem.hpp>
#include <boost/program_options.hpp>
#include <Poco/AutoPtr.h>
@ -36,7 +36,7 @@
#include "ReportBuilder.h"
namespace fs = boost::filesystem;
namespace fs = std::filesystem;
namespace po = boost::program_options;
namespace DB

View File

@ -440,6 +440,7 @@ namespace ErrorCodes
extern const int CANNOT_FCNTL = 463;
extern const int CANNOT_PARSE_ELF = 464;
extern const int CANNOT_PARSE_DWARF = 465;
extern const int INSECURE_PATH = 466;
extern const int KEEPER_EXCEPTION = 999;
extern const int POCO_EXCEPTION = 1000;

View File

@ -85,7 +85,8 @@ namespace
constexpr size_t buf_size = sizeof(char) + // TraceCollector stop flag
8 * sizeof(char) + // maximum VarUInt length for string size
QUERY_ID_MAX_LEN * sizeof(char) + // maximum query_id length
sizeof(StackTrace) + // collected stack trace
sizeof(UInt8) + // number of stack frames
sizeof(StackTrace::Frames) + // collected stack trace, maximum capacity
sizeof(TimerType) + // timer type
sizeof(UInt32); // thread_number
char buffer[buf_size];
@ -101,7 +102,13 @@ namespace
writeChar(false, out);
writeStringBinary(query_id, out);
writePODBinary(stack_trace, out);
size_t stack_trace_size = stack_trace.getSize();
size_t stack_trace_offset = stack_trace.getOffset();
writeIntBinary(UInt8(stack_trace_size - stack_trace_offset), out);
for (size_t i = stack_trace_offset; i < stack_trace_size; ++i)
writePODBinary(stack_trace.getFrames()[i], out);
writePODBinary(timer_type, out);
writePODBinary(thread_number, out);
out.next();

View File

@ -155,7 +155,7 @@ std::string signalToErrorMessage(int sig, const siginfo_t & info, const ucontext
return error.str();
}
void * getCallerAddress(const ucontext_t & context)
static void * getCallerAddress(const ucontext_t & context)
{
#if defined(__x86_64__)
/// Get the address at the time the signal was raised from the RIP (x86-64)
@ -182,12 +182,25 @@ StackTrace::StackTrace(const ucontext_t & signal_context)
{
tryCapture();
if (size == 0)
void * caller_address = getCallerAddress(signal_context);
if (size == 0 && caller_address)
{
/// No stack trace was captured. At least we can try parsing caller address
void * caller_address = getCallerAddress(signal_context);
if (caller_address)
frames[size++] = reinterpret_cast<void *>(caller_address);
frames[0] = caller_address;
size = 1;
}
else
{
/// Skip excessive stack frames that we have created while finding stack trace.
for (size_t i = 0; i < size; ++i)
{
if (frames[i] == caller_address)
{
offset = i;
break;
}
}
}
}
@ -214,21 +227,18 @@ size_t StackTrace::getSize() const
return size;
}
size_t StackTrace::getOffset() const
{
return offset;
}
const StackTrace::Frames & StackTrace::getFrames() const
{
return frames;
}
std::string StackTrace::toString() const
{
/// Calculation of stack trace text is extremely slow.
/// We use simple cache because otherwise the server could be overloaded by trash queries.
static SimpleCache<decltype(StackTrace::toStringImpl), &StackTrace::toStringImpl> func_cached;
return func_cached(frames, size);
}
std::string StackTrace::toStringImpl(const Frames & frames, size_t size)
static std::string toStringImpl(const StackTrace::Frames & frames, size_t offset, size_t size)
{
if (size == 0)
return "<Empty trace>";
@ -238,7 +248,7 @@ std::string StackTrace::toStringImpl(const Frames & frames, size_t size)
std::stringstream out;
for (size_t i = 0; i < size; ++i)
for (size_t i = offset; i < size; ++i)
{
const void * addr = frames[i];
@ -275,3 +285,12 @@ std::string StackTrace::toStringImpl(const Frames & frames, size_t size)
return out.str();
}
std::string StackTrace::toString() const
{
/// Calculation of stack trace text is extremely slow.
/// We use simple cache because otherwise the server could be overloaded by trash queries.
static SimpleCache<decltype(toStringImpl), &toStringImpl> func_cached;
return func_cached(frames, offset, size);
}

View File

@ -34,28 +34,17 @@ public:
/// Creates empty object for deferred initialization
StackTrace(NoCapture);
/// Fills stack trace frames with provided sequence
template <typename Iterator>
StackTrace(Iterator it, Iterator end)
{
while (size < capacity && it != end)
{
frames[size++] = *(it++);
}
}
size_t getSize() const;
size_t getOffset() const;
const Frames & getFrames() const;
std::string toString() const;
protected:
void tryCapture();
static std::string toStringImpl(const Frames & frames, size_t size);
size_t size = 0;
size_t offset = 0; /// How many frames to skip while displaying.
Frames frames{};
};
std::string signalToErrorMessage(int sig, const siginfo_t & info, const ucontext_t & context);
void * getCallerAddress(const ucontext_t & context);

View File

@ -235,7 +235,7 @@ void collectSymbolsFromELF(dl_phdr_info * info,
return;
/// Debug info and symbol table sections may be splitted to separate binary.
std::filesystem::path debug_info_path = std::filesystem::path("/usr/lib/debug") / canonical_path;
std::filesystem::path debug_info_path = std::filesystem::path("/usr/lib/debug") / canonical_path.relative_path();
object_name = std::filesystem::exists(debug_info_path) ? debug_info_path : canonical_path;

View File

@ -105,25 +105,28 @@ void TraceCollector::run()
break;
std::string query_id;
StackTrace stack_trace(NoCapture{});
TimerType timer_type;
UInt32 thread_number;
readStringBinary(query_id, in);
readPODBinary(stack_trace, in);
readPODBinary(timer_type, in);
readPODBinary(thread_number, in);
const auto size = stack_trace.getSize();
const auto & frames = stack_trace.getFrames();
UInt8 size = 0;
readIntBinary(size, in);
Array trace;
trace.reserve(size);
for (size_t i = 0; i < size; i++)
trace.emplace_back(UInt64(reinterpret_cast<uintptr_t>(frames[i])));
{
uintptr_t addr = 0;
readPODBinary(addr, in);
trace.emplace_back(UInt64(addr));
}
TimerType timer_type;
readPODBinary(timer_type, in);
UInt32 thread_number;
readPODBinary(thread_number, in);
TraceLogElement element{std::time(nullptr), timer_type, thread_number, query_id, trace};
trace_log->add(element);
}
}

View File

@ -7,7 +7,7 @@
#include <Common/CompactArray.h>
#include <IO/WriteBufferFromFile.h>
#include <IO/ReadBufferFromFile.h>
#include <boost/filesystem.hpp>
#include <filesystem>
#include <string>
#include <iostream>
#include <fstream>
@ -15,7 +15,7 @@
#include <cstdlib>
#include <port/unistd.h>
namespace fs = boost::filesystem;
namespace fs = std::filesystem;
std::string createTmpPath(const std::string & filename)
{

View File

@ -2,7 +2,7 @@
#include <Functions/FunctionFactory.h>
#include <DataTypes/DataTypesNumber.h>
#include <Interpreters/Context.h>
#include <boost/filesystem.hpp>
#include <filesystem>
#include <Poco/Util/AbstractConfiguration.h>
namespace DB
@ -11,19 +11,19 @@ namespace DB
struct FilesystemAvailable
{
static constexpr auto name = "filesystemAvailable";
static boost::uintmax_t get(boost::filesystem::space_info & spaceinfo) { return spaceinfo.available; }
static std::uintmax_t get(std::filesystem::space_info & spaceinfo) { return spaceinfo.available; }
};
struct FilesystemFree
{
static constexpr auto name = "filesystemFree";
static boost::uintmax_t get(boost::filesystem::space_info & spaceinfo) { return spaceinfo.free; }
static std::uintmax_t get(std::filesystem::space_info & spaceinfo) { return spaceinfo.free; }
};
struct FilesystemCapacity
{
static constexpr auto name = "filesystemCapacity";
static boost::uintmax_t get(boost::filesystem::space_info & spaceinfo) { return spaceinfo.capacity; }
static std::uintmax_t get(std::filesystem::space_info & spaceinfo) { return spaceinfo.capacity; }
};
template <typename Impl>
@ -34,10 +34,10 @@ public:
static FunctionPtr create(const Context & context)
{
return std::make_shared<FilesystemImpl<Impl>>(boost::filesystem::space(context.getConfigRef().getString("path")));
return std::make_shared<FilesystemImpl<Impl>>(std::filesystem::space(context.getConfigRef().getString("path")));
}
explicit FilesystemImpl(boost::filesystem::space_info spaceinfo_) : spaceinfo(spaceinfo_) { }
explicit FilesystemImpl(std::filesystem::space_info spaceinfo_) : spaceinfo(spaceinfo_) { }
String getName() const override { return name; }
size_t getNumberOfArguments() const override { return 0; }
@ -54,7 +54,7 @@ public:
}
private:
boost::filesystem::space_info spaceinfo;
std::filesystem::space_info spaceinfo;
};

View File

@ -55,7 +55,13 @@ struct DivideIntegralImpl
static inline Result apply(A a, B b)
{
throwIfDivisionLeadsToFPE(a, b);
return a / b;
/// Otherwise overflow may occur due to integer promotion. Example: int8_t(-1) / uint64_t(2).
/// NOTE: overflow is still possible when dividing large signed number to large unsigned number or vice-versa. But it's less harmful.
if constexpr (std::is_integral_v<A> && std::is_integral_v<B> && (std::is_signed_v<A> || std::is_signed_v<B>))
return std::make_signed_t<A>(a) / std::make_signed_t<B>(b);
else
return a / b;
}
#if USE_EMBEDDED_COMPILER

View File

@ -1,6 +1,9 @@
#include <Functions/FunctionFactory.h>
#include <Functions/FunctionBinaryArithmetic.h>
#include "intDiv.h"
namespace DB
{
@ -12,7 +15,10 @@ struct DivideIntegralOrZeroImpl
template <typename Result = ResultType>
static inline Result apply(A a, B b)
{
return unlikely(divisionLeadsToFPE(a, b)) ? 0 : a / b;
if (unlikely(divisionLeadsToFPE(a, b)))
return 0;
return DivideIntegralImpl<A, B>::template apply<Result>(a, b);
}
#if USE_EMBEDDED_COMPILER

View File

@ -1,6 +1,6 @@
#include <IO/ReadBufferAIO.h>
#include <Core/Defines.h>
#include <boost/filesystem.hpp>
#include <filesystem>
#include <vector>
#include <iostream>
#include <fstream>
@ -44,7 +44,7 @@ bool test20(const std::string & filename, const std::string & buf);
void run()
{
namespace fs = boost::filesystem;
namespace fs = std::filesystem;
std::string filename;
std::string buf;

View File

@ -1,8 +1,7 @@
#include <IO/WriteBufferAIO.h>
#include <Core/Defines.h>
#include <boost/filesystem.hpp>
#include <filesystem>
#include <iostream>
#include <fstream>
#include <streambuf>
@ -11,7 +10,7 @@
namespace
{
namespace fs = boost::filesystem;
namespace fs = std::filesystem;
void run();
[[noreturn]] void die(const std::string & msg);

View File

@ -35,6 +35,8 @@ namespace ErrorCodes
extern const int TYPE_MISMATCH;
}
/// Read comment near usage
static constexpr auto DUMMY_COLUMN_NAME = "_dummy";
Names ExpressionAction::getNeededColumns() const
{
@ -508,11 +510,15 @@ void ExpressionAction::execute(Block & block, bool dry_run) const
if (can_replace && block.has(result_name))
{
auto & result = block.getByName(result_name);
result.type = result_type;
result.column = block.getByName(source_name).column;
const auto & source = block.getByName(source_name);
result.type = source.type;
result.column = source.column;
}
else
block.insert({ block.getByName(source_name).column, result_type, result_name });
{
const auto & source_column = block.getByName(source_name);
block.insert({source_column.column, source_column.type, result_name});
}
break;
}
@ -927,13 +933,44 @@ void ExpressionActions::finalize(const Names & output_columns)
}
}
/// 1) Sometimes we don't need any columns to perform actions and sometimes actions doesn't produce any columns as result.
/// But Block class doesn't store any information about structure itself, it uses information from column.
/// If we remove all columns from input or output block we will lose information about amount of rows in it.
/// To avoid this situation we always leaving one of the columns in required columns (input)
/// and output column. We choose that "redundant" column by size with help of getSmallestColumn.
///
/// 2) Sometimes we have to read data from different Storages to execute query.
/// For example in 'remote' function which requires to read data from local table (for example MergeTree) and
/// remote table (doesn't know anything about it).
///
/// If we have combination of two previous cases, our heuristic from (1) can choose absolutely different columns,
/// so generated streams with these actions will have different headers. To avoid this we addionaly rename our "redundant" column
/// to DUMMY_COLUMN_NAME with help of COPY_COLUMN action and consequent remove of original column.
/// It doesn't affect any logic, but all streams will have same "redundant" column in header called "_dummy".
/// Also, it seems like we will always have same type (UInt8) of "redundant" column, but it's not obvious.
bool dummy_column_copied = false;
/// We will not throw out all the input columns, so as not to lose the number of rows in the block.
if (needed_columns.empty() && !input_columns.empty())
needed_columns.insert(getSmallestColumn(input_columns));
{
auto colname = getSmallestColumn(input_columns);
needed_columns.insert(colname);
actions.insert(actions.begin(), ExpressionAction::copyColumn(colname, DUMMY_COLUMN_NAME, true));
dummy_column_copied = true;
}
/// We will not leave the block empty so as not to lose the number of rows in it.
if (final_columns.empty() && !input_columns.empty())
final_columns.insert(getSmallestColumn(input_columns));
{
auto colname = getSmallestColumn(input_columns);
final_columns.insert(DUMMY_COLUMN_NAME);
if (!dummy_column_copied) /// otherwise we already have this column
actions.insert(actions.begin(), ExpressionAction::copyColumn(colname, DUMMY_COLUMN_NAME, true));
}
for (NamesAndTypesList::iterator it = input_columns.begin(); it != input_columns.end();)
{
@ -948,9 +985,9 @@ void ExpressionActions::finalize(const Names & output_columns)
}
/* std::cerr << "\n";
for (const auto & action : actions)
std::cerr << action.toString() << "\n";
std::cerr << "\n";*/
for (const auto & action : actions)
std::cerr << action.toString() << "\n";
std::cerr << "\n";*/
/// Deletes unnecessary temporary columns.

View File

@ -257,9 +257,13 @@ public:
};
private:
/// These columns have to be in input blocks (arguments of execute* methods)
NamesAndTypesList input_columns;
/// These actions will be executed on input blocks
Actions actions;
/// The example of result (output) block.
Block sample_block;
Settings settings;
#if USE_EMBEDDED_COMPILER
std::shared_ptr<CompiledExpressionCache> compilation_cache;

View File

@ -1,6 +1,6 @@
#include <Common/Config/ConfigProcessor.h>
#include <Interpreters/UsersManager.h>
#include <boost/filesystem.hpp>
#include <filesystem>
#include <vector>
#include <string>
#include <tuple>
@ -14,7 +14,7 @@
namespace
{
namespace fs = boost::filesystem;
namespace fs = std::filesystem;
struct TestEntry
{

View File

@ -263,7 +263,7 @@ protected:
class ParserColumnsOrIndicesDeclarationList : public IParserBase
{
protected:
protected:
const char * getName() const override { return "columns or indices declaration list"; }
bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override;
};

View File

@ -27,6 +27,7 @@ namespace ErrorCodes
extern const int CANNOT_WRITE_TO_OSTREAM;
extern const int CHECKSUM_DOESNT_MATCH;
extern const int UNKNOWN_TABLE;
extern const int INSECURE_PATH;
}
namespace DataPartsExchange
@ -200,7 +201,7 @@ MergeTreeData::MutableDataPartPtr Fetcher::fetchPart(
String tmp_prefix = tmp_prefix_.empty() ? TMP_PREFIX : tmp_prefix_;
String relative_part_path = String(to_detached ? "detached/" : "") + tmp_prefix + part_name;
String absolute_part_path = data.getFullPath() + relative_part_path + "/";
String absolute_part_path = Poco::Path(data.getFullPath() + relative_part_path + "/").absolute().toString();
Poco::File part_file(absolute_part_path);
if (part_file.exists())
@ -225,7 +226,15 @@ MergeTreeData::MutableDataPartPtr Fetcher::fetchPart(
readStringBinary(file_name, in);
readBinary(file_size, in);
WriteBufferFromFile file_out(absolute_part_path + file_name);
/// File must be inside "absolute_part_path" directory.
/// Otherwise malicious ClickHouse replica may force us to write to arbitrary path.
String absolute_file_path = Poco::Path(absolute_part_path + file_name).absolute().toString();
if (!startsWith(absolute_file_path, absolute_part_path))
throw Exception("File path (" + absolute_file_path + ") doesn't appear to be inside part path (" + absolute_part_path + ")."
" This may happen if we are trying to download part from malicious replica or logical error.",
ErrorCodes::INSECURE_PATH);
WriteBufferFromFile file_out(absolute_file_path);
HashingWriteBuffer hashing_out(file_out);
copyData(in, hashing_out, file_size, blocker.getCounter());

View File

@ -47,7 +47,7 @@
#include <Poco/DirectoryIterator.h>
#include <memory>
#include <boost/filesystem.hpp>
#include <filesystem>
namespace DB
@ -120,13 +120,13 @@ UInt64 getMaximumFileNumber(const std::string & dir_path)
{
UInt64 res = 0;
boost::filesystem::recursive_directory_iterator begin(dir_path);
boost::filesystem::recursive_directory_iterator end;
std::filesystem::recursive_directory_iterator begin(dir_path);
std::filesystem::recursive_directory_iterator end;
for (auto it = begin; it != end; ++it)
{
const auto & file_path = it->path();
if (it->status().type() != boost::filesystem::regular_file || !endsWith(file_path.filename().string(), ".bin"))
if (!std::filesystem::is_regular_file(*it) || !endsWith(file_path.filename().string(), ".bin"))
continue;
UInt64 num = 0;
@ -431,10 +431,10 @@ void StorageDistributed::createDirectoryMonitors()
Poco::File{path}.createDirectory();
boost::filesystem::directory_iterator begin(path);
boost::filesystem::directory_iterator end;
std::filesystem::directory_iterator begin(path);
std::filesystem::directory_iterator end;
for (auto it = begin; it != end; ++it)
if (it->status().type() == boost::filesystem::directory_file)
if (std::filesystem::is_directory(*it))
requireDirectoryMonitor(it->path().filename().string());
}

View File

@ -59,7 +59,8 @@ class TinyLogBlockInputStream final : public IBlockInputStream
public:
TinyLogBlockInputStream(size_t block_size_, const NamesAndTypesList & columns_, StorageTinyLog & storage_, size_t max_read_buffer_size_)
: block_size(block_size_), columns(columns_),
storage(storage_), max_read_buffer_size(max_read_buffer_size_) {}
storage(storage_), lock(storage_.rwlock),
max_read_buffer_size(max_read_buffer_size_) {}
String getName() const override { return "TinyLog"; }
@ -79,6 +80,7 @@ private:
size_t block_size;
NamesAndTypesList columns;
StorageTinyLog & storage;
std::shared_lock<std::shared_mutex> lock;
bool finished = false;
size_t max_read_buffer_size;
@ -109,7 +111,7 @@ class TinyLogBlockOutputStream final : public IBlockOutputStream
{
public:
explicit TinyLogBlockOutputStream(StorageTinyLog & storage_)
: storage(storage_)
: storage(storage_), lock(storage_.rwlock)
{
}
@ -132,6 +134,7 @@ public:
private:
StorageTinyLog & storage;
std::unique_lock<std::shared_mutex> lock;
bool done = false;
struct Stream
@ -373,6 +376,8 @@ void StorageTinyLog::addFiles(const String & column_name, const IDataType & type
void StorageTinyLog::rename(const String & new_path_to_db, const String & new_database_name, const String & new_table_name)
{
std::unique_lock<std::shared_mutex> lock(rwlock);
/// Rename directory with data.
Poco::File(path + escapeForFileName(table_name)).renameTo(new_path_to_db + escapeForFileName(new_table_name));
@ -395,6 +400,8 @@ BlockInputStreams StorageTinyLog::read(
const unsigned /*num_streams*/)
{
check(column_names);
// When reading, we lock the entire storage, because we only have one file
// per column and can't modify it concurrently.
return BlockInputStreams(1, std::make_shared<TinyLogBlockInputStream>(
max_block_size, Nested::collect(getColumns().getAllPhysical().addTypes(column_names)), *this, context.getSettingsRef().max_read_buffer_size));
}
@ -409,6 +416,7 @@ BlockOutputStreamPtr StorageTinyLog::write(
CheckResults StorageTinyLog::checkData(const ASTPtr & /* query */, const Context & /* context */)
{
std::shared_lock<std::shared_mutex> lock(rwlock);
return file_checker.check();
}
@ -417,6 +425,8 @@ void StorageTinyLog::truncate(const ASTPtr &, const Context &)
if (table_name.empty())
throw Exception("Logical error: table name is empty", ErrorCodes::LOGICAL_ERROR);
std::unique_lock<std::shared_mutex> lock(rwlock);
auto file = Poco::File(path + escapeForFileName(table_name));
file.remove(true);
file.createDirectories();

View File

@ -65,6 +65,7 @@ private:
Files_t files;
FileChecker file_checker;
mutable std::shared_mutex rwlock;
Logger * log;

View File

@ -0,0 +1,28 @@
#include <Storages/IStorage.h>
#include <Storages/StorageValues.h>
#include <DataStreams/OneBlockInputStream.h>
namespace DB
{
StorageValues::StorageValues(const std::string & database_name_, const std::string & table_name_, const Block & res_block_)
: database_name(database_name_), table_name(table_name_), res_block(res_block_)
{
setColumns(ColumnsDescription(res_block.getNamesAndTypesList()));
}
BlockInputStreams StorageValues::read(
const Names & column_names,
const SelectQueryInfo & /*query_info*/,
const Context & /*context*/,
QueryProcessingStage::Enum /*processed_stage*/,
size_t /*max_block_size*/,
unsigned /*num_streams*/)
{
check(column_names);
return BlockInputStreams(1, std::make_shared<OneBlockInputStream>(res_block));
}
}

View File

@ -0,0 +1,36 @@
#pragma once
#include <ext/shared_ptr_helper.h>
#include <Storages/IStorage.h>
namespace DB
{
/* One block storage used for values table function
* It's structure is similar to IStorageSystemOneBlock
*/
class StorageValues : public ext::shared_ptr_helper<StorageValues>, public IStorage
{
public:
std::string getName() const override { return "Values"; }
std::string getTableName() const override { return table_name; }
std::string getDatabaseName() const override { return database_name; }
BlockInputStreams read(
const Names & column_names,
const SelectQueryInfo & query_info,
const Context & context,
QueryProcessingStage::Enum processed_stage,
size_t max_block_size,
unsigned num_streams) override;
private:
std::string database_name;
std::string table_name;
Block res_block;
protected:
StorageValues(const std::string & database_name_, const std::string & table_name_, const Block & res_block_);
};
}

View File

@ -11,10 +11,10 @@ NamesAndTypesList StorageSystemTableFunctions::getNamesAndTypes()
void StorageSystemTableFunctions::fillData(MutableColumns & res_columns, const Context &, const SelectQueryInfo &) const
{
const auto & functions = TableFunctionFactory::instance().getAllTableFunctions();
for (const auto & pair : functions)
const auto & functions_names = TableFunctionFactory::instance().getAllRegisteredNames();
for (const auto & name : functions_names)
{
res_columns[0]->insert(pair.first);
res_columns[0]->insert(name);
}
}

View File

@ -1,14 +1,17 @@
#include <TableFunctions/ITableFunction.h>
#include <TableFunctions/ITableFunctionFileLike.h>
#include <TableFunctions/parseColumnsListForTableFunction.h>
#include <Parsers/ASTFunction.h>
#include <Parsers/ASTLiteral.h>
#include <Common/Exception.h>
#include <Common/typeid_cast.h>
#include <Storages/StorageFile.h>
#include <DataTypes/DataTypeFactory.h>
#include <Interpreters/Context.h>
#include <Interpreters/evaluateConstantExpression.h>
#include <boost/algorithm/string.hpp>
namespace DB
@ -21,7 +24,7 @@ namespace ErrorCodes
StoragePtr ITableFunctionFileLike::executeImpl(const ASTPtr & ast_function, const Context & context, const std::string & table_name) const
{
// Parse args
/// Parse args
ASTs & args_func = ast_function->children;
if (args_func.size() != 1)
@ -40,26 +43,12 @@ StoragePtr ITableFunctionFileLike::executeImpl(const ASTPtr & ast_function, cons
std::string format = args[1]->as<ASTLiteral &>().value.safeGet<String>();
std::string structure = args[2]->as<ASTLiteral &>().value.safeGet<String>();
// Create sample block
std::vector<std::string> structure_vals;
boost::split(structure_vals, structure, boost::algorithm::is_any_of(" ,"), boost::algorithm::token_compress_on);
if (structure_vals.size() % 2 != 0)
throw Exception("Odd number of elements in section structure: must be a list of name type pairs", ErrorCodes::LOGICAL_ERROR);
/// Create sample block
Block sample_block;
const DataTypeFactory & data_type_factory = DataTypeFactory::instance();
parseColumnsListFromString(structure, sample_block, context);
for (size_t i = 0, size = structure_vals.size(); i < size; i += 2)
{
ColumnWithTypeAndName column;
column.name = structure_vals[i];
column.type = data_type_factory.get(structure_vals[i + 1]);
column.column = column.type->createColumn();
sample_block.insert(std::move(column));
}
// Create table
/// Create table
StoragePtr storage = getStorage(filename, format, sample_block, const_cast<Context &>(context), table_name);
storage->startup();

View File

@ -1,11 +1,10 @@
#include <TableFunctions/TableFunctionFactory.h>
#include <Interpreters/Context.h>
#include <Common/Exception.h>
#include <IO/WriteHelpers.h>
namespace DB
{
@ -17,11 +16,16 @@ namespace ErrorCodes
}
void TableFunctionFactory::registerFunction(const std::string & name, Creator creator)
void TableFunctionFactory::registerFunction(const std::string & name, Creator creator, CaseSensitiveness case_sensitiveness)
{
if (!functions.emplace(name, std::move(creator)).second)
if (!table_functions.emplace(name, creator).second)
throw Exception("TableFunctionFactory: the table function name '" + name + "' is not unique",
ErrorCodes::LOGICAL_ERROR);
if (case_sensitiveness == CaseInsensitive
&& !case_insensitive_table_functions.emplace(Poco::toLower(name), creator).second)
throw Exception("TableFunctionFactory: the case insensitive table function name '" + name + "' is not unique",
ErrorCodes::LOGICAL_ERROR);
}
TableFunctionPtr TableFunctionFactory::get(
@ -31,8 +35,8 @@ TableFunctionPtr TableFunctionFactory::get(
if (context.getSettings().readonly == 1) /** For example, for readonly = 2 - allowed. */
throw Exception("Table functions are forbidden in readonly mode", ErrorCodes::READONLY);
auto it = functions.find(name);
if (it == functions.end())
auto res = tryGet(name, context);
if (!res)
{
auto hints = getHints(name);
if (!hints.empty())
@ -41,12 +45,29 @@ TableFunctionPtr TableFunctionFactory::get(
throw Exception("Unknown table function " + name, ErrorCodes::UNKNOWN_FUNCTION);
}
return it->second();
return res;
}
TableFunctionPtr TableFunctionFactory::tryGet(
const std::string & name_param,
const Context &) const
{
String name = getAliasToOrName(name_param);
auto it = table_functions.find(name);
if (table_functions.end() != it)
return it->second();
it = case_insensitive_table_functions.find(Poco::toLower(name));
if (case_insensitive_table_functions.end() != it)
return it->second();
return {};
}
bool TableFunctionFactory::isTableFunctionName(const std::string & name) const
{
return functions.count(name);
return table_functions.count(name);
}
}

View File

@ -1,6 +1,7 @@
#pragma once
#include <TableFunctions/ITableFunction.h>
#include <Common/IFactoryWithAliases.h>
#include <Common/NamePrompter.h>
#include <ext/singleton.h>
@ -16,51 +17,47 @@ namespace DB
class Context;
using TableFunctionCreator = std::function<TableFunctionPtr()>;
/** Lets you get a table function by its name.
*/
class TableFunctionFactory final: public ext::singleton<TableFunctionFactory>, public IHints<1, TableFunctionFactory>
class TableFunctionFactory final: public ext::singleton<TableFunctionFactory>, public IFactoryWithAliases<TableFunctionCreator>
{
public:
using Creator = std::function<TableFunctionPtr()>;
using TableFunctions = std::unordered_map<std::string, Creator>;
/// Register a function by its name.
/// No locking, you must register all functions before usage of get.
void registerFunction(const std::string & name, Creator creator);
void registerFunction(const std::string & name, Creator creator, CaseSensitiveness case_sensitiveness = CaseSensitive);
template <typename Function>
void registerFunction()
void registerFunction(CaseSensitiveness case_sensitiveness = CaseSensitive)
{
auto creator = [] () -> TableFunctionPtr
{
return std::make_shared<Function>();
};
registerFunction(Function::name, std::move(creator));
registerFunction(Function::name, std::move(creator), case_sensitiveness);
}
/// Throws an exception if not found.
TableFunctionPtr get(
const std::string & name,
const Context & context) const;
TableFunctionPtr get(const std::string & name, const Context & context) const;
/// Returns nullptr if not found.
TableFunctionPtr tryGet(const std::string & name, const Context & context) const;
bool isTableFunctionName(const std::string & name) const;
const TableFunctions & getAllTableFunctions() const
{
return functions;
}
std::vector<String> getAllRegisteredNames() const override
{
std::vector<String> result;
auto getter = [](const auto & pair) { return pair.first; };
std::transform(functions.begin(), functions.end(), std::back_inserter(result), getter);
return result;
}
private:
TableFunctions functions;
using TableFunctions = std::unordered_map<std::string, Creator>;
const TableFunctions & getCreatorMap() const override { return table_functions; }
const TableFunctions & getCaseInsensitiveCreatorMap() const override { return case_insensitive_table_functions; }
String getFactoryName() const override { return "TableFunctionFactory"; }
TableFunctions table_functions;
TableFunctions case_insensitive_table_functions;
};
}

View File

@ -0,0 +1,95 @@
#include <Common/typeid_cast.h>
#include <Common/Exception.h>
#include <Core/Block.h>
#include <Storages/StorageValues.h>
#include <Parsers/ASTExpressionList.h>
#include <Parsers/ASTLiteral.h>
#include <Parsers/ASTFunction.h>
#include <TableFunctions/ITableFunction.h>
#include <TableFunctions/TableFunctionValues.h>
#include <TableFunctions/TableFunctionFactory.h>
#include <TableFunctions/parseColumnsListForTableFunction.h>
#include <Interpreters/convertFieldToType.h>
#include <Interpreters/evaluateConstantExpression.h>
namespace DB
{
namespace ErrorCodes
{
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
}
static void parseAndInsertValues(MutableColumns & res_columns, const ASTs & args, const Block & sample_block, const Context & context)
{
if (res_columns.size() == 1) /// Parsing arguments as Fields
{
for (size_t i = 1; i < args.size(); ++i)
{
const auto & [value_field, value_type_ptr] = evaluateConstantExpression(args[i], context);
Field value = convertFieldToType(value_field, *sample_block.getByPosition(0).type, value_type_ptr.get());
res_columns[0]->insert(value);
}
}
else /// Parsing arguments as Tuples
{
for (size_t i = 1; i < args.size(); ++i)
{
const auto & [value_field, value_type_ptr] = evaluateConstantExpression(args[i], context);
const TupleBackend & value_tuple = value_field.safeGet<Tuple>().toUnderType();
if (value_tuple.size() != sample_block.columns())
throw Exception("Values size should match with number of columns", ErrorCodes::LOGICAL_ERROR);
for (size_t j = 0; j < value_tuple.size(); ++j)
{
Field value = convertFieldToType(value_tuple[j], *sample_block.getByPosition(j).type, value_type_ptr.get());
res_columns[j]->insert(value);
}
}
}
}
StoragePtr TableFunctionValues::executeImpl(const ASTPtr & ast_function, const Context & context, const std::string & table_name) const
{
ASTs & args_func = ast_function->children;
if (args_func.size() != 1)
throw Exception("Table function '" + getName() + "' must have arguments.", ErrorCodes::LOGICAL_ERROR);
ASTs & args = args_func.at(0)->children;
if (args.size() < 2)
throw Exception("Table function '" + getName() + "' requires 2 or more arguments: structure and values.",
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
/// Parsing first argument as table structure and creating a sample block
std::string structure = args[0]->as<ASTLiteral &>().value.safeGet<String>();
Block sample_block;
parseColumnsListFromString(structure, sample_block, context);
MutableColumns res_columns = sample_block.cloneEmptyColumns();
/// Parsing other arguments as values and inserting them into columns
parseAndInsertValues(res_columns, args, sample_block, context);
Block res_block = sample_block.cloneWithColumns(std::move(res_columns));
auto res = StorageValues::create(getDatabaseName(), table_name, res_block);
res->startup();
return res;
}
void registerTableFunctionValues(TableFunctionFactory & factory)
{
factory.registerFunction<TableFunctionValues>(TableFunctionFactory::CaseInsensitive);
}
}

View File

@ -0,0 +1,20 @@
#pragma once
#include <TableFunctions/ITableFunction.h>
namespace DB
{
/* values(structure, values...) - creates a temporary storage filling columns with values
* values is case-insensitive table function
*/
class TableFunctionValues : public ITableFunction
{
public:
static constexpr auto name = "values";
std::string getName() const override { return name; }
private:
StoragePtr executeImpl(const ASTPtr & ast_function, const Context & context, const std::string & table_name) const override;
};
}

View File

@ -0,0 +1,44 @@
#include <Parsers/ASTExpressionList.h>
#include <Parsers/ParserCreateQuery.h>
#include <Interpreters/InterpreterCreateQuery.h>
#include <TableFunctions/parseColumnsListForTableFunction.h>
namespace DB
{
namespace ErrorCodes
{
extern const int SYNTAX_ERROR;
}
void parseColumnsListFromString(const std::string & structure, Block & sample_block, const Context & context)
{
Expected expected;
Tokens tokens(structure.c_str(), structure.c_str() + structure.size());
TokenIterator token_iterator(tokens);
ParserColumnDeclarationList parser;
ASTPtr columns_list_raw;
if (!parser.parse(token_iterator, columns_list_raw, expected))
throw Exception("Cannot parse columns declaration list.", ErrorCodes::SYNTAX_ERROR);
auto * columns_list = dynamic_cast<ASTExpressionList *>(columns_list_raw.get());
if (!columns_list)
throw Exception("Could not cast AST to ASTExpressionList", ErrorCodes::LOGICAL_ERROR);
ColumnsDescription columns_desc = InterpreterCreateQuery::getColumnsDescription(*columns_list, context);
for (const auto & [name, type]: columns_desc.getAllPhysical())
{
ColumnWithTypeAndName column;
column.name = name;
column.type = type;
column.column = type->createColumn();
sample_block.insert(std::move(column));
}
}
}

View File

@ -0,0 +1,11 @@
#pragma once
#include <Core/Block.h>
namespace DB
{
/// Parses a common argument for table functions such as table structure given in string
void parseColumnsListFromString(const std::string & structure, Block & sample_block, const Context & context);
}

View File

@ -13,6 +13,7 @@ void registerTableFunctionNumbers(TableFunctionFactory & factory);
void registerTableFunctionCatBoostPool(TableFunctionFactory & factory);
void registerTableFunctionFile(TableFunctionFactory & factory);
void registerTableFunctionURL(TableFunctionFactory & factory);
void registerTableFunctionValues(TableFunctionFactory & factory);
#if USE_HDFS
void registerTableFunctionHDFS(TableFunctionFactory & factory);
@ -39,6 +40,7 @@ void registerTableFunctions()
registerTableFunctionCatBoostPool(factory);
registerTableFunctionFile(factory);
registerTableFunctionURL(factory);
registerTableFunctionValues(factory);
#if USE_HDFS
registerTableFunctionHDFS(factory);

View File

@ -0,0 +1,12 @@
<yandex>
<shutdown_wait_unfinished>3</shutdown_wait_unfinished>
<logger>
<level>trace</level>
<log>/var/log/clickhouse-server/log.log</log>
<errorlog>/var/log/clickhouse-server/log.err.log</errorlog>
<size>1000M</size>
<count>10</count>
<stderr>/var/log/clickhouse-server/stderr.log</stderr>
<stdout>/var/log/clickhouse-server/stdout.log</stdout>
</logger>
</yandex>

View File

@ -0,0 +1,35 @@
import time
import pytest
from helpers.cluster import ClickHouseCluster
from helpers.client import QueryRuntimeException, QueryTimeoutExceedException
cluster = ClickHouseCluster(__file__)
node1 = cluster.add_instance('node1', main_configs=['configs/log_conf.xml'])
node2 = cluster.add_instance('node2', main_configs=['configs/log_conf.xml'])
@pytest.fixture(scope="module")
def start_cluster():
try:
cluster.start()
for node in [node1, node2]:
node.query("""
CREATE TABLE test_table(
APIKey UInt32,
CustomAttributeId UInt64,
ProfileIDHash UInt64,
DeviceIDHash UInt64,
Data String)
ENGINE = SummingMergeTree()
ORDER BY (APIKey, CustomAttributeId, ProfileIDHash, DeviceIDHash, intHash32(DeviceIDHash))
""")
yield cluster
finally:
cluster.shutdown()
def test_remote(start_cluster):
assert node1.query("SELECT 1 FROM remote('node{1,2}', default.test_table) WHERE (APIKey = 137715) AND (CustomAttributeId IN (45, 66)) AND (ProfileIDHash != 0) LIMIT 1") == ""

View File

@ -13,9 +13,14 @@ SET max_block_size = 1, min_insert_block_size_rows = 0, min_insert_block_size_by
INSERT INTO memory SELECT * FROM numbers(1000);"
# NOTE Most of the time this query can start before the table will be dropped.
# And TRUNCATE or DROP query will test for correct locking inside ClickHouse.
# But if the table will be dropped before query - just pass.
# It's Ok, because otherwise the test will depend on the race condition in the test itself.
${CLICKHOUSE_CLIENT} --multiquery --query="
SET max_threads = 1;
SELECT count() FROM memory WHERE NOT ignore(sleep(0.0001));" &
SELECT count() FROM memory WHERE NOT ignore(sleep(0.0001));" 2>&1 | grep -c -P '^1000$|Table .+? doesn.t exist' &
sleep 0.05;

View File

@ -32,7 +32,7 @@
991
990
ContextLock Number of times the lock of Context was acquired or tried to acquire. This is global lock.
Query Number of queries started to be interpreted and maybe executed. Does not include queries that are failed to parse, that are rejected due to AST size limits; rejected due to quota limits or limits on number of simultaneously running queries. May include internal queries initiated by ClickHouse itself. Does not count subqueries.
Query Number of queries to be interpreted and potentially executed. Does not include queries that failed to parse or were rejected due to AST size limits, quota limits or limits on the number of simultaneously running queries. May include internal queries initiated by ClickHouse itself. Does not count subqueries.
original:
-128 0 -32768 0 -2147483648 0 -9223372036854775808 0 -1.032 -1.064 string-1 fixedstring-1\0\0 2003-04-05 2003-02-03 04:05:06
-108 108 -1016 1116 -1032 1132 -1064 1164 -1.032 -1.064 string-0 fixedstring\0\0\0\0 2001-02-03 2002-02-03 04:05:06

View File

@ -0,0 +1,13 @@
1 one
2 two
3 three
1 one
2 two
3 three
2018-01-01 2018-01-01 00:00:00
abra
cadabra
abracadabra
23 23 23
24 24 24
1.6660 a b

View File

@ -0,0 +1,14 @@
DROP TABLE IF EXISTS values_list;
SELECT * FROM VALUES('a UInt64, s String', (1, 'one'), (2, 'two'), (3, 'three'));
CREATE TABLE values_list AS VALUES('a UInt64, s String', (1, 'one'), (2, 'two'), (3, 'three'));
SELECT * FROM values_list;
SELECT subtractYears(date, 1), subtractYears(date_time, 1) FROM VALUES('date Date, date_time DateTime', (toDate('2019-01-01'), toDateTime('2019-01-01 00:00:00')));
SELECT * FROM VALUES('s String', ('abra'), ('cadabra'), ('abracadabra'));
SELECT * FROM VALUES('n UInt64, s String, ss String', (1 + 22, '23', toString(23)), (toUInt64('24'), '24', concat('2', '4')));
SELECT * FROM VALUES('a Decimal(4, 4), b String, c String', (divide(toDecimal32(5, 3), 3), 'a', 'b'));
DROP TABLE values_list;

View File

@ -0,0 +1,23 @@
-2000 -1 1
-1
-1
-1
0
0
0
0
0
0
0
0
0
-1
0
0
0
0
0
0
0
0
0

View File

@ -0,0 +1,19 @@
SELECT
sum(ASD) AS asd,
intDiv(toInt64(asd), abs(toInt64(asd))) AS int_div_with_abs,
intDiv(toInt64(asd), toInt64(asd)) AS int_div_without_abs
FROM
(
SELECT ASD
FROM
(
SELECT [-1000, -1000] AS asds
)
ARRAY JOIN asds AS ASD
);
SELECT intDivOrZero( CAST(-1000, 'Int64') , CAST(1000, 'UInt64') );
SELECT intDivOrZero( CAST(-1000, 'Int64') , CAST(1000, 'Int64') );
SELECT intDiv(-1, number) FROM numbers(1, 10);
SELECT intDivOrZero(-1, number) FROM numbers(1, 10);

View File

@ -12,7 +12,7 @@ CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
) ENGINE = Join(join_strictness, join_type, k1[, k2, ...])
```
See the detailed description of [CREATE TABLE](../../query_language/create.md#create-table-query) query.
See the detailed description of the [CREATE TABLE](../../query_language/create.md#create-table-query) query.
**Engine Parameters**
@ -20,7 +20,7 @@ See the detailed description of [CREATE TABLE](../../query_language/create.md#cr
- `join_type` [JOIN type](../../query_language/select.md#select-join-types).
- `k1[, k2, ...]` Key columns from the `USING` clause that the `JOIN` operation is made with.
Set the parameters `join_strictness` and `join_type` without quotes, for example, `Join(ANY, LEFT, col1)`. They must match the `JOIN` operation that the table will be used for. If parameters don't match, ClickHouse doesn't throw an exception and may return incorrect data.
Enter `join_strictness` and `join_type` parameters without quotes, for example, `Join(ANY, LEFT, col1)`. They must match the `JOIN` operation that the table will be used for. If the parameters don't match, ClickHouse doesn't throw an exception and may return incorrect data.
## Table Usage
@ -29,14 +29,18 @@ Set the parameters `join_strictness` and `join_type` without quotes, for example
Creating the left-side table:
```sql
CREATE TABLE id_val(`id` UInt32, `val` UInt32) ENGINE = TinyLog;
INSERT INTO id_val VALUES (1,11)(2,12)(3,13);
CREATE TABLE id_val(`id` UInt32, `val` UInt32) ENGINE = TinyLog
```
```sql
INSERT INTO id_val VALUES (1,11)(2,12)(3,13)
```
Creating the right-side `Join` table:
```sql
CREATE TABLE id_val_join(`id` UInt32, `val` UInt8) ENGINE = Join(ANY, LEFT, id);
CREATE TABLE id_val_join(`id` UInt32, `val` UInt8) ENGINE = Join(ANY, LEFT, id)
```
```sql
INSERT INTO id_val_join VALUES (1,21)(1,22)(3,23)
```
@ -53,7 +57,7 @@ SELECT * FROM id_val ANY LEFT JOIN id_val_join USING (id) SETTINGS join_use_null
└────┴─────┴─────────────────┘
```
Retrieving the data from the `Join` table, specifying the join key value:
As an alternative, you can retrieve data from the `Join` table, specifying the join key value:
```sql
SELECT joinGet('id_val_join', 'val', toUInt32(1))
@ -66,12 +70,12 @@ SELECT joinGet('id_val_join', 'val', toUInt32(1))
### Selecting and Inserting Data
You can use `INSERT` to add data to the table. For the `ANY` strictness, data for duplicated keys are ignored. For the `ALL` strictness, all rows are kept.
You can use `INSERT` queries to add data to the `Join`-engine tables. If the table was created with the `ANY` strictness, data for duplicate keys are ignored. With the `ALL` strictness, all rows are added.
You cannot perform the `SELECT` query directly from the table. Use one of the following ways:
You cannot perform a `SELECT` query directly from the table. Instead, use one of the following methods:
- Place the table at the right side in a `JOIN` clause.
- Call the [joinGet](../../query_language/functions/other_functions.md#other_functions-joinget) function, which allows to extract data from the table as from a dictionary.
- Place the table to the right side in a `JOIN` clause.
- Call the [joinGet](../../query_language/functions/other_functions.md#other_functions-joinget) function, which lets you extract data from the table the same way as from a dictionary.
### Limitations and Settings
@ -83,12 +87,12 @@ When creating a table, the following settings are applied:
- [join_overflow_mode](../settings/query_complexity.md#settings-join_overflow_mode)
- [join_any_take_last_row](../settings/settings.md#settings-join_any_take_last_row)
The table can't be used in `GLOBAL JOIN` operations.
The `Join`-engine tables can't be used in `GLOBAL JOIN` operations.
## Data Storage
Data for the `Join` tables is always located in RAM. When inserting rows into the table, ClickHouse writes the data blocks to the directory on disk to be able to restore them on server restart.
`Join` table data is always located in the RAM. When inserting rows into a table, ClickHouse writes data blocks to the directory on the disk so that they can be restored when the server restarts.
At the abnormal server restart, the block of data on the disk might be lost or damaged. In this case, you may need to manually delete the file with damaged data.
If the server restarts incorrectly, the data block on the disk might get lost or damaged. In this case, you may need to manually delete the file with damaged data.
[Original article](https://clickhouse.yandex/docs/en/operations/table_engines/join/) <!--hide-->

View File

@ -672,6 +672,146 @@ Optional parameters:
- The default value for substituting in empty positions.
- The length of the resulting array. This allows you to receive arrays of the same size for all the aggregate keys. When using this parameter, the default value must be specified.
## groupArrayMovingSum {#agg_function-grouparraymovingsum}
Calculates the moving sum of input values.
```
groupArrayMovingSum(numbers_for_summing)
groupArrayMovingSum(window_size)(numbers_for_summing)
```
The function can take the window size as a parameter. If it not specified, the function takes the window size equal to the number of rows in the column.
**Parameters**
- `numbers_for_summing` — [Expression](../syntax.md#syntax-expressions) resulting with a value in a numeric data type.
- `window_size` — Size of the calculation window.
**Returned values**
- Array of the same size and type as the input data.
**Example**
The sample table:
```sql
CREATE TABLE t
(
`int` UInt8,
`float` Float32,
`dec` Decimal32(2)
)
ENGINE = TinyLog
```
```text
┌─int─┬─float─┬──dec─┐
│ 1 │ 1.1 │ 1.10 │
│ 2 │ 2.2 │ 2.20 │
│ 4 │ 4.4 │ 4.40 │
│ 7 │ 7.77 │ 7.77 │
└─────┴───────┴──────┘
```
The queries:
```sql
SELECT
groupArrayMovingSum(int) AS I,
groupArrayMovingSum(float) AS F,
groupArrayMovingSum(dec) AS D
FROM t
```
```text
┌─I──────────┬─F───────────────────────────────┬─D──────────────────────┐
│ [1,3,7,14] │ [1.1,3.3000002,7.7000003,15.47] │ [1.10,3.30,7.70,15.47] │
└────────────┴─────────────────────────────────┴────────────────────────┘
```
```sql
SELECT
groupArrayMovingSum(2)(int) AS I,
groupArrayMovingSum(2)(float) AS F,
groupArrayMovingSum(2)(dec) AS D
FROM t
```
```text
┌─I──────────┬─F───────────────────────────────┬─D──────────────────────┐
│ [1,3,6,11] │ [1.1,3.3000002,6.6000004,12.17] │ [1.10,3.30,6.60,12.17] │
└────────────┴─────────────────────────────────┴────────────────────────┘
```
## groupArrayMovingAvg {#agg_function-grouparraymovingavg}
Calculates the moving average of input values.
```
groupArrayMovingAvg(numbers_for_summing)
groupArrayMovingAvg(window_size)(numbers_for_summing)
```
The function can take the window size as a parameter. If it not specified, the function takes the window size equal to the number of rows in the column.
**Parameters**
- `numbers_for_summing` — [Expression](../syntax.md#syntax-expressions) resulting with a value in a numeric data type.
- `window_size` — Size of the calculation window.
**Returned values**
- Array of the same size and type as the input data.
The function uses [rounding towards zero](https://en.wikipedia.org/wiki/Rounding#Rounding_towards_zero). It truncates the decimal places insignificant for the resulting data type.
**Example**
The sample table `b`:
```sql
CREATE TABLE t
(
`int` UInt8,
`float` Float32,
`dec` Decimal32(2)
)
ENGINE = TinyLog
```
```text
┌─int─┬─float─┬──dec─┐
│ 1 │ 1.1 │ 1.10 │
│ 2 │ 2.2 │ 2.20 │
│ 4 │ 4.4 │ 4.40 │
│ 7 │ 7.77 │ 7.77 │
└─────┴───────┴──────┘
```
The queries:
```sql
SELECT
groupArrayMovingAvg(int) AS I,
groupArrayMovingAvg(float) AS F,
groupArrayMovingAvg(dec) AS D
FROM t
```
```text
┌─I─────────┬─F───────────────────────────────────┬─D─────────────────────┐
│ [0,0,1,3] │ [0.275,0.82500005,1.9250001,3.8675] │ [0.27,0.82,1.92,3.86] │
└───────────┴─────────────────────────────────────┴───────────────────────┘
```
```sql
SELECT
groupArrayMovingAvg(2)(int) AS I,
groupArrayMovingAvg(2)(float) AS F,
groupArrayMovingAvg(2)(dec) AS D
FROM t
```
```text
┌─I─────────┬─F────────────────────────────────┬─D─────────────────────┐
│ [0,1,3,5] │ [0.55,1.6500001,3.3000002,6.085] │ [0.55,1.65,3.30,6.08] │
└───────────┴──────────────────────────────────┴───────────────────────┘
```
## groupUniqArray(x), groupUniqArray(max_size)(x)
Creates an array from different argument values. Memory consumption is the same as for the `uniqExact` function.

View File

@ -15,29 +15,29 @@ dictGetOrDefault('dict_name', 'attr_name', id_expr, default_value_expr)
- `dict_name` — Name of the dictionary. [String literal](../syntax.md#syntax-string-literal).
- `attr_name` — Name of the column of the dictionary. [String literal](../syntax.md#syntax-string-literal).
- `id_expr` — Key value. [Expression](../syntax.md#syntax-expressions) returning [UInt64](../../data_types/int_uint.md)-typed value or [Tuple](../../data_types/tuple.md) depending on the dictionary configuration.
- `default_value_expr` — Value which is returned if the dictionary doesn't contain a row with the `id_expr` key. [Expression](../syntax.md#syntax-expressions) returning the value of the data type configured for the `attr_name` attribute.
- `id_expr` — Key value. [Expression](../syntax.md#syntax-expressions) returning a [UInt64](../../data_types/int_uint.md) or [Tuple](../../data_types/tuple.md)-type value depending on the dictionary configuration.
- `default_value_expr` — Value returned if the dictionary doesn't contain a row with the `id_expr` key. [Expression](../syntax.md#syntax-expressions) returning the value in the data type configured for the `attr_name` attribute.
**Returned value**
- If ClickHouse parses the attribute successfully with the [attribute's data type](../dicts/external_dicts_dict_structure.md#ext_dict_structure-attributes), functions return the value of the dictionary attribute that corresponds to `id_expr`.
- If there is no requested `id_expr` in the dictionary then:
- If ClickHouse parses the attribute successfully in the [attribute's data type](../dicts/external_dicts_dict_structure.md#ext_dict_structure-attributes), functions return the value of the dictionary attribute that corresponds to `id_expr`.
- If there is no the key, corresponding to `id_expr`, in the dictionary, then:
- `dictGet` returns the content of the `<null_value>` element which is specified for the attribute in the dictionary configuration.
- `dictGet` returns the content of the `<null_value>` element specified for the attribute in the dictionary configuration.
- `dictGetOrDefault` returns the value passed as the `default_value_expr` parameter.
ClickHouse throws an exception if it cannot parse the value of the attribute or the value doesn't match the attribute data type.
**Example of Use**
**Example**
Create the text file `ext-dict-text.csv` with the following content:
Create a text file `ext-dict-text.csv` containing the following:
```text
1,1
2,2
```
The first column is `id`, the second column is `c1`
The first column is `id`, the second column is `c1`.
Configure the external dictionary:
@ -93,7 +93,7 @@ LIMIT 3
## dictHas
Checks whether the dictionary has the key.
Checks whether a key is present in a dictionary.
```
dictHas('dict_name', id_expr)
@ -102,7 +102,7 @@ dictHas('dict_name', id_expr)
**Parameters**
- `dict_name` — Name of the dictionary. [String literal](../syntax.md#syntax-string-literal).
- `id_expr` — Key value. [Expression](../syntax.md#syntax-expressions) returning [UInt64](../../data_types/int_uint.md)-typed value.
- `id_expr` — Key value. [Expression](../syntax.md#syntax-expressions) returning a [UInt64](../../data_types/int_uint.md)-type value.
**Returned value**
@ -113,7 +113,7 @@ Type: `UInt8`.
## dictGetHierarchy
For the hierarchical dictionary, returns an array of dictionary keys starting from passed `id_expr` and continuing along the chain of parent elements.
For the hierarchical dictionary, returns an array of dictionary keys starting from the passed `id_expr` and continuing along the chain of parent elements.
```
dictGetHierarchy('dict_name', id_expr)
@ -122,7 +122,7 @@ dictGetHierarchy('dict_name', id_expr)
**Parameters**
- `dict_name` — Name of the dictionary. [String literal](../syntax.md#syntax-string-literal).
- `id_expr` — Key value. [Expression](../syntax.md#syntax-expressions) returning [UInt64](../../data_types/int_uint.md)-typed value.
- `id_expr` — Key value. [Expression](../syntax.md#syntax-expressions) returning a [UInt64](../../data_types/int_uint.md)-type value.
**Returned value**
@ -132,15 +132,17 @@ Type: Array(UInt64).
## dictIsIn
Checks the ancestor of a key in the hierarchical dictionary.
Checks the ancestor of a key through the whole hierarchical chain in the dictionary.
`dictIsIn ('dict_name', child_id_expr, ancestor_id_expr)`
```
dictIsIn('dict_name', child_id_expr, ancestor_id_expr)
```
**Parameters**
- `dict_name` — Name of the dictionary. [String literal](../syntax.md#syntax-string-literal).
- `child_id_expr` — Key that should be checked. [Expression](../syntax.md#syntax-expressions) returning [UInt64](../../data_types/int_uint.md)-typed value.
- `ancestor_id_expr` — Alleged ancestor of the `child_id_expr` key. [Expression](../syntax.md#syntax-expressions) returning [UInt64](../../data_types/int_uint.md)-typed value.
- `child_id_expr` — Key to be checked. [Expression](../syntax.md#syntax-expressions) returning a [UInt64](../../data_types/int_uint.md)-type value.
- `ancestor_id_expr` — Alleged ancestor of the `child_id_expr` key. [Expression](../syntax.md#syntax-expressions) returning a [UInt64](../../data_types/int_uint.md)-type value.
**Returned value**
@ -151,7 +153,7 @@ Type: `UInt8`.
## Other functions {#ext_dict_functions-other}
ClickHouse supports the specialized functions that convert the dictionary attribute values to the strict data type independently of the configuration of the dictionary.
ClickHouse supports specialized functions that convert dictionary attribute values to a specific data type regardless of the dictionary configuration.
Functions:
@ -176,17 +178,17 @@ dictGet[Type]OrDefault('dict_name', 'attr_name', id_expr, default_value_expr)
- `dict_name` — Name of the dictionary. [String literal](../syntax.md#syntax-string-literal).
- `attr_name` — Name of the column of the dictionary. [String literal](../syntax.md#syntax-string-literal).
- `id_expr` — Key value. [Expression](../syntax.md#syntax-expressions) returning [UInt64](../../data_types/int_uint.md)-typed value.
- `default_value_expr` — Value which is returned if the dictionary doesn't contain a row with the `id_expr` key. [Expression](../syntax.md#syntax-expressions) returning the value of the data type configured for the `attr_name` attribute.
- `id_expr` — Key value. [Expression](../syntax.md#syntax-expressions) returning a [UInt64](../../data_types/int_uint.md)-type value.
- `default_value_expr` — Value which is returned if the dictionary doesn't contain a row with the `id_expr` key. [Expression](../syntax.md#syntax-expressions) returning a value in the data type configured for the `attr_name` attribute.
**Returned value**
- If ClickHouse parses the attribute successfully with the [attribute's data type](../dicts/external_dicts_dict_structure.md#ext_dict_structure-attributes), functions return the value of the dictionary attribute that corresponds to `id_expr`.
- If ClickHouse parses the attribute successfully in the [attribute's data type](../dicts/external_dicts_dict_structure.md#ext_dict_structure-attributes), functions return the value of the dictionary attribute that corresponds to `id_expr`.
- If there is no requested `id_expr` in the dictionary then:
- `dictGet[Type]` returns the content of the `<null_value>` element which is specified for the attribute in the dictionary configuration.
- `dictGet[Type]` returns the content of the `<null_value>` element specified for the attribute in the dictionary configuration.
- `dictGet[Type]OrDefault` returns the value passed as the `default_value_expr` parameter.
ClickHouse throws an exception, if it cannot parse the value of the attribute or the value doesn't match the attribute data type.
ClickHouse throws an exception if it cannot parse the value of the attribute or the value doesn't match the attribute data type.
[Original article](https://clickhouse.yandex/docs/en/query_language/functions/ext_dict_functions/) <!--hide-->

View File

@ -668,9 +668,9 @@ So, result of function depends on partition of data to blocks and on order of da
## joinGet('join_storage_table_name', 'get_column', join_key) {#other_functions-joinget}
Gets data from the [Join](../../operations/table_engines/join.md) table using the specified join key.
Gets data from [Join](../../operations/table_engines/join.md) tables using the specified join key.
Supports only tables created with `ENGINE = Join(ANY, LEFT, <join_keys>)` statement.
Only supports tables created with the `ENGINE = Join(ANY, LEFT, <join_keys>)` statement.
## modelEvaluate(model_name, ...)
Evaluate external model.

View File

@ -1,18 +1,100 @@
# Join
Представляет собой подготовленную структуру данных для JOIN-а, постоянно находящуюся в оперативке.
Подготовленная структура данных для использования в операциях [JOIN](../../query_language/select.md#select-join).
## Создание таблицы
```
Join(ANY|ALL, LEFT|INNER, k1[, k2, ...])
CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
(
name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1] [TTL expr1],
name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2] [TTL expr2],
) ENGINE = Join(join_strictness, join_type, k1[, k2, ...])
```
Параметры движка: `ANY|ALL` - строгость, `LEFT|INNER` - тип.
Эти параметры (задаются без кавычек) должны соответствовать тому JOIN-у, для которого будет использоваться таблица. k1, k2, ... - ключевые столбцы из секции USING, по которым будет делаться соединение.
Смотрите подробное описание запроса [CREATE TABLE](../../query_language/create.md#create-table-query).
Таблица не может использоваться для GLOBAL JOIN-ов.
**Параметры движка**
В таблицу можно вставлять данные INSERT-ом, аналогично движку Set. В случае ANY, данные для дублирующихся ключей будут проигнорированы; в случае ALL - будут учитываться. Из таблицы нельзя, непосредственно, делать SELECT. Единственная возможность чтения - использование в качестве "правой" таблицы для JOIN.
- `join_strictness` [строгость JOIN](../../query_language/select.md#select-join-strictness).
- `join_type` [тип JOIN](../../query_language/select.md#select-join-types).
- `k1[, k2, ...]` ключевые столбцы секции `USING` с которыми выполняется операция `JOIN`.
Хранение данных на диске аналогично движку Set.
Вводите параметры `join_strictness` и `join_type` без кавычек, например, `Join(ANY, LEFT, col1)`. Они должны быть такими же как и в той операции `JOIN`, в которой таблица будет использоваться. Если параметры не совпадают, ClickHouse не генерирует исключение и может возвращать неверные данные.
## Использование таблицы
### Пример
Создание левой таблицы:
```sql
CREATE TABLE id_val(`id` UInt32, `val` UInt32) ENGINE = TinyLog
```
```sql
INSERT INTO id_val VALUES (1,11)(2,12)(3,13)
```
Создание правой таблицы с движком `Join`:
```sql
CREATE TABLE id_val_join(`id` UInt32, `val` UInt8) ENGINE = Join(ANY, LEFT, id)
```
```sql
INSERT INTO id_val_join VALUES (1,21)(1,22)(3,23)
```
Объединение таблиц:
```sql
SELECT * FROM id_val ANY LEFT JOIN id_val_join USING (id) SETTINGS join_use_nulls = 1
```
```text
┌─id─┬─val─┬─id_val_join.val─┐
│ 1 │ 11 │ 21 │
│ 2 │ 12 │ ᴺᵁᴸᴸ │
│ 3 │ 13 │ 23 │
└────┴─────┴─────────────────┘
```
В качестве альтернативы, можно извлечь данные из таблицы `Join`, указав значение ключа объединения:
```sql
SELECT joinGet('id_val_join', 'val', toUInt32(1))
```
```text
┌─joinGet('id_val_join', 'val', toUInt32(1))─┐
│ 21 │
└────────────────────────────────────────────┘
```
### Выборка и вставка данных
Для добавления данных в таблицы с движком `Join` используйте запрос `INSERT`. Если таблица создавалась со строгостью `ANY`, то данные с повторяющимися ключами игнорируются. Если задавалась строгость `ALL`, то добавляются все строки.
Из таблиц нельзя выбрать данные с помощью запроса `SELECT`. Вместо этого, используйте один из следующих методов:
- Используйте таблицу как правую в секции `JOIN`.
- Используйте функцию [joinGet](../../query_language/functions/other_functions.md#other_functions-joinget), которая позволяет извлекать данные из таблицы таким же образом как из словаря.
### Ограничения и настройки
При создании таблицы, применяются следующие параметры :
- [join_use_nulls](../settings/settings.md#settings-join_use_nulls)
- [max_rows_in_join](../settings/query_complexity.md#settings-max_rows_in_join)
- [max_bytes_in_join](../settings/query_complexity.md#settings-max_bytes_in_join)
- [join_overflow_mode](../settings/query_complexity.md#settings-join_overflow_mode)
- [join_any_take_last_row](../settings/settings.md#settings-join_any_take_last_row)
Таблицы с движком `Join` нельзя использовать в операциях `GLOBAL JOIN`.
## Хранение данных
Данные таблиц `Join` всегда находятся в RAM. При вставке строк в таблицу ClickHouse записывает блоки данных в каталог на диске, чтобы их можно было восстановить при перезапуске сервера.
При аварийном перезапуске сервера блок данных на диске может быть потерян или повреждён. В последнем случае, может потребоваться вручную удалить файл с повреждёнными данными.
[Оригинальная статья](https://clickhouse.yandex/docs/ru/operations/table_engines/join/) <!--hide-->

View File

@ -1,10 +1,10 @@
# Функции для работы с внешними словарями {#ext_dict_functions}
Для получения информации о подключении и настройке, читайте раздел про [внешние словари](../dicts/external_dicts.md).
Информацию о подключении и настройке внешних словарей смотрите в разделе [Внешние словари](../dicts/external_dicts.md).
## dictGet
Получение значения из внешнего словаря.
Извлекает значение из внешнего словаря.
```
dictGet('dict_name', 'attr_name', id_expr)
@ -13,33 +13,33 @@ dictGetOrDefault('dict_name', 'attr_name', id_expr, default_value_expr)
**Параметры**
- `dict_name`Название словаря. [Строковый литерал](../syntax.md#syntax-string-literal).
- `attr_name`Название колонки словаря. [Строковый литерал](../syntax.md#syntax-string-literal).
- `id_expr`Значение ключа. [Выражение](../syntax.md#syntax-expressions) возвращает значение типа [UInt64](../../data_types/int_uint.md) или [Tuple](../../data_types/tuple.md) в зависимости от конфигурации словаря.
- `default_value_expr`Значение которое возвращается, если словарь не содержит колонку с ключом `id_expr`. [Выражение](../syntax.md#syntax-expressions) возвращает значение такого же типа, что и у атрибута `attr_name`.
- `dict_name`имя словаря. [Строковый литерал](../syntax.md#syntax-string-literal).
- `attr_name`имя столбца словаря. [Строковый литерал](../syntax.md#syntax-string-literal).
- `id_expr`значение ключа словаря. [Выражение](../syntax.md#syntax-expressions), возвращающее значение типа [UInt64](../../data_types/int_uint.md) или [Tuple](../../data_types/tuple.md) в зависимости от конфигурации словаря.
- `default_value_expr`значение, возвращаемое в том случае, когда словарь не содержит строки с заданным ключем `id_expr`. [Выражение](../syntax.md#syntax-expressions) возвращающее значение с типом данных, сконфигурированным для атрибута `attr_name`.
**Возвращаемое значение**
- Если ClickHouse успешно обрабатывает атрибут в соотвествии с указаным [типом данных](../dicts/external_dicts_dict_structure.md#ext_dict_structure-attributes), то функция возвращает значение для заданного ключа `id_expr`.
- Если запрашиваемого `id_expr` не оказалось в словаре:
- Значение атрибута, соответствующее ключу `id_expr`, если ClickHouse смог привести это значение к [заданному типу данных](../dicts/external_dicts_dict_structure.md#ext_dict_structure-attributes).
- `dictGet` возвратит содержимое элемента `<null_value>` определенного в настройках словаря.
- `dictGetOrDefault` вернет значение переданного `default_value_expr` параметра.
- Если ключа, соответствущего `id_expr` в словаре нет, то:
- `dictGet` возвращает содержимое элемента `<null_value>`, указанного для атрибута в конфигурации словаря.
- `dictGetOrDefault` возвращает атрибут `default_value_expr`.
ClickHouse бросает исключение, если не может обработать значение атрибута или значение несопоставимо с типом атрибута.
Если значение атрибута не удалось обработать или оно не соответствует типу данных атрибута, то ClickHouse генерирует исключение.
**Пример использования**
**Пример**
Создайте файл `ext-dict-text.csv` со следующим содержимым:
Создадим текстовый файл `ext-dict-text.csv` со следующим содержимым:
```text
1,1
2,2
```
Первая колонка - это `id`, вторая - `c1`
Первый столбец — `id`, второй столбец — `c1`.
Конфигурация внешнего словаря:
Настройка внешнего словаря:
```xml
<yandex>
@ -69,15 +69,16 @@ ClickHouse бросает исключение, если не может обр
</yandex>
```
Выполните запрос:
Выполним запрос:
```sql
SELECT
dictGetOrDefault('ext-dict-test', 'c1', number + 1, toUInt32(number * 10)) AS val,
toТипName(val) AS Type
toTypeName(val) AS type
FROM system.numbers
LIMIT 3
```
```text
┌─val─┬─type───┐
│ 1 │ UInt32 │
@ -90,68 +91,67 @@ LIMIT 3
- [Внешние словари](../dicts/external_dicts.md)
## dictHas
Проверяет наличие записи с заданным ключом в словаре.
Проверяет, присутствует ли запись с указанным ключом в словаре.
```
dictHas('dict_name', id_expr)
dictHas('dict_name', id)
```
**Параметры**
- `dict_name`Название словаря. [Строковый литерал](../syntax.md#syntax-string-literal).
- `id_expr`Значение ключа. [Выражение](../syntax.md#syntax-expressions) возвращает значение типа [UInt64](../../data_types/int_uint.md).
- `dict_name`имя словаря. [Строковый литерал](../syntax.md#syntax-string-literal).
- `id_expr`значение ключа словаря. [Выражение](../syntax.md#syntax-expressions), возвращающее значение типа [UInt64](../../data_types/int_uint.md).
**Возвращаемое значение**
- 0, если ключ не был обнаружен
- 1, если ключ присутствует в словаре
- 0, если ключа нет.
- 1, если ключ есть.
Тип: `UInt8`.
Тип `UInt8`.
## dictGetHierarchy
Для иерархических словарей возвращает массив ключей, содержащий ключ `id_expr` и все ключи родительских элементов по цепочке.
Для иерархического словаря возвращает массив ключей словаря, начиная с переданного `id_expr` и продолжая цепочкой родительских элементов.
```
dictGetHierarchy('dict_name', id_expr)
dictGetHierarchy('dict_name', id)
```
**Параметры**
- `dict_name`Название словаря. [Строковый литерал](../syntax.md#syntax-string-literal).
- `id_expr`Значение ключа. [Выражение](../syntax.md#syntax-expressions) возвращает значение типа [UInt64](../../data_types/int_uint.md).
- `dict_name`имя словаря. [Строковый литерал](../syntax.md#syntax-string-literal).
- `id_expr`значение ключа словаря. [Выражение](../syntax.md#syntax-expressions), возвращающее значение типа [UInt64](../../data_types/int_uint.md).
**Возвращаемое значение**
Иерархию ключей словаря.
Иерархия ключей словаря.
Тип: [Array(UInt64)](../../data_types/array.md).
Тип — Array(UInt64).
## dictIsIn
Осуществляет проверку - является ли ключ родительским во всей иерархической цепочке словаря.
Проверяет предка ключа по всей иерархической цепочке словаря.
`dictIsIn ('dict_name', child_id_expr, ancestor_id_expr)`
**Параметры**
- `dict_name`Название словаря. [Строковый литерал](../syntax.md#syntax-string-literal).
- `child_id_expr`Ключ который должен быть проверен. [Выражение](../syntax.md#syntax-expressions) возвращает значение типа [UInt64](../../data_types/int_uint.md).
- `ancestor_id_expr`Родительский ключ для ключа `child_id_expr`. [Выражение](../syntax.md#syntax-expressions) возвращает значение типа [UInt64](../../data_types/int_uint.md).
- `dict_name`имя словаря. [Строковый литерал](../syntax.md#syntax-string-literal).
- `child_id_expr`ключ для проверки. [Выражение](../syntax.md#syntax-expressions), возвращающее значение типа [UInt64](../../data_types/int_uint.md).
- `ancestor_id_expr`предполагаемый предок ключа `child_id_expr`. [Выражение](../syntax.md#syntax-expressions), возвращающее значение типа [UInt64](../../data_types/int_uint.md).
**Возвращаемое значение**
- 0, если `child_id_expr` не является потомком для `ancestor_id_expr`.
- 1, если `child_id_expr` является потомком для `ancestor_id_expr` или если `child_id_expr` равен `ancestor_id_expr`.
- 0, если `child_id_expr` — не дочерний элемент `ancestor_id_expr`.
- 1, если `child_id_expr` — дочерний элемент `ancestor_id_expr` или если `child_id_expr` и есть `ancestor_id_expr`.
Тип: `UInt8`.
Тип `UInt8`.
## Другие функции {#ext_dict_functions-other}
## Прочие функции {#ext_dict_functions-other}
ClickHouse поддерживает специализированные функции для конвертации значений атрибутов словаря к определенному типу, независимо от настроек словаря.
ClickHouse поддерживает специализированные функции, которые приводят значения атрибутов словаря к определённому типу данных независимо от конфигурации словаря.
Функции:
@ -163,30 +163,30 @@ ClickHouse поддерживает специализированные фун
- `dictGetUUID`
- `dictGetString`
Все эти функции имеют так же `OrDefault` версию. Например, `dictGetDateOrDefault`.
Все эти функции можно использовать с модификатором `OrDefault`. Например, `dictGetDateOrDefault`.
Синтаксис:
```
dictGet[Тип]('dict_name', 'attr_name', id_expr)
dictGet[Тип]OrDefault('dict_name', 'attr_name', id_expr, default_value_expr)
dictGet[Type]('dict_name', 'attr_name', id_expr)
dictGet[Type]OrDefault('dict_name', 'attr_name', id_expr, default_value_expr)
```
**Параметры**
- `dict_name`Название словаря. [Строковый литерал](../syntax.md#syntax-string-literal).
- `attr_name`Название колонки словаря. [Строковый литерал](../syntax.md#syntax-string-literal).
- `id_expr`Значение ключа. [Выражение](../syntax.md#syntax-expressions) возвращает значение типа [UInt64](../../data_types/int_uint.md).
- `default_value_expr`Значение которое возвращается, если словарь не содержит строку с ключом `id_expr`. [Выражение](../syntax.md#syntax-expressions) возвращает значение с таким же типом, что и тип атрибута `attr_name`.
- `dict_name`имя словаря. [Строковый литерал](../syntax.md#syntax-string-literal).
- `attr_name`имя столбца словаря. [Строковый литерал](../syntax.md#syntax-string-literal).
- `id_expr`значение ключа словаря. [Выражение](../syntax.md#syntax-expressions), возвращающее значение типа [UInt64](../../data_types/int_uint.md).
- `default_value_expr`значение, возвращаемое в том случае, когда словарь не содержит строки с заданным ключем `id_expr`. [Выражение](../syntax.md#syntax-expressions) возвращающее значение с типом данных, сконфигурированным для атрибута `attr_name`.
**Возвращаемое значение**
- Если ClickHouse успешно обрабатывает атрибут в соотвествии с указаным [типом данных](../dicts/external_dicts_dict_structure.md#ext_dict_structure-attributes),то функция возвращает значение для заданного ключа `id_expr`.
- Если запрашиваемого `id_expr` не оказалось в словаре:
- Если ClickHouse успешно обработал атрибут в соответствии с [заданным типом данных](../dicts/external_dicts_dict_structure.md#ext_dict_structure-attributes), то функции возвращают значение атрибута, соответствующее ключу `id_expr`.
- `dictGet[Тип]` возвратит содержимое элемента `<null_value>` определенного в настройках словаря.
- `dictGet[Тип]OrDefault` вернет значение переданного `default_value_expr` параметра.
- Если запрошенного `id_expr` нет в словаре, то:
- `dictGet[Type]` возвращает содержимое элемента `<null_value>`, указанного для атрибута в конфигурации словаря.
- `dictGet[Type]OrDefault` возвращает аргумент `default_value_expr`.
ClickHouse бросает исключение, если не может обработать значение атрибута или значение несопоставимо с типом атрибута
Если значение атрибута не удалось обработать или оно не соответствует типу данных атрибута, то ClickHouse генерирует исключение.
[Оригинальная статья](https://clickhouse.yandex/docs/ru/query_language/functions/ext_dict_functions/) <!--hide-->

View File

@ -637,4 +637,10 @@ SELECT filesystemAvailable() AS "Free space", toTypeName(filesystemAvailable())
Принимает на вход состояния агрегатной функции и возвращает столбец со значениями, которые представляют собой результат мёржа этих состояний для выборки строк из блока от первой до текущей строки. Например, принимает состояние агрегатной функции (например, `runningAccumulate(uniqState(UserID))`), и для каждой строки блока возвращает результат агрегатной функции после мёржа состояний функции для всех предыдущих строк и текущей. Таким образом, результат зависит от разбиения данных по блокам и от порядка данных в блоке.
## joinGet('join_storage_table_name', 'get_column', join_key) {#other_functions-joinget}
Получает данные из таблиц [Join](../../operations/table_engines/join.md) по ключу.
Поддержаны только таблицы, созданные запросом с `ENGINE = Join(ANY, LEFT, <join_keys>)`.
[Оригинальная статья](https://clickhouse.yandex/docs/ru/query_language/functions/other_functions/) <!--hide-->

View File

@ -506,7 +506,7 @@ FROM <left_subquery>
Вместо `<left_subquery>` и `<right_subquery>` можно указать имена таблиц. Это эквивалентно подзапросу `SELECT * FROM table`, за исключением особого случая таблицы с движком [Join](../operations/table_engines/join.md) массива, подготовленного для присоединения.
**Поддерживаемые типы `JOIN`**
#### Поддерживаемые типы `JOIN` {#select-join-types}
- `INNER JOIN` (or `JOIN`)
- `LEFT JOIN` (or `LEFT OUTER JOIN`)