dbms: development [#CONV-2944].

This commit is contained in:
Alexey Milovidov 2011-08-12 18:27:39 +00:00
parent 568a4de64c
commit 082aac3771
18 changed files with 395 additions and 48 deletions

View File

@ -19,11 +19,11 @@ template <typename T>
class ColumnConst : public IColumn
{
public:
ColumnConst(size_t s_, T & data_) : s(s_), data(data_) {}
ColumnConst(size_t s_, const T & data_) : s(s_), data(data_) {}
ColumnPtr cloneEmpty() const { return new ColumnConst(0, data); }
size_t size() const { return s; }
Field operator[](size_t n) const { return data; }
Field operator[](size_t n) const { return typename NearestFieldType<T>::Type(data); }
void cut(size_t start, size_t length) { s = length; }
void clear() { s = 0; }
void insert(const Field & x)
@ -37,7 +37,7 @@ public:
const T & getData() const { return data; }
/** Преобразование из константы в полноценный столбец */
virtual ColumnPtr convertToFullColumn() const = 0;
// virtual ColumnPtr convertToFullColumn() const = 0;
private:
size_t s;

View File

@ -53,6 +53,10 @@ namespace ErrorCodes
ILLEGAL_TYPE_OF_ARGUMENT,
ILLEGAL_COLUMN,
ILLEGAL_NUMBER_OF_RESULT_COLUMNS,
UNKNOWN_FUNCTION,
UNKNOWN_IDENTIFIER,
NOT_IMPLEMENTED,
LOGICAL_ERROR,
};
}

View File

@ -102,6 +102,40 @@ public:
}
};
/** Выводит текстовое представление типа, как литерала в SQL запросе */
class FieldVisitorToString : public boost::static_visitor<String>
{
public:
String operator() (const Null & x) const { return "NULL"; }
String operator() (const UInt64 & x) const { return Poco::NumberFormatter::format(x); }
String operator() (const Int64 & x) const { return Poco::NumberFormatter::format(x); }
String operator() (const Float64 & x) const { return Poco::NumberFormatter::format(x); }
String operator() (const String & x) const
{
std::stringstream s;
s << mysqlxx::quote << x;
return s.str();
}
String operator() (const Array & x) const
{
std::stringstream s;
FieldVisitorToString visitor;
s << "[";
for (Array::const_iterator it = x.begin(); it != x.end(); ++it)
{
if (it != x.begin())
s << ", ";
s << boost::apply_visitor(FieldVisitorToString(), *it);
}
s << "]";
return s.str();
}
};
template <typename T> struct NearestFieldType;

View File

@ -47,6 +47,7 @@ public:
void deserializeTextQuoted(Field & field, ReadBuffer & istr, bool compatible = false) const;
ColumnPtr createColumn() const;
ColumnPtr createConstColumn(size_t size, const Field & field) const;
};
}

View File

@ -42,6 +42,7 @@ public:
void deserializeTextQuoted(Field & field, ReadBuffer & istr, bool compatible = false) const;
ColumnPtr createColumn() const;
ColumnPtr createConstColumn(size_t size, const Field & field) const;
};
}

View File

@ -0,0 +1,53 @@
#pragma once
#include <DB/DataTypes/DataTypesNumberFixed.h>
#include <DB/DataTypes/DataTypeString.h>
namespace DB
{
/// Для заданного значения Field возвращает минимальный тип данных, позволяющий хранить значение этого типа.
class FieldToDataType : public boost::static_visitor<DataTypePtr>
{
public:
DataTypePtr operator() (const Null & x) const
{
throw Exception("NULL literals is not implemented yet", ErrorCodes::NOT_IMPLEMENTED);
}
DataTypePtr operator() (const UInt64 & x) const
{
if (x <= std::numeric_limits<UInt8>::max()) return new DataTypeUInt8;
if (x <= std::numeric_limits<UInt16>::max()) return new DataTypeUInt16;
if (x <= std::numeric_limits<UInt32>::max()) return new DataTypeUInt32;
return new DataTypeUInt64;
}
DataTypePtr operator() (const Int64 & x) const
{
if (x <= std::numeric_limits<Int8>::max() && x >= std::numeric_limits<Int8>::min()) return new DataTypeInt8;
if (x <= std::numeric_limits<Int16>::max() && x >= std::numeric_limits<Int16>::min()) return new DataTypeInt16;
if (x <= std::numeric_limits<Int32>::max() && x >= std::numeric_limits<Int32>::min()) return new DataTypeInt32;
return new DataTypeInt64;
}
DataTypePtr operator() (const Float64 & x) const
{
return new DataTypeFloat64;
}
DataTypePtr operator() (const String & x) const
{
return new DataTypeString;
}
DataTypePtr operator() (const Array & x) const
{
throw Exception("Logical error: array literals cannot be passed to FieldToDataType function.", ErrorCodes::LOGICAL_ERROR);
}
};
}

View File

@ -57,6 +57,10 @@ public:
*/
virtual SharedPtr<IColumn> createColumn() const = 0;
/** Создать столбец соответствующего типа, содержащий константу со значением Field, длины size.
*/
virtual SharedPtr<IColumn> createConstColumn(size_t size, const Field & field) const = 0;
virtual ~IDataType() {}
};

View File

@ -5,6 +5,8 @@
#include <DB/DataTypes/IDataTypeNumber.h>
#include <DB/Columns/ColumnConst.h>
namespace DB
{
@ -26,7 +28,7 @@ public:
void serializeBinary(const Field & field, WriteBuffer & ostr) const
{
/// ColumnType::value_type - более узкий тип. Например, UInt8, когда тип Field - UInt64
typename ColumnType::value_type x = boost::get<FieldType>(field);
typename ColumnType::value_type x = boost::get<typename NearestFieldType<FieldType>::Type>(field);
writeBinary(x, ostr);
}
@ -55,6 +57,11 @@ public:
{
return new ColumnType;
}
ColumnPtr createConstColumn(size_t size, const Field & field) const
{
return new ColumnConst<FieldType>(size, boost::get<typename NearestFieldType<FieldType>::Type>(field));
}
};

View File

@ -6,6 +6,8 @@
#include <DB/IO/VarInt.h>
#include <DB/Columns/ColumnConst.h>
#include <DB/DataTypes/IDataTypeNumber.h>
@ -62,6 +64,11 @@ public:
{
return new ColumnType;
}
ColumnPtr createConstColumn(size_t size, const Field & field) const
{
return new ColumnConst<FieldType>(size, boost::get<FieldType>(field));
}
};
}

View File

@ -13,7 +13,7 @@ namespace DB
using Poco::SharedPtr;
typedef std::map<String, SharedPtr<IFunction> > Functions;
typedef std::map<String, FunctionPtr> Functions;
/** Набор известных объектов, которые могут быть использованы в запросе.
*/

View File

@ -15,38 +15,49 @@ namespace DB
class Expression
{
public:
Expression(IAST & ast_, const Context & context_) : ast(ast_), context(context_)
Expression(ASTPtr ast_, const Context & context_) : ast(ast_), context(context_)
{
addSemantic();
checkTypes();
glueTree();
addSemantic(ast);
checkTypes(ast);
glueTree(ast);
}
/** Выполнить выражение над блоком. Блок должен содержать все столбцы - идентификаторы.
* Функция добавляет в блок новые столбцы - результаты вычислений.
*/
void execute(Block & block);
private:
IAST & ast;
Context & context;
ASTPtr ast;
const Context & context;
/** Для узлов - литералов, создать соответствующие столбцы-константы и типы данных.
* Для узлов - функций - прописать ссылки на функции и заменить имена на канонические.
* Для узлов - идентификаторов - прописать ссылки на их типы.
*/
void addSemantic();
void addSemantic(ASTPtr ast);
/** Проверить, что все функции применимы.
*/
void checkTypes();
void checkTypes(ASTPtr ast);
/** Склеить одинаковые узлы в синтаксическом дереве (превращая его в направленный ациклический граф).
* Это означает, в том числе то, что функции с одними и теми же аргументами, будут выполняться только один раз.
* Например, выражение rand(), rand() вернёт два идентичных столбца.
* Поэтому, все функции, в которых такое поведение нежелательно, должны содержать дополнительный параметр "тег".
*/
void glueTree();
void glueTree(ASTPtr ast);
typedef std::map<String, ASTPtr> Subtrees;
void glueTreeImpl(ASTPtr ast, Subtrees & Subtrees);
/** Прописать во всех узлах, что они ещё не вычислены.
*/
void setNotCalculated(ASTPtr ast);
void executeImpl(ASTPtr ast, Block & block);
};

View File

@ -21,6 +21,8 @@ public:
/// сама функция
FunctionPtr function;
/// типы возвращаемых значений
DataTypes return_types;
ASTFunction() {}
ASTFunction(StringRange range_) : range(range_) {}

View File

@ -2,6 +2,7 @@
#define DBMS_PARSERS_ASTLITERAL_H
#include <DB/Core/Field.h>
#include <DB/DataTypes/IDataType.h>
#include <DB/Parsers/IAST.h>
@ -16,6 +17,8 @@ public:
StringRange range;
/// значение
Field value;
/// тип
DataTypePtr type;
ASTLiteral() {}
ASTLiteral(StringRange range_, const Field & value_) : range(range_), value(value_) {}

View File

@ -3,6 +3,7 @@
#include <DB/Columns/ColumnFixedArray.h>
#include <DB/Columns/ColumnFixedString.h>
#include <DB/Columns/ColumnsNumber.h>
#include <DB/Columns/ColumnConst.h>
#include <DB/DataTypes/DataTypeFixedString.h>
@ -110,4 +111,10 @@ ColumnPtr DataTypeFixedString::createColumn() const
return new ColumnFixedString(n);
}
ColumnPtr DataTypeFixedString::createConstColumn(size_t size, const Field & field) const
{
return new ColumnConst<String>(size, boost::get<String>(field));
}
}

View File

@ -3,6 +3,7 @@
#include <DB/Columns/ColumnArray.h>
#include <DB/Columns/ColumnString.h>
#include <DB/Columns/ColumnsNumber.h>
#include <DB/Columns/ColumnConst.h>
#include <DB/DataTypes/DataTypeString.h>
@ -136,4 +137,10 @@ ColumnPtr DataTypeString::createColumn() const
return new ColumnString;
}
ColumnPtr DataTypeString::createConstColumn(size_t size, const Field & field) const
{
return new ColumnConst<String>(size, boost::get<String>(field));
}
}

View File

@ -0,0 +1,152 @@
#include <DB/DataTypes/FieldToDataType.h>
#include <DB/Parsers/ASTFunction.h>
#include <DB/Parsers/ASTIdentifier.h>
#include <DB/Parsers/ASTLiteral.h>
#include <DB/Parsers/ASTExpressionList.h>
#include <DB/Interpreters/Expression.h>
namespace DB
{
void Expression::addSemantic(ASTPtr ast)
{
if (ASTFunction * node = dynamic_cast<ASTFunction *>(&*ast))
{
Functions::const_iterator it = context.functions.find(node->name);
if (it == context.functions.end())
throw Exception("Unknown function " + node->name, ErrorCodes::UNKNOWN_FUNCTION);
node->function = it->second;
}
else if (ASTIdentifier * node = dynamic_cast<ASTIdentifier *>(&*ast))
{
NamesAndTypes::const_iterator it = context.columns.find(node->name);
if (it == context.columns.end())
throw Exception("Unknown identifier " + node->name, ErrorCodes::UNKNOWN_IDENTIFIER);
node->type = it->second;
}
else if (ASTLiteral * node = dynamic_cast<ASTLiteral *>(&*ast))
{
node->type = boost::apply_visitor(FieldToDataType(), node->value);
}
ASTs children = ast->getChildren();
for (ASTs::iterator it = children.begin(); it != children.end(); ++it)
addSemantic(*it);
}
void Expression::checkTypes(ASTPtr ast)
{
/// Обход в глубину
ASTs children = ast->getChildren();
for (ASTs::iterator it = children.begin(); it != children.end(); ++it)
checkTypes(*it);
if (ASTFunction * node = dynamic_cast<ASTFunction *>(&*ast))
{
/// Типы аргументов
DataTypes argument_types;
ASTs & arguments = dynamic_cast<ASTExpressionList &>(*node->arguments).children;
for (ASTs::iterator it = arguments.begin(); it != arguments.end(); ++it)
{
if (ASTFunction * arg = dynamic_cast<ASTFunction *>(&**it))
argument_types.insert(argument_types.end(), arg->return_types.begin(), arg->return_types.end());
else if (ASTIdentifier * arg = dynamic_cast<ASTIdentifier *>(&**it))
argument_types.push_back(arg->type);
else if (ASTLiteral * arg = dynamic_cast<ASTLiteral *>(&**it))
argument_types.push_back(arg->type);
}
/// Получаем типы результата
node->return_types = node->function->getReturnTypes(argument_types);
}
}
void Expression::glueTree(ASTPtr ast)
{
Subtrees subtrees;
glueTreeImpl(ast, subtrees);
}
void Expression::glueTreeImpl(ASTPtr ast, Subtrees & subtrees)
{
/// Обход в глубину
ASTs children = ast->getChildren();
for (ASTs::iterator it = children.begin(); it != children.end(); ++it)
glueTreeImpl(*it, subtrees);
if (ASTFunction * node = dynamic_cast<ASTFunction *>(&*ast))
{
String tree_id = node->arguments->getTreeID();
if (subtrees.end() == subtrees.find(tree_id))
subtrees[tree_id] = node->arguments;
else
node->arguments = subtrees[tree_id];
}
else if (ASTExpressionList * node = dynamic_cast<ASTExpressionList *>(&*ast))
{
for (ASTs::iterator it = node->children.begin(); it != node->children.end(); ++it)
{
String tree_id = (*it)->getTreeID();
if (subtrees.end() == subtrees.find(tree_id))
subtrees[tree_id] = *it;
else
*it = subtrees[tree_id];
}
}
}
void Expression::setNotCalculated(ASTPtr ast)
{
ast->calculated = false;
ASTs children = ast->getChildren();
for (ASTs::iterator it = children.begin(); it != children.end(); ++it)
setNotCalculated(ast);
}
void Expression::execute(Block & block)
{
setNotCalculated(ast);
executeImpl(ast, block);
}
void Expression::executeImpl(ASTPtr ast, Block & block)
{
/// Обход в глубину
ASTs children = ast->getChildren();
for (ASTs::iterator it = children.begin(); it != children.end(); ++it)
executeImpl(*it, block);
if (ast->calculated)
return;
/** Столбцы из таблицы уже загружены в блок.
* Вычисление состоит в добавлении в блок новых столбцов - констант и результатов вычислений функций.
*/
if (ASTFunction * node = dynamic_cast<ASTFunction *>(&*ast))
{
}
else if (ASTLiteral * node = dynamic_cast<ASTLiteral *>(&*ast))
{
}
}
}

View File

@ -0,0 +1,88 @@
#include <iostream>
#include <mysqlxx/mysqlxx.h>
#include <DB/DataTypes/DataTypesNumberFixed.h>
#include <DB/Functions/FunctionsArithmetic.h>
#include <DB/Parsers/ASTSelectQuery.h>
#include <DB/Parsers/ParserSelectQuery.h>
#include <DB/Parsers/formatAST.h>
#include <DB/Interpreters/Expression.h>
void dump(DB::IAST & ast, int level = 0)
{
std::string prefix(level, ' ');
if (DB::ASTFunction * node = dynamic_cast<DB::ASTFunction *>(&ast))
{
std::cout << prefix << node << " Function, name = " << node->function->getName() << ", return types: ";
for (DB::DataTypes::const_iterator it = node->return_types.begin(); it != node->return_types.end(); ++it)
{
if (it != node->return_types.begin())
std::cout << ", ";
std::cout << (*it)->getName();
}
std::cout << std::endl;
}
else if (DB::ASTIdentifier * node = dynamic_cast<DB::ASTIdentifier *>(&ast))
{
std::cout << prefix << node << " Identifier, name = " << node->name << ", type = " << node->type->getName() << std::endl;
}
else if (DB::ASTLiteral * node = dynamic_cast<DB::ASTLiteral *>(&ast))
{
std::cout << prefix << node << " Literal, " << boost::apply_visitor(DB::FieldVisitorToString(), node->value)
<< ", type = " << node->type->getName() << std::endl;
}
DB::ASTs children = ast.getChildren();
for (DB::ASTs::iterator it = children.begin(); it != children.end(); ++it)
dump(**it, level + 1);
}
int main(int argc, char ** argv)
{
try
{
DB::ParserSelectQuery parser;
DB::ASTPtr ast;
std::string input = "SELECT 2 + x * 2, x * 2";
std::string expected;
const char * begin = input.data();
const char * end = begin + input.size();
const char * pos = begin;
if (parser.parse(pos, end, ast, expected))
{
std::cout << "Success." << std::endl;
DB::formatAST(*ast, std::cout);
std::cout << std::endl << ast->getTreeID() << std::endl;
}
else
{
std::cout << "Failed at position " << (pos - begin) << ": "
<< mysqlxx::quote << input.substr(pos - begin, 10)
<< ", expected " << expected << "." << std::endl;
}
DB::Context context;
context.columns["x"] = new DB::DataTypeInt16;
context.functions["plus"] = new DB::FunctionPlus;
context.functions["multiply"] = new DB::FunctionMultiply;
DB::Expression expression(ast, context);
dump(*ast);
}
catch (const DB::Exception & e)
{
std::cerr << e.message() << std::endl;
}
return 0;
}

View File

@ -15,40 +15,6 @@
namespace DB
{
/** Выводит текстовое представление типа, как литерала в SQL запросе */
class FieldVisitorToString : public boost::static_visitor<String>
{
public:
String operator() (const Null & x) const { return "NULL"; }
String operator() (const UInt64 & x) const { return Poco::NumberFormatter::format(x); }
String operator() (const Int64 & x) const { return Poco::NumberFormatter::format(x); }
String operator() (const Float64 & x) const { return Poco::NumberFormatter::format(x); }
String operator() (const String & x) const
{
std::stringstream s;
s << mysqlxx::quote << x;
return s.str();
}
String operator() (const Array & x) const
{
std::stringstream s;
FieldVisitorToString visitor;
s << "[";
for (Array::const_iterator it = x.begin(); it != x.end(); ++it)
{
if (it != x.begin())
s << ", ";
s << boost::apply_visitor(FieldVisitorToString(), *it);
}
s << "]";
return s.str();
}
};
void formatAST(const IAST & ast, std::ostream & s)
{