Skip to content

Commit

Permalink
fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
nooblose committed May 25, 2024
1 parent 7a75b8c commit afa684a
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 105 deletions.
40 changes: 22 additions & 18 deletions src/Server/HTTP/HTTPQueryAST.cpp → src/Server/HTTP/HTTPQuery.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#include "HTTPQueryAST.h"
#include "HTTPQuery.h"

#include <format>

#include <Parsers/CommonParsers.h>
#include <Parsers/ExpressionListParsers.h>
Expand All @@ -17,6 +19,17 @@ static constexpr auto kSelect = "select";
static constexpr auto kWhere = "where";
static constexpr auto kOrder = "order";

static const NameSet reserved_param_names{"compress", "decompress",
"user", "password",
"quota_key", "query_id",
"stacktrace", "role",
"buffer_size", "wait_end_of_query",
"session_id", "session_timeout",
"session_check", "client_protocol_version",
"close_session", "execute",
"where", "columns",
"select", "order"};

template <typename T>
ASTPtr parseExpression(const std::string & expression, const std::optional<ParserKeyword> & keyword = std::nullopt)
{
Expand All @@ -34,21 +47,6 @@ ASTPtr parseExpression(const std::string & expression, const std::optional<Parse
return ast;
}

ASTPtr parseSelect(const std::string & select)
{
ParserKeyword s_select(Keyword::SELECT);

ASTPtr result;
Tokens tokens(select.c_str(), select.c_str() + select.size());
IParser::Pos pos(tokens, 0, 0);
Expected expected;

s_select.ignore(pos, expected);

ParserNotEmptyExpressionList(false).parse(pos, result, expected);
return result;
}

ASTPtr parseColumns(const std::string & columns)
{
ASTPtr result;
Expand Down Expand Up @@ -84,21 +82,27 @@ ASTPtr parseOrder(const std::string & order)

}

HTTPQueryAST getHTTPQueryAST(HTMLForm & params)
template <typename T>
HTTPQueryAST getHTTPQueryAST(const T & params)
{
HTTPQueryAST result;

for (const auto & [key, value] : params)
if (key == kColumns)
result.select_expressions.push_back(parseColumns(value));
else if (key == kSelect)
result.select_expressions.push_back(parseSelect(value));
result.select_expressions.push_back(parseColumns(value));
else if (key == kWhere)
result.where_expressions.push_back(parseWhere(value));
else if (key == kOrder)
result.order_expressions.push_back(parseOrder(value));
else if (!reserved_param_names.contains(key))
result.where_expressions.push_back(parseWhere(std::format("{}={}", key, value)));

return result;
}

template HTTPQueryAST getHTTPQueryAST(const std::map<std::string, std::string> & params);
template HTTPQueryAST getHTTPQueryAST(const HTMLForm & params);

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ struct HTTPQueryAST
std::vector<ASTPtr> order_expressions;
};

HTTPQueryAST getHTTPQueryAST(HTMLForm & params);
template <typename T>
HTTPQueryAST getHTTPQueryAST(const T & params);

}
104 changes: 34 additions & 70 deletions src/Server/HTTPHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@
#include <Parsers/ASTFunction.h>
#include <Parsers/ASTSelectQuery.h>
#include <Parsers/ASTSelectWithUnionQuery.h>
#include <Server/HTTP/HTTPQueryAST.h>
#include <Parsers/formatAST.h>
#include <Server/HTTP/HTTPQuery.h>

#include <Poco/Base64Decoder.h>
#include <Poco/Base64Encoder.h>
Expand Down Expand Up @@ -606,7 +607,7 @@ void combineSelectQuery(ASTSelectQuery * select_query, HTTPQueryAST & http_query
combineOrderExpressions(select_query, http_query_ast.order_expressions);
}

void combineQueryWithParams(QueryData& query_data, HTMLForm & params)
void combineQueryWithParams(QueryData & query_data, const std::map<std::string, std::string> & params)
{
auto http_query_ast = getHTTPQueryAST(params);

Expand Down Expand Up @@ -670,6 +671,10 @@ void HTTPHandler::processQuery(
context->setClientProtocolVersion(version_param);
}

/// this parameter is used to execute the request
/// if the value is false, the request will not be executed and will be returned in text form
bool execute = params.getParsed<bool>("execute", true);

/// The client can pass a HTTP header indicating supported compression method (gzip or deflate).
String http_response_compression_methods = request.get("Accept-Encoding", "");
CompressionMethod http_response_compression_method = CompressionMethod::None;
Expand Down Expand Up @@ -790,51 +795,11 @@ void HTTPHandler::processQuery(
else
in_post_maybe_compressed = std::move(in_post);

static const NameSet reserved_param_names{
"compress",
"decompress",
"user",
"password",
"quota_key",
"query_id",
"stacktrace",
"role",
"buffer_size",
"wait_end_of_query",
"session_id",
"session_timeout",
"session_check",
"client_protocol_version",
"close_session",
"where",
"columns",
"select",
"order"};

Names reserved_param_suffixes;

auto param_could_be_skipped = [&] (const String & name)
{
/// Empty parameter appears when URL like ?&a=b or a=b&&c=d. Just skip them for user's convenience.
if (name.empty())
return true;

if (reserved_param_names.contains(name))
return true;

for (const String & suffix : reserved_param_suffixes)
{
if (endsWith(name, suffix))
return true;
}

return false;
};
const auto & access_control = context->getAccessControl();

auto roles = params.getAll("role");
if (!roles.empty())
{
const auto & access_control = context->getAccessControl();
const auto & user = context->getUser();
std::vector<UUID> roles_ids(roles.size());
for (size_t i = 0; i < roles.size(); i++)
Expand Down Expand Up @@ -870,20 +835,14 @@ void HTTPHandler::processQuery(

bool has_external_data = startsWith(request.getContentType(), "multipart/form-data");

if (has_external_data)
{
/// Skip unneeded parameters to avoid confusing them later with context settings or query parameters.
reserved_param_suffixes.reserve(3);
/// It is a bug and ambiguity with `date_time_input_format` and `low_cardinality_allow_in_native_format` formats/settings.
reserved_param_suffixes.emplace_back("_format");
reserved_param_suffixes.emplace_back("_types");
reserved_param_suffixes.emplace_back("_structure");
}

std::string database = request.get("X-ClickHouse-Database", "");
std::string default_format = request.get("X-ClickHouse-Format", "");

SettingsChanges settings_changes;

/// Using std::map is used instead of std::unordered_map for consistency in the output order
std::map<std::string, std::string> additional_params;

for (const auto & [key, value] : params)
{
if (key == "database")
Expand All @@ -896,14 +855,12 @@ void HTTPHandler::processQuery(
if (default_format.empty())
default_format = value;
}
else if (param_could_be_skipped(key))
else if (!customizeQueryParam(context, key, value))
{
}
else
{
/// Other than query parameters are treated as settings.
if (!customizeQueryParam(context, key, value))
if (access_control.isSettingNameAllowed(key))
settings_changes.push_back({key, value});
else
additional_params.insert({key, value});
}
}

Expand Down Expand Up @@ -1037,17 +994,24 @@ void HTTPHandler::processQuery(
else
throw Exception(ErrorCodes::SYNTAX_ERROR, "Empty request");

combineQueryWithParams(*query_data, params);

executeQuery(
*query_data,
*used_output.out_maybe_delayed_and_compressed,
/* allow_into_outfile = */ false,
context,
set_query_result,
QueryFlags{},
{},
handle_exception_in_output_format);
combineQueryWithParams(*query_data, additional_params);

if (execute)
{
executeQuery(
*query_data,
*used_output.out_maybe_delayed_and_compressed,
/* allow_into_outfile = */ false,
context,
set_query_result,
QueryFlags{},
{},
handle_exception_in_output_format);
}
else
{
formatAST(*query_data->ast, *used_output.out_maybe_delayed_and_compressed, /*hilite=*/false, /*one_line=*/true);
}

session->releaseSessionID();

Expand Down
2 changes: 1 addition & 1 deletion src/Server/HTTPHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ class HTTPHandler : public HTTPRequestHandler

class DynamicQueryHandler : public HTTPHandler
{
private:
protected:
std::string param_name;
public:
explicit DynamicQueryHandler(IServer & server_, const std::string & param_name_ = "query", const std::optional<String>& content_type_override_ = std::nullopt);
Expand Down
7 changes: 7 additions & 0 deletions src/Server/HTTPHandlerFactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "InterserverIOHTTPHandler.h"
#include "PrometheusRequestHandler.h"
#include "WebUIRequestHandler.h"
#include "TabularHandler.h"


namespace DB
Expand Down Expand Up @@ -255,6 +256,12 @@ void addCommonDefaultHandlersFactory(HTTPRequestHandlerFactoryMain & factory, IS
js_handler->attachNonStrictPath("/js/");
js_handler->allowGetAndHeadRequest();
factory.addHandler(js_handler);

auto tabular_handler = std::make_shared<HandlingRuleHTTPHandlerFactory<TabularHandler>>(server);
tabular_handler->attachNonStrictPath("/tabular");
tabular_handler->allowGetAndHeadRequest();
factory.addPathToHints("/tabular");
factory.addHandler(tabular_handler);
}

void addDefaultHandlersFactory(
Expand Down
2 changes: 1 addition & 1 deletion src/Server/TabularHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
#include <Parsers/formatAST.h>

#include <Interpreters/Context.h>
#include <Server/HTTP/HTTPQueryAST.h>
#include <Server/HTTP/HTTPQuery.h>
#include <Poco/URI.h>

namespace DB
Expand Down
Loading

0 comments on commit afa684a

Please sign in to comment.