mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-12-04 21:42:39 +00:00
Revert all changes but save commit history from @kuskarov
This commit is contained in:
parent
fcd7fd8e7c
commit
275075b6f7
@ -8,6 +8,7 @@ set (SRCS
|
|||||||
getMemoryAmount.cpp
|
getMemoryAmount.cpp
|
||||||
getThreadId.cpp
|
getThreadId.cpp
|
||||||
JSON.cpp
|
JSON.cpp
|
||||||
|
LineReader.cpp
|
||||||
mremap.cpp
|
mremap.cpp
|
||||||
phdr_cache.cpp
|
phdr_cache.cpp
|
||||||
preciseExp10.cpp
|
preciseExp10.cpp
|
||||||
@ -17,6 +18,12 @@ set (SRCS
|
|||||||
terminalColors.cpp
|
terminalColors.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (ENABLE_REPLXX)
|
||||||
|
list (APPEND SRCS ReplxxLineReader.cpp)
|
||||||
|
elseif (ENABLE_READLINE)
|
||||||
|
list (APPEND SRCS ReadlineLineReader.cpp)
|
||||||
|
endif ()
|
||||||
|
|
||||||
if (USE_DEBUG_HELPERS)
|
if (USE_DEBUG_HELPERS)
|
||||||
set (INCLUDE_DEBUG_HELPERS "-include ${ClickHouse_SOURCE_DIR}/base/common/iostream_debug_helpers.h")
|
set (INCLUDE_DEBUG_HELPERS "-include ${ClickHouse_SOURCE_DIR}/base/common/iostream_debug_helpers.h")
|
||||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${INCLUDE_DEBUG_HELPERS}")
|
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${INCLUDE_DEBUG_HELPERS}")
|
||||||
@ -40,6 +47,28 @@ if (NOT USE_INTERNAL_BOOST_LIBRARY)
|
|||||||
target_include_directories (common SYSTEM BEFORE PUBLIC ${Boost_INCLUDE_DIRS})
|
target_include_directories (common SYSTEM BEFORE PUBLIC ${Boost_INCLUDE_DIRS})
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
# Allow explicit fallback to readline
|
||||||
|
if (NOT ENABLE_REPLXX AND ENABLE_READLINE)
|
||||||
|
message (STATUS "Attempt to fallback to readline explicitly")
|
||||||
|
set (READLINE_PATHS "/usr/local/opt/readline/lib")
|
||||||
|
# First try find custom lib for macos users (default lib without history support)
|
||||||
|
find_library (READLINE_LIB NAMES readline PATHS ${READLINE_PATHS} NO_DEFAULT_PATH)
|
||||||
|
if (NOT READLINE_LIB)
|
||||||
|
find_library (READLINE_LIB NAMES readline PATHS ${READLINE_PATHS})
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(READLINE_INCLUDE_PATHS "/usr/local/opt/readline/include")
|
||||||
|
find_path (READLINE_INCLUDE_DIR NAMES readline/readline.h PATHS ${READLINE_INCLUDE_PATHS} NO_DEFAULT_PATH)
|
||||||
|
if (NOT READLINE_INCLUDE_DIR)
|
||||||
|
find_path (READLINE_INCLUDE_DIR NAMES readline/readline.h PATHS ${READLINE_INCLUDE_PATHS})
|
||||||
|
endif ()
|
||||||
|
if (READLINE_INCLUDE_DIR AND READLINE_LIB)
|
||||||
|
target_link_libraries(common PUBLIC ${READLINE_LIB})
|
||||||
|
target_compile_definitions(common PUBLIC USE_READLINE=1)
|
||||||
|
message (STATUS "Using readline: ${READLINE_INCLUDE_DIR} : ${READLINE_LIB}")
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
target_link_libraries (common
|
target_link_libraries (common
|
||||||
PUBLIC
|
PUBLIC
|
||||||
${CITYHASH_LIBRARIES}
|
${CITYHASH_LIBRARIES}
|
||||||
@ -49,6 +78,7 @@ target_link_libraries (common
|
|||||||
Poco::Net::SSL
|
Poco::Net::SSL
|
||||||
Poco::Util
|
Poco::Util
|
||||||
Poco::Foundation
|
Poco::Foundation
|
||||||
|
replxx
|
||||||
fmt
|
fmt
|
||||||
|
|
||||||
PRIVATE
|
PRIVATE
|
||||||
|
185
base/common/LineReader.cpp
Normal file
185
base/common/LineReader.cpp
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
#include <common/LineReader.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifdef OS_LINUX
|
||||||
|
/// We can detect if code is linked with one or another readline variants or open the library dynamically.
|
||||||
|
# include <dlfcn.h>
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
char * readline(const char *) __attribute__((__weak__));
|
||||||
|
char * (*readline_ptr)(const char *) = readline;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Trim ending whitespace inplace
|
||||||
|
void trim(String & s)
|
||||||
|
{
|
||||||
|
s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { return !std::isspace(ch); }).base(), s.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if multi-line query is inserted from the paste buffer.
|
||||||
|
/// Allows delaying the start of query execution until the entirety of query is inserted.
|
||||||
|
bool hasInputData()
|
||||||
|
{
|
||||||
|
timeval timeout = {0, 0};
|
||||||
|
fd_set fds{};
|
||||||
|
FD_ZERO(&fds);
|
||||||
|
FD_SET(STDIN_FILENO, &fds);
|
||||||
|
return select(1, &fds, nullptr, nullptr, &timeout) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
LineReader::Suggest::WordsRange LineReader::Suggest::getCompletions(const String & prefix, size_t prefix_length) const
|
||||||
|
{
|
||||||
|
if (!ready)
|
||||||
|
return std::make_pair(words.end(), words.end());
|
||||||
|
|
||||||
|
std::string_view last_word;
|
||||||
|
|
||||||
|
auto last_word_pos = prefix.find_last_of(word_break_characters);
|
||||||
|
if (std::string::npos == last_word_pos)
|
||||||
|
last_word = prefix;
|
||||||
|
else
|
||||||
|
last_word = std::string_view(prefix).substr(last_word_pos + 1, std::string::npos);
|
||||||
|
|
||||||
|
/// last_word can be empty.
|
||||||
|
|
||||||
|
/// Only perform case sensitive completion when the prefix string contains any uppercase characters
|
||||||
|
if (std::none_of(prefix.begin(), prefix.end(), [&](auto c) { return c >= 'A' && c <= 'Z'; }))
|
||||||
|
return std::equal_range(
|
||||||
|
words_no_case.begin(), words_no_case.end(), last_word, [prefix_length](std::string_view s, std::string_view prefix_searched)
|
||||||
|
{
|
||||||
|
return strncasecmp(s.data(), prefix_searched.data(), prefix_length) < 0;
|
||||||
|
});
|
||||||
|
else
|
||||||
|
return std::equal_range(words.begin(), words.end(), last_word, [prefix_length](std::string_view s, std::string_view prefix_searched)
|
||||||
|
{
|
||||||
|
return strncmp(s.data(), prefix_searched.data(), prefix_length) < 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
LineReader::LineReader(const String & history_file_path_, bool multiline_, Patterns extenders_, Patterns delimiters_)
|
||||||
|
: history_file_path(history_file_path_), multiline(multiline_), extenders(std::move(extenders_)), delimiters(std::move(delimiters_))
|
||||||
|
{
|
||||||
|
/// FIXME: check extender != delimiter
|
||||||
|
}
|
||||||
|
|
||||||
|
String LineReader::readLine(const String & first_prompt, const String & second_prompt)
|
||||||
|
{
|
||||||
|
String line;
|
||||||
|
bool need_next_line = false;
|
||||||
|
|
||||||
|
while (auto status = readOneLine(need_next_line ? second_prompt : first_prompt))
|
||||||
|
{
|
||||||
|
if (status == RESET_LINE)
|
||||||
|
{
|
||||||
|
line.clear();
|
||||||
|
need_next_line = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.empty())
|
||||||
|
{
|
||||||
|
if (!line.empty() && !multiline && !hasInputData())
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(ARCADIA_BUILD) /// C++20
|
||||||
|
const char * has_extender = nullptr;
|
||||||
|
for (const auto * extender : extenders)
|
||||||
|
{
|
||||||
|
if (input.ends_with(extender))
|
||||||
|
{
|
||||||
|
has_extender = extender;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * has_delimiter = nullptr;
|
||||||
|
for (const auto * delimiter : delimiters)
|
||||||
|
{
|
||||||
|
if (input.ends_with(delimiter))
|
||||||
|
{
|
||||||
|
has_delimiter = delimiter;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
need_next_line = has_extender || (multiline && !has_delimiter) || hasInputData();
|
||||||
|
|
||||||
|
if (has_extender)
|
||||||
|
{
|
||||||
|
input.resize(input.size() - strlen(has_extender));
|
||||||
|
trim(input);
|
||||||
|
if (input.empty())
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
line += (line.empty() ? "" : " ") + input;
|
||||||
|
|
||||||
|
if (!need_next_line)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!line.empty() && line != prev_line)
|
||||||
|
{
|
||||||
|
addToHistory(line);
|
||||||
|
prev_line = line;
|
||||||
|
}
|
||||||
|
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
LineReader::InputStatus LineReader::readOneLine(const String & prompt)
|
||||||
|
{
|
||||||
|
input.clear();
|
||||||
|
|
||||||
|
#ifdef OS_LINUX
|
||||||
|
if (!readline_ptr)
|
||||||
|
{
|
||||||
|
for (const auto * name : {"libreadline.so", "libreadline.so.0", "libeditline.so", "libeditline.so.0"})
|
||||||
|
{
|
||||||
|
void * dl_handle = dlopen(name, RTLD_LAZY);
|
||||||
|
if (dl_handle)
|
||||||
|
{
|
||||||
|
readline_ptr = reinterpret_cast<char * (*)(const char *)>(dlsym(dl_handle, "readline"));
|
||||||
|
if (readline_ptr)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Minimal support for readline
|
||||||
|
if (readline_ptr)
|
||||||
|
{
|
||||||
|
char * line_read = (*readline_ptr)(prompt.c_str());
|
||||||
|
if (!line_read)
|
||||||
|
return ABORT;
|
||||||
|
input = line_read;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
std::cout << prompt;
|
||||||
|
std::getline(std::cin, input);
|
||||||
|
if (!std::cin.good())
|
||||||
|
return ABORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
trim(input);
|
||||||
|
return INPUT_LINE;
|
||||||
|
}
|
65
base/common/LineReader.h
Normal file
65
base/common/LineReader.h
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <common/types.h>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class LineReader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct Suggest
|
||||||
|
{
|
||||||
|
using Words = std::vector<std::string>;
|
||||||
|
using WordsRange = std::pair<Words::const_iterator, Words::const_iterator>;
|
||||||
|
|
||||||
|
Words words;
|
||||||
|
Words words_no_case;
|
||||||
|
std::atomic<bool> ready{false};
|
||||||
|
|
||||||
|
/// Get iterators for the matched range of words if any.
|
||||||
|
WordsRange getCompletions(const String & prefix, size_t prefix_length) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
using Patterns = std::vector<const char *>;
|
||||||
|
|
||||||
|
LineReader(const String & history_file_path, bool multiline, Patterns extenders, Patterns delimiters);
|
||||||
|
virtual ~LineReader() {}
|
||||||
|
|
||||||
|
/// Reads the whole line until delimiter (in multiline mode) or until the last line without extender.
|
||||||
|
/// If resulting line is empty, it means the user interrupted the input.
|
||||||
|
/// Non-empty line is appended to history - without duplication.
|
||||||
|
/// Typical delimiter is ';' (semicolon) and typical extender is '\' (backslash).
|
||||||
|
String readLine(const String & first_prompt, const String & second_prompt);
|
||||||
|
|
||||||
|
/// When bracketed paste mode is set, pasted text is bracketed with control sequences so
|
||||||
|
/// that the program can differentiate pasted text from typed-in text. This helps
|
||||||
|
/// clickhouse-client so that without -m flag, one can still paste multiline queries, and
|
||||||
|
/// possibly get better pasting performance. See https://cirw.in/blog/bracketed-paste for
|
||||||
|
/// more details.
|
||||||
|
virtual void enableBracketedPaste() {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
enum InputStatus
|
||||||
|
{
|
||||||
|
ABORT = 0,
|
||||||
|
RESET_LINE,
|
||||||
|
INPUT_LINE,
|
||||||
|
};
|
||||||
|
|
||||||
|
const String history_file_path;
|
||||||
|
static constexpr char word_break_characters[] = " \t\n\r\"\\'`@$><=;|&{(.";
|
||||||
|
|
||||||
|
String input;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool multiline;
|
||||||
|
|
||||||
|
Patterns extenders;
|
||||||
|
Patterns delimiters;
|
||||||
|
|
||||||
|
String prev_line;
|
||||||
|
|
||||||
|
virtual InputStatus readOneLine(const String & prompt);
|
||||||
|
virtual void addToHistory(const String &) {}
|
||||||
|
};
|
176
base/common/ReadlineLineReader.cpp
Normal file
176
base/common/ReadlineLineReader.cpp
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
#include <common/ReadlineLineReader.h>
|
||||||
|
#include <ext/scope_guard.h>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Trim ending whitespace inplace
|
||||||
|
void trim(String & s)
|
||||||
|
{
|
||||||
|
s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { return !std::isspace(ch); }).base(), s.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static const LineReader::Suggest * suggest;
|
||||||
|
|
||||||
|
/// Points to current word to suggest.
|
||||||
|
static LineReader::Suggest::Words::const_iterator pos;
|
||||||
|
/// Points after the last possible match.
|
||||||
|
static LineReader::Suggest::Words::const_iterator end;
|
||||||
|
|
||||||
|
/// Set iterators to the matched range of words if any.
|
||||||
|
static void findRange(const char * prefix, size_t prefix_length)
|
||||||
|
{
|
||||||
|
std::string prefix_str(prefix);
|
||||||
|
std::tie(pos, end) = suggest->getCompletions(prefix_str, prefix_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterates through matched range.
|
||||||
|
static char * nextMatch()
|
||||||
|
{
|
||||||
|
if (pos >= end)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
/// readline will free memory by itself.
|
||||||
|
char * word = strdup(pos->c_str());
|
||||||
|
++pos;
|
||||||
|
return word;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char * generate(const char * text, int state)
|
||||||
|
{
|
||||||
|
if (!suggest->ready)
|
||||||
|
return nullptr;
|
||||||
|
if (state == 0)
|
||||||
|
findRange(text, strlen(text));
|
||||||
|
|
||||||
|
/// Do not append whitespace after word. For unknown reason, rl_completion_append_character = '\0' does not work.
|
||||||
|
rl_completion_suppress_append = 1;
|
||||||
|
|
||||||
|
return nextMatch();
|
||||||
|
};
|
||||||
|
|
||||||
|
ReadlineLineReader::ReadlineLineReader(
|
||||||
|
const Suggest & suggest_, const String & history_file_path_, bool multiline_, Patterns extenders_, Patterns delimiters_)
|
||||||
|
: LineReader(history_file_path_, multiline_, std::move(extenders_), std::move(delimiters_))
|
||||||
|
{
|
||||||
|
suggest = &suggest_;
|
||||||
|
|
||||||
|
if (!history_file_path.empty())
|
||||||
|
{
|
||||||
|
int res = read_history(history_file_path.c_str());
|
||||||
|
if (res)
|
||||||
|
std::cerr << "Cannot read history from file " + history_file_path + ": "+ strerror(errno) << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Added '.' to the default list. Because it is used to separate database and table.
|
||||||
|
rl_basic_word_break_characters = word_break_characters;
|
||||||
|
|
||||||
|
/// Not append whitespace after single suggestion. Because whitespace after function name is meaningless.
|
||||||
|
rl_completion_append_character = '\0';
|
||||||
|
|
||||||
|
rl_completion_entry_function = generate;
|
||||||
|
|
||||||
|
/// Install Ctrl+C signal handler that will be used in interactive mode.
|
||||||
|
|
||||||
|
if (rl_initialize())
|
||||||
|
throw std::runtime_error("Cannot initialize readline");
|
||||||
|
|
||||||
|
auto clear_prompt_or_exit = [](int)
|
||||||
|
{
|
||||||
|
/// This is signal safe.
|
||||||
|
ssize_t res = write(STDOUT_FILENO, "\n", 1);
|
||||||
|
|
||||||
|
/// Allow to quit client while query is in progress by pressing Ctrl+C twice.
|
||||||
|
/// (First press to Ctrl+C will try to cancel query by InterruptListener).
|
||||||
|
if (res == 1 && rl_line_buffer[0] && !RL_ISSTATE(RL_STATE_DONE))
|
||||||
|
{
|
||||||
|
rl_replace_line("", 0);
|
||||||
|
if (rl_forced_update_display())
|
||||||
|
_exit(0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/// A little dirty, but we struggle to find better way to correctly
|
||||||
|
/// force readline to exit after returning from the signal handler.
|
||||||
|
_exit(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (signal(SIGINT, clear_prompt_or_exit) == SIG_ERR)
|
||||||
|
throw std::runtime_error(std::string("Cannot set signal handler for readline: ") + strerror(errno));
|
||||||
|
|
||||||
|
rl_variable_bind("completion-ignore-case", "on");
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadlineLineReader::~ReadlineLineReader()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
LineReader::InputStatus ReadlineLineReader::readOneLine(const String & prompt)
|
||||||
|
{
|
||||||
|
input.clear();
|
||||||
|
|
||||||
|
const char* cinput = readline(prompt.c_str());
|
||||||
|
if (cinput == nullptr)
|
||||||
|
return (errno != EAGAIN) ? ABORT : RESET_LINE;
|
||||||
|
input = cinput;
|
||||||
|
|
||||||
|
trim(input);
|
||||||
|
return INPUT_LINE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadlineLineReader::addToHistory(const String & line)
|
||||||
|
{
|
||||||
|
add_history(line.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
#if RL_VERSION_MAJOR >= 7
|
||||||
|
|
||||||
|
#define BRACK_PASTE_PREF "\033[200~"
|
||||||
|
#define BRACK_PASTE_SUFF "\033[201~"
|
||||||
|
|
||||||
|
#define BRACK_PASTE_LAST '~'
|
||||||
|
#define BRACK_PASTE_SLEN 6
|
||||||
|
|
||||||
|
/// This handler bypasses some unused macro/event checkings and remove trailing newlines before insertion.
|
||||||
|
static int clickhouse_rl_bracketed_paste_begin(int /* count */, int /* key */)
|
||||||
|
{
|
||||||
|
std::string buf;
|
||||||
|
buf.reserve(128);
|
||||||
|
|
||||||
|
RL_SETSTATE(RL_STATE_MOREINPUT);
|
||||||
|
SCOPE_EXIT(RL_UNSETSTATE(RL_STATE_MOREINPUT));
|
||||||
|
int c;
|
||||||
|
while ((c = rl_read_key()) >= 0)
|
||||||
|
{
|
||||||
|
if (c == '\r')
|
||||||
|
c = '\n';
|
||||||
|
buf.push_back(c);
|
||||||
|
if (buf.size() >= BRACK_PASTE_SLEN && c == BRACK_PASTE_LAST && buf.substr(buf.size() - BRACK_PASTE_SLEN) == BRACK_PASTE_SUFF)
|
||||||
|
{
|
||||||
|
buf.resize(buf.size() - BRACK_PASTE_SLEN);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trim(buf);
|
||||||
|
return static_cast<size_t>(rl_insert_text(buf.c_str())) == buf.size() ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void ReadlineLineReader::enableBracketedPaste()
|
||||||
|
{
|
||||||
|
#if RL_VERSION_MAJOR >= 7
|
||||||
|
rl_variable_bind("enable-bracketed-paste", "on");
|
||||||
|
|
||||||
|
/// Use our bracketed paste handler to get better user experience. See comments above.
|
||||||
|
rl_bind_keyseq(BRACK_PASTE_PREF, clickhouse_rl_bracketed_paste_begin);
|
||||||
|
#endif
|
||||||
|
};
|
19
base/common/ReadlineLineReader.h
Normal file
19
base/common/ReadlineLineReader.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "LineReader.h"
|
||||||
|
|
||||||
|
#include <readline/readline.h>
|
||||||
|
#include <readline/history.h>
|
||||||
|
|
||||||
|
class ReadlineLineReader : public LineReader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ReadlineLineReader(const Suggest & suggest, const String & history_file_path, bool multiline, Patterns extenders_, Patterns delimiters_);
|
||||||
|
~ReadlineLineReader() override;
|
||||||
|
|
||||||
|
void enableBracketedPaste() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
InputStatus readOneLine(const String & prompt) override;
|
||||||
|
void addToHistory(const String & line) override;
|
||||||
|
};
|
77
base/common/ReplxxLineReader.cpp
Normal file
77
base/common/ReplxxLineReader.cpp
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#include <common/ReplxxLineReader.h>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Trim ending whitespace inplace
|
||||||
|
void trim(String & s)
|
||||||
|
{
|
||||||
|
s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { return !std::isspace(ch); }).base(), s.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ReplxxLineReader::ReplxxLineReader(
|
||||||
|
const Suggest & suggest, const String & history_file_path_, bool multiline_, Patterns extenders_, Patterns delimiters_)
|
||||||
|
: LineReader(history_file_path_, multiline_, std::move(extenders_), std::move(delimiters_))
|
||||||
|
{
|
||||||
|
using namespace std::placeholders;
|
||||||
|
using Replxx = replxx::Replxx;
|
||||||
|
|
||||||
|
if (!history_file_path.empty())
|
||||||
|
rx.history_load(history_file_path);
|
||||||
|
|
||||||
|
auto callback = [&suggest] (const String & context, size_t context_size)
|
||||||
|
{
|
||||||
|
auto range = suggest.getCompletions(context, context_size);
|
||||||
|
return Replxx::completions_t(range.first, range.second);
|
||||||
|
};
|
||||||
|
|
||||||
|
rx.set_completion_callback(callback);
|
||||||
|
rx.set_complete_on_empty(false);
|
||||||
|
rx.set_word_break_characters(word_break_characters);
|
||||||
|
|
||||||
|
/// By default C-p/C-n binded to COMPLETE_NEXT/COMPLETE_PREV,
|
||||||
|
/// bind C-p/C-n to history-previous/history-next like readline.
|
||||||
|
rx.bind_key(Replxx::KEY::control('N'), [this](char32_t code) { return rx.invoke(Replxx::ACTION::HISTORY_NEXT, code); });
|
||||||
|
rx.bind_key(Replxx::KEY::control('P'), [this](char32_t code) { return rx.invoke(Replxx::ACTION::HISTORY_PREVIOUS, code); });
|
||||||
|
/// By default COMPLETE_NEXT/COMPLETE_PREV was binded to C-p/C-n, re-bind
|
||||||
|
/// to M-P/M-N (that was used for HISTORY_COMMON_PREFIX_SEARCH before, but
|
||||||
|
/// it also binded to M-p/M-n).
|
||||||
|
rx.bind_key(Replxx::KEY::meta('N'), [this](char32_t code) { return rx.invoke(Replxx::ACTION::COMPLETE_NEXT, code); });
|
||||||
|
rx.bind_key(Replxx::KEY::meta('P'), [this](char32_t code) { return rx.invoke(Replxx::ACTION::COMPLETE_PREVIOUS, code); });
|
||||||
|
}
|
||||||
|
|
||||||
|
ReplxxLineReader::~ReplxxLineReader()
|
||||||
|
{
|
||||||
|
if (!history_file_path.empty())
|
||||||
|
rx.history_save(history_file_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
LineReader::InputStatus ReplxxLineReader::readOneLine(const String & prompt)
|
||||||
|
{
|
||||||
|
input.clear();
|
||||||
|
|
||||||
|
const char* cinput = rx.input(prompt);
|
||||||
|
if (cinput == nullptr)
|
||||||
|
return (errno != EAGAIN) ? ABORT : RESET_LINE;
|
||||||
|
input = cinput;
|
||||||
|
|
||||||
|
trim(input);
|
||||||
|
return INPUT_LINE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReplxxLineReader::addToHistory(const String & line)
|
||||||
|
{
|
||||||
|
rx.history_add(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReplxxLineReader::enableBracketedPaste()
|
||||||
|
{
|
||||||
|
rx.enable_bracketed_paste();
|
||||||
|
};
|
@ -1,13 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
#include "LineReader.h"
|
#include "LineReader.h"
|
||||||
#include <Parsers/Lexer.h>
|
|
||||||
|
|
||||||
#include <replxx.hxx>
|
#include <replxx.hxx>
|
||||||
|
|
||||||
|
|
||||||
class ReplxxLineReader : public LineReader
|
class ReplxxLineReader : public LineReader
|
||||||
{
|
{
|
||||||
public:
|
public:
|
@ -39,6 +39,7 @@ SRCS(
|
|||||||
getMemoryAmount.cpp
|
getMemoryAmount.cpp
|
||||||
getThreadId.cpp
|
getThreadId.cpp
|
||||||
JSON.cpp
|
JSON.cpp
|
||||||
|
LineReader.cpp
|
||||||
mremap.cpp
|
mremap.cpp
|
||||||
phdr_cache.cpp
|
phdr_cache.cpp
|
||||||
preciseExp10.cpp
|
preciseExp10.cpp
|
||||||
|
@ -3,11 +3,11 @@
|
|||||||
#include "Suggest.h"
|
#include "Suggest.h"
|
||||||
|
|
||||||
#if USE_REPLXX
|
#if USE_REPLXX
|
||||||
# include <Common/ReplxxLineReader.h>
|
# include <common/ReplxxLineReader.h>
|
||||||
#elif defined(USE_READLINE) && USE_READLINE
|
#elif defined(USE_READLINE) && USE_READLINE
|
||||||
# include <Common/ReadlineLineReader.h>
|
# include <common/ReadlineLineReader.h>
|
||||||
#else
|
#else
|
||||||
# include <Common/LineReader.h>
|
# include <common/LineReader.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -27,7 +27,7 @@
|
|||||||
#include <Poco/File.h>
|
#include <Poco/File.h>
|
||||||
#include <Poco/Util/Application.h>
|
#include <Poco/Util/Application.h>
|
||||||
#include <common/find_symbols.h>
|
#include <common/find_symbols.h>
|
||||||
#include <Common/LineReader.h>
|
#include <common/LineReader.h>
|
||||||
#include <Common/ClickHouseRevision.h>
|
#include <Common/ClickHouseRevision.h>
|
||||||
#include <Common/Stopwatch.h>
|
#include <Common/Stopwatch.h>
|
||||||
#include <Common/Exception.h>
|
#include <Common/Exception.h>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#include <Client/Connection.h>
|
#include <Client/Connection.h>
|
||||||
#include <IO/ConnectionTimeouts.h>
|
#include <IO/ConnectionTimeouts.h>
|
||||||
#include <Common/LineReader.h>
|
#include <common/LineReader.h>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
|
||||||
|
@ -235,29 +235,6 @@ if(RE2_ST_LIBRARY)
|
|||||||
target_link_libraries(clickhouse_common_io PUBLIC ${RE2_ST_LIBRARY})
|
target_link_libraries(clickhouse_common_io PUBLIC ${RE2_ST_LIBRARY})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Allow explicit fallback to readline
|
|
||||||
if (NOT ENABLE_REPLXX AND ENABLE_READLINE)
|
|
||||||
message (STATUS "Attempt to fallback to readline explicitly")
|
|
||||||
set (READLINE_PATHS "/usr/local/opt/readline/lib")
|
|
||||||
# First try find custom lib for macos users (default lib without history support)
|
|
||||||
find_library (READLINE_LIB NAMES readline PATHS ${READLINE_PATHS} NO_DEFAULT_PATH)
|
|
||||||
if (NOT READLINE_LIB)
|
|
||||||
find_library (READLINE_LIB NAMES readline PATHS ${READLINE_PATHS})
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
set(READLINE_INCLUDE_PATHS "/usr/local/opt/readline/include")
|
|
||||||
find_path (READLINE_INCLUDE_DIR NAMES readline/readline.h PATHS ${READLINE_INCLUDE_PATHS} NO_DEFAULT_PATH)
|
|
||||||
if (NOT READLINE_INCLUDE_DIR)
|
|
||||||
find_path (READLINE_INCLUDE_DIR NAMES readline/readline.h PATHS ${READLINE_INCLUDE_PATHS})
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (READLINE_INCLUDE_DIR AND READLINE_LIB)
|
|
||||||
target_link_libraries(clickhouse_common_io PUBLIC ${READLINE_LIB})
|
|
||||||
target_compile_definitions(clickhouse_common_io PUBLIC USE_READLINE=1)
|
|
||||||
message (STATUS "Using readline: ${READLINE_INCLUDE_DIR} : ${READLINE_LIB}")
|
|
||||||
endif ()
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
target_link_libraries(clickhouse_common_io
|
target_link_libraries(clickhouse_common_io
|
||||||
PRIVATE
|
PRIVATE
|
||||||
${EXECINFO_LIBRARIES}
|
${EXECINFO_LIBRARIES}
|
||||||
@ -270,7 +247,6 @@ target_link_libraries(clickhouse_common_io
|
|||||||
pcg_random
|
pcg_random
|
||||||
Poco::Foundation
|
Poco::Foundation
|
||||||
roaring
|
roaring
|
||||||
replxx
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if (USE_RDKAFKA)
|
if (USE_RDKAFKA)
|
||||||
@ -285,7 +261,6 @@ if(RE2_INCLUDE_DIR)
|
|||||||
target_include_directories(clickhouse_common_io SYSTEM BEFORE PUBLIC ${RE2_INCLUDE_DIR})
|
target_include_directories(clickhouse_common_io SYSTEM BEFORE PUBLIC ${RE2_INCLUDE_DIR})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
dbms_target_link_libraries (
|
dbms_target_link_libraries (
|
||||||
PRIVATE
|
PRIVATE
|
||||||
${BTRIE_LIBRARIES}
|
${BTRIE_LIBRARIES}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
add_subdirectory(StringUtils)
|
add_subdirectory(StringUtils)
|
||||||
|
# after common_io
|
||||||
|
#add_subdirectory(ZooKeeper)
|
||||||
|
#add_subdirectory(ConfigProcessor)
|
||||||
|
|
||||||
if (ENABLE_TESTS)
|
if (ENABLE_TESTS)
|
||||||
add_subdirectory (tests)
|
add_subdirectory (tests)
|
||||||
|
@ -1,154 +0,0 @@
|
|||||||
#include <Common/ReplxxLineReader.h>
|
|
||||||
#include <Common/UTF8Helpers.h>
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
/// Trim ending whitespace inplace
|
|
||||||
void trim(String & s)
|
|
||||||
{
|
|
||||||
s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { return !std::isspace(ch); }).base(), s.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void highlightCallback(const String & query, std::vector<replxx::Replxx::Color> & colors)
|
|
||||||
{
|
|
||||||
using namespace DB;
|
|
||||||
using namespace replxx;
|
|
||||||
|
|
||||||
static const std::unordered_map<TokenType, Replxx::Color> token_to_color =
|
|
||||||
{
|
|
||||||
{ TokenType::Whitespace, Replxx::Color::DEFAULT },
|
|
||||||
{ TokenType::Comment, Replxx::Color::GRAY },
|
|
||||||
{ TokenType::BareWord, Replxx::Color::WHITE },
|
|
||||||
{ TokenType::Number, Replxx::Color::BRIGHTGREEN },
|
|
||||||
{ TokenType::StringLiteral, Replxx::Color::BRIGHTCYAN },
|
|
||||||
{ TokenType::QuotedIdentifier, Replxx::Color::BRIGHTMAGENTA },
|
|
||||||
{ TokenType::OpeningRoundBracket, Replxx::Color::LIGHTGRAY },
|
|
||||||
{ TokenType::ClosingRoundBracket, Replxx::Color::LIGHTGRAY },
|
|
||||||
{ TokenType::OpeningSquareBracket, Replxx::Color::BROWN },
|
|
||||||
{ TokenType::ClosingSquareBracket, Replxx::Color::BROWN },
|
|
||||||
{ TokenType::OpeningCurlyBrace, Replxx::Color::WHITE },
|
|
||||||
{ TokenType::ClosingCurlyBrace, Replxx::Color::WHITE },
|
|
||||||
|
|
||||||
{ TokenType::Comma, Replxx::Color::YELLOW },
|
|
||||||
{ TokenType::Semicolon, Replxx::Color::YELLOW },
|
|
||||||
{ TokenType::Dot, Replxx::Color::YELLOW },
|
|
||||||
{ TokenType::Asterisk, Replxx::Color::YELLOW },
|
|
||||||
{ TokenType::Plus, Replxx::Color::YELLOW },
|
|
||||||
{ TokenType::Minus, Replxx::Color::YELLOW },
|
|
||||||
{ TokenType::Slash, Replxx::Color::YELLOW },
|
|
||||||
{ TokenType::Percent, Replxx::Color::YELLOW },
|
|
||||||
{ TokenType::Arrow, Replxx::Color::YELLOW },
|
|
||||||
{ TokenType::QuestionMark, Replxx::Color::YELLOW },
|
|
||||||
{ TokenType::Colon, Replxx::Color::YELLOW },
|
|
||||||
{ TokenType::Equals, Replxx::Color::YELLOW },
|
|
||||||
{ TokenType::NotEquals, Replxx::Color::YELLOW },
|
|
||||||
{ TokenType::Less, Replxx::Color::YELLOW },
|
|
||||||
{ TokenType::Greater, Replxx::Color::YELLOW },
|
|
||||||
{ TokenType::LessOrEquals, Replxx::Color::YELLOW },
|
|
||||||
{ TokenType::GreaterOrEquals, Replxx::Color::YELLOW },
|
|
||||||
{ TokenType::Concatenation, Replxx::Color::YELLOW },
|
|
||||||
{ TokenType::At, Replxx::Color::YELLOW },
|
|
||||||
|
|
||||||
{ TokenType::EndOfStream, Replxx::Color::DEFAULT },
|
|
||||||
|
|
||||||
{ TokenType::Error, Replxx::Color::RED },
|
|
||||||
{ TokenType::ErrorMultilineCommentIsNotClosed, Replxx::Color::RED },
|
|
||||||
{ TokenType::ErrorSingleQuoteIsNotClosed, Replxx::Color::RED },
|
|
||||||
{ TokenType::ErrorDoubleQuoteIsNotClosed, Replxx::Color::RED },
|
|
||||||
{ TokenType::ErrorSinglePipeMark, Replxx::Color::RED },
|
|
||||||
{ TokenType::ErrorWrongNumber, Replxx::Color::RED },
|
|
||||||
{ TokenType::ErrorMaxQuerySizeExceeded, Replxx::Color::RED }
|
|
||||||
};
|
|
||||||
|
|
||||||
const Replxx::Color unknown_token_color = Replxx::Color::RED;
|
|
||||||
|
|
||||||
Lexer lexer(query.data(), query.data() + query.size());
|
|
||||||
size_t pos = 0;
|
|
||||||
|
|
||||||
for (Token token = lexer.nextToken(); !token.isEnd(); token = lexer.nextToken())
|
|
||||||
{
|
|
||||||
size_t utf8_len = UTF8::countCodePoints(reinterpret_cast<const UInt8 *>(token.begin), token.size());
|
|
||||||
for (size_t code_point_index = 0; code_point_index < utf8_len; ++code_point_index)
|
|
||||||
{
|
|
||||||
if (token_to_color.find(token.type) != token_to_color.end())
|
|
||||||
colors[pos + code_point_index] = token_to_color.at(token.type);
|
|
||||||
else
|
|
||||||
colors[pos + code_point_index] = unknown_token_color;
|
|
||||||
}
|
|
||||||
|
|
||||||
pos += utf8_len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ReplxxLineReader::ReplxxLineReader(
|
|
||||||
const Suggest & suggest, const String & history_file_path_, bool multiline_, Patterns extenders_, Patterns delimiters_)
|
|
||||||
: LineReader(history_file_path_, multiline_, std::move(extenders_), std::move(delimiters_))
|
|
||||||
{
|
|
||||||
using namespace std::placeholders;
|
|
||||||
using Replxx = replxx::Replxx;
|
|
||||||
using namespace DB;
|
|
||||||
|
|
||||||
if (!history_file_path.empty())
|
|
||||||
rx.history_load(history_file_path);
|
|
||||||
|
|
||||||
auto suggesting_callback = [&suggest] (const String & context, size_t context_size)
|
|
||||||
{
|
|
||||||
auto range = suggest.getCompletions(context, context_size);
|
|
||||||
return Replxx::completions_t(range.first, range.second);
|
|
||||||
};
|
|
||||||
|
|
||||||
rx.set_highlighter_callback(highlightCallback);
|
|
||||||
rx.set_completion_callback(suggesting_callback);
|
|
||||||
rx.set_complete_on_empty(false);
|
|
||||||
rx.set_word_break_characters(word_break_characters);
|
|
||||||
|
|
||||||
/// By default C-p/C-n binded to COMPLETE_NEXT/COMPLETE_PREV,
|
|
||||||
/// bind C-p/C-n to history-previous/history-next like readline.
|
|
||||||
rx.bind_key(Replxx::KEY::control('N'), [this](char32_t code) { return rx.invoke(Replxx::ACTION::HISTORY_NEXT, code); });
|
|
||||||
rx.bind_key(Replxx::KEY::control('P'), [this](char32_t code) { return rx.invoke(Replxx::ACTION::HISTORY_PREVIOUS, code); });
|
|
||||||
|
|
||||||
/// By default COMPLETE_NEXT/COMPLETE_PREV was binded to C-p/C-n, re-bind
|
|
||||||
/// to M-P/M-N (that was used for HISTORY_COMMON_PREFIX_SEARCH before, but
|
|
||||||
/// it also binded to M-p/M-n).
|
|
||||||
rx.bind_key(Replxx::KEY::meta('N'), [this](char32_t code) { return rx.invoke(Replxx::ACTION::COMPLETE_NEXT, code); });
|
|
||||||
rx.bind_key(Replxx::KEY::meta('P'), [this](char32_t code) { return rx.invoke(Replxx::ACTION::COMPLETE_PREVIOUS, code); });
|
|
||||||
}
|
|
||||||
|
|
||||||
ReplxxLineReader::~ReplxxLineReader()
|
|
||||||
{
|
|
||||||
if (!history_file_path.empty())
|
|
||||||
rx.history_save(history_file_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
LineReader::InputStatus ReplxxLineReader::readOneLine(const String & prompt)
|
|
||||||
{
|
|
||||||
input.clear();
|
|
||||||
|
|
||||||
const char* cinput = rx.input(prompt);
|
|
||||||
if (cinput == nullptr)
|
|
||||||
return (errno != EAGAIN) ? ABORT : RESET_LINE;
|
|
||||||
input = cinput;
|
|
||||||
|
|
||||||
trim(input);
|
|
||||||
return INPUT_LINE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReplxxLineReader::addToHistory(const String & line)
|
|
||||||
{
|
|
||||||
rx.history_add(line);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReplxxLineReader::enableBracketedPaste()
|
|
||||||
{
|
|
||||||
rx.enable_bracketed_paste();
|
|
||||||
};
|
|
@ -52,7 +52,6 @@ SRCS(
|
|||||||
IntervalKind.cpp
|
IntervalKind.cpp
|
||||||
IPv6ToBinary.cpp
|
IPv6ToBinary.cpp
|
||||||
isLocalAddress.cpp
|
isLocalAddress.cpp
|
||||||
LineReader.cpp
|
|
||||||
Macros.cpp
|
Macros.cpp
|
||||||
malloc.cpp
|
malloc.cpp
|
||||||
MemoryStatisticsOS.cpp
|
MemoryStatisticsOS.cpp
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
#include <Poco/ConsoleChannel.h>
|
#include <Poco/ConsoleChannel.h>
|
||||||
#include <Common/ZooKeeper/KeeperException.h>
|
#include <Common/ZooKeeper/KeeperException.h>
|
||||||
#include <Common/ZooKeeper/ZooKeeper.h>
|
#include <Common/ZooKeeper/ZooKeeper.h>
|
||||||
#include <Common/LineReader.h>
|
#include <common/LineReader.h>
|
||||||
#include <common/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
Loading…
Reference in New Issue
Block a user