dbms: development [#CONV-2944].

This commit is contained in:
Alexey Milovidov 2012-08-22 20:29:01 +00:00
parent 40281aa96f
commit 8ea28ddd6e
9 changed files with 342 additions and 6 deletions

View File

@ -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;

View 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
};
}

View File

@ -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();

View 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;
}

View 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); }
};
}

View File

@ -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"; };

View File

@ -195,7 +195,9 @@ public:
(">", "greater")
("=", "equals")
("LIKE", "like")
("NOT LIKE", "notLike"),
("NOT LIKE", "notLike")
("IN", "in")
("NOT IN", "notIn"),
elem_parser)
{
}

View File

@ -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);
}
}

View File

@ -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"))