mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-21 15:12:02 +00:00
dbms: development [#CONV-2944].
This commit is contained in:
parent
568a4de64c
commit
082aac3771
@ -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;
|
||||
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
53
dbms/include/DB/DataTypes/FieldToDataType.h
Normal file
53
dbms/include/DB/DataTypes/FieldToDataType.h
Normal 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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
@ -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() {}
|
||||
};
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ namespace DB
|
||||
|
||||
using Poco::SharedPtr;
|
||||
|
||||
typedef std::map<String, SharedPtr<IFunction> > Functions;
|
||||
typedef std::map<String, FunctionPtr> Functions;
|
||||
|
||||
/** Набор известных объектов, которые могут быть использованы в запросе.
|
||||
*/
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
@ -21,6 +21,8 @@ public:
|
||||
|
||||
/// сама функция
|
||||
FunctionPtr function;
|
||||
/// типы возвращаемых значений
|
||||
DataTypes return_types;
|
||||
|
||||
ASTFunction() {}
|
||||
ASTFunction(StringRange range_) : range(range_) {}
|
||||
|
@ -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_) {}
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
152
dbms/src/Interpreters/Expression.cpp
Normal file
152
dbms/src/Interpreters/Expression.cpp
Normal 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))
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
88
dbms/src/Interpreters/tests/expression.cpp
Normal file
88
dbms/src/Interpreters/tests/expression.cpp
Normal 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;
|
||||
}
|
@ -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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user