This commit is contained in:
scanhex12 2024-08-27 17:29:28 -07:00 committed by GitHub
commit bde1e77af7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 1860 additions and 5 deletions

View File

@ -730,7 +730,8 @@ bool Client::processWithFuzzing(const String & full_query)
}
// Kusto is not a subject for fuzzing (yet)
if (client_context->getSettingsRef().dialect == DB::Dialect::kusto)
if (client_context->getSettingsRef().dialect == DB::Dialect::kusto
|| client_context->getSettingsRef().dialect == DB::Dialect::mongo)
{
return true;
}

View File

@ -46,6 +46,8 @@
#include <Parsers/PRQL/ParserPRQLQuery.h>
#include <Parsers/Kusto/ParserKQLStatement.h>
#include <Parsers/Kusto/parseKQLQuery.h>
#include <Parsers/Mongo/parseMongoQuery.h>
#include <Parsers/Mongo/ParserMongoQuery.h>
#include <Processors/Formats/Impl/NullFormat.h>
#include <Processors/Formats/IInputFormat.h>
@ -302,6 +304,8 @@ ASTPtr ClientBase::parseQuery(const char *& pos, const char * end, const Setting
parser = std::make_unique<ParserKQLStatement>(end, settings.allow_settings_after_format_in_insert);
else if (dialect == Dialect::prql)
parser = std::make_unique<ParserPRQLQuery>(max_length, settings.max_parser_depth, settings.max_parser_backtracks);
else if (dialect == Dialect::mongo)
parser = std::make_unique<Mongo::ParserMongoQuery>(max_length, settings.max_parser_depth, settings.max_parser_backtracks);
else
parser = std::make_unique<ParserQuery>(end, settings.allow_settings_after_format_in_insert);
@ -310,6 +314,8 @@ ASTPtr ClientBase::parseQuery(const char *& pos, const char * end, const Setting
String message;
if (dialect == Dialect::kusto)
res = tryParseKQLQuery(*parser, pos, end, message, true, "", allow_multi_statements, max_length, settings.max_parser_depth, settings.max_parser_backtracks, true);
else if (dialect == Dialect::mongo)
res = Mongo::tryParseMongoQuery(*parser, pos, end, message, true, "", allow_multi_statements, max_length, settings.max_parser_depth, settings.max_parser_backtracks, true);
else
res = tryParseQuery(*parser, pos, end, message, true, "", allow_multi_statements, max_length, settings.max_parser_depth, settings.max_parser_backtracks, true);
@ -323,6 +329,8 @@ ASTPtr ClientBase::parseQuery(const char *& pos, const char * end, const Setting
{
if (dialect == Dialect::kusto)
res = parseKQLQueryAndMovePosition(*parser, pos, end, "", allow_multi_statements, max_length, settings.max_parser_depth, settings.max_parser_backtracks);
else if (dialect == Dialect::mongo)
res = Mongo::parseMongoQueryAndMovePosition(*parser, pos, end, "", allow_multi_statements, max_length, settings.max_parser_depth, settings.max_parser_backtracks);
else
res = parseQueryAndMovePosition(*parser, pos, end, "", allow_multi_statements, max_length, settings.max_parser_depth, settings.max_parser_backtracks);
}

View File

@ -20,6 +20,8 @@
#include <Parsers/PRQL/ParserPRQLQuery.h>
#include <Parsers/Kusto/ParserKQLStatement.h>
#include <Parsers/Kusto/parseKQLQuery.h>
#include <Parsers/Mongo/parseMongoQuery.h>
#include <Parsers/Mongo/ParserMongoQuery.h>
namespace DB
{
@ -165,12 +167,16 @@ void LocalConnection::sendQuery(
parser = std::make_unique<ParserKQLStatement>(end, settings.allow_settings_after_format_in_insert);
else if (dialect == Dialect::prql)
parser = std::make_unique<ParserPRQLQuery>(settings.max_query_size, settings.max_parser_depth, settings.max_parser_backtracks);
else if (dialect == Dialect::mongo)
parser = std::make_unique<Mongo::ParserMongoQuery>(settings.max_query_size, settings.max_parser_depth, settings.max_parser_backtracks);
else
parser = std::make_unique<ParserQuery>(end, settings.allow_settings_after_format_in_insert);
ASTPtr parsed_query;
if (dialect == Dialect::kusto)
parsed_query = parseKQLQueryAndMovePosition(*parser, begin, end, "", /*allow_multi_statements*/false, settings.max_query_size, settings.max_parser_depth, settings.max_parser_backtracks);
else if (dialect == Dialect::mongo)
parsed_query = Mongo::parseMongoQueryAndMovePosition(*parser, begin, end, "", /*allow_multi_statements*/false, settings.max_query_size, settings.max_parser_depth, settings.max_parser_backtracks);
else
parsed_query = parseQueryAndMovePosition(*parser, begin, end, "", /*allow_multi_statements*/false, settings.max_query_size, settings.max_parser_depth, settings.max_parser_backtracks);

View File

@ -166,7 +166,8 @@ IMPLEMENT_SETTING_ENUM(MsgPackUUIDRepresentation, ErrorCodes::BAD_ARGUMENTS,
IMPLEMENT_SETTING_ENUM(Dialect, ErrorCodes::BAD_ARGUMENTS,
{{"clickhouse", Dialect::clickhouse},
{"kusto", Dialect::kusto},
{"prql", Dialect::prql}})
{"prql", Dialect::prql},
{"mongo", Dialect::mongo}})
IMPLEMENT_SETTING_ENUM(ParallelReplicasCustomKeyFilterType, ErrorCodes::BAD_ARGUMENTS,

View File

@ -295,6 +295,7 @@ enum class Dialect : uint8_t
clickhouse,
kusto,
prql,
mongo,
};
DECLARE_SETTING_ENUM(Dialect)

View File

@ -66,6 +66,9 @@
#include <Interpreters/executeQuery.h>
#include <Interpreters/DatabaseCatalog.h>
#include <Common/ProfileEvents.h>
#include "Core/SettingsEnums.h"
#include <Parsers/Mongo/ParserMongoQuery.h>
#include <Parsers/Mongo/parseMongoQuery.h>
#include <IO/CompressionMethod.h>
@ -715,7 +718,7 @@ static std::tuple<ASTPtr, BlockIO> executeQueryImpl(
ReadBuffer * istr)
{
const bool internal = flags.internal;
std::string full_inp(begin, end);
/// query_span is a special span, when this function exits, it's lifetime is not ended, but ends when the query finishes.
/// Some internal queries might call this function recursively by setting 'internal' parameter to 'true',
/// to make sure SpanHolders in current stack ends in correct order, we disable this span for these internal queries
@ -776,6 +779,11 @@ static std::tuple<ASTPtr, BlockIO> executeQueryImpl(
ParserPRQLQuery parser(max_query_size, settings.max_parser_depth, settings.max_parser_backtracks);
ast = parseQuery(parser, begin, end, "", max_query_size, settings.max_parser_depth, settings.max_parser_backtracks);
}
else if (settings.dialect == Dialect::mongo && !internal)
{
Mongo::ParserMongoQuery parser(max_query_size, settings.max_parser_depth, settings.max_parser_backtracks);
ast = parseMongoQuery(parser, begin, end, "", max_query_size, settings.max_parser_depth, settings.max_parser_backtracks);
}
else
{
ParserQuery parser(end, settings.allow_settings_after_format_in_insert);
@ -822,8 +830,9 @@ static std::tuple<ASTPtr, BlockIO> executeQueryImpl(
const char * query_end = end;
if (const auto * insert_query = ast->as<ASTInsertQuery>(); insert_query && insert_query->data)
{
query_end = insert_query->data;
}
bool is_create_parameterized_view = false;
if (const auto * create_query = ast->as<ASTCreateQuery>())
{
@ -839,7 +848,7 @@ static std::tuple<ASTPtr, BlockIO> executeQueryImpl(
/// Replace ASTQueryParameter with ASTLiteral for prepared statements.
/// Even if we don't have parameters in query_context, check that AST doesn't have unknown parameters
bool probably_has_params = find_first_symbols<'{'>(begin, end) != end;
if (!is_create_parameterized_view && probably_has_params)
if (!is_create_parameterized_view && probably_has_params && settings.dialect != Dialect::mongo)
{
ReplaceQueryParameterVisitor visitor(context->getQueryParameters());
visitor.visit(ast);

View File

@ -5,9 +5,14 @@ add_headers_and_sources(clickhouse_parsers ./Access)
add_headers_and_sources(clickhouse_parsers ./MySQL)
add_headers_and_sources(clickhouse_parsers ./Kusto)
add_headers_and_sources(clickhouse_parsers ./PRQL)
add_headers_and_sources(clickhouse_parsers ./Mongo)
add_headers_and_sources(clickhouse_parsers ./Kusto/KustoFunctions)
add_library(clickhouse_parsers ${clickhouse_parsers_headers} ${clickhouse_parsers_sources})
target_link_libraries(clickhouse_parsers PUBLIC clickhouse_common_io clickhouse_common_access)
if (TARGET ch_contrib::rapidjson)
target_link_libraries(clickhouse_parsers PRIVATE ch_contrib::rapidjson)
endif ()
if (TARGET ch_rust::prql)
target_link_libraries(clickhouse_parsers PRIVATE ch_rust::prql)
endif ()

View File

@ -0,0 +1,59 @@
#include "Metadata.h"
#include <Parsers/Mongo/Utils.h>
namespace DB
{
namespace ErrorCodes
{
extern const int BAD_ARGUMENTS;
}
namespace Mongo
{
QueryMetadata::QueryMetadata(
const char * begin, const char * end, QueryType query_type_, std::optional<int> limit_, std::optional<std::string> order_by_)
: collection_name(begin, end), query_type(query_type_), limit(limit_), order_by(order_by_)
{
}
std::shared_ptr<QueryMetadata> extractMetadataFromRequest(const char * begin, const char * end)
{
auto [token_begin, token_end] = getMetadataSubstring(begin, end);
const char * token_begin_collection_name = findKth<'.'>(token_begin, token_end, 1) + 1;
const char * token_end_collection_name = findKth<'.'>(token_begin, token_end, 2);
const char * token_begin_query_type = token_end_collection_name + 1;
const char * token_end_query_type = token_end;
std::string key(token_begin_query_type, token_end_query_type);
std::optional<QueryMetadata::QueryType> query_type;
for (const auto & [key_query, query] : QueryMetadata::queryTypeKeyWords)
{
if (key_query == key)
{
query_type = query;
}
}
if (!query_type)
{
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Invalid query");
}
MongoQueryKeyNameExtractor limit_extractor(".limit");
auto limit = limit_extractor.extractInt(begin, end);
MongoQueryKeyNameExtractor order_by_extractor(".sort");
auto order_by = order_by_extractor.extractString(begin, end);
return std::make_shared<QueryMetadata>(token_begin_collection_name, token_end_collection_name, *query_type, limit, order_by);
}
}
}

View File

@ -0,0 +1,57 @@
#pragma once
#include <memory>
#include <optional>
#include <Parsers/IParserBase.h>
namespace DB
{
namespace Mongo
{
/// Metadata for MongoDB queries. Contains collection, query type, limi and order by data.
class QueryMetadata
{
public:
bool add_data_to_query = true;
enum class QueryType : uint8_t
{
select = 0,
insert_many = 1,
insert_one = 2,
};
static constexpr std::pair<const char *, QueryType> queryTypeKeyWords[] = {
{"find", QueryType::select},
{"insertMany", QueryType::insert_many},
{"insertOne", QueryType::insert_one},
};
static constexpr size_t queryTypeKeyWordsLength = sizeof(queryTypeKeyWords) / sizeof(queryTypeKeyWords[0]);
explicit QueryMetadata(
const char * begin, const char * end, QueryType query_type_, std::optional<int> limit_, std::optional<std::string> order_by_);
const std::string & getCollectionName() const { return collection_name; }
QueryType getQueryType() const { return query_type; }
std::optional<size_t> getLimit() const { return limit; }
std::optional<std::string> getOrderBy() const { return order_by; }
private:
std::string collection_name;
QueryType query_type;
std::optional<size_t> limit;
std::optional<std::string> order_by;
};
std::shared_ptr<QueryMetadata> extractMetadataFromRequest(const char * begin, const char * end);
}
}

View File

@ -0,0 +1,35 @@
#include "ParserMongoCompareFunctions.h"
#include <Parsers/ASTFunction.h>
#include <Parsers/ASTIdentifier.h>
#include <Parsers/ASTLiteral.h>
namespace DB
{
namespace Mongo
{
bool ICompareFunction::parseImpl(ASTPtr & node)
{
auto identifier = std::make_shared<ASTIdentifier>(edge_name);
if (data.IsInt())
{
auto literal = std::make_shared<ASTLiteral>(Field(data.GetInt()));
auto where_condition = makeASTFunction(getFunctionAlias(), identifier, literal);
node = where_condition;
return true;
}
if (data.IsString())
{
auto literal = std::make_shared<ASTLiteral>(Field(data.GetString()));
auto where_condition = makeASTFunction(getFunctionAlias(), identifier, literal);
node = where_condition;
return true;
}
return false;
}
}
}

View File

@ -0,0 +1,107 @@
#pragma once
#include <Parsers/Mongo/ParserMongoFunction.h>
namespace DB
{
namespace Mongo
{
class ICompareFunction : public IMongoFunction
{
public:
explicit ICompareFunction(rapidjson::Value array_elements_, std::shared_ptr<QueryMetadata> metadata_, const std::string & edge_name_)
: IMongoFunction(std::move(array_elements_), metadata_, edge_name_)
{
}
virtual std::string getFunctionAlias() const = 0;
bool parseImpl(ASTPtr & node) override;
};
class MongoLtFunction : public ICompareFunction
{
public:
std::string getFunctionName() const override { return "$lt"; }
std::string getFunctionAlias() const override { return "less"; }
explicit MongoLtFunction(rapidjson::Value array_elements_, std::shared_ptr<QueryMetadata> metadata_, const std::string & edge_name_)
: ICompareFunction(std::move(array_elements_), metadata_, edge_name_)
{
}
};
class MongoLteFunction : public ICompareFunction
{
public:
std::string getFunctionName() const override { return "$lte"; }
std::string getFunctionAlias() const override { return "lessOrEquals"; }
explicit MongoLteFunction(rapidjson::Value array_elements_, std::shared_ptr<QueryMetadata> metadata_, const std::string & edge_name_)
: ICompareFunction(std::move(array_elements_), metadata_, edge_name_)
{
}
};
class MongoGtFunction : public ICompareFunction
{
public:
std::string getFunctionName() const override { return "$gt"; }
std::string getFunctionAlias() const override { return "greater"; }
explicit MongoGtFunction(rapidjson::Value array_elements_, std::shared_ptr<QueryMetadata> metadata_, const std::string & edge_name_)
: ICompareFunction(std::move(array_elements_), metadata_, edge_name_)
{
}
};
class MongoGteFunction : public ICompareFunction
{
public:
std::string getFunctionName() const override { return "$gte"; }
std::string getFunctionAlias() const override { return "greaterOrEquals"; }
explicit MongoGteFunction(rapidjson::Value array_elements_, std::shared_ptr<QueryMetadata> metadata_, const std::string & edge_name_)
: ICompareFunction(std::move(array_elements_), metadata_, edge_name_)
{
}
};
class MongoNotEqualsFunction : public ICompareFunction
{
public:
std::string getFunctionName() const override { return "$ne"; }
std::string getFunctionAlias() const override { return "notEquals"; }
explicit MongoNotEqualsFunction(
rapidjson::Value array_elements_, std::shared_ptr<QueryMetadata> metadata_, const std::string & edge_name_)
: ICompareFunction(std::move(array_elements_), metadata_, edge_name_)
{
}
};
class MongoLikeFunction : public ICompareFunction
{
public:
std::string getFunctionName() const override { return "$regex"; }
std::string getFunctionAlias() const override { return "like"; }
explicit MongoLikeFunction(rapidjson::Value array_elements_, std::shared_ptr<QueryMetadata> metadata_, const std::string & edge_name_)
: ICompareFunction(std::move(array_elements_), metadata_, edge_name_)
{
}
};
}
}

View File

@ -0,0 +1,54 @@
#include "ParserMongoFilter.h"
#include <memory>
#include <Parsers/ASTQueryParameter.h>
#include <Parsers/ASTFunction.h>
#include <Parsers/IAST_fwd.h>
#include <Parsers/Mongo/Utils.h>
namespace DB
{
namespace Mongo
{
bool ParserMongoFilter::parseImpl(ASTPtr & node)
{
chassert(data.IsObject());
std::vector<ASTPtr> child_trees;
for (auto it = data.MemberBegin(); it != data.MemberEnd(); ++it)
{
auto parser = createParser(copyValue(it->value), metadata, it->name.GetString());
ASTPtr child_node;
if (!parser->parseImpl(child_node))
{
return false;
}
child_trees.push_back(child_node);
}
if (child_trees.empty())
{
return true;
}
if (child_trees.size() == 1)
{
node = child_trees[0];
return true;
}
auto result = makeASTFunction("and");
for (const auto & elem : child_trees)
{
result->arguments->children.push_back(elem);
}
node = result;
return true;
}
}
}

View File

@ -0,0 +1,31 @@
#pragma once
#include <rapidjson/document.h>
#include <Parsers/IParserBase.h>
#include <Parsers/Mongo/Metadata.h>
#include <Parsers/Mongo/ParserMongoQuery.h>
namespace DB
{
namespace Mongo
{
class ParserMongoFilter : public IMongoParser
{
public:
explicit ParserMongoFilter(rapidjson::Value data_, std::shared_ptr<QueryMetadata> metadata_, const std::string & edge_name_)
: IMongoParser(std::move(data_), metadata_, edge_name_)
{
}
bool parseImpl(ASTPtr & node) override;
~ParserMongoFilter() override = default;
};
}
}

View File

@ -0,0 +1,186 @@
#include "ParserMongoFunction.h"
#include <Core/Field.h>
#include <Parsers/ASTIdentifier.h>
#include <Parsers/ASTLiteral.h>
#include <Parsers/ASTFunction.h>
#include <Parsers/IAST_fwd.h>
#include <Parsers/Mongo/ParserMongoQuery.h>
#include <Parsers/Mongo/Utils.h>
namespace DB
{
namespace Mongo
{
bool MongoIdentityFunction::parseImpl(ASTPtr & node)
{
if (data.IsInt())
{
auto identifier = std::make_shared<ASTIdentifier>(edge_name);
auto literal = std::make_shared<ASTLiteral>(Field(data.GetInt()));
auto where_condition = makeASTFunction("equals", identifier, literal);
node = where_condition;
return true;
}
if (data.IsString())
{
auto identifier = std::make_shared<ASTIdentifier>(edge_name);
auto literal = std::make_shared<ASTLiteral>(Field(data.GetString()));
auto where_condition = makeASTFunction("equals", identifier, literal);
node = where_condition;
return true;
}
if (data.IsObject())
{
auto parser = createInversedParser(std::move(data), metadata, edge_name);
if (!parser->parseImpl(node))
{
return false;
}
return true;
}
return false;
}
bool MongoLiteralFunction::parseImpl(ASTPtr & node)
{
if (data.IsString())
{
auto literal = std::make_shared<ASTIdentifier>(data.GetString());
node = literal;
return true;
}
if (data.IsObject())
{
if (data.Size() != 1)
{
return false;
}
auto it = data.MemberBegin();
const char * name = it->name.GetString();
auto parser = createParser(copyValue(it->value), metadata, name);
ASTPtr child_node;
if (!parser->parseImpl(child_node))
{
return false;
}
node = child_node;
return true;
}
return false;
}
bool MongoOrFunction::parseImpl(ASTPtr & node)
{
if (!data.IsArray())
{
return false;
}
std::vector<ASTPtr> child_trees;
for (unsigned int i = 0; i < data.Size(); ++i)
{
auto parser = createParser(copyValue(data[i]), metadata, "");
ASTPtr child_node;
if (!parser->parseImpl(child_node))
{
return false;
}
child_trees.push_back(child_node);
}
if (child_trees.empty())
{
return false;
}
if (child_trees.size() == 1)
{
node = child_trees[0];
return true;
}
auto result = makeASTFunction("or");
for (const auto & elem : child_trees)
{
result->arguments->children.push_back(elem);
}
node = result;
return true;
}
bool IMongoArithmeticFunction::parseImpl(ASTPtr & node)
{
if (data.Size() < 2)
{
return false;
}
std::vector<ASTPtr> children;
for (unsigned int i = 0; i < data.Size(); ++i)
{
auto parser = createParser(copyValue(data[i]), metadata, "$arithmetic_function_element");
ASTPtr child_node;
if (!parser->parseImpl(child_node))
{
return false;
}
children.push_back(std::move(child_node));
}
/// Wrap function as tree of binary operators like
///
/// +
/// / \
/// c0 +
/// / \
/// c1 c2
///
auto function = makeASTFunction(getFunctionAlias(), children[0], children[1]);
for (size_t i = 2; i < children.size(); ++i)
{
function = makeASTFunction(getFunctionAlias(), function, children[i]);
}
node = function;
return true;
}
bool MongoArithmeticFunctionElement::parseImpl(ASTPtr & node)
{
if (data.IsInt())
{
auto literal = std::make_shared<ASTLiteral>(Field(data.GetInt()));
node = literal;
return true;
}
if (data.IsString())
{
auto identifier = std::make_shared<ASTIdentifier>(data.GetString());
node = identifier;
return true;
}
if (data.IsObject())
{
auto parser = createParser(std::move(data), metadata, "");
if (!parser->parseImpl(node))
{
return false;
}
return true;
}
return false;
}
}
}

View File

@ -0,0 +1,155 @@
#pragma once
#include <string>
#include <Parsers/Mongo/ParserMongoSelectQuery.h>
namespace DB
{
namespace Mongo
{
class IMongoFunction : public IMongoParser
{
protected:
explicit IMongoFunction(rapidjson::Value data_, std::shared_ptr<QueryMetadata> metadata_, const std::string & edge_name_)
: IMongoParser(std::move(data_), metadata_, edge_name_)
{
}
public:
virtual std::string getFunctionName() const = 0;
~IMongoFunction() override = default;
};
class MongoIdentityFunction : public IMongoFunction
{
public:
std::string getFunctionName() const override { return edge_name; }
explicit MongoIdentityFunction(
rapidjson::Value array_elements_, std::shared_ptr<QueryMetadata> metadata_, const std::string & edge_name_)
: IMongoFunction(std::move(array_elements_), metadata_, edge_name_)
{
}
bool parseImpl(ASTPtr & node) override;
};
class MongoLiteralFunction : public IMongoFunction
{
public:
std::string getFunctionName() const override { return edge_name; }
explicit MongoLiteralFunction(
rapidjson::Value array_elements_, std::shared_ptr<QueryMetadata> metadata_, const std::string & edge_name_)
: IMongoFunction(std::move(array_elements_), metadata_, edge_name_)
{
}
bool parseImpl(ASTPtr & node) override;
};
class MongoOrFunction : public IMongoFunction
{
public:
std::string getFunctionName() const override { return "$or"; }
explicit MongoOrFunction(rapidjson::Value array_elements_, std::shared_ptr<QueryMetadata> metadata_, const std::string & edge_name_)
: IMongoFunction(std::move(array_elements_), metadata_, edge_name_)
{
}
bool parseImpl(ASTPtr & node) override;
};
/// Base class for arithmetic functions like add, multiplication and others.
class IMongoArithmeticFunction : public IMongoFunction
{
public:
explicit IMongoArithmeticFunction(
rapidjson::Value array_elements_, std::shared_ptr<QueryMetadata> metadata_, const std::string & edge_name_)
: IMongoFunction(std::move(array_elements_), metadata_, edge_name_)
{
}
virtual std::string getFunctionAlias() const = 0;
bool parseImpl(ASTPtr & node) override;
};
class MongoSumFunction : public IMongoArithmeticFunction
{
public:
std::string getFunctionName() const override { return "$add"; }
std::string getFunctionAlias() const override { return "plus"; }
explicit MongoSumFunction(rapidjson::Value array_elements_, std::shared_ptr<QueryMetadata> metadata_, const std::string & edge_name_)
: IMongoArithmeticFunction(std::move(array_elements_), metadata_, edge_name_)
{
}
};
class MongoMultiplyFunction : public IMongoArithmeticFunction
{
public:
std::string getFunctionName() const override { return "$mul"; }
std::string getFunctionAlias() const override { return "multiply"; }
explicit MongoMultiplyFunction(
rapidjson::Value array_elements_, std::shared_ptr<QueryMetadata> metadata_, const std::string & edge_name_)
: IMongoArithmeticFunction(std::move(array_elements_), metadata_, edge_name_)
{
}
};
class MongoDivideFunction : public IMongoArithmeticFunction
{
public:
std::string getFunctionName() const override { return "$div"; }
std::string getFunctionAlias() const override { return "divide"; }
explicit MongoDivideFunction(rapidjson::Value array_elements_, std::shared_ptr<QueryMetadata> metadata_, const std::string & edge_name_)
: IMongoArithmeticFunction(std::move(array_elements_), metadata_, edge_name_)
{
}
};
class MongoMinusFunction : public IMongoArithmeticFunction
{
public:
std::string getFunctionName() const override { return "$sub"; }
std::string getFunctionAlias() const override { return "minus"; }
explicit MongoMinusFunction(rapidjson::Value array_elements_, std::shared_ptr<QueryMetadata> metadata_, const std::string & edge_name_)
: IMongoArithmeticFunction(std::move(array_elements_), metadata_, edge_name_)
{
}
};
class MongoArithmeticFunctionElement : public IMongoFunction
{
public:
std::string getFunctionName() const override { return "$arithmetic_function_element"; }
explicit MongoArithmeticFunctionElement(
rapidjson::Value array_elements_, std::shared_ptr<QueryMetadata> metadata_, const std::string & edge_name_)
: IMongoFunction(std::move(array_elements_), metadata_, edge_name_)
{
}
bool parseImpl(ASTPtr & node) override;
};
}
}

View File

@ -0,0 +1,31 @@
#include "ParserMongoInsertQuery.h"
#include <memory>
#include <Parsers/ParserQuery.h>
#include <Parsers/Mongo/Utils.h>
namespace DB
{
namespace Mongo
{
/// TODO (scanhex12): add insert queries in mongo
bool ParserMongoInsertManyQuery::parseImpl(ASTPtr & node)
{
node = nullptr;
return false;
}
/// TODO (scanhex12): add insert queries in mongo
bool ParserMongoInsertOneQuery::parseImpl(ASTPtr & node)
{
node = nullptr;
return false;
}
}
}

View File

@ -0,0 +1,39 @@
#pragma once
#include <Parsers/Mongo/ParserMongoQuery.h>
namespace DB
{
namespace Mongo
{
class ParserMongoInsertManyQuery : public IMongoParser
{
public:
explicit ParserMongoInsertManyQuery(rapidjson::Value data_, std::shared_ptr<QueryMetadata> metadata_)
: IMongoParser(std::move(data_), metadata_, "")
{
}
bool parseImpl(ASTPtr & node) override;
~ParserMongoInsertManyQuery() override = default;
};
class ParserMongoInsertOneQuery : public IMongoParser
{
public:
explicit ParserMongoInsertOneQuery(rapidjson::Value data_, std::shared_ptr<QueryMetadata> metadata_)
: IMongoParser(std::move(data_), metadata_, "")
{
}
bool parseImpl(ASTPtr & node) override;
~ParserMongoInsertOneQuery() override = default;
};
}
}

View File

@ -0,0 +1,41 @@
#include "Parsers/Mongo/ParserMongoOrderBy.h"
#include <memory>
#include <Parsers/ASTQueryParameter.h>
#include <Parsers/ASTExpressionList.h>
#include <Parsers/ASTIdentifier.h>
#include <Parsers/ASTOrderByElement.h>
#include <Parsers/IAST_fwd.h>
#include <Parsers/Mongo/Utils.h>
namespace DB
{
namespace Mongo
{
bool ParserMongoOrderBy::parseImpl(ASTPtr & node)
{
if (!data.IsObject())
{
return false;
}
auto result = std::make_shared<ASTExpressionList>();
for (auto it = data.MemberBegin(); it != data.MemberEnd(); ++it)
{
std::shared_ptr<ASTOrderByElement> element = std::make_shared<ASTOrderByElement>();
element->direction = it->value.GetInt();
element->children.push_back(std::make_shared<ASTIdentifier>(it->name.GetString()));
result->children.push_back(element);
}
node = result;
return true;
}
}
}

View File

@ -0,0 +1,31 @@
#pragma once
#include <rapidjson/document.h>
#include <Parsers/IParserBase.h>
#include <Parsers/Mongo/Metadata.h>
#include <Parsers/Mongo/ParserMongoQuery.h>
namespace DB
{
namespace Mongo
{
class ParserMongoOrderBy : public IMongoParser
{
public:
explicit ParserMongoOrderBy(rapidjson::Value data_, std::shared_ptr<QueryMetadata> metadata_, const std::string & edge_name_)
: IMongoParser(std::move(data_), metadata_, edge_name_)
{
}
bool parseImpl(ASTPtr & node) override;
~ParserMongoOrderBy() override = default;
};
}
}

View File

@ -0,0 +1,54 @@
#include "Parsers/Mongo/ParserMongoProjection.h"
#include <memory>
#include <Parsers/ASTQueryParameter.h>
#include <Parsers/ASTExpressionList.h>
#include <Parsers/ASTIdentifier.h>
#include <Parsers/IAST_fwd.h>
#include <Parsers/Mongo/ParserMongoQuery.h>
#include <Parsers/Mongo/Utils.h>
namespace DB
{
namespace Mongo
{
bool ParserMongoProjection::parseImpl(ASTPtr & node)
{
if (!data.IsObject())
{
return false;
}
auto result = std::make_shared<ASTExpressionList>();
for (auto it = data.MemberBegin(); it != data.MemberEnd(); ++it)
{
if (it->value.IsInt())
{
auto include_field = it->value.GetInt();
if (include_field)
{
result->children.push_back(std::make_shared<ASTIdentifier>(it->name.GetString()));
}
continue;
}
ASTPtr child_node;
auto parser = createParser(copyValue(it->value), metadata, it->name.GetString(), true);
if (!parser->parseImpl(child_node))
{
return false;
}
child_node->setAlias(it->name.GetString());
result->children.push_back(child_node);
}
node = result;
return true;
}
}
}

View File

@ -0,0 +1,31 @@
#pragma once
#include <rapidjson/document.h>
#include <Parsers/IParserBase.h>
#include <Parsers/Mongo/Metadata.h>
#include <Parsers/Mongo/ParserMongoQuery.h>
namespace DB
{
namespace Mongo
{
class ParserMongoProjection : public IMongoParser
{
public:
explicit ParserMongoProjection(rapidjson::Value data_, std::shared_ptr<QueryMetadata> metadata_, const std::string & edge_name_)
: IMongoParser(std::move(data_), metadata_, edge_name_)
{
}
bool parseImpl(ASTPtr & node) override;
~ParserMongoProjection() override = default;
};
}
}

View File

@ -0,0 +1,127 @@
#include "ParserMongoQuery.h"
#include <memory>
#include <Core/Settings.h>
#include <Interpreters/Context.h>
#include <Common/CurrentThread.h>
#include <Parsers/ASTLiteral.h>
#include <Parsers/ASTSelectQuery.h>
#include <Parsers/ASTTablesInSelectQuery.h>
#include <Parsers/IParserBase.h>
#include <Parsers/ASTAsterisk.h>
#include <Parsers/ASTExpressionList.h>
#include <Parsers/ASTFunction.h>
#include <Parsers/ASTIdentifier.h>
#include <Parsers/ASTSelectWithUnionQuery.h>
#include <Parsers/ASTSubquery.h>
#include <Parsers/ASTWithElement.h>
#include <Parsers/Mongo/ParserMongoFilter.h>
#include <Parsers/Mongo/ParserMongoFunction.h>
#include <Parsers/Mongo/ParserMongoSelectQuery.h>
#include <Parsers/Mongo/Metadata.h>
#include <Parsers/Mongo/ParserMongoInsertQuery.h>
#include <Parsers/Mongo/Utils.h>
#include <Parsers/Mongo/ParserMongoCompareFunctions.h>
namespace DB
{
namespace Mongo
{
bool ParserMongoQuery::parseImpl(Pos & /*pos*/, ASTPtr & node, Expected & /*expected*/)
{
switch (metadata->getQueryType())
{
case QueryMetadata::QueryType::select:
{
return ParserMongoSelectQuery(std::move(data), metadata).parseImpl(node);
}
case QueryMetadata::QueryType::insert_many:
{
return ParserMongoInsertManyQuery(std::move(data), metadata).parseImpl(node);
}
case QueryMetadata::QueryType::insert_one:
{
return ParserMongoInsertOneQuery(std::move(data), metadata).parseImpl(node);
}
}
}
std::shared_ptr<IMongoParser>
createParser(rapidjson::Value data_, std::shared_ptr<QueryMetadata> metadata_, const std::string & edge_name_, bool literal_as_default)
{
if (edge_name_ == "$or")
{
return std::make_shared<MongoOrFunction>(std::move(data_), metadata_, edge_name_);
}
if (edge_name_ == "$add")
{
return std::make_shared<MongoSumFunction>(std::move(data_), metadata_, edge_name_);
}
if (edge_name_ == "$mul")
{
return std::make_shared<MongoMultiplyFunction>(std::move(data_), metadata_, edge_name_);
}
if (edge_name_ == "$div")
{
return std::make_shared<MongoDivideFunction>(std::move(data_), metadata_, edge_name_);
}
if (edge_name_ == "$sub")
{
return std::make_shared<MongoMinusFunction>(std::move(data_), metadata_, edge_name_);
}
if (edge_name_ == "")
{
return std::make_shared<ParserMongoFilter>(std::move(data_), metadata_, edge_name_);
}
if (edge_name_ == "$arithmetic_function_element")
{
return std::make_shared<MongoArithmeticFunctionElement>(std::move(data_), metadata_, edge_name_);
}
if (!literal_as_default)
{
return std::make_shared<MongoIdentityFunction>(std::move(data_), metadata_, edge_name_);
}
else
{
return std::make_shared<MongoLiteralFunction>(std::move(data_), metadata_, edge_name_);
}
}
std::shared_ptr<IMongoParser>
createInversedParser(rapidjson::Value data_, std::shared_ptr<QueryMetadata> metadata_, const std::string & edge_name_)
{
if (static_cast<std::string>(data_.MemberBegin()->name.GetString()) == "$lt")
{
return std::make_shared<MongoLtFunction>(copyValue(data_.MemberBegin()->value), metadata_, edge_name_);
}
if (static_cast<std::string>(data_.MemberBegin()->name.GetString()) == "$lte")
{
return std::make_shared<MongoLteFunction>(copyValue(data_.MemberBegin()->value), metadata_, edge_name_);
}
if (static_cast<std::string>(data_.MemberBegin()->name.GetString()) == "$gt")
{
return std::make_shared<MongoGtFunction>(copyValue(data_.MemberBegin()->value), metadata_, edge_name_);
}
if (static_cast<std::string>(data_.MemberBegin()->name.GetString()) == "$gte")
{
return std::make_shared<MongoGteFunction>(copyValue(data_.MemberBegin()->value), metadata_, edge_name_);
}
if (static_cast<std::string>(data_.MemberBegin()->name.GetString()) == "$ne")
{
return std::make_shared<MongoNotEqualsFunction>(copyValue(data_.MemberBegin()->value), metadata_, edge_name_);
}
if (static_cast<std::string>(data_.MemberBegin()->name.GetString()) == "$regex")
{
return std::make_shared<MongoLikeFunction>(copyValue(data_.MemberBegin()->value), metadata_, edge_name_);
}
return nullptr;
}
}
}

View File

@ -0,0 +1,103 @@
#pragma once
#include <rapidjson/document.h>
#include <Parsers/IParserBase.h>
#include <Parsers/Mongo/Metadata.h>
namespace DB
{
namespace Mongo
{
/// Base class for Mongo parsers.
/// Contains data, that should be parsed in parseImpl method,
/// metadata about query (for example collection),
/// edge name for parent edge.
///
/// Example:
///
/// { quantity: { $lt: 20 } }
/// ^
/// |
/// current vertex
///
/// For this vertex edge_name = "quantity" and data = { [$lt]: 20 }
class IMongoParser
{
protected:
rapidjson::Value data;
std::shared_ptr<QueryMetadata> metadata;
std::string edge_name;
public:
explicit IMongoParser(rapidjson::Value data_, std::shared_ptr<QueryMetadata> metadata_, const std::string & edge_name_)
: data(std::move(data_)), metadata(metadata_), edge_name(edge_name_)
{
}
/// Returns false if data is incorrect and cannot be parsed into node.
virtual bool parseImpl(ASTPtr & node) = 0;
virtual ~IMongoParser() = default;
};
/// Creates a parser based on edge name and data.
std::shared_ptr<IMongoParser> createParser(
rapidjson::Value data_, std::shared_ptr<QueryMetadata> metadata_, const std::string & edge_name_, bool literal_as_default = false);
/// Creates a inversed parsed based on edge name and data.
/// Inversed means that mongo syntax have inversed order of elements in tree.
/// Example : $lt operator. In mongo syntax it should have structure like { quantity: { $lt: 20 } }
/// So tree for this json looks like
///
/// quantity
/// / \
/// (operator <) 20
///
/// But in AST representation looks like
///
/// (operator <)
/// / \
/// quantity 20
///
/// So some operators needs in special parsers
std::shared_ptr<IMongoParser>
createInversedParser(rapidjson::Value data_, std::shared_ptr<QueryMetadata> metadata_, const std::string & edge_name_);
/// Class to connect mongo and clickhouse parsers
class ParserMongoQuery : public IParserBase
{
private:
// These fields are not used when Mongo is disabled at build time.
[[maybe_unused]] size_t max_query_size;
[[maybe_unused]] size_t max_parser_depth;
[[maybe_unused]] size_t max_parser_backtracks;
rapidjson::Value data;
std::shared_ptr<QueryMetadata> metadata;
public:
explicit ParserMongoQuery(size_t max_query_size_, size_t max_parser_depth_, size_t max_parser_backtracks_)
: max_query_size(max_query_size_), max_parser_depth(max_parser_depth_), max_parser_backtracks(max_parser_backtracks_)
{
}
void setParsingData(rapidjson::Value data_, std::shared_ptr<QueryMetadata> metadata_)
{
data = data_;
metadata = metadata_;
}
protected:
const char * getName() const override { return "Mongo query"; }
bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override;
};
}
}

View File

@ -0,0 +1,104 @@
#include "ParserMongoSelectQuery.h"
#include <rapidjson/document.h>
#include <Parsers/ASTLiteral.h>
#include <Parsers/ASTSelectQuery.h>
#include <Parsers/ASTTablesInSelectQuery.h>
#include <Parsers/ASTAsterisk.h>
#include <Parsers/ASTExpressionList.h>
#include <Parsers/ASTFunction.h>
#include <Parsers/ASTIdentifier.h>
#include <Parsers/ASTSelectWithUnionQuery.h>
#include <Parsers/ASTSubquery.h>
#include <Parsers/ASTWithElement.h>
#include <Parsers/IParserBase.h>
#include <Parsers/IAST_fwd.h>
#include <Parsers/Mongo/ParserMongoFilter.h>
#include <Parsers/Mongo/ParserMongoOrderBy.h>
#include <Parsers/Mongo/ParserMongoProjection.h>
#include <Parsers/Mongo/Utils.h>
namespace DB
{
namespace Mongo
{
bool ParserMongoSelectQuery::parseImpl(ASTPtr & node)
{
auto select_query = std::make_shared<ASTSelectQuery>();
auto projection = findField(data, "$projection");
/// Equals to SELECT * ...
if (!projection)
{
select_query->setExpression(ASTSelectQuery::Expression::SELECT, std::make_shared<ASTExpressionList>());
select_query->select()->children.push_back(std::make_shared<ASTAsterisk>());
}
else
{
/// Otherwise traverse projection tree to parse projection
data.EraseMember("$projection");
ASTPtr projection_node;
auto parser = ParserMongoProjection(std::move(*projection), metadata, "$projection");
if (!parser.parseImpl(projection_node))
{
return false;
}
select_query->setExpression(ASTSelectQuery::Expression::SELECT, std::move(projection_node));
}
/// Attach collection to AST.
node = select_query;
ASTPtr tables = std::make_shared<ASTTablesInSelectQuery>();
auto table_expression_ast = std::make_shared<ASTTableExpression>();
table_expression_ast->children.push_back(std::make_shared<ASTTableIdentifier>(metadata->getCollectionName()));
table_expression_ast->database_and_table_name = table_expression_ast->children.back();
auto tables_in_select_query_element_ast = std::make_shared<ASTTablesInSelectQueryElement>();
tables_in_select_query_element_ast->children.push_back(std::move(table_expression_ast));
tables_in_select_query_element_ast->table_expression = tables_in_select_query_element_ast->children.back();
tables->children.push_back(std::move(tables_in_select_query_element_ast));
select_query->setExpression(ASTSelectQuery::Expression::TABLES, std::move(tables));
if (metadata->getLimit())
{
size_t limit_value = *metadata->getLimit();
auto literal = std::make_shared<ASTLiteral>(Field(limit_value));
select_query->setExpression(ASTSelectQuery::Expression::LIMIT_LENGTH, std::move(literal));
}
if (metadata->getOrderBy())
{
ASTPtr order_by_node;
auto order_by = *metadata->getOrderBy();
auto order_by_tree = parseData(order_by.data(), order_by.data() + order_by.size());
if (!ParserMongoOrderBy(std::move(order_by_tree), metadata, "").parseImpl(order_by_node))
{
return false;
}
select_query->setExpression(ASTSelectQuery::Expression::ORDER_BY, std::move(order_by_node));
}
/// Traverse data tree for WHERE operator
ASTPtr where_condition;
if (ParserMongoFilter(std::move(data), metadata, "").parseImpl(where_condition))
{
select_query->setExpression(ASTSelectQuery::Expression::WHERE, std::move(where_condition));
return true;
}
else
{
return false;
}
}
}
}

View File

@ -0,0 +1,31 @@
#pragma once
#include <memory>
#include <Parsers/IParserBase.h>
#include <Parsers/Mongo/Metadata.h>
#include <Parsers/Mongo/ParserMongoQuery.h>
namespace DB
{
namespace Mongo
{
class ParserMongoSelectQuery : public IMongoParser
{
public:
explicit ParserMongoSelectQuery(rapidjson::Value data_, std::shared_ptr<QueryMetadata> metadata_)
: IMongoParser(std::move(data_), metadata_, "")
{
}
bool parseImpl(ASTPtr & node) override;
~ParserMongoSelectQuery() override = default;
};
}
}

144
src/Parsers/Mongo/Utils.cpp Normal file
View File

@ -0,0 +1,144 @@
#include "Utils.h"
#include <optional>
#include <string>
namespace DB
{
namespace ErrorCodes
{
extern const int BAD_ARGUMENTS;
}
namespace Mongo
{
std::pair<const char *, const char *> getMetadataSubstring(const char * begin, const char * end)
{
const char * position = findKth<'('>(begin, end, 1);
if (position == end)
{
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Invalid query: can not parse metadata in query");
}
return {begin, position};
}
std::pair<const char *, const char *> getSettingsSubstring(const char * begin, const char * end)
{
const char * position_start = findKth<'('>(begin, end, 1);
if (position_start == end)
{
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Invalid query: can not find settings in query");
}
const char * position_end = findKth<')'>(begin, end, 1);
if (position_end == end)
{
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Invalid query: can not find settings in your query ");
}
return {position_start + 1, position_end};
}
void validateFirstMetadataArgument(const char * begin, const char * end)
{
size_t size = end - begin;
if (size < 2 || *(end - 1) != 'b' || *(end - 2) != 'd')
{
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Invalid query: first argument of query should be 'db'");
}
}
std::optional<rapidjson::Value> findField(const rapidjson::Value & value, const std::string & key)
{
for (auto it = value.MemberBegin(); it != value.MemberEnd(); ++it)
{
if (it->name.GetString() == key)
{
return copyValue(it->value);
}
}
return std::nullopt;
}
rapidjson::Value parseData(const char * begin, const char * end)
{
std::string input(begin, end);
std::replace(input.begin(), input.end(), '\'', '"');
rapidjson::Document document;
if (document.Parse(input.data()).HasParseError())
{
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Error while parsing json in parseData {}", input);
}
return copyValue(document);
}
std::optional<size_t> MongoQueryKeyNameExtractor::findPosition(const char * begin, const char * end)
{
size_t size_str = end - begin;
for (size_t i = 0; i < size_str - pattern.size() + 1; ++i)
{
bool match = true;
for (size_t j = 0; j < pattern.size(); ++j)
{
if (begin[i + j] != pattern[j])
{
match = false;
break;
}
}
if (match)
{
if (begin[i + pattern.size()] != '(')
{
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Incorrect query : after {} should be (", pattern);
}
return i + pattern.size() + 1;
}
}
return std::nullopt;
}
std::optional<int> MongoQueryKeyNameExtractor::extractInt(const char * begin, const char * end)
{
auto maybe_start_position = findPosition(begin, end);
if (!maybe_start_position)
{
return std::nullopt;
}
auto start_position = *maybe_start_position;
std::string str_representation;
while (begin[start_position] != ')')
{
if (begin[start_position] < '0' || begin[start_position] > '9')
{
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Incorrect query : pattern {} should contain only numbers", pattern);
}
str_representation.push_back(begin[start_position]);
++start_position;
}
return std::stoi(str_representation);
}
std::optional<std::string> MongoQueryKeyNameExtractor::extractString(const char * begin, const char * end)
{
auto maybe_start_position = findPosition(begin, end);
if (!maybe_start_position)
{
return std::nullopt;
}
auto start_position = *maybe_start_position;
std::string result;
while (begin[start_position] != ')')
{
result.push_back(begin[start_position]);
++start_position;
}
return result;
}
}
}

81
src/Parsers/Mongo/Utils.h Normal file
View File

@ -0,0 +1,81 @@
#pragma once
#include <optional>
#include <rapidjson/document.h>
#include <Common/Exception.h>
namespace DB
{
namespace ErrorCodes
{
extern const int BAD_ARGUMENTS;
}
namespace Mongo
{
template <char token>
const char * findKth(const char * begin, const char * end, size_t k)
{
const char * iter = begin;
for (size_t i = 0; i < k; ++i)
{
if (i != 0 && iter != end)
{
iter++;
}
while (iter < end && iter[0] != token)
{
iter++;
}
if (iter == end)
{
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Invalid query: there is less than {} tokens {}", k, token);
}
}
return iter;
}
std::pair<const char *, const char *> getMetadataSubstring(const char * begin, const char * end);
std::pair<const char *, const char *> getSettingsSubstring(const char * begin, const char * end);
void validateFirstMetadataArgument(const char * begin, const char * end);
template <typename T>
rapidjson::Value copyValue(const T & value)
{
static rapidjson::Document::AllocatorType allocator;
rapidjson::Value result;
result.CopyFrom(value, allocator);
return result;
}
std::optional<rapidjson::Value> findField(const rapidjson::Value & value, const std::string & key);
rapidjson::Value parseData(const char * begin, const char * end);
class MongoQueryKeyNameExtractor
{
public:
explicit MongoQueryKeyNameExtractor(const std::string & pattern_) : pattern(pattern_) { }
std::optional<int> extractInt(const char * begin, const char * end);
std::optional<std::string> extractString(const char * begin, const char * end);
private:
std::optional<size_t> findPosition(const char * begin, const char * end);
std::string pattern;
};
}
}

View File

@ -0,0 +1,116 @@
#include "parseMongoQuery.h"
#include <Parsers/ASTInsertQuery.h>
#include <Parsers/IParser.h>
#include <Parsers/Mongo/ParserMongoQuery.h>
#include <Parsers/Mongo/Utils.h>
namespace DB
{
namespace ErrorCodes
{
extern const int SYNTAX_ERROR;
}
namespace Mongo
{
ASTPtr tryParseMongoQuery(
IParser & parser,
const char *& _out_query_end, // query start as input parameter, query end as output
const char *& end,
std::string & /*out_error_message*/,
bool /*hilite*/,
const std::string & /*description*/,
bool /*allow_multi_statements*/,
size_t max_query_size,
size_t max_parser_depth,
size_t max_parser_backtracks,
bool /*skip_insignificant*/)
{
Expected expected;
ASTPtr res;
Tokens token_subquery(_out_query_end, end, max_query_size, true);
IParser::Pos token_iterator(token_subquery, static_cast<uint32_t>(max_parser_depth), static_cast<uint32_t>(max_parser_backtracks));
std::shared_ptr<QueryMetadata> metadata;
auto [data_begin, data_end] = getSettingsSubstring(_out_query_end, end);
metadata = extractMetadataFromRequest(_out_query_end, end);
dynamic_cast<ParserMongoQuery &>(parser).setParsingData(parseData(data_begin, data_end), metadata);
_out_query_end = findKth<';'>(_out_query_end, end, 1) + 1;
const bool parse_res = parser.parse(token_iterator, res, expected);
if (!parse_res)
{
return nullptr;
}
return res;
}
/// Parse query or throw an exception with error message.
ASTPtr parseMongoQueryAndMovePosition(
IParser & parser,
const char *& pos, /// Moved to end of parsed fragment.
const char * end,
const std::string & description,
bool allow_multi_statements,
size_t max_query_size,
size_t max_parser_depth,
size_t max_parser_backtracks)
{
std::string error_message;
ASTPtr res = tryParseMongoQuery(
parser,
pos,
end,
error_message,
false,
description,
allow_multi_statements,
max_query_size,
max_parser_depth,
max_parser_backtracks);
if (res)
return res;
throw Exception::createDeprecated(error_message, ErrorCodes::SYNTAX_ERROR);
}
ASTPtr parseMongoQuery(
IParser & parser,
const char * begin,
const char * end,
const std::string & /*description*/,
size_t max_query_size,
size_t max_parser_depth,
size_t max_parser_backtracks)
{
if (begin == end)
{
return nullptr;
}
Expected expected;
ASTPtr res;
Tokens token_subquery(begin, end, max_query_size, true);
auto metadata = extractMetadataFromRequest(begin, end);
metadata->add_data_to_query = false;
auto [data_begin, data_end] = getSettingsSubstring(begin, end);
dynamic_cast<ParserMongoQuery &>(parser).setParsingData(parseData(data_begin, data_end), metadata);
IParser::Pos token_iterator(token_subquery, static_cast<uint32_t>(max_parser_depth), static_cast<uint32_t>(max_parser_backtracks));
const bool parse_res = parser.parse(token_iterator, res, expected);
if (!parse_res)
{
return nullptr;
}
return res;
}
}
}

View File

@ -0,0 +1,51 @@
#pragma once
#include <IO/WriteBufferFromString.h>
#include <Parsers/IAST_fwd.h>
#include <Parsers/parseQuery.h>
namespace DB
{
namespace Mongo
{
ASTPtr tryParseMongoQuery(
IParser & parser,
const char *& _out_query_end, // query start as input parameter, query end as output
const char *& end,
std::string & out_error_message,
bool hilite,
const std::string & description,
bool allow_multi_statements,
size_t max_query_size,
size_t max_parser_depth,
size_t max_parser_backtracks,
bool skip_insignificant = true);
/// Parse query or throw an exception with error message.
ASTPtr parseMongoQueryAndMovePosition(
IParser & parser,
const char *& pos, /// Moved to end of parsed fragment.
const char * end,
const std::string & description,
bool allow_multi_statements,
size_t max_query_size,
size_t max_parser_depth,
size_t max_parser_backtracks);
ASTPtr parseMongoQuery(
IParser & parser,
const char * begin,
const char * end,
const std::string & description,
size_t max_query_size,
size_t max_parser_depth,
size_t max_parser_backtracks);
}
}

View File

@ -0,0 +1,112 @@
1 2 ac
8 6 gh
2 9 ij
3 4 kl
5 7 mn
6 1 qr
10 12 st
11 14 uv
13 15 wx
14 13 ab
16 11 yz
7 3 efaaaa
9 8 op
4 5 b
1 2 ac
2 9 ij
1 2 ac
1 2 ac
3 4 kl
1 2 ac
1 2 ac
1 2 ac
1 2 ac
1 2 ac
14 13 ab
1 2 ac
7 3 efaaaa
1 2 ac
1 2 ac
2 9 ij
3 4 kl
4 5 b
5 7 mn
6 1 qr
7 3 efaaaa
8 6 gh
9 8 op
10 12 st
11 14 uv
13 15 wx
14 13 ab
16 11 yz
7
1
8
2
3
4
5
9
10
11
13
16
14
6
7 10
1 3
2 11
8 14
4 9
5 12
3 7
9 17
6 7
10 22
11 25
14 27
16 27
13 28
1 2
8 48
5 35
2 18
10 120
3 12
6 6
9 72
13 195
11 154
4 20
16 176
14 182
7 21
1 0.5
7 2.3333333333333335
8 1.3333333333333333
2 0.2222222222222222
3 0.75
5 0.7142857142857143
9 1.125
11 0.7857142857142857
6 6
10 0.8333333333333334
13 0.8666666666666667
14 1.0769230769230769
4 0.8
16 1.4545454545454546
7 28
1 3
8 56
4 24
5 40
9 81
16 192
3 15
13 208
11 165
10 130
2 20
14 196
6 12

View File

@ -0,0 +1,44 @@
SET dialect='clickhouse';
DROP TABLE IF EXISTS test;
CREATE TABLE test
(
c0 Int32,
c1 Int32,
c2 String,
)
ENGINE = Memory;
INSERT INTO test(c0, c1, c2) VALUES (1, 2, 'ac');
INSERT INTO test(c0, c1, c2) VALUES (4, 5, 'b');
INSERT INTO test(c0, c1, c2) VALUES (7, 3, 'efaaaa');
INSERT INTO test(c0, c1, c2) VALUES (8, 6, 'gh');
INSERT INTO test(c0, c1, c2) VALUES (2, 9, 'ij');
INSERT INTO test(c0, c1, c2) VALUES (3, 4, 'kl');
INSERT INTO test(c0, c1, c2) VALUES (5, 7, 'mn');
INSERT INTO test(c0, c1, c2) VALUES (9, 8, 'op');
INSERT INTO test(c0, c1, c2) VALUES (6, 1, 'qr');
INSERT INTO test(c0, c1, c2) VALUES (10, 12, 'st');
INSERT INTO test(c0, c1, c2) VALUES (11, 14, 'uv');
INSERT INTO test(c0, c1, c2) VALUES (13, 15, 'wx');
INSERT INTO test(c0, c1, c2) VALUES (16, 11, 'yz');
INSERT INTO test(c0, c1, c2) VALUES (14, 13, 'ab');
SET dialect='mongo';
db.test.find({});
db.test.find({"c0" : 1});
db.test.find({"c0" : 2});
db.test.find({"c0" : 1, "c1" : 2});
db.test.find({"$or" : [{"c0" : 1}, {"c0" : 3}]});
db.test.find({"c0" : 1, "c1" : {"$lt" : 3}});
db.test.find({"c0" : 1, "c1" : {"$lte" : 3}});
db.test.find({"c0" : 1, "c1" : {"$gt" : 1}});
db.test.find({"c0" : 1, "c1" : {"$gte" : 2}});
db.test.find({"c0" : 1, "c1" : {"$ne" : 0}});
db.test.find({"c2" : {"$regex" : "%a%"}});
db.test.find({}).limit(1);
db.test.find({}).sort({"c0" : 1});
db.test.find({"$projection" : {"b0" : "c0"}});
db.test.find({"$projection" : {"b0" : "c0", "b1" : {"$add" : ["c0", "c1"]}}});
db.test.find({"$projection" : {"b0" : "c0", "b1" : {"$mul" : ["c0", "c1"]}}});
db.test.find({"$projection" : {"b0" : "c0", "b1" : {"$div" : ["c0", "c1"]}}});
db.test.find({"$projection" : {"b0" : "c0", "b1" : {"$add" : ["c0", {"$mul" : ["c0", "c1"]}]}}});