Merge branch 'master' into alexey-milovidov-patch-5
2
.github/workflows/debug.yml
vendored
@ -2,7 +2,7 @@
|
|||||||
name: Debug
|
name: Debug
|
||||||
|
|
||||||
'on':
|
'on':
|
||||||
[push, pull_request, release, workflow_dispatch]
|
[push, pull_request, release, workflow_dispatch, workflow_call]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
DebugInfo:
|
DebugInfo:
|
||||||
|
92
.github/workflows/master.yml
vendored
@ -1056,6 +1056,23 @@ jobs:
|
|||||||
docker ps --quiet | xargs --no-run-if-empty docker kill ||:
|
docker ps --quiet | xargs --no-run-if-empty docker kill ||:
|
||||||
docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||:
|
docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||:
|
||||||
sudo rm -fr "$TEMP_PATH"
|
sudo rm -fr "$TEMP_PATH"
|
||||||
|
MarkReleaseReady:
|
||||||
|
needs:
|
||||||
|
- BuilderBinDarwin
|
||||||
|
- BuilderBinDarwinAarch64
|
||||||
|
- BuilderDebRelease
|
||||||
|
- BuilderDebAarch64
|
||||||
|
runs-on: [self-hosted, style-checker]
|
||||||
|
steps:
|
||||||
|
- name: Clear repository
|
||||||
|
run: |
|
||||||
|
sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE"
|
||||||
|
- name: Check out repository code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Mark Commit Release Ready
|
||||||
|
run: |
|
||||||
|
cd "$GITHUB_WORKSPACE/tests/ci"
|
||||||
|
python3 mark_release_ready.py
|
||||||
##############################################################################################
|
##############################################################################################
|
||||||
########################### FUNCTIONAl STATELESS TESTS #######################################
|
########################### FUNCTIONAl STATELESS TESTS #######################################
|
||||||
##############################################################################################
|
##############################################################################################
|
||||||
@ -2994,10 +3011,83 @@ jobs:
|
|||||||
docker ps --quiet | xargs --no-run-if-empty docker kill ||:
|
docker ps --quiet | xargs --no-run-if-empty docker kill ||:
|
||||||
docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||:
|
docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||:
|
||||||
sudo rm -fr "$TEMP_PATH"
|
sudo rm -fr "$TEMP_PATH"
|
||||||
|
##############################################################################################
|
||||||
|
###################################### SQLANCER FUZZERS ######################################
|
||||||
|
##############################################################################################
|
||||||
|
SQLancerTestRelease:
|
||||||
|
needs: [BuilderDebRelease]
|
||||||
|
runs-on: [self-hosted, fuzzer-unit-tester]
|
||||||
|
steps:
|
||||||
|
- name: Set envs
|
||||||
|
run: |
|
||||||
|
cat >> "$GITHUB_ENV" << 'EOF'
|
||||||
|
TEMP_PATH=${{runner.temp}}/sqlancer_release
|
||||||
|
REPORTS_PATH=${{runner.temp}}/reports_dir
|
||||||
|
CHECK_NAME=SQLancer (release)
|
||||||
|
REPO_COPY=${{runner.temp}}/sqlancer_release/ClickHouse
|
||||||
|
EOF
|
||||||
|
- name: Download json reports
|
||||||
|
uses: actions/download-artifact@v2
|
||||||
|
with:
|
||||||
|
path: ${{ env.REPORTS_PATH }}
|
||||||
|
- name: Clear repository
|
||||||
|
run: |
|
||||||
|
sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE"
|
||||||
|
- name: Check out repository code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: SQLancer
|
||||||
|
run: |
|
||||||
|
sudo rm -fr "$TEMP_PATH"
|
||||||
|
mkdir -p "$TEMP_PATH"
|
||||||
|
cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH"
|
||||||
|
cd "$REPO_COPY/tests/ci"
|
||||||
|
python3 sqlancer_check.py "$CHECK_NAME"
|
||||||
|
- name: Cleanup
|
||||||
|
if: always()
|
||||||
|
run: |
|
||||||
|
docker ps --quiet | xargs --no-run-if-empty docker kill ||:
|
||||||
|
docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||:
|
||||||
|
sudo rm -fr "$TEMP_PATH"
|
||||||
|
SQLancerTestDebug:
|
||||||
|
needs: [BuilderDebDebug]
|
||||||
|
runs-on: [self-hosted, fuzzer-unit-tester]
|
||||||
|
steps:
|
||||||
|
- name: Set envs
|
||||||
|
run: |
|
||||||
|
cat >> "$GITHUB_ENV" << 'EOF'
|
||||||
|
TEMP_PATH=${{runner.temp}}/sqlancer_debug
|
||||||
|
REPORTS_PATH=${{runner.temp}}/reports_dir
|
||||||
|
CHECK_NAME=SQLancer (debug)
|
||||||
|
REPO_COPY=${{runner.temp}}/sqlancer_debug/ClickHouse
|
||||||
|
EOF
|
||||||
|
- name: Download json reports
|
||||||
|
uses: actions/download-artifact@v2
|
||||||
|
with:
|
||||||
|
path: ${{ env.REPORTS_PATH }}
|
||||||
|
- name: Clear repository
|
||||||
|
run: |
|
||||||
|
sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE"
|
||||||
|
- name: Check out repository code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: SQLancer
|
||||||
|
run: |
|
||||||
|
sudo rm -fr "$TEMP_PATH"
|
||||||
|
mkdir -p "$TEMP_PATH"
|
||||||
|
cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH"
|
||||||
|
cd "$REPO_COPY/tests/ci"
|
||||||
|
python3 sqlancer_check.py "$CHECK_NAME"
|
||||||
|
- name: Cleanup
|
||||||
|
if: always()
|
||||||
|
run: |
|
||||||
|
docker ps --quiet | xargs --no-run-if-empty docker kill ||:
|
||||||
|
docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||:
|
||||||
|
sudo rm -fr "$TEMP_PATH"
|
||||||
FinishCheck:
|
FinishCheck:
|
||||||
needs:
|
needs:
|
||||||
- DockerHubPush
|
- DockerHubPush
|
||||||
- BuilderReport
|
- BuilderReport
|
||||||
|
- BuilderSpecialReport
|
||||||
|
- MarkReleaseReady
|
||||||
- FunctionalStatelessTestDebug0
|
- FunctionalStatelessTestDebug0
|
||||||
- FunctionalStatelessTestDebug1
|
- FunctionalStatelessTestDebug1
|
||||||
- FunctionalStatelessTestDebug2
|
- FunctionalStatelessTestDebug2
|
||||||
@ -3053,6 +3143,8 @@ jobs:
|
|||||||
- UnitTestsUBsan
|
- UnitTestsUBsan
|
||||||
- UnitTestsReleaseClang
|
- UnitTestsReleaseClang
|
||||||
- SharedBuildSmokeTest
|
- SharedBuildSmokeTest
|
||||||
|
- SQLancerTestRelease
|
||||||
|
- SQLancerTestDebug
|
||||||
runs-on: [self-hosted, style-checker]
|
runs-on: [self-hosted, style-checker]
|
||||||
steps:
|
steps:
|
||||||
- name: Clear repository
|
- name: Clear repository
|
||||||
|
3
.github/workflows/nightly.yml
vendored
@ -10,6 +10,9 @@ env:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
Debug:
|
||||||
|
# The task for having a preserved ENV and event.json for later investigation
|
||||||
|
uses: ./.github/workflows/debug.yml
|
||||||
DockerHubPushAarch64:
|
DockerHubPushAarch64:
|
||||||
runs-on: [self-hosted, style-checker-aarch64]
|
runs-on: [self-hosted, style-checker-aarch64]
|
||||||
steps:
|
steps:
|
||||||
|
75
.github/workflows/pull_request.yml
vendored
@ -3491,6 +3491,77 @@ jobs:
|
|||||||
docker ps --quiet | xargs --no-run-if-empty docker kill ||:
|
docker ps --quiet | xargs --no-run-if-empty docker kill ||:
|
||||||
docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||:
|
docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||:
|
||||||
sudo rm -fr "$TEMP_PATH"
|
sudo rm -fr "$TEMP_PATH"
|
||||||
|
##############################################################################################
|
||||||
|
###################################### SQLANCER FUZZERS ######################################
|
||||||
|
##############################################################################################
|
||||||
|
SQLancerTestRelease:
|
||||||
|
needs: [BuilderDebRelease]
|
||||||
|
runs-on: [self-hosted, fuzzer-unit-tester]
|
||||||
|
steps:
|
||||||
|
- name: Set envs
|
||||||
|
run: |
|
||||||
|
cat >> "$GITHUB_ENV" << 'EOF'
|
||||||
|
TEMP_PATH=${{runner.temp}}/sqlancer_release
|
||||||
|
REPORTS_PATH=${{runner.temp}}/reports_dir
|
||||||
|
CHECK_NAME=SQLancer (release)
|
||||||
|
REPO_COPY=${{runner.temp}}/sqlancer_release/ClickHouse
|
||||||
|
EOF
|
||||||
|
- name: Download json reports
|
||||||
|
uses: actions/download-artifact@v2
|
||||||
|
with:
|
||||||
|
path: ${{ env.REPORTS_PATH }}
|
||||||
|
- name: Clear repository
|
||||||
|
run: |
|
||||||
|
sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE"
|
||||||
|
- name: Check out repository code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: SQLancer
|
||||||
|
run: |
|
||||||
|
sudo rm -fr "$TEMP_PATH"
|
||||||
|
mkdir -p "$TEMP_PATH"
|
||||||
|
cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH"
|
||||||
|
cd "$REPO_COPY/tests/ci"
|
||||||
|
python3 sqlancer_check.py "$CHECK_NAME"
|
||||||
|
- name: Cleanup
|
||||||
|
if: always()
|
||||||
|
run: |
|
||||||
|
docker ps --quiet | xargs --no-run-if-empty docker kill ||:
|
||||||
|
docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||:
|
||||||
|
sudo rm -fr "$TEMP_PATH"
|
||||||
|
SQLancerTestDebug:
|
||||||
|
needs: [BuilderDebDebug]
|
||||||
|
runs-on: [self-hosted, fuzzer-unit-tester]
|
||||||
|
steps:
|
||||||
|
- name: Set envs
|
||||||
|
run: |
|
||||||
|
cat >> "$GITHUB_ENV" << 'EOF'
|
||||||
|
TEMP_PATH=${{runner.temp}}/sqlancer_debug
|
||||||
|
REPORTS_PATH=${{runner.temp}}/reports_dir
|
||||||
|
CHECK_NAME=SQLancer (debug)
|
||||||
|
REPO_COPY=${{runner.temp}}/sqlancer_debug/ClickHouse
|
||||||
|
EOF
|
||||||
|
- name: Download json reports
|
||||||
|
uses: actions/download-artifact@v2
|
||||||
|
with:
|
||||||
|
path: ${{ env.REPORTS_PATH }}
|
||||||
|
- name: Clear repository
|
||||||
|
run: |
|
||||||
|
sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE"
|
||||||
|
- name: Check out repository code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: SQLancer
|
||||||
|
run: |
|
||||||
|
sudo rm -fr "$TEMP_PATH"
|
||||||
|
mkdir -p "$TEMP_PATH"
|
||||||
|
cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH"
|
||||||
|
cd "$REPO_COPY/tests/ci"
|
||||||
|
python3 sqlancer_check.py "$CHECK_NAME"
|
||||||
|
- name: Cleanup
|
||||||
|
if: always()
|
||||||
|
run: |
|
||||||
|
docker ps --quiet | xargs --no-run-if-empty docker kill ||:
|
||||||
|
docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||:
|
||||||
|
sudo rm -fr "$TEMP_PATH"
|
||||||
#############################################################################################
|
#############################################################################################
|
||||||
###################################### JEPSEN TESTS #########################################
|
###################################### JEPSEN TESTS #########################################
|
||||||
#############################################################################################
|
#############################################################################################
|
||||||
@ -3501,7 +3572,6 @@ jobs:
|
|||||||
if: contains(github.event.pull_request.labels.*.name, 'jepsen-test')
|
if: contains(github.event.pull_request.labels.*.name, 'jepsen-test')
|
||||||
needs: [BuilderBinRelease]
|
needs: [BuilderBinRelease]
|
||||||
uses: ./.github/workflows/jepsen.yml
|
uses: ./.github/workflows/jepsen.yml
|
||||||
|
|
||||||
FinishCheck:
|
FinishCheck:
|
||||||
needs:
|
needs:
|
||||||
- StyleCheck
|
- StyleCheck
|
||||||
@ -3509,6 +3579,7 @@ jobs:
|
|||||||
- DockerServerImages
|
- DockerServerImages
|
||||||
- CheckLabels
|
- CheckLabels
|
||||||
- BuilderReport
|
- BuilderReport
|
||||||
|
- BuilderSpecialReport
|
||||||
- FastTest
|
- FastTest
|
||||||
- FunctionalStatelessTestDebug0
|
- FunctionalStatelessTestDebug0
|
||||||
- FunctionalStatelessTestDebug1
|
- FunctionalStatelessTestDebug1
|
||||||
@ -3576,6 +3647,8 @@ jobs:
|
|||||||
- SharedBuildSmokeTest
|
- SharedBuildSmokeTest
|
||||||
- CompatibilityCheck
|
- CompatibilityCheck
|
||||||
- IntegrationTestsFlakyCheck
|
- IntegrationTestsFlakyCheck
|
||||||
|
- SQLancerTestRelease
|
||||||
|
- SQLancerTestDebug
|
||||||
runs-on: [self-hosted, style-checker]
|
runs-on: [self-hosted, style-checker]
|
||||||
steps:
|
steps:
|
||||||
- name: Clear repository
|
- name: Clear repository
|
||||||
|
18
.github/workflows/release_branches.yml
vendored
@ -615,6 +615,23 @@ jobs:
|
|||||||
docker ps --quiet | xargs --no-run-if-empty docker kill ||:
|
docker ps --quiet | xargs --no-run-if-empty docker kill ||:
|
||||||
docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||:
|
docker ps --all --quiet | xargs --no-run-if-empty docker rm -f ||:
|
||||||
sudo rm -fr "$TEMP_PATH"
|
sudo rm -fr "$TEMP_PATH"
|
||||||
|
MarkReleaseReady:
|
||||||
|
needs:
|
||||||
|
- BuilderBinDarwin
|
||||||
|
- BuilderBinDarwinAarch64
|
||||||
|
- BuilderDebRelease
|
||||||
|
- BuilderDebAarch64
|
||||||
|
runs-on: [self-hosted, style-checker]
|
||||||
|
steps:
|
||||||
|
- name: Clear repository
|
||||||
|
run: |
|
||||||
|
sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE"
|
||||||
|
- name: Check out repository code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Mark Commit Release Ready
|
||||||
|
run: |
|
||||||
|
cd "$GITHUB_WORKSPACE/tests/ci"
|
||||||
|
python3 mark_release_ready.py
|
||||||
##############################################################################################
|
##############################################################################################
|
||||||
########################### FUNCTIONAl STATELESS TESTS #######################################
|
########################### FUNCTIONAl STATELESS TESTS #######################################
|
||||||
##############################################################################################
|
##############################################################################################
|
||||||
@ -1888,6 +1905,7 @@ jobs:
|
|||||||
- DockerServerImages
|
- DockerServerImages
|
||||||
- BuilderReport
|
- BuilderReport
|
||||||
- BuilderSpecialReport
|
- BuilderSpecialReport
|
||||||
|
- MarkReleaseReady
|
||||||
- FunctionalStatelessTestDebug0
|
- FunctionalStatelessTestDebug0
|
||||||
- FunctionalStatelessTestDebug1
|
- FunctionalStatelessTestDebug1
|
||||||
- FunctionalStatelessTestDebug2
|
- FunctionalStatelessTestDebug2
|
||||||
|
3
.gitignore
vendored
@ -154,3 +154,6 @@ website/package-lock.json
|
|||||||
/programs/server/metadata
|
/programs/server/metadata
|
||||||
/programs/server/store
|
/programs/server/store
|
||||||
|
|
||||||
|
# temporary test files
|
||||||
|
tests/queries/0_stateless/test_*
|
||||||
|
tests/queries/0_stateless/*.binary
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
#if defined(OS_LINUX)
|
#if defined(OS_LINUX)
|
||||||
# include <sys/syscall.h>
|
# include <sys/syscall.h>
|
||||||
#endif
|
#endif
|
||||||
|
#include <cstdlib>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <base/safeExit.h>
|
#include <base/safeExit.h>
|
||||||
|
#include <base/defines.h> /// for THREAD_SANITIZER
|
||||||
|
|
||||||
[[noreturn]] void safeExit(int code)
|
[[noreturn]] void safeExit(int code)
|
||||||
{
|
{
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
#include <link.h> // ElfW
|
#include <link.h> // ElfW
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "syscall.h"
|
||||||
|
|
||||||
#define ARRAY_SIZE(a) sizeof((a))/sizeof((a[0]))
|
#define ARRAY_SIZE(a) sizeof((a))/sizeof((a[0]))
|
||||||
|
|
||||||
/// Suppress TSan since it is possible for this code to be called from multiple threads,
|
/// Suppress TSan since it is possible for this code to be called from multiple threads,
|
||||||
@ -39,7 +41,9 @@ ssize_t __retry_read(int fd, void * buf, size_t count)
|
|||||||
{
|
{
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
ssize_t ret = read(fd, buf, count);
|
// We cannot use the read syscall as it will be intercept by sanitizers, which aren't
|
||||||
|
// initialized yet. Emit syscall directly.
|
||||||
|
ssize_t ret = __syscall_ret(__syscall(SYS_read, fd, buf, count));
|
||||||
if (ret == -1)
|
if (ret == -1)
|
||||||
{
|
{
|
||||||
if (errno == EINTR)
|
if (errno == EINTR)
|
||||||
@ -90,6 +94,11 @@ static unsigned long NO_SANITIZE_THREAD __auxv_init_procfs(unsigned long type)
|
|||||||
_Static_assert(sizeof(aux) < 4096, "Unexpected sizeof(aux)");
|
_Static_assert(sizeof(aux) < 4096, "Unexpected sizeof(aux)");
|
||||||
while (__retry_read(fd, &aux, sizeof(aux)) == sizeof(aux))
|
while (__retry_read(fd, &aux, sizeof(aux)) == sizeof(aux))
|
||||||
{
|
{
|
||||||
|
#if defined(__has_feature)
|
||||||
|
#if __has_feature(memory_sanitizer)
|
||||||
|
__msan_unpoison(&aux, sizeof(aux));
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
if (aux.a_type == AT_NULL)
|
if (aux.a_type == AT_NULL)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
|
@ -3,10 +3,20 @@ option (ENABLE_CLANG_TIDY "Use clang-tidy static analyzer" OFF)
|
|||||||
|
|
||||||
if (ENABLE_CLANG_TIDY)
|
if (ENABLE_CLANG_TIDY)
|
||||||
|
|
||||||
|
find_program (CLANG_TIDY_CACHE_PATH NAMES "clang-tidy-cache")
|
||||||
|
if (CLANG_TIDY_CACHE_PATH)
|
||||||
|
find_program (_CLANG_TIDY_PATH NAMES "clang-tidy" "clang-tidy-15" "clang-tidy-14" "clang-tidy-13" "clang-tidy-12")
|
||||||
|
|
||||||
|
# Why do we use ';' here?
|
||||||
|
# It's a cmake black magic: https://cmake.org/cmake/help/latest/prop_tgt/LANG_CLANG_TIDY.html#prop_tgt:%3CLANG%3E_CLANG_TIDY
|
||||||
|
# The CLANG_TIDY_PATH is passed to CMAKE_CXX_CLANG_TIDY, which follows CXX_CLANG_TIDY syntax.
|
||||||
|
set (CLANG_TIDY_PATH "${CLANG_TIDY_CACHE_PATH};${_CLANG_TIDY_PATH}" CACHE STRING "A combined command to run clang-tidy with caching wrapper")
|
||||||
|
else ()
|
||||||
find_program (CLANG_TIDY_PATH NAMES "clang-tidy" "clang-tidy-15" "clang-tidy-14" "clang-tidy-13" "clang-tidy-12")
|
find_program (CLANG_TIDY_PATH NAMES "clang-tidy" "clang-tidy-15" "clang-tidy-14" "clang-tidy-13" "clang-tidy-12")
|
||||||
|
endif ()
|
||||||
|
|
||||||
if (CLANG_TIDY_PATH)
|
if (CLANG_TIDY_PATH)
|
||||||
message(STATUS
|
message (STATUS
|
||||||
"Using clang-tidy: ${CLANG_TIDY_PATH}.
|
"Using clang-tidy: ${CLANG_TIDY_PATH}.
|
||||||
The checks will be run during build process.
|
The checks will be run during build process.
|
||||||
See the .clang-tidy file at the root directory to configure the checks.")
|
See the .clang-tidy file at the root directory to configure the checks.")
|
||||||
@ -15,11 +25,15 @@ if (ENABLE_CLANG_TIDY)
|
|||||||
|
|
||||||
# clang-tidy requires assertions to guide the analysis
|
# clang-tidy requires assertions to guide the analysis
|
||||||
# Note that NDEBUG is set implicitly by CMake for non-debug builds
|
# Note that NDEBUG is set implicitly by CMake for non-debug builds
|
||||||
set(COMPILER_FLAGS "${COMPILER_FLAGS} -UNDEBUG")
|
set (COMPILER_FLAGS "${COMPILER_FLAGS} -UNDEBUG")
|
||||||
|
|
||||||
# The variable CMAKE_CXX_CLANG_TIDY will be set inside src and base directories with non third-party code.
|
# The variable CMAKE_CXX_CLANG_TIDY will be set inside the following directories with non third-party code.
|
||||||
|
# - base
|
||||||
|
# - programs
|
||||||
|
# - src
|
||||||
|
# - utils
|
||||||
# set (CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_PATH}")
|
# set (CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_PATH}")
|
||||||
else ()
|
else ()
|
||||||
message(${RECONFIGURE_MESSAGE_LEVEL} "clang-tidy is not found")
|
message (${RECONFIGURE_MESSAGE_LEVEL} "clang-tidy is not found")
|
||||||
endif ()
|
endif ()
|
||||||
endif ()
|
endif ()
|
||||||
|
2
contrib/NuRaft
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 1be805e7cb2494aa8170015493474379b0362dfc
|
Subproject commit e4e746a24eb56861a86f3672771e3308d8c40722
|
@ -91,6 +91,9 @@ ENV PATH="$PATH:/usr/local/go/bin"
|
|||||||
ENV GOPATH=/workdir/go
|
ENV GOPATH=/workdir/go
|
||||||
ENV GOCACHE=/workdir/
|
ENV GOCACHE=/workdir/
|
||||||
|
|
||||||
|
RUN curl https://raw.githubusercontent.com/matus-chochlik/ctcache/7fd516e91c17779cbc6fc18bd119313d9532dd90/clang-tidy-cache -Lo /usr/bin/clang-tidy-cache \
|
||||||
|
&& chmod +x /usr/bin/clang-tidy-cache
|
||||||
|
|
||||||
RUN mkdir /workdir && chmod 777 /workdir
|
RUN mkdir /workdir && chmod 777 /workdir
|
||||||
WORKDIR /workdir
|
WORKDIR /workdir
|
||||||
|
|
||||||
|
@ -258,6 +258,10 @@ def parse_env_variables(
|
|||||||
if clang_tidy:
|
if clang_tidy:
|
||||||
# 15G is not enough for tidy build
|
# 15G is not enough for tidy build
|
||||||
cache_maxsize = "25G"
|
cache_maxsize = "25G"
|
||||||
|
|
||||||
|
# `CTCACHE_DIR` has the same purpose as the `CCACHE_DIR` above.
|
||||||
|
# It's there to have the clang-tidy cache embedded into our standard `CCACHE_DIR`
|
||||||
|
result.append("CTCACHE_DIR=/ccache/clang-tidy-cache")
|
||||||
result.append(f"CCACHE_MAXSIZE={cache_maxsize}")
|
result.append(f"CCACHE_MAXSIZE={cache_maxsize}")
|
||||||
|
|
||||||
if distcc_hosts:
|
if distcc_hosts:
|
||||||
@ -282,9 +286,7 @@ def parse_env_variables(
|
|||||||
cmake_flags.append("-DENABLE_TESTS=1")
|
cmake_flags.append("-DENABLE_TESTS=1")
|
||||||
|
|
||||||
if shared_libraries:
|
if shared_libraries:
|
||||||
cmake_flags.append(
|
cmake_flags.append("-DUSE_STATIC_LIBRARIES=0 -DSPLIT_SHARED_LIBRARIES=1")
|
||||||
"-DUSE_STATIC_LIBRARIES=0 -DSPLIT_SHARED_LIBRARIES=1"
|
|
||||||
)
|
|
||||||
# We can't always build utils because it requires too much space, but
|
# We can't always build utils because it requires too much space, but
|
||||||
# we have to build them at least in some way in CI. The shared library
|
# we have to build them at least in some way in CI. The shared library
|
||||||
# build is probably the least heavy disk-wise.
|
# build is probably the least heavy disk-wise.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# docker build -t clickhouse/sqlancer-test .
|
# docker build -t clickhouse/sqlancer-test .
|
||||||
FROM ubuntu:20.04
|
FROM ubuntu:22.04
|
||||||
|
|
||||||
# ARG for quick switch to a given ubuntu mirror
|
# ARG for quick switch to a given ubuntu mirror
|
||||||
ARG apt_archive="http://archive.ubuntu.com"
|
ARG apt_archive="http://archive.ubuntu.com"
|
||||||
|
@ -11,13 +11,15 @@ def process_result(result_folder):
|
|||||||
summary = []
|
summary = []
|
||||||
paths = []
|
paths = []
|
||||||
tests = [
|
tests = [
|
||||||
"TLPWhere",
|
"TLPAggregate",
|
||||||
|
"TLPDistinct",
|
||||||
"TLPGroupBy",
|
"TLPGroupBy",
|
||||||
"TLPHaving",
|
"TLPHaving",
|
||||||
|
"TLPWhere",
|
||||||
"TLPWhereGroupBy",
|
"TLPWhereGroupBy",
|
||||||
"TLPDistinct",
|
"NoREC",
|
||||||
"TLPAggregate",
|
|
||||||
]
|
]
|
||||||
|
failed_tests = []
|
||||||
|
|
||||||
for test in tests:
|
for test in tests:
|
||||||
err_path = "{}/{}.err".format(result_folder, test)
|
err_path = "{}/{}.err".format(result_folder, test)
|
||||||
@ -33,15 +35,11 @@ def process_result(result_folder):
|
|||||||
with open(err_path, "r") as f:
|
with open(err_path, "r") as f:
|
||||||
if "AssertionError" in f.read():
|
if "AssertionError" in f.read():
|
||||||
summary.append((test, "FAIL"))
|
summary.append((test, "FAIL"))
|
||||||
|
failed_tests.append(test)
|
||||||
status = "failure"
|
status = "failure"
|
||||||
else:
|
else:
|
||||||
summary.append((test, "OK"))
|
summary.append((test, "OK"))
|
||||||
|
|
||||||
logs_path = "{}/logs.tar.gz".format(result_folder)
|
|
||||||
if not os.path.exists(logs_path):
|
|
||||||
logging.info("No logs tar on path %s", logs_path)
|
|
||||||
else:
|
|
||||||
paths.append(logs_path)
|
|
||||||
stdout_path = "{}/stdout.log".format(result_folder)
|
stdout_path = "{}/stdout.log".format(result_folder)
|
||||||
if not os.path.exists(stdout_path):
|
if not os.path.exists(stdout_path):
|
||||||
logging.info("No stdout log on path %s", stdout_path)
|
logging.info("No stdout log on path %s", stdout_path)
|
||||||
@ -53,18 +51,23 @@ def process_result(result_folder):
|
|||||||
else:
|
else:
|
||||||
paths.append(stderr_path)
|
paths.append(stderr_path)
|
||||||
|
|
||||||
description = "SQLancer test run. See report"
|
description = "SQLancer run successfully"
|
||||||
|
if status == "failure":
|
||||||
|
description = f"Failed oracles: {failed_tests}"
|
||||||
|
|
||||||
return status, description, summary, paths
|
return status, description, summary, paths
|
||||||
|
|
||||||
|
|
||||||
def write_results(results_file, status_file, results, status):
|
def write_results(
|
||||||
|
results_file, status_file, description_file, results, status, description
|
||||||
|
):
|
||||||
with open(results_file, "w") as f:
|
with open(results_file, "w") as f:
|
||||||
out = csv.writer(f, delimiter="\t")
|
out = csv.writer(f, delimiter="\t")
|
||||||
out.writerows(results)
|
out.writerows(results)
|
||||||
with open(status_file, "w") as f:
|
with open(status_file, "w") as f:
|
||||||
out = csv.writer(f, delimiter="\t")
|
f.write(status + "\n")
|
||||||
out.writerow(status)
|
with open(description_file, "w") as f:
|
||||||
|
f.write(description + "\n")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
@ -72,13 +75,20 @@ if __name__ == "__main__":
|
|||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="ClickHouse script for parsing results of sqlancer test"
|
description="ClickHouse script for parsing results of sqlancer test"
|
||||||
)
|
)
|
||||||
parser.add_argument("--in-results-dir", default="/test_output/")
|
parser.add_argument("--in-results-dir", default="/workspace/")
|
||||||
parser.add_argument("--out-results-file", default="/test_output/test_results.tsv")
|
parser.add_argument("--out-results-file", default="/workspace/summary.tsv")
|
||||||
parser.add_argument("--out-status-file", default="/test_output/check_status.tsv")
|
parser.add_argument("--out-description-file", default="/workspace/description.txt")
|
||||||
|
parser.add_argument("--out-status-file", default="/workspace/status.txt")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
state, description, test_results, logs = process_result(args.in_results_dir)
|
status, description, summary, logs = process_result(args.in_results_dir)
|
||||||
logging.info("Result parsed")
|
logging.info("Result parsed")
|
||||||
status = (state, description)
|
write_results(
|
||||||
write_results(args.out_results_file, args.out_status_file, test_results, status)
|
args.out_results_file,
|
||||||
|
args.out_status_file,
|
||||||
|
args.out_description_file,
|
||||||
|
summary,
|
||||||
|
status,
|
||||||
|
description,
|
||||||
|
)
|
||||||
logging.info("Result written")
|
logging.info("Result written")
|
||||||
|
@ -1,33 +1,62 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
set -exu
|
||||||
|
trap "exit" INT TERM
|
||||||
|
|
||||||
set -e -x
|
function wget_with_retry
|
||||||
|
{
|
||||||
|
for _ in 1 2 3 4; do
|
||||||
|
if wget -nv -nd -c "$1";then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
sleep 0.5
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
dpkg -i package_folder/clickhouse-common-static_*.deb
|
if [ -z ${BINARY_URL_TO_DOWNLOAD+x} ]
|
||||||
dpkg -i package_folder/clickhouse-common-static-dbg_*.deb
|
then
|
||||||
dpkg -i package_folder/clickhouse-server_*.deb
|
echo "No BINARY_URL_TO_DOWNLOAD provided."
|
||||||
dpkg -i package_folder/clickhouse-client_*.deb
|
else
|
||||||
|
wget_with_retry "$BINARY_URL_TO_DOWNLOAD"
|
||||||
|
chmod +x /clickhouse
|
||||||
|
fi
|
||||||
|
|
||||||
service clickhouse-server start && sleep 5
|
if [[ -f "/clickhouse" ]]; then
|
||||||
|
echo "/clickhouse exists"
|
||||||
|
else
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd /workspace
|
||||||
|
/clickhouse server -P /workspace/clickhouse-server.pid -L /workspace/clickhouse-server.log -E /workspace/clickhouse-server.log.err --daemon
|
||||||
|
|
||||||
|
for _ in $(seq 1 60); do if [[ $(wget -q 'localhost:8123' -O-) == 'Ok.' ]]; then break ; else sleep 1; fi ; done
|
||||||
|
|
||||||
cd /sqlancer/sqlancer-master
|
cd /sqlancer/sqlancer-master
|
||||||
|
|
||||||
export TIMEOUT=300
|
TIMEOUT=300
|
||||||
export NUM_QUERIES=1000
|
NUM_QUERIES=1000
|
||||||
|
NUM_THREADS=10
|
||||||
|
TESTS=( "TLPGroupBy" "TLPHaving" "TLPWhere" "TLPDistinct" "TLPAggregate" "NoREC" )
|
||||||
|
echo "${TESTS[@]}"
|
||||||
|
|
||||||
( java -jar target/sqlancer-*.jar --num-threads 10 --timeout-seconds $TIMEOUT --num-queries $NUM_QUERIES --username default --password "" clickhouse --oracle TLPWhere | tee /test_output/TLPWhere.out ) 3>&1 1>&2 2>&3 | tee /test_output/TLPWhere.err
|
for TEST in "${TESTS[@]}"; do
|
||||||
( java -jar target/sqlancer-*.jar --num-threads 10 --timeout-seconds $TIMEOUT --num-queries $NUM_QUERIES --username default --password "" clickhouse --oracle TLPGroupBy | tee /test_output/TLPGroupBy.out ) 3>&1 1>&2 2>&3 | tee /test_output/TLPGroupBy.err
|
echo "$TEST"
|
||||||
( java -jar target/sqlancer-*.jar --num-threads 10 --timeout-seconds $TIMEOUT --num-queries $NUM_QUERIES --username default --password "" clickhouse --oracle TLPHaving | tee /test_output/TLPHaving.out ) 3>&1 1>&2 2>&3 | tee /test_output/TLPHaving.err
|
if [[ $(wget -q 'localhost:8123' -O-) == 'Ok.' ]]
|
||||||
( java -jar target/sqlancer-*.jar --num-threads 10 --timeout-seconds $TIMEOUT --num-queries $NUM_QUERIES --username default --password "" clickhouse --oracle TLPWhere --oracle TLPGroupBy | tee /test_output/TLPWhereGroupBy.out ) 3>&1 1>&2 2>&3 | tee /test_output/TLPWhereGroupBy.err
|
then
|
||||||
( java -jar target/sqlancer-*.jar --num-threads 10 --timeout-seconds $TIMEOUT --num-queries $NUM_QUERIES --username default --password "" clickhouse --oracle TLPDistinct | tee /test_output/TLPDistinct.out ) 3>&1 1>&2 2>&3 | tee /test_output/TLPDistinct.err
|
echo "Server is OK"
|
||||||
( java -jar target/sqlancer-*.jar --num-threads 10 --timeout-seconds $TIMEOUT --num-queries $NUM_QUERIES --username default --password "" clickhouse --oracle TLPAggregate | tee /test_output/TLPAggregate.out ) 3>&1 1>&2 2>&3 | tee /test_output/TLPAggregate.err
|
( java -jar target/sqlancer-*.jar --log-each-select true --print-failed false --num-threads "$NUM_THREADS" --timeout-seconds "$TIMEOUT" --num-queries "$NUM_QUERIES" --username default --password "" clickhouse --oracle "$TEST" | tee "/workspace/$TEST.out" ) 3>&1 1>&2 2>&3 | tee "/workspace/$TEST.err"
|
||||||
|
else
|
||||||
|
touch "/workspace/$TEST.err" "/workspace/$TEST.out"
|
||||||
|
echo "Server is not responding" | tee /workspace/server_crashed.log
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
service clickhouse stop
|
ls /workspace
|
||||||
|
pkill -F /workspace/clickhouse-server.pid || true
|
||||||
|
|
||||||
ls /var/log/clickhouse-server/
|
for _ in $(seq 1 60); do if [[ $(wget -q 'localhost:8123' -O-) == 'Ok.' ]]; then sleep 1 ; else break; fi ; done
|
||||||
tar czf /test_output/logs.tar.gz -C /var/log/clickhouse-server/ .
|
|
||||||
tail -n 1000 /var/log/clickhouse-server/stderr.log > /test_output/stderr.log
|
|
||||||
tail -n 1000 /var/log/clickhouse-server/stdout.log > /test_output/stdout.log
|
|
||||||
tail -n 1000 /var/log/clickhouse-server/clickhouse-server.log > /test_output/clickhouse-server.log
|
|
||||||
|
|
||||||
/process_sqlancer_result.py || echo -e "failure\tCannot parse results" > /test_output/check_status.tsv
|
/process_sqlancer_result.py || echo -e "failure\tCannot parse results" > /workspace/check_status.tsv
|
||||||
ls /test_output
|
ls /workspace
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# docker build -t clickhouse/style-test .
|
# docker build -t clickhouse/style-test .
|
||||||
FROM ubuntu:20.04
|
FROM ubuntu:20.04
|
||||||
ARG ACT_VERSION=0.2.25
|
ARG ACT_VERSION=0.2.33
|
||||||
ARG ACTIONLINT_VERSION=1.6.8
|
ARG ACTIONLINT_VERSION=1.6.22
|
||||||
|
|
||||||
# ARG for quick switch to a given ubuntu mirror
|
# ARG for quick switch to a given ubuntu mirror
|
||||||
ARG apt_archive="http://archive.ubuntu.com"
|
ARG apt_archive="http://archive.ubuntu.com"
|
||||||
|
@ -86,7 +86,7 @@ node1 :) SELECT materialize(hostName()) AS host, groupArray(n) FROM r.d GROUP BY
|
|||||||
|
|
||||||
``` text
|
``` text
|
||||||
┌─hosts─┬─groupArray(n)─┐
|
┌─hosts─┬─groupArray(n)─┐
|
||||||
│ node1 │ [1,3,5,7,9] │
|
│ node3 │ [1,3,5,7,9] │
|
||||||
│ node2 │ [0,2,4,6,8] │
|
│ node2 │ [0,2,4,6,8] │
|
||||||
└───────┴───────────────┘
|
└───────┴───────────────┘
|
||||||
```
|
```
|
||||||
|
@ -68,36 +68,57 @@ In the results of `SELECT` query, the values of `AggregateFunction` type have im
|
|||||||
|
|
||||||
## Example of an Aggregated Materialized View {#example-of-an-aggregated-materialized-view}
|
## Example of an Aggregated Materialized View {#example-of-an-aggregated-materialized-view}
|
||||||
|
|
||||||
`AggregatingMergeTree` materialized view that watches the `test.visits` table:
|
We will create the table `test.visits` that contain the raw data:
|
||||||
|
|
||||||
``` sql
|
``` sql
|
||||||
CREATE MATERIALIZED VIEW test.basic
|
CREATE TABLE test.visits
|
||||||
ENGINE = AggregatingMergeTree() PARTITION BY toYYYYMM(StartDate) ORDER BY (CounterID, StartDate)
|
(
|
||||||
|
StartDate DateTime64 NOT NULL,
|
||||||
|
CounterID UInt64,
|
||||||
|
Sign Nullable(Int32),
|
||||||
|
UserID Nullable(Int32)
|
||||||
|
) ENGINE = MergeTree ORDER BY (StartDate, CounterID);
|
||||||
|
```
|
||||||
|
|
||||||
|
`AggregatingMergeTree` materialized view that watches the `test.visits` table, and use the `AggregateFunction` type:
|
||||||
|
|
||||||
|
``` sql
|
||||||
|
CREATE MATERIALIZED VIEW test.mv_visits
|
||||||
|
(
|
||||||
|
StartDate DateTime64 NOT NULL,
|
||||||
|
CounterID UInt64,
|
||||||
|
Visits AggregateFunction(sum, Nullable(Int32)),
|
||||||
|
Users AggregateFunction(uniq, Nullable(Int32))
|
||||||
|
)
|
||||||
|
ENGINE = AggregatingMergeTree() ORDER BY (StartDate, CounterID)
|
||||||
AS SELECT
|
AS SELECT
|
||||||
CounterID,
|
|
||||||
StartDate,
|
StartDate,
|
||||||
|
CounterID,
|
||||||
sumState(Sign) AS Visits,
|
sumState(Sign) AS Visits,
|
||||||
uniqState(UserID) AS Users
|
uniqState(UserID) AS Users
|
||||||
FROM test.visits
|
FROM test.visits
|
||||||
GROUP BY CounterID, StartDate;
|
GROUP BY StartDate, CounterID;
|
||||||
```
|
```
|
||||||
|
|
||||||
Inserting data into the `test.visits` table.
|
Inserting data into the `test.visits` table.
|
||||||
|
|
||||||
``` sql
|
``` sql
|
||||||
INSERT INTO test.visits ...
|
INSERT INTO test.visits (StartDate, CounterID, Sign, UserID)
|
||||||
|
VALUES (1667446031, 1, 3, 4)
|
||||||
|
INSERT INTO test.visits (StartDate, CounterID, Sign, UserID)
|
||||||
|
VALUES (1667446031, 1, 6, 3)
|
||||||
```
|
```
|
||||||
|
|
||||||
The data are inserted in both the table and view `test.basic` that will perform the aggregation.
|
The data are inserted in both the table and the materialized view `test.mv_visits`.
|
||||||
|
|
||||||
To get the aggregated data, we need to execute a query such as `SELECT ... GROUP BY ...` from the view `test.basic`:
|
To get the aggregated data, we need to execute a query such as `SELECT ... GROUP BY ...` from the materialized view `test.mv_visits`:
|
||||||
|
|
||||||
``` sql
|
``` sql
|
||||||
SELECT
|
SELECT
|
||||||
StartDate,
|
StartDate,
|
||||||
sumMerge(Visits) AS Visits,
|
sumMerge(Visits) AS Visits,
|
||||||
uniqMerge(Users) AS Users
|
uniqMerge(Users) AS Users
|
||||||
FROM test.basic
|
FROM test.mv_visits
|
||||||
GROUP BY StartDate
|
GROUP BY StartDate
|
||||||
ORDER BY StartDate;
|
ORDER BY StartDate;
|
||||||
```
|
```
|
||||||
|
@ -4,25 +4,39 @@ sidebar_label: Cell Towers
|
|||||||
sidebar_position: 3
|
sidebar_position: 3
|
||||||
title: "Cell Towers"
|
title: "Cell Towers"
|
||||||
---
|
---
|
||||||
|
import ConnectionDetails from '@site/docs/en/_snippets/_gather_your_details_http.mdx';
|
||||||
|
|
||||||
import Tabs from '@theme/Tabs';
|
import Tabs from '@theme/Tabs';
|
||||||
import TabItem from '@theme/TabItem';
|
import TabItem from '@theme/TabItem';
|
||||||
import CodeBlock from '@theme/CodeBlock';
|
import CodeBlock from '@theme/CodeBlock';
|
||||||
import ActionsMenu from '@site/docs/en/_snippets/_service_actions_menu.md';
|
import ActionsMenu from '@site/docs/en/_snippets/_service_actions_menu.md';
|
||||||
import SQLConsoleDetail from '@site/docs/en/_snippets/_launch_sql_console.md';
|
import SQLConsoleDetail from '@site/docs/en/_snippets/_launch_sql_console.md';
|
||||||
|
import SupersetDocker from '@site/docs/en/_snippets/_add_superset_detail.md';
|
||||||
|
|
||||||
This dataset is from [OpenCellid](https://www.opencellid.org/) - The world's largest Open Database of Cell Towers.
|
## Goal
|
||||||
|
|
||||||
|
In this guide you will learn how to:
|
||||||
|
- Load the OpenCelliD data in Clickhouse
|
||||||
|
- Connect Apache Superset to ClickHouse
|
||||||
|
- Build a dashboard based on data available in the dataset
|
||||||
|
|
||||||
|
Here is a preview of the dashboard created in this guide:
|
||||||
|
|
||||||
|
![Dashboard of cell towers by radio type in mcc 204](@site/docs/en/getting-started/example-datasets/images/superset-cell-tower-dashboard.png)
|
||||||
|
|
||||||
|
## Get the Dataset {#get-the-dataset}
|
||||||
|
|
||||||
|
This dataset is from [OpenCelliD](https://www.opencellid.org/) - The world's largest Open Database of Cell Towers.
|
||||||
|
|
||||||
As of 2021, it contains more than 40 million records about cell towers (GSM, LTE, UMTS, etc.) around the world with their geographical coordinates and metadata (country code, network, etc).
|
As of 2021, it contains more than 40 million records about cell towers (GSM, LTE, UMTS, etc.) around the world with their geographical coordinates and metadata (country code, network, etc).
|
||||||
|
|
||||||
OpenCelliD Project is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License, and we redistribute a snapshot of this dataset under the terms of the same license. The up-to-date version of the dataset is available to download after sign in.
|
OpenCelliD Project is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License, and we redistribute a snapshot of this dataset under the terms of the same license. The up-to-date version of the dataset is available to download after sign in.
|
||||||
|
|
||||||
|
|
||||||
## Get the Dataset {#get-the-dataset}
|
|
||||||
|
|
||||||
<Tabs groupId="deployMethod">
|
<Tabs groupId="deployMethod">
|
||||||
<TabItem value="serverless" label="ClickHouse Cloud" default>
|
<TabItem value="serverless" label="ClickHouse Cloud" default>
|
||||||
|
|
||||||
|
### Load the sample data
|
||||||
|
|
||||||
ClickHouse Cloud provides an easy-button for uploading this dataset from S3. Log in to your ClickHouse Cloud organization, or create a free trial at [ClickHouse.cloud](https://clickhouse.cloud).
|
ClickHouse Cloud provides an easy-button for uploading this dataset from S3. Log in to your ClickHouse Cloud organization, or create a free trial at [ClickHouse.cloud](https://clickhouse.cloud).
|
||||||
<ActionsMenu menu="Load Data" />
|
<ActionsMenu menu="Load Data" />
|
||||||
|
|
||||||
@ -30,13 +44,33 @@ Choose the **Cell Towers** dataset from the **Sample data** tab, and **Load data
|
|||||||
|
|
||||||
![Load cell towers dataset](@site/docs/en/_snippets/images/cloud-load-data-sample.png)
|
![Load cell towers dataset](@site/docs/en/_snippets/images/cloud-load-data-sample.png)
|
||||||
|
|
||||||
Examine the schema of the cell_towers table:
|
### Examine the schema of the cell_towers table
|
||||||
```sql
|
```sql
|
||||||
DESCRIBE TABLE cell_towers
|
DESCRIBE TABLE cell_towers
|
||||||
```
|
```
|
||||||
|
|
||||||
<SQLConsoleDetail />
|
<SQLConsoleDetail />
|
||||||
|
|
||||||
|
This is the output of `DESCRIBE`. Down further in this guide the field type choices will be described.
|
||||||
|
```response
|
||||||
|
┌─name──────────┬─type──────────────────────────────────────────────────────────────────┬
|
||||||
|
│ radio │ Enum8('' = 0, 'CDMA' = 1, 'GSM' = 2, 'LTE' = 3, 'NR' = 4, 'UMTS' = 5) │
|
||||||
|
│ mcc │ UInt16 │
|
||||||
|
│ net │ UInt16 │
|
||||||
|
│ area │ UInt16 │
|
||||||
|
│ cell │ UInt64 │
|
||||||
|
│ unit │ Int16 │
|
||||||
|
│ lon │ Float64 │
|
||||||
|
│ lat │ Float64 │
|
||||||
|
│ range │ UInt32 │
|
||||||
|
│ samples │ UInt32 │
|
||||||
|
│ changeable │ UInt8 │
|
||||||
|
│ created │ DateTime │
|
||||||
|
│ updated │ DateTime │
|
||||||
|
│ averageSignal │ UInt8 │
|
||||||
|
└───────────────┴───────────────────────────────────────────────────────────────────────┴
|
||||||
|
```
|
||||||
|
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem value="selfmanaged" label="Self-managed">
|
<TabItem value="selfmanaged" label="Self-managed">
|
||||||
|
|
||||||
@ -86,7 +120,7 @@ clickhouse-client --query "INSERT INTO cell_towers FORMAT CSVWithNames" < cell_t
|
|||||||
</TabItem>
|
</TabItem>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
## Example queries {#examples}
|
## Run some example queries {#examples}
|
||||||
|
|
||||||
1. A number of cell towers by type:
|
1. A number of cell towers by type:
|
||||||
|
|
||||||
@ -127,13 +161,13 @@ SELECT mcc, count() FROM cell_towers GROUP BY mcc ORDER BY count() DESC LIMIT 10
|
|||||||
10 rows in set. Elapsed: 0.019 sec. Processed 43.28 million rows, 86.55 MB (2.33 billion rows/s., 4.65 GB/s.)
|
10 rows in set. Elapsed: 0.019 sec. Processed 43.28 million rows, 86.55 MB (2.33 billion rows/s., 4.65 GB/s.)
|
||||||
```
|
```
|
||||||
|
|
||||||
So, the top countries are: the USA, Germany, and Russia.
|
Based on the above query and the [MCC list](https://en.wikipedia.org/wiki/Mobile_country_code), the countries with the most cell towers are: the USA, Germany, and Russia.
|
||||||
|
|
||||||
You may want to create an [External Dictionary](../../sql-reference/dictionaries/external-dictionaries/external-dicts.md) in ClickHouse to decode these values.
|
You may want to create an [External Dictionary](../../sql-reference/dictionaries/external-dictionaries/external-dicts.md) in ClickHouse to decode these values.
|
||||||
|
|
||||||
## Use case: Incorporate geo data {#use-case}
|
## Use case: Incorporate geo data {#use-case}
|
||||||
|
|
||||||
Using `pointInPolygon` function.
|
Using the [`pointInPolygon`](/docs/en/sql-reference/functions/geo/coordinates.md/#pointinpolygon) function.
|
||||||
|
|
||||||
1. Create a table where we will store polygons:
|
1. Create a table where we will store polygons:
|
||||||
|
|
||||||
@ -224,6 +258,110 @@ WHERE pointInPolygon((lon, lat), (SELECT * FROM moscow))
|
|||||||
1 rows in set. Elapsed: 0.067 sec. Processed 43.28 million rows, 692.42 MB (645.83 million rows/s., 10.33 GB/s.)
|
1 rows in set. Elapsed: 0.067 sec. Processed 43.28 million rows, 692.42 MB (645.83 million rows/s., 10.33 GB/s.)
|
||||||
```
|
```
|
||||||
|
|
||||||
The data is also available for interactive queries in the [Playground](https://play.clickhouse.com/play?user=play), [example](https://play.clickhouse.com/play?user=play#U0VMRUNUIG1jYywgY291bnQoKSBGUk9NIGNlbGxfdG93ZXJzIEdST1VQIEJZIG1jYyBPUkRFUiBCWSBjb3VudCgpIERFU0M=).
|
## Review of the schema
|
||||||
|
|
||||||
Although you cannot create temporary tables there.
|
Before building visualizations in Superset have a look at the columns that you will use. This dataset primarily provides the location (Longitude and Latitude) and radio types at mobile cellular towers worldwide. The column descriptions can be found in the [community forum](https://community.opencellid.org/t/documenting-the-columns-in-the-downloadable-cells-database-csv/186). The columns used in the visualizations that will be built are described below
|
||||||
|
|
||||||
|
Here is a description of the columns taken from the OpenCelliD forum:
|
||||||
|
|
||||||
|
| Column | Description |
|
||||||
|
|--------------|--------------------------------------------------------|
|
||||||
|
| radio | Technology generation: CDMA, GSM, UMTS, 5G NR |
|
||||||
|
| mcc | Mobile Country Code: `204` is The Netherlands |
|
||||||
|
| lon | Longitude: With Latitude, approximate tower location |
|
||||||
|
| lat | Latitude: With Longitude, approximate tower location |
|
||||||
|
|
||||||
|
:::tip mcc
|
||||||
|
To find your MCC check [Mobile network codes](https://en.wikipedia.org/wiki/Mobile_country_code), and use the three digits in the **Mobile country code** column.
|
||||||
|
:::
|
||||||
|
|
||||||
|
The schema for this table was designed for compact storage on disk and query speed.
|
||||||
|
- The `radio` data is stored as an `Enum8` (`UInt8`) rather than a string.
|
||||||
|
- `mcc` or Mobile country code, is stored as a `UInt16` as we know the range is 1 - 999.
|
||||||
|
- `lon` and `lat` are `Float64`.
|
||||||
|
|
||||||
|
None of the other fields are used in the queries or visualizations in this guide, but they are described in the forum linked above if you are interested.
|
||||||
|
|
||||||
|
## Build visualizations with Apache Superset
|
||||||
|
|
||||||
|
Superset is easy to run from Docker. If you already have Superset running, all you need to do is add ClickHouse Connect with `pip install clickhouse-connect`. If you need to install Superset open the **Launch Apache Superset in Docker** directly below.
|
||||||
|
|
||||||
|
<SupersetDocker />
|
||||||
|
|
||||||
|
To build a Superset dashboard using the OpenCelliD dataset you should:
|
||||||
|
- Add your ClickHouse service as a Superset **database**
|
||||||
|
- Add the table **cell_towers** as a Superset **dataset**
|
||||||
|
- Create some **charts**
|
||||||
|
- Add the charts to a **dashboard**
|
||||||
|
|
||||||
|
### Add your ClickHouse service as a Superset database
|
||||||
|
|
||||||
|
<ConnectionDetails />
|
||||||
|
|
||||||
|
In Superset a database can be added by choosing the database type, and then providing the connection details. Open Superset and look for the **+**, it has a menu with **Data** and then **Connect database** options.
|
||||||
|
|
||||||
|
![Add a database](@site/docs/en/getting-started/example-datasets/images/superset-add.png)
|
||||||
|
|
||||||
|
Choose **ClickHouse Connect** from the list:
|
||||||
|
|
||||||
|
![Choose clickhouse connect as database type](@site/docs/en/getting-started/example-datasets/images/superset-choose-a-database.png)
|
||||||
|
|
||||||
|
:::note
|
||||||
|
If **ClickHouse Connect** is not one of your options, then you will need to install it. The comand is `pip install clickhouse-connect`, and more info is [available here](https://pypi.org/project/clickhouse-connect/).
|
||||||
|
:::
|
||||||
|
|
||||||
|
#### Add your connection details:
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
Make sure that you set **SSL** on when connecting to ClickHouse Cloud or other ClickHouse systems that enforce the use of SSL.
|
||||||
|
:::
|
||||||
|
|
||||||
|
![Add ClickHouse as a Superset datasource](@site/docs/en/getting-started/example-datasets/images/superset-connect-a-database.png)
|
||||||
|
|
||||||
|
### Add the table **cell_towers** as a Superset **dataset**
|
||||||
|
|
||||||
|
In Superset a **dataset** maps to a table within a database. Click on add a dataset and choose your ClickHouse service, the database containing your table (`default`), and choose the `cell_towers` table:
|
||||||
|
|
||||||
|
![Add cell_towers table as a dataset](@site/docs/en/getting-started/example-datasets/images/superset-add-dataset.png)
|
||||||
|
|
||||||
|
### Create some **charts**
|
||||||
|
|
||||||
|
When you choose to add a chart in Superset you have to specify the dataset (`cell_towers`) and the chart type. Since the OpenCelliD dataset provides longitude and latitude coordinates for cell towers we will create a **Map** chart. The **deck.gL Scatterplot** type is suited to this dataset as it works well with dense data points on a map.
|
||||||
|
|
||||||
|
![Create a map in Superset](@site/docs/en/getting-started/example-datasets/images/superset-create-map.png)
|
||||||
|
|
||||||
|
#### Specify the query used for the map
|
||||||
|
|
||||||
|
A deck.gl Scatterplot requires a longitude and latitude, and one or more filters can also be applied to the query. In this example two filters are applied, one for cell towers with UMTS radios, and one for the Mobile country code assigned to The Netherlands.
|
||||||
|
|
||||||
|
The fields `lon` and `lat` contain the longitude and latitude:
|
||||||
|
|
||||||
|
![Specify longitude and latitude fields](@site/docs/en/getting-started/example-datasets/images/superset-lon-lat.png)
|
||||||
|
|
||||||
|
Add a filter with `mcc` = `204` (or substitute any other `mcc` value):
|
||||||
|
|
||||||
|
![Filter on MCC 204](@site/docs/en/getting-started/example-datasets/images/superset-mcc-204.png)
|
||||||
|
|
||||||
|
Add a filter with `radio` = `'UMTS'` (or substitute any other `radio` value, you can see the choices in the output of `DESCRIBE TABLE cell_towers`):
|
||||||
|
|
||||||
|
![Filter on radio = UMTS](@site/docs/en/getting-started/example-datasets/images/superset-radio-umts.png)
|
||||||
|
|
||||||
|
This is the full configuration for the chart that filters on `radio = 'UMTS'` and `mcc = 204`:
|
||||||
|
|
||||||
|
![Chart for UMTS radios in MCC 204](@site/docs/en/getting-started/example-datasets/images/superset-umts-netherlands.png)
|
||||||
|
|
||||||
|
Click on **UPDATE CHART** to render the visualization.
|
||||||
|
|
||||||
|
### Add the charts to a **dashboard**
|
||||||
|
|
||||||
|
This screenshot shows cell tower locations with LTE, UMTS, and GSM radios. The charts are all created in the same way and they are added to a dashboard.
|
||||||
|
|
||||||
|
![Dashboard of cell towers by radio type in mcc 204](@site/docs/en/getting-started/example-datasets/images/superset-cell-tower-dashboard.png)
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
The data is also available for interactive queries in the [Playground](https://play.clickhouse.com/play?user=play).
|
||||||
|
|
||||||
|
This [example](https://play.clickhouse.com/play?user=play#U0VMRUNUIG1jYywgY291bnQoKSBGUk9NIGNlbGxfdG93ZXJzIEdST1VQIEJZIG1jYyBPUkRFUiBCWSBjb3VudCgpIERFU0M=) will populate the username and even the query for you.
|
||||||
|
|
||||||
|
Although you cannot create tables in the Playground, you can run all of the queries and even use Superset (adjust the hostname and port number).
|
||||||
|
:::
|
||||||
|
After Width: | Height: | Size: 35 KiB |
BIN
docs/en/getting-started/example-datasets/images/superset-add.png
Normal file
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 475 KiB |
After Width: | Height: | Size: 53 KiB |
After Width: | Height: | Size: 73 KiB |
After Width: | Height: | Size: 290 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 46 KiB |
@ -4,7 +4,7 @@ sidebar_label: Recipes Dataset
|
|||||||
title: "Recipes Dataset"
|
title: "Recipes Dataset"
|
||||||
---
|
---
|
||||||
|
|
||||||
RecipeNLG dataset is available for download [here](https://recipenlg.cs.put.poznan.pl/dataset). It contains 2.2 million recipes. The size is slightly less than 1 GB.
|
The RecipeNLG dataset is available for download [here](https://recipenlg.cs.put.poznan.pl/dataset). It contains 2.2 million recipes. The size is slightly less than 1 GB.
|
||||||
|
|
||||||
## Download and Unpack the Dataset
|
## Download and Unpack the Dataset
|
||||||
|
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
---
|
|
||||||
slug: /en/operations/troubleshooting
|
[//]: # (This file is included in FAQ > Troubleshooting)
|
||||||
sidebar_position: 46
|
|
||||||
sidebar_label: Troubleshooting
|
|
||||||
title: Troubleshooting
|
|
||||||
---
|
|
||||||
|
|
||||||
- [Installation](#troubleshooting-installation-errors)
|
- [Installation](#troubleshooting-installation-errors)
|
||||||
- [Connecting to the server](#troubleshooting-accepts-no-connections)
|
- [Connecting to the server](#troubleshooting-accepts-no-connections)
|
||||||
@ -28,18 +24,34 @@ sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 8919F6BD2B48D7
|
|||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
```
|
```
|
||||||
|
|
||||||
### You Get the Unsupported Architecture Warning with Apt-get {#you-get-the-unsupported-architecture-warning-with-apt-get}
|
### You Get Different Warnings with `apt-get update` {#you-get-different-warnings-with-apt-get-update}
|
||||||
|
|
||||||
- The completed warning message is as follows:
|
- The completed warning messages are as one of following:
|
||||||
|
|
||||||
```
|
```
|
||||||
N: Skipping acquire of configured file 'main/binary-i386/Packages' as repository 'https://packages.clickhouse.com/deb stable InRelease' doesn't support architecture 'i386'
|
N: Skipping acquire of configured file 'main/binary-i386/Packages' as repository 'https://packages.clickhouse.com/deb stable InRelease' doesn't support architecture 'i386'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
E: Failed to fetch https://packages.clickhouse.com/deb/dists/stable/main/binary-amd64/Packages.gz File has unexpected size (30451 != 28154). Mirror sync in progress?
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
E: Repository 'https://packages.clickhouse.com/deb stable InRelease' changed its 'Origin' value from 'Artifactory' to 'ClickHouse'
|
||||||
|
E: Repository 'https://packages.clickhouse.com/deb stable InRelease' changed its 'Label' value from 'Artifactory' to 'ClickHouse'
|
||||||
|
N: Repository 'https://packages.clickhouse.com/deb stable InRelease' changed its 'Suite' value from 'stable' to ''
|
||||||
|
N: This must be accepted explicitly before updates for this repository can be applied. See apt-secure(8) manpage for details.
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
Err:11 https://packages.clickhouse.com/deb stable InRelease
|
||||||
|
400 Bad Request [IP: 172.66.40.249 443]
|
||||||
|
```
|
||||||
|
|
||||||
To resolve the above issue, please use the following script:
|
To resolve the above issue, please use the following script:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo rm /var/lib/apt/lists/packages.clickhouse.com_* /var/lib/dpkg/arch
|
sudo rm /var/lib/apt/lists/packages.clickhouse.com_* /var/lib/dpkg/arch /var/lib/apt/lists/partial/packages.clickhouse.com_*
|
||||||
sudo apt-get clean
|
sudo apt-get clean
|
||||||
sudo apt-get autoclean
|
sudo apt-get autoclean
|
||||||
```
|
```
|
@ -126,7 +126,7 @@ clickhouse keeper --config /etc/your_path_to_config/config.xml
|
|||||||
|
|
||||||
ClickHouse Keeper also provides 4lw commands which are almost the same with Zookeeper. Each command is composed of four letters such as `mntr`, `stat` etc. There are some more interesting commands: `stat` gives some general information about the server and connected clients, while `srvr` and `cons` give extended details on server and connections respectively.
|
ClickHouse Keeper also provides 4lw commands which are almost the same with Zookeeper. Each command is composed of four letters such as `mntr`, `stat` etc. There are some more interesting commands: `stat` gives some general information about the server and connected clients, while `srvr` and `cons` give extended details on server and connections respectively.
|
||||||
|
|
||||||
The 4lw commands has a white list configuration `four_letter_word_white_list` which has default value `conf,cons,crst,envi,ruok,srst,srvr,stat,wchc,wchs,dirs,mntr,isro`.
|
The 4lw commands has a white list configuration `four_letter_word_white_list` which has default value `conf,cons,crst,envi,ruok,srst,srvr,stat,wchs,dirs,mntr,isro,rcvr,apiv,csnp,lgif`.
|
||||||
|
|
||||||
You can issue the commands to ClickHouse Keeper via telnet or nc, at the client port.
|
You can issue the commands to ClickHouse Keeper via telnet or nc, at the client port.
|
||||||
|
|
||||||
@ -309,7 +309,26 @@ Sessions with Ephemerals (1):
|
|||||||
/clickhouse/task_queue/ddl
|
/clickhouse/task_queue/ddl
|
||||||
```
|
```
|
||||||
|
|
||||||
## [experimental] Migration from ZooKeeper {#migration-from-zookeeper}
|
- `csnp`: Schedule a snapshot creation task. Return the last committed log index of the scheduled snapshot if success or `Failed to schedule snapshot creation task.` if failed. Note that `lgif` command can help you determine whether the snapshot is done.
|
||||||
|
|
||||||
|
```
|
||||||
|
100
|
||||||
|
```
|
||||||
|
|
||||||
|
- `lgif`: Keeper log information. `first_log_idx` : my first log index in log store; `first_log_term` : my first log term; `last_log_idx` : my last log index in log store; `last_log_term` : my last log term; `last_committed_log_idx` : my last committed log index in state machine; `leader_committed_log_idx` : leader's committed log index from my perspective; `target_committed_log_idx` : target log index should be committed to; `last_snapshot_idx` : the largest committed log index in last snapshot.
|
||||||
|
|
||||||
|
```
|
||||||
|
first_log_idx 1
|
||||||
|
first_log_term 1
|
||||||
|
last_log_idx 101
|
||||||
|
last_log_term 1
|
||||||
|
last_committed_log_idx 100
|
||||||
|
leader_committed_log_idx 101
|
||||||
|
target_committed_log_idx 101
|
||||||
|
last_snapshot_idx 50
|
||||||
|
```
|
||||||
|
|
||||||
|
## Migration from ZooKeeper {#migration-from-zookeeper}
|
||||||
|
|
||||||
Seamlessly migration from ZooKeeper to ClickHouse Keeper is impossible you have to stop your ZooKeeper cluster, convert data and start ClickHouse Keeper. `clickhouse-keeper-converter` tool allows converting ZooKeeper logs and snapshots to ClickHouse Keeper snapshot. It works only with ZooKeeper > 3.4. Steps for migration:
|
Seamlessly migration from ZooKeeper to ClickHouse Keeper is impossible you have to stop your ZooKeeper cluster, convert data and start ClickHouse Keeper. `clickhouse-keeper-converter` tool allows converting ZooKeeper logs and snapshots to ClickHouse Keeper snapshot. It works only with ZooKeeper > 3.4. Steps for migration:
|
||||||
|
|
||||||
|
@ -2939,7 +2939,7 @@ Possible values:
|
|||||||
- 0 — Projection optimization disabled.
|
- 0 — Projection optimization disabled.
|
||||||
- 1 — Projection optimization enabled.
|
- 1 — Projection optimization enabled.
|
||||||
|
|
||||||
Default value: `0`.
|
Default value: `1`.
|
||||||
|
|
||||||
## force_optimize_projection {#force-optimize-projection}
|
## force_optimize_projection {#force-optimize-projection}
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ Columns:
|
|||||||
- `DOUBLE_SHA1_PASSWORD`
|
- `DOUBLE_SHA1_PASSWORD`
|
||||||
- `LDAP`
|
- `LDAP`
|
||||||
- `KERBEROS`
|
- `KERBEROS`
|
||||||
|
- `SSL_CERTIFICATE`
|
||||||
- `profiles` ([Array](../../sql-reference/data-types/array.md)([LowCardinality(String)](../../sql-reference/data-types/lowcardinality.md))) — The list of profiles set for all roles and/or users.
|
- `profiles` ([Array](../../sql-reference/data-types/array.md)([LowCardinality(String)](../../sql-reference/data-types/lowcardinality.md))) — The list of profiles set for all roles and/or users.
|
||||||
- `roles` ([Array](../../sql-reference/data-types/array.md)([LowCardinality(String)](../../sql-reference/data-types/lowcardinality.md))) — The list of roles to which the profile is applied.
|
- `roles` ([Array](../../sql-reference/data-types/array.md)([LowCardinality(String)](../../sql-reference/data-types/lowcardinality.md))) — The list of roles to which the profile is applied.
|
||||||
- `settings` ([Array](../../sql-reference/data-types/array.md)([Tuple](../../sql-reference/data-types/tuple.md)([LowCardinality(String)](../../sql-reference/data-types/lowcardinality.md), [String](../../sql-reference/data-types/string.md)))) — Settings that were changed when the client logged in/out.
|
- `settings` ([Array](../../sql-reference/data-types/array.md)([Tuple](../../sql-reference/data-types/tuple.md)([LowCardinality(String)](../../sql-reference/data-types/lowcardinality.md), [String](../../sql-reference/data-types/string.md)))) — Settings that were changed when the client logged in/out.
|
||||||
|
@ -12,7 +12,7 @@ Columns:
|
|||||||
|
|
||||||
- `storage` ([String](../../sql-reference/data-types/string.md)) — Path to the storage of users. Configured in the `access_control_path` parameter.
|
- `storage` ([String](../../sql-reference/data-types/string.md)) — Path to the storage of users. Configured in the `access_control_path` parameter.
|
||||||
|
|
||||||
- `auth_type` ([Enum8](../../sql-reference/data-types/enum.md)('no_password' = 0,'plaintext_password' = 1, 'sha256_password' = 2, 'double_sha1_password' = 3)) — Shows the authentication type. There are multiple ways of user identification: with no password, with plain text password, with [SHA256](https://ru.wikipedia.org/wiki/SHA-2)-encoded password or with [double SHA-1](https://ru.wikipedia.org/wiki/SHA-1)-encoded password.
|
- `auth_type` ([Enum8](../../sql-reference/data-types/enum.md)('no_password' = 0,'plaintext_password' = 1, 'sha256_password' = 2, 'double_sha1_password' = 3, 'ldap' = 4, 'kerberos' = 5, 'ssl_certificate' = 6)) — Shows the authentication type. There are multiple ways of user identification: with no password, with plain text password, with [SHA256](https://ru.wikipedia.org/wiki/SHA-2)-encoded password or with [double SHA-1](https://ru.wikipedia.org/wiki/SHA-1)-encoded password.
|
||||||
|
|
||||||
- `auth_params` ([String](../../sql-reference/data-types/string.md)) — Authentication parameters in the JSON format depending on the `auth_type`.
|
- `auth_params` ([String](../../sql-reference/data-types/string.md)) — Authentication parameters in the JSON format depending on the `auth_type`.
|
||||||
|
|
||||||
|
@ -109,56 +109,38 @@ In the report you can find:
|
|||||||
|
|
||||||
`clickhouse-benchmark` can compare performances for two running ClickHouse servers.
|
`clickhouse-benchmark` can compare performances for two running ClickHouse servers.
|
||||||
|
|
||||||
To use the comparison mode, specify endpoints of both servers by two pairs of `--host`, `--port` keys. Keys matched together by position in arguments list, the first `--host` is matched with the first `--port` and so on. `clickhouse-benchmark` establishes connections to both servers, then sends queries. Each query addressed to a randomly selected server. The results are shown for each server separately.
|
To use the comparison mode, specify endpoints of both servers by two pairs of `--host`, `--port` keys. Keys matched together by position in arguments list, the first `--host` is matched with the first `--port` and so on. `clickhouse-benchmark` establishes connections to both servers, then sends queries. Each query addressed to a randomly selected server. The results are shown in a table.
|
||||||
|
|
||||||
## Example {#clickhouse-benchmark-example}
|
## Example {#clickhouse-benchmark-example}
|
||||||
|
|
||||||
``` bash
|
``` bash
|
||||||
$ echo "SELECT * FROM system.numbers LIMIT 10000000 OFFSET 10000000" | clickhouse-benchmark -i 10
|
$ echo "SELECT * FROM system.numbers LIMIT 10000000 OFFSET 10000000" | clickhouse-benchmark --host=localhost --port=9001 --host=localhost --port=9000 -i 10
|
||||||
```
|
```
|
||||||
|
|
||||||
``` text
|
``` text
|
||||||
Loaded 1 queries.
|
Loaded 1 queries.
|
||||||
|
|
||||||
Queries executed: 6.
|
Queries executed: 5.
|
||||||
|
|
||||||
localhost:9000, queries 6, QPS: 6.153, RPS: 123398340.957, MiB/s: 941.455, result RPS: 61532982.200, result MiB/s: 469.459.
|
localhost:9001, queries 2, QPS: 3.764, RPS: 75446929.370, MiB/s: 575.614, result RPS: 37639659.982, result MiB/s: 287.168.
|
||||||
|
localhost:9000, queries 3, QPS: 3.815, RPS: 76466659.385, MiB/s: 583.394, result RPS: 38148392.297, result MiB/s: 291.049.
|
||||||
|
|
||||||
0.000% 0.159 sec.
|
0.000% 0.258 sec. 0.250 sec.
|
||||||
10.000% 0.159 sec.
|
10.000% 0.258 sec. 0.250 sec.
|
||||||
20.000% 0.159 sec.
|
20.000% 0.258 sec. 0.250 sec.
|
||||||
30.000% 0.160 sec.
|
30.000% 0.258 sec. 0.267 sec.
|
||||||
40.000% 0.160 sec.
|
40.000% 0.258 sec. 0.267 sec.
|
||||||
50.000% 0.162 sec.
|
50.000% 0.273 sec. 0.267 sec.
|
||||||
60.000% 0.164 sec.
|
60.000% 0.273 sec. 0.267 sec.
|
||||||
70.000% 0.165 sec.
|
70.000% 0.273 sec. 0.267 sec.
|
||||||
80.000% 0.166 sec.
|
80.000% 0.273 sec. 0.269 sec.
|
||||||
90.000% 0.166 sec.
|
90.000% 0.273 sec. 0.269 sec.
|
||||||
95.000% 0.167 sec.
|
95.000% 0.273 sec. 0.269 sec.
|
||||||
99.000% 0.167 sec.
|
99.000% 0.273 sec. 0.269 sec.
|
||||||
99.900% 0.167 sec.
|
99.900% 0.273 sec. 0.269 sec.
|
||||||
99.990% 0.167 sec.
|
99.990% 0.273 sec. 0.269 sec.
|
||||||
|
|
||||||
|
No difference proven at 99.5% confidence
|
||||||
|
|
||||||
Queries executed: 10.
|
|
||||||
|
|
||||||
localhost:9000, queries 10, QPS: 6.082, RPS: 121959604.568, MiB/s: 930.478, result RPS: 60815551.642, result MiB/s: 463.986.
|
|
||||||
|
|
||||||
0.000% 0.159 sec.
|
|
||||||
10.000% 0.159 sec.
|
|
||||||
20.000% 0.160 sec.
|
|
||||||
30.000% 0.163 sec.
|
|
||||||
40.000% 0.164 sec.
|
|
||||||
50.000% 0.165 sec.
|
|
||||||
60.000% 0.166 sec.
|
|
||||||
70.000% 0.166 sec.
|
|
||||||
80.000% 0.167 sec.
|
|
||||||
90.000% 0.167 sec.
|
|
||||||
95.000% 0.170 sec.
|
|
||||||
99.000% 0.172 sec.
|
|
||||||
99.900% 0.172 sec.
|
|
||||||
99.990% 0.172 sec.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
[Original article](https://clickhouse.com/docs/en/operations/utilities/clickhouse-benchmark.md) <!--hide-->
|
[Original article](https://clickhouse.com/docs/en/operations/utilities/clickhouse-benchmark.md) <!--hide-->
|
||||||
|
@ -303,17 +303,25 @@ or
|
|||||||
CREATE DICTIONARY somedict (
|
CREATE DICTIONARY somedict (
|
||||||
id UInt64,
|
id UInt64,
|
||||||
first Date,
|
first Date,
|
||||||
last Date
|
last Date,
|
||||||
|
advertiser_id UInt64
|
||||||
)
|
)
|
||||||
PRIMARY KEY id
|
PRIMARY KEY id
|
||||||
|
SOURCE(CLICKHOUSE(TABLE 'date_table'))
|
||||||
|
LIFETIME(MIN 1 MAX 1000)
|
||||||
LAYOUT(RANGE_HASHED())
|
LAYOUT(RANGE_HASHED())
|
||||||
RANGE(MIN first MAX last)
|
RANGE(MIN first MAX last)
|
||||||
```
|
```
|
||||||
|
|
||||||
To work with these dictionaries, you need to pass an additional argument to the `dictGetT` function, for which a range is selected:
|
To work with these dictionaries, you need to pass an additional argument to the `dictGet` function, for which a range is selected:
|
||||||
|
|
||||||
``` sql
|
``` sql
|
||||||
dictGetT('dict_name', 'attr_name', id, date)
|
dictGet('dict_name', 'attr_name', id, date)
|
||||||
|
```
|
||||||
|
Query example:
|
||||||
|
|
||||||
|
``` sql
|
||||||
|
SELECT dictGet('somedict', 'advertiser_id', 1, '2022-10-20 23:20:10.000'::DateTime64::UInt64);
|
||||||
```
|
```
|
||||||
|
|
||||||
This function returns the value for the specified `id`s and the date range that includes the passed date.
|
This function returns the value for the specified `id`s and the date range that includes the passed date.
|
||||||
|
@ -8,70 +8,69 @@ title: "Geo Functions"
|
|||||||
|
|
||||||
## Geographical Coordinates Functions
|
## Geographical Coordinates Functions
|
||||||
|
|
||||||
- [greatCircleDistance](./coordinates.md#greatCircleDistance)
|
- [greatCircleDistance](./coordinates.md#greatcircledistance)
|
||||||
- [geoDistance](./coordinates.md#geoDistance)
|
- [geoDistance](./coordinates.md#geodistance)
|
||||||
- [greatCircleAngle](./coordinates.md#greatCircleAngle)
|
- [greatCircleAngle](./coordinates.md#greatcircleangle)
|
||||||
- [pointInEllipses](./coordinates.md#pointInEllipses)
|
- [pointInEllipses](./coordinates.md#pointinellipses)
|
||||||
- [pointInPolygon](./coordinates.md#pointInPolygon)
|
- [pointInPolygon](./coordinates.md#pointinpolygon)
|
||||||
|
|
||||||
## Geohash Functions
|
## Geohash Functions
|
||||||
- [geohashEncode](./geohash.md#geohashEncode)
|
- [geohashEncode](./geohash.md#geohashencode)
|
||||||
- [geohashDecode](./geohash.md#geohashDecode)
|
- [geohashDecode](./geohash.md#geohashdecode)
|
||||||
- [geohashesInBox](./geohash.md#geohashesInBox)
|
- [geohashesInBox](./geohash.md#geohashesinbox)
|
||||||
|
|
||||||
## H3 Indexes Functions
|
## H3 Indexes Functions
|
||||||
|
|
||||||
- [h3IsValid](./h3.md#h3IsValid)
|
- [h3IsValid](./h3.md#h3isvalid)
|
||||||
- [h3GetResolution](./h3.md#h3GetResolution)
|
- [h3GetResolution](./h3.md#h3getresolution)
|
||||||
- [h3EdgeAngle](./h3.md#h3EdgeAngle)
|
- [h3EdgeAngle](./h3.md#h3edgeangle)
|
||||||
- [h3EdgeLengthM](./h3.md#h3EdgeLengthM)
|
- [h3EdgeLengthM](./h3.md#h3edgelengthm)
|
||||||
- [h3EdgeLengthKm](./h3.md#h3EdgeLengthKm)
|
- [h3EdgeLengthKm](./h3.md#h3edgelengthkm)
|
||||||
- [geoToH3](./h3.md#geoToH3)
|
- [geoToH3](./h3.md#geotoh3)
|
||||||
- [h3ToGeo](./h3.md#h3ToGeo)
|
- [h3ToGeo](./h3.md#h3togeo)
|
||||||
- [h3ToGeoBoundary](./h3.md#h3ToGeoBoundary)
|
- [h3ToGeoBoundary](./h3.md#h3togeoboundary)
|
||||||
- [h3kRing](./h3.md#h3kRing)
|
- [h3kRing](./h3.md#h3kring)
|
||||||
- [h3GetBaseCell](./h3.md#h3GetBaseCell)
|
- [h3GetBaseCell](./h3.md#h3getbasecell)
|
||||||
- [h3HexAreaM2](./h3.md#h3HexAreaM2)
|
- [h3HexAreaM2](./h3.md#h3hexaream2)
|
||||||
- [h3HexAreaKm2](./h3.md#h3HexAreaKm2)
|
- [h3HexAreaKm2](./h3.md#h3hexareakm2)
|
||||||
- [h3IndexesAreNeighbors](./h3.md#h3IndexesAreNeighbors)
|
- [h3IndexesAreNeighbors](./h3.md#h3indexesareneighbors)
|
||||||
- [h3ToChildren](./h3.md#h3ToChildren)
|
- [h3ToChildren](./h3.md#h3tochildren)
|
||||||
- [h3ToParent](./h3.md#h3ToParent)
|
- [h3ToParent](./h3.md#h3toparent)
|
||||||
- [h3ToString](./h3.md#h3ToString)
|
- [h3ToString](./h3.md#h3tostring)
|
||||||
- [stringToH3](./h3.md#stringToH3)
|
- [stringToH3](./h3.md#stringtoh3)
|
||||||
- [h3GetResolution](./h3.md#h3GetResolution)
|
- [h3GetResolution](./h3.md#h3getresolution)
|
||||||
- [h3IsResClassIII](./h3.md#h3IsResClassIII)
|
- [h3IsResClassIII](./h3.md#h3isresclassiii)
|
||||||
- [h3IsPentagon](./h3.md#h3IsPentagon)
|
- [h3IsPentagon](./h3.md#h3ispentagon)
|
||||||
- [h3GetFaces](./h3.md#h3GetFaces)
|
- [h3GetFaces](./h3.md#h3getfaces)
|
||||||
- [h3CellAreaM2](./h3.md#h3CellAreaM2)
|
- [h3CellAreaM2](./h3.md#h3cellaream2)
|
||||||
- [h3CellAreaRads2](./h3.md#h3CellAreaRads2)
|
- [h3CellAreaRads2](./h3.md#h3cellarearads2)
|
||||||
- [h3ToCenterChild](./h3.md#h3ToCenterChild)
|
- [h3ToCenterChild](./h3.md#h3tocenterchild)
|
||||||
- [h3ExactEdgeLengthM](./h3.md#h3ExactEdgeLengthM)
|
- [h3ExactEdgeLengthM](./h3.md#h3exactedgelengthm)
|
||||||
- [h3ExactEdgeLengthKm](./h3.md#h3ExactEdgeLengthKm)
|
- [h3ExactEdgeLengthKm](./h3.md#h3exactedgelengthkm)
|
||||||
- [h3ExactEdgeLengthRads](./h3.md#h3ExactEdgeLengthRads)
|
- [h3ExactEdgeLengthRads](./h3.md#h3exactedgelengthrads)
|
||||||
- [h3NumHexagons](./h3.md#h3NumHexagons)
|
- [h3NumHexagons](./h3.md#h3numhexagons)
|
||||||
- [h3Line](./h3.md#h3Line)
|
- [h3Line](./h3.md#h3line)
|
||||||
- [h3Distance](./h3.md#h3Distance)
|
- [h3Distance](./h3.md#h3distance)
|
||||||
- [h3HexRing](./h3.md#h3HexRing)
|
- [h3HexRing](./h3.md#h3hexring)
|
||||||
- [h3GetUnidirectionalEdge](./h3.md#h3GetUnidirectionalEdge)
|
- [h3GetUnidirectionalEdge](./h3.md#h3getunidirectionaledge)
|
||||||
- [h3UnidirectionalEdgeIsValid](./h3.md#h3UnidirectionalEdgeIsValid)
|
- [h3UnidirectionalEdgeIsValid](./h3.md#h3unidirectionaledgeisvalid)
|
||||||
- [h3GetOriginIndexFromUnidirectionalEdge](./h3.md#h3GetOriginIndexFromUnidirectionalEdge)
|
- [h3GetOriginIndexFromUnidirectionalEdge](./h3.md#h3getoriginindexfromunidirectionaledge)
|
||||||
- [h3GetDestinationIndexFromUnidirectionalEdge](./h3.md#h3GetDestinationIndexFromUnidirectionalEdge)
|
- [h3GetDestinationIndexFromUnidirectionalEdge](./h3.md#h3getdestinationindexfromunidirectionaledge)
|
||||||
- [h3GetIndexesFromUnidirectionalEdge](./h3.md#h3GetIndexesFromUnidirectionalEdge)
|
- [h3GetIndexesFromUnidirectionalEdge](./h3.md#h3getindexesfromunidirectionaledge)
|
||||||
- [h3GetUnidirectionalEdgesFromHexagon](./h3.md#h3GetUnidirectionalEdgesFromHexagon)
|
- [h3GetUnidirectionalEdgesFromHexagon](./h3.md#h3getunidirectionaledgesfromhexagon)
|
||||||
- [h3GetUnidirectionalEdgeBoundary](./h3.md#h3GetUnidirectionalEdgeBoundary)
|
- [h3GetUnidirectionalEdgeBoundary](./h3.md#h3getunidirectionaledgeboundary)
|
||||||
|
|
||||||
## S2 Index Functions
|
## S2 Index Functions
|
||||||
|
|
||||||
- [geoToS2](./s2.md#geoToS2)
|
- [geoToS2](./s2.md#geotos2)
|
||||||
- [s2ToGeo](./s2.md#s2ToGeo)
|
- [s2ToGeo](./s2.md#s2togeo)
|
||||||
- [s2GetNeighbors](./s2.md#s2GetNeighbors)
|
- [s2GetNeighbors](./s2.md#s2getneighbors)
|
||||||
- [s2CellsIntersect](./s2.md#s2CellsIntersect)
|
- [s2CellsIntersect](./s2.md#s2cellsintersect)
|
||||||
- [s2CapContains](./s2.md#s2CapContains)
|
- [s2CapContains](./s2.md#s2capcontains)
|
||||||
- [s2CapUnion](./s2.md#s2CapUnion)
|
- [s2CapUnion](./s2.md#s2capunion)
|
||||||
- [s2RectAdd](./s2.md#s2RectAdd)
|
- [s2RectAdd](./s2.md#s2rectadd)
|
||||||
- [s2RectContains](./s2.md#s2RectContains)
|
- [s2RectContains](./s2.md#s2rectcontains)
|
||||||
- [s2RectUinion](./s2.md#s2RectUinion)
|
- [s2RectUnion](./s2.md#s2rectunion)
|
||||||
- [s2RectIntersection](./s2.md#s2RectIntersection)
|
- [s2RectIntersection](./s2.md#s2rectintersection)
|
||||||
|
|
||||||
|
|
||||||
[Original article](https://clickhouse.com/docs/en/sql-reference/functions/geo/) <!--hide-->
|
|
||||||
|
@ -593,6 +593,27 @@ LIMIT 10
|
|||||||
└────────────────┴─────────┘
|
└────────────────┴─────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## formatReadableDecimalSize(x)
|
||||||
|
|
||||||
|
Accepts the size (number of bytes). Returns a rounded size with a suffix (KB, MB, etc.) as a string.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
``` sql
|
||||||
|
SELECT
|
||||||
|
arrayJoin([1, 1024, 1024*1024, 192851925]) AS filesize_bytes,
|
||||||
|
formatReadableDecimalSize(filesize_bytes) AS filesize
|
||||||
|
```
|
||||||
|
|
||||||
|
``` text
|
||||||
|
┌─filesize_bytes─┬─filesize───┐
|
||||||
|
│ 1 │ 1.00 B │
|
||||||
|
│ 1024 │ 1.02 KB │
|
||||||
|
│ 1048576 │ 1.05 MB │
|
||||||
|
│ 192851925 │ 192.85 MB │
|
||||||
|
└────────────────┴────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
## formatReadableSize(x)
|
## formatReadableSize(x)
|
||||||
|
|
||||||
Accepts the size (number of bytes). Returns a rounded size with a suffix (KiB, MiB, etc.) as a string.
|
Accepts the size (number of bytes). Returns a rounded size with a suffix (KiB, MiB, etc.) as a string.
|
||||||
|
@ -571,13 +571,13 @@ Similar to base58Decode, but returns an empty string in case of error.
|
|||||||
|
|
||||||
## base64Encode(s)
|
## base64Encode(s)
|
||||||
|
|
||||||
Encodes ‘s’ string into base64
|
Encodes ‘s’ FixedString or String into base64.
|
||||||
|
|
||||||
Alias: `TO_BASE64`.
|
Alias: `TO_BASE64`.
|
||||||
|
|
||||||
## base64Decode(s)
|
## base64Decode(s)
|
||||||
|
|
||||||
Decode base64-encoded string ‘s’ into original string. In case of failure raises an exception.
|
Decode base64-encoded FixedString or String ‘s’ into original string. In case of failure raises an exception.
|
||||||
|
|
||||||
Alias: `FROM_BASE64`.
|
Alias: `FROM_BASE64`.
|
||||||
|
|
||||||
@ -1150,3 +1150,13 @@ A text with tags .
|
|||||||
The content within <b>CDATA</b>
|
The content within <b>CDATA</b>
|
||||||
Do Nothing for 2 Minutes 2:00
|
Do Nothing for 2 Minutes 2:00
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## ascii(s) {#ascii}
|
||||||
|
|
||||||
|
Returns the ASCII code point of the first character of str. The result type is Int32.
|
||||||
|
|
||||||
|
If s is empty, the result is 0. If the first character is not an ASCII character or not part of the Latin-1 Supplement range of UTF-16, the result is undefined.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,22 +12,23 @@ Functions for [searching](../../sql-reference/functions/string-search-functions.
|
|||||||
|
|
||||||
## replaceOne(haystack, pattern, replacement)
|
## replaceOne(haystack, pattern, replacement)
|
||||||
|
|
||||||
Replaces the first occurrence, if it exists, of the ‘pattern’ substring in ‘haystack’ with the ‘replacement’ substring.
|
Replaces the first occurrence of the substring ‘pattern’ (if it exists) in ‘haystack’ by the ‘replacement’ string.
|
||||||
Hereafter, ‘pattern’ and ‘replacement’ must be constants.
|
‘pattern’ and ‘replacement’ must be constants.
|
||||||
|
|
||||||
## replaceAll(haystack, pattern, replacement), replace(haystack, pattern, replacement)
|
## replaceAll(haystack, pattern, replacement), replace(haystack, pattern, replacement)
|
||||||
|
|
||||||
Replaces all occurrences of the ‘pattern’ substring in ‘haystack’ with the ‘replacement’ substring.
|
Replaces all occurrences of the substring ‘pattern’ in ‘haystack’ by the ‘replacement’ string.
|
||||||
|
|
||||||
## replaceRegexpOne(haystack, pattern, replacement)
|
## replaceRegexpOne(haystack, pattern, replacement)
|
||||||
|
|
||||||
Replacement using the ‘pattern’ regular expression. A re2 regular expression.
|
Replaces the first occurrence of the substring matching the regular expression ‘pattern’ in ‘haystack‘ by the ‘replacement‘ string.
|
||||||
Replaces only the first occurrence, if it exists.
|
‘pattern‘ must be a constant [re2 regular expression](https://github.com/google/re2/wiki/Syntax).
|
||||||
A pattern can be specified as ‘replacement’. This pattern can include substitutions `\0-\9`.
|
‘replacement’ must be a plain constant string or a constant string containing substitutions `\0-\9`.
|
||||||
The substitution `\0` includes the entire regular expression. Substitutions `\1-\9` correspond to the subpattern numbers.To use the `\` character in a template, escape it using `\`.
|
Substitutions `\1-\9` correspond to the 1st to 9th capturing group (submatch), substitution `\0` corresponds to the entire match.
|
||||||
Also keep in mind that a string literal requires an extra escape.
|
To use a verbatim `\` character in the ‘pattern‘ or ‘replacement‘ string, escape it using `\`.
|
||||||
|
Also keep in mind that string literals require an extra escaping.
|
||||||
|
|
||||||
Example 1. Converting the date to American format:
|
Example 1. Converting ISO dates to American format:
|
||||||
|
|
||||||
``` sql
|
``` sql
|
||||||
SELECT DISTINCT
|
SELECT DISTINCT
|
||||||
@ -62,7 +63,7 @@ SELECT replaceRegexpOne('Hello, World!', '.*', '\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0')
|
|||||||
|
|
||||||
## replaceRegexpAll(haystack, pattern, replacement)
|
## replaceRegexpAll(haystack, pattern, replacement)
|
||||||
|
|
||||||
This does the same thing, but replaces all the occurrences. Example:
|
Like ‘replaceRegexpOne‘, but replaces all occurrences of the pattern. Example:
|
||||||
|
|
||||||
``` sql
|
``` sql
|
||||||
SELECT replaceRegexpAll('Hello, World!', '.', '\\0\\0') AS res
|
SELECT replaceRegexpAll('Hello, World!', '.', '\\0\\0') AS res
|
||||||
|
@ -7,18 +7,26 @@ title: "Manipulating Projections"
|
|||||||
|
|
||||||
The following operations with [projections](../../../engines/table-engines/mergetree-family/mergetree.md#projections) are available:
|
The following operations with [projections](../../../engines/table-engines/mergetree-family/mergetree.md#projections) are available:
|
||||||
|
|
||||||
- `ALTER TABLE [db].name ADD PROJECTION name ( SELECT <COLUMN LIST EXPR> [GROUP BY] [ORDER BY] )` - Adds projection description to tables metadata.
|
## ADD PROJECTION
|
||||||
|
|
||||||
- `ALTER TABLE [db].name DROP PROJECTION name` - Removes projection description from tables metadata and deletes projection files from disk. Implemented as a [mutation](../../../sql-reference/statements/alter/index.md#mutations).
|
`ALTER TABLE [db].name ADD PROJECTION name ( SELECT <COLUMN LIST EXPR> [GROUP BY] [ORDER BY] )` - Adds projection description to tables metadata.
|
||||||
|
|
||||||
- `ALTER TABLE [db.]table MATERIALIZE PROJECTION name IN PARTITION partition_name` - The query rebuilds the projection `name` in the partition `partition_name`. Implemented as a [mutation](../../../sql-reference/statements/alter/index.md#mutations).
|
## DROP PROJECTION
|
||||||
|
|
||||||
- `ALTER TABLE [db.]table CLEAR PROJECTION name IN PARTITION partition_name` - Deletes projection files from disk without removing description. Implemented as a [mutation](../../../sql-reference/statements/alter/index.md#mutations).
|
`ALTER TABLE [db].name DROP PROJECTION name` - Removes projection description from tables metadata and deletes projection files from disk. Implemented as a [mutation](../../../sql-reference/statements/alter/index.md#mutations).
|
||||||
|
|
||||||
|
## MATERIALIZE PROJECTION
|
||||||
|
|
||||||
|
`ALTER TABLE [db.]table MATERIALIZE PROJECTION name IN PARTITION partition_name` - The query rebuilds the projection `name` in the partition `partition_name`. Implemented as a [mutation](../../../sql-reference/statements/alter/index.md#mutations).
|
||||||
|
|
||||||
|
## CLEAR PROJECTION
|
||||||
|
|
||||||
|
`ALTER TABLE [db.]table CLEAR PROJECTION name IN PARTITION partition_name` - Deletes projection files from disk without removing description. Implemented as a [mutation](../../../sql-reference/statements/alter/index.md#mutations).
|
||||||
|
|
||||||
|
|
||||||
The commands `ADD`, `DROP` and `CLEAR` are lightweight in a sense that they only change metadata or remove files.
|
The commands `ADD`, `DROP` and `CLEAR` are lightweight in a sense that they only change metadata or remove files.
|
||||||
|
|
||||||
Also, they are replicated, syncing projections metadata via ZooKeeper.
|
Also, they are replicated, syncing projections metadata via ClickHouse Keeper or ZooKeeper.
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
Projection manipulation is supported only for tables with [`*MergeTree`](../../../engines/table-engines/mergetree-family/mergetree.md) engine (including [replicated](../../../engines/table-engines/mergetree-family/replication.md) variants).
|
Projection manipulation is supported only for tables with [`*MergeTree`](../../../engines/table-engines/mergetree-family/mergetree.md) engine (including [replicated](../../../engines/table-engines/mergetree-family/replication.md) variants).
|
||||||
|
@ -12,7 +12,7 @@ Syntax:
|
|||||||
``` sql
|
``` sql
|
||||||
ALTER USER [IF EXISTS] name1 [ON CLUSTER cluster_name1] [RENAME TO new_name1]
|
ALTER USER [IF EXISTS] name1 [ON CLUSTER cluster_name1] [RENAME TO new_name1]
|
||||||
[, name2 [ON CLUSTER cluster_name2] [RENAME TO new_name2] ...]
|
[, name2 [ON CLUSTER cluster_name2] [RENAME TO new_name2] ...]
|
||||||
[NOT IDENTIFIED | IDENTIFIED {[WITH {no_password | plaintext_password | sha256_password | sha256_hash | double_sha1_password | double_sha1_hash}] BY {'password' | 'hash'}} | {WITH ldap SERVER 'server_name'} | {WITH kerberos [REALM 'realm']}]
|
[NOT IDENTIFIED | IDENTIFIED {[WITH {no_password | plaintext_password | sha256_password | sha256_hash | double_sha1_password | double_sha1_hash}] BY {'password' | 'hash'}} | {WITH ldap SERVER 'server_name'} | {WITH kerberos [REALM 'realm']} | {WITH ssl_certificate CN 'common_name'}]
|
||||||
[[ADD | DROP] HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]
|
[[ADD | DROP] HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]
|
||||||
[DEFAULT ROLE role [,...] | ALL | ALL EXCEPT role [,...] ]
|
[DEFAULT ROLE role [,...] | ALL | ALL EXCEPT role [,...] ]
|
||||||
[GRANTEES {user | role | ANY | NONE} [,...] [EXCEPT {user | role} [,...]]]
|
[GRANTEES {user | role | ANY | NONE} [,...] [EXCEPT {user | role} [,...]]]
|
||||||
|
@ -8,7 +8,7 @@ title: "CHECK TABLE Statement"
|
|||||||
Checks if the data in the table is corrupted.
|
Checks if the data in the table is corrupted.
|
||||||
|
|
||||||
``` sql
|
``` sql
|
||||||
CHECK TABLE [db.]name
|
CHECK TABLE [db.]name [PARTITION partition_expr]
|
||||||
```
|
```
|
||||||
|
|
||||||
The `CHECK TABLE` query compares actual file sizes with the expected values which are stored on the server. If the file sizes do not match the stored values, it means the data is corrupted. This can be caused, for example, by a system crash during query execution.
|
The `CHECK TABLE` query compares actual file sizes with the expected values which are stored on the server. If the file sizes do not match the stored values, it means the data is corrupted. This can be caused, for example, by a system crash during query execution.
|
||||||
|
@ -12,7 +12,7 @@ Syntax:
|
|||||||
``` sql
|
``` sql
|
||||||
CREATE USER [IF NOT EXISTS | OR REPLACE] name1 [ON CLUSTER cluster_name1]
|
CREATE USER [IF NOT EXISTS | OR REPLACE] name1 [ON CLUSTER cluster_name1]
|
||||||
[, name2 [ON CLUSTER cluster_name2] ...]
|
[, name2 [ON CLUSTER cluster_name2] ...]
|
||||||
[NOT IDENTIFIED | IDENTIFIED {[WITH {no_password | plaintext_password | sha256_password | sha256_hash | double_sha1_password | double_sha1_hash}] BY {'password' | 'hash'}} | {WITH ldap SERVER 'server_name'} | {WITH kerberos [REALM 'realm']}]
|
[NOT IDENTIFIED | IDENTIFIED {[WITH {no_password | plaintext_password | sha256_password | sha256_hash | double_sha1_password | double_sha1_hash}] BY {'password' | 'hash'}} | {WITH ldap SERVER 'server_name'} | {WITH kerberos [REALM 'realm']} | {WITH ssl_certificate CN 'common_name'}]
|
||||||
[HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]
|
[HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]
|
||||||
[DEFAULT ROLE role [,...]]
|
[DEFAULT ROLE role [,...]]
|
||||||
[DEFAULT DATABASE database | NONE]
|
[DEFAULT DATABASE database | NONE]
|
||||||
@ -34,6 +34,7 @@ There are multiple ways of user identification:
|
|||||||
- `IDENTIFIED WITH double_sha1_hash BY 'hash'`
|
- `IDENTIFIED WITH double_sha1_hash BY 'hash'`
|
||||||
- `IDENTIFIED WITH ldap SERVER 'server_name'`
|
- `IDENTIFIED WITH ldap SERVER 'server_name'`
|
||||||
- `IDENTIFIED WITH kerberos` or `IDENTIFIED WITH kerberos REALM 'realm'`
|
- `IDENTIFIED WITH kerberos` or `IDENTIFIED WITH kerberos REALM 'realm'`
|
||||||
|
- `IDENTIFIED WITH ssl_certificate CN 'mysite.com:user'`
|
||||||
|
|
||||||
For identification with sha256_hash using `SALT` - hash must be calculated from concatination of 'password' and 'salt'.
|
For identification with sha256_hash using `SALT` - hash must be calculated from concatination of 'password' and 'salt'.
|
||||||
|
|
||||||
|
@ -281,8 +281,8 @@ After running this statement the `[db.]replicated_merge_tree_family_table_name`
|
|||||||
|
|
||||||
### RESTART REPLICA
|
### RESTART REPLICA
|
||||||
|
|
||||||
Provides possibility to reinitialize Zookeeper sessions state for `ReplicatedMergeTree` table, will compare current state with Zookeeper as source of true and add tasks to Zookeeper queue if needed.
|
Provides possibility to reinitialize Zookeeper session's state for `ReplicatedMergeTree` table, will compare current state with Zookeeper as source of truth and add tasks to Zookeeper queue if needed.
|
||||||
Initialization replication queue based on ZooKeeper date happens in the same way as `ATTACH TABLE` statement. For a short time the table will be unavailable for any operations.
|
Initialization of replication queue based on ZooKeeper data happens in the same way as for `ATTACH TABLE` statement. For a short time, the table will be unavailable for any operations.
|
||||||
|
|
||||||
``` sql
|
``` sql
|
||||||
SYSTEM RESTART REPLICA [db.]replicated_merge_tree_family_table_name
|
SYSTEM RESTART REPLICA [db.]replicated_merge_tree_family_table_name
|
||||||
|
@ -39,3 +39,7 @@ You can’t use table functions if the [allow_ddl](../../operations/settings/per
|
|||||||
| [s3](../../sql-reference/table-functions/s3.md) | Creates a [S3](../../engines/table-engines/integrations/s3.md)-engine table. |
|
| [s3](../../sql-reference/table-functions/s3.md) | Creates a [S3](../../engines/table-engines/integrations/s3.md)-engine table. |
|
||||||
| [sqlite](../../sql-reference/table-functions/sqlite.md) | Creates a [sqlite](../../engines/table-engines/integrations/sqlite.md)-engine table. |
|
| [sqlite](../../sql-reference/table-functions/sqlite.md) | Creates a [sqlite](../../engines/table-engines/integrations/sqlite.md)-engine table. |
|
||||||
|
|
||||||
|
:::note
|
||||||
|
Only these table functions are enabled in readonly mode :
|
||||||
|
null, view, viewIfPermitted, numbers, numbers_mt, generateRandom, values, cluster, clusterAllReplicas
|
||||||
|
:::
|
@ -37,7 +37,7 @@ deb:
|
|||||||
contents:
|
contents:
|
||||||
- src: root/etc/clickhouse-client/config.xml
|
- src: root/etc/clickhouse-client/config.xml
|
||||||
dst: /etc/clickhouse-client/config.xml
|
dst: /etc/clickhouse-client/config.xml
|
||||||
type: config
|
type: config|noreplace
|
||||||
- src: root/usr/bin/clickhouse-benchmark
|
- src: root/usr/bin/clickhouse-benchmark
|
||||||
dst: /usr/bin/clickhouse-benchmark
|
dst: /usr/bin/clickhouse-benchmark
|
||||||
- src: root/usr/bin/clickhouse-compressor
|
- src: root/usr/bin/clickhouse-compressor
|
||||||
|
@ -29,7 +29,7 @@ deb:
|
|||||||
contents:
|
contents:
|
||||||
- src: root/etc/clickhouse-keeper/keeper_config.xml
|
- src: root/etc/clickhouse-keeper/keeper_config.xml
|
||||||
dst: /etc/clickhouse-keeper/keeper_config.xml
|
dst: /etc/clickhouse-keeper/keeper_config.xml
|
||||||
type: config
|
type: config|noreplace
|
||||||
- src: root/usr/bin/clickhouse-keeper
|
- src: root/usr/bin/clickhouse-keeper
|
||||||
dst: /usr/bin/clickhouse-keeper
|
dst: /usr/bin/clickhouse-keeper
|
||||||
# docs
|
# docs
|
||||||
|
@ -44,10 +44,10 @@ deb:
|
|||||||
contents:
|
contents:
|
||||||
- src: root/etc/clickhouse-server/config.xml
|
- src: root/etc/clickhouse-server/config.xml
|
||||||
dst: /etc/clickhouse-server/config.xml
|
dst: /etc/clickhouse-server/config.xml
|
||||||
type: config
|
type: config|noreplace
|
||||||
- src: root/etc/clickhouse-server/users.xml
|
- src: root/etc/clickhouse-server/users.xml
|
||||||
dst: /etc/clickhouse-server/users.xml
|
dst: /etc/clickhouse-server/users.xml
|
||||||
type: config
|
type: config|noreplace
|
||||||
- src: clickhouse-server.init
|
- src: clickhouse-server.init
|
||||||
dst: /etc/init.d/clickhouse-server
|
dst: /etc/init.d/clickhouse-server
|
||||||
- src: clickhouse-server.service
|
- src: clickhouse-server.service
|
||||||
|
@ -1088,7 +1088,8 @@ void Client::processConfig()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
need_render_progress = config().getBool("progress", false);
|
std::string progress = config().getString("progress", "tty");
|
||||||
|
need_render_progress = (Poco::icompare(progress, "off") && Poco::icompare(progress, "no") && Poco::icompare(progress, "false") && Poco::icompare(progress, "0"));
|
||||||
echo_queries = config().getBool("echo", false);
|
echo_queries = config().getBool("echo", false);
|
||||||
ignore_error = config().getBool("ignore-error", false);
|
ignore_error = config().getBool("ignore-error", false);
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#include "ExternalDictionaryLibraryHandler.h"
|
#include "ExternalDictionaryLibraryHandler.h"
|
||||||
|
|
||||||
#include <base/scope_guard.h>
|
#include <base/scope_guard.h>
|
||||||
#include <base/bit_cast.h>
|
|
||||||
#include <base/find_symbols.h>
|
#include <base/find_symbols.h>
|
||||||
#include <IO/ReadHelpers.h>
|
#include <IO/ReadHelpers.h>
|
||||||
|
|
||||||
@ -113,7 +112,7 @@ Block ExternalDictionaryLibraryHandler::loadAll()
|
|||||||
|
|
||||||
Block ExternalDictionaryLibraryHandler::loadIds(const std::vector<uint64_t> & ids)
|
Block ExternalDictionaryLibraryHandler::loadIds(const std::vector<uint64_t> & ids)
|
||||||
{
|
{
|
||||||
const ExternalDictionaryLibraryAPI::VectorUInt64 ids_data{bit_cast<decltype(ExternalDictionaryLibraryAPI::VectorUInt64::data)>(ids.data()), ids.size()};
|
const ExternalDictionaryLibraryAPI::VectorUInt64 ids_data{std::bit_cast<decltype(ExternalDictionaryLibraryAPI::VectorUInt64::data)>(ids.data()), ids.size()};
|
||||||
|
|
||||||
auto columns_holder = std::make_unique<ExternalDictionaryLibraryAPI::CString[]>(attributes_names.size());
|
auto columns_holder = std::make_unique<ExternalDictionaryLibraryAPI::CString[]>(attributes_names.size());
|
||||||
ExternalDictionaryLibraryAPI::CStrings columns_pass{static_cast<decltype(ExternalDictionaryLibraryAPI::CStrings::data)>(columns_holder.get()), attributes_names.size()};
|
ExternalDictionaryLibraryAPI::CStrings columns_pass{static_cast<decltype(ExternalDictionaryLibraryAPI::CStrings::data)>(columns_holder.get()), attributes_names.size()};
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include <Common/StringUtils/StringUtils.h>
|
#include <Common/StringUtils/StringUtils.h>
|
||||||
#include <Core/Block.h>
|
#include <Core/Block.h>
|
||||||
#include <base/bit_cast.h>
|
|
||||||
#include <base/range.h>
|
#include <base/range.h>
|
||||||
|
|
||||||
#include "ExternalDictionaryLibraryAPI.h"
|
#include "ExternalDictionaryLibraryAPI.h"
|
||||||
|
@ -489,7 +489,8 @@ void LocalServer::processConfig()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
need_render_progress = config().getBool("progress", false);
|
std::string progress = config().getString("progress", "tty");
|
||||||
|
need_render_progress = (Poco::icompare(progress, "off") && Poco::icompare(progress, "no") && Poco::icompare(progress, "false") && Poco::icompare(progress, "0"));
|
||||||
echo_queries = config().hasOption("echo") || config().hasOption("verbose");
|
echo_queries = config().hasOption("echo") || config().hasOption("verbose");
|
||||||
ignore_error = config().getBool("ignore-error", false);
|
ignore_error = config().getBool("ignore-error", false);
|
||||||
is_multiquery = true;
|
is_multiquery = true;
|
||||||
|
@ -32,7 +32,6 @@
|
|||||||
#include <Core/Block.h>
|
#include <Core/Block.h>
|
||||||
#include <base/StringRef.h>
|
#include <base/StringRef.h>
|
||||||
#include <Common/DateLUT.h>
|
#include <Common/DateLUT.h>
|
||||||
#include <base/bit_cast.h>
|
|
||||||
#include <IO/ReadBufferFromFileDescriptor.h>
|
#include <IO/ReadBufferFromFileDescriptor.h>
|
||||||
#include <IO/WriteBufferFromFileDescriptor.h>
|
#include <IO/WriteBufferFromFileDescriptor.h>
|
||||||
#include <IO/ReadBufferFromFile.h>
|
#include <IO/ReadBufferFromFile.h>
|
||||||
@ -278,9 +277,9 @@ Float transformFloatMantissa(Float x, UInt64 seed)
|
|||||||
using UInt = std::conditional_t<std::is_same_v<Float, Float32>, UInt32, UInt64>;
|
using UInt = std::conditional_t<std::is_same_v<Float, Float32>, UInt32, UInt64>;
|
||||||
constexpr size_t mantissa_num_bits = std::is_same_v<Float, Float32> ? 23 : 52;
|
constexpr size_t mantissa_num_bits = std::is_same_v<Float, Float32> ? 23 : 52;
|
||||||
|
|
||||||
UInt x_uint = bit_cast<UInt>(x);
|
UInt x_uint = std::bit_cast<UInt>(x);
|
||||||
x_uint = static_cast<UInt>(feistelNetwork(x_uint, mantissa_num_bits, seed));
|
x_uint = static_cast<UInt>(feistelNetwork(x_uint, mantissa_num_bits, seed));
|
||||||
return bit_cast<Float>(x_uint);
|
return std::bit_cast<Float>(x_uint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1336,17 +1336,13 @@
|
|||||||
name - name for the rule (optional)
|
name - name for the rule (optional)
|
||||||
regexp - RE2 compatible regular expression (mandatory)
|
regexp - RE2 compatible regular expression (mandatory)
|
||||||
replace - substitution string for sensitive data (optional, by default - six asterisks)
|
replace - substitution string for sensitive data (optional, by default - six asterisks)
|
||||||
-->
|
|
||||||
<query_masking_rules>
|
<query_masking_rules>
|
||||||
<rule>
|
<rule>
|
||||||
<name>hide encrypt/decrypt arguments</name>
|
<name>hide encrypt/decrypt arguments</name>
|
||||||
<regexp>((?:aes_)?(?:encrypt|decrypt)(?:_mysql)?)\s*\(\s*(?:'(?:\\'|.)+'|.*?)\s*\)</regexp>
|
<regexp>((?:aes_)?(?:encrypt|decrypt)(?:_mysql)?)\s*\(\s*(?:'(?:\\'|.)+'|.*?)\s*\)</regexp>
|
||||||
<!-- or more secure, but also more invasive:
|
|
||||||
(aes_\w+)\s*\(.*\)
|
|
||||||
-->
|
|
||||||
<replace>\1(???)</replace>
|
<replace>\1(???)</replace>
|
||||||
</rule>
|
</rule>
|
||||||
</query_masking_rules>
|
</query_masking_rules> -->
|
||||||
|
|
||||||
<!-- Uncomment to use custom http handlers.
|
<!-- Uncomment to use custom http handlers.
|
||||||
rules are checked from top to bottom, first match runs the handler
|
rules are checked from top to bottom, first match runs the handler
|
||||||
|
@ -18,8 +18,10 @@ AggregateFunctionPtr createAggregateFunctionAnalysisOfVariance(const std::string
|
|||||||
assertNoParameters(name, parameters);
|
assertNoParameters(name, parameters);
|
||||||
assertBinary(name, arguments);
|
assertBinary(name, arguments);
|
||||||
|
|
||||||
if (!isNumber(arguments[0]) || !isNumber(arguments[1]))
|
if (!isNumber(arguments[0]))
|
||||||
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Aggregate function {} only supports numerical types", name);
|
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Aggregate function {} only supports numerical argument types", name);
|
||||||
|
if (!WhichDataType(arguments[1]).isNativeUInt())
|
||||||
|
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Second argument of aggregate function {} should be a native unsigned integer", name);
|
||||||
|
|
||||||
return std::make_shared<AggregateFunctionAnalysisOfVariance>(arguments, parameters);
|
return std::make_shared<AggregateFunctionAnalysisOfVariance>(arguments, parameters);
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ public:
|
|||||||
void insertResultInto(AggregateDataPtr __restrict place, IColumn & to, Arena *) const override
|
void insertResultInto(AggregateDataPtr __restrict place, IColumn & to, Arena *) const override
|
||||||
{
|
{
|
||||||
auto f_stat = data(place).getFStatistic();
|
auto f_stat = data(place).getFStatistic();
|
||||||
if (std::isinf(f_stat) || isNaN(f_stat))
|
if (std::isinf(f_stat) || isNaN(f_stat) || f_stat < 0)
|
||||||
throw Exception("F statistic is not defined or infinite for these arguments", ErrorCodes::BAD_ARGUMENTS);
|
throw Exception("F statistic is not defined or infinite for these arguments", ErrorCodes::BAD_ARGUMENTS);
|
||||||
|
|
||||||
auto p_value = data(place).getPValue(f_stat);
|
auto p_value = data(place).getPValue(f_stat);
|
||||||
|
@ -482,6 +482,8 @@ struct ZTestMoments
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
struct AnalysisOfVarianceMoments
|
struct AnalysisOfVarianceMoments
|
||||||
{
|
{
|
||||||
|
constexpr static size_t MAX_GROUPS_NUMBER = 1024 * 1024;
|
||||||
|
|
||||||
/// Sums of values within a group
|
/// Sums of values within a group
|
||||||
std::vector<T> xs1{};
|
std::vector<T> xs1{};
|
||||||
/// Sums of squared values within a group
|
/// Sums of squared values within a group
|
||||||
@ -494,6 +496,10 @@ struct AnalysisOfVarianceMoments
|
|||||||
if (xs1.size() >= possible_size)
|
if (xs1.size() >= possible_size)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (possible_size > MAX_GROUPS_NUMBER)
|
||||||
|
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Too many groups for analysis of variance (should be no more than {}, got {})",
|
||||||
|
MAX_GROUPS_NUMBER, possible_size);
|
||||||
|
|
||||||
xs1.resize(possible_size, 0.0);
|
xs1.resize(possible_size, 0.0);
|
||||||
xs2.resize(possible_size, 0.0);
|
xs2.resize(possible_size, 0.0);
|
||||||
ns.resize(possible_size, 0);
|
ns.resize(possible_size, 0);
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <base/types.h>
|
#include <base/types.h>
|
||||||
#include <base/bit_cast.h>
|
|
||||||
#include <base/sort.h>
|
#include <base/sort.h>
|
||||||
#include <Common/HashTable/HashMap.h>
|
#include <Common/HashTable/HashMap.h>
|
||||||
|
|
||||||
@ -104,13 +103,13 @@ private:
|
|||||||
/// Take the most significant 16 bits of the floating point number.
|
/// Take the most significant 16 bits of the floating point number.
|
||||||
BFloat16 toBFloat16(const Value & x) const
|
BFloat16 toBFloat16(const Value & x) const
|
||||||
{
|
{
|
||||||
return bit_cast<UInt32>(static_cast<Float32>(x)) >> 16;
|
return std::bit_cast<UInt32>(static_cast<Float32>(x)) >> 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Put the bits into most significant 16 bits of the floating point number and fill other bits with zeros.
|
/// Put the bits into most significant 16 bits of the floating point number and fill other bits with zeros.
|
||||||
Float32 toFloat32(const BFloat16 & x) const
|
Float32 toFloat32(const BFloat16 & x) const
|
||||||
{
|
{
|
||||||
return bit_cast<Float32>(x << 16);
|
return std::bit_cast<Float32>(x << 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
using Pair = PairNoInit<Float32, Weight>;
|
using Pair = PairNoInit<Float32, Weight>;
|
||||||
|
@ -16,7 +16,7 @@ namespace DB
|
|||||||
* Dependencies between passes must be avoided.
|
* Dependencies between passes must be avoided.
|
||||||
*/
|
*/
|
||||||
class IQueryTreePass;
|
class IQueryTreePass;
|
||||||
using QueryTreePassPtr = std::shared_ptr<IQueryTreePass>;
|
using QueryTreePassPtr = std::unique_ptr<IQueryTreePass>;
|
||||||
using QueryTreePasses = std::vector<QueryTreePassPtr>;
|
using QueryTreePasses = std::vector<QueryTreePassPtr>;
|
||||||
|
|
||||||
class IQueryTreePass
|
class IQueryTreePass
|
||||||
|
@ -5527,9 +5527,15 @@ void QueryAnalyzer::resolveQuery(const QueryTreeNodePtr & query_node, Identifier
|
|||||||
* 3. Check that there are no columns that are not specified in GROUP BY keys.
|
* 3. Check that there are no columns that are not specified in GROUP BY keys.
|
||||||
* 4. Validate GROUP BY modifiers.
|
* 4. Validate GROUP BY modifiers.
|
||||||
*/
|
*/
|
||||||
|
auto join_tree_node_type = query_node_typed.getJoinTree()->getNodeType();
|
||||||
|
bool join_tree_is_subquery = join_tree_node_type == QueryTreeNodeType::QUERY || join_tree_node_type == QueryTreeNodeType::UNION;
|
||||||
|
|
||||||
|
if (!join_tree_is_subquery)
|
||||||
|
{
|
||||||
assertNoAggregateFunctionNodes(query_node_typed.getJoinTree(), "in JOIN TREE");
|
assertNoAggregateFunctionNodes(query_node_typed.getJoinTree(), "in JOIN TREE");
|
||||||
assertNoGroupingFunction(query_node_typed.getJoinTree(), "in JOIN TREE");
|
assertNoGroupingFunction(query_node_typed.getJoinTree(), "in JOIN TREE");
|
||||||
assertNoWindowFunctionNodes(query_node_typed.getJoinTree(), "in JOIN TREE");
|
assertNoWindowFunctionNodes(query_node_typed.getJoinTree(), "in JOIN TREE");
|
||||||
|
}
|
||||||
|
|
||||||
if (query_node_typed.hasWhere())
|
if (query_node_typed.hasWhere())
|
||||||
{
|
{
|
||||||
|
@ -18,6 +18,9 @@
|
|||||||
#include <IO/Operators.h>
|
#include <IO/Operators.h>
|
||||||
|
|
||||||
#include <Interpreters/Context.h>
|
#include <Interpreters/Context.h>
|
||||||
|
#include <Analyzer/ColumnNode.h>
|
||||||
|
#include <Analyzer/InDepthQueryTreeVisitor.h>
|
||||||
|
#include <Common/Exception.h>
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
{
|
{
|
||||||
@ -25,6 +28,38 @@ namespace DB
|
|||||||
namespace ErrorCodes
|
namespace ErrorCodes
|
||||||
{
|
{
|
||||||
extern const int BAD_ARGUMENTS;
|
extern const int BAD_ARGUMENTS;
|
||||||
|
extern const int LOGICAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
|
||||||
|
/** This visitor checks if Query Tree structure is valid after each pass
|
||||||
|
* in debug build.
|
||||||
|
*/
|
||||||
|
class ValidationChecker : public InDepthQueryTreeVisitor<ValidationChecker>
|
||||||
|
{
|
||||||
|
String pass_name;
|
||||||
|
public:
|
||||||
|
explicit ValidationChecker(String pass_name_)
|
||||||
|
: pass_name(std::move(pass_name_))
|
||||||
|
{}
|
||||||
|
|
||||||
|
void visitImpl(QueryTreeNodePtr & node) const
|
||||||
|
{
|
||||||
|
auto * column = node->as<ColumnNode>();
|
||||||
|
if (!column)
|
||||||
|
return;
|
||||||
|
if (column->getColumnSourceOrNull() == nullptr)
|
||||||
|
throw Exception(ErrorCodes::LOGICAL_ERROR,
|
||||||
|
"Column {} {} query tree node does not have valid source node after running {} pass",
|
||||||
|
column->getColumnName(), column->getColumnType(), pass_name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ClickHouse query tree pass manager.
|
/** ClickHouse query tree pass manager.
|
||||||
@ -61,7 +96,12 @@ void QueryTreePassManager::run(QueryTreeNodePtr query_tree_node)
|
|||||||
size_t passes_size = passes.size();
|
size_t passes_size = passes.size();
|
||||||
|
|
||||||
for (size_t i = 0; i < passes_size; ++i)
|
for (size_t i = 0; i < passes_size; ++i)
|
||||||
|
{
|
||||||
passes[i]->run(query_tree_node, current_context);
|
passes[i]->run(query_tree_node, current_context);
|
||||||
|
#ifndef NDEBUG
|
||||||
|
ValidationChecker(passes[i]->getName()).visit(query_tree_node);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QueryTreePassManager::run(QueryTreeNodePtr query_tree_node, size_t up_to_pass_index)
|
void QueryTreePassManager::run(QueryTreeNodePtr query_tree_node, size_t up_to_pass_index)
|
||||||
@ -75,7 +115,12 @@ void QueryTreePassManager::run(QueryTreeNodePtr query_tree_node, size_t up_to_pa
|
|||||||
|
|
||||||
auto current_context = getContext();
|
auto current_context = getContext();
|
||||||
for (size_t i = 0; i < up_to_pass_index; ++i)
|
for (size_t i = 0; i < up_to_pass_index; ++i)
|
||||||
|
{
|
||||||
passes[i]->run(query_tree_node, current_context);
|
passes[i]->run(query_tree_node, current_context);
|
||||||
|
#ifndef NDEBUG
|
||||||
|
ValidationChecker(passes[i]->getName()).visit(query_tree_node);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QueryTreePassManager::dump(WriteBuffer & buffer)
|
void QueryTreePassManager::dump(WriteBuffer & buffer)
|
||||||
@ -114,38 +159,38 @@ void addQueryTreePasses(QueryTreePassManager & manager)
|
|||||||
auto context = manager.getContext();
|
auto context = manager.getContext();
|
||||||
const auto & settings = context->getSettingsRef();
|
const auto & settings = context->getSettingsRef();
|
||||||
|
|
||||||
manager.addPass(std::make_shared<QueryAnalysisPass>());
|
manager.addPass(std::make_unique<QueryAnalysisPass>());
|
||||||
|
|
||||||
if (settings.optimize_functions_to_subcolumns)
|
if (settings.optimize_functions_to_subcolumns)
|
||||||
manager.addPass(std::make_shared<FunctionToSubcolumnsPass>());
|
manager.addPass(std::make_unique<FunctionToSubcolumnsPass>());
|
||||||
|
|
||||||
if (settings.count_distinct_optimization)
|
if (settings.count_distinct_optimization)
|
||||||
manager.addPass(std::make_shared<CountDistinctPass>());
|
manager.addPass(std::make_unique<CountDistinctPass>());
|
||||||
|
|
||||||
if (settings.optimize_rewrite_sum_if_to_count_if)
|
if (settings.optimize_rewrite_sum_if_to_count_if)
|
||||||
manager.addPass(std::make_shared<SumIfToCountIfPass>());
|
manager.addPass(std::make_unique<SumIfToCountIfPass>());
|
||||||
|
|
||||||
if (settings.optimize_normalize_count_variants)
|
if (settings.optimize_normalize_count_variants)
|
||||||
manager.addPass(std::make_shared<NormalizeCountVariantsPass>());
|
manager.addPass(std::make_unique<NormalizeCountVariantsPass>());
|
||||||
|
|
||||||
manager.addPass(std::make_shared<CustomizeFunctionsPass>());
|
manager.addPass(std::make_unique<CustomizeFunctionsPass>());
|
||||||
|
|
||||||
if (settings.optimize_arithmetic_operations_in_aggregate_functions)
|
if (settings.optimize_arithmetic_operations_in_aggregate_functions)
|
||||||
manager.addPass(std::make_shared<AggregateFunctionsArithmericOperationsPass>());
|
manager.addPass(std::make_unique<AggregateFunctionsArithmericOperationsPass>());
|
||||||
|
|
||||||
if (settings.optimize_injective_functions_inside_uniq)
|
if (settings.optimize_injective_functions_inside_uniq)
|
||||||
manager.addPass(std::make_shared<UniqInjectiveFunctionsEliminationPass>());
|
manager.addPass(std::make_unique<UniqInjectiveFunctionsEliminationPass>());
|
||||||
|
|
||||||
if (settings.optimize_multiif_to_if)
|
if (settings.optimize_multiif_to_if)
|
||||||
manager.addPass(std::make_shared<MultiIfToIfPass>());
|
manager.addPass(std::make_unique<MultiIfToIfPass>());
|
||||||
|
|
||||||
manager.addPass(std::make_shared<IfConstantConditionPass>());
|
manager.addPass(std::make_unique<IfConstantConditionPass>());
|
||||||
|
|
||||||
if (settings.optimize_if_chain_to_multiif)
|
if (settings.optimize_if_chain_to_multiif)
|
||||||
manager.addPass(std::make_shared<IfChainToMultiIfPass>());
|
manager.addPass(std::make_unique<IfChainToMultiIfPass>());
|
||||||
|
|
||||||
manager.addPass(std::make_shared<OrderByTupleEliminationPass>());
|
manager.addPass(std::make_unique<OrderByTupleEliminationPass>());
|
||||||
manager.addPass(std::make_shared<OrderByLimitByDuplicateEliminationPass>());
|
manager.addPass(std::make_unique<OrderByLimitByDuplicateEliminationPass>());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -126,6 +126,7 @@ BackupWriterS3::BackupWriterS3(
|
|||||||
, max_single_read_retries(context_->getSettingsRef().s3_max_single_read_retries)
|
, max_single_read_retries(context_->getSettingsRef().s3_max_single_read_retries)
|
||||||
, read_settings(context_->getReadSettings())
|
, read_settings(context_->getReadSettings())
|
||||||
, rw_settings(context_->getStorageS3Settings().getSettings(s3_uri.uri.toString()).rw_settings)
|
, rw_settings(context_->getStorageS3Settings().getSettings(s3_uri.uri.toString()).rw_settings)
|
||||||
|
, log(&Poco::Logger::get("BackupWriterS3"))
|
||||||
{
|
{
|
||||||
rw_settings.updateFromSettingsIfEmpty(context_->getSettingsRef());
|
rw_settings.updateFromSettingsIfEmpty(context_->getSettingsRef());
|
||||||
}
|
}
|
||||||
@ -146,9 +147,12 @@ void BackupWriterS3::copyObjectImpl(
|
|||||||
const String & src_key,
|
const String & src_key,
|
||||||
const String & dst_bucket,
|
const String & dst_bucket,
|
||||||
const String & dst_key,
|
const String & dst_key,
|
||||||
std::optional<Aws::S3::Model::HeadObjectResult> head,
|
const Aws::S3::Model::HeadObjectResult & head,
|
||||||
std::optional<ObjectAttributes> metadata) const
|
const std::optional<ObjectAttributes> & metadata) const
|
||||||
{
|
{
|
||||||
|
size_t size = head.GetContentLength();
|
||||||
|
LOG_TRACE(log, "Copying {} bytes using single-operation copy", size);
|
||||||
|
|
||||||
Aws::S3::Model::CopyObjectRequest request;
|
Aws::S3::Model::CopyObjectRequest request;
|
||||||
request.SetCopySource(src_bucket + "/" + src_key);
|
request.SetCopySource(src_bucket + "/" + src_key);
|
||||||
request.SetBucket(dst_bucket);
|
request.SetBucket(dst_bucket);
|
||||||
@ -186,13 +190,11 @@ void BackupWriterS3::copyObjectMultipartImpl(
|
|||||||
const String & src_key,
|
const String & src_key,
|
||||||
const String & dst_bucket,
|
const String & dst_bucket,
|
||||||
const String & dst_key,
|
const String & dst_key,
|
||||||
std::optional<Aws::S3::Model::HeadObjectResult> head,
|
const Aws::S3::Model::HeadObjectResult & head,
|
||||||
std::optional<ObjectAttributes> metadata) const
|
const std::optional<ObjectAttributes> & metadata) const
|
||||||
{
|
{
|
||||||
if (!head)
|
size_t size = head.GetContentLength();
|
||||||
head = requestObjectHeadData(src_bucket, src_key).GetResult();
|
LOG_TRACE(log, "Copying {} bytes using multipart upload copy", size);
|
||||||
|
|
||||||
size_t size = head->GetContentLength();
|
|
||||||
|
|
||||||
String multipart_upload_id;
|
String multipart_upload_id;
|
||||||
|
|
||||||
@ -213,16 +215,20 @@ void BackupWriterS3::copyObjectMultipartImpl(
|
|||||||
|
|
||||||
std::vector<String> part_tags;
|
std::vector<String> part_tags;
|
||||||
|
|
||||||
|
size_t position = 0;
|
||||||
size_t upload_part_size = rw_settings.min_upload_part_size;
|
size_t upload_part_size = rw_settings.min_upload_part_size;
|
||||||
for (size_t position = 0, part_number = 1; position < size; ++part_number, position += upload_part_size)
|
|
||||||
|
for (size_t part_number = 1; position < size; ++part_number)
|
||||||
{
|
{
|
||||||
|
size_t next_position = std::min(position + upload_part_size, size);
|
||||||
|
|
||||||
Aws::S3::Model::UploadPartCopyRequest part_request;
|
Aws::S3::Model::UploadPartCopyRequest part_request;
|
||||||
part_request.SetCopySource(src_bucket + "/" + src_key);
|
part_request.SetCopySource(src_bucket + "/" + src_key);
|
||||||
part_request.SetBucket(dst_bucket);
|
part_request.SetBucket(dst_bucket);
|
||||||
part_request.SetKey(dst_key);
|
part_request.SetKey(dst_key);
|
||||||
part_request.SetUploadId(multipart_upload_id);
|
part_request.SetUploadId(multipart_upload_id);
|
||||||
part_request.SetPartNumber(static_cast<int>(part_number));
|
part_request.SetPartNumber(static_cast<int>(part_number));
|
||||||
part_request.SetCopySourceRange(fmt::format("bytes={}-{}", position, std::min(size, position + upload_part_size) - 1));
|
part_request.SetCopySourceRange(fmt::format("bytes={}-{}", position, next_position - 1));
|
||||||
|
|
||||||
auto outcome = client->UploadPartCopy(part_request);
|
auto outcome = client->UploadPartCopy(part_request);
|
||||||
if (!outcome.IsSuccess())
|
if (!outcome.IsSuccess())
|
||||||
@ -239,6 +245,14 @@ void BackupWriterS3::copyObjectMultipartImpl(
|
|||||||
|
|
||||||
auto etag = outcome.GetResult().GetCopyPartResult().GetETag();
|
auto etag = outcome.GetResult().GetCopyPartResult().GetETag();
|
||||||
part_tags.push_back(etag);
|
part_tags.push_back(etag);
|
||||||
|
|
||||||
|
position = next_position;
|
||||||
|
|
||||||
|
if (part_number % rw_settings.upload_part_size_multiply_parts_count_threshold == 0)
|
||||||
|
{
|
||||||
|
upload_part_size *= rw_settings.upload_part_size_multiply_factor;
|
||||||
|
upload_part_size = std::min(upload_part_size, rw_settings.max_upload_part_size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -280,15 +294,14 @@ void BackupWriterS3::copyFileNative(DiskPtr from_disk, const String & file_name_
|
|||||||
auto file_path = fs::path(s3_uri.key) / file_name_to;
|
auto file_path = fs::path(s3_uri.key) / file_name_to;
|
||||||
|
|
||||||
auto head = requestObjectHeadData(source_bucket, objects[0].absolute_path).GetResult();
|
auto head = requestObjectHeadData(source_bucket, objects[0].absolute_path).GetResult();
|
||||||
static constexpr int64_t multipart_upload_threashold = 5UL * 1024 * 1024 * 1024;
|
if (static_cast<size_t>(head.GetContentLength()) < rw_settings.max_single_operation_copy_size)
|
||||||
if (head.GetContentLength() >= multipart_upload_threashold)
|
|
||||||
{
|
{
|
||||||
copyObjectMultipartImpl(
|
copyObjectImpl(
|
||||||
source_bucket, objects[0].absolute_path, s3_uri.bucket, file_path, head);
|
source_bucket, objects[0].absolute_path, s3_uri.bucket, file_path, head);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
copyObjectImpl(
|
copyObjectMultipartImpl(
|
||||||
source_bucket, objects[0].absolute_path, s3_uri.bucket, file_path, head);
|
source_bucket, objects[0].absolute_path, s3_uri.bucket, file_path, head);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,6 @@ public:
|
|||||||
void copyFileNative(DiskPtr from_disk, const String & file_name_from, const String & file_name_to) override;
|
void copyFileNative(DiskPtr from_disk, const String & file_name_from, const String & file_name_to) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
Aws::S3::Model::HeadObjectOutcome requestObjectHeadData(const std::string & bucket_from, const std::string & key) const;
|
Aws::S3::Model::HeadObjectOutcome requestObjectHeadData(const std::string & bucket_from, const std::string & key) const;
|
||||||
|
|
||||||
void copyObjectImpl(
|
void copyObjectImpl(
|
||||||
@ -69,22 +68,23 @@ private:
|
|||||||
const String & src_key,
|
const String & src_key,
|
||||||
const String & dst_bucket,
|
const String & dst_bucket,
|
||||||
const String & dst_key,
|
const String & dst_key,
|
||||||
std::optional<Aws::S3::Model::HeadObjectResult> head = std::nullopt,
|
const Aws::S3::Model::HeadObjectResult & head,
|
||||||
std::optional<ObjectAttributes> metadata = std::nullopt) const;
|
const std::optional<ObjectAttributes> & metadata = std::nullopt) const;
|
||||||
|
|
||||||
void copyObjectMultipartImpl(
|
void copyObjectMultipartImpl(
|
||||||
const String & src_bucket,
|
const String & src_bucket,
|
||||||
const String & src_key,
|
const String & src_key,
|
||||||
const String & dst_bucket,
|
const String & dst_bucket,
|
||||||
const String & dst_key,
|
const String & dst_key,
|
||||||
std::optional<Aws::S3::Model::HeadObjectResult> head = std::nullopt,
|
const Aws::S3::Model::HeadObjectResult & head,
|
||||||
std::optional<ObjectAttributes> metadata = std::nullopt) const;
|
const std::optional<ObjectAttributes> & metadata = std::nullopt) const;
|
||||||
|
|
||||||
S3::URI s3_uri;
|
S3::URI s3_uri;
|
||||||
std::shared_ptr<Aws::S3::S3Client> client;
|
std::shared_ptr<Aws::S3::S3Client> client;
|
||||||
UInt64 max_single_read_retries;
|
UInt64 max_single_read_retries;
|
||||||
ReadSettings read_settings;
|
ReadSettings read_settings;
|
||||||
S3Settings::ReadWriteSettings rw_settings;
|
S3Settings::ReadWriteSettings rw_settings;
|
||||||
|
Poco::Logger * log;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -138,12 +138,12 @@ private:
|
|||||||
|
|
||||||
|
|
||||||
BackupImpl::BackupImpl(
|
BackupImpl::BackupImpl(
|
||||||
const String & backup_name_,
|
const String & backup_name_for_logging_,
|
||||||
const ArchiveParams & archive_params_,
|
const ArchiveParams & archive_params_,
|
||||||
const std::optional<BackupInfo> & base_backup_info_,
|
const std::optional<BackupInfo> & base_backup_info_,
|
||||||
std::shared_ptr<IBackupReader> reader_,
|
std::shared_ptr<IBackupReader> reader_,
|
||||||
const ContextPtr & context_)
|
const ContextPtr & context_)
|
||||||
: backup_name(backup_name_)
|
: backup_name_for_logging(backup_name_for_logging_)
|
||||||
, archive_params(archive_params_)
|
, archive_params(archive_params_)
|
||||||
, use_archives(!archive_params.archive_name.empty())
|
, use_archives(!archive_params.archive_name.empty())
|
||||||
, open_mode(OpenMode::READ)
|
, open_mode(OpenMode::READ)
|
||||||
@ -158,7 +158,7 @@ BackupImpl::BackupImpl(
|
|||||||
|
|
||||||
|
|
||||||
BackupImpl::BackupImpl(
|
BackupImpl::BackupImpl(
|
||||||
const String & backup_name_,
|
const String & backup_name_for_logging_,
|
||||||
const ArchiveParams & archive_params_,
|
const ArchiveParams & archive_params_,
|
||||||
const std::optional<BackupInfo> & base_backup_info_,
|
const std::optional<BackupInfo> & base_backup_info_,
|
||||||
std::shared_ptr<IBackupWriter> writer_,
|
std::shared_ptr<IBackupWriter> writer_,
|
||||||
@ -166,7 +166,7 @@ BackupImpl::BackupImpl(
|
|||||||
bool is_internal_backup_,
|
bool is_internal_backup_,
|
||||||
const std::shared_ptr<IBackupCoordination> & coordination_,
|
const std::shared_ptr<IBackupCoordination> & coordination_,
|
||||||
const std::optional<UUID> & backup_uuid_)
|
const std::optional<UUID> & backup_uuid_)
|
||||||
: backup_name(backup_name_)
|
: backup_name_for_logging(backup_name_for_logging_)
|
||||||
, archive_params(archive_params_)
|
, archive_params(archive_params_)
|
||||||
, use_archives(!archive_params.archive_name.empty())
|
, use_archives(!archive_params.archive_name.empty())
|
||||||
, open_mode(OpenMode::WRITE)
|
, open_mode(OpenMode::WRITE)
|
||||||
@ -225,10 +225,19 @@ void BackupImpl::open(const ContextPtr & context)
|
|||||||
base_backup = BackupFactory::instance().createBackup(params);
|
base_backup = BackupFactory::instance().createBackup(params);
|
||||||
|
|
||||||
if (open_mode == OpenMode::WRITE)
|
if (open_mode == OpenMode::WRITE)
|
||||||
|
{
|
||||||
base_backup_uuid = base_backup->getUUID();
|
base_backup_uuid = base_backup->getUUID();
|
||||||
|
}
|
||||||
else if (base_backup_uuid != base_backup->getUUID())
|
else if (base_backup_uuid != base_backup->getUUID())
|
||||||
throw Exception(ErrorCodes::WRONG_BASE_BACKUP, "Backup {}: The base backup {} has different UUID ({} != {})",
|
{
|
||||||
backup_name, base_backup->getName(), toString(base_backup->getUUID()), (base_backup_uuid ? toString(*base_backup_uuid) : ""));
|
throw Exception(
|
||||||
|
ErrorCodes::WRONG_BASE_BACKUP,
|
||||||
|
"Backup {}: The base backup {} has different UUID ({} != {})",
|
||||||
|
backup_name_for_logging,
|
||||||
|
base_backup->getNameForLogging(),
|
||||||
|
toString(base_backup->getUUID()),
|
||||||
|
(base_backup_uuid ? toString(*base_backup_uuid) : ""));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,14 +358,14 @@ void BackupImpl::readBackupMetadata()
|
|||||||
if (use_archives)
|
if (use_archives)
|
||||||
{
|
{
|
||||||
if (!reader->fileExists(archive_params.archive_name))
|
if (!reader->fileExists(archive_params.archive_name))
|
||||||
throw Exception(ErrorCodes::BACKUP_NOT_FOUND, "Backup {} not found", backup_name);
|
throw Exception(ErrorCodes::BACKUP_NOT_FOUND, "Backup {} not found", backup_name_for_logging);
|
||||||
setCompressedSize();
|
setCompressedSize();
|
||||||
in = getArchiveReader("")->readFile(".backup");
|
in = getArchiveReader("")->readFile(".backup");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!reader->fileExists(".backup"))
|
if (!reader->fileExists(".backup"))
|
||||||
throw Exception(ErrorCodes::BACKUP_NOT_FOUND, "Backup {} not found", backup_name);
|
throw Exception(ErrorCodes::BACKUP_NOT_FOUND, "Backup {} not found", backup_name_for_logging);
|
||||||
in = reader->readFile(".backup");
|
in = reader->readFile(".backup");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,7 +378,8 @@ void BackupImpl::readBackupMetadata()
|
|||||||
|
|
||||||
version = config->getInt("version");
|
version = config->getInt("version");
|
||||||
if ((version < INITIAL_BACKUP_VERSION) || (version > CURRENT_BACKUP_VERSION))
|
if ((version < INITIAL_BACKUP_VERSION) || (version > CURRENT_BACKUP_VERSION))
|
||||||
throw Exception(ErrorCodes::BACKUP_VERSION_NOT_SUPPORTED, "Backup {}: Version {} is not supported", backup_name, version);
|
throw Exception(
|
||||||
|
ErrorCodes::BACKUP_VERSION_NOT_SUPPORTED, "Backup {}: Version {} is not supported", backup_name_for_logging, version);
|
||||||
|
|
||||||
timestamp = parse<LocalDateTime>(config->getString("timestamp")).to_time_t();
|
timestamp = parse<LocalDateTime>(config->getString("timestamp")).to_time_t();
|
||||||
uuid = parse<UUID>(config->getString("uuid"));
|
uuid = parse<UUID>(config->getString("uuid"));
|
||||||
@ -400,7 +410,13 @@ void BackupImpl::readBackupMetadata()
|
|||||||
use_base = true;
|
use_base = true;
|
||||||
|
|
||||||
if (info.base_size > info.size)
|
if (info.base_size > info.size)
|
||||||
throw Exception(ErrorCodes::BACKUP_DAMAGED, "Backup {}: Base size must not be greater than the size of entry {}", backup_name, quoteString(info.file_name));
|
{
|
||||||
|
throw Exception(
|
||||||
|
ErrorCodes::BACKUP_DAMAGED,
|
||||||
|
"Backup {}: Base size must not be greater than the size of entry {}",
|
||||||
|
backup_name_for_logging,
|
||||||
|
quoteString(info.file_name));
|
||||||
|
}
|
||||||
|
|
||||||
if (use_base)
|
if (use_base)
|
||||||
{
|
{
|
||||||
@ -436,14 +452,14 @@ void BackupImpl::checkBackupDoesntExist() const
|
|||||||
file_name_to_check_existence = ".backup";
|
file_name_to_check_existence = ".backup";
|
||||||
|
|
||||||
if (writer->fileExists(file_name_to_check_existence))
|
if (writer->fileExists(file_name_to_check_existence))
|
||||||
throw Exception(ErrorCodes::BACKUP_ALREADY_EXISTS, "Backup {} already exists", backup_name);
|
throw Exception(ErrorCodes::BACKUP_ALREADY_EXISTS, "Backup {} already exists", backup_name_for_logging);
|
||||||
|
|
||||||
/// Check that no other backup (excluding internal backups) is writing to the same destination.
|
/// Check that no other backup (excluding internal backups) is writing to the same destination.
|
||||||
if (!is_internal_backup)
|
if (!is_internal_backup)
|
||||||
{
|
{
|
||||||
assert(!lock_file_name.empty());
|
assert(!lock_file_name.empty());
|
||||||
if (writer->fileExists(lock_file_name))
|
if (writer->fileExists(lock_file_name))
|
||||||
throw Exception(ErrorCodes::BACKUP_ALREADY_EXISTS, "Backup {} is being written already", backup_name);
|
throw Exception(ErrorCodes::BACKUP_ALREADY_EXISTS, "Backup {} is being written already", backup_name_for_logging);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -466,8 +482,16 @@ bool BackupImpl::checkLockFile(bool throw_if_failed) const
|
|||||||
if (throw_if_failed)
|
if (throw_if_failed)
|
||||||
{
|
{
|
||||||
if (!writer->fileExists(lock_file_name))
|
if (!writer->fileExists(lock_file_name))
|
||||||
throw Exception(ErrorCodes::FAILED_TO_SYNC_BACKUP_OR_RESTORE, "Lock file {} suddenly disappeared while writing backup {}", lock_file_name, backup_name);
|
{
|
||||||
throw Exception(ErrorCodes::BACKUP_ALREADY_EXISTS, "A concurrent backup writing to the same destination {} detected", backup_name);
|
throw Exception(
|
||||||
|
ErrorCodes::FAILED_TO_SYNC_BACKUP_OR_RESTORE,
|
||||||
|
"Lock file {} suddenly disappeared while writing backup {}",
|
||||||
|
lock_file_name,
|
||||||
|
backup_name_for_logging);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception(
|
||||||
|
ErrorCodes::BACKUP_ALREADY_EXISTS, "A concurrent backup writing to the same destination {} detected", backup_name_for_logging);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -514,8 +538,13 @@ UInt64 BackupImpl::getFileSize(const String & file_name) const
|
|||||||
auto adjusted_path = removeLeadingSlash(file_name);
|
auto adjusted_path = removeLeadingSlash(file_name);
|
||||||
auto info = coordination->getFileInfo(adjusted_path);
|
auto info = coordination->getFileInfo(adjusted_path);
|
||||||
if (!info)
|
if (!info)
|
||||||
|
{
|
||||||
throw Exception(
|
throw Exception(
|
||||||
ErrorCodes::BACKUP_ENTRY_NOT_FOUND, "Backup {}: Entry {} not found in the backup", backup_name, quoteString(file_name));
|
ErrorCodes::BACKUP_ENTRY_NOT_FOUND,
|
||||||
|
"Backup {}: Entry {} not found in the backup",
|
||||||
|
backup_name_for_logging,
|
||||||
|
quoteString(file_name));
|
||||||
|
}
|
||||||
return info->size;
|
return info->size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -525,8 +554,13 @@ UInt128 BackupImpl::getFileChecksum(const String & file_name) const
|
|||||||
auto adjusted_path = removeLeadingSlash(file_name);
|
auto adjusted_path = removeLeadingSlash(file_name);
|
||||||
auto info = coordination->getFileInfo(adjusted_path);
|
auto info = coordination->getFileInfo(adjusted_path);
|
||||||
if (!info)
|
if (!info)
|
||||||
|
{
|
||||||
throw Exception(
|
throw Exception(
|
||||||
ErrorCodes::BACKUP_ENTRY_NOT_FOUND, "Backup {}: Entry {} not found in the backup", backup_name, quoteString(file_name));
|
ErrorCodes::BACKUP_ENTRY_NOT_FOUND,
|
||||||
|
"Backup {}: Entry {} not found in the backup",
|
||||||
|
backup_name_for_logging,
|
||||||
|
quoteString(file_name));
|
||||||
|
}
|
||||||
return info->checksum;
|
return info->checksum;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -536,8 +570,13 @@ SizeAndChecksum BackupImpl::getFileSizeAndChecksum(const String & file_name) con
|
|||||||
auto adjusted_path = removeLeadingSlash(file_name);
|
auto adjusted_path = removeLeadingSlash(file_name);
|
||||||
auto info = coordination->getFileInfo(adjusted_path);
|
auto info = coordination->getFileInfo(adjusted_path);
|
||||||
if (!info)
|
if (!info)
|
||||||
|
{
|
||||||
throw Exception(
|
throw Exception(
|
||||||
ErrorCodes::BACKUP_ENTRY_NOT_FOUND, "Backup {}: Entry {} not found in the backup", backup_name, quoteString(file_name));
|
ErrorCodes::BACKUP_ENTRY_NOT_FOUND,
|
||||||
|
"Backup {}: Entry {} not found in the backup",
|
||||||
|
backup_name_for_logging,
|
||||||
|
quoteString(file_name));
|
||||||
|
}
|
||||||
return {info->size, info->checksum};
|
return {info->size, info->checksum};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -560,8 +599,13 @@ BackupEntryPtr BackupImpl::readFile(const SizeAndChecksum & size_and_checksum) c
|
|||||||
|
|
||||||
auto info_opt = coordination->getFileInfo(size_and_checksum);
|
auto info_opt = coordination->getFileInfo(size_and_checksum);
|
||||||
if (!info_opt)
|
if (!info_opt)
|
||||||
|
{
|
||||||
throw Exception(
|
throw Exception(
|
||||||
ErrorCodes::BACKUP_ENTRY_NOT_FOUND, "Backup {}: Entry {} not found in the backup", backup_name, formatSizeAndChecksum(size_and_checksum));
|
ErrorCodes::BACKUP_ENTRY_NOT_FOUND,
|
||||||
|
"Backup {}: Entry {} not found in the backup",
|
||||||
|
backup_name_for_logging,
|
||||||
|
formatSizeAndChecksum(size_and_checksum));
|
||||||
|
}
|
||||||
|
|
||||||
const auto & info = *info_opt;
|
const auto & info = *info_opt;
|
||||||
|
|
||||||
@ -577,7 +621,7 @@ BackupEntryPtr BackupImpl::readFile(const SizeAndChecksum & size_and_checksum) c
|
|||||||
throw Exception(
|
throw Exception(
|
||||||
ErrorCodes::NO_BASE_BACKUP,
|
ErrorCodes::NO_BASE_BACKUP,
|
||||||
"Backup {}: Entry {} is marked to be read from a base backup, but there is no base backup specified",
|
"Backup {}: Entry {} is marked to be read from a base backup, but there is no base backup specified",
|
||||||
backup_name, formatSizeAndChecksum(size_and_checksum));
|
backup_name_for_logging, formatSizeAndChecksum(size_and_checksum));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!base_backup->fileExists(std::pair(info.base_size, info.base_checksum)))
|
if (!base_backup->fileExists(std::pair(info.base_size, info.base_checksum)))
|
||||||
@ -585,7 +629,7 @@ BackupEntryPtr BackupImpl::readFile(const SizeAndChecksum & size_and_checksum) c
|
|||||||
throw Exception(
|
throw Exception(
|
||||||
ErrorCodes::WRONG_BASE_BACKUP,
|
ErrorCodes::WRONG_BASE_BACKUP,
|
||||||
"Backup {}: Entry {} is marked to be read from a base backup, but doesn't exist there",
|
"Backup {}: Entry {} is marked to be read from a base backup, but doesn't exist there",
|
||||||
backup_name, formatSizeAndChecksum(size_and_checksum));
|
backup_name_for_logging, formatSizeAndChecksum(size_and_checksum));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto base_entry = base_backup->readFile(std::pair{info.base_size, info.base_checksum});
|
auto base_entry = base_backup->readFile(std::pair{info.base_size, info.base_checksum});
|
||||||
@ -695,9 +739,12 @@ void BackupImpl::writeFile(const String & file_name, BackupEntryPtr entry)
|
|||||||
LOG_TRACE(log, "Writing backup for file {} from {}", file_name, from_file_name);
|
LOG_TRACE(log, "Writing backup for file {} from {}", file_name, from_file_name);
|
||||||
|
|
||||||
auto adjusted_path = removeLeadingSlash(file_name);
|
auto adjusted_path = removeLeadingSlash(file_name);
|
||||||
|
|
||||||
if (coordination->getFileInfo(adjusted_path))
|
if (coordination->getFileInfo(adjusted_path))
|
||||||
|
{
|
||||||
throw Exception(
|
throw Exception(
|
||||||
ErrorCodes::BACKUP_ENTRY_ALREADY_EXISTS, "Backup {}: Entry {} already exists", backup_name, quoteString(file_name));
|
ErrorCodes::BACKUP_ENTRY_ALREADY_EXISTS, "Backup {}: Entry {} already exists", backup_name_for_logging, quoteString(file_name));
|
||||||
|
}
|
||||||
|
|
||||||
FileInfo info
|
FileInfo info
|
||||||
{
|
{
|
||||||
@ -893,12 +940,12 @@ void BackupImpl::finalizeWriting()
|
|||||||
|
|
||||||
if (!is_internal_backup)
|
if (!is_internal_backup)
|
||||||
{
|
{
|
||||||
LOG_TRACE(log, "Finalizing backup {}", backup_name);
|
LOG_TRACE(log, "Finalizing backup {}", backup_name_for_logging);
|
||||||
writeBackupMetadata();
|
writeBackupMetadata();
|
||||||
closeArchives();
|
closeArchives();
|
||||||
setCompressedSize();
|
setCompressedSize();
|
||||||
removeLockFile();
|
removeLockFile();
|
||||||
LOG_TRACE(log, "Finalized backup {}", backup_name);
|
LOG_TRACE(log, "Finalized backup {}", backup_name_for_logging);
|
||||||
}
|
}
|
||||||
|
|
||||||
writing_finalized = true;
|
writing_finalized = true;
|
||||||
@ -971,7 +1018,7 @@ void BackupImpl::removeAllFilesAfterFailure()
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
LOG_INFO(log, "Removing all files of backup {} after failure", backup_name);
|
LOG_INFO(log, "Removing all files of backup {} after failure", backup_name_for_logging);
|
||||||
|
|
||||||
Strings files_to_remove;
|
Strings files_to_remove;
|
||||||
if (use_archives)
|
if (use_archives)
|
||||||
|
@ -35,14 +35,14 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
BackupImpl(
|
BackupImpl(
|
||||||
const String & backup_name_,
|
const String & backup_name_for_logging_,
|
||||||
const ArchiveParams & archive_params_,
|
const ArchiveParams & archive_params_,
|
||||||
const std::optional<BackupInfo> & base_backup_info_,
|
const std::optional<BackupInfo> & base_backup_info_,
|
||||||
std::shared_ptr<IBackupReader> reader_,
|
std::shared_ptr<IBackupReader> reader_,
|
||||||
const ContextPtr & context_);
|
const ContextPtr & context_);
|
||||||
|
|
||||||
BackupImpl(
|
BackupImpl(
|
||||||
const String & backup_name_,
|
const String & backup_name_for_logging_,
|
||||||
const ArchiveParams & archive_params_,
|
const ArchiveParams & archive_params_,
|
||||||
const std::optional<BackupInfo> & base_backup_info_,
|
const std::optional<BackupInfo> & base_backup_info_,
|
||||||
std::shared_ptr<IBackupWriter> writer_,
|
std::shared_ptr<IBackupWriter> writer_,
|
||||||
@ -53,7 +53,7 @@ public:
|
|||||||
|
|
||||||
~BackupImpl() override;
|
~BackupImpl() override;
|
||||||
|
|
||||||
const String & getName() const override { return backup_name; }
|
const String & getNameForLogging() const override { return backup_name_for_logging; }
|
||||||
OpenMode getOpenMode() const override { return open_mode; }
|
OpenMode getOpenMode() const override { return open_mode; }
|
||||||
time_t getTimestamp() const override { return timestamp; }
|
time_t getTimestamp() const override { return timestamp; }
|
||||||
UUID getUUID() const override { return *uuid; }
|
UUID getUUID() const override { return *uuid; }
|
||||||
@ -107,7 +107,7 @@ private:
|
|||||||
/// Calculates and sets `compressed_size`.
|
/// Calculates and sets `compressed_size`.
|
||||||
void setCompressedSize();
|
void setCompressedSize();
|
||||||
|
|
||||||
const String backup_name;
|
const String backup_name_for_logging;
|
||||||
const ArchiveParams archive_params;
|
const ArchiveParams archive_params;
|
||||||
const bool use_archives;
|
const bool use_archives;
|
||||||
const OpenMode open_mode;
|
const OpenMode open_mode;
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include <Parsers/ExpressionElementParsers.h>
|
#include <Parsers/ExpressionElementParsers.h>
|
||||||
#include <Parsers/formatAST.h>
|
#include <Parsers/formatAST.h>
|
||||||
#include <Parsers/parseQuery.h>
|
#include <Parsers/parseQuery.h>
|
||||||
|
#include <Interpreters/maskSensitiveInfoInQueryForLogging.h>
|
||||||
|
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
@ -92,4 +93,10 @@ BackupInfo BackupInfo::fromAST(const IAST & ast)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
String BackupInfo::toStringForLogging(const ContextPtr & context) const
|
||||||
|
{
|
||||||
|
ASTPtr ast = toAST();
|
||||||
|
return maskSensitiveInfoInBackupNameForLogging(serializeAST(*ast), ast, context);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Core/Field.h>
|
#include <Core/Field.h>
|
||||||
|
#include <Interpreters/Context_fwd.h>
|
||||||
|
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
@ -20,6 +21,8 @@ struct BackupInfo
|
|||||||
|
|
||||||
ASTPtr toAST() const;
|
ASTPtr toAST() const;
|
||||||
static BackupInfo fromAST(const IAST & ast);
|
static BackupInfo fromAST(const IAST & ast);
|
||||||
|
|
||||||
|
String toStringForLogging(const ContextPtr & context) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -166,9 +166,10 @@ OperationID BackupsWorker::startMakingBackup(const ASTPtr & query, const Context
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto backup_info = BackupInfo::fromAST(*backup_query->backup_name);
|
auto backup_info = BackupInfo::fromAST(*backup_query->backup_name);
|
||||||
|
String backup_name_for_logging = backup_info.toStringForLogging(context);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
addInfo(backup_id, backup_info.toString(), backup_settings.internal, BackupStatus::CREATING_BACKUP);
|
addInfo(backup_id, backup_name_for_logging, backup_settings.internal, BackupStatus::CREATING_BACKUP);
|
||||||
|
|
||||||
/// Prepare context to use.
|
/// Prepare context to use.
|
||||||
ContextPtr context_in_use = context;
|
ContextPtr context_in_use = context;
|
||||||
@ -184,13 +185,14 @@ OperationID BackupsWorker::startMakingBackup(const ASTPtr & query, const Context
|
|||||||
if (backup_settings.async)
|
if (backup_settings.async)
|
||||||
{
|
{
|
||||||
backups_thread_pool.scheduleOrThrowOnError(
|
backups_thread_pool.scheduleOrThrowOnError(
|
||||||
[this, backup_query, backup_id, backup_settings, backup_info, backup_coordination, context_in_use, mutable_context]
|
[this, backup_query, backup_id, backup_name_for_logging, backup_info, backup_settings, backup_coordination, context_in_use, mutable_context]
|
||||||
{
|
{
|
||||||
doBackup(
|
doBackup(
|
||||||
backup_query,
|
backup_query,
|
||||||
backup_id,
|
backup_id,
|
||||||
backup_settings,
|
backup_name_for_logging,
|
||||||
backup_info,
|
backup_info,
|
||||||
|
backup_settings,
|
||||||
backup_coordination,
|
backup_coordination,
|
||||||
context_in_use,
|
context_in_use,
|
||||||
mutable_context,
|
mutable_context,
|
||||||
@ -202,8 +204,9 @@ OperationID BackupsWorker::startMakingBackup(const ASTPtr & query, const Context
|
|||||||
doBackup(
|
doBackup(
|
||||||
backup_query,
|
backup_query,
|
||||||
backup_id,
|
backup_id,
|
||||||
backup_settings,
|
backup_name_for_logging,
|
||||||
backup_info,
|
backup_info,
|
||||||
|
backup_settings,
|
||||||
backup_coordination,
|
backup_coordination,
|
||||||
context_in_use,
|
context_in_use,
|
||||||
mutable_context,
|
mutable_context,
|
||||||
@ -214,7 +217,7 @@ OperationID BackupsWorker::startMakingBackup(const ASTPtr & query, const Context
|
|||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
tryLogCurrentException(log, fmt::format("Failed to start {} {}", (backup_settings.internal ? "internal backup" : "backup"), backup_info.toString()));
|
tryLogCurrentException(log, fmt::format("Failed to start {} {}", (backup_settings.internal ? "internal backup" : "backup"), backup_name_for_logging));
|
||||||
/// Something bad happened, the backup has not built.
|
/// Something bad happened, the backup has not built.
|
||||||
setStatusSafe(backup_id, BackupStatus::BACKUP_FAILED);
|
setStatusSafe(backup_id, BackupStatus::BACKUP_FAILED);
|
||||||
sendCurrentExceptionToCoordination(backup_coordination, backup_settings.host_id);
|
sendCurrentExceptionToCoordination(backup_coordination, backup_settings.host_id);
|
||||||
@ -226,8 +229,9 @@ OperationID BackupsWorker::startMakingBackup(const ASTPtr & query, const Context
|
|||||||
void BackupsWorker::doBackup(
|
void BackupsWorker::doBackup(
|
||||||
const std::shared_ptr<ASTBackupQuery> & backup_query,
|
const std::shared_ptr<ASTBackupQuery> & backup_query,
|
||||||
const OperationID & backup_id,
|
const OperationID & backup_id,
|
||||||
BackupSettings backup_settings,
|
const String & backup_name_for_logging,
|
||||||
const BackupInfo & backup_info,
|
const BackupInfo & backup_info,
|
||||||
|
BackupSettings backup_settings,
|
||||||
std::shared_ptr<IBackupCoordination> backup_coordination,
|
std::shared_ptr<IBackupCoordination> backup_coordination,
|
||||||
const ContextPtr & context,
|
const ContextPtr & context,
|
||||||
ContextMutablePtr mutable_context,
|
ContextMutablePtr mutable_context,
|
||||||
@ -336,7 +340,7 @@ void BackupsWorker::doBackup(
|
|||||||
/// Close the backup.
|
/// Close the backup.
|
||||||
backup.reset();
|
backup.reset();
|
||||||
|
|
||||||
LOG_INFO(log, "{} {} was created successfully", (backup_settings.internal ? "Internal backup" : "Backup"), backup_info.toString());
|
LOG_INFO(log, "{} {} was created successfully", (backup_settings.internal ? "Internal backup" : "Backup"), backup_name_for_logging);
|
||||||
setStatus(backup_id, BackupStatus::BACKUP_CREATED);
|
setStatus(backup_id, BackupStatus::BACKUP_CREATED);
|
||||||
setNumFilesAndSize(backup_id, num_files, uncompressed_size, compressed_size);
|
setNumFilesAndSize(backup_id, num_files, uncompressed_size, compressed_size);
|
||||||
}
|
}
|
||||||
@ -345,7 +349,7 @@ void BackupsWorker::doBackup(
|
|||||||
/// Something bad happened, the backup has not built.
|
/// Something bad happened, the backup has not built.
|
||||||
if (called_async)
|
if (called_async)
|
||||||
{
|
{
|
||||||
tryLogCurrentException(log, fmt::format("Failed to make {} {}", (backup_settings.internal ? "internal backup" : "backup"), backup_info.toString()));
|
tryLogCurrentException(log, fmt::format("Failed to make {} {}", (backup_settings.internal ? "internal backup" : "backup"), backup_name_for_logging));
|
||||||
setStatusSafe(backup_id, BackupStatus::BACKUP_FAILED);
|
setStatusSafe(backup_id, BackupStatus::BACKUP_FAILED);
|
||||||
sendCurrentExceptionToCoordination(backup_coordination, backup_settings.host_id);
|
sendCurrentExceptionToCoordination(backup_coordination, backup_settings.host_id);
|
||||||
}
|
}
|
||||||
@ -384,7 +388,8 @@ OperationID BackupsWorker::startRestoring(const ASTPtr & query, ContextMutablePt
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto backup_info = BackupInfo::fromAST(*restore_query->backup_name);
|
auto backup_info = BackupInfo::fromAST(*restore_query->backup_name);
|
||||||
addInfo(restore_id, backup_info.toString(), restore_settings.internal, BackupStatus::RESTORING);
|
String backup_name_for_logging = backup_info.toStringForLogging(context);
|
||||||
|
addInfo(restore_id, backup_name_for_logging, restore_settings.internal, BackupStatus::RESTORING);
|
||||||
|
|
||||||
/// Prepare context to use.
|
/// Prepare context to use.
|
||||||
ContextMutablePtr context_in_use = context;
|
ContextMutablePtr context_in_use = context;
|
||||||
@ -399,12 +404,14 @@ OperationID BackupsWorker::startRestoring(const ASTPtr & query, ContextMutablePt
|
|||||||
if (restore_settings.async)
|
if (restore_settings.async)
|
||||||
{
|
{
|
||||||
backups_thread_pool.scheduleOrThrowOnError(
|
backups_thread_pool.scheduleOrThrowOnError(
|
||||||
[this, restore_query, restore_id, restore_settings, backup_info, restore_coordination, context_in_use] {
|
[this, restore_query, restore_id, backup_name_for_logging, backup_info, restore_settings, restore_coordination, context_in_use]
|
||||||
|
{
|
||||||
doRestore(
|
doRestore(
|
||||||
restore_query,
|
restore_query,
|
||||||
restore_id,
|
restore_id,
|
||||||
restore_settings,
|
backup_name_for_logging,
|
||||||
backup_info,
|
backup_info,
|
||||||
|
restore_settings,
|
||||||
restore_coordination,
|
restore_coordination,
|
||||||
context_in_use,
|
context_in_use,
|
||||||
/* called_async= */ true);
|
/* called_async= */ true);
|
||||||
@ -415,8 +422,9 @@ OperationID BackupsWorker::startRestoring(const ASTPtr & query, ContextMutablePt
|
|||||||
doRestore(
|
doRestore(
|
||||||
restore_query,
|
restore_query,
|
||||||
restore_id,
|
restore_id,
|
||||||
restore_settings,
|
backup_name_for_logging,
|
||||||
backup_info,
|
backup_info,
|
||||||
|
restore_settings,
|
||||||
restore_coordination,
|
restore_coordination,
|
||||||
context_in_use,
|
context_in_use,
|
||||||
/* called_async= */ false);
|
/* called_async= */ false);
|
||||||
@ -437,8 +445,9 @@ OperationID BackupsWorker::startRestoring(const ASTPtr & query, ContextMutablePt
|
|||||||
void BackupsWorker::doRestore(
|
void BackupsWorker::doRestore(
|
||||||
const std::shared_ptr<ASTBackupQuery> & restore_query,
|
const std::shared_ptr<ASTBackupQuery> & restore_query,
|
||||||
const OperationID & restore_id,
|
const OperationID & restore_id,
|
||||||
RestoreSettings restore_settings,
|
const String & backup_name_for_logging,
|
||||||
const BackupInfo & backup_info,
|
const BackupInfo & backup_info,
|
||||||
|
RestoreSettings restore_settings,
|
||||||
std::shared_ptr<IRestoreCoordination> restore_coordination,
|
std::shared_ptr<IRestoreCoordination> restore_coordination,
|
||||||
ContextMutablePtr context,
|
ContextMutablePtr context,
|
||||||
bool called_async)
|
bool called_async)
|
||||||
@ -541,7 +550,7 @@ void BackupsWorker::doRestore(
|
|||||||
restore_coordination->setStage(restore_settings.host_id, Stage::COMPLETED, "");
|
restore_coordination->setStage(restore_settings.host_id, Stage::COMPLETED, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_INFO(log, "Restored from {} {} successfully", (restore_settings.internal ? "internal backup" : "backup"), backup_info.toString());
|
LOG_INFO(log, "Restored from {} {} successfully", (restore_settings.internal ? "internal backup" : "backup"), backup_name_for_logging);
|
||||||
setStatus(restore_id, BackupStatus::RESTORED);
|
setStatus(restore_id, BackupStatus::RESTORED);
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
@ -549,7 +558,7 @@ void BackupsWorker::doRestore(
|
|||||||
/// Something bad happened, the backup has not built.
|
/// Something bad happened, the backup has not built.
|
||||||
if (called_async)
|
if (called_async)
|
||||||
{
|
{
|
||||||
tryLogCurrentException(log, fmt::format("Failed to restore from {} {}", (restore_settings.internal ? "internal backup" : "backup"), backup_info.toString()));
|
tryLogCurrentException(log, fmt::format("Failed to restore from {} {}", (restore_settings.internal ? "internal backup" : "backup"), backup_name_for_logging));
|
||||||
setStatusSafe(restore_id, BackupStatus::RESTORE_FAILED);
|
setStatusSafe(restore_id, BackupStatus::RESTORE_FAILED);
|
||||||
sendCurrentExceptionToCoordination(restore_coordination, restore_settings.host_id);
|
sendCurrentExceptionToCoordination(restore_coordination, restore_settings.host_id);
|
||||||
}
|
}
|
||||||
|
@ -76,14 +76,28 @@ public:
|
|||||||
private:
|
private:
|
||||||
OperationID startMakingBackup(const ASTPtr & query, const ContextPtr & context);
|
OperationID startMakingBackup(const ASTPtr & query, const ContextPtr & context);
|
||||||
|
|
||||||
void doBackup(const std::shared_ptr<ASTBackupQuery> & backup_query, const OperationID & backup_id, BackupSettings backup_settings,
|
void doBackup(
|
||||||
const BackupInfo & backup_info, std::shared_ptr<IBackupCoordination> backup_coordination, const ContextPtr & context,
|
const std::shared_ptr<ASTBackupQuery> & backup_query,
|
||||||
ContextMutablePtr mutable_context, bool called_async);
|
const OperationID & backup_id,
|
||||||
|
const String & backup_name_for_logging,
|
||||||
|
const BackupInfo & backup_info,
|
||||||
|
BackupSettings backup_settings,
|
||||||
|
std::shared_ptr<IBackupCoordination> backup_coordination,
|
||||||
|
const ContextPtr & context,
|
||||||
|
ContextMutablePtr mutable_context,
|
||||||
|
bool called_async);
|
||||||
|
|
||||||
OperationID startRestoring(const ASTPtr & query, ContextMutablePtr context);
|
OperationID startRestoring(const ASTPtr & query, ContextMutablePtr context);
|
||||||
|
|
||||||
void doRestore(const std::shared_ptr<ASTBackupQuery> & restore_query, const OperationID & restore_id, RestoreSettings restore_settings, const BackupInfo & backup_info,
|
void doRestore(
|
||||||
std::shared_ptr<IRestoreCoordination> restore_coordination, ContextMutablePtr context, bool called_async);
|
const std::shared_ptr<ASTBackupQuery> & restore_query,
|
||||||
|
const OperationID & restore_id,
|
||||||
|
const String & backup_name_for_logging,
|
||||||
|
const BackupInfo & backup_info,
|
||||||
|
RestoreSettings restore_settings,
|
||||||
|
std::shared_ptr<IRestoreCoordination> restore_coordination,
|
||||||
|
ContextMutablePtr context,
|
||||||
|
bool called_async);
|
||||||
|
|
||||||
void addInfo(const OperationID & id, const String & name, bool internal, BackupStatus status);
|
void addInfo(const OperationID & id, const String & name, bool internal, BackupStatus status);
|
||||||
void setStatus(const OperationID & id, BackupStatus status, bool throw_if_error = true);
|
void setStatus(const OperationID & id, BackupStatus status, bool throw_if_error = true);
|
||||||
|
@ -19,7 +19,8 @@ public:
|
|||||||
virtual ~IBackup() = default;
|
virtual ~IBackup() = default;
|
||||||
|
|
||||||
/// Name of the backup.
|
/// Name of the backup.
|
||||||
virtual const String & getName() const = 0;
|
//virtual const String & getName() const = 0;
|
||||||
|
virtual const String & getNameForLogging() const = 0;
|
||||||
|
|
||||||
enum class OpenMode
|
enum class OpenMode
|
||||||
{
|
{
|
||||||
|
@ -47,7 +47,7 @@ void registerBackupEngineS3(BackupFactory & factory)
|
|||||||
auto creator_fn = []([[maybe_unused]] const BackupFactory::CreateParams & params) -> std::unique_ptr<IBackup>
|
auto creator_fn = []([[maybe_unused]] const BackupFactory::CreateParams & params) -> std::unique_ptr<IBackup>
|
||||||
{
|
{
|
||||||
#if USE_AWS_S3
|
#if USE_AWS_S3
|
||||||
String backup_name = params.backup_info.toString();
|
String backup_name_for_logging = params.backup_info.toStringForLogging(params.context);
|
||||||
const String & id_arg = params.backup_info.id_arg;
|
const String & id_arg = params.backup_info.id_arg;
|
||||||
const auto & args = params.backup_info.args;
|
const auto & args = params.backup_info.args;
|
||||||
|
|
||||||
@ -111,12 +111,12 @@ void registerBackupEngineS3(BackupFactory & factory)
|
|||||||
if (params.open_mode == IBackup::OpenMode::READ)
|
if (params.open_mode == IBackup::OpenMode::READ)
|
||||||
{
|
{
|
||||||
auto reader = std::make_shared<BackupReaderS3>(S3::URI{Poco::URI{s3_uri}}, access_key_id, secret_access_key, params.context);
|
auto reader = std::make_shared<BackupReaderS3>(S3::URI{Poco::URI{s3_uri}}, access_key_id, secret_access_key, params.context);
|
||||||
return std::make_unique<BackupImpl>(backup_name, archive_params, params.base_backup_info, reader, params.context);
|
return std::make_unique<BackupImpl>(backup_name_for_logging, archive_params, params.base_backup_info, reader, params.context);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto writer = std::make_shared<BackupWriterS3>(S3::URI{Poco::URI{s3_uri}}, access_key_id, secret_access_key, params.context);
|
auto writer = std::make_shared<BackupWriterS3>(S3::URI{Poco::URI{s3_uri}}, access_key_id, secret_access_key, params.context);
|
||||||
return std::make_unique<BackupImpl>(backup_name, archive_params, params.base_backup_info, writer, params.context, params.is_internal_backup, params.backup_coordination, params.backup_uuid);
|
return std::make_unique<BackupImpl>(backup_name_for_logging, archive_params, params.base_backup_info, writer, params.context, params.is_internal_backup, params.backup_coordination, params.backup_uuid);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
throw Exception("S3 support is disabled", ErrorCodes::SUPPORT_IS_DISABLED);
|
throw Exception("S3 support is disabled", ErrorCodes::SUPPORT_IS_DISABLED);
|
||||||
|
@ -99,7 +99,7 @@ void registerBackupEnginesFileAndDisk(BackupFactory & factory)
|
|||||||
{
|
{
|
||||||
auto creator_fn = [](const BackupFactory::CreateParams & params) -> std::unique_ptr<IBackup>
|
auto creator_fn = [](const BackupFactory::CreateParams & params) -> std::unique_ptr<IBackup>
|
||||||
{
|
{
|
||||||
String backup_name = params.backup_info.toString();
|
String backup_name_for_logging = params.backup_info.toStringForLogging(params.context);
|
||||||
const String & engine_name = params.backup_info.backup_engine_name;
|
const String & engine_name = params.backup_info.backup_engine_name;
|
||||||
|
|
||||||
if (!params.backup_info.id_arg.empty())
|
if (!params.backup_info.id_arg.empty())
|
||||||
@ -172,7 +172,7 @@ void registerBackupEnginesFileAndDisk(BackupFactory & factory)
|
|||||||
reader = std::make_shared<BackupReaderFile>(path);
|
reader = std::make_shared<BackupReaderFile>(path);
|
||||||
else
|
else
|
||||||
reader = std::make_shared<BackupReaderDisk>(disk, path);
|
reader = std::make_shared<BackupReaderDisk>(disk, path);
|
||||||
return std::make_unique<BackupImpl>(backup_name, archive_params, params.base_backup_info, reader, params.context);
|
return std::make_unique<BackupImpl>(backup_name_for_logging, archive_params, params.base_backup_info, reader, params.context);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -181,7 +181,7 @@ void registerBackupEnginesFileAndDisk(BackupFactory & factory)
|
|||||||
writer = std::make_shared<BackupWriterFile>(path);
|
writer = std::make_shared<BackupWriterFile>(path);
|
||||||
else
|
else
|
||||||
writer = std::make_shared<BackupWriterDisk>(disk, path);
|
writer = std::make_shared<BackupWriterDisk>(disk, path);
|
||||||
return std::make_unique<BackupImpl>(backup_name, archive_params, params.base_backup_info, writer, params.context, params.is_internal_backup, params.backup_coordination, params.backup_uuid);
|
return std::make_unique<BackupImpl>(backup_name_for_logging, archive_params, params.base_backup_info, writer, params.context, params.is_internal_backup, params.backup_coordination, params.backup_uuid);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -65,10 +65,12 @@
|
|||||||
#include <Interpreters/ReplaceQueryParameterVisitor.h>
|
#include <Interpreters/ReplaceQueryParameterVisitor.h>
|
||||||
#include <Interpreters/ProfileEventsExt.h>
|
#include <Interpreters/ProfileEventsExt.h>
|
||||||
#include <IO/WriteBufferFromOStream.h>
|
#include <IO/WriteBufferFromOStream.h>
|
||||||
|
#include <IO/WriteBufferFromFileDescriptor.h>
|
||||||
#include <IO/CompressionMethod.h>
|
#include <IO/CompressionMethod.h>
|
||||||
#include <Client/InternalTextLogs.h>
|
#include <Client/InternalTextLogs.h>
|
||||||
#include <IO/ForkWriteBuffer.h>
|
#include <IO/ForkWriteBuffer.h>
|
||||||
#include <Parsers/Kusto/ParserKQLStatement.h>
|
#include <Parsers/Kusto/ParserKQLStatement.h>
|
||||||
|
#include <boost/algorithm/string/case_conv.hpp>
|
||||||
|
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
@ -103,6 +105,7 @@ namespace ErrorCodes
|
|||||||
extern const int CANNOT_SET_SIGNAL_HANDLER;
|
extern const int CANNOT_SET_SIGNAL_HANDLER;
|
||||||
extern const int UNRECOGNIZED_ARGUMENTS;
|
extern const int UNRECOGNIZED_ARGUMENTS;
|
||||||
extern const int LOGICAL_ERROR;
|
extern const int LOGICAL_ERROR;
|
||||||
|
extern const int CANNOT_OPEN_FILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -116,6 +119,25 @@ namespace ProfileEvents
|
|||||||
namespace DB
|
namespace DB
|
||||||
{
|
{
|
||||||
|
|
||||||
|
std::istream& operator>> (std::istream & in, ProgressOption & progress)
|
||||||
|
{
|
||||||
|
std::string token;
|
||||||
|
in >> token;
|
||||||
|
|
||||||
|
boost::to_upper(token);
|
||||||
|
|
||||||
|
if (token == "OFF" || token == "FALSE" || token == "0" || token == "NO")
|
||||||
|
progress = ProgressOption::OFF;
|
||||||
|
else if (token == "TTY" || token == "ON" || token == "TRUE" || token == "1" || token == "YES")
|
||||||
|
progress = ProgressOption::TTY;
|
||||||
|
else if (token == "ERR")
|
||||||
|
progress = ProgressOption::ERR;
|
||||||
|
else
|
||||||
|
throw boost::program_options::validation_error(boost::program_options::validation_error::invalid_option_value);
|
||||||
|
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
static ClientInfo::QueryKind parseQueryKind(const String & query_kind)
|
static ClientInfo::QueryKind parseQueryKind(const String & query_kind)
|
||||||
{
|
{
|
||||||
if (query_kind == "initial_query")
|
if (query_kind == "initial_query")
|
||||||
@ -413,8 +435,8 @@ void ClientBase::onData(Block & block, ASTPtr parsed_query)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
/// If results are written INTO OUTFILE, we can avoid clearing progress to avoid flicker.
|
/// If results are written INTO OUTFILE, we can avoid clearing progress to avoid flicker.
|
||||||
if (need_render_progress && (stdout_is_a_tty || is_interactive) && (!select_into_file || select_into_file_and_stdout))
|
if (need_render_progress && tty_buf && (!select_into_file || select_into_file_and_stdout))
|
||||||
progress_indication.clearProgressOutput();
|
progress_indication.clearProgressOutput(*tty_buf);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -431,11 +453,11 @@ void ClientBase::onData(Block & block, ASTPtr parsed_query)
|
|||||||
output_format->flush();
|
output_format->flush();
|
||||||
|
|
||||||
/// Restore progress bar after data block.
|
/// Restore progress bar after data block.
|
||||||
if (need_render_progress && (stdout_is_a_tty || is_interactive))
|
if (need_render_progress && tty_buf)
|
||||||
{
|
{
|
||||||
if (select_into_file && !select_into_file_and_stdout)
|
if (select_into_file && !select_into_file_and_stdout)
|
||||||
std::cerr << "\r";
|
std::cerr << "\r";
|
||||||
progress_indication.writeProgress();
|
progress_indication.writeProgress(*tty_buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -443,7 +465,8 @@ void ClientBase::onData(Block & block, ASTPtr parsed_query)
|
|||||||
void ClientBase::onLogData(Block & block)
|
void ClientBase::onLogData(Block & block)
|
||||||
{
|
{
|
||||||
initLogsOutputStream();
|
initLogsOutputStream();
|
||||||
progress_indication.clearProgressOutput();
|
if (need_render_progress && tty_buf)
|
||||||
|
progress_indication.clearProgressOutput(*tty_buf);
|
||||||
logs_out_stream->writeLogs(block);
|
logs_out_stream->writeLogs(block);
|
||||||
logs_out_stream->flush();
|
logs_out_stream->flush();
|
||||||
}
|
}
|
||||||
@ -639,6 +662,58 @@ void ClientBase::initLogsOutputStream()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClientBase::initTtyBuffer(bool to_err)
|
||||||
|
{
|
||||||
|
if (!tty_buf)
|
||||||
|
{
|
||||||
|
static constexpr auto tty_file_name = "/dev/tty";
|
||||||
|
|
||||||
|
/// Output all progress bar commands to terminal at once to avoid flicker.
|
||||||
|
/// This size is usually greater than the window size.
|
||||||
|
static constexpr size_t buf_size = 1024;
|
||||||
|
|
||||||
|
if (!to_err)
|
||||||
|
{
|
||||||
|
std::error_code ec;
|
||||||
|
std::filesystem::file_status tty = std::filesystem::status(tty_file_name, ec);
|
||||||
|
|
||||||
|
if (!ec && exists(tty) && is_character_file(tty)
|
||||||
|
&& (tty.permissions() & std::filesystem::perms::others_write) != std::filesystem::perms::none)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
tty_buf = std::make_unique<WriteBufferFromFile>(tty_file_name, buf_size);
|
||||||
|
|
||||||
|
/// It is possible that the terminal file has writeable permissions
|
||||||
|
/// but we cannot write anything there. Check it with invisible character.
|
||||||
|
tty_buf->write('\0');
|
||||||
|
tty_buf->next();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (const Exception & e)
|
||||||
|
{
|
||||||
|
if (tty_buf)
|
||||||
|
tty_buf.reset();
|
||||||
|
|
||||||
|
if (e.code() != ErrorCodes::CANNOT_OPEN_FILE)
|
||||||
|
throw;
|
||||||
|
|
||||||
|
/// It is normal if file exists, indicated as writeable but still cannot be opened.
|
||||||
|
/// Fallback to other options.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stderr_is_a_tty)
|
||||||
|
{
|
||||||
|
tty_buf = std::make_unique<WriteBufferFromFileDescriptor>(STDERR_FILENO, buf_size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
need_render_progress = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ClientBase::updateSuggest(const ASTPtr & ast)
|
void ClientBase::updateSuggest(const ASTPtr & ast)
|
||||||
{
|
{
|
||||||
std::vector<std::string> new_words;
|
std::vector<std::string> new_words;
|
||||||
@ -937,14 +1012,15 @@ void ClientBase::onProgress(const Progress & value)
|
|||||||
if (output_format)
|
if (output_format)
|
||||||
output_format->onProgress(value);
|
output_format->onProgress(value);
|
||||||
|
|
||||||
if (need_render_progress)
|
if (need_render_progress && tty_buf)
|
||||||
progress_indication.writeProgress();
|
progress_indication.writeProgress(*tty_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ClientBase::onEndOfStream()
|
void ClientBase::onEndOfStream()
|
||||||
{
|
{
|
||||||
progress_indication.clearProgressOutput();
|
if (need_render_progress && tty_buf)
|
||||||
|
progress_indication.clearProgressOutput(*tty_buf);
|
||||||
|
|
||||||
if (output_format)
|
if (output_format)
|
||||||
output_format->finalize();
|
output_format->finalize();
|
||||||
@ -952,10 +1028,7 @@ void ClientBase::onEndOfStream()
|
|||||||
resetOutput();
|
resetOutput();
|
||||||
|
|
||||||
if (is_interactive && !written_first_block)
|
if (is_interactive && !written_first_block)
|
||||||
{
|
|
||||||
progress_indication.clearProgressOutput();
|
|
||||||
std::cout << "Ok." << std::endl;
|
std::cout << "Ok." << std::endl;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -998,15 +1071,16 @@ void ClientBase::onProfileEvents(Block & block)
|
|||||||
}
|
}
|
||||||
progress_indication.updateThreadEventData(thread_times);
|
progress_indication.updateThreadEventData(thread_times);
|
||||||
|
|
||||||
if (need_render_progress)
|
if (need_render_progress && tty_buf)
|
||||||
progress_indication.writeProgress();
|
progress_indication.writeProgress(*tty_buf);
|
||||||
|
|
||||||
if (profile_events.print)
|
if (profile_events.print)
|
||||||
{
|
{
|
||||||
if (profile_events.watch.elapsedMilliseconds() >= profile_events.delay_ms)
|
if (profile_events.watch.elapsedMilliseconds() >= profile_events.delay_ms)
|
||||||
{
|
{
|
||||||
initLogsOutputStream();
|
initLogsOutputStream();
|
||||||
progress_indication.clearProgressOutput();
|
if (need_render_progress && tty_buf)
|
||||||
|
progress_indication.clearProgressOutput(*tty_buf);
|
||||||
logs_out_stream->writeProfileEvents(block);
|
logs_out_stream->writeProfileEvents(block);
|
||||||
logs_out_stream->flush();
|
logs_out_stream->flush();
|
||||||
|
|
||||||
@ -1173,14 +1247,15 @@ void ClientBase::sendData(Block & sample, const ColumnsDescription & columns_des
|
|||||||
|
|
||||||
bool have_data_in_stdin = !is_interactive && !stdin_is_a_tty && !std_in.eof();
|
bool have_data_in_stdin = !is_interactive && !stdin_is_a_tty && !std_in.eof();
|
||||||
|
|
||||||
if (need_render_progress && have_data_in_stdin)
|
if (need_render_progress)
|
||||||
{
|
{
|
||||||
/// Set total_bytes_to_read for current fd.
|
/// Set total_bytes_to_read for current fd.
|
||||||
FileProgress file_progress(0, std_in.getFileSize());
|
FileProgress file_progress(0, std_in.getFileSize());
|
||||||
progress_indication.updateProgress(Progress(file_progress));
|
progress_indication.updateProgress(Progress(file_progress));
|
||||||
|
|
||||||
/// Set callback to be called on file progress.
|
/// Set callback to be called on file progress.
|
||||||
progress_indication.setFileProgressCallback(global_context, true);
|
if (tty_buf)
|
||||||
|
progress_indication.setFileProgressCallback(global_context, *tty_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If data fetched from file (maybe compressed file)
|
/// If data fetched from file (maybe compressed file)
|
||||||
@ -1432,12 +1507,12 @@ bool ClientBase::receiveEndOfQuery()
|
|||||||
void ClientBase::cancelQuery()
|
void ClientBase::cancelQuery()
|
||||||
{
|
{
|
||||||
connection->sendCancel();
|
connection->sendCancel();
|
||||||
|
if (need_render_progress && tty_buf)
|
||||||
|
progress_indication.clearProgressOutput(*tty_buf);
|
||||||
|
|
||||||
if (is_interactive)
|
if (is_interactive)
|
||||||
{
|
|
||||||
progress_indication.clearProgressOutput();
|
|
||||||
std::cout << "Cancelling query." << std::endl;
|
std::cout << "Cancelling query." << std::endl;
|
||||||
|
|
||||||
}
|
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1557,7 +1632,8 @@ void ClientBase::processParsedSingleQuery(const String & full_query, const Strin
|
|||||||
if (profile_events.last_block)
|
if (profile_events.last_block)
|
||||||
{
|
{
|
||||||
initLogsOutputStream();
|
initLogsOutputStream();
|
||||||
progress_indication.clearProgressOutput();
|
if (need_render_progress && tty_buf)
|
||||||
|
progress_indication.clearProgressOutput(*tty_buf);
|
||||||
logs_out_stream->writeProfileEvents(profile_events.last_block);
|
logs_out_stream->writeProfileEvents(profile_events.last_block);
|
||||||
logs_out_stream->flush();
|
logs_out_stream->flush();
|
||||||
|
|
||||||
@ -2248,7 +2324,7 @@ void ClientBase::init(int argc, char ** argv)
|
|||||||
("stage", po::value<std::string>()->default_value("complete"), "Request query processing up to specified stage: complete,fetch_columns,with_mergeable_state,with_mergeable_state_after_aggregation,with_mergeable_state_after_aggregation_and_limit")
|
("stage", po::value<std::string>()->default_value("complete"), "Request query processing up to specified stage: complete,fetch_columns,with_mergeable_state,with_mergeable_state_after_aggregation,with_mergeable_state_after_aggregation_and_limit")
|
||||||
("query_kind", po::value<std::string>()->default_value("initial_query"), "One of initial_query/secondary_query/no_query")
|
("query_kind", po::value<std::string>()->default_value("initial_query"), "One of initial_query/secondary_query/no_query")
|
||||||
("query_id", po::value<std::string>(), "query_id")
|
("query_id", po::value<std::string>(), "query_id")
|
||||||
("progress", "print progress of queries execution")
|
("progress", po::value<ProgressOption>()->implicit_value(ProgressOption::TTY, "tty")->default_value(ProgressOption::TTY, "tty"), "Print progress of queries execution - to TTY (default): tty|on|1|true|yes; to STDERR: err; OFF: off|0|false|no")
|
||||||
|
|
||||||
("disable_suggestion,A", "Disable loading suggestion data. Note that suggestion data is loaded asynchronously through a second connection to ClickHouse server. Also it is reasonable to disable suggestion if you want to paste a query with TAB characters. Shorthand option -A is for those who get used to mysql client.")
|
("disable_suggestion,A", "Disable loading suggestion data. Note that suggestion data is loaded asynchronously through a second connection to ClickHouse server. Also it is reasonable to disable suggestion if you want to paste a query with TAB characters. Shorthand option -A is for those who get used to mysql client.")
|
||||||
("time,t", "print query execution time to stderr in non-interactive mode (for benchmarks)")
|
("time,t", "print query execution time to stderr in non-interactive mode (for benchmarks)")
|
||||||
@ -2303,6 +2379,11 @@ void ClientBase::init(int argc, char ** argv)
|
|||||||
parseAndCheckOptions(options_description, options, common_arguments);
|
parseAndCheckOptions(options_description, options, common_arguments);
|
||||||
po::notify(options);
|
po::notify(options);
|
||||||
|
|
||||||
|
if (options["progress"].as<ProgressOption>() == ProgressOption::OFF)
|
||||||
|
need_render_progress = false;
|
||||||
|
else
|
||||||
|
initTtyBuffer(options["progress"].as<ProgressOption>() == ProgressOption::ERR);
|
||||||
|
|
||||||
if (options.count("version") || options.count("V"))
|
if (options.count("version") || options.count("V"))
|
||||||
{
|
{
|
||||||
showClientVersion();
|
showClientVersion();
|
||||||
@ -2353,7 +2434,20 @@ void ClientBase::init(int argc, char ** argv)
|
|||||||
if (options.count("profile-events-delay-ms"))
|
if (options.count("profile-events-delay-ms"))
|
||||||
config().setUInt64("profile-events-delay-ms", options["profile-events-delay-ms"].as<UInt64>());
|
config().setUInt64("profile-events-delay-ms", options["profile-events-delay-ms"].as<UInt64>());
|
||||||
if (options.count("progress"))
|
if (options.count("progress"))
|
||||||
config().setBool("progress", true);
|
{
|
||||||
|
switch (options["progress"].as<ProgressOption>())
|
||||||
|
{
|
||||||
|
case OFF:
|
||||||
|
config().setString("progress", "off");
|
||||||
|
break;
|
||||||
|
case TTY:
|
||||||
|
config().setString("progress", "tty");
|
||||||
|
break;
|
||||||
|
case ERR:
|
||||||
|
config().setString("progress", "err");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (options.count("echo"))
|
if (options.count("echo"))
|
||||||
config().setBool("echo", true);
|
config().setBool("echo", true);
|
||||||
if (options.count("disable_suggestion"))
|
if (options.count("disable_suggestion"))
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include <Storages/StorageFile.h>
|
#include <Storages/StorageFile.h>
|
||||||
#include <Storages/SelectQueryInfo.h>
|
#include <Storages/SelectQueryInfo.h>
|
||||||
|
|
||||||
|
|
||||||
namespace po = boost::program_options;
|
namespace po = boost::program_options;
|
||||||
|
|
||||||
|
|
||||||
@ -35,9 +36,18 @@ enum MultiQueryProcessingStage
|
|||||||
PARSING_FAILED,
|
PARSING_FAILED,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum ProgressOption
|
||||||
|
{
|
||||||
|
OFF,
|
||||||
|
TTY,
|
||||||
|
ERR,
|
||||||
|
};
|
||||||
|
std::istream& operator>> (std::istream & in, ProgressOption & progress);
|
||||||
|
|
||||||
void interruptSignalHandler(int signum);
|
void interruptSignalHandler(int signum);
|
||||||
|
|
||||||
class InternalTextLogs;
|
class InternalTextLogs;
|
||||||
|
class WriteBufferFromFileDescriptor;
|
||||||
|
|
||||||
class ClientBase : public Poco::Util::Application, public IHints<2, ClientBase>
|
class ClientBase : public Poco::Util::Application, public IHints<2, ClientBase>
|
||||||
{
|
{
|
||||||
@ -143,6 +153,7 @@ private:
|
|||||||
|
|
||||||
void initOutputFormat(const Block & block, ASTPtr parsed_query);
|
void initOutputFormat(const Block & block, ASTPtr parsed_query);
|
||||||
void initLogsOutputStream();
|
void initLogsOutputStream();
|
||||||
|
void initTtyBuffer(bool to_err = false);
|
||||||
|
|
||||||
String prompt() const;
|
String prompt() const;
|
||||||
|
|
||||||
@ -218,6 +229,10 @@ protected:
|
|||||||
String server_logs_file;
|
String server_logs_file;
|
||||||
std::unique_ptr<InternalTextLogs> logs_out_stream;
|
std::unique_ptr<InternalTextLogs> logs_out_stream;
|
||||||
|
|
||||||
|
/// /dev/tty if accessible or std::cerr - for progress bar.
|
||||||
|
/// We prefer to output progress bar directly to tty to allow user to redirect stdout and stderr and still get the progress indication.
|
||||||
|
std::unique_ptr<WriteBufferFromFileDescriptor> tty_buf;
|
||||||
|
|
||||||
String home_path;
|
String home_path;
|
||||||
String history_file; /// Path to a file containing command history.
|
String history_file; /// Path to a file containing command history.
|
||||||
|
|
||||||
|
@ -1331,7 +1331,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename DateOrTime>
|
template <typename DateOrTime>
|
||||||
inline auto addQuarters(DateOrTime d, Int64 delta) const
|
inline auto NO_SANITIZE_UNDEFINED addQuarters(DateOrTime d, Int64 delta) const
|
||||||
{
|
{
|
||||||
return addMonths(d, delta * 3);
|
return addMonths(d, delta * 3);
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
M(FailedQuery, "Number of failed queries.") \
|
M(FailedQuery, "Number of failed queries.") \
|
||||||
M(FailedSelectQuery, "Same as FailedQuery, but only for SELECT queries.") \
|
M(FailedSelectQuery, "Same as FailedQuery, but only for SELECT queries.") \
|
||||||
M(FailedInsertQuery, "Same as FailedQuery, but only for INSERT queries.") \
|
M(FailedInsertQuery, "Same as FailedQuery, but only for INSERT queries.") \
|
||||||
|
M(FailedAsyncInsertQuery, "Number of failed ASYNC INSERT queries.") \
|
||||||
M(QueryTimeMicroseconds, "Total time of all queries.") \
|
M(QueryTimeMicroseconds, "Total time of all queries.") \
|
||||||
M(SelectQueryTimeMicroseconds, "Total time of SELECT queries.") \
|
M(SelectQueryTimeMicroseconds, "Total time of SELECT queries.") \
|
||||||
M(InsertQueryTimeMicroseconds, "Total time of INSERT queries.") \
|
M(InsertQueryTimeMicroseconds, "Total time of INSERT queries.") \
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
|
#include <filesystem>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <IO/WriteBufferFromFileDescriptor.h>
|
#include <IO/WriteBufferFromFileDescriptor.h>
|
||||||
#include <base/types.h>
|
#include <base/types.h>
|
||||||
@ -11,6 +12,9 @@
|
|||||||
#include "IO/WriteBufferFromString.h"
|
#include "IO/WriteBufferFromString.h"
|
||||||
#include <Databases/DatabaseMemory.h>
|
#include <Databases/DatabaseMemory.h>
|
||||||
|
|
||||||
|
/// http://en.wikipedia.org/wiki/ANSI_escape_code
|
||||||
|
#define CLEAR_TO_END_OF_LINE "\033[K"
|
||||||
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@ -44,15 +48,6 @@ bool ProgressIndication::updateProgress(const Progress & value)
|
|||||||
return progress.incrementPiecewiseAtomically(value);
|
return progress.incrementPiecewiseAtomically(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProgressIndication::clearProgressOutput()
|
|
||||||
{
|
|
||||||
if (written_progress_chars)
|
|
||||||
{
|
|
||||||
written_progress_chars = 0;
|
|
||||||
std::cerr << "\r" CLEAR_TO_END_OF_LINE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProgressIndication::resetProgress()
|
void ProgressIndication::resetProgress()
|
||||||
{
|
{
|
||||||
watch.restart();
|
watch.restart();
|
||||||
@ -67,15 +62,12 @@ void ProgressIndication::resetProgress()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProgressIndication::setFileProgressCallback(ContextMutablePtr context, bool write_progress_on_update_)
|
void ProgressIndication::setFileProgressCallback(ContextMutablePtr context, WriteBufferFromFileDescriptor & message)
|
||||||
{
|
{
|
||||||
write_progress_on_update = write_progress_on_update_;
|
|
||||||
context->setFileProgressCallback([&](const FileProgress & file_progress)
|
context->setFileProgressCallback([&](const FileProgress & file_progress)
|
||||||
{
|
{
|
||||||
progress.incrementPiecewiseAtomically(Progress(file_progress));
|
progress.incrementPiecewiseAtomically(Progress(file_progress));
|
||||||
|
writeProgress(message);
|
||||||
if (write_progress_on_update)
|
|
||||||
writeProgress();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,13 +134,10 @@ void ProgressIndication::writeFinalProgress()
|
|||||||
std::cout << ". ";
|
std::cout << ". ";
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProgressIndication::writeProgress()
|
void ProgressIndication::writeProgress(WriteBufferFromFileDescriptor & message)
|
||||||
{
|
{
|
||||||
std::lock_guard lock(progress_mutex);
|
std::lock_guard lock(progress_mutex);
|
||||||
|
|
||||||
/// Output all progress bar commands to stderr at once to avoid flicker.
|
|
||||||
WriteBufferFromFileDescriptor message(STDERR_FILENO, 1024);
|
|
||||||
|
|
||||||
static size_t increment = 0;
|
static size_t increment = 0;
|
||||||
static const char * indicators[8] = {
|
static const char * indicators[8] = {
|
||||||
"\033[1;30m→\033[0m",
|
"\033[1;30m→\033[0m",
|
||||||
@ -307,4 +296,14 @@ void ProgressIndication::writeProgress()
|
|||||||
message.next();
|
message.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProgressIndication::clearProgressOutput(WriteBufferFromFileDescriptor & message)
|
||||||
|
{
|
||||||
|
if (written_progress_chars)
|
||||||
|
{
|
||||||
|
written_progress_chars = 0;
|
||||||
|
message << "\r" CLEAR_TO_END_OF_LINE;
|
||||||
|
message.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,12 +9,12 @@
|
|||||||
#include <Common/Stopwatch.h>
|
#include <Common/Stopwatch.h>
|
||||||
#include <Common/EventRateMeter.h>
|
#include <Common/EventRateMeter.h>
|
||||||
|
|
||||||
/// http://en.wikipedia.org/wiki/ANSI_escape_code
|
|
||||||
#define CLEAR_TO_END_OF_LINE "\033[K"
|
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class WriteBufferFromFileDescriptor;
|
||||||
|
|
||||||
struct ThreadEventData
|
struct ThreadEventData
|
||||||
{
|
{
|
||||||
UInt64 time() const noexcept { return user_ms + system_ms; }
|
UInt64 time() const noexcept { return user_ms + system_ms; }
|
||||||
@ -30,14 +30,13 @@ using HostToThreadTimesMap = std::unordered_map<String, ThreadIdToTimeMap>;
|
|||||||
class ProgressIndication
|
class ProgressIndication
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Write progress to stderr.
|
/// Write progress bar.
|
||||||
void writeProgress();
|
void writeProgress(WriteBufferFromFileDescriptor & message);
|
||||||
|
void clearProgressOutput(WriteBufferFromFileDescriptor & message);
|
||||||
|
|
||||||
|
/// Write summary.
|
||||||
void writeFinalProgress();
|
void writeFinalProgress();
|
||||||
|
|
||||||
/// Clear stderr output.
|
|
||||||
void clearProgressOutput();
|
|
||||||
|
|
||||||
/// Reset progress values.
|
/// Reset progress values.
|
||||||
void resetProgress();
|
void resetProgress();
|
||||||
|
|
||||||
@ -52,7 +51,7 @@ public:
|
|||||||
/// In some cases there is a need to update progress value, when there is no access to progress_inidcation object.
|
/// In some cases there is a need to update progress value, when there is no access to progress_inidcation object.
|
||||||
/// In this case it is added via context.
|
/// In this case it is added via context.
|
||||||
/// `write_progress_on_update` is needed to write progress for loading files data via pipe in non-interactive mode.
|
/// `write_progress_on_update` is needed to write progress for loading files data via pipe in non-interactive mode.
|
||||||
void setFileProgressCallback(ContextMutablePtr context, bool write_progress_on_update = false);
|
void setFileProgressCallback(ContextMutablePtr context, WriteBufferFromFileDescriptor & message);
|
||||||
|
|
||||||
/// How much seconds passed since query execution start.
|
/// How much seconds passed since query execution start.
|
||||||
double elapsedSeconds() const { return getElapsedNanoseconds() / 1e9; }
|
double elapsedSeconds() const { return getElapsedNanoseconds() / 1e9; }
|
||||||
|
@ -3,10 +3,10 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <Core/Defines.h>
|
#include <Core/Defines.h>
|
||||||
#include <Common/Stopwatch.h>
|
|
||||||
#include <Common/TargetSpecific.h>
|
|
||||||
#include <base/types.h>
|
#include <base/types.h>
|
||||||
#include <base/unaligned.h>
|
#include <base/unaligned.h>
|
||||||
|
#include <Common/Stopwatch.h>
|
||||||
|
#include <Common/TargetSpecific.h>
|
||||||
|
|
||||||
#ifdef __SSE2__
|
#ifdef __SSE2__
|
||||||
#include <emmintrin.h>
|
#include <emmintrin.h>
|
||||||
@ -599,6 +599,9 @@ bool NO_INLINE decompressImpl(const char * const source, char * const dest, size
|
|||||||
|
|
||||||
copy_end = op + length;
|
copy_end = op + length;
|
||||||
|
|
||||||
|
if (unlikely(copy_end > output_end))
|
||||||
|
return false;
|
||||||
|
|
||||||
/** Here we can write up to copy_amount - 1 - 4 * 2 bytes after buffer.
|
/** Here we can write up to copy_amount - 1 - 4 * 2 bytes after buffer.
|
||||||
* The worst case when offset = 1 and length = 4
|
* The worst case when offset = 1 and length = 4
|
||||||
*/
|
*/
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
#include <Compression/CompressionFactory.h>
|
#include <Compression/CompressionFactory.h>
|
||||||
|
|
||||||
#include <Common/PODArray.h>
|
|
||||||
#include <Common/Stopwatch.h>
|
|
||||||
#include <base/types.h>
|
|
||||||
#include <DataTypes/DataTypesNumber.h>
|
#include <DataTypes/DataTypesNumber.h>
|
||||||
#include <DataTypes/IDataType.h>
|
#include <DataTypes/IDataType.h>
|
||||||
#include <IO/ReadBufferFromMemory.h>
|
#include <IO/ReadBufferFromMemory.h>
|
||||||
@ -10,6 +7,12 @@
|
|||||||
#include <Parsers/ExpressionElementParsers.h>
|
#include <Parsers/ExpressionElementParsers.h>
|
||||||
#include <Parsers/IParser.h>
|
#include <Parsers/IParser.h>
|
||||||
#include <Parsers/TokenIterator.h>
|
#include <Parsers/TokenIterator.h>
|
||||||
|
#include <base/types.h>
|
||||||
|
#include <Common/PODArray.h>
|
||||||
|
#include <Common/Stopwatch.h>
|
||||||
|
|
||||||
|
#include <Compression/LZ4_decompress_faster.h>
|
||||||
|
#include <IO/BufferWithOwnMemory.h>
|
||||||
|
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
@ -1319,4 +1322,34 @@ INSTANTIATE_TEST_SUITE_P(Gorilla,
|
|||||||
// ),
|
// ),
|
||||||
//);
|
//);
|
||||||
|
|
||||||
|
TEST(LZ4Test, DecompressMalformedInput)
|
||||||
|
{
|
||||||
|
/// This malformed input was initially found by lz4_decompress_fuzzer and causes failure under UBSAN.
|
||||||
|
constexpr unsigned char data[]
|
||||||
|
= {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00,
|
||||||
|
0x00, 0x20, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0xff, 0xff, 0xff, 0x17, 0xff, 0xff, 0x0f, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||||
|
0xfe, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||||
|
|
||||||
|
const char * const source = reinterpret_cast<const char * const>(data);
|
||||||
|
const uint32_t source_size = std::size(data);
|
||||||
|
constexpr uint32_t uncompressed_size = 80;
|
||||||
|
|
||||||
|
DB::Memory<> memory;
|
||||||
|
memory.resize(ICompressionCodec::getHeaderSize() + uncompressed_size + LZ4::ADDITIONAL_BYTES_AT_END_OF_BUFFER);
|
||||||
|
unalignedStoreLE<uint8_t>(memory.data(), static_cast<uint8_t>(CompressionMethodByte::LZ4));
|
||||||
|
unalignedStoreLE<uint32_t>(&memory[1], source_size);
|
||||||
|
unalignedStoreLE<uint32_t>(&memory[5], uncompressed_size);
|
||||||
|
|
||||||
|
auto codec = CompressionCodecFactory::instance().get("LZ4", {});
|
||||||
|
ASSERT_THROW(codec->decompress(source, source_size, memory.data()), Exception);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ void CoordinationSettings::loadFromConfig(const String & config_elem, const Poco
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const String KeeperConfigurationAndSettings::DEFAULT_FOUR_LETTER_WORD_CMD = "conf,cons,crst,envi,ruok,srst,srvr,stat,wchs,dirs,mntr,isro,rcvr,apiv";
|
const String KeeperConfigurationAndSettings::DEFAULT_FOUR_LETTER_WORD_CMD = "conf,cons,crst,envi,ruok,srst,srvr,stat,wchs,dirs,mntr,isro,rcvr,apiv,csnp,lgif";
|
||||||
|
|
||||||
KeeperConfigurationAndSettings::KeeperConfigurationAndSettings()
|
KeeperConfigurationAndSettings::KeeperConfigurationAndSettings()
|
||||||
: server_id(NOT_EXIST)
|
: server_id(NOT_EXIST)
|
||||||
|
@ -136,6 +136,12 @@ void FourLetterCommandFactory::registerCommands(KeeperDispatcher & keeper_dispat
|
|||||||
FourLetterCommandPtr api_version_command = std::make_shared<ApiVersionCommand>(keeper_dispatcher);
|
FourLetterCommandPtr api_version_command = std::make_shared<ApiVersionCommand>(keeper_dispatcher);
|
||||||
factory.registerCommand(api_version_command);
|
factory.registerCommand(api_version_command);
|
||||||
|
|
||||||
|
FourLetterCommandPtr create_snapshot_command = std::make_shared<CreateSnapshotCommand>(keeper_dispatcher);
|
||||||
|
factory.registerCommand(create_snapshot_command);
|
||||||
|
|
||||||
|
FourLetterCommandPtr log_info_command = std::make_shared<LogInfoCommand>(keeper_dispatcher);
|
||||||
|
factory.registerCommand(log_info_command);
|
||||||
|
|
||||||
factory.initializeAllowList(keeper_dispatcher);
|
factory.initializeAllowList(keeper_dispatcher);
|
||||||
factory.setInitialize(true);
|
factory.setInitialize(true);
|
||||||
}
|
}
|
||||||
@ -472,4 +478,33 @@ String ApiVersionCommand::run()
|
|||||||
return toString(static_cast<uint8_t>(Coordination::current_keeper_api_version));
|
return toString(static_cast<uint8_t>(Coordination::current_keeper_api_version));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String CreateSnapshotCommand::run()
|
||||||
|
{
|
||||||
|
auto log_index = keeper_dispatcher.createSnapshot();
|
||||||
|
return log_index > 0 ? std::to_string(log_index) : "Failed to schedule snapshot creation task.";
|
||||||
|
}
|
||||||
|
|
||||||
|
String LogInfoCommand::run()
|
||||||
|
{
|
||||||
|
KeeperLogInfo log_info = keeper_dispatcher.getKeeperLogInfo();
|
||||||
|
StringBuffer ret;
|
||||||
|
|
||||||
|
auto append = [&ret] (String key, uint64_t value) -> void
|
||||||
|
{
|
||||||
|
writeText(key, ret);
|
||||||
|
writeText('\t', ret);
|
||||||
|
writeText(std::to_string(value), ret);
|
||||||
|
writeText('\n', ret);
|
||||||
|
};
|
||||||
|
append("first_log_idx", log_info.first_log_idx);
|
||||||
|
append("first_log_term", log_info.first_log_idx);
|
||||||
|
append("last_log_idx", log_info.last_log_idx);
|
||||||
|
append("last_log_term", log_info.last_log_term);
|
||||||
|
append("last_committed_log_idx", log_info.last_committed_log_idx);
|
||||||
|
append("leader_committed_log_idx", log_info.leader_committed_log_idx);
|
||||||
|
append("target_committed_log_idx", log_info.target_committed_log_idx);
|
||||||
|
append("last_snapshot_idx", log_info.last_snapshot_idx);
|
||||||
|
return ret.str();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ using FourLetterCommandPtr = std::shared_ptr<DB::IFourLetterCommand>;
|
|||||||
/// Just like zookeeper Four Letter Words commands, CH Keeper responds to a small set of commands.
|
/// Just like zookeeper Four Letter Words commands, CH Keeper responds to a small set of commands.
|
||||||
/// Each command is composed of four letters, these commands are useful to monitor and issue system problems.
|
/// Each command is composed of four letters, these commands are useful to monitor and issue system problems.
|
||||||
/// The feature is based on Zookeeper 3.5.9, details is in https://zookeeper.apache.org/doc/r3.5.9/zookeeperAdmin.html#sc_zkCommands.
|
/// The feature is based on Zookeeper 3.5.9, details is in https://zookeeper.apache.org/doc/r3.5.9/zookeeperAdmin.html#sc_zkCommands.
|
||||||
|
/// Also we add some additional commands such as csnp, lgif etc.
|
||||||
struct IFourLetterCommand
|
struct IFourLetterCommand
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -327,4 +328,40 @@ struct ApiVersionCommand : public IFourLetterCommand
|
|||||||
String run() override;
|
String run() override;
|
||||||
~ApiVersionCommand() override = default;
|
~ApiVersionCommand() override = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Create snapshot manually
|
||||||
|
struct CreateSnapshotCommand : public IFourLetterCommand
|
||||||
|
{
|
||||||
|
explicit CreateSnapshotCommand(KeeperDispatcher & keeper_dispatcher_)
|
||||||
|
: IFourLetterCommand(keeper_dispatcher_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
String name() override { return "csnp"; }
|
||||||
|
String run() override;
|
||||||
|
~CreateSnapshotCommand() override = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Raft log information:
|
||||||
|
* first_log_idx 1
|
||||||
|
* first_log_term 1
|
||||||
|
* last_log_idx 101
|
||||||
|
* last_log_term 1
|
||||||
|
* last_committed_idx 100
|
||||||
|
* leader_committed_log_idx 101
|
||||||
|
* target_committed_log_idx 101
|
||||||
|
* last_snapshot_idx 50
|
||||||
|
*/
|
||||||
|
struct LogInfoCommand : public IFourLetterCommand
|
||||||
|
{
|
||||||
|
explicit LogInfoCommand(KeeperDispatcher & keeper_dispatcher_)
|
||||||
|
: IFourLetterCommand(keeper_dispatcher_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
String name() override { return "lgif"; }
|
||||||
|
String run() override;
|
||||||
|
~LogInfoCommand() override = default;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -47,4 +47,32 @@ struct Keeper4LWInfo
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Keeper log information for 4lw commands
|
||||||
|
struct KeeperLogInfo
|
||||||
|
{
|
||||||
|
/// My first log index in log store.
|
||||||
|
uint64_t first_log_idx;
|
||||||
|
|
||||||
|
/// My first log term.
|
||||||
|
uint64_t first_log_term;
|
||||||
|
|
||||||
|
/// My last log index in log store.
|
||||||
|
uint64_t last_log_idx;
|
||||||
|
|
||||||
|
/// My last log term.
|
||||||
|
uint64_t last_log_term;
|
||||||
|
|
||||||
|
/// My last committed log index in state machine.
|
||||||
|
uint64_t last_committed_log_idx;
|
||||||
|
|
||||||
|
/// Leader's committed log index from my perspective.
|
||||||
|
uint64_t leader_committed_log_idx;
|
||||||
|
|
||||||
|
/// Target log index should be committed to.
|
||||||
|
uint64_t target_committed_log_idx;
|
||||||
|
|
||||||
|
/// The largest committed log index in last snapshot.
|
||||||
|
uint64_t last_snapshot_idx;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -203,6 +203,18 @@ public:
|
|||||||
{
|
{
|
||||||
keeper_stats.reset();
|
keeper_stats.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create snapshot manually, return the last committed log index in the snapshot
|
||||||
|
uint64_t createSnapshot()
|
||||||
|
{
|
||||||
|
return server->createSnapshot();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get Raft information
|
||||||
|
KeeperLogInfo getKeeperLogInfo()
|
||||||
|
{
|
||||||
|
return server->getKeeperLogInfo();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -907,4 +907,29 @@ Keeper4LWInfo KeeperServer::getPartiallyFilled4LWInfo() const
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t KeeperServer::createSnapshot()
|
||||||
|
{
|
||||||
|
uint64_t log_idx = raft_instance->create_snapshot();
|
||||||
|
if (log_idx != 0)
|
||||||
|
LOG_INFO(log, "Snapshot creation scheduled with last committed log index {}.", log_idx);
|
||||||
|
else
|
||||||
|
LOG_WARNING(log, "Failed to schedule snapshot creation task.");
|
||||||
|
return log_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
KeeperLogInfo KeeperServer::getKeeperLogInfo()
|
||||||
|
{
|
||||||
|
KeeperLogInfo log_info;
|
||||||
|
auto log_store = state_manager->load_log_store();
|
||||||
|
log_info.first_log_idx = log_store->start_index();
|
||||||
|
log_info.first_log_term = log_store->term_at(log_info.first_log_idx);
|
||||||
|
log_info.last_log_idx = raft_instance->get_last_log_idx();
|
||||||
|
log_info.last_log_term = raft_instance->get_last_log_term();
|
||||||
|
log_info.last_committed_log_idx = raft_instance->get_committed_log_idx();
|
||||||
|
log_info.leader_committed_log_idx = raft_instance->get_leader_committed_log_idx();
|
||||||
|
log_info.target_committed_log_idx = raft_instance->get_target_committed_log_idx();
|
||||||
|
log_info.last_snapshot_idx = raft_instance->get_last_snapshot_idx();
|
||||||
|
return log_info;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -131,6 +131,10 @@ public:
|
|||||||
/// Wait configuration update for action. Used by followers.
|
/// Wait configuration update for action. Used by followers.
|
||||||
/// Return true if update was successfully received.
|
/// Return true if update was successfully received.
|
||||||
bool waitConfigurationUpdate(const ConfigUpdateAction & task);
|
bool waitConfigurationUpdate(const ConfigUpdateAction & task);
|
||||||
|
|
||||||
|
uint64_t createSnapshot();
|
||||||
|
|
||||||
|
KeeperLogInfo getKeeperLogInfo();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -377,6 +377,9 @@ void KeeperStorage::UncommittedState::commit(int64_t commit_zxid)
|
|||||||
{
|
{
|
||||||
assert(deltas.empty() || deltas.front().zxid >= commit_zxid);
|
assert(deltas.empty() || deltas.front().zxid >= commit_zxid);
|
||||||
|
|
||||||
|
// collect nodes that have no further modification in the current transaction
|
||||||
|
std::unordered_set<std::string> modified_nodes;
|
||||||
|
|
||||||
while (!deltas.empty() && deltas.front().zxid == commit_zxid)
|
while (!deltas.empty() && deltas.front().zxid == commit_zxid)
|
||||||
{
|
{
|
||||||
if (std::holds_alternative<SubDeltaEnd>(deltas.front().operation))
|
if (std::holds_alternative<SubDeltaEnd>(deltas.front().operation))
|
||||||
@ -393,7 +396,17 @@ void KeeperStorage::UncommittedState::commit(int64_t commit_zxid)
|
|||||||
assert(path_deltas.front() == &front_delta);
|
assert(path_deltas.front() == &front_delta);
|
||||||
path_deltas.pop_front();
|
path_deltas.pop_front();
|
||||||
if (path_deltas.empty())
|
if (path_deltas.empty())
|
||||||
|
{
|
||||||
deltas_for_path.erase(front_delta.path);
|
deltas_for_path.erase(front_delta.path);
|
||||||
|
|
||||||
|
// no more deltas for path -> no modification
|
||||||
|
modified_nodes.insert(std::move(front_delta.path));
|
||||||
|
}
|
||||||
|
else if (path_deltas.front()->zxid > commit_zxid)
|
||||||
|
{
|
||||||
|
// next delta has a zxid from a different transaction -> no modification in this transaction
|
||||||
|
modified_nodes.insert(std::move(front_delta.path));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (auto * add_auth = std::get_if<AddAuthDelta>(&front_delta.operation))
|
else if (auto * add_auth = std::get_if<AddAuthDelta>(&front_delta.operation))
|
||||||
{
|
{
|
||||||
@ -409,9 +422,12 @@ void KeeperStorage::UncommittedState::commit(int64_t commit_zxid)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// delete all cached nodes that were not modified after the commit_zxid
|
// delete all cached nodes that were not modified after the commit_zxid
|
||||||
// the commit can end on SubDeltaEnd so we don't want to clear cached nodes too soon
|
// we only need to check the nodes that were modified in this transaction
|
||||||
if (deltas.empty() || deltas.front().zxid > commit_zxid)
|
for (const auto & node : modified_nodes)
|
||||||
std::erase_if(nodes, [commit_zxid](const auto & node) { return node.second.zxid == commit_zxid; });
|
{
|
||||||
|
if (nodes[node].zxid == commit_zxid)
|
||||||
|
nodes.erase(node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeeperStorage::UncommittedState::rollback(int64_t rollback_zxid)
|
void KeeperStorage::UncommittedState::rollback(int64_t rollback_zxid)
|
||||||
|
@ -411,6 +411,7 @@ inline bool isDecimal(const DataTypePtr & data_type) { return WhichDataType(data
|
|||||||
inline bool isTuple(const DataTypePtr & data_type) { return WhichDataType(data_type).isTuple(); }
|
inline bool isTuple(const DataTypePtr & data_type) { return WhichDataType(data_type).isTuple(); }
|
||||||
inline bool isArray(const DataTypePtr & data_type) { return WhichDataType(data_type).isArray(); }
|
inline bool isArray(const DataTypePtr & data_type) { return WhichDataType(data_type).isArray(); }
|
||||||
inline bool isMap(const DataTypePtr & data_type) {return WhichDataType(data_type).isMap(); }
|
inline bool isMap(const DataTypePtr & data_type) {return WhichDataType(data_type).isMap(); }
|
||||||
|
inline bool isInterval(const DataTypePtr & data_type) {return WhichDataType(data_type).isInterval(); }
|
||||||
inline bool isNothing(const DataTypePtr & data_type) { return WhichDataType(data_type).isNothing(); }
|
inline bool isNothing(const DataTypePtr & data_type) { return WhichDataType(data_type).isNothing(); }
|
||||||
inline bool isUUID(const DataTypePtr & data_type) { return WhichDataType(data_type).isUUID(); }
|
inline bool isUUID(const DataTypePtr & data_type) { return WhichDataType(data_type).isUUID(); }
|
||||||
|
|
||||||
|
@ -84,11 +84,12 @@ void SerializationString::deserializeBinary(IColumn & column, ReadBuffer & istr)
|
|||||||
|
|
||||||
void SerializationString::serializeBinaryBulk(const IColumn & column, WriteBuffer & ostr, size_t offset, size_t limit) const
|
void SerializationString::serializeBinaryBulk(const IColumn & column, WriteBuffer & ostr, size_t offset, size_t limit) const
|
||||||
{
|
{
|
||||||
const ColumnString & column_string = typeid_cast<const ColumnString &>(column);
|
const auto & full_column = column.convertToFullColumnIfLowCardinality();
|
||||||
|
const ColumnString & column_string = typeid_cast<const ColumnString &>(*full_column);
|
||||||
const ColumnString::Chars & data = column_string.getChars();
|
const ColumnString::Chars & data = column_string.getChars();
|
||||||
const ColumnString::Offsets & offsets = column_string.getOffsets();
|
const ColumnString::Offsets & offsets = column_string.getOffsets();
|
||||||
|
|
||||||
size_t size = column.size();
|
size_t size = column_string.size();
|
||||||
if (!size)
|
if (!size)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -62,11 +62,12 @@ std::pair<String, StoragePtr> createTableFromAST(
|
|||||||
if (ast_create_query.as_table_function)
|
if (ast_create_query.as_table_function)
|
||||||
{
|
{
|
||||||
const auto & factory = TableFunctionFactory::instance();
|
const auto & factory = TableFunctionFactory::instance();
|
||||||
auto table_function = factory.get(ast_create_query.as_table_function, context);
|
auto table_function_ast = ast_create_query.as_table_function->ptr();
|
||||||
|
auto table_function = factory.get(table_function_ast, context);
|
||||||
ColumnsDescription columns;
|
ColumnsDescription columns;
|
||||||
if (ast_create_query.columns_list && ast_create_query.columns_list->columns)
|
if (ast_create_query.columns_list && ast_create_query.columns_list->columns)
|
||||||
columns = InterpreterCreateQuery::getColumnsDescription(*ast_create_query.columns_list->columns, context, true);
|
columns = InterpreterCreateQuery::getColumnsDescription(*ast_create_query.columns_list->columns, context, true);
|
||||||
StoragePtr storage = table_function->execute(ast_create_query.as_table_function, context, ast_create_query.getTable(), std::move(columns));
|
StoragePtr storage = table_function->execute(table_function_ast, context, ast_create_query.getTable(), std::move(columns));
|
||||||
storage->renameInMemory(ast_create_query);
|
storage->renameInMemory(ast_create_query);
|
||||||
return {ast_create_query.getTable(), storage};
|
return {ast_create_query.getTable(), storage};
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,6 @@
|
|||||||
#include <Common/quoteString.h>
|
#include <Common/quoteString.h>
|
||||||
#include <Common/setThreadName.h>
|
#include <Common/setThreadName.h>
|
||||||
#include <base/sleep.h>
|
#include <base/sleep.h>
|
||||||
#include <base/bit_cast.h>
|
|
||||||
#include <boost/algorithm/string/split.hpp>
|
#include <boost/algorithm/string/split.hpp>
|
||||||
#include <boost/algorithm/string/trim.hpp>
|
#include <boost/algorithm/string/trim.hpp>
|
||||||
#include <Parsers/CommonParsers.h>
|
#include <Parsers/CommonParsers.h>
|
||||||
|
@ -443,6 +443,11 @@ ASTPtr DatabasePostgreSQL::getColumnDeclaration(const DataTypePtr & data_type) c
|
|||||||
if (which.isArray())
|
if (which.isArray())
|
||||||
return makeASTFunction("Array", getColumnDeclaration(typeid_cast<const DataTypeArray *>(data_type.get())->getNestedType()));
|
return makeASTFunction("Array", getColumnDeclaration(typeid_cast<const DataTypeArray *>(data_type.get())->getNestedType()));
|
||||||
|
|
||||||
|
if (which.isDateTime64())
|
||||||
|
{
|
||||||
|
return makeASTFunction("DateTime64", std::make_shared<ASTLiteral>(static_cast<UInt32>(6)));
|
||||||
|
}
|
||||||
|
|
||||||
return std::make_shared<ASTIdentifier>(data_type->getName());
|
return std::make_shared<ASTIdentifier>(data_type->getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ enum class DataSourceType
|
|||||||
Local,
|
Local,
|
||||||
RAM,
|
RAM,
|
||||||
S3,
|
S3,
|
||||||
|
S3_Plain,
|
||||||
HDFS,
|
HDFS,
|
||||||
WebServer,
|
WebServer,
|
||||||
AzureBlobStorage,
|
AzureBlobStorage,
|
||||||
@ -26,6 +27,8 @@ inline String toString(DataSourceType data_source_type)
|
|||||||
return "memory";
|
return "memory";
|
||||||
case DataSourceType::S3:
|
case DataSourceType::S3:
|
||||||
return "s3";
|
return "s3";
|
||||||
|
case DataSourceType::S3_Plain:
|
||||||
|
return "s3_plain";
|
||||||
case DataSourceType::HDFS:
|
case DataSourceType::HDFS:
|
||||||
return "hdfs";
|
return "hdfs";
|
||||||
case DataSourceType::WebServer:
|
case DataSourceType::WebServer:
|
||||||
|
@ -213,7 +213,9 @@ public:
|
|||||||
template <class ...Args>
|
template <class ...Args>
|
||||||
S3PlainObjectStorage(Args && ...args)
|
S3PlainObjectStorage(Args && ...args)
|
||||||
: S3ObjectStorage("S3PlainObjectStorage", std::forward<Args>(args)...)
|
: S3ObjectStorage("S3PlainObjectStorage", std::forward<Args>(args)...)
|
||||||
{}
|
{
|
||||||
|
data_source_description.type = DataSourceType::S3_Plain;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,21 +2,19 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#if USE_BASE64
|
#if USE_BASE64
|
||||||
# include <Columns/ColumnConst.h>
|
# include <Columns/ColumnFixedString.h>
|
||||||
# include <Common/MemorySanitizer.h>
|
|
||||||
# include <Columns/ColumnString.h>
|
# include <Columns/ColumnString.h>
|
||||||
# include <DataTypes/DataTypeString.h>
|
# include <DataTypes/DataTypeString.h>
|
||||||
# include <Functions/FunctionFactory.h>
|
|
||||||
# include <Functions/FunctionHelpers.h>
|
# include <Functions/FunctionHelpers.h>
|
||||||
# include <Functions/GatherUtils/Algorithms.h>
|
# include <Functions/IFunction.h>
|
||||||
# include <IO/WriteHelpers.h>
|
# include <Interpreters/Context_fwd.h>
|
||||||
# include <turbob64.h>
|
# include <turbob64.h>
|
||||||
|
# include <Common/MemorySanitizer.h>
|
||||||
|
|
||||||
|
# include <span>
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
{
|
{
|
||||||
using namespace GatherUtils;
|
|
||||||
|
|
||||||
namespace ErrorCodes
|
namespace ErrorCodes
|
||||||
{
|
{
|
||||||
extern const int BAD_ARGUMENTS;
|
extern const int BAD_ARGUMENTS;
|
||||||
@ -25,33 +23,86 @@ namespace ErrorCodes
|
|||||||
extern const int INCORRECT_DATA;
|
extern const int INCORRECT_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Detail
|
||||||
|
{
|
||||||
|
inline size_t base64Decode(const std::span<const UInt8> src, UInt8 * dst)
|
||||||
|
{
|
||||||
|
# if defined(__aarch64__)
|
||||||
|
return tb64sdec(reinterpret_cast<const uint8_t *>(src.data()), src.size(), reinterpret_cast<uint8_t *>(dst));
|
||||||
|
# else
|
||||||
|
return _tb64d(reinterpret_cast<const uint8_t *>(src.data()), src.size(), reinterpret_cast<uint8_t *>(dst));
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct Base64Encode
|
struct Base64Encode
|
||||||
{
|
{
|
||||||
static constexpr auto name = "base64Encode";
|
static constexpr auto name = "base64Encode";
|
||||||
static size_t getBufferSize(size_t string_length, size_t string_count)
|
|
||||||
|
static size_t getBufferSize(const size_t string_length, const size_t string_count)
|
||||||
{
|
{
|
||||||
return ((string_length - string_count) / 3 + string_count) * 4 + string_count;
|
return ((string_length - string_count) / 3 + string_count) * 4 + string_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t performCoding(const std::span<const UInt8> src, UInt8 * dst)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Some bug in sse arm64 implementation?
|
||||||
|
* `base64Encode(repeat('a', 46))` returns wrong padding character
|
||||||
|
*/
|
||||||
|
# if defined(__aarch64__)
|
||||||
|
return tb64senc(reinterpret_cast<const uint8_t *>(src.data()), src.size(), reinterpret_cast<uint8_t *>(dst));
|
||||||
|
# else
|
||||||
|
return _tb64e(reinterpret_cast<const uint8_t *>(src.data()), src.size(), reinterpret_cast<uint8_t *>(dst));
|
||||||
|
# endif
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Base64Decode
|
struct Base64Decode
|
||||||
{
|
{
|
||||||
static constexpr auto name = "base64Decode";
|
static constexpr auto name = "base64Decode";
|
||||||
|
|
||||||
static size_t getBufferSize(size_t string_length, size_t string_count)
|
static size_t getBufferSize(const size_t string_length, const size_t string_count)
|
||||||
{
|
{
|
||||||
return ((string_length - string_count) / 4 + string_count) * 3 + string_count;
|
return ((string_length - string_count) / 4 + string_count) * 3 + string_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t performCoding(const std::span<const UInt8> src, UInt8 * dst)
|
||||||
|
{
|
||||||
|
const auto outlen = Detail::base64Decode(src, dst);
|
||||||
|
if (src.size() > 0 && !outlen)
|
||||||
|
throw Exception(
|
||||||
|
ErrorCodes::INCORRECT_DATA,
|
||||||
|
"Failed to {} input '{}'",
|
||||||
|
name,
|
||||||
|
String(reinterpret_cast<const char *>(src.data()), src.size()));
|
||||||
|
|
||||||
|
return outlen;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TryBase64Decode
|
struct TryBase64Decode
|
||||||
{
|
{
|
||||||
static constexpr auto name = "tryBase64Decode";
|
static constexpr auto name = "tryBase64Decode";
|
||||||
|
|
||||||
static size_t getBufferSize(size_t string_length, size_t string_count)
|
static size_t getBufferSize(const size_t string_length, const size_t string_count)
|
||||||
{
|
{
|
||||||
return Base64Decode::getBufferSize(string_length, string_count);
|
return Base64Decode::getBufferSize(string_length, string_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t performCoding(const std::span<const UInt8> src, UInt8 * dst)
|
||||||
|
{
|
||||||
|
if (src.empty())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
const auto outlen = Detail::base64Decode(src, dst);
|
||||||
|
// during decoding character array can be partially polluted
|
||||||
|
// if fail, revert back and clean
|
||||||
|
if (!outlen)
|
||||||
|
*dst = 0;
|
||||||
|
|
||||||
|
return outlen;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Func>
|
template <typename Func>
|
||||||
@ -71,99 +122,60 @@ public:
|
|||||||
if (arguments.size() != 1)
|
if (arguments.size() != 1)
|
||||||
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Wrong number of arguments for function {}: 1 expected.", getName());
|
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Wrong number of arguments for function {}: 1 expected.", getName());
|
||||||
|
|
||||||
if (!WhichDataType(arguments[0].type).isString())
|
if (!WhichDataType(arguments[0].type).isStringOrFixedString())
|
||||||
throw Exception(
|
throw Exception(
|
||||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
||||||
"Illegal type {} of 1st argument of function {}. Must be String.",
|
"Illegal type {} of 1st argument of function {}. Must be FixedString or String.",
|
||||||
arguments[0].type->getName(), getName());
|
arguments[0].type->getName(),
|
||||||
|
getName());
|
||||||
|
|
||||||
return std::make_shared<DataTypeString>();
|
return std::make_shared<DataTypeString>();
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t input_rows_count) const override
|
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, const size_t input_rows_count) const override
|
||||||
{
|
{
|
||||||
const ColumnPtr column_string = arguments[0].column;
|
const auto & input_column = arguments[0].column;
|
||||||
const ColumnString * input = checkAndGetColumn<ColumnString>(column_string.get());
|
if (const auto * src_column_as_fixed_string = checkAndGetColumn<ColumnFixedString>(*input_column))
|
||||||
|
return execute(*src_column_as_fixed_string, input_rows_count);
|
||||||
|
else if (const auto * src_column_as_string = checkAndGetColumn<ColumnString>(*input_column))
|
||||||
|
return execute(*src_column_as_string, input_rows_count);
|
||||||
|
|
||||||
if (!input)
|
|
||||||
throw Exception(
|
throw Exception(
|
||||||
ErrorCodes::ILLEGAL_COLUMN,
|
ErrorCodes::ILLEGAL_COLUMN,
|
||||||
"Illegal column {} of first argument of function {}, must be of type String",
|
"Illegal column {} of first argument of function {}, must be of type FixedString or String.",
|
||||||
arguments[0].column->getName(), getName());
|
input_column->getName(),
|
||||||
|
getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static ColumnPtr execute(const ColumnString & src_column, const size_t src_row_count)
|
||||||
|
{
|
||||||
auto dst_column = ColumnString::create();
|
auto dst_column = ColumnString::create();
|
||||||
auto & dst_data = dst_column->getChars();
|
auto & dst_chars = dst_column->getChars();
|
||||||
auto & dst_offsets = dst_column->getOffsets();
|
auto & dst_offsets = dst_column->getOffsets();
|
||||||
|
|
||||||
size_t reserve = Func::getBufferSize(input->getChars().size(), input->size());
|
const auto reserve = Func::getBufferSize(src_column.byteSize(), src_column.size());
|
||||||
dst_data.resize(reserve);
|
dst_chars.resize(reserve);
|
||||||
dst_offsets.resize(input_rows_count);
|
dst_offsets.resize(src_row_count);
|
||||||
|
|
||||||
const ColumnString::Offsets & src_offsets = input->getOffsets();
|
const auto & src_chars = src_column.getChars();
|
||||||
|
const auto & src_offsets = src_column.getOffsets();
|
||||||
|
|
||||||
const auto * source = input->getChars().data();
|
auto * dst = dst_chars.data();
|
||||||
auto * dst = dst_data.data();
|
|
||||||
auto * dst_pos = dst;
|
auto * dst_pos = dst;
|
||||||
|
const auto * src = src_chars.data();
|
||||||
|
|
||||||
size_t src_offset_prev = 0;
|
size_t src_offset_prev = 0;
|
||||||
|
for (size_t row = 0; row < src_row_count; ++row)
|
||||||
for (size_t row = 0; row < input_rows_count; ++row)
|
|
||||||
{
|
{
|
||||||
size_t srclen = src_offsets[row] - src_offset_prev - 1;
|
const size_t src_length = src_offsets[row] - src_offset_prev - 1;
|
||||||
size_t outlen = 0;
|
const auto outlen = Func::performCoding({src, src_length}, dst_pos);
|
||||||
|
|
||||||
if constexpr (std::is_same_v<Func, Base64Encode>)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Some bug in sse arm64 implementation?
|
|
||||||
* `base64Encode(repeat('a', 46))` returns wrong padding character
|
|
||||||
*/
|
|
||||||
#if defined(__aarch64__)
|
|
||||||
outlen = tb64senc(reinterpret_cast<const uint8_t *>(source), srclen, reinterpret_cast<uint8_t *>(dst_pos));
|
|
||||||
#else
|
|
||||||
outlen = _tb64e(reinterpret_cast<const uint8_t *>(source), srclen, reinterpret_cast<uint8_t *>(dst_pos));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else if constexpr (std::is_same_v<Func, Base64Decode>)
|
|
||||||
{
|
|
||||||
if (srclen > 0)
|
|
||||||
{
|
|
||||||
#if defined(__aarch64__)
|
|
||||||
outlen = tb64sdec(reinterpret_cast<const uint8_t *>(source), srclen, reinterpret_cast<uint8_t *>(dst_pos));
|
|
||||||
#else
|
|
||||||
outlen = _tb64d(reinterpret_cast<const uint8_t *>(source), srclen, reinterpret_cast<uint8_t *>(dst_pos));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!outlen)
|
|
||||||
throw Exception(
|
|
||||||
ErrorCodes::INCORRECT_DATA,
|
|
||||||
"Failed to {} input '{}'",
|
|
||||||
getName(), String(reinterpret_cast<const char *>(source), srclen));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (srclen > 0)
|
|
||||||
{
|
|
||||||
// during decoding character array can be partially polluted
|
|
||||||
// if fail, revert back and clean
|
|
||||||
auto * savepoint = dst_pos;
|
|
||||||
outlen = _tb64d(reinterpret_cast<const uint8_t *>(source), srclen, reinterpret_cast<uint8_t *>(dst_pos));
|
|
||||||
if (!outlen)
|
|
||||||
{
|
|
||||||
outlen = 0;
|
|
||||||
dst_pos = savepoint; //-V1048
|
|
||||||
// clean the symbol
|
|
||||||
dst_pos[0] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Base64 library is using AVX-512 with some shuffle operations.
|
/// Base64 library is using AVX-512 with some shuffle operations.
|
||||||
/// Memory sanitizer don't understand if there was uninitialized memory in SIMD register but it was not used in the result of shuffle.
|
/// Memory sanitizer don't understand if there was uninitialized memory in SIMD register but it was not used in the result of shuffle.
|
||||||
__msan_unpoison(dst_pos, outlen);
|
__msan_unpoison(dst_pos, outlen);
|
||||||
|
|
||||||
source += srclen + 1;
|
src += src_length + 1;
|
||||||
dst_pos += outlen;
|
dst_pos += outlen;
|
||||||
*dst_pos = '\0';
|
*dst_pos = '\0';
|
||||||
dst_pos += 1;
|
dst_pos += 1;
|
||||||
@ -172,8 +184,44 @@ public:
|
|||||||
src_offset_prev = src_offsets[row];
|
src_offset_prev = src_offsets[row];
|
||||||
}
|
}
|
||||||
|
|
||||||
dst_data.resize(dst_pos - dst);
|
dst_chars.resize(dst_pos - dst);
|
||||||
|
return dst_column;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ColumnPtr execute(const ColumnFixedString & src_column, const size_t src_row_count)
|
||||||
|
{
|
||||||
|
auto dst_column = ColumnString::create();
|
||||||
|
auto & dst_chars = dst_column->getChars();
|
||||||
|
auto & dst_offsets = dst_column->getOffsets();
|
||||||
|
|
||||||
|
const auto reserve = Func::getBufferSize(src_column.byteSize(), src_column.size());
|
||||||
|
dst_chars.resize(reserve);
|
||||||
|
dst_offsets.resize(src_row_count);
|
||||||
|
|
||||||
|
const auto & src_chars = src_column.getChars();
|
||||||
|
const auto & src_n = src_column.getN();
|
||||||
|
|
||||||
|
auto * dst = dst_chars.data();
|
||||||
|
auto * dst_pos = dst;
|
||||||
|
const auto * src = src_chars.data();
|
||||||
|
|
||||||
|
for (size_t row = 0; row < src_row_count; ++row)
|
||||||
|
{
|
||||||
|
const auto outlen = Func::performCoding({src, src_n}, dst_pos);
|
||||||
|
|
||||||
|
/// Base64 library is using AVX-512 with some shuffle operations.
|
||||||
|
/// Memory sanitizer don't understand if there was uninitialized memory in SIMD register but it was not used in the result of shuffle.
|
||||||
|
__msan_unpoison(dst_pos, outlen);
|
||||||
|
|
||||||
|
src += src_n;
|
||||||
|
dst_pos += outlen;
|
||||||
|
*dst_pos = '\0';
|
||||||
|
dst_pos += 1;
|
||||||
|
|
||||||
|
dst_offsets[row] = dst_pos - dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
dst_chars.resize(dst_pos - dst);
|
||||||
return dst_column;
|
return dst_column;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include <DataTypes/DataTypeFactory.h>
|
#include <DataTypes/DataTypeFactory.h>
|
||||||
#include <DataTypes/DataTypeFixedString.h>
|
#include <DataTypes/DataTypeFixedString.h>
|
||||||
#include <DataTypes/DataTypeInterval.h>
|
#include <DataTypes/DataTypeInterval.h>
|
||||||
|
#include <DataTypes/DataTypeTuple.h>
|
||||||
#include <DataTypes/DataTypeString.h>
|
#include <DataTypes/DataTypeString.h>
|
||||||
#include <DataTypes/DataTypesDecimal.h>
|
#include <DataTypes/DataTypesDecimal.h>
|
||||||
#include <DataTypes/DataTypesNumber.h>
|
#include <DataTypes/DataTypesNumber.h>
|
||||||
@ -642,7 +643,8 @@ class FunctionBinaryArithmetic : public IFunction
|
|||||||
DataTypeInt8, DataTypeInt16, DataTypeInt32, DataTypeInt64, DataTypeInt128, DataTypeInt256,
|
DataTypeInt8, DataTypeInt16, DataTypeInt32, DataTypeInt64, DataTypeInt128, DataTypeInt256,
|
||||||
DataTypeDecimal32, DataTypeDecimal64, DataTypeDecimal128, DataTypeDecimal256,
|
DataTypeDecimal32, DataTypeDecimal64, DataTypeDecimal128, DataTypeDecimal256,
|
||||||
DataTypeDate, DataTypeDateTime,
|
DataTypeDate, DataTypeDateTime,
|
||||||
DataTypeFixedString, DataTypeString>;
|
DataTypeFixedString, DataTypeString,
|
||||||
|
DataTypeInterval>;
|
||||||
|
|
||||||
using Floats = TypeList<DataTypeFloat32, DataTypeFloat64>;
|
using Floats = TypeList<DataTypeFloat32, DataTypeFloat64>;
|
||||||
|
|
||||||
@ -717,6 +719,82 @@ class FunctionBinaryArithmetic : public IFunction
|
|||||||
return FunctionFactory::instance().get(function_name, context);
|
return FunctionFactory::instance().get(function_name, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static FunctionOverloadResolverPtr
|
||||||
|
getFunctionForDateTupleOfIntervalsArithmetic(const DataTypePtr & type0, const DataTypePtr & type1, ContextPtr context)
|
||||||
|
{
|
||||||
|
bool first_is_date_or_datetime = isDateOrDate32(type0) || isDateTime(type0) || isDateTime64(type0);
|
||||||
|
bool second_is_date_or_datetime = isDateOrDate32(type1) || isDateTime(type1) || isDateTime64(type1);
|
||||||
|
|
||||||
|
/// Exactly one argument must be Date or DateTime
|
||||||
|
if (first_is_date_or_datetime == second_is_date_or_datetime)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (!isTuple(type0) && !isTuple(type1))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
/// Special case when the function is plus or minus, one of arguments is Date/DateTime and another is Tuple.
|
||||||
|
/// We construct another function and call it.
|
||||||
|
if constexpr (!is_plus && !is_minus)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (isTuple(type0) && second_is_date_or_datetime && is_minus)
|
||||||
|
throw Exception("Wrong order of arguments for function " + String(name) + ": argument of Tuple type cannot be first",
|
||||||
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
||||||
|
|
||||||
|
std::string function_name;
|
||||||
|
if (is_plus)
|
||||||
|
{
|
||||||
|
function_name = "addTupleOfIntervals";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
function_name = "subtractTupleOfIntervals";
|
||||||
|
}
|
||||||
|
|
||||||
|
return FunctionFactory::instance().get(function_name, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
static FunctionOverloadResolverPtr
|
||||||
|
getFunctionForMergeIntervalsArithmetic(const DataTypePtr & type0, const DataTypePtr & type1, ContextPtr context)
|
||||||
|
{
|
||||||
|
/// Special case when the function is plus or minus, first argument is Interval or Tuple of Intervals
|
||||||
|
/// and the second argument is the Interval of a different kind.
|
||||||
|
/// We construct another function (example: addIntervals) and call it
|
||||||
|
|
||||||
|
if constexpr (!is_plus && !is_minus)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const auto * tuple_data_type_0 = checkAndGetDataType<DataTypeTuple>(type0.get());
|
||||||
|
const auto * interval_data_type_0 = checkAndGetDataType<DataTypeInterval>(type0.get());
|
||||||
|
const auto * interval_data_type_1 = checkAndGetDataType<DataTypeInterval>(type1.get());
|
||||||
|
|
||||||
|
if ((!tuple_data_type_0 && !interval_data_type_0) || !interval_data_type_1)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (interval_data_type_0 && interval_data_type_0->equals(*interval_data_type_1))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (tuple_data_type_0)
|
||||||
|
{
|
||||||
|
auto & tuple_types = tuple_data_type_0->getElements();
|
||||||
|
for (auto & type : tuple_types)
|
||||||
|
if (!isInterval(type))
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string function_name;
|
||||||
|
if (is_plus)
|
||||||
|
{
|
||||||
|
function_name = "addInterval";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
function_name = "subtractInterval";
|
||||||
|
}
|
||||||
|
|
||||||
|
return FunctionFactory::instance().get(function_name, context);
|
||||||
|
}
|
||||||
|
|
||||||
static FunctionOverloadResolverPtr
|
static FunctionOverloadResolverPtr
|
||||||
getFunctionForTupleArithmetic(const DataTypePtr & type0, const DataTypePtr & type1, ContextPtr context)
|
getFunctionForTupleArithmetic(const DataTypePtr & type0, const DataTypePtr & type1, ContextPtr context)
|
||||||
{
|
{
|
||||||
@ -915,6 +993,30 @@ class FunctionBinaryArithmetic : public IFunction
|
|||||||
return function->execute(new_arguments, result_type, input_rows_count);
|
return function->execute(new_arguments, result_type, input_rows_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ColumnPtr executeDateTimeTupleOfIntervalsPlusMinus(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type,
|
||||||
|
size_t input_rows_count, const FunctionOverloadResolverPtr & function_builder) const
|
||||||
|
{
|
||||||
|
ColumnsWithTypeAndName new_arguments = arguments;
|
||||||
|
|
||||||
|
/// Tuple argument must be second.
|
||||||
|
if (isTuple(arguments[0].type))
|
||||||
|
std::swap(new_arguments[0], new_arguments[1]);
|
||||||
|
|
||||||
|
auto function = function_builder->build(new_arguments);
|
||||||
|
|
||||||
|
return function->execute(new_arguments, result_type, input_rows_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnPtr executeIntervalTupleOfIntervalsPlusMinus(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type,
|
||||||
|
size_t input_rows_count, const FunctionOverloadResolverPtr & function_builder) const
|
||||||
|
{
|
||||||
|
ColumnsWithTypeAndName new_arguments = arguments;
|
||||||
|
|
||||||
|
auto function = function_builder->build(new_arguments);
|
||||||
|
|
||||||
|
return function->execute(new_arguments, result_type, input_rows_count);
|
||||||
|
}
|
||||||
|
|
||||||
ColumnPtr executeTupleNumberOperator(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type,
|
ColumnPtr executeTupleNumberOperator(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type,
|
||||||
size_t input_rows_count, const FunctionOverloadResolverPtr & function_builder) const
|
size_t input_rows_count, const FunctionOverloadResolverPtr & function_builder) const
|
||||||
{
|
{
|
||||||
@ -1134,6 +1236,34 @@ public:
|
|||||||
return function->getResultType();
|
return function->getResultType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Special case when the function is plus or minus, one of arguments is Date/DateTime and another is Tuple.
|
||||||
|
if (auto function_builder = getFunctionForDateTupleOfIntervalsArithmetic(arguments[0], arguments[1], context))
|
||||||
|
{
|
||||||
|
ColumnsWithTypeAndName new_arguments(2);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 2; ++i)
|
||||||
|
new_arguments[i].type = arguments[i];
|
||||||
|
|
||||||
|
/// Tuple argument must be second.
|
||||||
|
if (isTuple(new_arguments[0].type))
|
||||||
|
std::swap(new_arguments[0], new_arguments[1]);
|
||||||
|
|
||||||
|
auto function = function_builder->build(new_arguments);
|
||||||
|
return function->getResultType();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Special case when the function is plus or minus, one of arguments is Interval/Tuple of Intervals and another is Interval.
|
||||||
|
if (auto function_builder = getFunctionForMergeIntervalsArithmetic(arguments[0], arguments[1], context))
|
||||||
|
{
|
||||||
|
ColumnsWithTypeAndName new_arguments(2);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 2; ++i)
|
||||||
|
new_arguments[i].type = arguments[i];
|
||||||
|
|
||||||
|
auto function = function_builder->build(new_arguments);
|
||||||
|
return function->getResultType();
|
||||||
|
}
|
||||||
|
|
||||||
/// Special case when the function is multiply or divide, one of arguments is Tuple and another is Number.
|
/// Special case when the function is multiply or divide, one of arguments is Tuple and another is Number.
|
||||||
if (auto function_builder = getFunctionForTupleAndNumberArithmetic(arguments[0], arguments[1], context))
|
if (auto function_builder = getFunctionForTupleAndNumberArithmetic(arguments[0], arguments[1], context))
|
||||||
{
|
{
|
||||||
@ -1185,6 +1315,21 @@ public:
|
|||||||
type_res = std::make_shared<DataTypeString>();
|
type_res = std::make_shared<DataTypeString>();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else if constexpr (std::is_same_v<LeftDataType, DataTypeInterval> || std::is_same_v<RightDataType, DataTypeInterval>)
|
||||||
|
{
|
||||||
|
if constexpr (std::is_same_v<LeftDataType, DataTypeInterval> &&
|
||||||
|
std::is_same_v<RightDataType, DataTypeInterval>)
|
||||||
|
{
|
||||||
|
if constexpr (is_plus || is_minus)
|
||||||
|
{
|
||||||
|
if (left.getKind() == right.getKind())
|
||||||
|
{
|
||||||
|
type_res = std::make_shared<LeftDataType>(left.getKind());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
using ResultDataType = typename BinaryOperationTraits<Op, LeftDataType, RightDataType>::ResultDataType;
|
using ResultDataType = typename BinaryOperationTraits<Op, LeftDataType, RightDataType>::ResultDataType;
|
||||||
@ -1566,6 +1711,18 @@ public:
|
|||||||
return executeDateTimeIntervalPlusMinus(arguments, result_type, input_rows_count, function_builder);
|
return executeDateTimeIntervalPlusMinus(arguments, result_type, input_rows_count, function_builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Special case when the function is plus or minus, one of arguments is Date/DateTime and another is Tuple.
|
||||||
|
if (auto function_builder = getFunctionForDateTupleOfIntervalsArithmetic(arguments[0].type, arguments[1].type, context))
|
||||||
|
{
|
||||||
|
return executeDateTimeTupleOfIntervalsPlusMinus(arguments, result_type, input_rows_count, function_builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Special case when the function is plus or minus, one of arguments is Interval/Tuple of Intervals and another is Interval.
|
||||||
|
if (auto function_builder = getFunctionForMergeIntervalsArithmetic(arguments[0].type, arguments[1].type, context))
|
||||||
|
{
|
||||||
|
return executeIntervalTupleOfIntervalsPlusMinus(arguments, result_type, input_rows_count, function_builder);
|
||||||
|
}
|
||||||
|
|
||||||
/// Special case when the function is plus, minus or multiply, both arguments are tuples.
|
/// Special case when the function is plus, minus or multiply, both arguments are tuples.
|
||||||
if (auto function_builder = getFunctionForTupleArithmetic(arguments[0].type, arguments[1].type, context))
|
if (auto function_builder = getFunctionForTupleArithmetic(arguments[0].type, arguments[1].type, context))
|
||||||
{
|
{
|
||||||
|
@ -38,18 +38,21 @@ public:
|
|||||||
{
|
{
|
||||||
if (!isStringOrFixedString(arguments[0]))
|
if (!isStringOrFixedString(arguments[0]))
|
||||||
throw Exception(
|
throw Exception(
|
||||||
"Illegal type " + arguments[0]->getName() + " of first argument of function " + getName(),
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
||||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
"Illegal type {} of first argument of function {}",
|
||||||
|
arguments[0]->getName(), getName());
|
||||||
|
|
||||||
if (!isStringOrFixedString(arguments[1]))
|
if (!isStringOrFixedString(arguments[1]))
|
||||||
throw Exception(
|
throw Exception(
|
||||||
"Illegal type " + arguments[1]->getName() + " of second argument of function " + getName(),
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
||||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
"Illegal type {} of second argument of function {}",
|
||||||
|
arguments[1]->getName(), getName());
|
||||||
|
|
||||||
if (!isStringOrFixedString(arguments[2]))
|
if (!isStringOrFixedString(arguments[2]))
|
||||||
throw Exception(
|
throw Exception(
|
||||||
"Illegal type " + arguments[2]->getName() + " of third argument of function " + getName(),
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
||||||
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
"Illegal type {} of third argument of function {}",
|
||||||
|
arguments[2]->getName(), getName());
|
||||||
|
|
||||||
return std::make_shared<DataTypeString>();
|
return std::make_shared<DataTypeString>();
|
||||||
}
|
}
|
||||||
@ -61,7 +64,10 @@ public:
|
|||||||
const ColumnPtr column_replacement = arguments[2].column;
|
const ColumnPtr column_replacement = arguments[2].column;
|
||||||
|
|
||||||
if (!isColumnConst(*column_needle) || !isColumnConst(*column_replacement))
|
if (!isColumnConst(*column_needle) || !isColumnConst(*column_replacement))
|
||||||
throw Exception("2nd and 3rd arguments of function " + getName() + " must be constants.", ErrorCodes::ILLEGAL_COLUMN);
|
throw Exception(
|
||||||
|
ErrorCodes::ILLEGAL_COLUMN,
|
||||||
|
"2nd and 3rd arguments of function {} must be constants.",
|
||||||
|
getName());
|
||||||
|
|
||||||
const IColumn * c1 = arguments[1].column.get();
|
const IColumn * c1 = arguments[1].column.get();
|
||||||
const IColumn * c2 = arguments[2].column.get();
|
const IColumn * c2 = arguments[2].column.get();
|
||||||
@ -71,7 +77,9 @@ public:
|
|||||||
String replacement = c2_const->getValue<String>();
|
String replacement = c2_const->getValue<String>();
|
||||||
|
|
||||||
if (needle.empty())
|
if (needle.empty())
|
||||||
throw Exception("Length of the second argument of function replace must be greater than 0.", ErrorCodes::ARGUMENT_OUT_OF_BOUND);
|
throw Exception(
|
||||||
|
ErrorCodes::ARGUMENT_OUT_OF_BOUND,
|
||||||
|
"Length of the second argument of function replace must be greater than 0.");
|
||||||
|
|
||||||
if (const ColumnString * col = checkAndGetColumn<ColumnString>(column_src.get()))
|
if (const ColumnString * col = checkAndGetColumn<ColumnString>(column_src.get()))
|
||||||
{
|
{
|
||||||
@ -87,8 +95,9 @@ public:
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
throw Exception(
|
throw Exception(
|
||||||
"Illegal column " + arguments[0].column->getName() + " of first argument of function " + getName(),
|
ErrorCodes::ILLEGAL_COLUMN,
|
||||||
ErrorCodes::ILLEGAL_COLUMN);
|
"Illegal column {} of first argument of function {}",
|
||||||
|
arguments[0].column->getName(), getName());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|