mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-09-20 00:30:49 +00:00
Merge branch 'master' into rs/gcc-removal
This commit is contained in:
commit
3f7ce60e03
7
.github/workflows/backport_branches.yml
vendored
7
.github/workflows/backport_branches.yml
vendored
@ -349,6 +349,13 @@ jobs:
|
||||
with:
|
||||
clear-repository: true
|
||||
submodules: true
|
||||
- name: Apply sparse checkout for contrib # in order to check that it doesn't break build
|
||||
run: |
|
||||
rm -rf "$GITHUB_WORKSPACE/contrib" && echo 'removed'
|
||||
git -C "$GITHUB_WORKSPACE" checkout . && echo 'restored'
|
||||
"$GITHUB_WORKSPACE/contrib/update-submodules.sh" && echo 'OK'
|
||||
du -hs "$GITHUB_WORKSPACE/contrib" ||:
|
||||
find "$GITHUB_WORKSPACE/contrib" -type f | wc -l ||:
|
||||
- name: Build
|
||||
run: |
|
||||
sudo rm -fr "$TEMP_PATH"
|
||||
|
7
.github/workflows/master.yml
vendored
7
.github/workflows/master.yml
vendored
@ -487,6 +487,13 @@ jobs:
|
||||
with:
|
||||
clear-repository: true
|
||||
submodules: true
|
||||
- name: Apply sparse checkout for contrib # in order to check that it doesn't break build
|
||||
run: |
|
||||
rm -rf "$GITHUB_WORKSPACE/contrib" && echo 'removed'
|
||||
git -C "$GITHUB_WORKSPACE" checkout . && echo 'restored'
|
||||
"$GITHUB_WORKSPACE/contrib/update-submodules.sh" && echo 'OK'
|
||||
du -hs "$GITHUB_WORKSPACE/contrib" ||:
|
||||
find "$GITHUB_WORKSPACE/contrib" -type f | wc -l ||:
|
||||
- name: Build
|
||||
run: |
|
||||
sudo rm -fr "$TEMP_PATH"
|
||||
|
7
.github/workflows/pull_request.yml
vendored
7
.github/workflows/pull_request.yml
vendored
@ -550,6 +550,13 @@ jobs:
|
||||
with:
|
||||
clear-repository: true
|
||||
submodules: true
|
||||
- name: Apply sparse checkout for contrib # in order to check that it doesn't break build
|
||||
run: |
|
||||
rm -rf "$GITHUB_WORKSPACE/contrib" && echo 'removed'
|
||||
git -C "$GITHUB_WORKSPACE" checkout . && echo 'restored'
|
||||
"$GITHUB_WORKSPACE/contrib/update-submodules.sh" && echo 'OK'
|
||||
du -hs "$GITHUB_WORKSPACE/contrib" ||:
|
||||
find "$GITHUB_WORKSPACE/contrib" -type f | wc -l ||:
|
||||
- name: Build
|
||||
run: |
|
||||
sudo rm -fr "$TEMP_PATH"
|
||||
|
7
.github/workflows/release_branches.yml
vendored
7
.github/workflows/release_branches.yml
vendored
@ -406,6 +406,13 @@ jobs:
|
||||
with:
|
||||
clear-repository: true
|
||||
submodules: true
|
||||
- name: Apply sparse checkout for contrib # in order to check that it doesn't break build
|
||||
run: |
|
||||
rm -rf "$GITHUB_WORKSPACE/contrib" && echo 'removed'
|
||||
git -C "$GITHUB_WORKSPACE" checkout . && echo 'restored'
|
||||
"$GITHUB_WORKSPACE/contrib/update-submodules.sh" && echo 'OK'
|
||||
du -hs "$GITHUB_WORKSPACE/contrib" ||:
|
||||
find "$GITHUB_WORKSPACE/contrib" -type f | wc -l ||:
|
||||
- name: Build
|
||||
run: |
|
||||
sudo rm -fr "$TEMP_PATH"
|
||||
|
2
contrib/libhdfs3
vendored
2
contrib/libhdfs3
vendored
@ -1 +1 @@
|
||||
Subproject commit 9ee3ce77215fca83b7fdfcfe2186a3db0d0bdb74
|
||||
Subproject commit 3c91d96ff29fe5928f055519c6d979c4b104db9e
|
19
contrib/sparse-checkout/setup-sparse-checkout.sh
Executable file
19
contrib/sparse-checkout/setup-sparse-checkout.sh
Executable file
@ -0,0 +1,19 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
git config submodule."contrib/llvm-project".update '!../sparse-checkout/update-llvm-project.sh'
|
||||
git config submodule."contrib/croaring".update '!../sparse-checkout/update-croaring.sh'
|
||||
git config submodule."contrib/aws".update '!../sparse-checkout/update-aws.sh'
|
||||
git config submodule."contrib/openssl".update '!../sparse-checkout/update-openssl.sh'
|
||||
git config submodule."contrib/boringssl".update '!../sparse-checkout/update-boringssl.sh'
|
||||
git config submodule."contrib/arrow".update '!../sparse-checkout/update-arrow.sh'
|
||||
git config submodule."contrib/grpc".update '!../sparse-checkout/update-grpc.sh'
|
||||
git config submodule."contrib/orc".update '!../sparse-checkout/update-orc.sh'
|
||||
git config submodule."contrib/h3".update '!../sparse-checkout/update-h3.sh'
|
||||
git config submodule."contrib/icu".update '!../sparse-checkout/update-icu.sh'
|
||||
git config submodule."contrib/boost".update '!../sparse-checkout/update-boost.sh'
|
||||
git config submodule."contrib/aws-s2n-tls".update '!../sparse-checkout/update-aws-s2n-tls.sh'
|
||||
git config submodule."contrib/protobuf".update '!../sparse-checkout/update-protobuf.sh'
|
||||
git config submodule."contrib/libxml2".update '!../sparse-checkout/update-libxml2.sh'
|
||||
git config submodule."contrib/brotli".update '!../sparse-checkout/update-brotli.sh'
|
12
contrib/sparse-checkout/update-arrow.sh
Executable file
12
contrib/sparse-checkout/update-arrow.sh
Executable file
@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "Using sparse checkout for arrow"
|
||||
|
||||
FILES_TO_CHECKOUT=$(git rev-parse --git-dir)/info/sparse-checkout
|
||||
echo '/*' > $FILES_TO_CHECKOUT
|
||||
echo '!/*/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/cpp/*' >> $FILES_TO_CHECKOUT
|
||||
|
||||
git config core.sparsecheckout true
|
||||
git checkout $1
|
||||
git read-tree -mu HEAD
|
13
contrib/sparse-checkout/update-aws-s2n-tls.sh
Executable file
13
contrib/sparse-checkout/update-aws-s2n-tls.sh
Executable file
@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "Using sparse checkout for aws-s2n-tls"
|
||||
|
||||
FILES_TO_CHECKOUT=$(git rev-parse --git-dir)/info/sparse-checkout
|
||||
echo '/*' > $FILES_TO_CHECKOUT
|
||||
echo '!/test/*' >> $FILES_TO_CHECKOUT
|
||||
echo '!/docs/*' >> $FILES_TO_CHECKOUT
|
||||
echo '!/compliance/*' >> $FILES_TO_CHECKOUT
|
||||
|
||||
git config core.sparsecheckout true
|
||||
git checkout $1
|
||||
git read-tree -mu HEAD
|
13
contrib/sparse-checkout/update-aws.sh
Executable file
13
contrib/sparse-checkout/update-aws.sh
Executable file
@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "Using sparse checkout for aws"
|
||||
|
||||
FILES_TO_CHECKOUT=$(git rev-parse --git-dir)/info/sparse-checkout
|
||||
echo '/*' > $FILES_TO_CHECKOUT
|
||||
echo '!/*/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/aws-cpp-sdk-core/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/aws-cpp-sdk-s3/*' >> $FILES_TO_CHECKOUT
|
||||
|
||||
git config core.sparsecheckout true
|
||||
git checkout $1
|
||||
git read-tree -mu HEAD
|
85
contrib/sparse-checkout/update-boost.sh
Executable file
85
contrib/sparse-checkout/update-boost.sh
Executable file
@ -0,0 +1,85 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "Using sparse checkout for boost"
|
||||
|
||||
FILES_TO_CHECKOUT=$(git rev-parse --git-dir)/info/sparse-checkout
|
||||
echo '/*' > $FILES_TO_CHECKOUT
|
||||
echo '!/*/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/*' > $FILES_TO_CHECKOUT
|
||||
echo '!/boost/*/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/algorithm/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/any/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/atomic/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/assert/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/bind/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/concept/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/config/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/container/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/container_hash/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/context/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/convert/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/coroutine/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/core/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/detail/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/dynamic_bitset/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/exception/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/filesystem/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/functional/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/function/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/geometry/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/graph/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/heap/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/integer/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/intrusive/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/iostreams/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/io/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/iterator/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/math/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/move/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/mpl/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/multi_index/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/multiprecision/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/numeric/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/predef/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/preprocessor/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/program_options/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/range/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/regex/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/smart_ptr/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/type_index/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/type_traits/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/system/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/tti/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/utility/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/lexical_cast/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/optional/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/property_map/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/pending/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/multi_array/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/tuple/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/icl/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/unordered/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/typeof/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/parameter/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/mp11/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/archive/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/function_types/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/serialization/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/fusion/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/variant/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/format/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/locale/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/random/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/spirit/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/uuid/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/xpressive/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/asio/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/circular_buffer/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/proto/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/qvm/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/boost/property_tree/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/libs/*' >> $FILES_TO_CHECKOUT
|
||||
|
||||
git config core.sparsecheckout true
|
||||
git checkout $1
|
||||
git read-tree -mu HEAD
|
14
contrib/sparse-checkout/update-boringssl.sh
Executable file
14
contrib/sparse-checkout/update-boringssl.sh
Executable file
@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "Using sparse checkout for boringsll"
|
||||
|
||||
FILES_TO_CHECKOUT=$(git rev-parse --git-dir)/info/sparse-checkout
|
||||
echo '/*' > $FILES_TO_CHECKOUT
|
||||
echo '!/fuzz/*' >> $FILES_TO_CHECKOUT
|
||||
echo '!/crypto/cipher_extra/test/*' >> $FILES_TO_CHECKOUT
|
||||
echo '!/third_party/wycheproof_testvectors/*' >> $FILES_TO_CHECKOUT
|
||||
echo '!/third_party/googletest/*' >> $FILES_TO_CHECKOUT
|
||||
|
||||
git config core.sparsecheckout true
|
||||
git checkout $1
|
||||
git read-tree -mu HEAD
|
12
contrib/sparse-checkout/update-brotli.sh
Executable file
12
contrib/sparse-checkout/update-brotli.sh
Executable file
@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "Using sparse checkout for brotli"
|
||||
|
||||
FILES_TO_CHECKOUT=$(git rev-parse --git-dir)/info/sparse-checkout
|
||||
echo '/*' > $FILES_TO_CHECKOUT
|
||||
echo '!/*/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/c/*' >> $FILES_TO_CHECKOUT
|
||||
|
||||
git config core.sparsecheckout true
|
||||
git checkout $1
|
||||
git read-tree -mu HEAD
|
12
contrib/sparse-checkout/update-croaring.sh
Executable file
12
contrib/sparse-checkout/update-croaring.sh
Executable file
@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "Using sparse checkout for croaring"
|
||||
|
||||
FILES_TO_CHECKOUT=$(git rev-parse --git-dir)/info/sparse-checkout
|
||||
echo '/*' > $FILES_TO_CHECKOUT
|
||||
echo '!/benchmarks/*' >> $FILES_TO_CHECKOUT
|
||||
echo '!/tests/*' >> $FILES_TO_CHECKOUT
|
||||
|
||||
git config core.sparsecheckout true
|
||||
git checkout $1
|
||||
git read-tree -mu HEAD
|
22
contrib/sparse-checkout/update-grpc.sh
Executable file
22
contrib/sparse-checkout/update-grpc.sh
Executable file
@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "Using sparse checkout for grpc"
|
||||
|
||||
FILES_TO_CHECKOUT=$(git rev-parse --git-dir)/info/sparse-checkout
|
||||
echo '/*' > $FILES_TO_CHECKOUT
|
||||
echo '!/test/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/test/build/*' >> $FILES_TO_CHECKOUT
|
||||
echo '!/tools/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/tools/codegen/*' >> $FILES_TO_CHECKOUT
|
||||
echo '!/examples/*' >> $FILES_TO_CHECKOUT
|
||||
echo '!/doc/*' >> $FILES_TO_CHECKOUT
|
||||
# FIXME why do we need csharp?
|
||||
#echo '!/src/csharp/*' >> $FILES_TO_CHECKOUT
|
||||
echo '!/src/python/*' >> $FILES_TO_CHECKOUT
|
||||
echo '!/src/objective-c/*' >> $FILES_TO_CHECKOUT
|
||||
echo '!/src/php/*' >> $FILES_TO_CHECKOUT
|
||||
echo '!/src/ruby/*' >> $FILES_TO_CHECKOUT
|
||||
|
||||
git config core.sparsecheckout true
|
||||
git checkout $1
|
||||
git read-tree -mu HEAD
|
12
contrib/sparse-checkout/update-h3.sh
Executable file
12
contrib/sparse-checkout/update-h3.sh
Executable file
@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "Using sparse checkout for h3"
|
||||
|
||||
FILES_TO_CHECKOUT=$(git rev-parse --git-dir)/info/sparse-checkout
|
||||
echo '/*' > $FILES_TO_CHECKOUT
|
||||
echo '!/tests/*' >> $FILES_TO_CHECKOUT
|
||||
echo '!/website/*' >> $FILES_TO_CHECKOUT
|
||||
|
||||
git config core.sparsecheckout true
|
||||
git checkout $1
|
||||
git read-tree -mu HEAD
|
12
contrib/sparse-checkout/update-icu.sh
Executable file
12
contrib/sparse-checkout/update-icu.sh
Executable file
@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "Using sparse checkout for icu"
|
||||
|
||||
FILES_TO_CHECKOUT=$(git rev-parse --git-dir)/info/sparse-checkout
|
||||
echo '/*' > $FILES_TO_CHECKOUT
|
||||
echo '!/*/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/icu4c/*' >> $FILES_TO_CHECKOUT
|
||||
|
||||
git config core.sparsecheckout true
|
||||
git checkout $1
|
||||
git read-tree -mu HEAD
|
16
contrib/sparse-checkout/update-libxml2.sh
Executable file
16
contrib/sparse-checkout/update-libxml2.sh
Executable file
@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "Using sparse checkout for libxml2"
|
||||
|
||||
FILES_TO_CHECKOUT=$(git rev-parse --git-dir)/info/sparse-checkout
|
||||
echo '/*' > $FILES_TO_CHECKOUT
|
||||
echo '!/result/*' >> $FILES_TO_CHECKOUT
|
||||
echo '!/test/*' >> $FILES_TO_CHECKOUT
|
||||
echo '!/doc/*' >> $FILES_TO_CHECKOUT
|
||||
echo '!/os400/*' >> $FILES_TO_CHECKOUT
|
||||
echo '!/fuzz/*' >> $FILES_TO_CHECKOUT
|
||||
echo '!/python/*' >> $FILES_TO_CHECKOUT
|
||||
|
||||
git config core.sparsecheckout true
|
||||
git checkout $1
|
||||
git read-tree -mu HEAD
|
27
contrib/sparse-checkout/update-llvm-project.sh
Executable file
27
contrib/sparse-checkout/update-llvm-project.sh
Executable file
@ -0,0 +1,27 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "Using sparse checkout for llvm-project"
|
||||
|
||||
FILES_TO_CHECKOUT=$(git rev-parse --git-dir)/info/sparse-checkout
|
||||
echo '/*' > $FILES_TO_CHECKOUT
|
||||
echo '!/*/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/llvm/*' >> $FILES_TO_CHECKOUT
|
||||
echo '!/llvm/*/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/llvm/cmake/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/llvm/projects/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/llvm/include/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/llvm/lib/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/llvm/utils/TableGen/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/libcxxabi/*' >> $FILES_TO_CHECKOUT
|
||||
echo '!/libcxxabi/test/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/libcxx/*' >> $FILES_TO_CHECKOUT
|
||||
echo '!/libcxx/test/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/libunwind/*' >> $FILES_TO_CHECKOUT
|
||||
echo '!/libunwind/test/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/compiler-rt/*' >> $FILES_TO_CHECKOUT
|
||||
echo '!/compiler-rt/test/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/cmake/*' >> $FILES_TO_CHECKOUT
|
||||
|
||||
git config core.sparsecheckout true
|
||||
git checkout $1
|
||||
git read-tree -mu HEAD
|
15
contrib/sparse-checkout/update-openssl.sh
Executable file
15
contrib/sparse-checkout/update-openssl.sh
Executable file
@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "Using sparse checkout for openssl"
|
||||
|
||||
FILES_TO_CHECKOUT=$(git rev-parse --git-dir)/info/sparse-checkout
|
||||
echo '/*' > $FILES_TO_CHECKOUT
|
||||
echo '!/fuzz/*' >> $FILES_TO_CHECKOUT
|
||||
echo '!/test/*' >> $FILES_TO_CHECKOUT
|
||||
echo '!/doc/*' >> $FILES_TO_CHECKOUT
|
||||
echo '!/providers/*' >> $FILES_TO_CHECKOUT
|
||||
echo '!/apps/*' >> $FILES_TO_CHECKOUT
|
||||
|
||||
git config core.sparsecheckout true
|
||||
git checkout $1
|
||||
git read-tree -mu HEAD
|
13
contrib/sparse-checkout/update-orc.sh
Executable file
13
contrib/sparse-checkout/update-orc.sh
Executable file
@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "Using sparse checkout for orc"
|
||||
|
||||
FILES_TO_CHECKOUT=$(git rev-parse --git-dir)/info/sparse-checkout
|
||||
echo '/*' > $FILES_TO_CHECKOUT
|
||||
echo '!/*/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/c++/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/proto/*' >> $FILES_TO_CHECKOUT
|
||||
|
||||
git config core.sparsecheckout true
|
||||
git checkout $1
|
||||
git read-tree -mu HEAD
|
13
contrib/sparse-checkout/update-protobuf.sh
Executable file
13
contrib/sparse-checkout/update-protobuf.sh
Executable file
@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "Using sparse checkout for protobuf"
|
||||
|
||||
FILES_TO_CHECKOUT=$(git rev-parse --git-dir)/info/sparse-checkout
|
||||
echo '!/*' > $FILES_TO_CHECKOUT
|
||||
echo '/*/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/src/*' >> $FILES_TO_CHECKOUT
|
||||
echo '/cmake/*' >> $FILES_TO_CHECKOUT
|
||||
|
||||
git config core.sparsecheckout true
|
||||
git checkout $1
|
||||
git read-tree -mu HEAD
|
11
contrib/update-submodules.sh
vendored
Executable file
11
contrib/update-submodules.sh
vendored
Executable file
@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
WORKDIR=$(dirname "$0")
|
||||
WORKDIR=$(readlink -f "${WORKDIR}")
|
||||
|
||||
"$WORKDIR/sparse-checkout/setup-sparse-checkout.sh"
|
||||
git submodule init
|
||||
git submodule sync
|
||||
git submodule update --depth=1
|
@ -18,13 +18,13 @@ RUN apt-get update \
|
||||
# and MEMORY_LIMIT_EXCEEDED exceptions in Functional tests (total memory limit in Functional tests is ~55.24 GiB).
|
||||
# TSAN will flush shadow memory when reaching this limit.
|
||||
# It may cause false-negatives, but it's better than OOM.
|
||||
RUN echo "TSAN_OPTIONS='verbosity=1000 halt_on_error=1 history_size=7 memory_limit_mb=46080'" >> /etc/environment
|
||||
RUN echo "TSAN_OPTIONS='verbosity=1000 halt_on_error=1 history_size=7 memory_limit_mb=46080 second_deadlock_stack=1'" >> /etc/environment
|
||||
RUN echo "UBSAN_OPTIONS='print_stacktrace=1'" >> /etc/environment
|
||||
RUN echo "MSAN_OPTIONS='abort_on_error=1 poison_in_dtor=1'" >> /etc/environment
|
||||
RUN echo "LSAN_OPTIONS='suppressions=/usr/share/clickhouse-test/config/lsan_suppressions.txt'" >> /etc/environment
|
||||
# Sanitizer options for current shell (not current, but the one that will be spawned on "docker run")
|
||||
# (but w/o verbosity for TSAN, otherwise test.reference will not match)
|
||||
ENV TSAN_OPTIONS='halt_on_error=1 history_size=7 memory_limit_mb=46080'
|
||||
ENV TSAN_OPTIONS='halt_on_error=1 history_size=7 memory_limit_mb=46080 second_deadlock_stack=1'
|
||||
ENV UBSAN_OPTIONS='print_stacktrace=1'
|
||||
ENV MSAN_OPTIONS='abort_on_error=1 poison_in_dtor=1'
|
||||
|
||||
|
@ -108,6 +108,12 @@ RUN set -x \
|
||||
&& echo 'dockremap:165536:65536' >> /etc/subuid \
|
||||
&& echo 'dockremap:165536:65536' >> /etc/subgid
|
||||
|
||||
# Same options as in test/base/Dockerfile
|
||||
# (in case you need to override them in tests)
|
||||
ENV TSAN_OPTIONS='halt_on_error=1 history_size=7 memory_limit_mb=46080 second_deadlock_stack=1'
|
||||
ENV UBSAN_OPTIONS='print_stacktrace=1'
|
||||
ENV MSAN_OPTIONS='abort_on_error=1 poison_in_dtor=1'
|
||||
|
||||
EXPOSE 2375
|
||||
ENTRYPOINT ["dockerd-entrypoint.sh"]
|
||||
CMD ["sh", "-c", "pytest $PYTEST_OPTS"]
|
||||
|
@ -39,9 +39,15 @@ Next, you need to download the source files onto your working machine. This is c
|
||||
|
||||
In the command line terminal run:
|
||||
|
||||
git clone --recursive --shallow-submodules git@github.com:your_github_username/ClickHouse.git
|
||||
git clone --shallow-submodules git@github.com:your_github_username/ClickHouse.git
|
||||
cd ClickHouse
|
||||
|
||||
Or (if you'd like to use sparse checkout for submodules and avoid checking out unneeded files):
|
||||
|
||||
git clone git@github.com:your_github_username/ClickHouse.git
|
||||
cd ClickHouse
|
||||
./contrib/update-submodules.sh
|
||||
|
||||
Note: please, substitute *your_github_username* with what is appropriate!
|
||||
|
||||
This command will create a directory `ClickHouse` containing the working copy of the project.
|
||||
|
@ -140,3 +140,4 @@ DESCRIBE TABLE test_database.test_table;
|
||||
## Related content
|
||||
|
||||
- Blog: [ClickHouse and PostgreSQL - a match made in data heaven - part 1](https://clickhouse.com/blog/migrating-data-between-clickhouse-postgres)
|
||||
- Blog: [ClickHouse and PostgreSQL - a Match Made in Data Heaven - part 2](https://clickhouse.com/blog/migrating-data-between-clickhouse-postgres-part-2)
|
||||
|
@ -177,4 +177,6 @@ CREATE TABLE pg_table_schema_with_dots (a UInt32)
|
||||
- [Using PostgreSQL as a dictionary source](../../../sql-reference/dictionaries/index.md#dictionary-sources#dicts-external_dicts_dict_sources-postgresql)
|
||||
|
||||
## Related content
|
||||
|
||||
- Blog: [ClickHouse and PostgreSQL - a match made in data heaven - part 1](https://clickhouse.com/blog/migrating-data-between-clickhouse-postgres)
|
||||
- Blog: [ClickHouse and PostgreSQL - a Match Made in Data Heaven - part 2](https://clickhouse.com/blog/migrating-data-between-clickhouse-postgres-part-2)
|
||||
|
@ -122,3 +122,7 @@ FROM test.mv_visits
|
||||
GROUP BY StartDate
|
||||
ORDER BY StartDate;
|
||||
```
|
||||
|
||||
## Related Content
|
||||
|
||||
- Blog: [Using Aggregate Combinators in ClickHouse](https://clickhouse.com/blog/aggregate-functions-combinators-in-clickhouse-for-arrays-maps-and-states)
|
||||
|
@ -191,3 +191,7 @@ is performance. In practice, users often search for multiple terms at once. For
|
||||
'%big%'` can be evaluated directly using an inverted index by forming the union of the row id lists for terms "little" and "big". This also
|
||||
means that the parameter `GRANULARITY` supplied to index creation has no meaning (it may be removed from the syntax in the future).
|
||||
:::
|
||||
|
||||
## Related Content
|
||||
|
||||
- Blog: [Introducing Inverted Indices in ClickHouse](https://clickhouse.com/blog/clickhouse-search-with-inverted-indices)
|
||||
|
@ -186,3 +186,7 @@ ARRAY JOIN
|
||||
When requesting data, use the [sumMap(key, value)](../../../sql-reference/aggregate-functions/reference/summap.md) function for aggregation of `Map`.
|
||||
|
||||
For nested data structure, you do not need to specify its columns in the tuple of columns for summation.
|
||||
|
||||
## Related Content
|
||||
|
||||
- Blog: [Using Aggregate Combinators in ClickHouse](https://clickhouse.com/blog/aggregate-functions-combinators-in-clickhouse-for-arrays-maps-and-states)
|
||||
|
@ -112,3 +112,7 @@ If setting `keeper_map_strict_mode` is set to `true`, fetching and updating data
|
||||
```sql
|
||||
ALTER TABLE keeper_map_table UPDATE v1 = v1 * 10 + 2 WHERE key LIKE 'some%' AND v3 > 3.1;
|
||||
```
|
||||
|
||||
## Related content
|
||||
|
||||
- Blog: [Building a Real-time Analytics Apps with ClickHouse and Hex](https://clickhouse.com/blog/building-real-time-applications-with-clickhouse-and-hex-notebook-keeper-engine)
|
||||
|
@ -2499,7 +2499,9 @@ LIMIT 20
|
||||
We welcome exact and improved solutions here.
|
||||
|
||||
|
||||
# Related Content
|
||||
## Related Content
|
||||
|
||||
- [Git commits and our community](https://clickhouse.com/blog/clickhouse-git-community-commits)
|
||||
- [Window and array functions for Git commit sequences](https://clickhouse.com/blog/clickhouse-window-array-functions-git-commits)
|
||||
- Blog: [Git commits and our community](https://clickhouse.com/blog/clickhouse-git-community-commits)
|
||||
- Blog: [Window and array functions for Git commit sequences](https://clickhouse.com/blog/clickhouse-window-array-functions-git-commits)
|
||||
- Blog: [Building a Real-time Analytics Apps with ClickHouse and Hex](https://clickhouse.com/blog/building-real-time-applications-with-clickhouse-and-hex-notebook-keeper-engine)
|
||||
- Blog: [A Story of Open-source GitHub Activity using ClickHouse + Grafana](https://clickhouse.com/blog/introduction-to-clickhouse-and-grafana-webinar)
|
||||
|
@ -61,3 +61,7 @@ FROM system.opentelemetry_span_log
|
||||
```
|
||||
|
||||
In case of any errors, the part of the log data for which the error has occurred will be silently lost. Check the server log for error messages if the data does not arrive.
|
||||
|
||||
## Related Content
|
||||
|
||||
- Blog: [Building an Observability Solution with ClickHouse - Part 2 - Traces](https://clickhouse.com/blog/storing-traces-and-spans-open-telemetry-in-clickhouse)
|
||||
|
@ -124,3 +124,7 @@ Finally, entries in the query cache are not shared between users due to security
|
||||
row policy on a table by running the same query as another user B for whom no such policy exists. However, if necessary, cache entries can
|
||||
be marked accessible by other users (i.e. shared) by supplying setting
|
||||
[query_cache_share_between_users](settings/settings.md#query-cache-share-between-users).
|
||||
|
||||
## Related Content
|
||||
|
||||
- Blog: [Introducing the ClickHouse Query Cache](https://clickhouse.com/blog/introduction-to-the-clickhouse-query-cache-and-design)
|
||||
|
@ -40,6 +40,39 @@ SETTINGS additional_table_filters = (('table_1', 'x != 2'))
|
||||
└───┴──────┘
|
||||
```
|
||||
|
||||
## additional_result_filter
|
||||
|
||||
An additional filter expression to apply to the result of `SELECT` query.
|
||||
This setting is not applied to any subquery.
|
||||
|
||||
Default value: `''`.
|
||||
|
||||
**Example**
|
||||
|
||||
``` sql
|
||||
insert into table_1 values (1, 'a'), (2, 'bb'), (3, 'ccc'), (4, 'dddd');
|
||||
```
|
||||
```response
|
||||
┌─x─┬─y────┐
|
||||
│ 1 │ a │
|
||||
│ 2 │ bb │
|
||||
│ 3 │ ccc │
|
||||
│ 4 │ dddd │
|
||||
└───┴──────┘
|
||||
```
|
||||
```sql
|
||||
SELECT *
|
||||
FROM table_1
|
||||
SETTINGS additional_result_filter = 'x != 2'
|
||||
```
|
||||
```response
|
||||
┌─x─┬─y────┐
|
||||
│ 1 │ a │
|
||||
│ 3 │ ccc │
|
||||
│ 4 │ dddd │
|
||||
└───┴──────┘
|
||||
```
|
||||
|
||||
## allow_nondeterministic_mutations {#allow_nondeterministic_mutations}
|
||||
|
||||
User-level setting that allows mutations on replicated tables to make use of non-deterministic functions such as `dictGet`.
|
||||
@ -4071,6 +4104,36 @@ SELECT sum(number) FROM numbers(10000000000) SETTINGS partial_result_on_first_ca
|
||||
Possible values: `true`, `false`
|
||||
|
||||
Default value: `false`
|
||||
|
||||
## check_dictionary_primary_key {#check_dictionary_primary_key}
|
||||
|
||||
Enables the check at dictionay creation, dictionaries without word complex-key* in a layout have a key with UInt64 type. The primary key data type must be one of unsigned [integer types](../../sql-reference/data-types/int-uint.md): `UInt8`, `UInt16`, `UInt32`, `UInt64`.
|
||||
Possible values:
|
||||
|
||||
- true — The check is enabled.
|
||||
- false — The check is disabled at dictionay creation.
|
||||
|
||||
Default value: `true`.
|
||||
|
||||
If you already have dictionay with incorrect primar key and do not want the server to raise an exception during startup, set `check_dictionary_primary_key` to `false`.
|
||||
|
||||
Or you can create dictionay with settings `check_dictionary_primary_key` to `false`.
|
||||
|
||||
**Example**
|
||||
|
||||
```sql
|
||||
CREATE DICTIONARY test
|
||||
(
|
||||
`id` Int128,
|
||||
`name` String
|
||||
)
|
||||
PRIMARY KEY id
|
||||
SOURCE(CLICKHOUSE(TABLE 'test_local'))
|
||||
LIFETIME(MIN 0 MAX 300)
|
||||
LAYOUT(HASHED())
|
||||
SETTINGS(check_dictionary_primary_key = 0);
|
||||
```
|
||||
|
||||
## function_json_value_return_type_allow_nullable
|
||||
|
||||
Control whether allow to return `NULL` when value is not exist for JSON_VALUE function.
|
||||
|
@ -50,6 +50,7 @@ last_queue_update: 2021-10-12 14:50:08
|
||||
absolute_delay: 99
|
||||
total_replicas: 5
|
||||
active_replicas: 5
|
||||
lost_part_count: 0
|
||||
last_queue_update_exception:
|
||||
zookeeper_exception:
|
||||
replica_is_active: {'r1':1,'r2':1}
|
||||
@ -90,6 +91,7 @@ The next 4 columns have a non-zero value only where there is an active session w
|
||||
- `absolute_delay` (`UInt64`) - How big lag in seconds the current replica has.
|
||||
- `total_replicas` (`UInt8`) - The total number of known replicas of this table.
|
||||
- `active_replicas` (`UInt8`) - The number of replicas of this table that have a session in ClickHouse Keeper (i.e., the number of functioning replicas).
|
||||
- `lost_part_count` (`UInt64`) - The number of data parts lost in the table by all replicas in total since table creation. Value is persisted in ClickHouse Keeper and can only increase.
|
||||
- `last_queue_update_exception` (`String`) - When the queue contains broken entries. Especially important when ClickHouse breaks backward compatibility between versions and log entries written by newer versions aren't parseable by old versions.
|
||||
- `zookeeper_exception` (`String`) - The last exception message, got if the error happened when fetching the info from ClickHouse Keeper.
|
||||
- `replica_is_active` ([Map(String, UInt8)](../../sql-reference/data-types/map.md)) — Map between replica name and is replica active.
|
||||
|
@ -6,6 +6,10 @@ sidebar_label: clickhouse-local
|
||||
|
||||
# clickhouse-local
|
||||
|
||||
## Related Content
|
||||
|
||||
- Blog: [Extracting, Converting, and Querying Data in Local Files using clickhouse-local](https://clickhouse.com/blog/extracting-converting-querying-local-files-with-sql-clickhouse-local)
|
||||
|
||||
## When to use clickhouse-local vs. ClickHouse
|
||||
|
||||
`clickhouse-local` is an easy-to-use version of ClickHouse that is ideal for developers who need to perform fast processing on local and remote files using SQL without having to install a full database server. With `clickhouse-local`, developers can use SQL commands (using the [ClickHouse SQL dialect](../../sql-reference/index.md)) directly from the command line, providing a simple and efficient way to access ClickHouse features without the need for a full ClickHouse installation. One of the main benefits of `clickhouse-local` is that it is already included when installing [clickhouse-client](https://clickhouse.com/docs/en/integrations/sql-clients/clickhouse-client-local). This means that developers can get started with `clickhouse-local` quickly, without the need for a complex installation process.
|
||||
|
@ -285,3 +285,8 @@ FROM people
|
||||
│ [3,2] │ [11.5,12.949999809265137] │
|
||||
└────────┴───────────────────────────┘
|
||||
```
|
||||
|
||||
|
||||
## Related Content
|
||||
|
||||
- Blog: [Using Aggregate Combinators in ClickHouse](https://clickhouse.com/blog/aggregate-functions-combinators-in-clickhouse-for-arrays-maps-and-states)
|
||||
|
@ -63,3 +63,8 @@ SELECT uniqMerge(state) FROM (SELECT uniqState(UserID) AS state FROM table GROUP
|
||||
## Usage Example
|
||||
|
||||
See [AggregatingMergeTree](../../engines/table-engines/mergetree-family/aggregatingmergetree.md) engine description.
|
||||
|
||||
|
||||
## Related Content
|
||||
|
||||
- Blog: [Using Aggregate Combinators in ClickHouse](https://clickhouse.com/blog/aggregate-functions-combinators-in-clickhouse-for-arrays-maps-and-states)
|
||||
|
@ -108,3 +108,8 @@ Result:
|
||||
|
||||
- [map()](../../sql-reference/functions/tuple-map-functions.md#function-map) function
|
||||
- [CAST()](../../sql-reference/functions/type-conversion-functions.md#type_conversion_function-cast) function
|
||||
|
||||
|
||||
## Related content
|
||||
|
||||
- Blog: [Building an Observability Solution with ClickHouse - Part 2 - Traces](https://clickhouse.com/blog/storing-traces-and-spans-open-telemetry-in-clickhouse)
|
||||
|
@ -645,7 +645,7 @@ For an alternative to `date\_diff`, see function `age`.
|
||||
date_diff('unit', startdate, enddate, [timezone])
|
||||
```
|
||||
|
||||
Aliases: `dateDiff`, `DATE_DIFF`.
|
||||
Aliases: `dateDiff`, `DATE_DIFF`, `timestampDiff`, `timestamp_diff`, `TIMESTAMP_DIFF`.
|
||||
|
||||
**Arguments**
|
||||
|
||||
@ -1264,7 +1264,7 @@ Using replacement fields, you can define a pattern for the resulting string. “
|
||||
| %d | day of the month, zero-padded (01-31) | 02 |
|
||||
| %D | Short MM/DD/YY date, equivalent to %m/%d/%y | 01/02/18 |
|
||||
| %e | day of the month, space-padded (1-31) | 2 |
|
||||
| %f | fractional second from the fractional part of DateTime64 | 1234560 |
|
||||
| %f | fractional second, see 'Note 1' below | 1234560 |
|
||||
| %F | short YYYY-MM-DD date, equivalent to %Y-%m-%d | 2018-01-02 |
|
||||
| %g | two-digit year format, aligned to ISO 8601, abbreviated from four-digit notation | 18 |
|
||||
| %G | four-digit year format for ISO week number, calculated from the week-based year [defined by the ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Week_dates) standard, normally useful only with %V | 2018 |
|
||||
@ -1276,7 +1276,7 @@ Using replacement fields, you can define a pattern for the resulting string. “
|
||||
| %k | hour in 24h format (00-23) | 22 |
|
||||
| %l | hour in 12h format (01-12) | 09 |
|
||||
| %m | month as an integer number (01-12) | 01 |
|
||||
| %M | full month name (January-December), see (*) below | January |
|
||||
| %M | full month name (January-December), see 'Note 2' below | January |
|
||||
| %n | new-line character (‘’) | |
|
||||
| %p | AM or PM designation | PM |
|
||||
| %Q | Quarter (1-4) | 1 |
|
||||
@ -1295,7 +1295,9 @@ Using replacement fields, you can define a pattern for the resulting string. “
|
||||
| %z | Time offset from UTC as +HHMM or -HHMM | -0500 |
|
||||
| %% | a % sign | % |
|
||||
|
||||
(*) In ClickHouse versions earlier than v23.4, `%M` prints the minute (00-59) instead of the full month name (January-December). The previous behavior can be restored using setting `formatdatetime_parsedatetime_m_is_month_name = 0`.
|
||||
Note 1: In ClickHouse versions earlier than v23.4, `%f` prints a single zero (0) if the formatted value is a Date, Date32 or DateTime (which have no fractional seconds) or a DateTime64 with a precision of 0. The previous behavior can be restored using setting `formatdatetime_f_prints_single_zero = 1`.
|
||||
|
||||
Note 2: In ClickHouse versions earlier than v23.4, `%M` prints the minute (00-59) instead of the full month name (January-December). The previous behavior can be restored using setting `formatdatetime_parsedatetime_m_is_month_name = 0`.
|
||||
|
||||
**Example**
|
||||
|
||||
|
@ -194,7 +194,14 @@ Accepts a number. If the number is less than one, it returns 0. Otherwise, it ro
|
||||
|
||||
## roundAge(num)
|
||||
|
||||
Accepts a number. If the number is less than 18, it returns 0. Otherwise, it rounds the number down to a number from the set: 18, 25, 35, 45, 55.
|
||||
Accepts a number. If the number is
|
||||
- smaller than 1, it returns 0,
|
||||
- between 1 and 17, it returns 17,
|
||||
- between 18 and 24, it returns 18,
|
||||
- between 25 and 34, it returns 25,
|
||||
- between 35 and 44, it returns 35,
|
||||
- between 45 and 54, it returns 45,
|
||||
- larger than 55, it returns 55.
|
||||
|
||||
## roundDown(num, arr)
|
||||
|
||||
|
@ -1245,7 +1245,6 @@ Returns DateTime values parsed from input string according to a MySQL style form
|
||||
**Supported format specifiers**
|
||||
|
||||
All format specifiers listed in [formatDateTime](/docs/en/sql-reference/functions/date-time-functions.md#date_time_functions-formatDateTime) except:
|
||||
- %f: fractional second
|
||||
- %Q: Quarter (1-4)
|
||||
|
||||
**Example**
|
||||
|
@ -28,3 +28,7 @@ The synchronicity of the query processing is defined by the [mutations_sync](/do
|
||||
- [Mutations](/docs/en/sql-reference/statements/alter/index.md#mutations)
|
||||
- [Synchronicity of ALTER Queries](/docs/en/sql-reference/statements/alter/index.md#synchronicity-of-alter-queries)
|
||||
- [mutations_sync](/docs/en/operations/settings/settings.md/#mutations_sync) setting
|
||||
|
||||
## Related content
|
||||
|
||||
- Blog: [Handling Updates and Deletes in ClickHouse](https://clickhouse.com/blog/handling-updates-and-deletes-in-clickhouse)
|
||||
|
@ -61,3 +61,7 @@ For all `ALTER` queries, if `alter_sync = 2` and some replicas are not active fo
|
||||
:::
|
||||
|
||||
For `ALTER TABLE ... UPDATE|DELETE` queries the synchronicity is defined by the [mutations_sync](/docs/en/operations/settings/settings.md/#mutations_sync) setting.
|
||||
|
||||
## Related content
|
||||
|
||||
- Blog: [Handling Updates and Deletes in ClickHouse](https://clickhouse.com/blog/handling-updates-and-deletes-in-clickhouse)
|
||||
|
@ -27,3 +27,8 @@ The synchronicity of the query processing is defined by the [mutations_sync](/do
|
||||
- [Mutations](/docs/en/sql-reference/statements/alter/index.md#mutations)
|
||||
- [Synchronicity of ALTER Queries](/docs/en/sql-reference/statements/alter/index.md#synchronicity-of-alter-queries)
|
||||
- [mutations_sync](/docs/en/operations/settings/settings.md/#mutations_sync) setting
|
||||
|
||||
|
||||
## Related content
|
||||
|
||||
- Blog: [Handling Updates and Deletes in ClickHouse](https://clickhouse.com/blog/handling-updates-and-deletes-in-clickhouse)
|
||||
|
@ -364,3 +364,4 @@ The window view is useful in the following scenarios:
|
||||
## Related Content
|
||||
|
||||
- Blog: [Working with time series data in ClickHouse](https://clickhouse.com/blog/working-with-time-series-data-and-functions-ClickHouse)
|
||||
- Blog: [Building an Observability Solution with ClickHouse - Part 2 - Traces](https://clickhouse.com/blog/storing-traces-and-spans-open-telemetry-in-clickhouse)
|
||||
|
@ -55,3 +55,7 @@ With the described implementation now we can see what can negatively affect 'DEL
|
||||
- Table having a very large number of data parts
|
||||
- Having a lot of data in Compact parts—in a Compact part, all columns are stored in one file.
|
||||
|
||||
|
||||
## Related content
|
||||
|
||||
- Blog: [Handling Updates and Deletes in ClickHouse](https://clickhouse.com/blog/handling-updates-and-deletes-in-clickhouse)
|
||||
|
@ -18,6 +18,10 @@ FROM <left_table>
|
||||
|
||||
Expressions from `ON` clause and columns from `USING` clause are called “join keys”. Unless otherwise stated, join produces a [Cartesian product](https://en.wikipedia.org/wiki/Cartesian_product) from rows with matching “join keys”, which might produce results with much more rows than the source tables.
|
||||
|
||||
## Related Content
|
||||
|
||||
- Blog: [ClickHouse: A Blazingly Fast DBMS with Full SQL Join Support - Part 1](https://clickhouse.com/blog/clickhouse-fully-supports-joins)
|
||||
|
||||
## Supported Types of JOIN
|
||||
|
||||
All standard [SQL JOIN](https://en.wikipedia.org/wiki/Join_(SQL)) types are supported:
|
||||
|
@ -133,4 +133,6 @@ CREATE TABLE pg_table_schema_with_dots (a UInt32)
|
||||
- [Using PostgreSQL as a dictionary source](../../sql-reference/dictionaries/index.md#dictionary-sources#dicts-external_dicts_dict_sources-postgresql)
|
||||
|
||||
## Related content
|
||||
|
||||
- Blog: [ClickHouse and PostgreSQL - a match made in data heaven - part 1](https://clickhouse.com/blog/migrating-data-between-clickhouse-postgres)
|
||||
- Blog: [ClickHouse and PostgreSQL - a Match Made in Data Heaven - part 2](https://clickhouse.com/blog/migrating-data-between-clickhouse-postgres-part-2)
|
||||
|
@ -41,9 +41,15 @@ ClickHouse не работает и не собирается на 32-битны
|
||||
|
||||
Выполните в терминале:
|
||||
|
||||
git clone git@github.com:your_github_username/ClickHouse.git --recursive
|
||||
git clone --shallow-submodules git@github.com:your_github_username/ClickHouse.git
|
||||
cd ClickHouse
|
||||
|
||||
Или (если вы хотите использовать sparse checkout для submodules):
|
||||
|
||||
git clone git@github.com:your_github_username/ClickHouse.git
|
||||
cd ClickHouse
|
||||
./contrib/update-submodules.sh
|
||||
|
||||
Замените слово `your_github_username` в команде для git на имя вашего аккаунта на GitHub.
|
||||
|
||||
Эта команда создаст директорию ClickHouse, содержащую рабочую копию проекта.
|
||||
|
@ -491,7 +491,7 @@ std::vector<std::pair<ASTPtr, StoragePtr>> BackupEntriesCollector::findTablesInD
|
||||
{
|
||||
/// Database or table could be replicated - so may use ZooKeeper. We need to retry.
|
||||
auto zookeeper_retries_info = global_zookeeper_retries_info;
|
||||
ZooKeeperRetriesControl retries_ctl("getTablesForBackup", zookeeper_retries_info);
|
||||
ZooKeeperRetriesControl retries_ctl("getTablesForBackup", zookeeper_retries_info, nullptr);
|
||||
retries_ctl.retryLoop([&](){ db_tables = database->getTablesForBackup(filter_by_table_name, context); });
|
||||
}
|
||||
catch (Exception & e)
|
||||
|
@ -20,7 +20,7 @@ WithRetries::WithRetries(Poco::Logger * log_, zkutil::GetZooKeeper get_zookeeper
|
||||
|
||||
WithRetries::RetriesControlHolder::RetriesControlHolder(const WithRetries * parent, const String & name)
|
||||
: info(parent->global_zookeeper_retries_info)
|
||||
, retries_ctl(name, info)
|
||||
, retries_ctl(name, info, nullptr)
|
||||
, faulty_zookeeper(parent->getFaultyZooKeeper())
|
||||
{}
|
||||
|
||||
|
@ -90,7 +90,7 @@ void CurrentThread::attachInternalTextLogsQueue(const std::shared_ptr<InternalTe
|
||||
}
|
||||
|
||||
|
||||
ThreadGroupStatusPtr CurrentThread::getGroup()
|
||||
ThreadGroupPtr CurrentThread::getGroup()
|
||||
{
|
||||
if (unlikely(!current_thread))
|
||||
return nullptr;
|
||||
|
@ -39,7 +39,7 @@ public:
|
||||
static ThreadStatus & get();
|
||||
|
||||
/// Group to which belongs current thread
|
||||
static ThreadGroupStatusPtr getGroup();
|
||||
static ThreadGroupPtr getGroup();
|
||||
|
||||
/// A logs queue used by TCPHandler to pass logs to a client
|
||||
static void attachInternalTextLogsQueue(const std::shared_ptr<InternalTextLogsQueue> & logs_queue,
|
||||
@ -69,9 +69,9 @@ public:
|
||||
|
||||
/// You must call one of these methods when create a query child thread:
|
||||
/// Add current thread to a group associated with the thread group
|
||||
static void attachToGroup(const ThreadGroupStatusPtr & thread_group);
|
||||
static void attachToGroup(const ThreadGroupPtr & thread_group);
|
||||
/// Is useful for a ThreadPool tasks
|
||||
static void attachToGroupIfDetached(const ThreadGroupStatusPtr & thread_group);
|
||||
static void attachToGroupIfDetached(const ThreadGroupPtr & thread_group);
|
||||
|
||||
/// Non-master threads call this method in destructor automatically
|
||||
static void detachFromGroupIfNotDetached();
|
||||
|
@ -63,7 +63,7 @@ static thread_local ThreadStack alt_stack;
|
||||
static thread_local bool has_alt_stack = false;
|
||||
#endif
|
||||
|
||||
ThreadGroupStatus::ThreadGroupStatus()
|
||||
ThreadGroup::ThreadGroup()
|
||||
: master_thread_id(CurrentThread::get().thread_id)
|
||||
{}
|
||||
|
||||
@ -121,7 +121,7 @@ ThreadStatus::ThreadStatus()
|
||||
#endif
|
||||
}
|
||||
|
||||
ThreadGroupStatusPtr ThreadStatus::getThreadGroup() const
|
||||
ThreadGroupPtr ThreadStatus::getThreadGroup() const
|
||||
{
|
||||
return thread_group;
|
||||
}
|
||||
@ -141,7 +141,7 @@ ContextPtr ThreadStatus::getGlobalContext() const
|
||||
return global_context.lock();
|
||||
}
|
||||
|
||||
void ThreadGroupStatus::attachInternalTextLogsQueue(const InternalTextLogsQueuePtr & logs_queue, LogsLevel logs_level)
|
||||
void ThreadGroup::attachInternalTextLogsQueue(const InternalTextLogsQueuePtr & logs_queue, LogsLevel logs_level)
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
shared_data.logs_queue_ptr = logs_queue;
|
||||
|
@ -41,7 +41,6 @@ class TaskStatsInfoGetter;
|
||||
class InternalTextLogsQueue;
|
||||
struct ViewRuntimeData;
|
||||
class QueryViewsLog;
|
||||
class ThreadGroupSwitcher;
|
||||
using InternalTextLogsQueuePtr = std::shared_ptr<InternalTextLogsQueue>;
|
||||
using InternalTextLogsQueueWeakPtr = std::weak_ptr<InternalTextLogsQueue>;
|
||||
|
||||
@ -58,15 +57,15 @@ using ThreadStatusPtr = ThreadStatus *;
|
||||
* Create via CurrentThread::initializeQuery (for queries) or directly (for various background tasks).
|
||||
* Use via CurrentThread::getGroup.
|
||||
*/
|
||||
class ThreadGroupStatus;
|
||||
using ThreadGroupStatusPtr = std::shared_ptr<ThreadGroupStatus>;
|
||||
class ThreadGroup;
|
||||
using ThreadGroupPtr = std::shared_ptr<ThreadGroup>;
|
||||
|
||||
class ThreadGroupStatus
|
||||
class ThreadGroup
|
||||
{
|
||||
public:
|
||||
ThreadGroupStatus();
|
||||
ThreadGroup();
|
||||
using FatalErrorCallback = std::function<void()>;
|
||||
ThreadGroupStatus(ContextPtr query_context_, FatalErrorCallback fatal_error_callback_ = {});
|
||||
ThreadGroup(ContextPtr query_context_, FatalErrorCallback fatal_error_callback_ = {});
|
||||
|
||||
/// The first thread created this thread group
|
||||
const UInt64 master_thread_id;
|
||||
@ -104,9 +103,9 @@ public:
|
||||
void attachInternalProfileEventsQueue(const InternalProfileEventsQueuePtr & profile_queue);
|
||||
|
||||
/// When new query starts, new thread group is created for it, current thread becomes master thread of the query
|
||||
static ThreadGroupStatusPtr createForQuery(ContextPtr query_context_, FatalErrorCallback fatal_error_callback_ = {});
|
||||
static ThreadGroupPtr createForQuery(ContextPtr query_context_, FatalErrorCallback fatal_error_callback_ = {});
|
||||
|
||||
static ThreadGroupStatusPtr createForBackgroundProcess(ContextPtr storage_context);
|
||||
static ThreadGroupPtr createForBackgroundProcess(ContextPtr storage_context);
|
||||
|
||||
std::vector<UInt64> getInvolvedThreadIds() const;
|
||||
void linkThread(UInt64 thread_it);
|
||||
@ -120,6 +119,21 @@ private:
|
||||
std::unordered_set<UInt64> thread_ids;
|
||||
};
|
||||
|
||||
/**
|
||||
* Since merge is executed with multiple threads, this class
|
||||
* switches the parent MemoryTracker as part of the thread group to account all the memory used.
|
||||
*/
|
||||
class ThreadGroupSwitcher : private boost::noncopyable
|
||||
{
|
||||
public:
|
||||
explicit ThreadGroupSwitcher(ThreadGroupPtr thread_group);
|
||||
~ThreadGroupSwitcher();
|
||||
|
||||
private:
|
||||
ThreadGroupPtr prev_thread_group;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* We use **constinit** here to tell the compiler the current_thread variable is initialized.
|
||||
* If we didn't help the compiler, then it would most likely add a check before every use of the variable to initialize it if needed.
|
||||
@ -163,7 +177,7 @@ public:
|
||||
|
||||
private:
|
||||
/// Group of threads, to which this thread attached
|
||||
ThreadGroupStatusPtr thread_group;
|
||||
ThreadGroupPtr thread_group;
|
||||
|
||||
/// Is set once
|
||||
ContextWeakPtr global_context;
|
||||
@ -174,7 +188,7 @@ private:
|
||||
using FatalErrorCallback = std::function<void()>;
|
||||
FatalErrorCallback fatal_error_callback;
|
||||
|
||||
ThreadGroupStatus::SharedData local_data;
|
||||
ThreadGroup::SharedData local_data;
|
||||
|
||||
bool performance_counters_finalized = false;
|
||||
|
||||
@ -215,7 +229,7 @@ public:
|
||||
ThreadStatus();
|
||||
~ThreadStatus();
|
||||
|
||||
ThreadGroupStatusPtr getThreadGroup() const;
|
||||
ThreadGroupPtr getThreadGroup() const;
|
||||
|
||||
const String & getQueryId() const;
|
||||
|
||||
@ -239,7 +253,7 @@ public:
|
||||
void setInternalThread();
|
||||
|
||||
/// Attaches slave thread to existing thread group
|
||||
void attachToGroup(const ThreadGroupStatusPtr & thread_group_, bool check_detached = true);
|
||||
void attachToGroup(const ThreadGroupPtr & thread_group_, bool check_detached = true);
|
||||
|
||||
/// Detaches thread from the thread group and the query, dumps performance counters if they have not been dumped
|
||||
void detachFromGroup();
|
||||
@ -287,7 +301,7 @@ private:
|
||||
|
||||
void logToQueryThreadLog(QueryThreadLog & thread_log, const String & current_database);
|
||||
|
||||
void attachToGroupImpl(const ThreadGroupStatusPtr & thread_group_);
|
||||
void attachToGroupImpl(const ThreadGroupPtr & thread_group_);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -62,16 +62,10 @@ String getSHA1(const String & userdata)
|
||||
return String{digest_id.begin(), digest_id.end()};
|
||||
}
|
||||
|
||||
String generateDigest(const String & userdata)
|
||||
{
|
||||
std::vector<String> user_password;
|
||||
boost::split(user_password, userdata, [](char character) { return character == ':'; });
|
||||
return user_password[0] + ":" + base64Encode(getSHA1(userdata));
|
||||
}
|
||||
|
||||
bool fixupACL(
|
||||
const std::vector<Coordination::ACL> & request_acls,
|
||||
const std::vector<KeeperStorage::AuthID> & current_ids,
|
||||
int64_t session_id,
|
||||
const KeeperStorage::UncommittedState & uncommitted_state,
|
||||
std::vector<Coordination::ACL> & result_acls)
|
||||
{
|
||||
if (request_acls.empty())
|
||||
@ -82,14 +76,18 @@ bool fixupACL(
|
||||
{
|
||||
if (request_acl.scheme == "auth")
|
||||
{
|
||||
for (const auto & current_id : current_ids)
|
||||
uncommitted_state.forEachAuthInSession(
|
||||
session_id,
|
||||
[&](const KeeperStorage::AuthID & auth_id)
|
||||
{
|
||||
valid_found = true;
|
||||
Coordination::ACL new_acl = request_acl;
|
||||
new_acl.scheme = current_id.scheme;
|
||||
new_acl.id = current_id.id;
|
||||
|
||||
new_acl.scheme = auth_id.scheme;
|
||||
new_acl.id = auth_id.id;
|
||||
|
||||
result_acls.push_back(new_acl);
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (request_acl.scheme == "world" && request_acl.id == "anyone")
|
||||
{
|
||||
@ -565,6 +563,32 @@ Coordination::ACLs KeeperStorage::UncommittedState::getACLs(StringRef path) cons
|
||||
return storage.acl_map.convertNumber(node_it->value.acl_id);
|
||||
}
|
||||
|
||||
void KeeperStorage::UncommittedState::forEachAuthInSession(int64_t session_id, std::function<void(const AuthID &)> func) const
|
||||
{
|
||||
const auto call_for_each_auth = [&func](const auto & auth_ids)
|
||||
{
|
||||
for (const auto & auth : auth_ids)
|
||||
{
|
||||
using TAuth = std::remove_reference_t<decltype(auth)>;
|
||||
|
||||
const AuthID * auth_ptr = nullptr;
|
||||
if constexpr (std::is_pointer_v<TAuth>)
|
||||
auth_ptr = auth;
|
||||
else
|
||||
auth_ptr = &auth;
|
||||
|
||||
func(*auth_ptr);
|
||||
}
|
||||
};
|
||||
|
||||
// for committed
|
||||
if (storage.session_and_auth.contains(session_id))
|
||||
call_for_each_auth(storage.session_and_auth.at(session_id));
|
||||
// for uncommitted
|
||||
if (session_and_auth.contains(session_id))
|
||||
call_for_each_auth(session_and_auth.at(session_id));
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
@ -928,7 +952,7 @@ struct KeeperStorageCreateRequestProcessor final : public KeeperStorageRequestPr
|
||||
return {KeeperStorage::Delta{zxid, Coordination::Error::ZBADARGUMENTS}};
|
||||
|
||||
Coordination::ACLs node_acls;
|
||||
if (!fixupACL(request.acls, storage.session_and_auth[session_id], node_acls))
|
||||
if (!fixupACL(request.acls, session_id, storage.uncommitted_state, node_acls))
|
||||
return {KeeperStorage::Delta{zxid, Coordination::Error::ZINVALIDACL}};
|
||||
|
||||
if (request.is_ephemeral)
|
||||
@ -1534,10 +1558,8 @@ struct KeeperStorageSetACLRequestProcessor final : public KeeperStorageRequestPr
|
||||
return {KeeperStorage::Delta{zxid, Coordination::Error::ZBADVERSION}};
|
||||
|
||||
|
||||
auto & session_auth_ids = storage.session_and_auth[session_id];
|
||||
Coordination::ACLs node_acls;
|
||||
|
||||
if (!fixupACL(request.acls, session_auth_ids, node_acls))
|
||||
if (!fixupACL(request.acls, session_id, uncommitted_state, node_acls))
|
||||
return {KeeperStorage::Delta{zxid, Coordination::Error::ZINVALIDACL}};
|
||||
|
||||
std::vector<KeeperStorage::Delta> new_deltas
|
||||
@ -1841,7 +1863,7 @@ struct KeeperStorageAuthRequestProcessor final : public KeeperStorageRequestProc
|
||||
return {KeeperStorage::Delta{zxid, Coordination::Error::ZAUTHFAILED}};
|
||||
|
||||
std::vector<KeeperStorage::Delta> new_deltas;
|
||||
auto auth_digest = generateDigest(auth_request.data);
|
||||
auto auth_digest = KeeperStorage::generateDigest(auth_request.data);
|
||||
if (auth_digest == storage.superdigest)
|
||||
{
|
||||
KeeperStorage::AuthID auth{"super", ""};
|
||||
@ -2421,5 +2443,12 @@ void KeeperStorage::recalculateStats()
|
||||
container.recalculateDataSize();
|
||||
}
|
||||
|
||||
String KeeperStorage::generateDigest(const String & userdata)
|
||||
{
|
||||
std::vector<String> user_password;
|
||||
boost::split(user_password, userdata, [](char character) { return character == ':'; });
|
||||
return user_password[0] + ":" + base64Encode(getSHA1(userdata));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -105,6 +105,8 @@ public:
|
||||
return first.value == second.value;
|
||||
}
|
||||
|
||||
static String generateDigest(const String & userdata);
|
||||
|
||||
struct RequestForSession
|
||||
{
|
||||
int64_t session_id;
|
||||
@ -263,6 +265,8 @@ public:
|
||||
return check_auth(auth_it->second);
|
||||
}
|
||||
|
||||
void forEachAuthInSession(int64_t session_id, std::function<void(const AuthID &)> func) const;
|
||||
|
||||
std::shared_ptr<Node> tryGetNodeFromStorage(StringRef path) const;
|
||||
|
||||
std::unordered_map<int64_t, std::list<const AuthID *>> session_and_auth;
|
||||
|
@ -1579,6 +1579,113 @@ TEST_P(CoordinationTest, TestEphemeralNodeRemove)
|
||||
}
|
||||
|
||||
|
||||
TEST_P(CoordinationTest, TestCreateNodeWithAuthSchemeForAclWhenAuthIsPrecommitted)
|
||||
{
|
||||
using namespace Coordination;
|
||||
using namespace DB;
|
||||
|
||||
ChangelogDirTest snapshots("./snapshots");
|
||||
CoordinationSettingsPtr settings = std::make_shared<CoordinationSettings>();
|
||||
ResponsesQueue queue(std::numeric_limits<size_t>::max());
|
||||
SnapshotsQueue snapshots_queue{1};
|
||||
|
||||
auto state_machine = std::make_shared<KeeperStateMachine>(queue, snapshots_queue, "./snapshots", settings, keeper_context, nullptr);
|
||||
state_machine->init();
|
||||
|
||||
String user_auth_data = "test_user:test_password";
|
||||
String digest = KeeperStorage::generateDigest(user_auth_data);
|
||||
|
||||
std::shared_ptr<ZooKeeperAuthRequest> auth_req = std::make_shared<ZooKeeperAuthRequest>();
|
||||
auth_req->scheme = "digest";
|
||||
auth_req->data = user_auth_data;
|
||||
|
||||
// Add auth data to the session
|
||||
auto auth_entry = getLogEntryFromZKRequest(0, 1, state_machine->getNextZxid(), auth_req);
|
||||
state_machine->pre_commit(1, auth_entry->get_buf());
|
||||
|
||||
// Create a node with 'auth' scheme for ACL
|
||||
String node_path = "/hello";
|
||||
std::shared_ptr<ZooKeeperCreateRequest> create_req = std::make_shared<ZooKeeperCreateRequest>();
|
||||
create_req->path = node_path;
|
||||
// When 'auth' scheme is used the creator must have been authenticated by the server (for example, using 'digest' scheme) before it can
|
||||
// create nodes with this ACL.
|
||||
create_req->acls = {{.permissions = 31, .scheme = "auth", .id = ""}};
|
||||
auto create_entry = getLogEntryFromZKRequest(0, 1, state_machine->getNextZxid(), create_req);
|
||||
state_machine->pre_commit(2, create_entry->get_buf());
|
||||
|
||||
const auto & uncommitted_state = state_machine->getStorage().uncommitted_state;
|
||||
ASSERT_TRUE(uncommitted_state.nodes.contains(node_path));
|
||||
|
||||
// commit log entries
|
||||
state_machine->commit(1, auth_entry->get_buf());
|
||||
state_machine->commit(2, create_entry->get_buf());
|
||||
|
||||
auto node = uncommitted_state.getNode(node_path);
|
||||
ASSERT_NE(node, nullptr);
|
||||
auto acls = uncommitted_state.getACLs(node_path);
|
||||
ASSERT_EQ(acls.size(), 1);
|
||||
EXPECT_EQ(acls[0].scheme, "digest");
|
||||
EXPECT_EQ(acls[0].id, digest);
|
||||
EXPECT_EQ(acls[0].permissions, 31);
|
||||
}
|
||||
|
||||
TEST_P(CoordinationTest, TestSetACLWithAuthSchemeForAclWhenAuthIsPrecommitted)
|
||||
{
|
||||
using namespace Coordination;
|
||||
using namespace DB;
|
||||
|
||||
ChangelogDirTest snapshots("./snapshots");
|
||||
CoordinationSettingsPtr settings = std::make_shared<CoordinationSettings>();
|
||||
ResponsesQueue queue(std::numeric_limits<size_t>::max());
|
||||
SnapshotsQueue snapshots_queue{1};
|
||||
|
||||
auto state_machine = std::make_shared<KeeperStateMachine>(queue, snapshots_queue, "./snapshots", settings, keeper_context, nullptr);
|
||||
state_machine->init();
|
||||
|
||||
String user_auth_data = "test_user:test_password";
|
||||
String digest = KeeperStorage::generateDigest(user_auth_data);
|
||||
|
||||
std::shared_ptr<ZooKeeperAuthRequest> auth_req = std::make_shared<ZooKeeperAuthRequest>();
|
||||
auth_req->scheme = "digest";
|
||||
auth_req->data = user_auth_data;
|
||||
|
||||
// Add auth data to the session
|
||||
auto auth_entry = getLogEntryFromZKRequest(0, 1, state_machine->getNextZxid(), auth_req);
|
||||
state_machine->pre_commit(1, auth_entry->get_buf());
|
||||
|
||||
// Create a node
|
||||
String node_path = "/hello";
|
||||
std::shared_ptr<ZooKeeperCreateRequest> create_req = std::make_shared<ZooKeeperCreateRequest>();
|
||||
create_req->path = node_path;
|
||||
auto create_entry = getLogEntryFromZKRequest(0, 1, state_machine->getNextZxid(), create_req);
|
||||
state_machine->pre_commit(2, create_entry->get_buf());
|
||||
|
||||
// Set ACL with 'auth' scheme for ACL
|
||||
std::shared_ptr<ZooKeeperSetACLRequest> set_acl_req = std::make_shared<ZooKeeperSetACLRequest>();
|
||||
set_acl_req->path = node_path;
|
||||
// When 'auth' scheme is used the creator must have been authenticated by the server (for example, using 'digest' scheme) before it can
|
||||
// set this ACL.
|
||||
set_acl_req->acls = {{.permissions = 31, .scheme = "auth", .id = ""}};
|
||||
auto set_acl_entry = getLogEntryFromZKRequest(0, 1, state_machine->getNextZxid(), set_acl_req);
|
||||
state_machine->pre_commit(3, set_acl_entry->get_buf());
|
||||
|
||||
// commit all entries
|
||||
state_machine->commit(1, auth_entry->get_buf());
|
||||
state_machine->commit(2, create_entry->get_buf());
|
||||
state_machine->commit(3, set_acl_entry->get_buf());
|
||||
|
||||
const auto & uncommitted_state = state_machine->getStorage().uncommitted_state;
|
||||
auto node = uncommitted_state.getNode(node_path);
|
||||
|
||||
ASSERT_NE(node, nullptr);
|
||||
auto acls = uncommitted_state.getACLs(node_path);
|
||||
ASSERT_EQ(acls.size(), 1);
|
||||
EXPECT_EQ(acls[0].scheme, "digest");
|
||||
EXPECT_EQ(acls[0].id, digest);
|
||||
EXPECT_EQ(acls[0].permissions, 31);
|
||||
}
|
||||
|
||||
|
||||
TEST_P(CoordinationTest, TestRotateIntervalChanges)
|
||||
{
|
||||
using namespace Coordination;
|
||||
|
@ -467,7 +467,8 @@ class IColumn;
|
||||
M(Bool, allow_introspection_functions, false, "Allow functions for introspection of ELF and DWARF for query profiling. These functions are slow and may impose security considerations.", 0) \
|
||||
\
|
||||
M(Bool, allow_execute_multiif_columnar, true, "Allow execute multiIf function columnar", 0) \
|
||||
M(Bool, formatdatetime_parsedatetime_m_is_month_name, true, "Formatter '%M' in function 'formatDateTime' produces the month name instead of minutes.", 0) \
|
||||
M(Bool, formatdatetime_f_prints_single_zero, false, "Formatter '%f' in function 'formatDateTime()' produces a single zero instead of six zeros if the formatted value has no fractional seconds.", 0) \
|
||||
M(Bool, formatdatetime_parsedatetime_m_is_month_name, true, "Formatter '%M' in functions 'formatDateTime()' and 'parseDateTime()' produces the month name instead of minutes.", 0) \
|
||||
\
|
||||
M(UInt64, max_partitions_per_insert_block, 100, "Limit maximum number of partitions in single INSERTed block. Zero means unlimited. Throw exception if the block contains too many partitions. This setting is a safety threshold, because using large number of partitions is a common misconception.", 0) \
|
||||
M(Int64, max_partitions_to_read, -1, "Limit the max number of partitions that can be accessed in one query. <= 0 means unlimited.", 0) \
|
||||
@ -958,6 +959,7 @@ class IColumn;
|
||||
M(Bool, regexp_dict_allow_hyperscan, true, "Allow regexp_tree dictionary using Hyperscan library.", 0) \
|
||||
\
|
||||
M(Bool, dictionary_use_async_executor, false, "Execute a pipeline for reading from a dictionary with several threads. It's supported only by DIRECT dictionary with CLICKHOUSE source.", 0) \
|
||||
M(Bool, check_dictionary_primary_key, true, "Check primary key type for simple dictionary is native unsigned integer", 0) \
|
||||
|
||||
// End of FORMAT_FACTORY_SETTINGS
|
||||
// Please add settings non-related to formats into the COMMON_SETTINGS above.
|
||||
|
@ -101,6 +101,7 @@ static std::map<ClickHouseVersion, SettingsChangesHistory::SettingsChanges> sett
|
||||
{"query_plan_aggregation_in_order", 0, 1, "Enable some refactoring around query plan"},
|
||||
{"format_binary_max_string_size", 0, 1_GiB, "Prevent allocating large amount of memory"}}},
|
||||
{"22.11", {{"use_structure_from_insertion_table_in_table_functions", 0, 2, "Improve using structure from insertion table in table functions"}}},
|
||||
{"23.4", {{"formatdatetime_f_prints_single_zero", true, false, "Improved compatibility with MySQL DATE_FORMAT()/STR_TO_DATE()"}}},
|
||||
{"23.4", {{"formatdatetime_parsedatetime_m_is_month_name", false, true, "Improved compatibility with MySQL DATE_FORMAT/STR_TO_DATE"}}},
|
||||
{"22.9", {{"force_grouping_standard_compatibility", false, true, "Make GROUPING function output the same as in SQL standard and other DBMS"}}},
|
||||
{"22.7", {{"cross_to_inner_join_rewrite", 1, 2, "Force rewrite comma join to inner"},
|
||||
|
@ -453,8 +453,8 @@ struct SettingFieldMultiEnum
|
||||
explicit operator StorageType() const { return value.getValue(); }
|
||||
explicit operator Field() const { return toString(); }
|
||||
|
||||
SettingFieldMultiEnum & operator= (StorageType x) { changed = x != value.getValue(); value.setValue(x); return *this; }
|
||||
SettingFieldMultiEnum & operator= (ValueType x) { changed = !(x == value); value = x; return *this; }
|
||||
SettingFieldMultiEnum & operator= (StorageType x) { changed = true; value.setValue(x); return *this; }
|
||||
SettingFieldMultiEnum & operator= (ValueType x) { changed = true; value = x; return *this; }
|
||||
SettingFieldMultiEnum & operator= (const Field & x) { parseFromString(x.safeGet<const String &>()); return *this; }
|
||||
|
||||
String toString() const
|
||||
|
@ -122,7 +122,7 @@ GTEST_TEST(SettingMySQLDataTypesSupport, SetString)
|
||||
|
||||
// comma with spaces
|
||||
setting = " datetime64 , decimal ";
|
||||
ASSERT_FALSE(setting.changed); // false since value is the same as previous one.
|
||||
ASSERT_TRUE(setting.changed);
|
||||
ASSERT_TRUE(setting.value.isSet(MySQLDataTypesSupport::DECIMAL));
|
||||
ASSERT_TRUE(setting.value.isSet(MySQLDataTypesSupport::DATETIME64));
|
||||
ASSERT_EQ("decimal,datetime64", setting.toString());
|
||||
@ -136,7 +136,7 @@ GTEST_TEST(SettingMySQLDataTypesSupport, SetString)
|
||||
ASSERT_EQ(Field("decimal"), setting);
|
||||
|
||||
setting = String(",decimal,decimal,decimal,decimal,decimal,decimal,decimal,decimal,decimal,");
|
||||
ASSERT_FALSE(setting.changed); //since previous value was DECIMAL
|
||||
ASSERT_TRUE(setting.changed); //since previous value was DECIMAL
|
||||
ASSERT_TRUE(setting.value.isSet(MySQLDataTypesSupport::DECIMAL));
|
||||
ASSERT_FALSE(setting.value.isSet(MySQLDataTypesSupport::DATETIME64));
|
||||
ASSERT_EQ("decimal", setting.toString());
|
||||
@ -163,7 +163,7 @@ GTEST_TEST(SettingMySQLDataTypesSupport, SetInvalidString)
|
||||
ASSERT_EQ(0, setting.value.getValue());
|
||||
|
||||
EXPECT_NO_THROW(setting = String(", "));
|
||||
ASSERT_FALSE(setting.changed);
|
||||
ASSERT_TRUE(setting.changed);
|
||||
ASSERT_EQ(0, setting.value.getValue());
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@ namespace DB
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int ARGUMENT_OUT_OF_BOUND;
|
||||
extern const int LOGICAL_ERROR;
|
||||
}
|
||||
|
||||
static constexpr UInt32 max_scale = 9;
|
||||
@ -56,4 +57,14 @@ SerializationPtr DataTypeDateTime64::doGetDefaultSerialization() const
|
||||
return std::make_shared<SerializationDateTime64>(scale, *this);
|
||||
}
|
||||
|
||||
std::string getDateTimeTimezone(const IDataType & data_type)
|
||||
{
|
||||
if (const auto * type = typeid_cast<const DataTypeDateTime *>(&data_type))
|
||||
return type->hasExplicitTimeZone() ? type->getTimeZone().getTimeZone() : std::string();
|
||||
if (const auto * type = typeid_cast<const DataTypeDateTime64 *>(&data_type))
|
||||
return type->hasExplicitTimeZone() ? type->getTimeZone().getTimeZone() : std::string();
|
||||
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Cannot get time zone from type {}", data_type.getName());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -41,5 +41,7 @@ protected:
|
||||
SerializationPtr doGetDefaultSerialization() const override;
|
||||
};
|
||||
|
||||
std::string getDateTimeTimezone(const IDataType & data_type);
|
||||
|
||||
}
|
||||
|
||||
|
@ -556,6 +556,7 @@ inline bool isNullableOrLowCardinalityNullable(const DataTypePtr & data_type)
|
||||
template <typename DataType> constexpr bool IsDataTypeDecimal = false;
|
||||
template <typename DataType> constexpr bool IsDataTypeNumber = false;
|
||||
template <typename DataType> constexpr bool IsDataTypeDateOrDateTime = false;
|
||||
template <typename DataType> constexpr bool IsDataTypeDate = false;
|
||||
template <typename DataType> constexpr bool IsDataTypeEnum = false;
|
||||
|
||||
template <typename DataType> constexpr bool IsDataTypeDecimalOrNumber = IsDataTypeDecimal<DataType> || IsDataTypeNumber<DataType>;
|
||||
@ -576,6 +577,9 @@ template <> inline constexpr bool IsDataTypeDecimal<DataTypeDateTime64> = true;
|
||||
|
||||
template <typename T> constexpr bool IsDataTypeNumber<DataTypeNumber<T>> = true;
|
||||
|
||||
template <> inline constexpr bool IsDataTypeDate<DataTypeDate> = true;
|
||||
template <> inline constexpr bool IsDataTypeDate<DataTypeDate32> = true;
|
||||
|
||||
template <> inline constexpr bool IsDataTypeDateOrDateTime<DataTypeDate> = true;
|
||||
template <> inline constexpr bool IsDataTypeDateOrDateTime<DataTypeDate32> = true;
|
||||
template <> inline constexpr bool IsDataTypeDateOrDateTime<DataTypeDateTime> = true;
|
||||
|
@ -30,6 +30,7 @@ namespace ErrorCodes
|
||||
extern const int TYPE_MISMATCH;
|
||||
extern const int LOGICAL_ERROR;
|
||||
extern const int INCOMPATIBLE_COLUMNS;
|
||||
extern const int NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
size_t getNumberOfDimensions(const IDataType & type)
|
||||
@ -121,7 +122,7 @@ DataTypePtr getDataTypeByColumn(const IColumn & column)
|
||||
return makeNullable(getDataTypeByColumn(column_nullable->getNestedColumn()));
|
||||
|
||||
/// TODO: add more types.
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Cannot get data type of column {}", column.getFamilyName());
|
||||
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Cannot get data type of column {}", column.getFamilyName());
|
||||
}
|
||||
|
||||
template <size_t I, typename Tuple>
|
||||
|
@ -88,7 +88,7 @@ DataTypePtr getNumericType(const TypeIndexSet & types)
|
||||
maximize(max_bits_of_unsigned_integer, 8);
|
||||
else if (type == TypeIndex::UInt16)
|
||||
maximize(max_bits_of_unsigned_integer, 16);
|
||||
else if (type == TypeIndex::UInt32)
|
||||
else if (type == TypeIndex::UInt32 || type == TypeIndex::IPv4)
|
||||
maximize(max_bits_of_unsigned_integer, 32);
|
||||
else if (type == TypeIndex::UInt64)
|
||||
maximize(max_bits_of_unsigned_integer, 64);
|
||||
|
@ -115,9 +115,12 @@ void DDLLoadingDependencyVisitor::visit(const ASTStorage & storage, Data & data)
|
||||
{
|
||||
if (!storage.engine)
|
||||
return;
|
||||
if (storage.engine->name != "Dictionary")
|
||||
return;
|
||||
|
||||
if (storage.engine->name == "Distributed")
|
||||
/// Checks that dict* expression was used as sharding_key and builds dependency between the dictionary and current table.
|
||||
/// Distributed(logs, default, hits[, sharding_key[, policy_name]])
|
||||
extractTableNameFromArgument(*storage.engine, data, 3);
|
||||
else if (storage.engine->name == "Dictionary")
|
||||
extractTableNameFromArgument(*storage.engine, data, 0);
|
||||
}
|
||||
|
||||
@ -131,7 +134,29 @@ void DDLLoadingDependencyVisitor::extractTableNameFromArgument(const ASTFunction
|
||||
QualifiedTableName qualified_name;
|
||||
|
||||
const auto * arg = function.arguments->as<ASTExpressionList>()->children[arg_idx].get();
|
||||
if (const auto * literal = arg->as<ASTLiteral>())
|
||||
|
||||
if (const auto * dict_function = arg->as<ASTFunction>())
|
||||
{
|
||||
if (!functionIsDictGet(dict_function->name))
|
||||
return;
|
||||
|
||||
/// Get the dictionary name from `dict*` function.
|
||||
const auto * literal_arg = dict_function->arguments->as<ASTExpressionList>()->children[0].get();
|
||||
const auto * dictionary_name = literal_arg->as<ASTLiteral>();
|
||||
|
||||
if (!dictionary_name)
|
||||
return;
|
||||
|
||||
if (dictionary_name->value.getType() != Field::Types::String)
|
||||
return;
|
||||
|
||||
auto maybe_qualified_name = QualifiedTableName::tryParseFromString(dictionary_name->value.get<String>());
|
||||
if (!maybe_qualified_name)
|
||||
return;
|
||||
|
||||
qualified_name = std::move(*maybe_qualified_name);
|
||||
}
|
||||
else if (const auto * literal = arg->as<ASTLiteral>())
|
||||
{
|
||||
if (literal->value.getType() != Field::Types::String)
|
||||
return;
|
||||
@ -167,5 +192,4 @@ void DDLLoadingDependencyVisitor::extractTableNameFromArgument(const ASTFunction
|
||||
}
|
||||
data.dependencies.emplace(std::move(qualified_name));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -271,16 +271,18 @@ void RegExpTreeDictionary::initGraph()
|
||||
for (const auto & [id, value]: regex_nodes)
|
||||
if (value->parent_id == 0) // this is root node.
|
||||
initTopologyOrder(id, visited, topology_id);
|
||||
/// If there is a cycle and all nodes have a parent, this condition will be met.
|
||||
if (topology_order.size() != regex_nodes.size())
|
||||
throw Exception(ErrorCodes::INCORRECT_DICTIONARY_DEFINITION, "Invalid Regex tree");
|
||||
throw Exception(ErrorCodes::INCORRECT_DICTIONARY_DEFINITION, "The regexp tree is cyclical. Please check your config.");
|
||||
}
|
||||
|
||||
void RegExpTreeDictionary::initTopologyOrder(UInt64 node_idx, std::set<UInt64> & visited, UInt64 & topology_id)
|
||||
{
|
||||
visited.insert(node_idx);
|
||||
for (UInt64 child_idx : regex_nodes[node_idx]->children)
|
||||
/// there is a cycle when dfs the graph.
|
||||
if (visited.contains(child_idx))
|
||||
throw Exception(ErrorCodes::INCORRECT_DICTIONARY_DEFINITION, "Invalid Regex tree. The input tree is cyclical");
|
||||
throw Exception(ErrorCodes::INCORRECT_DICTIONARY_DEFINITION, "The regexp tree is cyclical. Please check your config.");
|
||||
else
|
||||
initTopologyOrder(child_idx, visited, topology_id);
|
||||
topology_order[node_idx] = topology_id++;
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <Functions/FunctionFactory.h>
|
||||
#include <Common/isLocalAddress.h>
|
||||
#include <Interpreters/Context.h>
|
||||
#include <DataTypes/DataTypeFactory.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
@ -341,7 +342,9 @@ void buildPrimaryKeyConfiguration(
|
||||
AutoPtr<Element> root,
|
||||
bool complex,
|
||||
const Names & key_names,
|
||||
const ASTExpressionList * dictionary_attributes)
|
||||
const ASTExpressionList * dictionary_attributes,
|
||||
const ASTDictionarySettings * dict_settings,
|
||||
ContextPtr context)
|
||||
{
|
||||
const auto & children = dictionary_attributes->children;
|
||||
|
||||
@ -376,6 +379,26 @@ void buildPrimaryKeyConfiguration(
|
||||
|
||||
const ASTDictionaryAttributeDeclaration * dict_attr = (*it)->as<const ASTDictionaryAttributeDeclaration>();
|
||||
|
||||
auto key_type = DataTypeFactory::instance().tryGet(dict_attr->type);
|
||||
|
||||
auto check_dictionary_primary_key = context->getSettingsRef().check_dictionary_primary_key;
|
||||
|
||||
if (dict_settings)
|
||||
{
|
||||
if (const auto * check_dictionary_primary_key_change = dict_settings->changes.tryGet("check_dictionary_primary_key"))
|
||||
{
|
||||
check_dictionary_primary_key = check_dictionary_primary_key_change->get<bool>();
|
||||
}
|
||||
}
|
||||
|
||||
if (check_dictionary_primary_key && !WhichDataType(key_type).isNativeUInt())
|
||||
{
|
||||
throw Exception(ErrorCodes::INCORRECT_DICTIONARY_DEFINITION,
|
||||
"Invalid Primary key type for simple dictionary: {}. Must be native unsigned integer type. "
|
||||
"To avoid checking it, please set check_dictionary_primary_key=false",
|
||||
dict_attr->name);
|
||||
}
|
||||
|
||||
AutoPtr<Text> name(doc->createTextNode(dict_attr->name));
|
||||
name_element->appendChild(name);
|
||||
|
||||
@ -614,7 +637,7 @@ getDictionaryConfigurationFromAST(const ASTCreateQuery & query, ContextPtr conte
|
||||
|
||||
checkPrimaryKey(all_attr_names_and_types, pk_attrs);
|
||||
|
||||
buildPrimaryKeyConfiguration(xml_document, structure_element, complex, pk_attrs, query.dictionary_attributes_list);
|
||||
buildPrimaryKeyConfiguration(xml_document, structure_element, complex, pk_attrs, query.dictionary_attributes_list, query.dictionary->dict_settings, context);
|
||||
|
||||
buildLayoutConfiguration(xml_document, current_dictionary, query.dictionary->dict_settings, dictionary_layout);
|
||||
buildSourceConfiguration(xml_document, current_dictionary, query.dictionary->source, query.dictionary->dict_settings, context);
|
||||
|
@ -5,6 +5,9 @@
|
||||
#include <Columns/ColumnsNumber.h>
|
||||
|
||||
#include <DataTypes/DataTypeArray.h>
|
||||
#include <DataTypes/DataTypeDate.h>
|
||||
#include <DataTypes/DataTypeDate32.h>
|
||||
#include <DataTypes/DataTypeDateTime.h>
|
||||
#include <DataTypes/DataTypeDateTime64.h>
|
||||
#include <DataTypes/DataTypesDecimal.h>
|
||||
#include <DataTypes/DataTypesNumber.h>
|
||||
@ -81,9 +84,10 @@ struct ArrayAggregateResultImpl<ArrayElement, AggregateOperation::sum>
|
||||
std::conditional_t<std::is_same_v<ArrayElement, Decimal64>, Decimal128,
|
||||
std::conditional_t<std::is_same_v<ArrayElement, Decimal128>, Decimal128,
|
||||
std::conditional_t<std::is_same_v<ArrayElement, Decimal256>, Decimal256,
|
||||
std::conditional_t<std::is_same_v<ArrayElement, DateTime64>, Decimal128,
|
||||
std::conditional_t<std::is_floating_point_v<ArrayElement>, Float64,
|
||||
std::conditional_t<std::is_signed_v<ArrayElement>, Int64,
|
||||
UInt64>>>>>>>>>>;
|
||||
UInt64>>>>>>>>>>>;
|
||||
};
|
||||
|
||||
template <typename ArrayElement, AggregateOperation operation>
|
||||
@ -108,6 +112,8 @@ struct ArrayAggregateImpl
|
||||
using Types = std::decay_t<decltype(types)>;
|
||||
using DataType = typename Types::LeftType;
|
||||
|
||||
if constexpr (!IsDataTypeDateOrDateTime<DataType>)
|
||||
{
|
||||
if constexpr (aggregate_operation == AggregateOperation::average || aggregate_operation == AggregateOperation::product)
|
||||
{
|
||||
result = std::make_shared<DataTypeFloat64>();
|
||||
@ -121,7 +127,7 @@ struct ArrayAggregateImpl
|
||||
|
||||
return true;
|
||||
}
|
||||
else if constexpr (IsDataTypeDecimal<DataType> && !IsDataTypeDateOrDateTime<DataType>)
|
||||
else if constexpr (IsDataTypeDecimal<DataType>)
|
||||
{
|
||||
using DecimalReturnType = ArrayAggregateResult<typename DataType::FieldType, aggregate_operation>;
|
||||
UInt32 scale = getDecimalScale(*expression_return);
|
||||
@ -129,6 +135,31 @@ struct ArrayAggregateImpl
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if constexpr (aggregate_operation == AggregateOperation::max || aggregate_operation == AggregateOperation::min)
|
||||
{
|
||||
if constexpr (IsDataTypeDate<DataType>)
|
||||
{
|
||||
result = std::make_shared<DataType>();
|
||||
|
||||
return true;
|
||||
}
|
||||
else if constexpr (!IsDataTypeDecimal<DataType>)
|
||||
{
|
||||
std::string timezone = getDateTimeTimezone(*expression_return);
|
||||
result = std::make_shared<DataTypeDateTime>(timezone);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string timezone = getDateTimeTimezone(*expression_return);
|
||||
UInt32 scale = getDecimalScale(*expression_return);
|
||||
result = std::make_shared<DataTypeDateTime64>(scale, timezone);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
@ -370,7 +401,8 @@ struct ArrayAggregateImpl
|
||||
executeType<Decimal32>(mapped, offsets, res) ||
|
||||
executeType<Decimal64>(mapped, offsets, res) ||
|
||||
executeType<Decimal128>(mapped, offsets, res) ||
|
||||
executeType<Decimal256>(mapped, offsets, res))
|
||||
executeType<Decimal256>(mapped, offsets, res) ||
|
||||
executeType<DateTime64>(mapped, offsets, res))
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
@ -35,10 +35,10 @@ struct ArrayDifferenceImpl
|
||||
if (which.isUInt8() || which.isInt8())
|
||||
return std::make_shared<DataTypeArray>(std::make_shared<DataTypeInt16>());
|
||||
|
||||
if (which.isUInt16() || which.isInt16())
|
||||
if (which.isUInt16() || which.isInt16() || which.isDate())
|
||||
return std::make_shared<DataTypeArray>(std::make_shared<DataTypeInt32>());
|
||||
|
||||
if (which.isUInt32() || which.isUInt64() || which.isInt32() || which.isInt64())
|
||||
if (which.isUInt32() || which.isUInt64() || which.isInt32() || which.isInt64() || which.isDate32() || which.isDateTime())
|
||||
return std::make_shared<DataTypeArray>(std::make_shared<DataTypeInt64>());
|
||||
|
||||
if (which.isFloat32() || which.isFloat64())
|
||||
@ -47,6 +47,14 @@ struct ArrayDifferenceImpl
|
||||
if (which.isDecimal())
|
||||
return std::make_shared<DataTypeArray>(expression_return);
|
||||
|
||||
if (which.isDateTime64())
|
||||
{
|
||||
UInt32 scale = getDecimalScale(*expression_return);
|
||||
UInt32 precision = getDecimalPrecision(*expression_return);
|
||||
|
||||
return std::make_shared<DataTypeArray>(std::make_shared<DataTypeDecimal<Decimal64>>(precision, scale));
|
||||
}
|
||||
|
||||
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "arrayDifference cannot process values of type {}", expression_return->getName());
|
||||
}
|
||||
|
||||
@ -146,7 +154,8 @@ struct ArrayDifferenceImpl
|
||||
executeType<Decimal32, Decimal32>(mapped, array, res) ||
|
||||
executeType<Decimal64, Decimal64>(mapped, array, res) ||
|
||||
executeType<Decimal128, Decimal128>(mapped, array, res) ||
|
||||
executeType<Decimal256, Decimal256>(mapped, array, res))
|
||||
executeType<Decimal256, Decimal256>(mapped, array, res) ||
|
||||
executeType<DateTime64, Decimal64>(mapped, array, res))
|
||||
return res;
|
||||
else
|
||||
throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Unexpected column for arrayDifference: {}", mapped->getName());
|
||||
|
@ -448,6 +448,11 @@ private:
|
||||
REGISTER_FUNCTION(DateDiff)
|
||||
{
|
||||
factory.registerFunction<FunctionDateDiff<true>>({}, FunctionFactory::CaseInsensitive);
|
||||
factory.registerAlias("date_diff", FunctionDateDiff<true>::name);
|
||||
factory.registerAlias("DATE_DIFF", FunctionDateDiff<true>::name);
|
||||
factory.registerAlias("timestampDiff", FunctionDateDiff<true>::name);
|
||||
factory.registerAlias("timestamp_diff", FunctionDateDiff<true>::name);
|
||||
factory.registerAlias("TIMESTAMP_DIFF", FunctionDateDiff<true>::name);
|
||||
}
|
||||
|
||||
REGISTER_FUNCTION(TimeDiff)
|
||||
|
@ -449,6 +449,20 @@ private:
|
||||
}
|
||||
|
||||
size_t mysqlFractionalSecond(char * dest, Time /*source*/, UInt64 fractional_second, UInt32 scale, const DateLUTImpl & /*timezone*/)
|
||||
{
|
||||
if (scale == 0)
|
||||
scale = 6;
|
||||
|
||||
for (Int64 i = scale, value = fractional_second; i > 0; --i)
|
||||
{
|
||||
dest[i - 1] += value % 10;
|
||||
value /= 10;
|
||||
}
|
||||
return scale;
|
||||
}
|
||||
|
||||
/// Same as mysqlFractionalSecond but prints a single zero if the value has no fractional seconds
|
||||
size_t mysqlFractionalSecondSingleZero(char * dest, Time /*source*/, UInt64 fractional_second, UInt32 scale, const DateLUTImpl & /*timezone*/)
|
||||
{
|
||||
if (scale == 0)
|
||||
scale = 1;
|
||||
@ -710,6 +724,7 @@ private:
|
||||
}
|
||||
|
||||
const bool mysql_M_is_month_name;
|
||||
const bool mysql_f_prints_single_zero;
|
||||
|
||||
public:
|
||||
static constexpr auto name = Name::name;
|
||||
@ -718,6 +733,7 @@ public:
|
||||
|
||||
explicit FunctionFormatDateTimeImpl(ContextPtr context)
|
||||
: mysql_M_is_month_name(context->getSettings().formatdatetime_parsedatetime_m_is_month_name)
|
||||
, mysql_f_prints_single_zero(context->getSettings().formatdatetime_f_prints_single_zero)
|
||||
{
|
||||
}
|
||||
|
||||
@ -978,15 +994,15 @@ public:
|
||||
instructions.push_back(std::move(instruction));
|
||||
};
|
||||
|
||||
auto add_extra_shift_or_literal_instruction = [&](size_t amount, std::string_view literal)
|
||||
auto add_extra_shift_or_literal_instruction = [&](std::string_view literal)
|
||||
{
|
||||
if (mysql_with_only_fixed_length_formatters)
|
||||
add_extra_shift(amount);
|
||||
add_extra_shift(literal.size());
|
||||
else
|
||||
add_literal_instruction(literal);
|
||||
};
|
||||
|
||||
auto add_time_instruction = [&]([[maybe_unused]] typename Instruction<T>::FuncMysql && func, [[maybe_unused]] size_t amount, [[maybe_unused]] std::string_view literal)
|
||||
auto add_time_instruction = [&]([[maybe_unused]] typename Instruction<T>::FuncMysql && func, [[maybe_unused]] std::string_view literal)
|
||||
{
|
||||
/// DateTime/DateTime64 --> insert instruction
|
||||
/// Other types cannot provide the requested data --> write out template
|
||||
@ -997,7 +1013,7 @@ public:
|
||||
instructions.push_back(std::move(instruction));
|
||||
}
|
||||
else
|
||||
add_extra_shift_or_literal_instruction(amount, literal);
|
||||
add_extra_shift_or_literal_instruction(literal);
|
||||
};
|
||||
|
||||
Pos pos = format.data();
|
||||
@ -1012,7 +1028,7 @@ public:
|
||||
if (pos < percent_pos)
|
||||
{
|
||||
/// Handle characters before next %
|
||||
add_extra_shift_or_literal_instruction(percent_pos - pos, std::string_view(pos, percent_pos - pos));
|
||||
add_extra_shift_or_literal_instruction(std::string_view(pos, percent_pos - pos));
|
||||
out_template += String(pos, percent_pos - pos);
|
||||
}
|
||||
|
||||
@ -1107,7 +1123,7 @@ public:
|
||||
else
|
||||
{
|
||||
static constexpr std::string_view val = "00";
|
||||
add_time_instruction(&Instruction<T>::mysqlMinute, 2, val);
|
||||
add_time_instruction(&Instruction<T>::mysqlMinute, val);
|
||||
out_template += val;
|
||||
}
|
||||
break;
|
||||
@ -1116,11 +1132,21 @@ public:
|
||||
// Fractional seconds
|
||||
case 'f':
|
||||
{
|
||||
/// If the time data type has no fractional part, then we print '0' as the fractional part.
|
||||
/// If the time data type has no fractional part, we print (default) '000000' or (deprecated) '0' as fractional part.
|
||||
if (mysql_f_prints_single_zero)
|
||||
{
|
||||
Instruction<T> instruction;
|
||||
instruction.setMysqlFunc(&Instruction<T>::mysqlFractionalSecondSingleZero);
|
||||
instructions.push_back(std::move(instruction));
|
||||
out_template += String(scale == 0 ? 1 : scale, '0');
|
||||
}
|
||||
else
|
||||
{
|
||||
Instruction<T> instruction;
|
||||
instruction.setMysqlFunc(&Instruction<T>::mysqlFractionalSecond);
|
||||
instructions.push_back(std::move(instruction));
|
||||
out_template += String(std::max<UInt32>(1, scale), '0');
|
||||
out_template += String(scale == 0 ? 6 : scale, '0');
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1260,7 +1286,7 @@ public:
|
||||
case 'p':
|
||||
{
|
||||
static constexpr std::string_view val = "AM";
|
||||
add_time_instruction(&Instruction<T>::mysqlAMPM, 2, val);
|
||||
add_time_instruction(&Instruction<T>::mysqlAMPM, val);
|
||||
out_template += val;
|
||||
break;
|
||||
}
|
||||
@ -1269,7 +1295,7 @@ public:
|
||||
case 'r':
|
||||
{
|
||||
static constexpr std::string_view val = "12:00 AM";
|
||||
add_time_instruction(&Instruction<T>::mysqlHHMM12, 8, val);
|
||||
add_time_instruction(&Instruction<T>::mysqlHHMM12, val);
|
||||
out_template += val;
|
||||
break;
|
||||
}
|
||||
@ -1278,7 +1304,7 @@ public:
|
||||
case 'R':
|
||||
{
|
||||
static constexpr std::string_view val = "00:00";
|
||||
add_time_instruction(&Instruction<T>::mysqlHHMM24, 5, val);
|
||||
add_time_instruction(&Instruction<T>::mysqlHHMM24, val);
|
||||
out_template += val;
|
||||
break;
|
||||
}
|
||||
@ -1287,7 +1313,7 @@ public:
|
||||
case 's':
|
||||
{
|
||||
static constexpr std::string_view val = "00";
|
||||
add_time_instruction(&Instruction<T>::mysqlSecond, 2, val);
|
||||
add_time_instruction(&Instruction<T>::mysqlSecond, val);
|
||||
out_template += val;
|
||||
break;
|
||||
}
|
||||
@ -1296,7 +1322,7 @@ public:
|
||||
case 'S':
|
||||
{
|
||||
static constexpr std::string_view val = "00";
|
||||
add_time_instruction(&Instruction<T>::mysqlSecond, 2, val);
|
||||
add_time_instruction(&Instruction<T>::mysqlSecond, val);
|
||||
out_template += val;
|
||||
break;
|
||||
}
|
||||
@ -1305,7 +1331,7 @@ public:
|
||||
case 'T':
|
||||
{
|
||||
static constexpr std::string_view val = "00:00:00";
|
||||
add_time_instruction(&Instruction<T>::mysqlISO8601Time, 8, val);
|
||||
add_time_instruction(&Instruction<T>::mysqlISO8601Time, val);
|
||||
out_template += val;
|
||||
break;
|
||||
}
|
||||
@ -1314,7 +1340,7 @@ public:
|
||||
case 'h':
|
||||
{
|
||||
static constexpr std::string_view val = "12";
|
||||
add_time_instruction(&Instruction<T>::mysqlHour12, 2, val);
|
||||
add_time_instruction(&Instruction<T>::mysqlHour12, val);
|
||||
out_template += val;
|
||||
break;
|
||||
}
|
||||
@ -1323,7 +1349,7 @@ public:
|
||||
case 'H':
|
||||
{
|
||||
static constexpr std::string_view val = "00";
|
||||
add_time_instruction(&Instruction<T>::mysqlHour24, 2, val);
|
||||
add_time_instruction(&Instruction<T>::mysqlHour24, val);
|
||||
out_template += val;
|
||||
break;
|
||||
}
|
||||
@ -1332,7 +1358,7 @@ public:
|
||||
case 'i':
|
||||
{
|
||||
static constexpr std::string_view val = "00";
|
||||
add_time_instruction(&Instruction<T>::mysqlMinute, 2, val);
|
||||
add_time_instruction(&Instruction<T>::mysqlMinute, val);
|
||||
out_template += val;
|
||||
break;
|
||||
}
|
||||
@ -1341,7 +1367,7 @@ public:
|
||||
case 'I':
|
||||
{
|
||||
static constexpr std::string_view val = "12";
|
||||
add_time_instruction(&Instruction<T>::mysqlHour12, 2, val);
|
||||
add_time_instruction(&Instruction<T>::mysqlHour12, val);
|
||||
out_template += val;
|
||||
break;
|
||||
}
|
||||
@ -1350,7 +1376,7 @@ public:
|
||||
case 'k':
|
||||
{
|
||||
static constexpr std::string_view val = "00";
|
||||
add_time_instruction(&Instruction<T>::mysqlHour24, 2, val);
|
||||
add_time_instruction(&Instruction<T>::mysqlHour24, val);
|
||||
out_template += val;
|
||||
break;
|
||||
}
|
||||
@ -1359,7 +1385,7 @@ public:
|
||||
case 'l':
|
||||
{
|
||||
static constexpr std::string_view val = "12";
|
||||
add_time_instruction(&Instruction<T>::mysqlHour12, 2, val);
|
||||
add_time_instruction(&Instruction<T>::mysqlHour12, val);
|
||||
out_template += val;
|
||||
break;
|
||||
}
|
||||
@ -1367,7 +1393,7 @@ public:
|
||||
case 't':
|
||||
{
|
||||
static constexpr std::string_view val = "\t";
|
||||
add_extra_shift_or_literal_instruction(1, val);
|
||||
add_extra_shift_or_literal_instruction(val);
|
||||
out_template += val;
|
||||
break;
|
||||
}
|
||||
@ -1375,7 +1401,7 @@ public:
|
||||
case 'n':
|
||||
{
|
||||
static constexpr std::string_view val = "\n";
|
||||
add_extra_shift_or_literal_instruction(1, val);
|
||||
add_extra_shift_or_literal_instruction(val);
|
||||
out_template += val;
|
||||
break;
|
||||
}
|
||||
@ -1384,7 +1410,7 @@ public:
|
||||
case '%':
|
||||
{
|
||||
static constexpr std::string_view val = "%";
|
||||
add_extra_shift_or_literal_instruction(1, val);
|
||||
add_extra_shift_or_literal_instruction(val);
|
||||
out_template += val;
|
||||
break;
|
||||
}
|
||||
@ -1411,7 +1437,7 @@ public:
|
||||
else
|
||||
{
|
||||
/// Handle characters after last %
|
||||
add_extra_shift_or_literal_instruction(end - pos, std::string_view(pos, end - pos));
|
||||
add_extra_shift_or_literal_instruction(std::string_view(pos, end - pos));
|
||||
out_template += String(pos, end - pos);
|
||||
break;
|
||||
}
|
||||
|
@ -723,7 +723,7 @@ namespace
|
||||
if constexpr (need_check_space == NeedCheckSpace::Yes)
|
||||
checkSpace(cur, end, 1, "assertChar requires size >= 1", fragment);
|
||||
|
||||
if (*cur != expected)
|
||||
if (*cur != expected) [[unlikely]]
|
||||
throw Exception(
|
||||
ErrorCodes::CANNOT_PARSE_DATETIME,
|
||||
"Unable to parse fragment {} from {} because char {} is expected but {} provided",
|
||||
@ -736,6 +736,24 @@ namespace
|
||||
return cur;
|
||||
}
|
||||
|
||||
template <NeedCheckSpace need_check_space>
|
||||
static Pos assertNumber(Pos cur, Pos end, const String & fragment)
|
||||
{
|
||||
if constexpr (need_check_space == NeedCheckSpace::Yes)
|
||||
checkSpace(cur, end, 1, "assertChar requires size >= 1", fragment);
|
||||
|
||||
if (*cur < '0' || *cur > '9') [[unlikely]]
|
||||
throw Exception(
|
||||
ErrorCodes::CANNOT_PARSE_DATETIME,
|
||||
"Unable to parse fragment {} from {} because {} is not a number",
|
||||
fragment,
|
||||
std::string_view(cur, end - cur),
|
||||
String(*cur, 1));
|
||||
|
||||
++cur;
|
||||
return cur;
|
||||
}
|
||||
|
||||
static Pos mysqlDayOfWeekTextShort(Pos cur, Pos end, const String & fragment, DateTime & date)
|
||||
{
|
||||
checkSpace(cur, end, 3, "mysqlDayOfWeekTextShort requires size >= 3", fragment);
|
||||
@ -1074,6 +1092,16 @@ namespace
|
||||
return cur;
|
||||
}
|
||||
|
||||
static Pos mysqlMicrosecond(Pos cur, Pos end, const String & fragment, DateTime & /*date*/)
|
||||
{
|
||||
checkSpace(cur, end, 6, "mysqlMicrosecond requires size >= 6", fragment);
|
||||
|
||||
for (size_t i = 0; i < 6; ++i)
|
||||
cur = assertNumber<NeedCheckSpace::No>(cur, end, fragment);
|
||||
|
||||
return cur;
|
||||
}
|
||||
|
||||
static Pos mysqlISO8601Time(Pos cur, Pos end, const String & fragment, DateTime & date)
|
||||
{
|
||||
checkSpace(cur, end, 8, "mysqlISO8601Time requires size >= 8", fragment);
|
||||
@ -1485,6 +1513,10 @@ namespace
|
||||
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlDayOfMonthSpacePadded));
|
||||
break;
|
||||
|
||||
// Fractional seconds
|
||||
case 'f':
|
||||
instructions.emplace_back(ACTION_ARGS(Instruction::mysqlMicrosecond));
|
||||
break;
|
||||
|
||||
// Short YYYY-MM-DD date, equivalent to %Y-%m-%d 2001-08-23
|
||||
case 'F':
|
||||
@ -1637,8 +1669,6 @@ namespace
|
||||
/// Unimplemented
|
||||
|
||||
/// Fractional seconds
|
||||
case 'f':
|
||||
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "format is not supported for fractional seconds");
|
||||
case 'U':
|
||||
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "format is not supported for WEEK (Sun-Sat)");
|
||||
case 'v':
|
||||
|
@ -112,5 +112,8 @@ REGISTER_FUNCTION(Trim)
|
||||
factory.registerFunction<FunctionTrimLeft>();
|
||||
factory.registerFunction<FunctionTrimRight>();
|
||||
factory.registerFunction<FunctionTrimBoth>();
|
||||
factory.registerAlias("ltrim", FunctionTrimLeft::name);
|
||||
factory.registerAlias("rtrim", FunctionTrimRight::name);
|
||||
factory.registerAlias("trim", FunctionTrimBoth::name);
|
||||
}
|
||||
}
|
||||
|
@ -1028,12 +1028,15 @@ inline ReturnType readDateTimeTextImpl(DateTime64 & datetime64, UInt32 scale, Re
|
||||
|
||||
bool is_ok = true;
|
||||
if constexpr (std::is_same_v<ReturnType, void>)
|
||||
datetime64 = DecimalUtils::decimalFromComponents<DateTime64>(components, scale);
|
||||
{
|
||||
datetime64 = DecimalUtils::decimalFromComponents<DateTime64>(components, scale) * negative_multiplier;
|
||||
}
|
||||
else
|
||||
{
|
||||
is_ok = DecimalUtils::tryGetDecimalFromComponents<DateTime64>(components, scale, datetime64);
|
||||
|
||||
if (is_ok)
|
||||
datetime64 *= negative_multiplier;
|
||||
|
||||
}
|
||||
|
||||
return ReturnType(is_ok);
|
||||
}
|
||||
|
@ -2516,8 +2516,21 @@ ActionsDAGPtr ActionsDAG::buildFilterActionsDAG(
|
||||
FindOriginalNodeForOutputName::FindOriginalNodeForOutputName(const ActionsDAGPtr & actions_)
|
||||
:actions(actions_)
|
||||
{
|
||||
for (const auto * node : actions->getOutputs())
|
||||
index.emplace(node->result_name, node);
|
||||
const auto & actions_outputs = actions->getOutputs();
|
||||
for (const auto * output_node : actions_outputs)
|
||||
{
|
||||
/// find input node which refers to the output node
|
||||
/// consider only aliases on the path
|
||||
const auto * node = output_node;
|
||||
while (node && node->type == ActionsDAG::ActionType::ALIAS)
|
||||
{
|
||||
/// alias has only one child
|
||||
chassert(node->children.size() == 1);
|
||||
node = node->children.front();
|
||||
}
|
||||
if (node && node->type == ActionsDAG::ActionType::INPUT)
|
||||
index.emplace(output_node->result_name, node);
|
||||
}
|
||||
}
|
||||
|
||||
const ActionsDAG::Node * FindOriginalNodeForOutputName::find(const String & output_name)
|
||||
@ -2526,17 +2539,36 @@ const ActionsDAG::Node * FindOriginalNodeForOutputName::find(const String & outp
|
||||
if (it == index.end())
|
||||
return nullptr;
|
||||
|
||||
/// find original(non alias) node it refers to
|
||||
const ActionsDAG::Node * node = it->second;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
FindAliasForInputName::FindAliasForInputName(const ActionsDAGPtr & actions_)
|
||||
:actions(actions_)
|
||||
{
|
||||
const auto & actions_outputs = actions->getOutputs();
|
||||
for (const auto * output_node : actions_outputs)
|
||||
{
|
||||
/// find input node which corresponds to alias
|
||||
const auto * node = output_node;
|
||||
while (node && node->type == ActionsDAG::ActionType::ALIAS)
|
||||
{
|
||||
chassert(!node->children.empty());
|
||||
/// alias has only one child
|
||||
chassert(node->children.size() == 1);
|
||||
node = node->children.front();
|
||||
}
|
||||
if (node && node->type != ActionsDAG::ActionType::INPUT)
|
||||
if (node && node->type == ActionsDAG::ActionType::INPUT)
|
||||
/// node can have several aliases but we consider only the first one
|
||||
index.emplace(node->result_name, output_node);
|
||||
}
|
||||
}
|
||||
|
||||
const ActionsDAG::Node * FindAliasForInputName::find(const String & name)
|
||||
{
|
||||
const auto it = index.find(name);
|
||||
if (it == index.end())
|
||||
return nullptr;
|
||||
|
||||
return node;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -417,6 +417,19 @@ private:
|
||||
NameToNodeIndex index;
|
||||
};
|
||||
|
||||
class FindAliasForInputName
|
||||
{
|
||||
using NameToNodeIndex = std::unordered_map<std::string_view, const ActionsDAG::Node *>;
|
||||
|
||||
public:
|
||||
explicit FindAliasForInputName(const ActionsDAGPtr & actions);
|
||||
const ActionsDAG::Node * find(const String & name);
|
||||
|
||||
private:
|
||||
ActionsDAGPtr actions;
|
||||
NameToNodeIndex index;
|
||||
};
|
||||
|
||||
/// This is an ugly way to bypass impossibility to forward declare ActionDAG::Node.
|
||||
struct ActionDAGNodes
|
||||
{
|
||||
|
@ -2316,7 +2316,7 @@ BlocksList Aggregator::prepareBlocksAndFillTwoLevelImpl(
|
||||
|
||||
std::atomic<UInt32> next_bucket_to_merge = 0;
|
||||
|
||||
auto converter = [&](size_t thread_id, ThreadGroupStatusPtr thread_group)
|
||||
auto converter = [&](size_t thread_id, ThreadGroupPtr thread_group)
|
||||
{
|
||||
SCOPE_EXIT_SAFE(
|
||||
if (thread_group)
|
||||
@ -3044,7 +3044,7 @@ void Aggregator::mergeBlocks(BucketToBlocks bucket_to_blocks, AggregatedDataVari
|
||||
|
||||
LOG_TRACE(log, "Merging partially aggregated two-level data.");
|
||||
|
||||
auto merge_bucket = [&bucket_to_blocks, &result, this](Int32 bucket, Arena * aggregates_pool, ThreadGroupStatusPtr thread_group)
|
||||
auto merge_bucket = [&bucket_to_blocks, &result, this](Int32 bucket, Arena * aggregates_pool, ThreadGroupPtr thread_group)
|
||||
{
|
||||
SCOPE_EXIT_SAFE(
|
||||
if (thread_group)
|
||||
|
@ -919,15 +919,14 @@ void Context::setTemporaryStoragePolicy(const String & policy_name, size_t max_s
|
||||
|
||||
void Context::setTemporaryStorageInCache(const String & cache_disk_name, size_t max_size)
|
||||
{
|
||||
auto lock = getLock();
|
||||
|
||||
if (shared->root_temp_data_on_disk)
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Temporary storage is already set");
|
||||
|
||||
auto disk_ptr = getDisk(cache_disk_name);
|
||||
if (!disk_ptr)
|
||||
throw Exception(ErrorCodes::NO_ELEMENTS_IN_CONFIG, "Disk '{}' is not found", cache_disk_name);
|
||||
|
||||
auto lock = getLock();
|
||||
if (shared->root_temp_data_on_disk)
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Temporary storage is already set");
|
||||
|
||||
const auto * disk_object_storage_ptr = dynamic_cast<const DiskObjectStorage *>(disk_ptr.get());
|
||||
if (!disk_object_storage_ptr)
|
||||
throw Exception(ErrorCodes::NO_ELEMENTS_IN_CONFIG, "Disk '{}' does not use cache", cache_disk_name);
|
||||
|
@ -968,7 +968,7 @@ private:
|
||||
}
|
||||
|
||||
/// Does the loading, possibly in the separate thread.
|
||||
void doLoading(const String & name, size_t loading_id, bool forced_to_reload, size_t min_id_to_finish_loading_dependencies_, bool async, ThreadGroupStatusPtr thread_group = {})
|
||||
void doLoading(const String & name, size_t loading_id, bool forced_to_reload, size_t min_id_to_finish_loading_dependencies_, bool async, ThreadGroupPtr thread_group = {})
|
||||
{
|
||||
SCOPE_EXIT_SAFE(
|
||||
if (thread_group)
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include <Storages/StorageMaterializedView.h>
|
||||
#include <Storages/WindowView/StorageWindowView.h>
|
||||
#include <TableFunctions/TableFunctionFactory.h>
|
||||
#include <Common/ThreadStatus.h>
|
||||
#include <Common/checkStackSize.h>
|
||||
|
||||
|
||||
@ -233,8 +234,14 @@ Chain InterpreterInsertQuery::buildChain(
|
||||
ThreadStatusesHolderPtr thread_status_holder,
|
||||
std::atomic_uint64_t * elapsed_counter_ms)
|
||||
{
|
||||
ThreadGroupPtr running_group;
|
||||
if (current_thread)
|
||||
running_group = current_thread->getThreadGroup();
|
||||
if (!running_group)
|
||||
running_group = std::make_shared<ThreadGroup>(getContext());
|
||||
|
||||
auto sample = getSampleBlock(columns, table, metadata_snapshot);
|
||||
return buildChainImpl(table, metadata_snapshot, sample, thread_status_holder, elapsed_counter_ms);
|
||||
return buildChainImpl(table, metadata_snapshot, sample, thread_status_holder, running_group, elapsed_counter_ms);
|
||||
}
|
||||
|
||||
Chain InterpreterInsertQuery::buildChainImpl(
|
||||
@ -242,6 +249,7 @@ Chain InterpreterInsertQuery::buildChainImpl(
|
||||
const StorageMetadataPtr & metadata_snapshot,
|
||||
const Block & query_sample_block,
|
||||
ThreadStatusesHolderPtr thread_status_holder,
|
||||
ThreadGroupPtr running_group,
|
||||
std::atomic_uint64_t * elapsed_counter_ms)
|
||||
{
|
||||
ThreadStatus * thread_status = current_thread;
|
||||
@ -273,7 +281,9 @@ Chain InterpreterInsertQuery::buildChainImpl(
|
||||
}
|
||||
else
|
||||
{
|
||||
out = buildPushingToViewsChain(table, metadata_snapshot, context_ptr, query_ptr, no_destination, thread_status_holder, elapsed_counter_ms);
|
||||
out = buildPushingToViewsChain(table, metadata_snapshot, context_ptr,
|
||||
query_ptr, no_destination,
|
||||
thread_status_holder, running_group, elapsed_counter_ms);
|
||||
}
|
||||
|
||||
/// Note that we wrap transforms one on top of another, so we write them in reverse of data processing order.
|
||||
@ -461,9 +471,17 @@ BlockIO InterpreterInsertQuery::execute()
|
||||
pipeline = interpreter_watch.buildQueryPipeline();
|
||||
}
|
||||
|
||||
ThreadGroupPtr running_group;
|
||||
if (current_thread)
|
||||
running_group = current_thread->getThreadGroup();
|
||||
if (!running_group)
|
||||
running_group = std::make_shared<ThreadGroup>(getContext());
|
||||
for (size_t i = 0; i < out_streams_size; ++i)
|
||||
{
|
||||
auto out = buildChainImpl(table, metadata_snapshot, query_sample_block, nullptr, nullptr);
|
||||
auto out = buildChainImpl(table, metadata_snapshot, query_sample_block,
|
||||
/* thread_status_holder= */ nullptr,
|
||||
running_group,
|
||||
/* elapsed_counter_ms= */ nullptr);
|
||||
out_chains.emplace_back(std::move(out));
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <Interpreters/IInterpreter.h>
|
||||
#include <Parsers/ASTInsertQuery.h>
|
||||
#include <Storages/StorageInMemoryMetadata.h>
|
||||
#include <Common/ThreadStatus.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
@ -70,6 +71,7 @@ private:
|
||||
const StorageMetadataPtr & metadata_snapshot,
|
||||
const Block & query_sample_block,
|
||||
ThreadStatusesHolderPtr thread_status_holder,
|
||||
ThreadGroupPtr running_group,
|
||||
std::atomic_uint64_t * elapsed_counter_ms);
|
||||
};
|
||||
|
||||
|
@ -340,7 +340,7 @@ QueryStatus::QueryStatus(
|
||||
const String & query_,
|
||||
const ClientInfo & client_info_,
|
||||
QueryPriorities::Handle && priority_handle_,
|
||||
ThreadGroupStatusPtr && thread_group_,
|
||||
ThreadGroupPtr && thread_group_,
|
||||
IAST::QueryKind query_kind_,
|
||||
UInt64 watch_start_nanoseconds)
|
||||
: WithContext(context_)
|
||||
|
@ -86,7 +86,7 @@ protected:
|
||||
ClientInfo client_info;
|
||||
|
||||
/// Info about all threads involved in query execution
|
||||
ThreadGroupStatusPtr thread_group;
|
||||
ThreadGroupPtr thread_group;
|
||||
|
||||
Stopwatch watch;
|
||||
|
||||
@ -162,7 +162,7 @@ public:
|
||||
const String & query_,
|
||||
const ClientInfo & client_info_,
|
||||
QueryPriorities::Handle && priority_handle_,
|
||||
ThreadGroupStatusPtr && thread_group_,
|
||||
ThreadGroupPtr && thread_group_,
|
||||
IAST::QueryKind query_kind_,
|
||||
UInt64 watch_start_nanoseconds);
|
||||
|
||||
|
@ -42,14 +42,14 @@ namespace ErrorCodes
|
||||
extern const int CANNOT_SET_THREAD_PRIORITY;
|
||||
}
|
||||
|
||||
ThreadGroupStatus::ThreadGroupStatus(ContextPtr query_context_, FatalErrorCallback fatal_error_callback_)
|
||||
ThreadGroup::ThreadGroup(ContextPtr query_context_, FatalErrorCallback fatal_error_callback_)
|
||||
: master_thread_id(CurrentThread::get().thread_id)
|
||||
, query_context(query_context_)
|
||||
, global_context(query_context_->getGlobalContext())
|
||||
, fatal_error_callback(fatal_error_callback_)
|
||||
{}
|
||||
|
||||
std::vector<UInt64> ThreadGroupStatus::getInvolvedThreadIds() const
|
||||
std::vector<UInt64> ThreadGroup::getInvolvedThreadIds() const
|
||||
{
|
||||
std::vector<UInt64> res;
|
||||
|
||||
@ -61,22 +61,22 @@ std::vector<UInt64> ThreadGroupStatus::getInvolvedThreadIds() const
|
||||
return res;
|
||||
}
|
||||
|
||||
void ThreadGroupStatus::linkThread(UInt64 thread_it)
|
||||
void ThreadGroup::linkThread(UInt64 thread_it)
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
thread_ids.insert(thread_it);
|
||||
}
|
||||
|
||||
ThreadGroupStatusPtr ThreadGroupStatus::createForQuery(ContextPtr query_context_, std::function<void()> fatal_error_callback_)
|
||||
ThreadGroupPtr ThreadGroup::createForQuery(ContextPtr query_context_, std::function<void()> fatal_error_callback_)
|
||||
{
|
||||
auto group = std::make_shared<ThreadGroupStatus>(query_context_, std::move(fatal_error_callback_));
|
||||
auto group = std::make_shared<ThreadGroup>(query_context_, std::move(fatal_error_callback_));
|
||||
group->memory_tracker.setDescription("(for query)");
|
||||
return group;
|
||||
}
|
||||
|
||||
ThreadGroupStatusPtr ThreadGroupStatus::createForBackgroundProcess(ContextPtr storage_context)
|
||||
ThreadGroupPtr ThreadGroup::createForBackgroundProcess(ContextPtr storage_context)
|
||||
{
|
||||
auto group = std::make_shared<ThreadGroupStatus>(storage_context);
|
||||
auto group = std::make_shared<ThreadGroup>(storage_context);
|
||||
|
||||
group->memory_tracker.setDescription("background process to apply mutate/merge in table");
|
||||
/// However settings from storage context have to be applied
|
||||
@ -90,7 +90,7 @@ ThreadGroupStatusPtr ThreadGroupStatus::createForBackgroundProcess(ContextPtr st
|
||||
return group;
|
||||
}
|
||||
|
||||
void ThreadGroupStatus::attachQueryForLog(const String & query_, UInt64 normalized_hash)
|
||||
void ThreadGroup::attachQueryForLog(const String & query_, UInt64 normalized_hash)
|
||||
{
|
||||
auto hash = normalized_hash ? normalized_hash : normalizedQueryHash<false>(query_);
|
||||
|
||||
@ -110,12 +110,30 @@ void ThreadStatus::attachQueryForLog(const String & query_)
|
||||
thread_group->attachQueryForLog(local_data.query_for_logs, local_data.normalized_query_hash);
|
||||
}
|
||||
|
||||
void ThreadGroupStatus::attachInternalProfileEventsQueue(const InternalProfileEventsQueuePtr & profile_queue)
|
||||
void ThreadGroup::attachInternalProfileEventsQueue(const InternalProfileEventsQueuePtr & profile_queue)
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
shared_data.profile_queue_ptr = profile_queue;
|
||||
}
|
||||
|
||||
ThreadGroupSwitcher::ThreadGroupSwitcher(ThreadGroupPtr thread_group)
|
||||
{
|
||||
chassert(thread_group);
|
||||
|
||||
/// might be nullptr
|
||||
prev_thread_group = CurrentThread::getGroup();
|
||||
|
||||
CurrentThread::detachFromGroupIfNotDetached();
|
||||
CurrentThread::attachToGroup(thread_group);
|
||||
}
|
||||
|
||||
ThreadGroupSwitcher::~ThreadGroupSwitcher()
|
||||
{
|
||||
CurrentThread::detachFromGroupIfNotDetached();
|
||||
if (prev_thread_group)
|
||||
CurrentThread::attachToGroup(prev_thread_group);
|
||||
}
|
||||
|
||||
void ThreadStatus::attachInternalProfileEventsQueue(const InternalProfileEventsQueuePtr & profile_queue)
|
||||
{
|
||||
if (!thread_group)
|
||||
@ -169,7 +187,7 @@ void ThreadStatus::applyQuerySettings()
|
||||
#endif
|
||||
}
|
||||
|
||||
void ThreadStatus::attachToGroupImpl(const ThreadGroupStatusPtr & thread_group_)
|
||||
void ThreadStatus::attachToGroupImpl(const ThreadGroupPtr & thread_group_)
|
||||
{
|
||||
/// Attach or init current thread to thread group and copy useful information from it
|
||||
thread_group = thread_group_;
|
||||
@ -235,7 +253,7 @@ void ThreadStatus::setInternalThread()
|
||||
internal_thread = true;
|
||||
}
|
||||
|
||||
void ThreadStatus::attachToGroup(const ThreadGroupStatusPtr & thread_group_, bool check_detached)
|
||||
void ThreadStatus::attachToGroup(const ThreadGroupPtr & thread_group_, bool check_detached)
|
||||
{
|
||||
if (thread_group && check_detached)
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Can't attach query to the thread, it is already attached");
|
||||
@ -542,14 +560,14 @@ void ThreadStatus::logToQueryViewsLog(const ViewRuntimeData & vinfo)
|
||||
views_log->add(element);
|
||||
}
|
||||
|
||||
void CurrentThread::attachToGroup(const ThreadGroupStatusPtr & thread_group)
|
||||
void CurrentThread::attachToGroup(const ThreadGroupPtr & thread_group)
|
||||
{
|
||||
if (unlikely(!current_thread))
|
||||
return;
|
||||
current_thread->attachToGroup(thread_group, true);
|
||||
}
|
||||
|
||||
void CurrentThread::attachToGroupIfDetached(const ThreadGroupStatusPtr & thread_group)
|
||||
void CurrentThread::attachToGroupIfDetached(const ThreadGroupPtr & thread_group)
|
||||
{
|
||||
if (unlikely(!current_thread))
|
||||
return;
|
||||
@ -575,7 +593,7 @@ CurrentThread::QueryScope::QueryScope(ContextMutablePtr query_context, std::func
|
||||
if (!query_context->hasQueryContext())
|
||||
query_context->makeQueryContext();
|
||||
|
||||
auto group = ThreadGroupStatus::createForQuery(query_context, std::move(fatal_error_callback));
|
||||
auto group = ThreadGroup::createForQuery(query_context, std::move(fatal_error_callback));
|
||||
CurrentThread::attachToGroup(group);
|
||||
}
|
||||
|
||||
@ -585,7 +603,7 @@ CurrentThread::QueryScope::QueryScope(ContextPtr query_context, std::function<vo
|
||||
throw Exception(
|
||||
ErrorCodes::LOGICAL_ERROR, "Cannot initialize query scope without query context");
|
||||
|
||||
auto group = ThreadGroupStatus::createForQuery(query_context, std::move(fatal_error_callback));
|
||||
auto group = ThreadGroup::createForQuery(query_context, std::move(fatal_error_callback));
|
||||
CurrentThread::attachToGroup(group);
|
||||
}
|
||||
|
||||
|
@ -437,7 +437,7 @@ Chunk DDLQueryStatusSource::generate()
|
||||
|
||||
{
|
||||
auto retries_info = getRetriesInfo();
|
||||
auto retries_ctl = ZooKeeperRetriesControl("executeDDLQueryOnCluster", retries_info);
|
||||
auto retries_ctl = ZooKeeperRetriesControl("executeDDLQueryOnCluster", retries_info, context->getProcessListElement());
|
||||
retries_ctl.retryLoop([&]()
|
||||
{
|
||||
auto zookeeper = context->getZooKeeper();
|
||||
@ -477,7 +477,7 @@ Chunk DDLQueryStatusSource::generate()
|
||||
bool finished_exists = false;
|
||||
|
||||
auto retries_info = getRetriesInfo();
|
||||
auto retries_ctl = ZooKeeperRetriesControl("executeDDLQueryOnCluster", retries_info);
|
||||
auto retries_ctl = ZooKeeperRetriesControl("executeDDLQueryOnCluster", retries_info, context->getProcessListElement());
|
||||
retries_ctl.retryLoop([&]()
|
||||
{
|
||||
finished_exists = context->getZooKeeper()->tryGet(fs::path(node_path) / "finished" / host_id, status_data);
|
||||
|
@ -303,9 +303,9 @@ ASTPtr makeBetweenOperator(bool negative, ASTs arguments)
|
||||
}
|
||||
}
|
||||
|
||||
ParserExpressionWithOptionalAlias::ParserExpressionWithOptionalAlias(bool allow_alias_without_as_keyword, bool is_table_function)
|
||||
ParserExpressionWithOptionalAlias::ParserExpressionWithOptionalAlias(bool allow_alias_without_as_keyword, bool is_table_function, bool allow_trailing_commas)
|
||||
: impl(std::make_unique<ParserWithOptionalAlias>(
|
||||
is_table_function ? ParserPtr(std::make_unique<ParserTableFunctionExpression>()) : ParserPtr(std::make_unique<ParserExpression>()),
|
||||
is_table_function ? ParserPtr(std::make_unique<ParserTableFunctionExpression>()) : ParserPtr(std::make_unique<ParserExpression>(allow_trailing_commas)),
|
||||
allow_alias_without_as_keyword))
|
||||
{
|
||||
}
|
||||
@ -314,7 +314,7 @@ ParserExpressionWithOptionalAlias::ParserExpressionWithOptionalAlias(bool allow_
|
||||
bool ParserExpressionList::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
{
|
||||
return ParserList(
|
||||
std::make_unique<ParserExpressionWithOptionalAlias>(allow_alias_without_as_keyword, is_table_function),
|
||||
std::make_unique<ParserExpressionWithOptionalAlias>(allow_alias_without_as_keyword, is_table_function, allow_trailing_commas),
|
||||
std::make_unique<ParserToken>(TokenType::Comma))
|
||||
.parse(pos, node, expected);
|
||||
}
|
||||
@ -779,13 +779,50 @@ protected:
|
||||
};
|
||||
|
||||
|
||||
struct ParserExpressionImpl
|
||||
{
|
||||
static std::vector<std::pair<const char *, Operator>> operators_table;
|
||||
static std::vector<std::pair<const char *, Operator>> unary_operators_table;
|
||||
static const char * overlapping_operators_to_skip[];
|
||||
|
||||
static Operator finish_between_operator;
|
||||
|
||||
ParserCompoundIdentifier identifier_parser{false, true};
|
||||
ParserNumber number_parser;
|
||||
ParserAsterisk asterisk_parser;
|
||||
ParserLiteral literal_parser;
|
||||
ParserTupleOfLiterals tuple_literal_parser;
|
||||
ParserArrayOfLiterals array_literal_parser;
|
||||
ParserSubstitution substitution_parser;
|
||||
ParserMySQLGlobalVariable mysql_global_variable_parser;
|
||||
|
||||
ParserKeyword any_parser{"ANY"};
|
||||
ParserKeyword all_parser{"ALL"};
|
||||
|
||||
// Recursion
|
||||
ParserQualifiedAsterisk qualified_asterisk_parser;
|
||||
ParserColumnsMatcher columns_matcher_parser;
|
||||
ParserQualifiedColumnsMatcher qualified_columns_matcher_parser;
|
||||
ParserSubquery subquery_parser;
|
||||
|
||||
bool parse(std::unique_ptr<Layer> start, IParser::Pos & pos, ASTPtr & node, Expected & expected);
|
||||
|
||||
using Layers = std::vector<std::unique_ptr<Layer>>;
|
||||
|
||||
Action tryParseOperand(Layers & layers, IParser::Pos & pos, Expected & expected);
|
||||
Action tryParseOperator(Layers & layers, IParser::Pos & pos, Expected & expected);
|
||||
};
|
||||
|
||||
|
||||
class ExpressionLayer : public Layer
|
||||
{
|
||||
public:
|
||||
|
||||
explicit ExpressionLayer(bool is_table_function_) : Layer(false, false)
|
||||
explicit ExpressionLayer(bool is_table_function_, bool allow_trailing_commas_ = false)
|
||||
: Layer(false, false)
|
||||
{
|
||||
is_table_function = is_table_function_;
|
||||
allow_trailing_commas = allow_trailing_commas_;
|
||||
}
|
||||
|
||||
bool getResult(ASTPtr & node) override
|
||||
@ -802,10 +839,62 @@ public:
|
||||
bool parse(IParser::Pos & pos, Expected & /*expected*/, Action & /*action*/) override
|
||||
{
|
||||
if (pos->type == TokenType::Comma)
|
||||
{
|
||||
finished = true;
|
||||
|
||||
if (!allow_trailing_commas)
|
||||
return true;
|
||||
|
||||
/// We support trailing commas at the end of the column declaration:
|
||||
/// - SELECT a, b, c, FROM table
|
||||
/// - SELECT 1,
|
||||
|
||||
/// For this purpose we need to eliminate the following cases:
|
||||
/// 1. WITH 1 AS from SELECT 2, from
|
||||
/// 2. SELECT to, from FROM table
|
||||
/// 3. SELECT to, from AS alias FROM table
|
||||
/// 4. SELECT to, from + to, from IN [1,2,3], FROM table
|
||||
|
||||
Expected test_expected;
|
||||
auto test_pos = pos;
|
||||
++test_pos;
|
||||
|
||||
/// End of query
|
||||
if (test_pos.isValid() && test_pos->type != TokenType::Semicolon)
|
||||
{
|
||||
/// If we can't parse FROM then return
|
||||
if (!ParserKeyword("FROM").ignore(test_pos, test_expected))
|
||||
return true;
|
||||
|
||||
/// If we parse a second FROM then the first one was a name of a column
|
||||
if (ParserKeyword("FROM").ignore(test_pos, test_expected))
|
||||
return true;
|
||||
|
||||
/// If we parse an explicit alias to FROM, then it was a name of a column
|
||||
if (ParserAlias(false).ignore(test_pos, test_expected))
|
||||
return true;
|
||||
|
||||
/// If we parse an operator after FROM then it was a name of a column
|
||||
auto cur_op = ParserExpressionImpl::operators_table.begin();
|
||||
for (; cur_op != ParserExpressionImpl::operators_table.end(); ++cur_op)
|
||||
{
|
||||
if (parseOperator(test_pos, cur_op->first, test_expected))
|
||||
break;
|
||||
}
|
||||
|
||||
if (cur_op != ParserExpressionImpl::operators_table.end())
|
||||
return true;
|
||||
}
|
||||
|
||||
++pos;
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool allow_trailing_commas;
|
||||
};
|
||||
|
||||
/// Basic layer for a function with certain separator and end tokens:
|
||||
@ -2164,44 +2253,10 @@ bool ParseTimestampOperatorExpression(IParser::Pos & pos, ASTPtr & node, Expecte
|
||||
return true;
|
||||
}
|
||||
|
||||
struct ParserExpressionImpl
|
||||
{
|
||||
static std::vector<std::pair<const char *, Operator>> operators_table;
|
||||
static std::vector<std::pair<const char *, Operator>> unary_operators_table;
|
||||
static const char * overlapping_operators_to_skip[];
|
||||
|
||||
static Operator finish_between_operator;
|
||||
|
||||
ParserCompoundIdentifier identifier_parser{false, true};
|
||||
ParserNumber number_parser;
|
||||
ParserAsterisk asterisk_parser;
|
||||
ParserLiteral literal_parser;
|
||||
ParserTupleOfLiterals tuple_literal_parser;
|
||||
ParserArrayOfLiterals array_literal_parser;
|
||||
ParserSubstitution substitution_parser;
|
||||
ParserMySQLGlobalVariable mysql_global_variable_parser;
|
||||
|
||||
ParserKeyword any_parser{"ANY"};
|
||||
ParserKeyword all_parser{"ALL"};
|
||||
|
||||
// Recursion
|
||||
ParserQualifiedAsterisk qualified_asterisk_parser;
|
||||
ParserColumnsMatcher columns_matcher_parser;
|
||||
ParserQualifiedColumnsMatcher qualified_columns_matcher_parser;
|
||||
ParserSubquery subquery_parser;
|
||||
|
||||
bool parse(std::unique_ptr<Layer> start, IParser::Pos & pos, ASTPtr & node, Expected & expected);
|
||||
|
||||
using Layers = std::vector<std::unique_ptr<Layer>>;
|
||||
|
||||
Action tryParseOperand(Layers & layers, IParser::Pos & pos, Expected & expected);
|
||||
Action tryParseOperator(Layers & layers, IParser::Pos & pos, Expected & expected);
|
||||
};
|
||||
|
||||
|
||||
bool ParserExpression::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
{
|
||||
auto start = std::make_unique<ExpressionLayer>(false);
|
||||
auto start = std::make_unique<ExpressionLayer>(false, allow_trailing_commas);
|
||||
return ParserExpressionImpl().parse(std::move(start), pos, node, expected);
|
||||
}
|
||||
|
||||
@ -2544,19 +2599,18 @@ Action ParserExpressionImpl::tryParseOperator(Layers & layers, IParser::Pos & po
|
||||
|
||||
if (cur_op == operators_table.end())
|
||||
{
|
||||
if (!layers.back()->allow_alias || layers.back()->parsed_alias)
|
||||
return Action::NONE;
|
||||
|
||||
ASTPtr alias;
|
||||
ParserAlias alias_parser(layers.back()->allow_alias_without_as_keyword);
|
||||
|
||||
if (layers.back()->allow_alias &&
|
||||
!layers.back()->parsed_alias &&
|
||||
alias_parser.parse(pos, alias, expected) &&
|
||||
layers.back()->insertAlias(alias))
|
||||
{
|
||||
if (!alias_parser.parse(pos, alias, expected) || !layers.back()->insertAlias(alias))
|
||||
return Action::NONE;
|
||||
|
||||
layers.back()->parsed_alias = true;
|
||||
return Action::OPERATOR;
|
||||
}
|
||||
return Action::NONE;
|
||||
}
|
||||
|
||||
auto op = cur_op->second;
|
||||
|
||||
|
@ -172,10 +172,15 @@ protected:
|
||||
|
||||
class ParserExpression : public IParserBase
|
||||
{
|
||||
public:
|
||||
ParserExpression(bool allow_trailing_commas_ = false) : allow_trailing_commas(allow_trailing_commas_) {}
|
||||
|
||||
protected:
|
||||
const char * getName() const override { return "lambda expression"; }
|
||||
|
||||
bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override;
|
||||
|
||||
bool allow_trailing_commas;
|
||||
};
|
||||
|
||||
|
||||
@ -192,7 +197,7 @@ protected:
|
||||
class ParserExpressionWithOptionalAlias : public IParserBase
|
||||
{
|
||||
public:
|
||||
explicit ParserExpressionWithOptionalAlias(bool allow_alias_without_as_keyword_, bool is_table_function_ = false);
|
||||
explicit ParserExpressionWithOptionalAlias(bool allow_alias_without_as_keyword_, bool is_table_function_ = false, bool allow_trailing_commas_ = false);
|
||||
protected:
|
||||
ParserPtr impl;
|
||||
|
||||
@ -209,12 +214,15 @@ protected:
|
||||
class ParserExpressionList : public IParserBase
|
||||
{
|
||||
public:
|
||||
explicit ParserExpressionList(bool allow_alias_without_as_keyword_, bool is_table_function_ = false)
|
||||
: allow_alias_without_as_keyword(allow_alias_without_as_keyword_), is_table_function(is_table_function_) {}
|
||||
explicit ParserExpressionList(bool allow_alias_without_as_keyword_, bool is_table_function_ = false, bool allow_trailing_commas_ = false)
|
||||
: allow_alias_without_as_keyword(allow_alias_without_as_keyword_)
|
||||
, is_table_function(is_table_function_)
|
||||
, allow_trailing_commas(allow_trailing_commas_) {}
|
||||
|
||||
protected:
|
||||
bool allow_alias_without_as_keyword;
|
||||
bool is_table_function; // This expression list is used by a table function
|
||||
bool allow_trailing_commas;
|
||||
|
||||
const char * getName() const override { return "list of expressions"; }
|
||||
bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override;
|
||||
@ -224,8 +232,8 @@ protected:
|
||||
class ParserNotEmptyExpressionList : public IParserBase
|
||||
{
|
||||
public:
|
||||
explicit ParserNotEmptyExpressionList(bool allow_alias_without_as_keyword)
|
||||
: nested_parser(allow_alias_without_as_keyword) {}
|
||||
explicit ParserNotEmptyExpressionList(bool allow_alias_without_as_keyword_, bool allow_trailing_commas_ = false)
|
||||
: nested_parser(allow_alias_without_as_keyword_, false, allow_trailing_commas_) {}
|
||||
private:
|
||||
ParserExpressionList nested_parser;
|
||||
protected:
|
||||
|
@ -68,7 +68,7 @@ bool ParserSelectQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||
|
||||
ParserNotEmptyExpressionList exp_list(false);
|
||||
ParserNotEmptyExpressionList exp_list_for_with_clause(false);
|
||||
ParserNotEmptyExpressionList exp_list_for_select_clause(true); /// Allows aliases without AS keyword.
|
||||
ParserNotEmptyExpressionList exp_list_for_select_clause(/*allow_alias_without_as_keyword*/ true, /*allow_trailing_commas*/ true);
|
||||
ParserExpressionWithOptionalAlias exp_elem(false);
|
||||
ParserOrderByExpressionList order_list;
|
||||
ParserGroupingSetsExpressionList grouping_sets_list;
|
||||
|
@ -225,7 +225,6 @@ bool ParserTablesInSelectQueryElement::parseImpl(Pos & pos, ASTPtr & node, Expec
|
||||
}
|
||||
else if (ParserKeyword("ON").ignore(pos, expected))
|
||||
{
|
||||
/// OR is operator with lowest priority, so start parsing from it.
|
||||
if (!ParserExpression().parse(pos, table_join->on_expression, expected))
|
||||
return false;
|
||||
}
|
||||
|
@ -34,8 +34,11 @@
|
||||
#include <QueryPipeline/QueryPipelineBuilder.h>
|
||||
|
||||
#include <Interpreters/Context.h>
|
||||
#include <Interpreters/StorageID.h>
|
||||
|
||||
#include <Storages/ColumnsDescription.h>
|
||||
#include <Storages/SelectQueryInfo.h>
|
||||
#include <Storages/StorageDummy.h>
|
||||
#include <Storages/IStorage.h>
|
||||
|
||||
#include <Analyzer/Utils.h>
|
||||
@ -912,6 +915,46 @@ void addBuildSubqueriesForSetsStepIfNeeded(QueryPlan & query_plan,
|
||||
addCreatingSetsStep(query_plan, std::move(subqueries_for_sets), planner_context->getQueryContext());
|
||||
}
|
||||
|
||||
/// Support for `additional_result_filter` setting
|
||||
void addAdditionalFilterStepIfNeeded(QueryPlan & query_plan,
|
||||
const QueryNode & query_node,
|
||||
const SelectQueryOptions & select_query_options,
|
||||
PlannerContextPtr & planner_context
|
||||
)
|
||||
{
|
||||
if (select_query_options.subquery_depth != 0)
|
||||
return;
|
||||
|
||||
const auto & query_context = planner_context->getQueryContext();
|
||||
const auto & settings = query_context->getSettingsRef();
|
||||
|
||||
auto additional_result_filter_ast = parseAdditionalResultFilter(settings);
|
||||
if (!additional_result_filter_ast)
|
||||
return;
|
||||
|
||||
ColumnsDescription fake_column_descriptions;
|
||||
NameSet fake_name_set;
|
||||
for (const auto & column : query_node.getProjectionColumns())
|
||||
{
|
||||
fake_column_descriptions.add(ColumnDescription(column.name, column.type));
|
||||
fake_name_set.emplace(column.name);
|
||||
}
|
||||
|
||||
auto storage = std::make_shared<StorageDummy>(StorageID{"dummy", "dummy"}, fake_column_descriptions);
|
||||
auto fake_table_expression = std::make_shared<TableNode>(std::move(storage), query_context);
|
||||
|
||||
auto filter_info = buildFilterInfo(additional_result_filter_ast, fake_table_expression, planner_context, std::move(fake_name_set));
|
||||
if (!filter_info.actions || !query_plan.isInitialized())
|
||||
return;
|
||||
|
||||
auto filter_step = std::make_unique<FilterStep>(query_plan.getCurrentDataStream(),
|
||||
filter_info.actions,
|
||||
filter_info.column_name,
|
||||
filter_info.do_remove_column);
|
||||
filter_step->setStepDescription("additional result filter");
|
||||
query_plan.addStep(std::move(filter_step));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
PlannerContextPtr buildPlannerContext(const QueryTreeNodePtr & query_tree_node,
|
||||
@ -1410,6 +1453,9 @@ void Planner::buildPlanForQueryNode()
|
||||
const auto & projection_analysis_result = expression_analysis_result.getProjection();
|
||||
addExpressionStep(query_plan, projection_analysis_result.project_names_actions, "Project names", result_actions_to_execute);
|
||||
}
|
||||
|
||||
// For additional_result_filter setting
|
||||
addAdditionalFilterStepIfNeeded(query_plan, query_node, select_query_options, planner_context);
|
||||
}
|
||||
|
||||
if (!select_query_options.only_analyze)
|
||||
|
@ -33,6 +33,9 @@
|
||||
#include <Analyzer/Passes/QueryAnalysisPass.h>
|
||||
#include <Analyzer/QueryTreeBuilder.h>
|
||||
|
||||
#include <Parsers/ExpressionListParsers.h>
|
||||
#include <Parsers/parseQuery.h>
|
||||
|
||||
#include <Processors/Sources/NullSource.h>
|
||||
#include <Processors/QueryPlan/SortingStep.h>
|
||||
#include <Processors/QueryPlan/CreateSetAndFilterOnTheFlyStep.h>
|
||||
@ -383,46 +386,6 @@ void updatePrewhereOutputsIfNeeded(SelectQueryInfo & table_expression_query_info
|
||||
prewhere_outputs.insert(prewhere_outputs.end(), required_output_nodes.begin(), required_output_nodes.end());
|
||||
}
|
||||
|
||||
FilterDAGInfo buildFilterInfo(ASTPtr filter_expression,
|
||||
SelectQueryInfo & table_expression_query_info,
|
||||
PlannerContextPtr & planner_context)
|
||||
{
|
||||
const auto & query_context = planner_context->getQueryContext();
|
||||
|
||||
auto filter_query_tree = buildQueryTree(filter_expression, query_context);
|
||||
|
||||
QueryAnalysisPass query_analysis_pass(table_expression_query_info.table_expression);
|
||||
query_analysis_pass.run(filter_query_tree, query_context);
|
||||
|
||||
auto & table_expression_data = planner_context->getTableExpressionDataOrThrow(table_expression_query_info.table_expression);
|
||||
const auto table_expression_names = table_expression_data.getColumnNames();
|
||||
NameSet table_expression_required_names_without_filter(table_expression_names.begin(), table_expression_names.end());
|
||||
|
||||
collectSourceColumns(filter_query_tree, planner_context);
|
||||
collectSets(filter_query_tree, *planner_context);
|
||||
|
||||
auto filter_actions_dag = std::make_shared<ActionsDAG>();
|
||||
|
||||
PlannerActionsVisitor actions_visitor(planner_context, false /*use_column_identifier_as_action_node_name*/);
|
||||
auto expression_nodes = actions_visitor.visit(filter_actions_dag, filter_query_tree);
|
||||
if (expression_nodes.size() != 1)
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS,
|
||||
"Filter actions must return single output node. Actual {}",
|
||||
expression_nodes.size());
|
||||
|
||||
auto & filter_actions_outputs = filter_actions_dag->getOutputs();
|
||||
filter_actions_outputs = std::move(expression_nodes);
|
||||
|
||||
std::string filter_node_name = filter_actions_outputs[0]->result_name;
|
||||
bool remove_filter_column = true;
|
||||
|
||||
for (const auto & filter_input_node : filter_actions_dag->getInputs())
|
||||
if (table_expression_required_names_without_filter.contains(filter_input_node->result_name))
|
||||
filter_actions_outputs.push_back(filter_input_node);
|
||||
|
||||
return {std::move(filter_actions_dag), std::move(filter_node_name), remove_filter_column};
|
||||
}
|
||||
|
||||
FilterDAGInfo buildRowPolicyFilterIfNeeded(const StoragePtr & storage,
|
||||
SelectQueryInfo & table_expression_query_info,
|
||||
PlannerContextPtr & planner_context)
|
||||
@ -434,7 +397,7 @@ FilterDAGInfo buildRowPolicyFilterIfNeeded(const StoragePtr & storage,
|
||||
if (!row_policy_filter)
|
||||
return {};
|
||||
|
||||
return buildFilterInfo(row_policy_filter->expression, table_expression_query_info, planner_context);
|
||||
return buildFilterInfo(row_policy_filter->expression, table_expression_query_info.table_expression, planner_context);
|
||||
}
|
||||
|
||||
FilterDAGInfo buildCustomKeyFilterIfNeeded(const StoragePtr & storage,
|
||||
@ -465,7 +428,48 @@ FilterDAGInfo buildCustomKeyFilterIfNeeded(const StoragePtr & storage,
|
||||
*storage,
|
||||
query_context);
|
||||
|
||||
return buildFilterInfo(parallel_replicas_custom_filter_ast, table_expression_query_info, planner_context);
|
||||
return buildFilterInfo(parallel_replicas_custom_filter_ast, table_expression_query_info.table_expression, planner_context);
|
||||
}
|
||||
|
||||
/// Apply filters from additional_table_filters setting
|
||||
FilterDAGInfo buildAdditionalFiltersIfNeeded(const StoragePtr & storage,
|
||||
const String & table_expression_alias,
|
||||
SelectQueryInfo & table_expression_query_info,
|
||||
PlannerContextPtr & planner_context)
|
||||
{
|
||||
const auto & query_context = planner_context->getQueryContext();
|
||||
const auto & settings = query_context->getSettingsRef();
|
||||
|
||||
auto const & additional_filters = settings.additional_table_filters.value;
|
||||
if (additional_filters.empty())
|
||||
return {};
|
||||
|
||||
auto const & storage_id = storage->getStorageID();
|
||||
|
||||
ASTPtr additional_filter_ast;
|
||||
for (size_t i = 0; i < additional_filters.size(); ++i)
|
||||
{
|
||||
const auto & tuple = additional_filters[i].safeGet<const Tuple &>();
|
||||
auto const & table = tuple.at(0).safeGet<String>();
|
||||
auto const & filter = tuple.at(1).safeGet<String>();
|
||||
|
||||
if (table == table_expression_alias ||
|
||||
(table == storage_id.getTableName() && query_context->getCurrentDatabase() == storage_id.getDatabaseName()) ||
|
||||
(table == storage_id.getFullNameNotQuoted()))
|
||||
{
|
||||
ParserExpression parser;
|
||||
additional_filter_ast = parseQuery(
|
||||
parser, filter.data(), filter.data() + filter.size(),
|
||||
"additional filter", settings.max_query_size, settings.max_parser_depth);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!additional_filter_ast)
|
||||
return {};
|
||||
|
||||
table_expression_query_info.additional_filter_ast = additional_filter_ast;
|
||||
return buildFilterInfo(additional_filter_ast, table_expression_query_info.table_expression, planner_context);
|
||||
}
|
||||
|
||||
JoinTreeQueryPlan buildQueryPlanForTableExpression(QueryTreeNodePtr table_expression,
|
||||
@ -696,6 +700,10 @@ JoinTreeQueryPlan buildQueryPlanForTableExpression(QueryTreeNodePtr table_expres
|
||||
}
|
||||
}
|
||||
|
||||
const auto & table_expression_alias = table_expression->getAlias();
|
||||
auto additional_filters_info = buildAdditionalFiltersIfNeeded(storage, table_expression_alias, table_expression_query_info, planner_context);
|
||||
add_filter(additional_filters_info, "additional filter");
|
||||
|
||||
from_stage = storage->getQueryProcessingStage(query_context, select_query_options.to_stage, storage_snapshot, table_expression_query_info);
|
||||
storage->read(query_plan, columns_names, storage_snapshot, table_expression_query_info, query_context, from_stage, max_block_size, max_streams);
|
||||
|
||||
|
@ -3,6 +3,8 @@
|
||||
#include <Parsers/ASTSelectWithUnionQuery.h>
|
||||
#include <Parsers/ASTSelectQuery.h>
|
||||
#include <Parsers/ASTSubquery.h>
|
||||
#include <Parsers/ExpressionListParsers.h>
|
||||
#include <Parsers/parseQuery.h>
|
||||
|
||||
#include <DataTypes/DataTypesNumber.h>
|
||||
#include <DataTypes/DataTypeLowCardinality.h>
|
||||
@ -28,14 +30,19 @@
|
||||
#include <Analyzer/TableFunctionNode.h>
|
||||
#include <Analyzer/ArrayJoinNode.h>
|
||||
#include <Analyzer/JoinNode.h>
|
||||
#include <Analyzer/QueryTreeBuilder.h>
|
||||
#include <Analyzer/Passes/QueryAnalysisPass.h>
|
||||
|
||||
#include <Planner/PlannerActionsVisitor.h>
|
||||
#include <Planner/CollectTableExpressionData.h>
|
||||
#include <Planner/CollectSets.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int BAD_ARGUMENTS;
|
||||
extern const int LOGICAL_ERROR;
|
||||
extern const int UNION_ALL_RESULT_STRUCTURES_MISMATCH;
|
||||
extern const int INTERSECT_OR_EXCEPT_RESULT_STRUCTURES_MISMATCH;
|
||||
@ -416,4 +423,61 @@ SelectQueryInfo buildSelectQueryInfo(const QueryTreeNodePtr & query_tree, const
|
||||
return select_query_info;
|
||||
}
|
||||
|
||||
FilterDAGInfo buildFilterInfo(ASTPtr filter_expression,
|
||||
const QueryTreeNodePtr & table_expression,
|
||||
PlannerContextPtr & planner_context,
|
||||
NameSet table_expression_required_names_without_filter)
|
||||
{
|
||||
const auto & query_context = planner_context->getQueryContext();
|
||||
|
||||
auto filter_query_tree = buildQueryTree(filter_expression, query_context);
|
||||
|
||||
QueryAnalysisPass query_analysis_pass(table_expression);
|
||||
query_analysis_pass.run(filter_query_tree, query_context);
|
||||
|
||||
if (table_expression_required_names_without_filter.empty())
|
||||
{
|
||||
auto & table_expression_data = planner_context->getTableExpressionDataOrThrow(table_expression);
|
||||
const auto & table_expression_names = table_expression_data.getColumnNames();
|
||||
table_expression_required_names_without_filter.insert(table_expression_names.begin(), table_expression_names.end());
|
||||
}
|
||||
|
||||
collectSourceColumns(filter_query_tree, planner_context);
|
||||
collectSets(filter_query_tree, *planner_context);
|
||||
|
||||
auto filter_actions_dag = std::make_shared<ActionsDAG>();
|
||||
|
||||
PlannerActionsVisitor actions_visitor(planner_context, false /*use_column_identifier_as_action_node_name*/);
|
||||
auto expression_nodes = actions_visitor.visit(filter_actions_dag, filter_query_tree);
|
||||
if (expression_nodes.size() != 1)
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS,
|
||||
"Filter actions must return single output node. Actual {}",
|
||||
expression_nodes.size());
|
||||
|
||||
auto & filter_actions_outputs = filter_actions_dag->getOutputs();
|
||||
filter_actions_outputs = std::move(expression_nodes);
|
||||
|
||||
std::string filter_node_name = filter_actions_outputs[0]->result_name;
|
||||
bool remove_filter_column = true;
|
||||
|
||||
for (const auto & filter_input_node : filter_actions_dag->getInputs())
|
||||
if (table_expression_required_names_without_filter.contains(filter_input_node->result_name))
|
||||
filter_actions_outputs.push_back(filter_input_node);
|
||||
|
||||
return {std::move(filter_actions_dag), std::move(filter_node_name), remove_filter_column};
|
||||
}
|
||||
|
||||
ASTPtr parseAdditionalResultFilter(const Settings & settings)
|
||||
{
|
||||
const String & additional_result_filter = settings.additional_result_filter;
|
||||
if (additional_result_filter.empty())
|
||||
return {};
|
||||
|
||||
ParserExpression parser;
|
||||
auto additional_result_filter_ast = parseQuery(
|
||||
parser, additional_result_filter.data(), additional_result_filter.data() + additional_result_filter.size(),
|
||||
"additional result filter", settings.max_query_size, settings.max_parser_depth);
|
||||
return additional_result_filter_ast;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -78,4 +78,12 @@ QueryTreeNodePtr buildSubqueryToReadColumnsFromTableExpression(const NamesAndTyp
|
||||
|
||||
SelectQueryInfo buildSelectQueryInfo(const QueryTreeNodePtr & query_tree, const PlannerContextPtr & planner_context);
|
||||
|
||||
/// Build filter for specific table_expression
|
||||
FilterDAGInfo buildFilterInfo(ASTPtr filter_expression,
|
||||
const QueryTreeNodePtr & table_expression,
|
||||
PlannerContextPtr & planner_context,
|
||||
NameSet table_expression_required_names_without_filter = {});
|
||||
|
||||
ASTPtr parseAdditionalResultFilter(const Settings & settings);
|
||||
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ struct CompletedPipelineExecutor::Data
|
||||
}
|
||||
};
|
||||
|
||||
static void threadFunction(CompletedPipelineExecutor::Data & data, ThreadGroupStatusPtr thread_group, size_t num_threads)
|
||||
static void threadFunction(CompletedPipelineExecutor::Data & data, ThreadGroupPtr thread_group, size_t num_threads)
|
||||
{
|
||||
SCOPE_EXIT_SAFE(
|
||||
if (thread_group)
|
||||
|
@ -67,7 +67,7 @@ const Block & PullingAsyncPipelineExecutor::getHeader() const
|
||||
return lazy_format->getPort(IOutputFormat::PortKind::Main).getHeader();
|
||||
}
|
||||
|
||||
static void threadFunction(PullingAsyncPipelineExecutor::Data & data, ThreadGroupStatusPtr thread_group, size_t num_threads)
|
||||
static void threadFunction(PullingAsyncPipelineExecutor::Data & data, ThreadGroupPtr thread_group, size_t num_threads)
|
||||
{
|
||||
SCOPE_EXIT_SAFE(
|
||||
if (thread_group)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user