mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-26 09:32:01 +00:00
dbms: development [#CONV-2944].
This commit is contained in:
parent
40281aa96f
commit
8ea28ddd6e
@ -29,6 +29,11 @@ public:
|
||||
*/
|
||||
Names getRequiredColumns();
|
||||
|
||||
/** Преобразовать подзапросы или перечисления значений в секции IN в множества.
|
||||
* Выполняет подзапросы, если требуется.
|
||||
*/
|
||||
void makeSets();
|
||||
|
||||
/** Выполнить выражение над блоком. Блок должен содержать все столбцы - идентификаторы.
|
||||
* Функция добавляет в блок новые столбцы - результаты вычислений.
|
||||
* part_id - какую часть выражения вычислять.
|
||||
@ -112,6 +117,8 @@ private:
|
||||
bool hasAggregatesImpl(ASTPtr ast);
|
||||
|
||||
void markBeforeAndAfterAggregationImpl(ASTPtr ast, unsigned before_part_id, unsigned after_part_id, bool below = false);
|
||||
|
||||
void makeSetsImpl(ASTPtr ast);
|
||||
};
|
||||
|
||||
typedef SharedPtr<Expression> ExpressionPtr;
|
||||
|
180
dbms/include/DB/Interpreters/HashSet.h
Normal file
180
dbms/include/DB/Interpreters/HashSet.h
Normal file
@ -0,0 +1,180 @@
|
||||
#pragma once
|
||||
|
||||
#include <DB/Interpreters/HashMap.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
|
||||
/** См. HashMap.h
|
||||
*
|
||||
* Очень простое хэш множество.
|
||||
* Можно только вставлять значения, а также проверять принадлежность.
|
||||
*
|
||||
* Итераторов нет. Не поддерживаются элементы с деструктором.
|
||||
*/
|
||||
|
||||
template
|
||||
<
|
||||
typename Key,
|
||||
typename Hash = default_hash<Key>,
|
||||
typename ZeroTraits = default_zero_traits<Key>,
|
||||
int INITIAL_SIZE_DEGREE = 16,
|
||||
int GROWTH_DEGREE = 2
|
||||
>
|
||||
class HashSet : private boost::noncopyable
|
||||
{
|
||||
private:
|
||||
typedef size_t HashValue;
|
||||
typedef HashSet<Key, Hash, ZeroTraits, INITIAL_SIZE_DEGREE, GROWTH_DEGREE> Self;
|
||||
|
||||
size_t m_size; /// Количество элементов
|
||||
UInt8 size_degree; /// Размер таблицы в виде степени двух
|
||||
bool has_zero; /// Хэш-таблица содержит элемент с ключём = 0.
|
||||
Key * buf; /// Кусок памяти для всех элементов кроме элемента с ключём 0.
|
||||
|
||||
Hash hash;
|
||||
|
||||
#ifdef DBMS_HASH_MAP_COUNT_COLLISIONS
|
||||
mutable size_t collisions;
|
||||
#endif
|
||||
|
||||
inline size_t buf_size() const { return 1 << size_degree; }
|
||||
inline size_t max_fill() const { return 1 << (size_degree - 1); }
|
||||
inline size_t mask() const { return buf_size() - 1; }
|
||||
inline size_t place(HashValue x) const { return x & mask(); }
|
||||
|
||||
|
||||
/// Увеличить размер буфера в 2 ^ GROWTH_DEGREE раз
|
||||
void resize()
|
||||
{
|
||||
size_t old_size = buf_size();
|
||||
|
||||
size_degree += GROWTH_DEGREE;
|
||||
Key * new_buf = reinterpret_cast<Key*>(calloc(buf_size(), sizeof(Key)));
|
||||
Key * old_buf = buf;
|
||||
buf = new_buf;
|
||||
|
||||
for (size_t i = 0; i < old_size; ++i)
|
||||
if (!ZeroTraits::check(old_buf[i]))
|
||||
reinsert(old_buf[i]);
|
||||
|
||||
free(reinterpret_cast<void*>(old_buf));
|
||||
}
|
||||
|
||||
|
||||
/** Вставить в новый буфер значение, которое было в старом буфере.
|
||||
* Используется при увеличении размера буфера.
|
||||
*/
|
||||
void reinsert(const Key & x)
|
||||
{
|
||||
size_t place_value = place(hash(x));
|
||||
while (!ZeroTraits::check(buf[place_value]))
|
||||
{
|
||||
++place_value;
|
||||
place_value &= mask();
|
||||
#ifdef DBMS_HASH_MAP_COUNT_COLLISIONS
|
||||
++collisions;
|
||||
#endif
|
||||
}
|
||||
memcpy(&buf[place_value], &x, sizeof(x));
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
typedef Key key_type;
|
||||
typedef Key value_type;
|
||||
|
||||
|
||||
HashSet() :
|
||||
m_size(0),
|
||||
size_degree(INITIAL_SIZE_DEGREE),
|
||||
has_zero(false)
|
||||
{
|
||||
buf = reinterpret_cast<Key*>(calloc(buf_size(), sizeof(Key)));
|
||||
#ifdef DBMS_HASH_MAP_COUNT_COLLISIONS
|
||||
collisions = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
~HashSet()
|
||||
{
|
||||
free(reinterpret_cast<void*>(buf));
|
||||
}
|
||||
|
||||
|
||||
/// Вставить ключ.
|
||||
void insert(const Key & x)
|
||||
{
|
||||
if (ZeroTraits::check(x))
|
||||
{
|
||||
if (!has_zero)
|
||||
{
|
||||
++m_size;
|
||||
has_zero = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
size_t place_value = place(hash(x));
|
||||
while (!ZeroTraits::check(buf[place_value]) && buf[place_value] != x)
|
||||
{
|
||||
++place_value;
|
||||
place_value &= mask();
|
||||
#ifdef DBMS_HASH_MAP_COUNT_COLLISIONS
|
||||
++collisions;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!ZeroTraits::check(buf[place_value]) && buf[place_value] == x)
|
||||
return;
|
||||
|
||||
buf[place_value] = x;
|
||||
++m_size;
|
||||
|
||||
if (unlikely(m_size > max_fill()))
|
||||
resize();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
bool has(Key x) const
|
||||
{
|
||||
if (ZeroTraits::check(x))
|
||||
return has_zero;
|
||||
|
||||
size_t place_value = place(hash(x));
|
||||
while (!ZeroTraits::check(buf[place_value]) && buf[place_value] != x)
|
||||
{
|
||||
++place_value;
|
||||
place_value &= mask();
|
||||
#ifdef DBMS_HASH_MAP_COUNT_COLLISIONS
|
||||
++collisions;
|
||||
#endif
|
||||
}
|
||||
|
||||
return !ZeroTraits::check(buf[place_value]);
|
||||
}
|
||||
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return 0 == m_size;
|
||||
}
|
||||
|
||||
#ifdef DBMS_HASH_MAP_COUNT_COLLISIONS
|
||||
size_t getCollisions() const
|
||||
{
|
||||
return collisions;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
@ -16,7 +16,7 @@ namespace DB
|
||||
class InterpreterSelectQuery
|
||||
{
|
||||
public:
|
||||
InterpreterSelectQuery(ASTPtr query_ptr_, Context & context_, QueryProcessingStage::Enum to_stage_ = QueryProcessingStage::Complete);
|
||||
InterpreterSelectQuery(ASTPtr query_ptr_, const Context & context_, QueryProcessingStage::Enum to_stage_ = QueryProcessingStage::Complete);
|
||||
|
||||
/// Выполнить запрос, получить поток блоков для чтения
|
||||
BlockInputStreamPtr execute();
|
||||
|
73
dbms/include/DB/Interpreters/Set.h
Normal file
73
dbms/include/DB/Interpreters/Set.h
Normal file
@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
|
||||
#include <DB/Parsers/IAST.h>
|
||||
|
||||
#include <DB/Interpreters/HashSet.h>
|
||||
#include <DB/Interpreters/Aggregator.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
|
||||
/** Структура данных для реализации выражения IN.
|
||||
*/
|
||||
struct Set
|
||||
{
|
||||
/** Разные структуры данных, которые могут использоваться для проверки принадлежности
|
||||
* одного или нескольких столбцов значений множеству.
|
||||
*/
|
||||
typedef std::set<Row> SetGeneric;
|
||||
typedef HashSet<UInt64> SetUInt64;
|
||||
typedef HashSet<StringRef, StringRefHash, StringRefZeroTraits> SetString;
|
||||
typedef HashSet<UInt128, UInt128Hash, UInt128ZeroTraits> SetHashed;
|
||||
|
||||
/// Наиболее общий вариант. Самый медленный. На данный момент, не используется.
|
||||
SetGeneric generic;
|
||||
|
||||
/// Специализация для случая, когда есть один числовой ключ (не с плавающей запятой).
|
||||
SetUInt64 key64;
|
||||
|
||||
/// Специализация для случая, когда есть один строковый ключ.
|
||||
SetString key_string;
|
||||
StringPool string_pool;
|
||||
|
||||
/** Сравнивает 128 битные хэши.
|
||||
* Если все ключи фиксированной длины, влезающие целиком в 128 бит, то укладывает их без изменений в 128 бит.
|
||||
* Иначе - вычисляет md5 от набора из всех ключей.
|
||||
* (При этом, строки, содержащие нули посередине, могут склеиться.)
|
||||
*/
|
||||
SetHashed hashed;
|
||||
|
||||
enum Type
|
||||
{
|
||||
EMPTY = 0,
|
||||
GENERIC = 1,
|
||||
KEY_64 = 2,
|
||||
KEY_STRING = 3,
|
||||
HASHED = 4,
|
||||
};
|
||||
Type type;
|
||||
|
||||
Set() : type(EMPTY) {}
|
||||
bool empty() { return type == EMPTY; }
|
||||
|
||||
|
||||
/** Создать множество по потоку блоков (для подзапроса). */
|
||||
void create(BlockInputStreamPtr stream) { /* TODO */ }
|
||||
|
||||
/** Создать множество по выражению (для перечисления в самом запросе). */
|
||||
void create(ASTPtr node);
|
||||
|
||||
/** Для указанных столбцов блока проверить принадлежность их значений множеству.
|
||||
* Записать результат в столбец в позиции result.
|
||||
*/
|
||||
void execute(Block & block, const ColumnNumbers & arguments, size_t result);
|
||||
};
|
||||
|
||||
typedef SharedPtr<Set> SetPtr;
|
||||
|
||||
|
||||
}
|
24
dbms/include/DB/Parsers/ASTSet.h
Normal file
24
dbms/include/DB/Parsers/ASTSet.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <DB/Interpreters/Set.h>
|
||||
#include <DB/Parsers/IAST.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
/** Множество. В процессе вычисления, на множество заменяется выражение в секции IN
|
||||
* - подзапрос или явное перечисление значений.
|
||||
*/
|
||||
class ASTSet : public IAST
|
||||
{
|
||||
public:
|
||||
SetPtr set;
|
||||
|
||||
ASTSet() {}
|
||||
ASTSet(StringRange range_) : IAST(range_) {}
|
||||
String getID() { return "Set"; }
|
||||
ASTPtr clone() const { return new ASTSet(*this); }
|
||||
};
|
||||
|
||||
}
|
@ -14,15 +14,13 @@ namespace DB
|
||||
class ASTSubquery : public IAST
|
||||
{
|
||||
public:
|
||||
/// находится ли внутри функции in
|
||||
bool is_in;
|
||||
/// тип возвращаемого значения
|
||||
DataTypePtr return_type;
|
||||
/// номер столбца возвращаемого значения
|
||||
size_t return_column_number;
|
||||
|
||||
ASTSubquery() {}
|
||||
ASTSubquery(StringRange range_) : IAST(range_), is_in(false), return_column_number(0) {}
|
||||
ASTSubquery(StringRange range_) : IAST(range_), return_column_number(0) {}
|
||||
|
||||
/** Получить текст, который идентифицирует этот элемент. */
|
||||
String getID() { return "Subquery"; };
|
||||
|
@ -195,7 +195,9 @@ public:
|
||||
(">", "greater")
|
||||
("=", "equals")
|
||||
("LIKE", "like")
|
||||
("NOT LIKE", "notLike"),
|
||||
("NOT LIKE", "notLike")
|
||||
("IN", "in")
|
||||
("NOT IN", "notIn"),
|
||||
elem_parser)
|
||||
{
|
||||
}
|
||||
|
@ -6,7 +6,10 @@
|
||||
#include <DB/Parsers/ASTAsterisk.h>
|
||||
#include <DB/Parsers/ASTExpressionList.h>
|
||||
#include <DB/Parsers/ASTSelectQuery.h>
|
||||
#include <DB/Parsers/ASTSubquery.h>
|
||||
#include <DB/Parsers/ASTSet.h>
|
||||
|
||||
#include <DB/Interpreters/InterpreterSelectQuery.h>
|
||||
#include <DB/Interpreters/Expression.h>
|
||||
|
||||
|
||||
@ -460,4 +463,53 @@ void Expression::markBeforeAndAfterAggregation(unsigned before_part_id, unsigned
|
||||
markBeforeAndAfterAggregationImpl(ast, before_part_id, after_part_id);
|
||||
}
|
||||
|
||||
|
||||
void Expression::makeSets()
|
||||
{
|
||||
makeSetsImpl(ast);
|
||||
}
|
||||
|
||||
|
||||
void Expression::makeSetsImpl(ASTPtr ast)
|
||||
{
|
||||
/// Обход в глубину. Ищем выражения IN.
|
||||
if (ASTFunction * func = dynamic_cast<ASTFunction *>(&*ast))
|
||||
{
|
||||
if (func->name == "in" || func->name == "notIn")
|
||||
{
|
||||
if (func->children.size() != 1)
|
||||
throw Exception("Function IN requires exactly 2 arguments",
|
||||
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
||||
|
||||
/// У функции должно быть два аргумента.
|
||||
ASTExpressionList & args = dynamic_cast<ASTExpressionList &>(*func->children[0]);
|
||||
if (args.children.size() != 2)
|
||||
throw Exception("Function IN requires exactly 2 arguments",
|
||||
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
|
||||
|
||||
/** Нужно преобразовать правый аргумент в множество.
|
||||
* Это может быть перечисление значений или подзапрос.
|
||||
* Перечисление значений парсится как функция tuple.
|
||||
*/
|
||||
ASTPtr arg = args.children[1];
|
||||
if (dynamic_cast<ASTSubquery *>(&*arg))
|
||||
{
|
||||
/// Исполняем подзапрос, превращаем результат в множество, и кладём это множество на место подзапроса.
|
||||
InterpreterSelectQuery interpreter(arg, context, QueryProcessingStage::Complete);
|
||||
ASTSet * ast_set = new ASTSet;
|
||||
ast_set->set = new Set;
|
||||
ast_set->set->create(interpreter.execute());
|
||||
arg = ast_set;
|
||||
}
|
||||
else /// TODO случай явного перечисления значений.
|
||||
throw Exception("Incorrect type of 2nd argument for function IN. Must be subquery or set of values.",
|
||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
||||
}
|
||||
}
|
||||
else
|
||||
for (ASTs::iterator it = ast->children.begin(); it != ast->children.end(); ++it)
|
||||
if (!dynamic_cast<ASTSelectQuery *>(&**it))
|
||||
makeSetsImpl(*it);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ namespace DB
|
||||
{
|
||||
|
||||
|
||||
InterpreterSelectQuery::InterpreterSelectQuery(ASTPtr query_ptr_, Context & context_, QueryProcessingStage::Enum to_stage_)
|
||||
InterpreterSelectQuery::InterpreterSelectQuery(ASTPtr query_ptr_, const Context & context_, QueryProcessingStage::Enum to_stage_)
|
||||
: query_ptr(query_ptr_), query(dynamic_cast<ASTSelectQuery &>(*query_ptr)),
|
||||
context(context_), settings(context.getSettings()), to_stage(to_stage_),
|
||||
log(&Logger::get("InterpreterSelectQuery"))
|
||||
|
Loading…
Reference in New Issue
Block a user