Compare commits

..

1 Commits

Author SHA1 Message Date
alesapin
29770bb741
Merge 3f95bade2a into 334b28f6db 2024-11-19 17:50:29 -08:00
230 changed files with 2104 additions and 5800 deletions

6
.gitmodules vendored
View File

@ -1,9 +1,6 @@
# Please do not use 'branch = ...' tags with submodule entries. Such tags make updating submodules a # Please do not use 'branch = ...' tags with submodule entries. Such tags make updating submodules a
# little bit more convenient but they do *not* specify the tracked submodule branch. Thus, they are # little bit more convenient but they do *not* specify the tracked submodule branch. Thus, they are
# more confusing than useful. # more confusing than useful.
[submodule "contrib/jwt-cpp"]
path = contrib/jwt-cpp
url = https://github.com/Thalhammer/jwt-cpp
[submodule "contrib/zstd"] [submodule "contrib/zstd"]
path = contrib/zstd path = contrib/zstd
url = https://github.com/facebook/zstd url = https://github.com/facebook/zstd
@ -351,9 +348,6 @@
[submodule "contrib/idna"] [submodule "contrib/idna"]
path = contrib/idna path = contrib/idna
url = https://github.com/ada-url/idna.git url = https://github.com/ada-url/idna.git
[submodule "contrib/google-cloud-cpp"]
path = contrib/google-cloud-cpp
url = https://github.com/ClickHouse/google-cloud-cpp.git
[submodule "contrib/rust_vendor"] [submodule "contrib/rust_vendor"]
path = contrib/rust_vendor path = contrib/rust_vendor
url = https://github.com/ClickHouse/rust_vendor.git url = https://github.com/ClickHouse/rust_vendor.git

View File

View File

@ -1,14 +0,0 @@
ARG FROM_TAG=latest
FROM clickhouse/stateless-test:$FROM_TAG
USER root
RUN apt-get update -y \
&& env DEBIAN_FRONTEND=noninteractive \
apt-get install --yes --no-install-recommends \
nodejs \
npm \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /var/cache/debconf /tmp/* \
USER clickhouse

View File

@ -1,117 +0,0 @@
# docker build -t clickhouse/stateless-test .
FROM ubuntu:22.04
# ARG for quick switch to a given ubuntu mirror
ARG apt_archive="http://archive.ubuntu.com"
RUN sed -i "s|http://archive.ubuntu.com|$apt_archive|g" /etc/apt/sources.list
ARG odbc_driver_url="https://github.com/ClickHouse/clickhouse-odbc/releases/download/v1.1.6.20200320/clickhouse-odbc-1.1.6-Linux.tar.gz"
RUN mkdir /etc/clickhouse-server /etc/clickhouse-keeper /etc/clickhouse-client && chmod 777 /etc/clickhouse-* \
&& mkdir -p /var/lib/clickhouse /var/log/clickhouse-server && chmod 777 /var/log/clickhouse-server /var/lib/clickhouse
RUN addgroup --gid 1001 clickhouse && adduser --uid 1001 --gid 1001 --disabled-password clickhouse
# moreutils - provides ts fo FT
# expect, bzip2 - requried by FT
# bsdmainutils - provides hexdump for FT
# golang version 1.13 on Ubuntu 20 is enough for tests
RUN apt-get update -y \
&& env DEBIAN_FRONTEND=noninteractive \
apt-get install --yes --no-install-recommends \
awscli \
brotli \
lz4 \
expect \
moreutils \
bzip2 \
bsdmainutils \
golang \
lsof \
mysql-client=8.0* \
ncdu \
netcat-openbsd \
nodejs \
npm \
odbcinst \
openjdk-11-jre-headless \
openssl \
postgresql-client \
python3 \
python3-pip \
qemu-user-static \
sqlite3 \
sudo \
tree \
unixodbc \
rustc \
cargo \
zstd \
file \
jq \
pv \
zip \
unzip \
p7zip-full \
curl \
wget \
xz-utils \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /var/cache/debconf /tmp/*
ARG PROTOC_VERSION=25.1
RUN curl -OL https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-linux-x86_64.zip \
&& unzip protoc-${PROTOC_VERSION}-linux-x86_64.zip -d /usr/local \
&& rm protoc-${PROTOC_VERSION}-linux-x86_64.zip
COPY requirements.txt /
RUN pip3 install --no-cache-dir -r /requirements.txt
RUN mkdir -p /tmp/clickhouse-odbc-tmp \
&& cd /tmp/clickhouse-odbc-tmp \
&& curl -L ${odbc_driver_url} | tar --strip-components=1 -xz clickhouse-odbc-1.1.6-Linux \
&& mkdir /usr/local/lib64 -p \
&& cp /tmp/clickhouse-odbc-tmp/lib64/*.so /usr/local/lib64/ \
&& odbcinst -i -d -f /tmp/clickhouse-odbc-tmp/share/doc/clickhouse-odbc/config/odbcinst.ini.sample \
&& odbcinst -i -s -l -f /tmp/clickhouse-odbc-tmp/share/doc/clickhouse-odbc/config/odbc.ini.sample \
&& sed -i 's"=libclickhouseodbc"=/usr/local/lib64/libclickhouseodbc"' /etc/odbcinst.ini \
&& rm -rf /tmp/clickhouse-odbc-tmp
ENV TZ=Europe/Amsterdam
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
ENV NUM_TRIES=1
# Unrelated to vars in setup_minio.sh, but should be the same there
# to have the same binaries for local running scenario
ARG MINIO_SERVER_VERSION=2024-08-03T04-33-23Z
ARG MINIO_CLIENT_VERSION=2024-07-31T15-58-33Z
ARG TARGETARCH
# Download Minio-related binaries
RUN arch=${TARGETARCH:-amd64} \
&& curl -L "https://dl.min.io/server/minio/release/linux-${arch}/archive/minio.RELEASE.${MINIO_SERVER_VERSION}" -o /minio \
&& curl -L "https://dl.min.io/client/mc/release/linux-${arch}/archive/mc.RELEASE.${MINIO_CLIENT_VERSION}" -o /mc \
&& chmod +x /mc /minio
ENV MINIO_ROOT_USER="clickhouse"
ENV MINIO_ROOT_PASSWORD="clickhouse"
# for minio to work without root
RUN chmod 777 /home
ENV HOME="/home"
ENV TEMP_DIR="/tmp/praktika"
ENV PATH="/wd/tests:/tmp/praktika/input:$PATH"
RUN curl -L --no-verbose -O 'https://archive.apache.org/dist/hadoop/common/hadoop-3.3.1/hadoop-3.3.1.tar.gz' \
&& tar -xvf hadoop-3.3.1.tar.gz \
&& rm -rf hadoop-3.3.1.tar.gz \
&& chmod 777 /hadoop-3.3.1
RUN npm install -g azurite@3.30.0 \
&& npm install -g tslib && npm install -g node
USER clickhouse

View File

@ -1,6 +0,0 @@
Jinja2==3.1.3
numpy==1.26.4
requests==2.32.3
pandas==1.5.3
scipy==1.12.0
pyarrow==18.0.0

View File

View File

@ -13,30 +13,11 @@ class JobStages(metaclass=MetaClasses.WithIter):
def parse_args(): def parse_args():
parser = argparse.ArgumentParser(description="ClickHouse Build Job") parser = argparse.ArgumentParser(description="ClickHouse Build Job")
parser.add_argument( parser.add_argument("BUILD_TYPE", help="Type: <amd|arm_debug|release_sanitizer>")
"--build-type", parser.add_argument("--param", help="Optional custom job start stage", default=None)
help="Type: <amd|arm>,<debug|release>,<asan|msan|..>",
)
parser.add_argument(
"--param",
help="Optional user-defined job start stage (for local run)",
default=None,
)
return parser.parse_args() return parser.parse_args()
CMAKE_CMD = """cmake --debug-trycompile -DCMAKE_VERBOSE_MAKEFILE=1 -LA \
-DCMAKE_BUILD_TYPE={BUILD_TYPE} \
-DSANITIZE={SANITIZER} \
-DENABLE_CHECK_HEAVY_BUILDS=1 -DENABLE_CLICKHOUSE_SELF_EXTRACTING=1 \
-DENABLE_UTILS=0 -DCMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY=ON -DCMAKE_INSTALL_PREFIX=/usr \
-DCMAKE_INSTALL_SYSCONFDIR=/etc -DCMAKE_INSTALL_LOCALSTATEDIR=/var -DCMAKE_SKIP_INSTALL_ALL_DEPENDENCY=ON \
{AUX_DEFS} \
-DCMAKE_C_COMPILER=clang-18 -DCMAKE_CXX_COMPILER=clang++-18 \
-DCOMPILER_CACHE={CACHE_TYPE} \
-DENABLE_BUILD_PROFILING=1 {DIR}"""
def main(): def main():
args = parse_args() args = parse_args()
@ -52,41 +33,23 @@ def main():
stages.pop(0) stages.pop(0)
stages.insert(0, stage) stages.insert(0, stage)
build_type = args.build_type cmake_build_type = "Release"
assert ( sanitizer = ""
build_type
), "build_type must be provided either as input argument or as a parameter of parametrized job in CI"
build_type = build_type.lower()
CACHE_TYPE = "sccache" if "debug" in args.BUILD_TYPE.lower():
BUILD_TYPE = "RelWithDebInfo"
SANITIZER = ""
AUX_DEFS = " -DENABLE_TESTS=0 "
if "debug" in build_type:
print("Build type set: debug") print("Build type set: debug")
BUILD_TYPE = "Debug" cmake_build_type = "Debug"
AUX_DEFS = " -DENABLE_TESTS=1 "
elif "release" in build_type: if "asan" in args.BUILD_TYPE.lower():
print("Build type set: release")
AUX_DEFS = (
" -DENABLE_TESTS=0 -DSPLIT_DEBUG_SYMBOLS=ON -DBUILD_STANDALONE_KEEPER=1 "
)
elif "asan" in build_type:
print("Sanitizer set: address") print("Sanitizer set: address")
SANITIZER = "address" sanitizer = "address"
else:
assert False
cmake_cmd = CMAKE_CMD.format( # if Environment.is_local_run():
BUILD_TYPE=BUILD_TYPE, # build_cache_type = "disabled"
CACHE_TYPE=CACHE_TYPE, # else:
SANITIZER=SANITIZER, build_cache_type = "sccache"
AUX_DEFS=AUX_DEFS,
DIR=Utils.cwd(),
)
current_directory = Utils.cwd()
build_dir = f"{Settings.TEMP_DIR}/build" build_dir = f"{Settings.TEMP_DIR}/build"
res = True res = True
@ -106,7 +69,12 @@ def main():
results.append( results.append(
Result.create_from_command_execution( Result.create_from_command_execution(
name="Cmake configuration", name="Cmake configuration",
command=cmake_cmd, command=f"cmake --debug-trycompile -DCMAKE_VERBOSE_MAKEFILE=1 -LA -DCMAKE_BUILD_TYPE={cmake_build_type} \
-DSANITIZE={sanitizer} -DENABLE_CHECK_HEAVY_BUILDS=1 -DENABLE_CLICKHOUSE_SELF_EXTRACTING=1 -DENABLE_TESTS=0 \
-DENABLE_UTILS=0 -DCMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY=ON -DCMAKE_INSTALL_PREFIX=/usr \
-DCMAKE_INSTALL_SYSCONFDIR=/etc -DCMAKE_INSTALL_LOCALSTATEDIR=/var -DCMAKE_SKIP_INSTALL_ALL_DEPENDENCY=ON \
-DCMAKE_C_COMPILER=clang-18 -DCMAKE_CXX_COMPILER=clang++-18 -DCOMPILER_CACHE={build_cache_type} -DENABLE_TESTS=1 \
-DENABLE_BUILD_PROFILING=1 {current_directory}",
workdir=build_dir, workdir=build_dir,
with_log=True, with_log=True,
) )
@ -127,7 +95,7 @@ def main():
Shell.check(f"ls -l {build_dir}/programs/") Shell.check(f"ls -l {build_dir}/programs/")
res = results[-1].is_ok() res = results[-1].is_ok()
Result.create_from(results=results, stopwatch=stop_watch).complete_job() Result.create_from(results=results, stopwatch=stop_watch).finish_job_accordingly()
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -379,4 +379,4 @@ if __name__ == "__main__":
) )
) )
Result.create_from(results=results, stopwatch=stop_watch).complete_job() Result.create_from(results=results, stopwatch=stop_watch).finish_job_accordingly()

View File

@ -1,13 +1,120 @@
import argparse import argparse
import threading
from pathlib import Path
from praktika.result import Result from praktika.result import Result
from praktika.settings import Settings from praktika.settings import Settings
from praktika.utils import MetaClasses, Shell, Utils from praktika.utils import MetaClasses, Shell, Utils
from ci.jobs.scripts.clickhouse_proc import ClickHouseProc
from ci.jobs.scripts.functional_tests_results import FTResultsProcessor from ci.jobs.scripts.functional_tests_results import FTResultsProcessor
class ClickHouseProc:
def __init__(self):
self.ch_config_dir = f"{Settings.TEMP_DIR}/etc/clickhouse-server"
self.pid_file = f"{self.ch_config_dir}/clickhouse-server.pid"
self.config_file = f"{self.ch_config_dir}/config.xml"
self.user_files_path = f"{self.ch_config_dir}/user_files"
self.test_output_file = f"{Settings.OUTPUT_DIR}/test_result.txt"
self.command = f"clickhouse-server --config-file {self.config_file} --pid-file {self.pid_file} -- --path {self.ch_config_dir} --user_files_path {self.user_files_path} --top_level_domains_path {self.ch_config_dir}/top_level_domains --keeper_server.storage_path {self.ch_config_dir}/coordination"
self.proc = None
self.pid = 0
nproc = int(Utils.cpu_count() / 2)
self.fast_test_command = f"clickhouse-test --hung-check --fast-tests-only --no-random-settings --no-random-merge-tree-settings --no-long --testname --shard --zookeeper --check-zookeeper-session --order random --print-time --report-logs-stats --jobs {nproc} -- '' | ts '%Y-%m-%d %H:%M:%S' \
| tee -a \"{self.test_output_file}\""
# TODO: store info in case of failure
self.info = ""
self.info_file = ""
Utils.set_env("CLICKHOUSE_CONFIG_DIR", self.ch_config_dir)
Utils.set_env("CLICKHOUSE_CONFIG", self.config_file)
Utils.set_env("CLICKHOUSE_USER_FILES", self.user_files_path)
Utils.set_env("CLICKHOUSE_SCHEMA_FILES", f"{self.ch_config_dir}/format_schemas")
def start(self):
print("Starting ClickHouse server")
Shell.check(f"rm {self.pid_file}")
def run_clickhouse():
self.proc = Shell.run_async(
self.command, verbose=True, suppress_output=True
)
thread = threading.Thread(target=run_clickhouse)
thread.daemon = True # Allow program to exit even if thread is still running
thread.start()
# self.proc = Shell.run_async(self.command, verbose=True)
started = False
try:
for _ in range(5):
pid = Shell.get_output(f"cat {self.pid_file}").strip()
if not pid:
Utils.sleep(1)
continue
started = True
print(f"Got pid from fs [{pid}]")
_ = int(pid)
break
except Exception:
pass
if not started:
stdout = self.proc.stdout.read().strip() if self.proc.stdout else ""
stderr = self.proc.stderr.read().strip() if self.proc.stderr else ""
Utils.print_formatted_error("Failed to start ClickHouse", stdout, stderr)
return False
print(f"ClickHouse server started successfully, pid [{pid}]")
return True
def wait_ready(self):
res, out, err = 0, "", ""
attempts = 30
delay = 2
for attempt in range(attempts):
res, out, err = Shell.get_res_stdout_stderr(
'clickhouse-client --query "select 1"', verbose=True
)
if out.strip() == "1":
print("Server ready")
break
else:
print(f"Server not ready, wait")
Utils.sleep(delay)
else:
Utils.print_formatted_error(
f"Server not ready after [{attempts*delay}s]", out, err
)
return False
return True
def run_fast_test(self):
if Path(self.test_output_file).exists():
Path(self.test_output_file).unlink()
exit_code = Shell.run(self.fast_test_command)
return exit_code == 0
def terminate(self):
print("Terminate ClickHouse process")
timeout = 10
if self.proc:
Utils.terminate_process_group(self.proc.pid)
self.proc.terminate()
try:
self.proc.wait(timeout=10)
print(f"Process {self.proc.pid} terminated gracefully.")
except Exception:
print(
f"Process {self.proc.pid} did not terminate in {timeout} seconds, killing it..."
)
Utils.terminate_process_group(self.proc.pid, force=True)
self.proc.wait() # Wait for the process to be fully killed
print(f"Process {self.proc} was killed.")
def clone_submodules(): def clone_submodules():
submodules_to_update = [ submodules_to_update = [
"contrib/sysroot", "contrib/sysroot",
@ -133,7 +240,7 @@ def main():
Shell.check(f"rm -rf {build_dir} && mkdir -p {build_dir}") Shell.check(f"rm -rf {build_dir} && mkdir -p {build_dir}")
results.append( results.append(
Result.create_from_command_execution( Result.create_from_command_execution(
name="Checkout Submodules", name="Checkout Submodules for Minimal Build",
command=clone_submodules, command=clone_submodules,
) )
) )
@ -188,8 +295,8 @@ def main():
if res and JobStages.CONFIG in stages: if res and JobStages.CONFIG in stages:
commands = [ commands = [
f"rm -rf {Settings.TEMP_DIR}/etc/ && mkdir -p {Settings.TEMP_DIR}/etc/clickhouse-client {Settings.TEMP_DIR}/etc/clickhouse-server", f"rm -rf {Settings.TEMP_DIR}/etc/ && mkdir -p {Settings.TEMP_DIR}/etc/clickhouse-client {Settings.TEMP_DIR}/etc/clickhouse-server",
f"cp ./programs/server/config.xml ./programs/server/users.xml {Settings.TEMP_DIR}/etc/clickhouse-server/", f"cp {current_directory}/programs/server/config.xml {current_directory}/programs/server/users.xml {Settings.TEMP_DIR}/etc/clickhouse-server/",
f"./tests/config/install.sh {Settings.TEMP_DIR}/etc/clickhouse-server {Settings.TEMP_DIR}/etc/clickhouse-client --fast-test", f"{current_directory}/tests/config/install.sh {Settings.TEMP_DIR}/etc/clickhouse-server {Settings.TEMP_DIR}/etc/clickhouse-client",
# f"cp -a {current_directory}/programs/server/config.d/log_to_console.xml {Settings.TEMP_DIR}/etc/clickhouse-server/config.d/", # f"cp -a {current_directory}/programs/server/config.d/log_to_console.xml {Settings.TEMP_DIR}/etc/clickhouse-server/config.d/",
f"rm -f {Settings.TEMP_DIR}/etc/clickhouse-server/config.d/secure_ports.xml", f"rm -f {Settings.TEMP_DIR}/etc/clickhouse-server/config.d/secure_ports.xml",
update_path_ch_config, update_path_ch_config,
@ -203,7 +310,7 @@ def main():
) )
res = results[-1].is_ok() res = results[-1].is_ok()
CH = ClickHouseProc(fast_test=True) CH = ClickHouseProc()
if res and JobStages.TEST in stages: if res and JobStages.TEST in stages:
stop_watch_ = Utils.Stopwatch() stop_watch_ = Utils.Stopwatch()
step_name = "Start ClickHouse Server" step_name = "Start ClickHouse Server"
@ -215,17 +322,15 @@ def main():
) )
if res and JobStages.TEST in stages: if res and JobStages.TEST in stages:
stop_watch_ = Utils.Stopwatch()
step_name = "Tests" step_name = "Tests"
print(step_name) print(step_name)
res = res and CH.run_fast_test() res = res and CH.run_fast_test()
if res: if res:
results.append(FTResultsProcessor(wd=Settings.OUTPUT_DIR).run()) results.append(FTResultsProcessor(wd=Settings.OUTPUT_DIR).run())
results[-1].set_timing(stopwatch=stop_watch_)
CH.terminate() CH.terminate()
Result.create_from(results=results, stopwatch=stop_watch).complete_job() Result.create_from(results=results, stopwatch=stop_watch).finish_job_accordingly()
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -1,171 +0,0 @@
import argparse
import os
import time
from pathlib import Path
from praktika.result import Result
from praktika.settings import Settings
from praktika.utils import MetaClasses, Shell, Utils
from ci.jobs.scripts.clickhouse_proc import ClickHouseProc
from ci.jobs.scripts.functional_tests_results import FTResultsProcessor
class JobStages(metaclass=MetaClasses.WithIter):
INSTALL_CLICKHOUSE = "install"
START = "start"
TEST = "test"
def parse_args():
parser = argparse.ArgumentParser(description="ClickHouse Build Job")
parser.add_argument(
"--ch-path", help="Path to clickhouse binary", default=f"{Settings.INPUT_DIR}"
)
parser.add_argument(
"--test-options",
help="Comma separated option(s): parallel|non-parallel|BATCH_NUM/BTATCH_TOT|..",
default="",
)
parser.add_argument("--param", help="Optional job start stage", default=None)
parser.add_argument("--test", help="Optional test name pattern", default="")
return parser.parse_args()
def run_test(
no_parallel: bool, no_sequiential: bool, batch_num: int, batch_total: int, test=""
):
test_output_file = f"{Settings.OUTPUT_DIR}/test_result.txt"
test_command = f"clickhouse-test --jobs 2 --testname --shard --zookeeper --check-zookeeper-session --no-stateless \
--hung-check --print-time \
--capture-client-stacktrace --queries ./tests/queries -- '{test}' \
| ts '%Y-%m-%d %H:%M:%S' | tee -a \"{test_output_file}\""
if Path(test_output_file).exists():
Path(test_output_file).unlink()
Shell.run(test_command, verbose=True)
def main():
args = parse_args()
test_options = args.test_options.split(",")
no_parallel = "non-parallel" in test_options
no_sequential = "parallel" in test_options
batch_num, total_batches = 0, 0
for to in test_options:
if "/" in to:
batch_num, total_batches = map(int, to.split("/"))
# os.environ["AZURE_CONNECTION_STRING"] = Shell.get_output(
# f"aws ssm get-parameter --region us-east-1 --name azure_connection_string --with-decryption --output text --query Parameter.Value",
# verbose=True,
# strict=True
# )
ch_path = args.ch_path
assert Path(
ch_path + "/clickhouse"
).is_file(), f"clickhouse binary not found under [{ch_path}]"
stop_watch = Utils.Stopwatch()
stages = list(JobStages)
logs_to_attach = []
stage = args.param or JobStages.INSTALL_CLICKHOUSE
if stage:
assert stage in JobStages, f"--param must be one of [{list(JobStages)}]"
print(f"Job will start from stage [{stage}]")
while stage in stages:
stages.pop(0)
stages.insert(0, stage)
res = True
results = []
Utils.add_to_PATH(f"{ch_path}:tests")
if res and JobStages.INSTALL_CLICKHOUSE in stages:
commands = [
f"rm -rf /tmp/praktika/var/log/clickhouse-server/clickhouse-server.*",
f"chmod +x {ch_path}/clickhouse",
f"ln -sf {ch_path}/clickhouse {ch_path}/clickhouse-server",
f"ln -sf {ch_path}/clickhouse {ch_path}/clickhouse-client",
f"ln -sf {ch_path}/clickhouse {ch_path}/clickhouse-compressor",
f"ln -sf {ch_path}/clickhouse {ch_path}/clickhouse-local",
f"rm -rf {Settings.TEMP_DIR}/etc/ && mkdir -p {Settings.TEMP_DIR}/etc/clickhouse-client {Settings.TEMP_DIR}/etc/clickhouse-server",
f"cp programs/server/config.xml programs/server/users.xml {Settings.TEMP_DIR}/etc/clickhouse-server/",
# TODO: find a way to work with Azure secret so it's ok for local tests as well, for now keep azure disabled
f"./tests/config/install.sh {Settings.TEMP_DIR}/etc/clickhouse-server {Settings.TEMP_DIR}/etc/clickhouse-client --s3-storage --no-azure",
# clickhouse benchmark segfaults with --config-path, so provide client config by its default location
f"cp {Settings.TEMP_DIR}/etc/clickhouse-client/* /etc/clickhouse-client/",
# update_path_ch_config,
# f"sed -i 's|>/var/|>{Settings.TEMP_DIR}/var/|g; s|>/etc/|>{Settings.TEMP_DIR}/etc/|g' {Settings.TEMP_DIR}/etc/clickhouse-server/config.xml",
# f"sed -i 's|>/etc/|>{Settings.TEMP_DIR}/etc/|g' {Settings.TEMP_DIR}/etc/clickhouse-server/config.d/ssl_certs.xml",
f"for file in /tmp/praktika/etc/clickhouse-server/config.d/*.xml; do [ -f $file ] && echo Change config $file && sed -i 's|>/var/log|>{Settings.TEMP_DIR}/var/log|g; s|>/etc/|>{Settings.TEMP_DIR}/etc/|g' $(readlink -f $file); done",
f"for file in /tmp/praktika/etc/clickhouse-server/*.xml; do [ -f $file ] && echo Change config $file && sed -i 's|>/var/log|>{Settings.TEMP_DIR}/var/log|g; s|>/etc/|>{Settings.TEMP_DIR}/etc/|g' $(readlink -f $file); done",
f"for file in /tmp/praktika/etc/clickhouse-server/config.d/*.xml; do [ -f $file ] && echo Change config $file && sed -i 's|<path>local_disk|<path>{Settings.TEMP_DIR}/local_disk|g' $(readlink -f $file); done",
f"clickhouse-server --version",
]
results.append(
Result.create_from_command_execution(
name="Install ClickHouse", command=commands, with_log=True
)
)
res = results[-1].is_ok()
CH = ClickHouseProc()
if res and JobStages.START in stages:
stop_watch_ = Utils.Stopwatch()
step_name = "Start ClickHouse Server"
print(step_name)
minio_log = "/tmp/praktika/output/minio.log"
res = res and CH.start_minio(test_type="stateful", log_file_path=minio_log)
logs_to_attach += [minio_log]
time.sleep(10)
Shell.check("ps -ef | grep minio", verbose=True)
res = res and Shell.check(
"aws s3 ls s3://test --endpoint-url http://localhost:11111/", verbose=True
)
res = res and CH.start()
res = res and CH.wait_ready()
if res:
print("ch started")
logs_to_attach += [
"/tmp/praktika/var/log/clickhouse-server/clickhouse-server.log",
"/tmp/praktika/var/log/clickhouse-server/clickhouse-server.err.log",
]
results.append(
Result.create_from(
name=step_name,
status=res,
stopwatch=stop_watch_,
)
)
res = results[-1].is_ok()
if res and JobStages.TEST in stages:
stop_watch_ = Utils.Stopwatch()
step_name = "Tests"
print(step_name)
# assert Shell.check("clickhouse-client -q \"insert into system.zookeeper (name, path, value) values ('auxiliary_zookeeper2', '/test/chroot/', '')\"", verbose=True)
run_test(
no_parallel=no_parallel,
no_sequiential=no_sequential,
batch_num=batch_num,
batch_total=total_batches,
test=args.test,
)
results.append(FTResultsProcessor(wd=Settings.OUTPUT_DIR).run())
results[-1].set_timing(stopwatch=stop_watch_)
res = results[-1].is_ok()
Result.create_from(
results=results, stopwatch=stop_watch, files=logs_to_attach if not res else []
).complete_job()
if __name__ == "__main__":
main()

View File

@ -1,183 +0,0 @@
import argparse
import os
import time
from pathlib import Path
from praktika.result import Result
from praktika.settings import Settings
from praktika.utils import MetaClasses, Shell, Utils
from ci.jobs.scripts.clickhouse_proc import ClickHouseProc
from ci.jobs.scripts.functional_tests_results import FTResultsProcessor
class JobStages(metaclass=MetaClasses.WithIter):
INSTALL_CLICKHOUSE = "install"
START = "start"
TEST = "test"
def parse_args():
parser = argparse.ArgumentParser(description="ClickHouse Build Job")
parser.add_argument(
"--ch-path", help="Path to clickhouse binary", default=f"{Settings.INPUT_DIR}"
)
parser.add_argument(
"--test-options",
help="Comma separated option(s): parallel|non-parallel|BATCH_NUM/BTATCH_TOT|..",
default="",
)
parser.add_argument("--param", help="Optional job start stage", default=None)
parser.add_argument("--test", help="Optional test name pattern", default="")
return parser.parse_args()
def run_stateless_test(
no_parallel: bool, no_sequiential: bool, batch_num: int, batch_total: int, test=""
):
assert not (no_parallel and no_sequiential)
test_output_file = f"{Settings.OUTPUT_DIR}/test_result.txt"
aux = ""
nproc = int(Utils.cpu_count() / 2)
if batch_num and batch_total:
aux = f"--run-by-hash-total {batch_total} --run-by-hash-num {batch_num-1}"
statless_test_command = f"clickhouse-test --testname --shard --zookeeper --check-zookeeper-session --hung-check --print-time \
--no-drop-if-fail --capture-client-stacktrace --queries /repo/tests/queries --test-runs 1 --hung-check \
{'--no-parallel' if no_parallel else ''} {'--no-sequential' if no_sequiential else ''} \
--print-time --jobs {nproc} --report-coverage --report-logs-stats {aux} \
--queries ./tests/queries -- '{test}' | ts '%Y-%m-%d %H:%M:%S' \
| tee -a \"{test_output_file}\""
if Path(test_output_file).exists():
Path(test_output_file).unlink()
Shell.run(statless_test_command, verbose=True)
def main():
args = parse_args()
test_options = args.test_options.split(",")
no_parallel = "non-parallel" in test_options
no_sequential = "parallel" in test_options
batch_num, total_batches = 0, 0
for to in test_options:
if "/" in to:
batch_num, total_batches = map(int, to.split("/"))
# os.environ["AZURE_CONNECTION_STRING"] = Shell.get_output(
# f"aws ssm get-parameter --region us-east-1 --name azure_connection_string --with-decryption --output text --query Parameter.Value",
# verbose=True,
# strict=True
# )
ch_path = args.ch_path
assert Path(
ch_path + "/clickhouse"
).is_file(), f"clickhouse binary not found under [{ch_path}]"
stop_watch = Utils.Stopwatch()
stages = list(JobStages)
logs_to_attach = []
stage = args.param or JobStages.INSTALL_CLICKHOUSE
if stage:
assert stage in JobStages, f"--param must be one of [{list(JobStages)}]"
print(f"Job will start from stage [{stage}]")
while stage in stages:
stages.pop(0)
stages.insert(0, stage)
res = True
results = []
Utils.add_to_PATH(f"{ch_path}:tests")
if res and JobStages.INSTALL_CLICKHOUSE in stages:
commands = [
f"rm -rf /tmp/praktika/var/log/clickhouse-server/clickhouse-server.*",
f"chmod +x {ch_path}/clickhouse",
f"ln -sf {ch_path}/clickhouse {ch_path}/clickhouse-server",
f"ln -sf {ch_path}/clickhouse {ch_path}/clickhouse-client",
f"ln -sf {ch_path}/clickhouse {ch_path}/clickhouse-compressor",
f"ln -sf {ch_path}/clickhouse {ch_path}/clickhouse-local",
f"rm -rf {Settings.TEMP_DIR}/etc/ && mkdir -p {Settings.TEMP_DIR}/etc/clickhouse-client {Settings.TEMP_DIR}/etc/clickhouse-server",
f"cp programs/server/config.xml programs/server/users.xml {Settings.TEMP_DIR}/etc/clickhouse-server/",
# TODO: find a way to work with Azure secret so it's ok for local tests as well, for now keep azure disabled
f"./tests/config/install.sh {Settings.TEMP_DIR}/etc/clickhouse-server {Settings.TEMP_DIR}/etc/clickhouse-client --s3-storage --no-azure",
# clickhouse benchmark segfaults with --config-path, so provide client config by its default location
f"cp {Settings.TEMP_DIR}/etc/clickhouse-client/* /etc/clickhouse-client/",
# update_path_ch_config,
# f"sed -i 's|>/var/|>{Settings.TEMP_DIR}/var/|g; s|>/etc/|>{Settings.TEMP_DIR}/etc/|g' {Settings.TEMP_DIR}/etc/clickhouse-server/config.xml",
# f"sed -i 's|>/etc/|>{Settings.TEMP_DIR}/etc/|g' {Settings.TEMP_DIR}/etc/clickhouse-server/config.d/ssl_certs.xml",
f"for file in /tmp/praktika/etc/clickhouse-server/config.d/*.xml; do [ -f $file ] && echo Change config $file && sed -i 's|>/var/log|>{Settings.TEMP_DIR}/var/log|g; s|>/etc/|>{Settings.TEMP_DIR}/etc/|g' $(readlink -f $file); done",
f"for file in /tmp/praktika/etc/clickhouse-server/*.xml; do [ -f $file ] && echo Change config $file && sed -i 's|>/var/log|>{Settings.TEMP_DIR}/var/log|g; s|>/etc/|>{Settings.TEMP_DIR}/etc/|g' $(readlink -f $file); done",
f"for file in /tmp/praktika/etc/clickhouse-server/config.d/*.xml; do [ -f $file ] && echo Change config $file && sed -i 's|<path>local_disk|<path>{Settings.TEMP_DIR}/local_disk|g' $(readlink -f $file); done",
f"clickhouse-server --version",
]
results.append(
Result.create_from_command_execution(
name="Install ClickHouse", command=commands, with_log=True
)
)
res = results[-1].is_ok()
CH = ClickHouseProc()
if res and JobStages.START in stages:
stop_watch_ = Utils.Stopwatch()
step_name = "Start ClickHouse Server"
print(step_name)
hdfs_log = "/tmp/praktika/output/hdfs_mini.log"
minio_log = "/tmp/praktika/output/minio.log"
res = res and CH.start_hdfs(log_file_path=hdfs_log)
res = res and CH.start_minio(test_type="stateful", log_file_path=minio_log)
logs_to_attach += [minio_log, hdfs_log]
time.sleep(10)
Shell.check("ps -ef | grep minio", verbose=True)
Shell.check("ps -ef | grep hdfs", verbose=True)
res = res and Shell.check(
"aws s3 ls s3://test --endpoint-url http://localhost:11111/", verbose=True
)
res = res and CH.start()
res = res and CH.wait_ready()
if res:
print("ch started")
logs_to_attach += [
"/tmp/praktika/var/log/clickhouse-server/clickhouse-server.log",
"/tmp/praktika/var/log/clickhouse-server/clickhouse-server.err.log",
]
results.append(
Result.create_from(
name=step_name,
status=res,
stopwatch=stop_watch_,
)
)
res = results[-1].is_ok()
if res and JobStages.TEST in stages:
stop_watch_ = Utils.Stopwatch()
step_name = "Tests"
print(step_name)
assert Shell.check(
"clickhouse-client -q \"insert into system.zookeeper (name, path, value) values ('auxiliary_zookeeper2', '/test/chroot/', '')\"",
verbose=True,
)
run_stateless_test(
no_parallel=no_parallel,
no_sequiential=no_sequential,
batch_num=batch_num,
batch_total=total_batches,
test=args.test,
)
results.append(FTResultsProcessor(wd=Settings.OUTPUT_DIR).run())
results[-1].set_timing(stopwatch=stop_watch_)
res = results[-1].is_ok()
Result.create_from(
results=results, stopwatch=stop_watch, files=logs_to_attach if not res else []
).complete_job()
if __name__ == "__main__":
main()

View File

@ -1,142 +0,0 @@
import subprocess
from pathlib import Path
from praktika.settings import Settings
from praktika.utils import Shell, Utils
class ClickHouseProc:
BACKUPS_XML = """
<clickhouse>
<backups>
<type>local</type>
<path>{CH_RUNTIME_DIR}/var/lib/clickhouse/disks/backups/</path>
</backups>
</clickhouse>
"""
def __init__(self, fast_test=False):
self.ch_config_dir = f"{Settings.TEMP_DIR}/etc/clickhouse-server"
self.pid_file = f"{self.ch_config_dir}/clickhouse-server.pid"
self.config_file = f"{self.ch_config_dir}/config.xml"
self.user_files_path = f"{self.ch_config_dir}/user_files"
self.test_output_file = f"{Settings.OUTPUT_DIR}/test_result.txt"
self.command = f"clickhouse-server --config-file {self.config_file} --pid-file {self.pid_file} -- --path {self.ch_config_dir} --user_files_path {self.user_files_path} --top_level_domains_path {self.ch_config_dir}/top_level_domains --keeper_server.storage_path {self.ch_config_dir}/coordination"
self.proc = None
self.pid = 0
nproc = int(Utils.cpu_count() / 2)
self.fast_test_command = f"clickhouse-test --hung-check --fast-tests-only --no-random-settings --no-random-merge-tree-settings --no-long --testname --shard --zookeeper --check-zookeeper-session --order random --print-time --report-logs-stats --jobs {nproc} -- '' | ts '%Y-%m-%d %H:%M:%S' \
| tee -a \"{self.test_output_file}\""
# TODO: store info in case of failure
self.info = ""
self.info_file = ""
Utils.set_env("CLICKHOUSE_CONFIG_DIR", self.ch_config_dir)
Utils.set_env("CLICKHOUSE_CONFIG", self.config_file)
Utils.set_env("CLICKHOUSE_USER_FILES", self.user_files_path)
# Utils.set_env("CLICKHOUSE_SCHEMA_FILES", f"{self.ch_config_dir}/format_schemas")
# if not fast_test:
# with open(f"{self.ch_config_dir}/config.d/backups.xml", "w") as file:
# file.write(self.BACKUPS_XML)
self.minio_proc = None
def start_hdfs(self, log_file_path):
command = ["./ci/jobs/scripts/functional_tests/setup_hdfs_minicluster.sh"]
with open(log_file_path, "w") as log_file:
process = subprocess.Popen(
command, stdout=log_file, stderr=subprocess.STDOUT
)
print(
f"Started setup_hdfs_minicluster.sh asynchronously with PID {process.pid}"
)
return True
def start_minio(self, test_type, log_file_path):
command = [
"./ci/jobs/scripts/functional_tests/setup_minio.sh",
test_type,
"./tests",
]
with open(log_file_path, "w") as log_file:
process = subprocess.Popen(
command, stdout=log_file, stderr=subprocess.STDOUT
)
print(f"Started setup_minio.sh asynchronously with PID {process.pid}")
return True
def start(self):
print("Starting ClickHouse server")
Shell.check(f"rm {self.pid_file}")
self.proc = subprocess.Popen(self.command, stderr=subprocess.STDOUT, shell=True)
started = False
try:
for _ in range(5):
pid = Shell.get_output(f"cat {self.pid_file}").strip()
if not pid:
Utils.sleep(1)
continue
started = True
print(f"Got pid from fs [{pid}]")
_ = int(pid)
break
except Exception:
pass
if not started:
stdout = self.proc.stdout.read().strip() if self.proc.stdout else ""
stderr = self.proc.stderr.read().strip() if self.proc.stderr else ""
Utils.print_formatted_error("Failed to start ClickHouse", stdout, stderr)
return False
print(f"ClickHouse server started successfully, pid [{pid}]")
return True
def wait_ready(self):
res, out, err = 0, "", ""
attempts = 30
delay = 2
for attempt in range(attempts):
res, out, err = Shell.get_res_stdout_stderr(
'clickhouse-client --query "select 1"', verbose=True
)
if out.strip() == "1":
print("Server ready")
break
else:
print(f"Server not ready, wait")
Utils.sleep(delay)
else:
Utils.print_formatted_error(
f"Server not ready after [{attempts*delay}s]", out, err
)
return False
return True
def run_fast_test(self):
if Path(self.test_output_file).exists():
Path(self.test_output_file).unlink()
exit_code = Shell.run(self.fast_test_command)
return exit_code == 0
def terminate(self):
print("Terminate ClickHouse process")
timeout = 10
if self.proc:
Utils.terminate_process_group(self.proc.pid)
self.proc.terminate()
try:
self.proc.wait(timeout=10)
print(f"Process {self.proc.pid} terminated gracefully.")
except Exception:
print(
f"Process {self.proc.pid} did not terminate in {timeout} seconds, killing it..."
)
Utils.terminate_process_group(self.proc.pid, force=True)
self.proc.wait() # Wait for the process to be fully killed
print(f"Process {self.proc} was killed.")
if self.minio_proc:
Utils.terminate_process_group(self.minio_proc.pid)

View File

@ -1,19 +0,0 @@
#!/bin/bash
# shellcheck disable=SC2024
set -e -x -a -u
ls -lha
cd /hadoop-3.3.1
export JAVA_HOME=/usr
mkdir -p target/test/data
bin/mapred minicluster -format -nomr -nnport 12222 &
while ! nc -z localhost 12222; do
sleep 1
done
lsof -i :12222

View File

@ -1,162 +0,0 @@
#!/bin/bash
set -euxf -o pipefail
export MINIO_ROOT_USER=${MINIO_ROOT_USER:-clickhouse}
export MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD:-clickhouse}
TEST_DIR=${2:-/repo/tests/}
if [ -d "$TEMP_DIR" ]; then
TEST_DIR=$(readlink -f $TEST_DIR)
cd "$TEMP_DIR"
# add / for minio mc in docker
PATH="/:.:$PATH"
fi
usage() {
echo $"Usage: $0 <stateful|stateless> <test_path> (default path: /usr/share/clickhouse-test)"
exit 1
}
check_arg() {
local query_dir
if [ ! $# -eq 1 ]; then
if [ ! $# -eq 2 ]; then
echo "ERROR: need either one or two arguments, <stateful|stateless> <test_path> (default path: /usr/share/clickhouse-test)"
usage
fi
fi
case "$1" in
stateless)
query_dir="0_stateless"
;;
stateful)
query_dir="1_stateful"
;;
*)
echo "unknown test type ${test_type}"
usage
;;
esac
echo ${query_dir}
}
find_arch() {
local arch
case $(uname -m) in
x86_64)
arch="amd64"
;;
aarch64)
arch="arm64"
;;
*)
echo "unknown architecture $(uname -m)";
exit 1
;;
esac
echo ${arch}
}
find_os() {
local os
os=$(uname -s | tr '[:upper:]' '[:lower:]')
echo "${os}"
}
download_minio() {
local os
local arch
local minio_server_version=${MINIO_SERVER_VERSION:-2024-08-03T04-33-23Z}
local minio_client_version=${MINIO_CLIENT_VERSION:-2024-07-31T15-58-33Z}
os=$(find_os)
arch=$(find_arch)
wget "https://dl.min.io/server/minio/release/${os}-${arch}/archive/minio.RELEASE.${minio_server_version}" -O ./minio
wget "https://dl.min.io/client/mc/release/${os}-${arch}/archive/mc.RELEASE.${minio_client_version}" -O ./mc
chmod +x ./mc ./minio
}
start_minio() {
pwd
mkdir -p ./minio_data
minio --version
nohup minio server --address ":11111" ./minio_data &
wait_for_it
lsof -i :11111
sleep 5
}
setup_minio() {
local test_type=$1
echo "setup_minio(), test_type=$test_type"
mc alias set clickminio http://localhost:11111 clickhouse clickhouse
mc admin user add clickminio test testtest
mc admin policy attach clickminio readwrite --user=test ||:
mc mb --ignore-existing clickminio/test
if [ "$test_type" = "stateless" ]; then
echo "Create @test bucket in minio"
mc anonymous set public clickminio/test
fi
}
# uploads data to minio, by default after unpacking all tests
# will be in /usr/share/clickhouse-test/queries
upload_data() {
local query_dir=$1
local test_path=$2
local data_path=${test_path}/queries/${query_dir}/data_minio
echo "upload_data() data_path=$data_path"
# iterating over globs will cause redundant file variable to be
# a path to a file, not a filename
# shellcheck disable=SC2045
if [ -d "${data_path}" ]; then
mc cp --recursive "${data_path}"/ clickminio/test/
fi
}
setup_aws_credentials() {
local minio_root_user=${MINIO_ROOT_USER:-clickhouse}
local minio_root_password=${MINIO_ROOT_PASSWORD:-clickhouse}
mkdir -p ~/.aws
cat <<EOT >> ~/.aws/credentials
[default]
aws_access_key_id=${minio_root_user}
aws_secret_access_key=${minio_root_password}
EOT
}
wait_for_it() {
local counter=0
local max_counter=60
local url="http://localhost:11111"
local params=(
--silent
--verbose
)
while ! curl "${params[@]}" "${url}" 2>&1 | grep AccessDenied
do
if [[ ${counter} == "${max_counter}" ]]; then
echo "failed to setup minio"
exit 0
fi
echo "trying to connect to minio"
sleep 1
counter=$((counter + 1))
done
}
main() {
local query_dir
query_dir=$(check_arg "$@")
if ! (minio --version && mc --version); then
download_minio
fi
start_minio
setup_minio "$1"
upload_data "${query_dir}" "$TEST_DIR"
setup_aws_credentials
}
main "$@"

View File

@ -1,6 +1,7 @@
import dataclasses import dataclasses
from typing import List from typing import List
from praktika.environment import Environment
from praktika.result import Result from praktika.result import Result
OK_SIGN = "[ OK " OK_SIGN = "[ OK "
@ -232,8 +233,6 @@ class FTResultsProcessor:
else: else:
pass pass
info = f"Total: {s.total - s.skipped}, Failed: {s.failed}"
# TODO: !!! # TODO: !!!
# def test_result_comparator(item): # def test_result_comparator(item):
# # sort by status then by check name # # sort by status then by check name
@ -251,11 +250,10 @@ class FTResultsProcessor:
# test_results.sort(key=test_result_comparator) # test_results.sort(key=test_result_comparator)
return Result.create_from( return Result.create_from(
name="Tests", name=Environment.JOB_NAME,
results=test_results, results=test_results,
status=state, status=state,
files=[self.tests_output_file], files=[self.tests_output_file],
info=info,
with_info_from_results=False, with_info_from_results=False,
) )

View File

@ -37,30 +37,6 @@ def create_parser():
type=str, type=str,
default=None, default=None,
) )
run_parser.add_argument(
"--test",
help="Custom parameter to pass into a job script, it's up to job script how to use it, for local test",
type=str,
default="",
)
run_parser.add_argument(
"--pr",
help="PR number. Optional parameter for local run. Set if you want an required artifact to be uploaded from CI run in that PR",
type=int,
default=None,
)
run_parser.add_argument(
"--sha",
help="Commit sha. Optional parameter for local run. Set if you want an required artifact to be uploaded from CI run on that sha, head sha will be used if not set",
type=str,
default=None,
)
run_parser.add_argument(
"--branch",
help="Commit sha. Optional parameter for local run. Set if you want an required artifact to be uploaded from CI run on that branch, main branch name will be used if not set",
type=str,
default=None,
)
run_parser.add_argument( run_parser.add_argument(
"--ci", "--ci",
help="When not set - dummy env will be generated, for local test", help="When not set - dummy env will be generated, for local test",
@ -109,13 +85,9 @@ if __name__ == "__main__":
workflow=workflow, workflow=workflow,
job=job, job=job,
docker=args.docker, docker=args.docker,
local_run=not args.ci, dummy_env=not args.ci,
no_docker=args.no_docker, no_docker=args.no_docker,
param=args.param, param=args.param,
test=args.test,
pr=args.pr,
branch=args.branch,
sha=args.sha,
) )
else: else:
parser.print_help() parser.print_help()

View File

@ -6,7 +6,7 @@ from types import SimpleNamespace
from typing import Any, Dict, List, Type from typing import Any, Dict, List, Type
from praktika import Workflow from praktika import Workflow
from praktika.settings import Settings from praktika._settings import _Settings
from praktika.utils import MetaClasses, T from praktika.utils import MetaClasses, T
@ -30,12 +30,13 @@ class _Environment(MetaClasses.Serializable):
INSTANCE_ID: str INSTANCE_ID: str
INSTANCE_LIFE_CYCLE: str INSTANCE_LIFE_CYCLE: str
LOCAL_RUN: bool = False LOCAL_RUN: bool = False
PARAMETER: Any = None
REPORT_INFO: List[str] = dataclasses.field(default_factory=list) REPORT_INFO: List[str] = dataclasses.field(default_factory=list)
name = "environment" name = "environment"
@classmethod @classmethod
def file_name_static(cls, _name=""): def file_name_static(cls, _name=""):
return f"{Settings.TEMP_DIR}/{cls.name}.json" return f"{_Settings.TEMP_DIR}/{cls.name}.json"
@classmethod @classmethod
def from_dict(cls: Type[T], obj: Dict[str, Any]) -> T: def from_dict(cls: Type[T], obj: Dict[str, Any]) -> T:
@ -66,12 +67,12 @@ class _Environment(MetaClasses.Serializable):
@staticmethod @staticmethod
def get_needs_statuses(): def get_needs_statuses():
if Path(Settings.WORKFLOW_STATUS_FILE).is_file(): if Path(_Settings.WORKFLOW_STATUS_FILE).is_file():
with open(Settings.WORKFLOW_STATUS_FILE, "r", encoding="utf8") as f: with open(_Settings.WORKFLOW_STATUS_FILE, "r", encoding="utf8") as f:
return json.load(f) return json.load(f)
else: else:
print( print(
f"ERROR: Status file [{Settings.WORKFLOW_STATUS_FILE}] does not exist" f"ERROR: Status file [{_Settings.WORKFLOW_STATUS_FILE}] does not exist"
) )
raise RuntimeError() raise RuntimeError()
@ -158,8 +159,7 @@ class _Environment(MetaClasses.Serializable):
@classmethod @classmethod
def get_s3_prefix_static(cls, pr_number, branch, sha, latest=False): def get_s3_prefix_static(cls, pr_number, branch, sha, latest=False):
prefix = "" prefix = ""
assert sha or latest if pr_number > 0:
if pr_number and pr_number > 0:
prefix += f"{pr_number}" prefix += f"{pr_number}"
else: else:
prefix += f"{branch}" prefix += f"{branch}"
@ -171,15 +171,18 @@ class _Environment(MetaClasses.Serializable):
# TODO: find a better place for the function. This file should not import praktika.settings # TODO: find a better place for the function. This file should not import praktika.settings
# as it's requires reading users config, that's why imports nested inside the function # as it's requires reading users config, that's why imports nested inside the function
def get_report_url(self, settings, latest=False): def get_report_url(self):
import urllib import urllib
path = settings.HTML_S3_PATH from praktika.settings import Settings
for bucket, endpoint in settings.S3_BUCKET_TO_HTTP_ENDPOINT.items(): from praktika.utils import Utils
path = Settings.HTML_S3_PATH
for bucket, endpoint in Settings.S3_BUCKET_TO_HTTP_ENDPOINT.items():
if bucket in path: if bucket in path:
path = path.replace(bucket, endpoint) path = path.replace(bucket, endpoint)
break break
REPORT_URL = f"https://{path}/{Path(settings.HTML_PAGE_FILE).name}?PR={self.PR_NUMBER}&sha={'latest' if latest else self.SHA}&name_0={urllib.parse.quote(self.WORKFLOW_NAME, safe='')}&name_1={urllib.parse.quote(self.JOB_NAME, safe='')}" REPORT_URL = f"https://{path}/{Path(Settings.HTML_PAGE_FILE).name}?PR={self.PR_NUMBER}&sha={self.SHA}&name_0={urllib.parse.quote(self.WORKFLOW_NAME, safe='')}&name_1={urllib.parse.quote(self.JOB_NAME, safe='')}"
return REPORT_URL return REPORT_URL
def is_local_run(self): def is_local_run(self):

124
ci/praktika/_settings.py Normal file
View File

@ -0,0 +1,124 @@
import dataclasses
from pathlib import Path
from typing import Dict, Iterable, List, Optional
@dataclasses.dataclass
class _Settings:
######################################
# Pipeline generation settings #
######################################
CI_PATH = "./ci"
WORKFLOW_PATH_PREFIX: str = "./.github/workflows"
WORKFLOWS_DIRECTORY: str = f"{CI_PATH}/workflows"
SETTINGS_DIRECTORY: str = f"{CI_PATH}/settings"
CI_CONFIG_JOB_NAME = "Config Workflow"
DOCKER_BUILD_JOB_NAME = "Docker Builds"
FINISH_WORKFLOW_JOB_NAME = "Finish Workflow"
READY_FOR_MERGE_STATUS_NAME = "Ready for Merge"
CI_CONFIG_RUNS_ON: Optional[List[str]] = None
DOCKER_BUILD_RUNS_ON: Optional[List[str]] = None
VALIDATE_FILE_PATHS: bool = True
######################################
# Runtime Settings #
######################################
MAX_RETRIES_S3 = 3
MAX_RETRIES_GH = 3
######################################
# S3 (artifact storage) settings #
######################################
S3_ARTIFACT_PATH: str = ""
######################################
# CI workspace settings #
######################################
TEMP_DIR: str = "/tmp/praktika"
OUTPUT_DIR: str = f"{TEMP_DIR}/output"
INPUT_DIR: str = f"{TEMP_DIR}/input"
PYTHON_INTERPRETER: str = "python3"
PYTHON_PACKET_MANAGER: str = "pip3"
PYTHON_VERSION: str = "3.9"
INSTALL_PYTHON_FOR_NATIVE_JOBS: bool = False
INSTALL_PYTHON_REQS_FOR_NATIVE_JOBS: str = "./ci/requirements.txt"
ENVIRONMENT_VAR_FILE: str = f"{TEMP_DIR}/environment.json"
RUN_LOG: str = f"{TEMP_DIR}/praktika_run.log"
SECRET_GH_APP_ID: str = "GH_APP_ID"
SECRET_GH_APP_PEM_KEY: str = "GH_APP_PEM_KEY"
ENV_SETUP_SCRIPT: str = "/tmp/praktika_setup_env.sh"
WORKFLOW_STATUS_FILE: str = f"{TEMP_DIR}/workflow_status.json"
######################################
# CI Cache settings #
######################################
CACHE_VERSION: int = 1
CACHE_DIGEST_LEN: int = 20
CACHE_S3_PATH: str = ""
CACHE_LOCAL_PATH: str = f"{TEMP_DIR}/ci_cache"
######################################
# Report settings #
######################################
HTML_S3_PATH: str = ""
HTML_PAGE_FILE: str = "./praktika/json.html"
TEXT_CONTENT_EXTENSIONS: Iterable[str] = frozenset([".txt", ".log"])
S3_BUCKET_TO_HTTP_ENDPOINT: Optional[Dict[str, str]] = None
DOCKERHUB_USERNAME: str = ""
DOCKERHUB_SECRET: str = ""
DOCKER_WD: str = "/wd"
######################################
# CI DB Settings #
######################################
SECRET_CI_DB_URL: str = "CI_DB_URL"
SECRET_CI_DB_PASSWORD: str = "CI_DB_PASSWORD"
CI_DB_DB_NAME = ""
CI_DB_TABLE_NAME = ""
CI_DB_INSERT_TIMEOUT_SEC = 5
_USER_DEFINED_SETTINGS = [
"S3_ARTIFACT_PATH",
"CACHE_S3_PATH",
"HTML_S3_PATH",
"S3_BUCKET_TO_HTTP_ENDPOINT",
"TEXT_CONTENT_EXTENSIONS",
"TEMP_DIR",
"OUTPUT_DIR",
"INPUT_DIR",
"CI_CONFIG_RUNS_ON",
"DOCKER_BUILD_RUNS_ON",
"CI_CONFIG_JOB_NAME",
"PYTHON_INTERPRETER",
"PYTHON_VERSION",
"PYTHON_PACKET_MANAGER",
"INSTALL_PYTHON_FOR_NATIVE_JOBS",
"INSTALL_PYTHON_REQS_FOR_NATIVE_JOBS",
"MAX_RETRIES_S3",
"MAX_RETRIES_GH",
"VALIDATE_FILE_PATHS",
"DOCKERHUB_USERNAME",
"DOCKERHUB_SECRET",
"READY_FOR_MERGE_STATUS_NAME",
"SECRET_CI_DB_URL",
"SECRET_CI_DB_PASSWORD",
"CI_DB_DB_NAME",
"CI_DB_TABLE_NAME",
"CI_DB_INSERT_TIMEOUT_SEC",
"SECRET_GH_APP_PEM_KEY",
"SECRET_GH_APP_ID",
]
class GHRunners:
ubuntu = "ubuntu-latest"
if __name__ == "__main__":
for setting in _USER_DEFINED_SETTINGS:
print(_Settings().__getattribute__(setting))
# print(dataclasses.asdict(_Settings()))

View File

@ -52,7 +52,7 @@ class CIDB:
check_status=result.status, check_status=result.status,
check_duration_ms=int(result.duration * 1000), check_duration_ms=int(result.duration * 1000),
check_start_time=Utils.timestamp_to_str(result.start_time), check_start_time=Utils.timestamp_to_str(result.start_time),
report_url=env.get_report_url(settings=Settings), report_url=env.get_report_url(),
pull_request_url=env.CHANGE_URL, pull_request_url=env.CHANGE_URL,
base_ref=env.BASE_BRANCH, base_ref=env.BASE_BRANCH,
base_repo=env.REPOSITORY, base_repo=env.REPOSITORY,

View File

@ -23,7 +23,7 @@ class Digest:
hash_string = hash_obj.hexdigest() hash_string = hash_obj.hexdigest()
return hash_string return hash_string
def calc_job_digest(self, job_config: Job.Config, docker_digests): def calc_job_digest(self, job_config: Job.Config):
config = job_config.digest_config config = job_config.digest_config
if not config: if not config:
return "f" * Settings.CACHE_DIGEST_LEN return "f" * Settings.CACHE_DIGEST_LEN
@ -31,32 +31,32 @@ class Digest:
cache_key = self._hash_digest_config(config) cache_key = self._hash_digest_config(config)
if cache_key in self.digest_cache: if cache_key in self.digest_cache:
print( return self.digest_cache[cache_key]
f"calc digest for job [{job_config.name}]: hash_key [{cache_key}] - from cache"
) included_files = Utils.traverse_paths(
digest = self.digest_cache[cache_key] job_config.digest_config.include_paths,
job_config.digest_config.exclude_paths,
sorted=True,
)
print(
f"calc digest for job [{job_config.name}]: hash_key [{cache_key}], include [{len(included_files)}] files"
)
# Sort files to ensure consistent hash calculation
included_files.sort()
# Calculate MD5 hash
res = ""
if not included_files:
res = "f" * Settings.CACHE_DIGEST_LEN
print(f"NOTE: empty digest config [{config}] - return dummy digest")
else: else:
included_files = Utils.traverse_paths(
job_config.digest_config.include_paths,
job_config.digest_config.exclude_paths,
sorted=True,
)
print(
f"calc digest for job [{job_config.name}]: hash_key [{cache_key}], include [{len(included_files)}] files"
)
hash_md5 = hashlib.md5() hash_md5 = hashlib.md5()
for i, file_path in enumerate(included_files): for file_path in included_files:
hash_md5 = self._calc_file_digest(file_path, hash_md5) res = self._calc_file_digest(file_path, hash_md5)
digest = hash_md5.hexdigest()[: Settings.CACHE_DIGEST_LEN] assert res
self.digest_cache[cache_key] = digest self.digest_cache[cache_key] = res
return res
if job_config.run_in_docker:
# respect docker digest in the job digest
docker_digest = docker_digests[job_config.run_in_docker.split("+")[0]]
digest = "-".join([docker_digest, digest])
return digest
def calc_docker_digest( def calc_docker_digest(
self, self,
@ -103,10 +103,10 @@ class Digest:
print( print(
f"WARNING: No valid file resolved by link {file_path} -> {resolved_path} - skipping digest calculation" f"WARNING: No valid file resolved by link {file_path} -> {resolved_path} - skipping digest calculation"
) )
return hash_md5 return hash_md5.hexdigest()[: Settings.CACHE_DIGEST_LEN]
with open(resolved_path, "rb") as f: with open(resolved_path, "rb") as f:
for chunk in iter(lambda: f.read(4096), b""): for chunk in iter(lambda: f.read(4096), b""):
hash_md5.update(chunk) hash_md5.update(chunk)
return hash_md5 return hash_md5.hexdigest()[: Settings.CACHE_DIGEST_LEN]

View File

@ -0,0 +1,3 @@
from praktika._environment import _Environment
Environment = _Environment.get()

View File

@ -18,7 +18,7 @@ class GH:
ret_code, out, err = Shell.get_res_stdout_stderr(command, verbose=True) ret_code, out, err = Shell.get_res_stdout_stderr(command, verbose=True)
res = ret_code == 0 res = ret_code == 0
if not res and "Validation Failed" in err: if not res and "Validation Failed" in err:
print(f"ERROR: GH command validation error.") print("ERROR: GH command validation error")
break break
if not res and "Bad credentials" in err: if not res and "Bad credentials" in err:
print("ERROR: GH credentials/auth failure") print("ERROR: GH credentials/auth failure")

View File

@ -1,5 +1,6 @@
from praktika._environment import _Environment from praktika._environment import _Environment
from praktika.cache import Cache from praktika.cache import Cache
from praktika.mangle import _get_workflows
from praktika.runtime import RunConfig from praktika.runtime import RunConfig
from praktika.settings import Settings from praktika.settings import Settings
from praktika.utils import Utils from praktika.utils import Utils
@ -7,10 +8,11 @@ from praktika.utils import Utils
class CacheRunnerHooks: class CacheRunnerHooks:
@classmethod @classmethod
def configure(cls, workflow): def configure(cls, _workflow):
workflow_config = RunConfig.from_fs(workflow.name) workflow_config = RunConfig.from_fs(_workflow.name)
docker_digests = workflow_config.digest_dockers
cache = Cache() cache = Cache()
assert _Environment.get().WORKFLOW_NAME
workflow = _get_workflows(name=_Environment.get().WORKFLOW_NAME)[0]
print(f"Workflow Configure, workflow [{workflow.name}]") print(f"Workflow Configure, workflow [{workflow.name}]")
assert ( assert (
workflow.enable_cache workflow.enable_cache
@ -18,13 +20,11 @@ class CacheRunnerHooks:
artifact_digest_map = {} artifact_digest_map = {}
job_digest_map = {} job_digest_map = {}
for job in workflow.jobs: for job in workflow.jobs:
digest = cache.digest.calc_job_digest(
job_config=job, docker_digests=docker_digests
)
if not job.digest_config: if not job.digest_config:
print( print(
f"NOTE: job [{job.name}] has no Config.digest_config - skip cache check, always run" f"NOTE: job [{job.name}] has no Config.digest_config - skip cache check, always run"
) )
digest = cache.digest.calc_job_digest(job_config=job)
job_digest_map[job.name] = digest job_digest_map[job.name] = digest
if job.provides: if job.provides:
# assign the job digest also to the artifacts it provides # assign the job digest also to the artifacts it provides
@ -50,6 +50,7 @@ class CacheRunnerHooks:
), f"BUG, Workflow with enabled cache must have job digests after configuration, wf [{workflow.name}]" ), f"BUG, Workflow with enabled cache must have job digests after configuration, wf [{workflow.name}]"
print("Check remote cache") print("Check remote cache")
job_to_cache_record = {}
for job_name, job_digest in workflow_config.digest_jobs.items(): for job_name, job_digest in workflow_config.digest_jobs.items():
record = cache.fetch_success(job_name=job_name, job_digest=job_digest) record = cache.fetch_success(job_name=job_name, job_digest=job_digest)
if record: if record:
@ -59,7 +60,7 @@ class CacheRunnerHooks:
) )
workflow_config.cache_success.append(job_name) workflow_config.cache_success.append(job_name)
workflow_config.cache_success_base64.append(Utils.to_base64(job_name)) workflow_config.cache_success_base64.append(Utils.to_base64(job_name))
workflow_config.cache_jobs[job_name] = record job_to_cache_record[job_name] = record
print("Check artifacts to reuse") print("Check artifacts to reuse")
for job in workflow.jobs: for job in workflow.jobs:
@ -67,7 +68,7 @@ class CacheRunnerHooks:
if job.provides: if job.provides:
for artifact_name in job.provides: for artifact_name in job.provides:
workflow_config.cache_artifacts[artifact_name] = ( workflow_config.cache_artifacts[artifact_name] = (
workflow_config.cache_jobs[job.name] job_to_cache_record[job.name]
) )
print(f"Write config to GH's job output") print(f"Write config to GH's job output")

View File

@ -1,125 +1,63 @@
import dataclasses import dataclasses
import json import json
import urllib.parse
from pathlib import Path from pathlib import Path
from typing import List from typing import List
from praktika._environment import _Environment from praktika._environment import _Environment
from praktika.gh import GH from praktika.gh import GH
from praktika.parser import WorkflowConfigParser from praktika.parser import WorkflowConfigParser
from praktika.result import Result, ResultInfo, _ResultS3 from praktika.result import Result, ResultInfo
from praktika.runtime import RunConfig from praktika.runtime import RunConfig
from praktika.s3 import S3 from praktika.s3 import S3
from praktika.settings import Settings from praktika.settings import Settings
from praktika.utils import Utils from praktika.utils import Shell, Utils
@dataclasses.dataclass @dataclasses.dataclass
class GitCommit: class GitCommit:
# date: str date: str
# message: str message: str
sha: str sha: str
@staticmethod @staticmethod
def from_json(file) -> List["GitCommit"]: def from_json(json_data: str) -> List["GitCommit"]:
commits = [] commits = []
json_data = None
try: try:
with open(file, "r", encoding="utf-8") as f: data = json.loads(json_data)
json_data = json.load(f)
commits = [ commits = [
GitCommit( GitCommit(
# message=commit["messageHeadline"], message=commit["messageHeadline"],
sha=commit["sha"], sha=commit["oid"],
# date=commit["committedDate"], date=commit["committedDate"],
) )
for commit in json_data for commit in data.get("commits", [])
] ]
except Exception as e: except Exception as e:
print( print(
f"ERROR: Failed to deserialize commit's data [{json_data}], ex: [{e}]" f"ERROR: Failed to deserialize commit's data: [{json_data}], ex: [{e}]"
) )
return commits return commits
@classmethod
def update_s3_data(cls):
env = _Environment.get()
sha = env.SHA
if not sha:
print("WARNING: Failed to retrieve commit sha")
return
commits = cls.pull_from_s3()
for commit in commits:
if sha == commit.sha:
print(
f"INFO: Sha already present in commits data [{sha}] - skip data update"
)
return
commits.append(GitCommit(sha=sha))
cls.push_to_s3(commits)
return
@classmethod
def dump(cls, commits):
commits_ = []
for commit in commits:
commits_.append(dataclasses.asdict(commit))
with open(cls.file_name(), "w", encoding="utf8") as f:
json.dump(commits_, f)
@classmethod
def pull_from_s3(cls):
local_path = Path(cls.file_name())
file_name = local_path.name
env = _Environment.get()
s3_path = f"{Settings.HTML_S3_PATH}/{cls.get_s3_prefix(pr_number=env.PR_NUMBER, branch=env.BRANCH)}/{file_name}"
if not S3.copy_file_from_s3(s3_path=s3_path, local_path=local_path):
print(f"WARNING: failed to cp file [{s3_path}] from s3")
return []
return cls.from_json(local_path)
@classmethod
def push_to_s3(cls, commits):
print(f"INFO: push commits data to s3, commits num [{len(commits)}]")
cls.dump(commits)
local_path = Path(cls.file_name())
file_name = local_path.name
env = _Environment.get()
s3_path = f"{Settings.HTML_S3_PATH}/{cls.get_s3_prefix(pr_number=env.PR_NUMBER, branch=env.BRANCH)}/{file_name}"
if not S3.copy_file_to_s3(s3_path=s3_path, local_path=local_path, text=True):
print(f"WARNING: failed to cp file [{local_path}] to s3")
@classmethod
def get_s3_prefix(cls, pr_number, branch):
prefix = ""
assert pr_number or branch
if pr_number and pr_number > 0:
prefix += f"{pr_number}"
else:
prefix += f"{branch}"
return prefix
@classmethod
def file_name(cls):
return f"{Settings.TEMP_DIR}/commits.json"
# def _get_pr_commits(pr_number):
# res = []
# if not pr_number:
# return res
# output = Shell.get_output(f"gh pr view {pr_number} --json commits")
# if output:
# res = GitCommit.from_json(output)
# return res
class HtmlRunnerHooks: class HtmlRunnerHooks:
@classmethod @classmethod
def configure(cls, _workflow): def configure(cls, _workflow):
def _get_pr_commits(pr_number):
res = []
if not pr_number:
return res
output = Shell.get_output(f"gh pr view {pr_number} --json commits")
if output:
res = GitCommit.from_json(output)
return res
# generate pending Results for all jobs in the workflow # generate pending Results for all jobs in the workflow
if _workflow.enable_cache: if _workflow.enable_cache:
skip_jobs = RunConfig.from_fs(_workflow.name).cache_success skip_jobs = RunConfig.from_fs(_workflow.name).cache_success
job_cache_records = RunConfig.from_fs(_workflow.name).cache_jobs
else: else:
skip_jobs = [] skip_jobs = []
@ -129,22 +67,36 @@ class HtmlRunnerHooks:
if job.name not in skip_jobs: if job.name not in skip_jobs:
result = Result.generate_pending(job.name) result = Result.generate_pending(job.name)
else: else:
result = Result.generate_skipped(job.name, job_cache_records[job.name]) result = Result.generate_skipped(job.name)
results.append(result) results.append(result)
summary_result = Result.generate_pending(_workflow.name, results=results) summary_result = Result.generate_pending(_workflow.name, results=results)
summary_result.links.append(env.CHANGE_URL) summary_result.aux_links.append(env.CHANGE_URL)
summary_result.links.append(env.RUN_URL) summary_result.aux_links.append(env.RUN_URL)
summary_result.start_time = Utils.timestamp() summary_result.start_time = Utils.timestamp()
page_url = "/".join(
["https:/", Settings.HTML_S3_PATH, str(Path(Settings.HTML_PAGE_FILE).name)]
)
for bucket, endpoint in Settings.S3_BUCKET_TO_HTTP_ENDPOINT.items():
page_url = page_url.replace(bucket, endpoint)
# TODO: add support for non-PRs (use branch?)
page_url += f"?PR={env.PR_NUMBER}&sha=latest&name_0={urllib.parse.quote(env.WORKFLOW_NAME, safe='')}"
summary_result.html_link = page_url
# clean the previous latest results in PR if any
if env.PR_NUMBER:
S3.clean_latest_result()
S3.copy_result_to_s3(
summary_result,
unlock=False,
)
assert _ResultS3.copy_result_to_s3_with_version(summary_result, version=0)
page_url = env.get_report_url(settings=Settings)
print(f"CI Status page url [{page_url}]") print(f"CI Status page url [{page_url}]")
res1 = GH.post_commit_status( res1 = GH.post_commit_status(
name=_workflow.name, name=_workflow.name,
status=Result.Status.PENDING, status=Result.Status.PENDING,
description="", description="",
url=env.get_report_url(settings=Settings, latest=True), url=page_url,
) )
res2 = GH.post_pr_comment( res2 = GH.post_pr_comment(
comment_body=f"Workflow [[{_workflow.name}]({page_url})], commit [{_Environment.get().SHA[:8]}]", comment_body=f"Workflow [[{_workflow.name}]({page_url})], commit [{_Environment.get().SHA[:8]}]",
@ -154,15 +106,23 @@ class HtmlRunnerHooks:
Utils.raise_with_error( Utils.raise_with_error(
"Failed to set both GH commit status and PR comment with Workflow Status, cannot proceed" "Failed to set both GH commit status and PR comment with Workflow Status, cannot proceed"
) )
if env.PR_NUMBER: if env.PR_NUMBER:
# TODO: enable for branch, add commit number limiting commits = _get_pr_commits(env.PR_NUMBER)
GitCommit.update_s3_data() # TODO: upload commits data to s3 to visualise it on a report page
print(commits)
@classmethod @classmethod
def pre_run(cls, _workflow, _job): def pre_run(cls, _workflow, _job):
result = Result.from_fs(_job.name) result = Result.from_fs(_job.name)
_ResultS3.update_workflow_results( S3.copy_result_from_s3(
workflow_name=_workflow.name, new_sub_results=result Result.file_name_static(_workflow.name),
)
workflow_result = Result.from_fs(_workflow.name)
workflow_result.update_sub_result(result)
S3.copy_result_to_s3(
workflow_result,
unlock=True,
) )
@classmethod @classmethod
@ -172,13 +132,14 @@ class HtmlRunnerHooks:
@classmethod @classmethod
def post_run(cls, _workflow, _job, info_errors): def post_run(cls, _workflow, _job, info_errors):
result = Result.from_fs(_job.name) result = Result.from_fs(_job.name)
_ResultS3.upload_result_files_to_s3(result)
_ResultS3.copy_result_to_s3(result)
env = _Environment.get() env = _Environment.get()
S3.copy_result_from_s3(
Result.file_name_static(_workflow.name),
lock=True,
)
workflow_result = Result.from_fs(_workflow.name)
print(f"Workflow info [{workflow_result.info}], info_errors [{info_errors}]")
new_sub_results = [result]
new_result_info = ""
env_info = env.REPORT_INFO env_info = env.REPORT_INFO
if env_info: if env_info:
print( print(
@ -190,8 +151,14 @@ class HtmlRunnerHooks:
info_str = f"{_job.name}:\n" info_str = f"{_job.name}:\n"
info_str += "\n".join(info_errors) info_str += "\n".join(info_errors)
print("Update workflow results with new info") print("Update workflow results with new info")
new_result_info = info_str workflow_result.set_info(info_str)
old_status = workflow_result.status
S3.upload_result_files_to_s3(result)
workflow_result.update_sub_result(result)
skipped_job_results = []
if not result.is_ok(): if not result.is_ok():
print( print(
"Current job failed - find dependee jobs in the workflow and set their statuses to skipped" "Current job failed - find dependee jobs in the workflow and set their statuses to skipped"
@ -204,7 +171,7 @@ class HtmlRunnerHooks:
print( print(
f"NOTE: Set job [{dependee_job.name}] status to [{Result.Status.SKIPPED}] due to current failure" f"NOTE: Set job [{dependee_job.name}] status to [{Result.Status.SKIPPED}] due to current failure"
) )
new_sub_results.append( skipped_job_results.append(
Result( Result(
name=dependee_job.name, name=dependee_job.name,
status=Result.Status.SKIPPED, status=Result.Status.SKIPPED,
@ -212,18 +179,20 @@ class HtmlRunnerHooks:
+ f" [{_job.name}]", + f" [{_job.name}]",
) )
) )
for skipped_job_result in skipped_job_results:
workflow_result.update_sub_result(skipped_job_result)
updated_status = _ResultS3.update_workflow_results( S3.copy_result_to_s3(
new_info=new_result_info, workflow_result,
new_sub_results=new_sub_results, unlock=True,
workflow_name=_workflow.name,
) )
if workflow_result.status != old_status:
if updated_status: print(
print(f"Update GH commit status [{result.name}]: [{updated_status}]") f"Update GH commit status [{result.name}]: [{old_status} -> {workflow_result.status}], link [{workflow_result.html_link}]"
GH.post_commit_status( )
name=_workflow.name, GH.post_commit_status(
status=GH.convert_to_gh_status(updated_status), name=workflow_result.name,
description="", status=GH.convert_to_gh_status(workflow_result.status),
url=env.get_report_url(settings=Settings, latest=True), description="",
url=workflow_result.html_link,
) )

View File

@ -52,58 +52,30 @@ class Job:
self, self,
parameter: Optional[List[Any]] = None, parameter: Optional[List[Any]] = None,
runs_on: Optional[List[List[str]]] = None, runs_on: Optional[List[List[str]]] = None,
provides: Optional[List[List[str]]] = None,
requires: Optional[List[List[str]]] = None,
timeout: Optional[List[int]] = None, timeout: Optional[List[int]] = None,
): ):
assert ( assert (
parameter or runs_on parameter or runs_on
), "Either :parameter or :runs_on must be non empty list for parametrisation" ), "Either :parameter or :runs_on must be non empty list for parametrisation"
if runs_on:
assert isinstance(runs_on, list) and isinstance(runs_on[0], list)
if not parameter: if not parameter:
parameter = [None] * len(runs_on) parameter = [None] * len(runs_on)
if not runs_on: if not runs_on:
runs_on = [None] * len(parameter) runs_on = [None] * len(parameter)
if not timeout: if not timeout:
timeout = [None] * len(parameter) timeout = [None] * len(parameter)
if not provides:
provides = [None] * len(parameter)
if not requires:
requires = [None] * len(parameter)
assert ( assert (
len(parameter) len(parameter) == len(runs_on) == len(timeout)
== len(runs_on) ), "Parametrization lists must be of the same size"
== len(timeout)
== len(provides)
== len(requires)
), f"Parametrization lists must be of the same size [{len(parameter)}, {len(runs_on)}, {len(timeout)}, {len(provides)}, {len(requires)}]"
res = [] res = []
for parameter_, runs_on_, timeout_, provides_, requires_ in zip( for parameter_, runs_on_, timeout_ in zip(parameter, runs_on, timeout):
parameter, runs_on, timeout, provides, requires
):
obj = copy.deepcopy(self) obj = copy.deepcopy(self)
assert (
not obj.provides
), "Job.Config.provides must be empty for parametrized jobs"
if parameter_: if parameter_:
obj.parameter = parameter_ obj.parameter = parameter_
obj.command = obj.command.format(PARAMETER=parameter_)
if runs_on_: if runs_on_:
obj.runs_on = runs_on_ obj.runs_on = runs_on_
if timeout_: if timeout_:
obj.timeout = timeout_ obj.timeout = timeout_
if provides_:
assert (
not obj.provides
), "Job.Config.provides must be empty for parametrized jobs"
obj.provides = provides_
if requires_:
assert (
not obj.requires
), "Job.Config.requires and parametrize(requires=...) are both set"
obj.requires = requires_
obj.name = obj.get_job_name_with_parameter() obj.name = obj.get_job_name_with_parameter()
res.append(obj) res.append(obj)
return res return res
@ -112,16 +84,13 @@ class Job:
name, parameter, runs_on = self.name, self.parameter, self.runs_on name, parameter, runs_on = self.name, self.parameter, self.runs_on
res = name res = name
name_params = [] name_params = []
if parameter: if isinstance(parameter, list) or isinstance(parameter, dict):
if isinstance(parameter, list) or isinstance(parameter, dict): name_params.append(json.dumps(parameter))
name_params.append(json.dumps(parameter)) elif parameter is not None:
else: name_params.append(parameter)
name_params.append(parameter) if runs_on:
elif runs_on:
assert isinstance(runs_on, list) assert isinstance(runs_on, list)
name_params.append(json.dumps(runs_on)) name_params.append(json.dumps(runs_on))
else:
assert False
if name_params: if name_params:
name_params = [str(param) for param in name_params] name_params = [str(param) for param in name_params]
res += f" ({', '.join(name_params)})" res += f" ({', '.join(name_params)})"

View File

@ -89,27 +89,15 @@
letter-spacing: -0.5px; letter-spacing: -0.5px;
} }
.dropdown-value {
width: 100px;
font-weight: normal;
font-family: inherit;
background-color: transparent;
color: inherit;
/*border: none;*/
/*outline: none;*/
/*cursor: pointer;*/
}
#result-container { #result-container {
background-color: var(--tile-background); background-color: var(--tile-background);
margin-left: calc(var(--status-width) + 20px); margin-left: calc(var(--status-width) + 20px);
padding: 0; padding: 20px;
box-sizing: border-box; box-sizing: border-box;
text-align: center; text-align: center;
font-size: 18px; font-size: 18px;
font-weight: normal; font-weight: normal;
flex-grow: 1; flex-grow: 1;
margin-bottom: 40px;
} }
#footer { #footer {
@ -201,7 +189,10 @@
} }
th.name-column, td.name-column { th.name-column, td.name-column {
min-width: 350px; max-width: 400px; /* Set the maximum width for the column */
white-space: nowrap; /* Prevent text from wrapping */
overflow: hidden; /* Hide the overflowed text */
text-overflow: ellipsis; /* Show ellipsis (...) for overflowed text */
} }
th.status-column, td.status-column { th.status-column, td.status-column {
@ -291,12 +282,6 @@
} }
} }
function updateUrlParameter(paramName, paramValue) {
const url = new URL(window.location.href);
url.searchParams.set(paramName, paramValue);
window.location.href = url.toString();
}
// Attach the toggle function to the click event of the icon // Attach the toggle function to the click event of the icon
document.getElementById('theme-toggle').addEventListener('click', toggleTheme); document.getElementById('theme-toggle').addEventListener('click', toggleTheme);
@ -306,14 +291,14 @@
const monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", const monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
const month = monthNames[date.getMonth()]; const month = monthNames[date.getMonth()];
//const year = date.getFullYear(); const year = date.getFullYear();
const hours = String(date.getHours()).padStart(2, '0'); const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0'); const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0'); const seconds = String(date.getSeconds()).padStart(2, '0');
//const milliseconds = String(date.getMilliseconds()).padStart(2, '0'); //const milliseconds = String(date.getMilliseconds()).padStart(2, '0');
return showDate return showDate
? `${day}'${month} ${hours}:${minutes}:${seconds}` ? `${day}-${month}-${year} ${hours}:${minutes}:${seconds}`
: `${hours}:${minutes}:${seconds}`; : `${hours}:${minutes}:${seconds}`;
} }
@ -343,7 +328,7 @@
const milliseconds = Math.floor((duration % 1) * 1000); const milliseconds = Math.floor((duration % 1) * 1000);
const formattedSeconds = String(seconds); const formattedSeconds = String(seconds);
const formattedMilliseconds = String(milliseconds).padStart(2, '0').slice(-2); const formattedMilliseconds = String(milliseconds).padStart(3, '0');
return `${formattedSeconds}.${formattedMilliseconds}`; return `${formattedSeconds}.${formattedMilliseconds}`;
} }
@ -361,7 +346,8 @@
return 'status-other'; return 'status-other';
} }
function addKeyValueToStatus(key, value, options = null) { function addKeyValueToStatus(key, value) {
const statusContainer = document.getElementById('status-container'); const statusContainer = document.getElementById('status-container');
let keyValuePair = document.createElement('div'); let keyValuePair = document.createElement('div');
@ -371,40 +357,12 @@
keyElement.className = 'json-key'; keyElement.className = 'json-key';
keyElement.textContent = key + ':'; keyElement.textContent = key + ':';
let valueElement; const valueElement = document.createElement('div');
valueElement.className = 'json-value';
valueElement.textContent = value;
if (options) { keyValuePair.appendChild(keyElement)
// Create dropdown if options are provided keyValuePair.appendChild(valueElement)
valueElement = document.createElement('select');
valueElement.className = 'dropdown-value';
options.forEach(optionValue => {
const option = document.createElement('option');
option.value = optionValue;
option.textContent = optionValue.slice(0, 10);
// Set the initially selected option
if (optionValue === value) {
option.selected = true;
}
valueElement.appendChild(option);
});
// Update the URL parameter when the selected value changes
valueElement.addEventListener('change', (event) => {
const selectedValue = event.target.value;
updateUrlParameter(key, selectedValue);
});
} else {
// Create a simple text display if no options are provided
valueElement = document.createElement('div');
valueElement.className = 'json-value';
valueElement.textContent = value || 'N/A'; // Display 'N/A' if value is null
}
keyValuePair.appendChild(keyElement);
keyValuePair.appendChild(valueElement);
statusContainer.appendChild(keyValuePair); statusContainer.appendChild(keyValuePair);
} }
@ -528,12 +486,12 @@
const columns = ['name', 'status', 'start_time', 'duration', 'info']; const columns = ['name', 'status', 'start_time', 'duration', 'info'];
const columnSymbols = { const columnSymbols = {
name: '🗂️', name: '📂',
status: '🧾', status: '✔️',
start_time: '🕒', start_time: '🕒',
duration: '⏳', duration: '⏳',
info: '📝', info: '',
files: '📎' files: '📄'
}; };
function createResultsTable(results, nest_level) { function createResultsTable(results, nest_level) {
@ -542,14 +500,16 @@
const thead = document.createElement('thead'); const thead = document.createElement('thead');
const tbody = document.createElement('tbody'); const tbody = document.createElement('tbody');
// Get the current URL parameters
const currentUrl = new URL(window.location.href);
// Create table headers based on the fixed columns // Create table headers based on the fixed columns
const headerRow = document.createElement('tr'); const headerRow = document.createElement('tr');
columns.forEach(column => { columns.forEach(column => {
const th = document.createElement('th'); const th = document.createElement('th');
th.textContent = columnSymbols[column] || column; th.textContent = th.textContent = columnSymbols[column] || column;
th.style.cursor = 'pointer'; // Make headers clickable th.style.cursor = 'pointer'; // Make headers clickable
th.setAttribute('data-sort-direction', 'asc'); // Default sort direction th.addEventListener('click', () => sortTable(results, column, tbody, nest_level)); // Add click event to sort the table
th.addEventListener('click', () => sortTable(results, column, columnSymbols[column] || column, tbody, nest_level, columns)); // Add click event to sort the table
headerRow.appendChild(th); headerRow.appendChild(th);
}); });
thead.appendChild(headerRow); thead.appendChild(headerRow);
@ -601,7 +561,8 @@
td.classList.add('time-column'); td.classList.add('time-column');
td.textContent = value ? formatDuration(value) : ''; td.textContent = value ? formatDuration(value) : '';
} else if (column === 'info') { } else if (column === 'info') {
td.textContent = value.includes('\n') ? '↵' : (value || ''); // For info and other columns, just display the value
td.textContent = value || '';
td.classList.add('info-column'); td.classList.add('info-column');
} }
@ -612,33 +573,39 @@
}); });
} }
function sortTable(results, column, key, tbody, nest_level, columns) { function sortTable(results, key, tbody, nest_level) {
// Find the table header element for the given key // Find the table header element for the given key
const tableHeaders = document.querySelectorAll('th'); let th = null;
let th = Array.from(tableHeaders).find(header => header.textContent === key); const tableHeaders = document.querySelectorAll('th'); // Select all table headers
tableHeaders.forEach(header => {
if (header.textContent.trim().toLowerCase() === key.toLowerCase()) {
th = header;
}
});
if (!th) { if (!th) {
console.error(`No table header found for key: ${key}`); console.error(`No table header found for key: ${key}`);
return; return;
} }
const ascending = th.getAttribute('data-sort-direction') === 'asc'; // Determine the current sort direction
th.setAttribute('data-sort-direction', ascending ? 'desc' : 'asc'); let ascending = th.getAttribute('data-sort-direction') === 'asc' ? false : true;
// Toggle the sort direction for the next click
th.setAttribute('data-sort-direction', ascending ? 'asc' : 'desc');
// Sort the results array by the given key
results.sort((a, b) => { results.sort((a, b) => {
if (a[column] < b[column]) return ascending ? -1 : 1; if (a[key] < b[key]) return ascending ? -1 : 1;
if (a[column] > b[column]) return ascending ? 1 : -1; if (a[key] > b[key]) return ascending ? 1 : -1;
return 0; return 0;
}); });
// Clear the existing rows in tbody
tbody.innerHTML = '';
// Re-populate the table with sorted data // Re-populate the table with sorted data
populateTableRows(tbody, results, columns, nest_level); populateTableRows(tbody, results, columns, nest_level);
} }
function loadResultsJSON(PR, sha, nameParams) { function loadJSON(PR, sha, nameParams) {
const infoElement = document.getElementById('info-container'); const infoElement = document.getElementById('info-container');
let lastModifiedTime = null; let lastModifiedTime = null;
const task = nameParams[0].toLowerCase(); const task = nameParams[0].toLowerCase();
@ -663,20 +630,19 @@
let targetData = navigatePath(data, nameParams); let targetData = navigatePath(data, nameParams);
let nest_level = nameParams.length; let nest_level = nameParams.length;
// Add footer links from top-level Result
if (Array.isArray(data.links) && data.links.length > 0) {
data.links.forEach(link => {
const a = document.createElement('a');
a.href = link;
a.textContent = link.split('/').pop();
a.target = '_blank';
footerRight.appendChild(a);
});
}
if (targetData) { if (targetData) {
//infoElement.style.display = 'none'; infoElement.style.display = 'none';
infoElement.innerHTML = (targetData.info || '').replace(/\n/g, '<br>');
// Handle footer links if present
if (Array.isArray(data.aux_links) && data.aux_links.length > 0) {
data.aux_links.forEach(link => {
const a = document.createElement('a');
a.href = link;
a.textContent = link.split('/').pop();
a.target = '_blank';
footerRight.appendChild(a);
});
}
addStatusToStatus(targetData.status, targetData.start_time, targetData.duration) addStatusToStatus(targetData.status, targetData.start_time, targetData.duration)
@ -755,62 +721,22 @@
} }
}); });
let path_commits_json = '';
let commitsArray = [];
if (PR) { if (PR) {
addKeyValueToStatus("PR", PR); addKeyValueToStatus("PR", PR)
const baseUrl = window.location.origin + window.location.pathname.replace('/json.html', '');
path_commits_json = `${baseUrl}/${encodeURIComponent(PR)}/commits.json`;
} else { } else {
// Placeholder for a different path when PR is missing console.error("TODO")
console.error("PR parameter is missing. Setting alternate commits path.");
path_commits_json = '/path/to/alternative/commits.json';
} }
addKeyValueToStatus("sha", sha);
function loadCommitsArray(path) { if (nameParams[1]) {
return fetch(path, { cache: "no-cache" }) addKeyValueToStatus("job", nameParams[1]);
.then(response => {
if (!response.ok) {
console.error(`HTTP error! status: ${response.status}`)
return [];
}
return response.json();
})
.then(data => {
if (Array.isArray(data) && data.every(item => typeof item === 'object' && item.hasOwnProperty('sha'))) {
return data.map(item => item.sha);
} else {
throw new Error('Invalid data format: expected array of objects with a "sha" key');
}
})
.catch(error => {
console.error('Error loading commits JSON:', error);
return []; // Return an empty array if an error occurs
});
} }
addKeyValueToStatus("workflow", nameParams[0]);
loadCommitsArray(path_commits_json) if (PR && sha && root_name) {
.then(data => { loadJSON(PR, sha, nameParams);
commitsArray = data; } else {
}) document.getElementById('title').textContent = 'Error: Missing required URL parameters: PR, sha, or name_0';
.finally(() => { }
// Proceed with the rest of the initialization
addKeyValueToStatus("sha", sha || "latest", commitsArray.concat(["latest"]));
if (nameParams[1]) {
addKeyValueToStatus("job", nameParams[1]);
}
addKeyValueToStatus("workflow", nameParams[0]);
// Check if all required parameters are present to load JSON
if (PR && sha && root_name) {
const shaToLoad = (sha === 'latest') ? commitsArray[commitsArray.length - 1] : sha;
loadResultsJSON(PR, shaToLoad, nameParams);
} else {
document.getElementById('title').textContent = 'Error: Missing required URL parameters: PR, sha, or name_0';
}
});
} }
window.onload = init; window.onload = init;

View File

@ -1,10 +1,11 @@
import copy import copy
import importlib.util import importlib.util
from pathlib import Path from pathlib import Path
from typing import Any, Dict
from praktika import Job from praktika import Job
from praktika.settings import Settings from praktika._settings import _USER_DEFINED_SETTINGS, _Settings
from praktika.utils import Utils from praktika.utils import ContextManager, Utils
def _get_workflows(name=None, file=None): def _get_workflows(name=None, file=None):
@ -13,34 +14,35 @@ def _get_workflows(name=None, file=None):
""" """
res = [] res = []
directory = Path(Settings.WORKFLOWS_DIRECTORY) with ContextManager.cd():
for py_file in directory.glob("*.py"): directory = Path(_Settings.WORKFLOWS_DIRECTORY)
if file and file not in str(py_file): for py_file in directory.glob("*.py"):
continue if file and file not in str(py_file):
module_name = py_file.name.removeprefix(".py") continue
spec = importlib.util.spec_from_file_location( module_name = py_file.name.removeprefix(".py")
module_name, f"{Settings.WORKFLOWS_DIRECTORY}/{module_name}" spec = importlib.util.spec_from_file_location(
) module_name, f"{_Settings.WORKFLOWS_DIRECTORY}/{module_name}"
assert spec
foo = importlib.util.module_from_spec(spec)
assert spec.loader
spec.loader.exec_module(foo)
try:
for workflow in foo.WORKFLOWS:
if name:
if name == workflow.name:
print(f"Read workflow [{name}] config from [{module_name}]")
res = [workflow]
break
else:
continue
else:
res += foo.WORKFLOWS
print(f"Read workflow configs from [{module_name}]")
except Exception as e:
print(
f"WARNING: Failed to add WORKFLOWS config from [{module_name}], exception [{e}]"
) )
assert spec
foo = importlib.util.module_from_spec(spec)
assert spec.loader
spec.loader.exec_module(foo)
try:
for workflow in foo.WORKFLOWS:
if name:
if name == workflow.name:
print(f"Read workflow [{name}] config from [{module_name}]")
res = [workflow]
break
else:
continue
else:
res += foo.WORKFLOWS
print(f"Read workflow configs from [{module_name}]")
except Exception as e:
print(
f"WARNING: Failed to add WORKFLOWS config from [{module_name}], exception [{e}]"
)
if not res: if not res:
Utils.raise_with_error(f"Failed to find workflow [{name or file}]") Utils.raise_with_error(f"Failed to find workflow [{name or file}]")
@ -56,6 +58,7 @@ def _update_workflow_artifacts(workflow):
artifact_job = {} artifact_job = {}
for job in workflow.jobs: for job in workflow.jobs:
for artifact_name in job.provides: for artifact_name in job.provides:
assert artifact_name not in artifact_job
artifact_job[artifact_name] = job.name artifact_job[artifact_name] = job.name
for artifact in workflow.artifacts: for artifact in workflow.artifacts:
artifact._provided_by = artifact_job[artifact.name] artifact._provided_by = artifact_job[artifact.name]
@ -105,3 +108,30 @@ def _update_workflow_with_native_jobs(workflow):
for job in workflow.jobs: for job in workflow.jobs:
aux_job.requires.append(job.name) aux_job.requires.append(job.name)
workflow.jobs.append(aux_job) workflow.jobs.append(aux_job)
def _get_user_settings() -> Dict[str, Any]:
"""
Gets user's settings
"""
res = {} # type: Dict[str, Any]
directory = Path(_Settings.SETTINGS_DIRECTORY)
for py_file in directory.glob("*.py"):
module_name = py_file.name.removeprefix(".py")
spec = importlib.util.spec_from_file_location(
module_name, f"{_Settings.SETTINGS_DIRECTORY}/{module_name}"
)
assert spec
foo = importlib.util.module_from_spec(spec)
assert spec.loader
spec.loader.exec_module(foo)
for setting in _USER_DEFINED_SETTINGS:
try:
value = getattr(foo, setting)
res[setting] = value
print(f"Apply user defined setting [{setting} = {value}]")
except Exception as e:
pass
return res

View File

@ -10,8 +10,9 @@ from praktika.gh import GH
from praktika.hook_cache import CacheRunnerHooks from praktika.hook_cache import CacheRunnerHooks
from praktika.hook_html import HtmlRunnerHooks from praktika.hook_html import HtmlRunnerHooks
from praktika.mangle import _get_workflows from praktika.mangle import _get_workflows
from praktika.result import Result, ResultInfo, _ResultS3 from praktika.result import Result, ResultInfo
from praktika.runtime import RunConfig from praktika.runtime import RunConfig
from praktika.s3 import S3
from praktika.settings import Settings from praktika.settings import Settings
from praktika.utils import Shell, Utils from praktika.utils import Shell, Utils
@ -150,7 +151,7 @@ def _config_workflow(workflow: Workflow.Config, job_name):
status = Result.Status.ERROR status = Result.Status.ERROR
print("ERROR: ", info) print("ERROR: ", info)
else: else:
assert Shell.check(f"{Settings.PYTHON_INTERPRETER} -m praktika yaml") Shell.check(f"{Settings.PYTHON_INTERPRETER} -m praktika --generate")
exit_code, output, err = Shell.get_res_stdout_stderr( exit_code, output, err = Shell.get_res_stdout_stderr(
f"git diff-index HEAD -- {Settings.WORKFLOW_PATH_PREFIX}" f"git diff-index HEAD -- {Settings.WORKFLOW_PATH_PREFIX}"
) )
@ -224,7 +225,6 @@ def _config_workflow(workflow: Workflow.Config, job_name):
cache_success=[], cache_success=[],
cache_success_base64=[], cache_success_base64=[],
cache_artifacts={}, cache_artifacts={},
cache_jobs={},
).dump() ).dump()
# checks: # checks:
@ -250,9 +250,6 @@ def _config_workflow(workflow: Workflow.Config, job_name):
info_lines.append(job_name + ": " + info) info_lines.append(job_name + ": " + info)
results.append(result_) results.append(result_)
if workflow.enable_merge_commit:
assert False, "NOT implemented"
# config: # config:
if workflow.dockers: if workflow.dockers:
print("Calculate docker's digests") print("Calculate docker's digests")
@ -310,8 +307,9 @@ def _finish_workflow(workflow, job_name):
print(env.get_needs_statuses()) print(env.get_needs_statuses())
print("Check Workflow results") print("Check Workflow results")
_ResultS3.copy_result_from_s3( S3.copy_result_from_s3(
Result.file_name_static(workflow.name), Result.file_name_static(workflow.name),
lock=False,
) )
workflow_result = Result.from_fs(workflow.name) workflow_result = Result.from_fs(workflow.name)
@ -341,12 +339,10 @@ def _finish_workflow(workflow, job_name):
f"NOTE: Result for [{result.name}] has not ok status [{result.status}]" f"NOTE: Result for [{result.name}] has not ok status [{result.status}]"
) )
ready_for_merge_status = Result.Status.FAILED ready_for_merge_status = Result.Status.FAILED
failed_results.append(result.name) failed_results.append(result.name.split("(", maxsplit=1)[0]) # cut name
if failed_results: if failed_results:
ready_for_merge_description = ( ready_for_merge_description = f"failed: {', '.join(failed_results)}"
f'Failed {len(failed_results)} "Required for Merge" jobs'
)
if not GH.post_commit_status( if not GH.post_commit_status(
name=Settings.READY_FOR_MERGE_STATUS_NAME + f" [{workflow.name}]", name=Settings.READY_FOR_MERGE_STATUS_NAME + f" [{workflow.name}]",
@ -358,11 +354,14 @@ def _finish_workflow(workflow, job_name):
env.add_info(ResultInfo.GH_STATUS_ERROR) env.add_info(ResultInfo.GH_STATUS_ERROR)
if update_final_report: if update_final_report:
_ResultS3.copy_result_to_s3( S3.copy_result_to_s3(
workflow_result, workflow_result,
) unlock=False,
) # no lock - no unlock
Result.from_fs(job_name).set_status(Result.Status.SUCCESS) Result.from_fs(job_name).set_status(Result.Status.SUCCESS).set_info(
ready_for_merge_description
)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -1,13 +1,12 @@
import dataclasses import dataclasses
import datetime import datetime
import sys import sys
from collections.abc import Container
from pathlib import Path from pathlib import Path
from typing import Any, Dict, List, Optional, Union from typing import Any, Dict, List, Optional
from praktika._environment import _Environment from praktika._environment import _Environment
from praktika.cache import Cache from praktika._settings import _Settings
from praktika.s3 import S3
from praktika.settings import Settings
from praktika.utils import ContextManager, MetaClasses, Shell, Utils from praktika.utils import ContextManager, MetaClasses, Shell, Utils
@ -28,6 +27,10 @@ class Result(MetaClasses.Serializable):
files (List[str]): A list of file paths or names related to the result. files (List[str]): A list of file paths or names related to the result.
links (List[str]): A list of URLs related to the result (e.g., links to reports or resources). links (List[str]): A list of URLs related to the result (e.g., links to reports or resources).
info (str): Additional information about the result. Free-form text. info (str): Additional information about the result. Free-form text.
# TODO: rename
aux_links (List[str]): A list of auxiliary links that provide additional context for the result.
# TODO: remove
html_link (str): A direct link to an HTML representation of the result (e.g., a detailed report page).
Inner Class: Inner Class:
Status: Defines possible statuses for the task, such as "success", "failure", etc. Status: Defines possible statuses for the task, such as "success", "failure", etc.
@ -49,6 +52,8 @@ class Result(MetaClasses.Serializable):
files: List[str] = dataclasses.field(default_factory=list) files: List[str] = dataclasses.field(default_factory=list)
links: List[str] = dataclasses.field(default_factory=list) links: List[str] = dataclasses.field(default_factory=list)
info: str = "" info: str = ""
aux_links: List[str] = dataclasses.field(default_factory=list)
html_link: str = ""
@staticmethod @staticmethod
def create_from( def create_from(
@ -57,15 +62,14 @@ class Result(MetaClasses.Serializable):
stopwatch: Utils.Stopwatch = None, stopwatch: Utils.Stopwatch = None,
status="", status="",
files=None, files=None,
info: Union[List[str], str] = "", info="",
with_info_from_results=True, with_info_from_results=True,
): ):
if isinstance(status, bool): if isinstance(status, bool):
status = Result.Status.SUCCESS if status else Result.Status.FAILED status = Result.Status.SUCCESS if status else Result.Status.FAILED
if not results and not status: if not results and not status:
Utils.raise_with_error( print("ERROR: Either .results or .status must be provided")
f"Either .results ({results}) or .status ({status}) must be provided" raise
)
if not name: if not name:
name = _Environment.get().JOB_NAME name = _Environment.get().JOB_NAME
if not name: if not name:
@ -74,10 +78,10 @@ class Result(MetaClasses.Serializable):
result_status = status or Result.Status.SUCCESS result_status = status or Result.Status.SUCCESS
infos = [] infos = []
if info: if info:
if isinstance(info, str): if isinstance(info, Container):
infos += [info]
else:
infos += info infos += info
else:
infos.append(info)
if results and not status: if results and not status:
for result in results: for result in results:
if result.status not in (Result.Status.SUCCESS, Result.Status.FAILED): if result.status not in (Result.Status.SUCCESS, Result.Status.FAILED):
@ -108,7 +112,7 @@ class Result(MetaClasses.Serializable):
return self.status not in (Result.Status.PENDING, Result.Status.RUNNING) return self.status not in (Result.Status.PENDING, Result.Status.RUNNING)
def is_running(self): def is_running(self):
return self.status in (Result.Status.RUNNING,) return self.status not in (Result.Status.RUNNING,)
def is_ok(self): def is_ok(self):
return self.status in (Result.Status.SKIPPED, Result.Status.SUCCESS) return self.status in (Result.Status.SKIPPED, Result.Status.SUCCESS)
@ -151,7 +155,7 @@ class Result(MetaClasses.Serializable):
@classmethod @classmethod
def file_name_static(cls, name): def file_name_static(cls, name):
return f"{Settings.TEMP_DIR}/result_{Utils.normalize_string(name)}.json" return f"{_Settings.TEMP_DIR}/result_{Utils.normalize_string(name)}.json"
@classmethod @classmethod
def from_dict(cls, obj: Dict[str, Any]) -> "Result": def from_dict(cls, obj: Dict[str, Any]) -> "Result":
@ -176,11 +180,6 @@ class Result(MetaClasses.Serializable):
) )
return self return self
def set_timing(self, stopwatch: Utils.Stopwatch):
self.start_time = stopwatch.start_time
self.duration = stopwatch.duration
return self
def update_sub_result(self, result: "Result"): def update_sub_result(self, result: "Result"):
assert self.results, "BUG?" assert self.results, "BUG?"
for i, result_ in enumerate(self.results): for i, result_ in enumerate(self.results):
@ -234,7 +233,7 @@ class Result(MetaClasses.Serializable):
) )
@classmethod @classmethod
def generate_skipped(cls, name, cache_record: Cache.CacheRecord, results=None): def generate_skipped(cls, name, results=None):
return Result( return Result(
name=name, name=name,
status=Result.Status.SKIPPED, status=Result.Status.SKIPPED,
@ -243,7 +242,7 @@ class Result(MetaClasses.Serializable):
results=results or [], results=results or [],
files=[], files=[],
links=[], links=[],
info=f"from cache: sha [{cache_record.sha}], pr/branch [{cache_record.pr_number or cache_record.branch}]", info="from cache",
) )
@classmethod @classmethod
@ -277,7 +276,7 @@ class Result(MetaClasses.Serializable):
# Set log file path if logging is enabled # Set log file path if logging is enabled
log_file = ( log_file = (
f"{Settings.TEMP_DIR}/{Utils.normalize_string(name)}.log" f"{_Settings.TEMP_DIR}/{Utils.normalize_string(name)}.log"
if with_log if with_log
else None else None
) )
@ -319,35 +318,18 @@ class Result(MetaClasses.Serializable):
files=[log_file] if log_file else None, files=[log_file] if log_file else None,
) )
def complete_job(self): def finish_job_accordingly(self):
self.dump() self.dump()
if not self.is_ok(): if not self.is_ok():
print("ERROR: Job Failed") print("ERROR: Job Failed")
print(self.to_stdout_formatted()) for result in self.results:
if not result.is_ok():
print("Failed checks:")
print(" | ", result)
sys.exit(1) sys.exit(1)
else: else:
print("ok") print("ok")
def to_stdout_formatted(self, indent="", res=""):
if self.is_ok():
return res
res += f"{indent}Task [{self.name}] failed.\n"
fail_info = ""
sub_indent = indent + " "
if not self.results:
if not self.is_ok():
fail_info += f"{sub_indent}{self.name}:\n"
for line in self.info.splitlines():
fail_info += f"{sub_indent}{sub_indent}{line}\n"
return res + fail_info
for sub_result in self.results:
res = sub_result.to_stdout_formatted(sub_indent, res)
return res
class ResultInfo: class ResultInfo:
SETUP_ENV_JOB_FAILED = ( SETUP_ENV_JOB_FAILED = (
@ -370,202 +352,3 @@ class ResultInfo:
) )
S3_ERROR = "S3 call failure" S3_ERROR = "S3 call failure"
class _ResultS3:
@classmethod
def copy_result_to_s3(cls, result, unlock=False):
result.dump()
env = _Environment.get()
s3_path = f"{Settings.HTML_S3_PATH}/{env.get_s3_prefix()}"
s3_path_full = f"{s3_path}/{Path(result.file_name()).name}"
url = S3.copy_file_to_s3(s3_path=s3_path, local_path=result.file_name())
# if unlock:
# if not cls.unlock(s3_path_full):
# print(f"ERROR: File [{s3_path_full}] unlock failure")
# assert False # TODO: investigate
return url
@classmethod
def copy_result_from_s3(cls, local_path, lock=False):
env = _Environment.get()
file_name = Path(local_path).name
s3_path = f"{Settings.HTML_S3_PATH}/{env.get_s3_prefix()}/{file_name}"
# if lock:
# cls.lock(s3_path)
if not S3.copy_file_from_s3(s3_path=s3_path, local_path=local_path):
print(f"ERROR: failed to cp file [{s3_path}] from s3")
raise
@classmethod
def copy_result_from_s3_with_version(cls, local_path):
env = _Environment.get()
file_name = Path(local_path).name
local_dir = Path(local_path).parent
file_name_pattern = f"{file_name}_*"
for file_path in local_dir.glob(file_name_pattern):
file_path.unlink()
s3_path = f"{Settings.HTML_S3_PATH}/{env.get_s3_prefix()}/"
if not S3.copy_file_from_s3_matching_pattern(
s3_path=s3_path, local_path=local_dir, include=file_name_pattern
):
print(f"ERROR: failed to cp file [{s3_path}] from s3")
raise
result_files = []
for file_path in local_dir.glob(file_name_pattern):
result_files.append(file_path)
assert result_files, "No result files found"
result_files.sort()
version = int(result_files[-1].name.split("_")[-1])
Shell.check(f"cp {result_files[-1]} {local_path}", strict=True, verbose=True)
return version
@classmethod
def copy_result_to_s3_with_version(cls, result, version):
result.dump()
filename = Path(result.file_name()).name
file_name_versioned = f"{filename}_{str(version).zfill(3)}"
env = _Environment.get()
s3_path_versioned = (
f"{Settings.HTML_S3_PATH}/{env.get_s3_prefix()}/{file_name_versioned}"
)
s3_path = f"{Settings.HTML_S3_PATH}/{env.get_s3_prefix()}/"
if version == 0:
S3.clean_s3_directory(s3_path=s3_path)
if not S3.put(
s3_path=s3_path_versioned,
local_path=result.file_name(),
if_none_matched=True,
):
print("Failed to put versioned Result")
return False
if not S3.put(s3_path=s3_path, local_path=result.file_name()):
print("Failed to put non-versioned Result")
return True
# @classmethod
# def lock(cls, s3_path, level=0):
# env = _Environment.get()
# s3_path_lock = s3_path + f".lock"
# file_path_lock = f"{Settings.TEMP_DIR}/{Path(s3_path_lock).name}"
# assert Shell.check(
# f"echo '''{env.JOB_NAME}''' > {file_path_lock}", verbose=True
# ), "Never"
#
# i = 20
# meta = S3.head_object(s3_path_lock)
# while meta:
# locked_by_job = meta.get("Metadata", {"job": ""}).get("job", "")
# if locked_by_job:
# decoded_bytes = base64.b64decode(locked_by_job)
# locked_by_job = decoded_bytes.decode("utf-8")
# print(
# f"WARNING: Failed to acquire lock, meta [{meta}], job [{locked_by_job}] - wait"
# )
# i -= 5
# if i < 0:
# info = f"ERROR: lock acquire failure - unlock forcefully"
# print(info)
# env.add_info(info)
# break
# time.sleep(5)
#
# metadata = {"job": Utils.to_base64(env.JOB_NAME)}
# S3.put(
# s3_path=s3_path_lock,
# local_path=file_path_lock,
# metadata=metadata,
# if_none_matched=True,
# )
# time.sleep(1)
# obj = S3.head_object(s3_path_lock)
# if not obj or not obj.has_tags(tags=metadata):
# print(f"WARNING: locked by another job [{obj}]")
# env.add_info("S3 lock file failure")
# cls.lock(s3_path, level=level + 1)
# print("INFO: lock acquired")
#
# @classmethod
# def unlock(cls, s3_path):
# s3_path_lock = s3_path + ".lock"
# env = _Environment.get()
# obj = S3.head_object(s3_path_lock)
# if not obj:
# print("ERROR: lock file is removed")
# assert False # investigate
# elif not obj.has_tags({"job": Utils.to_base64(env.JOB_NAME)}):
# print("ERROR: lock file was acquired by another job")
# assert False # investigate
#
# if not S3.delete(s3_path_lock):
# print(f"ERROR: File [{s3_path_lock}] delete failure")
# print("INFO: lock released")
# return True
@classmethod
def upload_result_files_to_s3(cls, result):
if result.results:
for result_ in result.results:
cls.upload_result_files_to_s3(result_)
for file in result.files:
if not Path(file).is_file():
print(f"ERROR: Invalid file [{file}] in [{result.name}] - skip upload")
result.info += f"\nWARNING: Result file [{file}] was not found"
file_link = S3._upload_file_to_s3(file, upload_to_s3=False)
else:
is_text = False
for text_file_suffix in Settings.TEXT_CONTENT_EXTENSIONS:
if file.endswith(text_file_suffix):
print(
f"File [{file}] matches Settings.TEXT_CONTENT_EXTENSIONS [{Settings.TEXT_CONTENT_EXTENSIONS}] - add text attribute for s3 object"
)
is_text = True
break
file_link = S3._upload_file_to_s3(
file,
upload_to_s3=True,
text=is_text,
s3_subprefix=Utils.normalize_string(result.name),
)
result.links.append(file_link)
if result.files:
print(
f"Result files [{result.files}] uploaded to s3 [{result.links[-len(result.files):]}] - clean files list"
)
result.files = []
result.dump()
@classmethod
def update_workflow_results(cls, workflow_name, new_info="", new_sub_results=None):
assert new_info or new_sub_results
attempt = 1
prev_status = ""
new_status = ""
done = False
while attempt < 10:
version = cls.copy_result_from_s3_with_version(
Result.file_name_static(workflow_name)
)
workflow_result = Result.from_fs(workflow_name)
prev_status = workflow_result.status
if new_info:
workflow_result.set_info(new_info)
if new_sub_results:
if isinstance(new_sub_results, Result):
new_sub_results = [new_sub_results]
for result_ in new_sub_results:
workflow_result.update_sub_result(result_)
new_status = workflow_result.status
if cls.copy_result_to_s3_with_version(workflow_result, version=version + 1):
done = True
break
print(f"Attempt [{attempt}] to upload workflow result failed")
attempt += 1
assert done
if prev_status != new_status:
return new_status
else:
return None

View File

@ -19,7 +19,7 @@ from praktika.utils import Shell, TeePopen, Utils
class Runner: class Runner:
@staticmethod @staticmethod
def generate_local_run_environment(workflow, job, pr=None, branch=None, sha=None): def generate_dummy_environment(workflow, job):
print("WARNING: Generate dummy env for local test") print("WARNING: Generate dummy env for local test")
Shell.check( Shell.check(
f"mkdir -p {Settings.TEMP_DIR} {Settings.INPUT_DIR} {Settings.OUTPUT_DIR}" f"mkdir -p {Settings.TEMP_DIR} {Settings.INPUT_DIR} {Settings.OUTPUT_DIR}"
@ -28,9 +28,9 @@ class Runner:
WORKFLOW_NAME=workflow.name, WORKFLOW_NAME=workflow.name,
JOB_NAME=job.name, JOB_NAME=job.name,
REPOSITORY="", REPOSITORY="",
BRANCH=branch or Settings.MAIN_BRANCH if not pr else "", BRANCH="",
SHA=sha or Shell.get_output("git rev-parse HEAD"), SHA="",
PR_NUMBER=pr or -1, PR_NUMBER=-1,
EVENT_TYPE="", EVENT_TYPE="",
JOB_OUTPUT_STREAM="", JOB_OUTPUT_STREAM="",
EVENT_FILE_PATH="", EVENT_FILE_PATH="",
@ -52,7 +52,6 @@ class Runner:
cache_success=[], cache_success=[],
cache_success_base64=[], cache_success_base64=[],
cache_artifacts={}, cache_artifacts={},
cache_jobs={},
) )
for docker in workflow.dockers: for docker in workflow.dockers:
workflow_config.digest_dockers[docker.name] = Digest().calc_docker_digest( workflow_config.digest_dockers[docker.name] = Digest().calc_docker_digest(
@ -81,12 +80,13 @@ class Runner:
print("Read GH Environment") print("Read GH Environment")
env = _Environment.from_env() env = _Environment.from_env()
env.JOB_NAME = job.name env.JOB_NAME = job.name
env.PARAMETER = job.parameter
env.dump() env.dump()
print(env) print(env)
return 0 return 0
def _pre_run(self, workflow, job, local_run=False): def _pre_run(self, workflow, job):
env = _Environment.get() env = _Environment.get()
result = Result( result = Result(
@ -96,10 +96,9 @@ class Runner:
) )
result.dump() result.dump()
if not local_run: if workflow.enable_report and job.name != Settings.CI_CONFIG_JOB_NAME:
if workflow.enable_report and job.name != Settings.CI_CONFIG_JOB_NAME: print("Update Job and Workflow Report")
print("Update Job and Workflow Report") HtmlRunnerHooks.pre_run(workflow, job)
HtmlRunnerHooks.pre_run(workflow, job)
print("Download required artifacts") print("Download required artifacts")
required_artifacts = [] required_artifacts = []
@ -124,48 +123,28 @@ class Runner:
return 0 return 0
def _run(self, workflow, job, docker="", no_docker=False, param=None, test=""): def _run(self, workflow, job, docker="", no_docker=False, param=None):
# re-set envs for local run
env = _Environment.get()
env.JOB_NAME = job.name
env.dump()
if param: if param:
if not isinstance(param, str): if not isinstance(param, str):
Utils.raise_with_error( Utils.raise_with_error(
f"Custom param for local tests must be of type str, got [{type(param)}]" f"Custom param for local tests must be of type str, got [{type(param)}]"
) )
env = _Environment.get()
env.dump()
if job.run_in_docker and not no_docker: if job.run_in_docker and not no_docker:
job.run_in_docker, docker_settings = ( # TODO: add support for any image, including not from ci config (e.g. ubuntu:latest)
job.run_in_docker.split("+")[0], docker_tag = RunConfig.from_fs(workflow.name).digest_dockers[
job.run_in_docker.split("+")[1:], job.run_in_docker
) ]
from_root = "root" in docker_settings docker = docker or f"{job.run_in_docker}:{docker_tag}"
settings = [s for s in docker_settings if s.startswith("--")] cmd = f"docker run --rm --user \"$(id -u):$(id -g)\" -e PYTHONPATH='{Settings.DOCKER_WD}:{Settings.DOCKER_WD}/ci' --volume ./:{Settings.DOCKER_WD} --volume {Settings.TEMP_DIR}:{Settings.TEMP_DIR} --workdir={Settings.DOCKER_WD} {docker} {job.command}"
if ":" in job.run_in_docker:
docker_name, docker_tag = job.run_in_docker.split(":")
print(
f"WARNING: Job [{job.name}] use custom docker image with a tag - praktika won't control docker version"
)
else:
docker_name, docker_tag = (
job.run_in_docker,
RunConfig.from_fs(workflow.name).digest_dockers[job.run_in_docker],
)
docker = docker or f"{docker_name}:{docker_tag}"
cmd = f"docker run --rm --name praktika {'--user $(id -u):$(id -g)' if not from_root else ''} -e PYTHONPATH='{Settings.DOCKER_WD}:{Settings.DOCKER_WD}/ci' --volume ./:{Settings.DOCKER_WD} --volume {Settings.TEMP_DIR}:{Settings.TEMP_DIR} --workdir={Settings.DOCKER_WD} {' '.join(settings)} {docker} {job.command}"
else: else:
cmd = job.command cmd = job.command
python_path = os.getenv("PYTHONPATH", ":")
os.environ["PYTHONPATH"] = f".:{python_path}"
if param: if param:
print(f"Custom --param [{param}] will be passed to job's script") print(f"Custom --param [{param}] will be passed to job's script")
cmd += f" --param {param}" cmd += f" --param {param}"
if test:
print(f"Custom --test [{test}] will be passed to job's script")
cmd += f" --test {test}"
print(f"--- Run command [{cmd}]") print(f"--- Run command [{cmd}]")
with TeePopen(cmd, timeout=job.timeout) as process: with TeePopen(cmd, timeout=job.timeout) as process:
@ -240,10 +219,13 @@ class Runner:
print(info) print(info)
result.set_info(info).set_status(Result.Status.ERROR).dump() result.set_info(info).set_status(Result.Status.ERROR).dump()
if not result.is_ok(): result.set_files(files=[Settings.RUN_LOG])
result.set_files(files=[Settings.RUN_LOG])
result.update_duration().dump() result.update_duration().dump()
if result.info and result.status != Result.Status.SUCCESS:
# provide job info to workflow level
info_errors.append(result.info)
if run_exit_code == 0: if run_exit_code == 0:
providing_artifacts = [] providing_artifacts = []
if job.provides and workflow.artifacts: if job.provides and workflow.artifacts:
@ -303,24 +285,14 @@ class Runner:
return True return True
def run( def run(
self, self, workflow, job, docker="", dummy_env=False, no_docker=False, param=None
workflow,
job,
docker="",
local_run=False,
no_docker=False,
param=None,
test="",
pr=None,
sha=None,
branch=None,
): ):
res = True res = True
setup_env_code = -10 setup_env_code = -10
prerun_code = -10 prerun_code = -10
run_code = -10 run_code = -10
if res and not local_run: if res and not dummy_env:
print( print(
f"\n\n=== Setup env script [{job.name}], workflow [{workflow.name}] ===" f"\n\n=== Setup env script [{job.name}], workflow [{workflow.name}] ==="
) )
@ -337,15 +309,13 @@ class Runner:
traceback.print_exc() traceback.print_exc()
print(f"=== Setup env finished ===\n\n") print(f"=== Setup env finished ===\n\n")
else: else:
self.generate_local_run_environment( self.generate_dummy_environment(workflow, job)
workflow, job, pr=pr, branch=branch, sha=sha
)
if res and (not local_run or pr or sha or branch): if res and not dummy_env:
res = False res = False
print(f"=== Pre run script [{job.name}], workflow [{workflow.name}] ===") print(f"=== Pre run script [{job.name}], workflow [{workflow.name}] ===")
try: try:
prerun_code = self._pre_run(workflow, job, local_run=local_run) prerun_code = self._pre_run(workflow, job)
res = prerun_code == 0 res = prerun_code == 0
if not res: if not res:
print(f"ERROR: Pre-run failed with exit code [{prerun_code}]") print(f"ERROR: Pre-run failed with exit code [{prerun_code}]")
@ -359,12 +329,7 @@ class Runner:
print(f"=== Run script [{job.name}], workflow [{workflow.name}] ===") print(f"=== Run script [{job.name}], workflow [{workflow.name}] ===")
try: try:
run_code = self._run( run_code = self._run(
workflow, workflow, job, docker=docker, no_docker=no_docker, param=param
job,
docker=docker,
no_docker=no_docker,
param=param,
test=test,
) )
res = run_code == 0 res = run_code == 0
if not res: if not res:
@ -374,7 +339,7 @@ class Runner:
traceback.print_exc() traceback.print_exc()
print(f"=== Run scrip finished ===\n\n") print(f"=== Run scrip finished ===\n\n")
if not local_run: if not dummy_env:
print(f"=== Post run script [{job.name}], workflow [{workflow.name}] ===") print(f"=== Post run script [{job.name}], workflow [{workflow.name}] ===")
self._post_run(workflow, job, setup_env_code, prerun_code, run_code) self._post_run(workflow, job, setup_env_code, prerun_code, run_code)
print(f"=== Post run scrip finished ===") print(f"=== Post run scrip finished ===")

View File

@ -15,23 +15,17 @@ class RunConfig(MetaClasses.Serializable):
# there are might be issue with special characters in job names if used directly in yaml syntax - create base64 encoded list to avoid this # there are might be issue with special characters in job names if used directly in yaml syntax - create base64 encoded list to avoid this
cache_success_base64: List[str] cache_success_base64: List[str]
cache_artifacts: Dict[str, Cache.CacheRecord] cache_artifacts: Dict[str, Cache.CacheRecord]
cache_jobs: Dict[str, Cache.CacheRecord]
sha: str sha: str
@classmethod @classmethod
def from_dict(cls, obj): def from_dict(cls, obj):
cache_artifacts = obj["cache_artifacts"] cache_artifacts = obj["cache_artifacts"]
cache_jobs = obj["cache_jobs"]
cache_artifacts_deserialized = {} cache_artifacts_deserialized = {}
cache_jobs_deserialized = {}
for artifact_name, cache_artifact in cache_artifacts.items(): for artifact_name, cache_artifact in cache_artifacts.items():
cache_artifacts_deserialized[artifact_name] = Cache.CacheRecord.from_dict( cache_artifacts_deserialized[artifact_name] = Cache.CacheRecord.from_dict(
cache_artifact cache_artifact
) )
obj["cache_artifacts"] = cache_artifacts_deserialized obj["cache_artifacts"] = cache_artifacts_deserialized
for job_name, cache_jobs in cache_jobs.items():
cache_jobs_deserialized[job_name] = Cache.CacheRecord.from_dict(cache_jobs)
obj["cache_jobs"] = cache_artifacts_deserialized
return RunConfig(**obj) return RunConfig(**obj)
@classmethod @classmethod

View File

@ -1,11 +1,12 @@
import dataclasses import dataclasses
import json import json
import time
from pathlib import Path from pathlib import Path
from typing import Dict from typing import Dict
from praktika._environment import _Environment from praktika._environment import _Environment
from praktika.settings import Settings from praktika.settings import Settings
from praktika.utils import Shell from praktika.utils import Shell, Utils
class S3: class S3:
@ -51,22 +52,23 @@ class S3:
cmd += " --content-type text/plain" cmd += " --content-type text/plain"
res = cls.run_command_with_retries(cmd) res = cls.run_command_with_retries(cmd)
if not res: if not res:
raise RuntimeError() raise
bucket = s3_path.split("/")[0] bucket = s3_path.split("/")[0]
endpoint = Settings.S3_BUCKET_TO_HTTP_ENDPOINT[bucket] endpoint = Settings.S3_BUCKET_TO_HTTP_ENDPOINT[bucket]
assert endpoint assert endpoint
return f"https://{s3_full_path}".replace(bucket, endpoint) return f"https://{s3_full_path}".replace(bucket, endpoint)
@classmethod @classmethod
def put(cls, s3_path, local_path, text=False, metadata=None, if_none_matched=False): def put(cls, s3_path, local_path, text=False, metadata=None):
assert Path(local_path).exists(), f"Path [{local_path}] does not exist" assert Path(local_path).exists(), f"Path [{local_path}] does not exist"
assert Path(s3_path), f"Invalid S3 Path [{s3_path}]" assert Path(s3_path), f"Invalid S3 Path [{s3_path}]"
assert Path( assert Path(
local_path local_path
).is_file(), f"Path [{local_path}] is not file. Only files are supported" ).is_file(), f"Path [{local_path}] is not file. Only files are supported"
file_name = Path(local_path).name
s3_full_path = s3_path s3_full_path = s3_path
if s3_full_path.endswith("/"): if not s3_full_path.endswith(file_name):
s3_full_path = f"{s3_path}{Path(local_path).name}" s3_full_path = f"{s3_path}/{Path(local_path).name}"
s3_full_path = str(s3_full_path).removeprefix("s3://") s3_full_path = str(s3_full_path).removeprefix("s3://")
bucket, key = s3_full_path.split("/", maxsplit=1) bucket, key = s3_full_path.split("/", maxsplit=1)
@ -74,8 +76,6 @@ class S3:
command = ( command = (
f"aws s3api put-object --bucket {bucket} --key {key} --body {local_path}" f"aws s3api put-object --bucket {bucket} --key {key} --body {local_path}"
) )
if if_none_matched:
command += f' --if-none-match "*"'
if metadata: if metadata:
for k, v in metadata.items(): for k, v in metadata.items():
command += f" --metadata {k}={v}" command += f" --metadata {k}={v}"
@ -84,7 +84,7 @@ class S3:
if text: if text:
cmd += " --content-type text/plain" cmd += " --content-type text/plain"
res = cls.run_command_with_retries(command) res = cls.run_command_with_retries(command)
return res assert res
@classmethod @classmethod
def run_command_with_retries(cls, command, retries=Settings.MAX_RETRIES_S3): def run_command_with_retries(cls, command, retries=Settings.MAX_RETRIES_S3):
@ -101,14 +101,6 @@ class S3:
elif "does not exist" in stderr: elif "does not exist" in stderr:
print("ERROR: requested file does not exist") print("ERROR: requested file does not exist")
break break
elif "Unknown options" in stderr:
print("ERROR: Invalid AWS CLI command or CLI client version:")
print(f" | awc error: {stderr}")
break
elif "PreconditionFailed" in stderr:
print("ERROR: AWS API Call Precondition Failed")
print(f" | awc error: {stderr}")
break
if ret_code != 0: if ret_code != 0:
print( print(
f"ERROR: aws s3 cp failed, stdout/stderr err: [{stderr}], out [{stdout}]" f"ERROR: aws s3 cp failed, stdout/stderr err: [{stderr}], out [{stdout}]"
@ -116,6 +108,13 @@ class S3:
res = ret_code == 0 res = ret_code == 0
return res return res
@classmethod
def get_link(cls, s3_path, local_path):
s3_full_path = f"{s3_path}/{Path(local_path).name}"
bucket = s3_path.split("/")[0]
endpoint = Settings.S3_BUCKET_TO_HTTP_ENDPOINT[bucket]
return f"https://{s3_full_path}".replace(bucket, endpoint)
@classmethod @classmethod
def copy_file_from_s3(cls, s3_path, local_path): def copy_file_from_s3(cls, s3_path, local_path):
assert Path(s3_path), f"Invalid S3 Path [{s3_path}]" assert Path(s3_path), f"Invalid S3 Path [{s3_path}]"
@ -129,19 +128,6 @@ class S3:
res = cls.run_command_with_retries(cmd) res = cls.run_command_with_retries(cmd)
return res return res
@classmethod
def copy_file_from_s3_matching_pattern(
cls, s3_path, local_path, include, exclude="*"
):
assert Path(s3_path), f"Invalid S3 Path [{s3_path}]"
assert Path(
local_path
).is_dir(), f"Path [{local_path}] does not exist or not a directory"
assert s3_path.endswith("/"), f"s3 path is invalid [{s3_path}]"
cmd = f'aws s3 cp s3://{s3_path} {local_path} --exclude "{exclude}" --include "{include}" --recursive'
res = cls.run_command_with_retries(cmd)
return res
@classmethod @classmethod
def head_object(cls, s3_path): def head_object(cls, s3_path):
s3_path = str(s3_path).removeprefix("s3://") s3_path = str(s3_path).removeprefix("s3://")
@ -162,6 +148,103 @@ class S3:
verbose=True, verbose=True,
) )
# TODO: apparently should be placed into separate file to be used only inside praktika
# keeping this module clean from importing Settings, Environment and etc, making it easy for use externally
@classmethod
def copy_result_to_s3(cls, result, unlock=True):
result.dump()
env = _Environment.get()
s3_path = f"{Settings.HTML_S3_PATH}/{env.get_s3_prefix()}"
s3_path_full = f"{s3_path}/{Path(result.file_name()).name}"
url = S3.copy_file_to_s3(s3_path=s3_path, local_path=result.file_name())
if env.PR_NUMBER:
print("Duplicate Result for latest commit alias in PR")
s3_path = f"{Settings.HTML_S3_PATH}/{env.get_s3_prefix(latest=True)}"
url = S3.copy_file_to_s3(s3_path=s3_path, local_path=result.file_name())
if unlock:
if not cls.unlock(s3_path_full):
print(f"ERROR: File [{s3_path_full}] unlock failure")
assert False # TODO: investigate
return url
@classmethod
def copy_result_from_s3(cls, local_path, lock=True):
env = _Environment.get()
file_name = Path(local_path).name
s3_path = f"{Settings.HTML_S3_PATH}/{env.get_s3_prefix()}/{file_name}"
if lock:
cls.lock(s3_path)
if not S3.copy_file_from_s3(s3_path=s3_path, local_path=local_path):
print(f"ERROR: failed to cp file [{s3_path}] from s3")
raise
@classmethod
def lock(cls, s3_path, level=0):
assert level < 3, "Never"
env = _Environment.get()
s3_path_lock = s3_path + f".lock"
file_path_lock = f"{Settings.TEMP_DIR}/{Path(s3_path_lock).name}"
assert Shell.check(
f"echo '''{env.JOB_NAME}''' > {file_path_lock}", verbose=True
), "Never"
i = 20
meta = S3.head_object(s3_path_lock)
while meta:
print(f"WARNING: Failed to acquire lock, meta [{meta}] - wait")
i -= 5
if i < 0:
info = f"ERROR: lock acquire failure - unlock forcefully"
print(info)
env.add_info(info)
break
time.sleep(5)
metadata = {"job": Utils.to_base64(env.JOB_NAME)}
S3.put(
s3_path=s3_path_lock,
local_path=file_path_lock,
metadata=metadata,
)
time.sleep(1)
obj = S3.head_object(s3_path_lock)
if not obj or not obj.has_tags(tags=metadata):
print(f"WARNING: locked by another job [{obj}]")
env.add_info("S3 lock file failure")
cls.lock(s3_path, level=level + 1)
print("INFO: lock acquired")
@classmethod
def unlock(cls, s3_path):
s3_path_lock = s3_path + ".lock"
env = _Environment.get()
obj = S3.head_object(s3_path_lock)
if not obj:
print("ERROR: lock file is removed")
assert False # investigate
elif not obj.has_tags({"job": Utils.to_base64(env.JOB_NAME)}):
print("ERROR: lock file was acquired by another job")
assert False # investigate
if not S3.delete(s3_path_lock):
print(f"ERROR: File [{s3_path_lock}] delete failure")
print("INFO: lock released")
return True
@classmethod
def get_result_link(cls, result):
env = _Environment.get()
s3_path = f"{Settings.HTML_S3_PATH}/{env.get_s3_prefix(latest=True if env.PR_NUMBER else False)}"
return S3.get_link(s3_path=s3_path, local_path=result.file_name())
@classmethod
def clean_latest_result(cls):
env = _Environment.get()
env.SHA = "latest"
assert env.PR_NUMBER
s3_path = f"{Settings.HTML_S3_PATH}/{env.get_s3_prefix()}"
S3.clean_s3_directory(s3_path=s3_path)
@classmethod @classmethod
def _upload_file_to_s3( def _upload_file_to_s3(
cls, local_file_path, upload_to_s3: bool, text: bool = False, s3_subprefix="" cls, local_file_path, upload_to_s3: bool, text: bool = False, s3_subprefix=""
@ -177,3 +260,36 @@ class S3:
) )
return html_link return html_link
return f"file://{Path(local_file_path).absolute()}" return f"file://{Path(local_file_path).absolute()}"
@classmethod
def upload_result_files_to_s3(cls, result):
if result.results:
for result_ in result.results:
cls.upload_result_files_to_s3(result_)
for file in result.files:
if not Path(file).is_file():
print(f"ERROR: Invalid file [{file}] in [{result.name}] - skip upload")
result.info += f"\nWARNING: Result file [{file}] was not found"
file_link = cls._upload_file_to_s3(file, upload_to_s3=False)
else:
is_text = False
for text_file_suffix in Settings.TEXT_CONTENT_EXTENSIONS:
if file.endswith(text_file_suffix):
print(
f"File [{file}] matches Settings.TEXT_CONTENT_EXTENSIONS [{Settings.TEXT_CONTENT_EXTENSIONS}] - add text attribute for s3 object"
)
is_text = True
break
file_link = cls._upload_file_to_s3(
file,
upload_to_s3=True,
text=is_text,
s3_subprefix=Utils.normalize_string(result.name),
)
result.links.append(file_link)
if result.files:
print(
f"Result files [{result.files}] uploaded to s3 [{result.links[-len(result.files):]}] - clean files list"
)
result.files = []
result.dump()

View File

@ -1,152 +1,8 @@
import dataclasses from praktika._settings import _Settings
import importlib.util from praktika.mangle import _get_user_settings
from pathlib import Path
from typing import Dict, Iterable, List, Optional
Settings = _Settings()
@dataclasses.dataclass user_settings = _get_user_settings()
class _Settings: for setting, value in user_settings.items():
###################################### Settings.__setattr__(setting, value)
# Pipeline generation settings #
######################################
MAIN_BRANCH = "main"
CI_PATH = "./ci"
WORKFLOW_PATH_PREFIX: str = "./.github/workflows"
WORKFLOWS_DIRECTORY: str = f"{CI_PATH}/workflows"
SETTINGS_DIRECTORY: str = f"{CI_PATH}/settings"
CI_CONFIG_JOB_NAME = "Config Workflow"
DOCKER_BUILD_JOB_NAME = "Docker Builds"
FINISH_WORKFLOW_JOB_NAME = "Finish Workflow"
READY_FOR_MERGE_STATUS_NAME = "Ready for Merge"
CI_CONFIG_RUNS_ON: Optional[List[str]] = None
DOCKER_BUILD_RUNS_ON: Optional[List[str]] = None
VALIDATE_FILE_PATHS: bool = True
######################################
# Runtime Settings #
######################################
MAX_RETRIES_S3 = 3
MAX_RETRIES_GH = 3
######################################
# S3 (artifact storage) settings #
######################################
S3_ARTIFACT_PATH: str = ""
######################################
# CI workspace settings #
######################################
TEMP_DIR: str = "/tmp/praktika"
OUTPUT_DIR: str = f"{TEMP_DIR}/output"
INPUT_DIR: str = f"{TEMP_DIR}/input"
PYTHON_INTERPRETER: str = "python3"
PYTHON_PACKET_MANAGER: str = "pip3"
PYTHON_VERSION: str = "3.9"
INSTALL_PYTHON_FOR_NATIVE_JOBS: bool = False
INSTALL_PYTHON_REQS_FOR_NATIVE_JOBS: str = "./ci/requirements.txt"
ENVIRONMENT_VAR_FILE: str = f"{TEMP_DIR}/environment.json"
RUN_LOG: str = f"{TEMP_DIR}/praktika_run.log"
SECRET_GH_APP_ID: str = "GH_APP_ID"
SECRET_GH_APP_PEM_KEY: str = "GH_APP_PEM_KEY"
ENV_SETUP_SCRIPT: str = "/tmp/praktika_setup_env.sh"
WORKFLOW_STATUS_FILE: str = f"{TEMP_DIR}/workflow_status.json"
######################################
# CI Cache settings #
######################################
CACHE_VERSION: int = 1
CACHE_DIGEST_LEN: int = 20
CACHE_S3_PATH: str = ""
CACHE_LOCAL_PATH: str = f"{TEMP_DIR}/ci_cache"
######################################
# Report settings #
######################################
HTML_S3_PATH: str = ""
HTML_PAGE_FILE: str = "./praktika/json.html"
TEXT_CONTENT_EXTENSIONS: Iterable[str] = frozenset([".txt", ".log"])
S3_BUCKET_TO_HTTP_ENDPOINT: Optional[Dict[str, str]] = None
DOCKERHUB_USERNAME: str = ""
DOCKERHUB_SECRET: str = ""
DOCKER_WD: str = "/wd"
######################################
# CI DB Settings #
######################################
SECRET_CI_DB_URL: str = "CI_DB_URL"
SECRET_CI_DB_PASSWORD: str = "CI_DB_PASSWORD"
CI_DB_DB_NAME = ""
CI_DB_TABLE_NAME = ""
CI_DB_INSERT_TIMEOUT_SEC = 5
DISABLE_MERGE_COMMIT = True
_USER_DEFINED_SETTINGS = [
"S3_ARTIFACT_PATH",
"CACHE_S3_PATH",
"HTML_S3_PATH",
"S3_BUCKET_TO_HTTP_ENDPOINT",
"TEXT_CONTENT_EXTENSIONS",
"TEMP_DIR",
"OUTPUT_DIR",
"INPUT_DIR",
"CI_CONFIG_RUNS_ON",
"DOCKER_BUILD_RUNS_ON",
"CI_CONFIG_JOB_NAME",
"PYTHON_INTERPRETER",
"PYTHON_VERSION",
"PYTHON_PACKET_MANAGER",
"INSTALL_PYTHON_FOR_NATIVE_JOBS",
"INSTALL_PYTHON_REQS_FOR_NATIVE_JOBS",
"MAX_RETRIES_S3",
"MAX_RETRIES_GH",
"VALIDATE_FILE_PATHS",
"DOCKERHUB_USERNAME",
"DOCKERHUB_SECRET",
"READY_FOR_MERGE_STATUS_NAME",
"SECRET_CI_DB_URL",
"SECRET_CI_DB_PASSWORD",
"CI_DB_DB_NAME",
"CI_DB_TABLE_NAME",
"CI_DB_INSERT_TIMEOUT_SEC",
"SECRET_GH_APP_PEM_KEY",
"SECRET_GH_APP_ID",
"MAIN_BRANCH",
"DISABLE_MERGE_COMMIT",
]
def _get_settings() -> _Settings:
res = _Settings()
directory = Path(_Settings.SETTINGS_DIRECTORY)
for py_file in directory.glob("*.py"):
module_name = py_file.name.removeprefix(".py")
spec = importlib.util.spec_from_file_location(
module_name, f"{_Settings.SETTINGS_DIRECTORY}/{module_name}"
)
assert spec
foo = importlib.util.module_from_spec(spec)
assert spec.loader
spec.loader.exec_module(foo)
for setting in _USER_DEFINED_SETTINGS:
try:
value = getattr(foo, setting)
res.__setattr__(setting, value)
# print(f"- read user defined setting [{setting} = {value}]")
except Exception as e:
# print(f"Exception while read user settings: {e}")
pass
return res
class GHRunners:
ubuntu = "ubuntu-latest"
Settings = _get_settings()

View File

@ -17,6 +17,8 @@ from threading import Thread
from types import SimpleNamespace from types import SimpleNamespace
from typing import Any, Dict, Iterator, List, Optional, Type, TypeVar, Union from typing import Any, Dict, Iterator, List, Optional, Type, TypeVar, Union
from praktika._settings import _Settings
T = TypeVar("T", bound="Serializable") T = TypeVar("T", bound="Serializable")
@ -79,26 +81,25 @@ class MetaClasses:
class ContextManager: class ContextManager:
@staticmethod @staticmethod
@contextmanager @contextmanager
def cd(to: Optional[Union[Path, str]]) -> Iterator[None]: def cd(to: Optional[Union[Path, str]] = None) -> Iterator[None]:
""" """
changes current working directory to @path or `git root` if @path is None changes current working directory to @path or `git root` if @path is None
:param to: :param to:
:return: :return:
""" """
# if not to: if not to:
# try: try:
# to = Shell.get_output_or_raise("git rev-parse --show-toplevel") to = Shell.get_output_or_raise("git rev-parse --show-toplevel")
# except: except:
# pass pass
# if not to: if not to:
# if Path(_Settings.DOCKER_WD).is_dir(): if Path(_Settings.DOCKER_WD).is_dir():
# to = _Settings.DOCKER_WD to = _Settings.DOCKER_WD
# if not to: if not to:
# assert False, "FIX IT" assert False, "FIX IT"
# assert to assert to
old_pwd = os.getcwd() old_pwd = os.getcwd()
if to: os.chdir(to)
os.chdir(to)
try: try:
yield yield
finally: finally:

View File

@ -4,8 +4,10 @@ from itertools import chain
from pathlib import Path from pathlib import Path
from praktika import Workflow from praktika import Workflow
from praktika._settings import GHRunners
from praktika.mangle import _get_workflows from praktika.mangle import _get_workflows
from praktika.settings import GHRunners, Settings from praktika.settings import Settings
from praktika.utils import ContextManager
class Validator: class Validator:
@ -117,56 +119,61 @@ class Validator:
def validate_file_paths_in_run_command(cls, workflow: Workflow.Config) -> None: def validate_file_paths_in_run_command(cls, workflow: Workflow.Config) -> None:
if not Settings.VALIDATE_FILE_PATHS: if not Settings.VALIDATE_FILE_PATHS:
return return
for job in workflow.jobs: with ContextManager.cd():
run_command = job.command for job in workflow.jobs:
command_parts = run_command.split(" ") run_command = job.command
for part in command_parts: command_parts = run_command.split(" ")
if ">" in part: for part in command_parts:
return if ">" in part:
if "/" in part: return
assert ( if "/" in part:
Path(part).is_file() or Path(part).is_dir() assert (
), f"Apparently run command [{run_command}] for job [{job}] has invalid path [{part}]. Setting to disable check: VALIDATE_FILE_PATHS" Path(part).is_file() or Path(part).is_dir()
), f"Apparently run command [{run_command}] for job [{job}] has invalid path [{part}]. Setting to disable check: VALIDATE_FILE_PATHS"
@classmethod @classmethod
def validate_file_paths_in_digest_configs(cls, workflow: Workflow.Config) -> None: def validate_file_paths_in_digest_configs(cls, workflow: Workflow.Config) -> None:
if not Settings.VALIDATE_FILE_PATHS: if not Settings.VALIDATE_FILE_PATHS:
return return
for job in workflow.jobs: with ContextManager.cd():
if not job.digest_config: for job in workflow.jobs:
continue if not job.digest_config:
for include_path in chain( continue
job.digest_config.include_paths, job.digest_config.exclude_paths for include_path in chain(
): job.digest_config.include_paths, job.digest_config.exclude_paths
if "*" in include_path: ):
assert glob.glob( if "*" in include_path:
include_path, recursive=True assert glob.glob(
), f"Apparently file glob [{include_path}] in job [{job.name}] digest_config [{job.digest_config}] invalid, workflow [{workflow.name}]. Setting to disable check: VALIDATE_FILE_PATHS" include_path, recursive=True
else: ), f"Apparently file glob [{include_path}] in job [{job.name}] digest_config [{job.digest_config}] invalid, workflow [{workflow.name}]. Setting to disable check: VALIDATE_FILE_PATHS"
assert ( else:
Path(include_path).is_file() or Path(include_path).is_dir() assert (
), f"Apparently file path [{include_path}] in job [{job.name}] digest_config [{job.digest_config}] invalid, workflow [{workflow.name}]. Setting to disable check: VALIDATE_FILE_PATHS" Path(include_path).is_file() or Path(include_path).is_dir()
), f"Apparently file path [{include_path}] in job [{job.name}] digest_config [{job.digest_config}] invalid, workflow [{workflow.name}]. Setting to disable check: VALIDATE_FILE_PATHS"
@classmethod @classmethod
def validate_requirements_txt_files(cls, workflow: Workflow.Config) -> None: def validate_requirements_txt_files(cls, workflow: Workflow.Config) -> None:
for job in workflow.jobs: with ContextManager.cd():
if job.job_requirements: for job in workflow.jobs:
if job.job_requirements.python_requirements_txt: if job.job_requirements:
path = Path(job.job_requirements.python_requirements_txt) if job.job_requirements.python_requirements_txt:
message = f"File with py requirement [{path}] does not exist" path = Path(job.job_requirements.python_requirements_txt)
if job.name in ( message = f"File with py requirement [{path}] does not exist"
Settings.DOCKER_BUILD_JOB_NAME, if job.name in (
Settings.CI_CONFIG_JOB_NAME, Settings.DOCKER_BUILD_JOB_NAME,
Settings.FINISH_WORKFLOW_JOB_NAME, Settings.CI_CONFIG_JOB_NAME,
): Settings.FINISH_WORKFLOW_JOB_NAME,
message += '\n If all requirements already installed on your runners - add setting INSTALL_PYTHON_REQS_FOR_NATIVE_JOBS""' ):
message += "\n If requirements needs to be installed - add requirements file (Settings.INSTALL_PYTHON_REQS_FOR_NATIVE_JOBS):" message += '\n If all requirements already installed on your runners - add setting INSTALL_PYTHON_REQS_FOR_NATIVE_JOBS""'
message += "\n echo jwt==1.3.1 > ./ci/requirements.txt" message += "\n If requirements needs to be installed - add requirements file (Settings.INSTALL_PYTHON_REQS_FOR_NATIVE_JOBS):"
message += ( message += "\n echo jwt==1.3.1 > ./ci/requirements.txt"
"\n echo requests==2.32.3 >> ./ci/requirements.txt" message += (
"\n echo requests==2.32.3 >> ./ci/requirements.txt"
)
message += "\n echo https://clickhouse-builds.s3.amazonaws.com/packages/praktika-0.1-py3-none-any.whl >> ./ci/requirements.txt"
cls.evaluate_check(
path.is_file(), message, job.name, workflow.name
) )
message += "\n echo https://clickhouse-builds.s3.amazonaws.com/packages/praktika-0.1-py3-none-any.whl >> ./ci/requirements.txt"
cls.evaluate_check(path.is_file(), message, job.name, workflow.name)
@classmethod @classmethod
def validate_dockers(cls, workflow: Workflow.Config): def validate_dockers(cls, workflow: Workflow.Config):

View File

@ -31,7 +31,6 @@ class Workflow:
enable_report: bool = False enable_report: bool = False
enable_merge_ready_status: bool = False enable_merge_ready_status: bool = False
enable_cidb: bool = False enable_cidb: bool = False
enable_merge_commit: bool = False
def is_event_pull_request(self): def is_event_pull_request(self):
return self.event == Workflow.Event.PULL_REQUEST return self.event == Workflow.Event.PULL_REQUEST

View File

@ -80,8 +80,6 @@ jobs:
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
with:
ref: ${{{{ github.head_ref }}}}
{JOB_ADDONS} {JOB_ADDONS}
- name: Prepare env script - name: Prepare env script
run: | run: |
@ -104,11 +102,7 @@ jobs:
run: | run: |
. /tmp/praktika_setup_env.sh . /tmp/praktika_setup_env.sh
set -o pipefail set -o pipefail
if command -v ts &> /dev/null; then {PYTHON} -m praktika run --job '''{JOB_NAME}''' --workflow "{WORKFLOW_NAME}" --ci |& tee {RUN_LOG}
python3 -m praktika run --job '''{JOB_NAME}''' --workflow "{WORKFLOW_NAME}" --ci |& ts '[%Y-%m-%d %H:%M:%S]' | tee /tmp/praktika/praktika_run.log
else
python3 -m praktika run --job '''{JOB_NAME}''' --workflow "{WORKFLOW_NAME}" --ci |& tee /tmp/praktika/praktika_run.log
fi
{UPLOADS_GITHUB}\ {UPLOADS_GITHUB}\
""" """
@ -190,10 +184,12 @@ jobs:
False False
), f"Workflow event not yet supported [{workflow_config.event}]" ), f"Workflow event not yet supported [{workflow_config.event}]"
with open(self._get_workflow_file_name(workflow_config.name), "w") as f: with ContextManager.cd():
f.write(yaml_workflow_str) with open(self._get_workflow_file_name(workflow_config.name), "w") as f:
f.write(yaml_workflow_str)
Shell.check("git add ./.github/workflows/*.yaml") with ContextManager.cd():
Shell.check("git add ./.github/workflows/*.yaml")
class PullRequestPushYamlGen: class PullRequestPushYamlGen:

View File

@ -7,33 +7,24 @@ S3_BUCKET_HTTP_ENDPOINT = "clickhouse-builds.s3.amazonaws.com"
class RunnerLabels: class RunnerLabels:
CI_SERVICES = "ci_services" CI_SERVICES = "ci_services"
CI_SERVICES_EBS = "ci_services_ebs" CI_SERVICES_EBS = "ci_services_ebs"
BUILDER_AMD = "builder" BUILDER = "builder"
BUILDER_ARM = "builder-aarch64"
FUNC_TESTER_AMD = "func-tester"
FUNC_TESTER_ARM = "func-tester-aarch64"
BASE_BRANCH = "master" BASE_BRANCH = "master"
azure_secret = Secret.Config(
name="azure_connection_string",
type=Secret.Type.AWS_SSM_VAR,
)
SECRETS = [ SECRETS = [
Secret.Config( Secret.Config(
name="dockerhub_robot_password", name="dockerhub_robot_password",
type=Secret.Type.AWS_SSM_VAR, type=Secret.Type.AWS_SSM_VAR,
), ),
azure_secret, Secret.Config(
# Secret.Config( name="woolenwolf_gh_app.clickhouse-app-id",
# name="woolenwolf_gh_app.clickhouse-app-id", type=Secret.Type.AWS_SSM_SECRET,
# type=Secret.Type.AWS_SSM_SECRET, ),
# ), Secret.Config(
# Secret.Config( name="woolenwolf_gh_app.clickhouse-app-key",
# name="woolenwolf_gh_app.clickhouse-app-key", type=Secret.Type.AWS_SSM_SECRET,
# type=Secret.Type.AWS_SSM_SECRET, ),
# ),
] ]
DOCKERS = [ DOCKERS = [
@ -127,18 +118,18 @@ DOCKERS = [
# platforms=Docker.Platforms.arm_amd, # platforms=Docker.Platforms.arm_amd,
# depends_on=["clickhouse/test-base"], # depends_on=["clickhouse/test-base"],
# ), # ),
Docker.Config( # Docker.Config(
name="clickhouse/stateless-test", # name="clickhouse/stateless-test",
path="./ci/docker/stateless-test", # path="./ci/docker/test/stateless",
platforms=Docker.Platforms.arm_amd, # platforms=Docker.Platforms.arm_amd,
depends_on=[], # depends_on=["clickhouse/test-base"],
), # ),
Docker.Config( # Docker.Config(
name="clickhouse/stateful-test", # name="clickhouse/stateful-test",
path="./ci/docker/stateful-test", # path="./ci/docker/test/stateful",
platforms=Docker.Platforms.arm_amd, # platforms=Docker.Platforms.arm_amd,
depends_on=["clickhouse/stateless-test"], # depends_on=["clickhouse/stateless-test"],
), # ),
# Docker.Config( # Docker.Config(
# name="clickhouse/stress-test", # name="clickhouse/stress-test",
# path="./ci/docker/test/stress", # path="./ci/docker/test/stress",
@ -239,6 +230,4 @@ DOCKERS = [
class JobNames: class JobNames:
STYLE_CHECK = "Style Check" STYLE_CHECK = "Style Check"
FAST_TEST = "Fast test" FAST_TEST = "Fast test"
BUILD = "Build" BUILD_AMD_DEBUG = "Build amd64 debug"
STATELESS = "Stateless tests"
STATEFUL = "Stateful tests"

View File

@ -4,8 +4,6 @@ from ci.settings.definitions import (
RunnerLabels, RunnerLabels,
) )
MAIN_BRANCH = "master"
S3_ARTIFACT_PATH = f"{S3_BUCKET_NAME}/artifacts" S3_ARTIFACT_PATH = f"{S3_BUCKET_NAME}/artifacts"
CI_CONFIG_RUNS_ON = [RunnerLabels.CI_SERVICES] CI_CONFIG_RUNS_ON = [RunnerLabels.CI_SERVICES]
DOCKER_BUILD_RUNS_ON = [RunnerLabels.CI_SERVICES_EBS] DOCKER_BUILD_RUNS_ON = [RunnerLabels.CI_SERVICES_EBS]

View File

@ -1,3 +1,5 @@
from typing import List
from praktika import Artifact, Job, Workflow from praktika import Artifact, Job, Workflow
from praktika.settings import Settings from praktika.settings import Settings
@ -11,10 +13,7 @@ from ci.settings.definitions import (
class ArtifactNames: class ArtifactNames:
CH_AMD_DEBUG = "CH_AMD_DEBUG" ch_debug_binary = "clickhouse_debug_binary"
CH_AMD_RELEASE = "CH_AMD_RELEASE"
CH_ARM_RELEASE = "CH_ARM_RELEASE"
CH_ARM_ASAN = "CH_ARM_ASAN"
style_check_job = Job.Config( style_check_job = Job.Config(
@ -26,7 +25,7 @@ style_check_job = Job.Config(
fast_test_job = Job.Config( fast_test_job = Job.Config(
name=JobNames.FAST_TEST, name=JobNames.FAST_TEST,
runs_on=[RunnerLabels.BUILDER_AMD], runs_on=[RunnerLabels.BUILDER],
command="python3 ./ci/jobs/fast_test.py", command="python3 ./ci/jobs/fast_test.py",
run_in_docker="clickhouse/fasttest", run_in_docker="clickhouse/fasttest",
digest_config=Job.CacheDigestConfig( digest_config=Job.CacheDigestConfig(
@ -38,13 +37,11 @@ fast_test_job = Job.Config(
), ),
) )
build_jobs = Job.Config( job_build_amd_debug = Job.Config(
name=JobNames.BUILD, name=JobNames.BUILD_AMD_DEBUG,
runs_on=["...from params..."], runs_on=[RunnerLabels.BUILDER],
requires=[JobNames.FAST_TEST], command="python3 ./ci/jobs/build_clickhouse.py amd_debug",
command="python3 ./ci/jobs/build_clickhouse.py --build-type {PARAMETER}",
run_in_docker="clickhouse/fasttest", run_in_docker="clickhouse/fasttest",
timeout=3600 * 2,
digest_config=Job.CacheDigestConfig( digest_config=Job.CacheDigestConfig(
include_paths=[ include_paths=[
"./src", "./src",
@ -57,85 +54,9 @@ build_jobs = Job.Config(
"./docker/packager/packager", "./docker/packager/packager",
"./rust", "./rust",
"./tests/ci/version_helper.py", "./tests/ci/version_helper.py",
"./ci/jobs/build_clickhouse.py",
], ],
), ),
).parametrize( provides=[ArtifactNames.ch_debug_binary],
parameter=["amd_debug", "amd_release", "arm_release", "arm_asan"],
provides=[
[ArtifactNames.CH_AMD_DEBUG],
[ArtifactNames.CH_AMD_RELEASE],
[ArtifactNames.CH_ARM_RELEASE],
[ArtifactNames.CH_ARM_ASAN],
],
runs_on=[
[RunnerLabels.BUILDER_AMD],
[RunnerLabels.BUILDER_AMD],
[RunnerLabels.BUILDER_ARM],
[RunnerLabels.BUILDER_ARM],
],
)
stateless_tests_jobs = Job.Config(
name=JobNames.STATELESS,
runs_on=[RunnerLabels.BUILDER_AMD],
command="python3 ./ci/jobs/functional_stateless_tests.py --test-options {PARAMETER}",
# many tests expect to see "/var/lib/clickhouse" in various output lines - add mount for now, consider creating this dir in docker file
run_in_docker="clickhouse/stateless-test+--security-opt seccomp=unconfined",
digest_config=Job.CacheDigestConfig(
include_paths=[
"./ci/jobs/functional_stateless_tests.py",
],
),
).parametrize(
parameter=[
"amd_debug,parallel",
"amd_debug,non-parallel",
"amd_release,parallel",
"amd_release,non-parallel",
"arm_asan,parallel",
"arm_asan,non-parallel",
],
runs_on=[
[RunnerLabels.BUILDER_AMD],
[RunnerLabels.FUNC_TESTER_AMD],
[RunnerLabels.BUILDER_AMD],
[RunnerLabels.FUNC_TESTER_AMD],
[RunnerLabels.BUILDER_ARM],
[RunnerLabels.FUNC_TESTER_ARM],
],
requires=[
[ArtifactNames.CH_AMD_DEBUG],
[ArtifactNames.CH_AMD_DEBUG],
[ArtifactNames.CH_AMD_RELEASE],
[ArtifactNames.CH_AMD_RELEASE],
[ArtifactNames.CH_ARM_ASAN],
[ArtifactNames.CH_ARM_ASAN],
],
)
stateful_tests_jobs = Job.Config(
name=JobNames.STATEFUL,
runs_on=[RunnerLabels.BUILDER_AMD],
command="python3 ./ci/jobs/functional_stateful_tests.py --test-options {PARAMETER}",
# many tests expect to see "/var/lib/clickhouse"
# some tests expect to see "/var/log/clickhouse"
run_in_docker="clickhouse/stateless-test+--security-opt seccomp=unconfined",
digest_config=Job.CacheDigestConfig(
include_paths=[
"./ci/jobs/functional_stateful_tests.py",
],
),
).parametrize(
parameter=[
"amd_debug,parallel",
],
runs_on=[
[RunnerLabels.BUILDER_AMD],
],
requires=[
[ArtifactNames.CH_AMD_DEBUG],
],
) )
workflow = Workflow.Config( workflow = Workflow.Config(
@ -145,31 +66,14 @@ workflow = Workflow.Config(
jobs=[ jobs=[
style_check_job, style_check_job,
fast_test_job, fast_test_job,
*build_jobs, job_build_amd_debug,
*stateless_tests_jobs,
*stateful_tests_jobs,
], ],
artifacts=[ artifacts=[
Artifact.Config( Artifact.Config(
name=ArtifactNames.CH_AMD_DEBUG, name=ArtifactNames.ch_debug_binary,
type=Artifact.Type.S3, type=Artifact.Type.S3,
path=f"{Settings.TEMP_DIR}/build/programs/clickhouse", path=f"{Settings.TEMP_DIR}/build/programs/clickhouse",
), )
Artifact.Config(
name=ArtifactNames.CH_AMD_RELEASE,
type=Artifact.Type.S3,
path=f"{Settings.TEMP_DIR}/build/programs/clickhouse",
),
Artifact.Config(
name=ArtifactNames.CH_ARM_RELEASE,
type=Artifact.Type.S3,
path=f"{Settings.TEMP_DIR}/build/programs/clickhouse",
),
Artifact.Config(
name=ArtifactNames.CH_ARM_ASAN,
type=Artifact.Type.S3,
path=f"{Settings.TEMP_DIR}/build/programs/clickhouse",
),
], ],
dockers=DOCKERS, dockers=DOCKERS,
secrets=SECRETS, secrets=SECRETS,
@ -180,14 +84,11 @@ workflow = Workflow.Config(
WORKFLOWS = [ WORKFLOWS = [
workflow, workflow,
] ] # type: List[Workflow.Config]
# if __name__ == "__main__": if __name__ == "__main__":
# # local job test inside praktika environment # local job test inside praktika environment
# from praktika.runner import Runner from praktika.runner import Runner
# from praktika.digest import Digest
# Runner().run(workflow, fast_test_job, docker="fasttest", dummy_env=True)
# print(Digest().calc_job_digest(amd_debug_build_job))
#
# Runner().run(workflow, fast_test_job, docker="fasttest", local_run=True)

View File

@ -217,11 +217,7 @@ add_contrib (libssh-cmake libssh)
add_contrib (prometheus-protobufs-cmake prometheus-protobufs prometheus-protobufs-gogo) add_contrib (prometheus-protobufs-cmake prometheus-protobufs prometheus-protobufs-gogo)
add_contrib (numactl-cmake numactl) add_contrib(numactl-cmake numactl)
add_contrib (google-cloud-cpp-cmake google-cloud-cpp) # requires grpc, protobuf, absl
add_contrib (jwt-cpp-cmake jwt-cpp)
# Put all targets defined here and in subdirectories under "contrib/<immediate-subdir>" folders in GUI-based IDEs. # Put all targets defined here and in subdirectories under "contrib/<immediate-subdir>" folders in GUI-based IDEs.
# Some of third-party projects may override CMAKE_FOLDER or FOLDER property of their targets, so they would not appear # Some of third-party projects may override CMAKE_FOLDER or FOLDER property of their targets, so they would not appear

@ -1 +0,0 @@
Subproject commit 83f30caadb8613fb5c408d8c2fd545291596b53f

View File

@ -1,105 +0,0 @@
set(ENABLE_GOOGLE_CLOUD_CPP_DEFAULT OFF)
if(ENABLE_LIBRARIES AND CLICKHOUSE_CLOUD AND OS_LINUX)
set(ENABLE_GOOGLE_CLOUD_CPP_DEFAULT ON)
endif()
option(ENABLE_GOOGLE_CLOUD_CPP "Enable Google Cloud Cpp" ${ENABLE_GOOGLE_CLOUD_CPP_DEFAULT})
if(NOT ENABLE_GOOGLE_CLOUD_CPP)
message(STATUS "Not using Google Cloud Cpp")
return()
endif()
if(NOT ENABLE_GRPC)
message (${RECONFIGURE_MESSAGE_LEVEL} "Can't use Google Cloud Cpp without gRPC")
endif()
if (NOT ENABLE_PROTOBUF)
message( ${RECONFIGURE_MESSAGE_LEVEL} "Can't use Google Cloud Cpp without protobuf")
endif()
# Gather sources and options.
set(GOOGLE_CLOUD_CPP_SOURCES)
set(GOOGLE_CLOUD_CPP_PUBLIC_INCLUDES)
set(GOOGLE_CLOUD_CPP_PRIVATE_INCLUDES)
set(GOOGLE_CLOUD_CPP_PRIVATE_LIBS)
# Directories.
SET(GOOGLE_CLOUD_CPP_DIR "${ClickHouse_SOURCE_DIR}/contrib/google-cloud-cpp" )
list(APPEND GOOGLE_CLOUD_CPP_PRIVATE_INCLUDES "${GOOGLE_CLOUD_CPP_DIR}")
# Set the PROJECT_SOURCE_DIR so that all Google Cloud cmake files work
set(PROJECT_SOURCE_DIR_BAK ${PROJECT_SOURCE_DIR})
set(PROJECT_SOURCE_DIR ${GOOGLE_CLOUD_CPP_DIR})
list(APPEND CMAKE_MODULE_PATH "${GOOGLE_CLOUD_CPP_DIR}/cmake")
# Building this target results in all protobufs being compiled.
add_custom_target(google-cloud-cpp-protos)
include("GoogleCloudCppLibrary")
# Set some variables required for googleapis CMakeLists.txt to work.
set(GOOGLE_CLOUD_CPP_ENABLE_GRPC ON)
set(PROJECT_VERSION "1")
set(PROJECT_VERSION_MAJOR "1")
set(PROTO_INCLUDE_DIR "${ClickHouse_SOURCE_DIR}/contrib/google-protobuf/src")
set(GOOGLE_CLOUD_CPP_GRPC_PLUGIN_EXECUTABLE $<TARGET_FILE:grpc_cpp_plugin>)
include(GoogleApis.cmake)
add_library(gRPC::grpc++ ALIAS _ch_contrib_grpc)
add_library(gRPC::grpc ALIAS _ch_contrib_grpc)
# google-cloud-cpp-kms.
google_cloud_cpp_add_library_protos(kms)
include(google_cloud_cpp_common.cmake)
include(google_cloud_cpp_grpc_utils.cmake)
SET(GOOGLE_CLOUD_CPP_KMS_DIR "${GOOGLE_CLOUD_CPP_DIR}/google/cloud/kms")
file(GLOB GOOGLE_CLOUD_CPP_KMS_SRC
"${GOOGLE_CLOUD_CPP_KMS_DIR}/v1/*.cc"
"${GOOGLE_CLOUD_CPP_KMS_DIR}/v1/internal/*.cc"
"${GOOGLE_CLOUD_CPP_KMS_DIR}/inventory/v1/*.cc"
)
list(APPEND GOOGLE_CLOUD_CPP_SOURCES ${GOOGLE_CLOUD_CPP_KMS_SRC})
list(APPEND GOOGLE_CLOUD_CPP_PUBLIC_INCLUDES "${GOOGLE_CLOUD_CPP_DIR}" "${CMAKE_CURRENT_BINARY_DIR}")
set(GRPC_INCLUDE_DIR "${ClickHouse_SOURCE_DIR}/contrib/grpc")
list(APPEND GOOGLE_CLOUD_CPP_PUBLIC_INCLUDES "${GRPC_INCLUDE_DIR}/include" "${GRPC_INCLUDE_DIR}/spm-cpp-include")
# Restore the PROJECT_SOURCE_DIR.
set(PROJECT_SOURCE_DIR ${PROJECT_SOURCE_DIR_BAK})
# Link against external libraries.
list(APPEND GOOGLE_CLOUD_CPP_PRIVATE_LIBS
google_cloud_cpp_common
google_cloud_cpp_grpc_utils
google_cloud_cpp_kms_protos
google_cloud_cpp_cloud_location_locations_protos
google_cloud_cpp_iam_v1_iam_policy_protos
gRPC::grpc++
absl::optional
)
list(APPEND GOOGLE_CLOUD_CPP_PUBLIC_LIBS
absl::optional
gRPC::grpc++
)
# Add library.
add_library(_gcloud ${GOOGLE_CLOUD_CPP_SOURCES})
target_include_directories(_gcloud SYSTEM PUBLIC ${GOOGLE_CLOUD_CPP_PUBLIC_INCLUDES})
target_include_directories(_gcloud SYSTEM PRIVATE ${GOOGLE_CLOUD_CPP_PRIVATE_INCLUDES})
target_link_libraries(_gcloud PRIVATE ${GOOGLE_CLOUD_CPP_PRIVATE_LIBS})
# The library is large - avoid bloat.
if (OMIT_HEAVY_DEBUG_SYMBOLS)
target_compile_options(_gcloud PRIVATE -g0)
endif()
add_library(ch_contrib::google_cloud_cpp ALIAS _gcloud)

View File

@ -1,469 +0,0 @@
# ~~~
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ~~~
# File copied from google-cloud-cpp/external/googleapis/CMakeLists.txt with minor modifications.
if (NOT GOOGLE_CLOUD_CPP_ENABLE_GRPC)
return()
endif ()
include(GoogleapisConfig)
set(GOOGLE_CLOUD_CPP_GOOGLEAPIS_URL
"https://github.com/googleapis/googleapis/archive/${_GOOGLE_CLOUD_CPP_GOOGLEAPIS_COMMIT_SHA}.tar.gz"
"https://storage.googleapis.com/cloud-cpp-community-archive/github.com/googleapis/googleapis/archive/${_GOOGLE_CLOUD_CPP_GOOGLEAPIS_COMMIT_SHA}.tar.gz"
)
set(GOOGLE_CLOUD_CPP_GOOGLEAPIS_URL_HASH
"${_GOOGLE_CLOUD_CPP_GOOGLEAPIS_SHA256}")
if (GOOGLE_CLOUD_CPP_OVERRIDE_GOOGLEAPIS_URL)
set(GOOGLE_CLOUD_CPP_GOOGLEAPIS_URL
${GOOGLE_CLOUD_CPP_OVERRIDE_GOOGLEAPIS_URL})
endif ()
if (GOOGLE_CLOUD_CPP_OVERRIDE_GOOGLEAPIS_URL_HASH)
set(GOOGLE_CLOUD_CPP_GOOGLEAPIS_URL_HASH
"${GOOGLE_CLOUD_CPP_OVERRIDE_GOOGLEAPIS_URL_HASH}")
endif ()
set(EXTERNAL_GOOGLEAPIS_PROTO_FILES
# cmake-format: sort
"google/api/annotations.proto"
"google/api/auth.proto"
"google/api/backend.proto"
"google/api/billing.proto"
"google/api/client.proto"
"google/api/config_change.proto"
"google/api/consumer.proto"
"google/api/context.proto"
"google/api/control.proto"
"google/api/distribution.proto"
"google/api/documentation.proto"
"google/api/endpoint.proto"
"google/api/error_reason.proto"
"google/api/field_behavior.proto"
"google/api/field_info.proto"
"google/api/http.proto"
"google/api/httpbody.proto"
"google/api/label.proto"
"google/api/launch_stage.proto"
"google/api/log.proto"
"google/api/logging.proto"
"google/api/metric.proto"
"google/api/monitored_resource.proto"
"google/api/monitoring.proto"
"google/api/policy.proto"
"google/api/quota.proto"
"google/api/resource.proto"
"google/api/routing.proto"
"google/api/service.proto"
"google/api/source_info.proto"
"google/api/system_parameter.proto"
"google/api/usage.proto"
"google/api/visibility.proto"
"google/cloud/extended_operations.proto"
"google/cloud/location/locations.proto"
# orgpolicy/v**1** is used *indirectly* by google/cloud/asset, therefore it
# does not appear in protolists/asset.list. In addition, it is not compiled
# by any other library. So, added manually.
"google/cloud/orgpolicy/v1/orgpolicy.proto"
# Some gRPC based authentication is implemented by the IAM Credentials
# service.
"google/iam/credentials/v1/common.proto"
"google/iam/credentials/v1/iamcredentials.proto"
# We expose google::iam::v1::Policy in our google::cloud::IAMUpdater
"google/iam/v1/iam_policy.proto"
"google/iam/v1/options.proto"
"google/iam/v1/policy.proto"
"google/longrunning/operations.proto"
"google/rpc/code.proto"
"google/rpc/context/attribute_context.proto"
"google/rpc/error_details.proto"
"google/rpc/status.proto"
"google/type/calendar_period.proto"
"google/type/color.proto"
"google/type/date.proto"
"google/type/datetime.proto"
"google/type/dayofweek.proto"
"google/type/decimal.proto"
"google/type/expr.proto"
"google/type/fraction.proto"
"google/type/interval.proto"
"google/type/latlng.proto"
"google/type/localized_text.proto"
"google/type/money.proto"
"google/type/month.proto"
"google/type/phone_number.proto"
"google/type/postal_address.proto"
"google/type/quaternion.proto"
"google/type/timeofday.proto")
include(GoogleCloudCppCommonOptions)
# Set EXTERNAL_GOOGLEAPIS_SOURCE in the parent directory, as it is used by all
# the generated libraries. The Conan packages (https://conan.io), will need to
# patch this value. Setting the value in a single place makes such patching
# easier.
set(EXTERNAL_GOOGLEAPIS_PREFIX "${PROJECT_BINARY_DIR}/external/googleapis")
set(EXTERNAL_GOOGLEAPIS_SOURCE
"${EXTERNAL_GOOGLEAPIS_PREFIX}/src/googleapis_download"
PARENT_SCOPE)
set(EXTERNAL_GOOGLEAPIS_SOURCE
"${EXTERNAL_GOOGLEAPIS_PREFIX}/src/googleapis_download")
# Include the functions to compile proto files and maintain proto libraries.
include(CompileProtos)
set(EXTERNAL_GOOGLEAPIS_BYPRODUCTS)
foreach (proto ${EXTERNAL_GOOGLEAPIS_PROTO_FILES})
list(APPEND EXTERNAL_GOOGLEAPIS_BYPRODUCTS
"${EXTERNAL_GOOGLEAPIS_SOURCE}/${proto}")
endforeach ()
file(GLOB protolists "protolists/*.list")
foreach (file IN LISTS protolists)
google_cloud_cpp_load_protolist(protos "${file}")
foreach (proto IN LISTS protos)
list(APPEND EXTERNAL_GOOGLEAPIS_BYPRODUCTS "${proto}")
endforeach ()
endforeach ()
include(ExternalProject)
# -- The build needs protobuf files. The original build scripts download them from a remote server (see target 'googleapis_download').
# This is too unreliable in the context of ClickHouse ... we instead ship the downloaded archive with the ClickHouse source and
# extract it into the build directory directly.
# Dummy googleapis_download target. This needs to exist because lots of other targets depend on it
# We however trick it a little bit saying this target generates the ${EXTERNAL_GOOGLEAPIS_BYPRODUCTS} BYPRODUCTS when
# actually the following section is the one actually providing such BYPRODUCTS.
externalproject_add(
googleapis_download
EXCLUDE_FROM_ALL ON
PREFIX "${EXTERNAL_GOOGLEAPIS_PREFIX}"
PATCH_COMMAND ""
DOWNLOAD_COMMAND ""
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
BUILD_BYPRODUCTS ${EXTERNAL_GOOGLEAPIS_BYPRODUCTS}
LOG_DOWNLOAD OFF)
# Command that extracts the tarball into the proper dir
# Note: The hash must match the Google Cloud Api version, otherwise funny things will happen.
# Find the right hash in "strip-prefix" in MODULE.bazel in the subrepository
message(STATUS "Extracting googleapis tarball")
set(PB_HASH "e60db19f11f94175ac682c5898cce0f77cc508ea")
set(PB_ARCHIVE "${PB_HASH}.tar.gz")
set(PB_DIR "googleapis-${PB_HASH}")
file(ARCHIVE_EXTRACT INPUT
"${ClickHouse_SOURCE_DIR}/contrib/google-cloud-cpp-cmake/googleapis/${PB_ARCHIVE}"
DESTINATION
"${EXTERNAL_GOOGLEAPIS_PREFIX}/tmp")
file(REMOVE_RECURSE "${EXTERNAL_GOOGLEAPIS_SOURCE}")
file(RENAME
"${EXTERNAL_GOOGLEAPIS_PREFIX}/tmp/${PB_DIR}"
"${EXTERNAL_GOOGLEAPIS_SOURCE}"
)
google_cloud_cpp_find_proto_include_dir(PROTO_INCLUDE_DIR)
google_cloud_cpp_add_protos_property()
function (external_googleapis_short_name var proto)
string(REPLACE "google/" "" short_name "${proto}")
string(REPLACE "/" "_" short_name "${short_name}")
string(REPLACE ".proto" "_protos" short_name "${short_name}")
set("${var}"
"${short_name}"
PARENT_SCOPE)
endfunction ()
# Create a single source proto library.
#
# * proto: the filename for the proto source.
# * (optional) ARGN: proto libraries the new library depends on.
function (external_googleapis_add_library proto)
external_googleapis_short_name(short_name "${proto}")
google_cloud_cpp_grpcpp_library(
google_cloud_cpp_${short_name} "${EXTERNAL_GOOGLEAPIS_SOURCE}/${proto}"
PROTO_PATH_DIRECTORIES "${EXTERNAL_GOOGLEAPIS_SOURCE}"
"${PROTO_INCLUDE_DIR}")
external_googleapis_set_version_and_alias("${short_name}")
set(public_deps)
foreach (dep_short_name ${ARGN})
list(APPEND public_deps "google-cloud-cpp::${dep_short_name}")
endforeach ()
list(LENGTH public_deps public_deps_length)
if (public_deps_length EQUAL 0)
target_link_libraries("google_cloud_cpp_${short_name}")
else ()
target_link_libraries("google_cloud_cpp_${short_name}"
PUBLIC ${public_deps})
endif ()
endfunction ()
function (external_googleapis_set_version_and_alias short_name)
add_dependencies("google_cloud_cpp_${short_name}" googleapis_download)
set_target_properties(
"google_cloud_cpp_${short_name}"
PROPERTIES EXPORT_NAME google-cloud-cpp::${short_name}
VERSION "${PROJECT_VERSION}"
SOVERSION ${PROJECT_VERSION_MAJOR})
add_library("google-cloud-cpp::${short_name}" ALIAS
"google_cloud_cpp_${short_name}")
endfunction ()
if (GOOGLE_CLOUD_CPP_USE_INSTALLED_COMMON)
return()
endif ()
# Avoid adding new proto libraries to this list as these libraries are always
# installed, regardless of whether or not they are needed. See #8022 for more
# details.
set(external_googleapis_installed_libraries_list
# cmake-format: sort
google_cloud_cpp_cloud_common_common_protos
google_cloud_cpp_iam_credentials_v1_common_protos
google_cloud_cpp_iam_credentials_v1_iamcredentials_protos
google_cloud_cpp_iam_v1_iam_policy_protos
google_cloud_cpp_iam_v1_options_protos
google_cloud_cpp_iam_v1_policy_protos
google_cloud_cpp_longrunning_operations_protos)
# These proto files cannot be added in the foreach() loop because they have
# dependencies.
set(PROTO_FILES_WITH_DEPENDENCIES
# cmake-format: sort
"google/api/annotations.proto"
"google/api/auth.proto"
"google/api/billing.proto"
"google/api/client.proto"
"google/api/control.proto"
"google/api/distribution.proto"
"google/api/endpoint.proto"
"google/api/log.proto"
"google/api/logging.proto"
"google/api/metric.proto"
"google/api/monitored_resource.proto"
"google/api/monitoring.proto"
"google/api/quota.proto"
"google/api/service.proto"
"google/api/usage.proto"
"google/cloud/location/locations.proto"
"google/rpc/status.proto")
# For some directories *most* (but not all) the proto files are simple enough
# that the libraries can be generated with a foreach() loop.
foreach (proto IN LISTS EXTERNAL_GOOGLEAPIS_PROTO_FILES)
if (proto MATCHES "^google/api/"
OR proto MATCHES "^google/type"
OR proto MATCHES "^google/rpc/"
OR proto MATCHES "^google/cloud/")
external_googleapis_short_name(short_name "${proto}")
list(APPEND external_googleapis_installed_libraries_list
google_cloud_cpp_${short_name})
list(FIND PROTO_FILES_WITH_DEPENDENCIES "${proto}" has_dependency)
if (has_dependency EQUAL -1)
external_googleapis_add_library("${proto}")
endif ()
endif ()
endforeach ()
# Out of order because they have dependencies.
external_googleapis_add_library("google/api/annotations.proto" api_http_protos)
external_googleapis_add_library("google/api/auth.proto" api_annotations_protos)
external_googleapis_add_library("google/api/client.proto"
api_launch_stage_protos)
external_googleapis_add_library("google/api/control.proto" api_policy_protos)
external_googleapis_add_library("google/api/metric.proto"
api_launch_stage_protos api_label_protos)
external_googleapis_add_library("google/api/billing.proto"
api_annotations_protos api_metric_protos)
external_googleapis_add_library("google/api/distribution.proto"
api_annotations_protos)
external_googleapis_add_library("google/api/endpoint.proto"
api_annotations_protos)
external_googleapis_add_library("google/api/log.proto" api_label_protos)
external_googleapis_add_library("google/api/logging.proto"
api_annotations_protos api_label_protos)
external_googleapis_add_library("google/api/monitored_resource.proto"
api_launch_stage_protos api_label_protos)
external_googleapis_add_library("google/api/monitoring.proto"
api_annotations_protos)
external_googleapis_add_library("google/api/quota.proto" api_annotations_protos)
external_googleapis_add_library("google/api/usage.proto" api_annotations_protos
api_visibility_protos)
external_googleapis_add_library(
"google/api/service.proto"
api_annotations_protos
api_auth_protos
api_backend_protos
api_billing_protos
api_client_protos
api_context_protos
api_control_protos
api_documentation_protos
api_endpoint_protos
api_http_protos
api_label_protos
api_log_protos
api_logging_protos
api_metric_protos
api_monitored_resource_protos
api_monitoring_protos
api_quota_protos
api_resource_protos
api_source_info_protos
api_system_parameter_protos
api_usage_protos)
external_googleapis_add_library("google/cloud/location/locations.proto"
api_annotations_protos api_client_protos)
external_googleapis_add_library("google/iam/v1/options.proto"
api_annotations_protos)
external_googleapis_add_library("google/iam/v1/policy.proto"
api_annotations_protos type_expr_protos)
external_googleapis_add_library("google/rpc/status.proto"
rpc_error_details_protos)
external_googleapis_add_library(
"google/longrunning/operations.proto" api_annotations_protos
api_client_protos rpc_status_protos)
external_googleapis_add_library(
"google/iam/v1/iam_policy.proto"
api_annotations_protos
api_client_protos
api_field_behavior_protos
api_resource_protos
iam_v1_options_protos
iam_v1_policy_protos)
external_googleapis_add_library("google/iam/credentials/v1/common.proto"
api_field_behavior_protos api_resource_protos)
external_googleapis_add_library(
"google/iam/credentials/v1/iamcredentials.proto" api_annotations_protos
api_client_protos iam_credentials_v1_common_protos)
google_cloud_cpp_load_protolist(cloud_common_list "${GOOGLE_CLOUD_CPP_DIR}/external/googleapis/protolists/common.list")
google_cloud_cpp_load_protodeps(cloud_common_deps "${GOOGLE_CLOUD_CPP_DIR}/external/googleapis/protodeps/common.deps")
google_cloud_cpp_grpcpp_library(
google_cloud_cpp_cloud_common_common_protos ${cloud_common_list}
PROTO_PATH_DIRECTORIES "${EXTERNAL_GOOGLEAPIS_SOURCE}"
"${PROTO_INCLUDE_DIR}")
external_googleapis_set_version_and_alias(cloud_common_common_protos)
target_link_libraries(google_cloud_cpp_cloud_common_common_protos
PUBLIC ${cloud_common_deps})
# Install the libraries and headers in the locations determined by
# GNUInstallDirs
include(GNUInstallDirs)
install(
TARGETS ${external_googleapis_installed_libraries_list}
EXPORT googleapis-targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT google_cloud_cpp_runtime
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT google_cloud_cpp_runtime
NAMELINK_COMPONENT google_cloud_cpp_development
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT google_cloud_cpp_development)
foreach (target ${external_googleapis_installed_libraries_list})
google_cloud_cpp_install_proto_library_headers("${target}")
google_cloud_cpp_install_proto_library_protos(
"${target}" "${EXTERNAL_GOOGLEAPIS_SOURCE}")
endforeach ()
# Create and install the pkg-config files.
foreach (target ${external_googleapis_installed_libraries_list})
external_googleapis_install_pc("${target}")
endforeach ()
# Create and install the googleapis pkg-config file for backwards compatibility.
set(GOOGLE_CLOUD_CPP_PC_LIBS "")
google_cloud_cpp_set_pkgconfig_paths()
set(GOOGLE_CLOUD_CPP_PC_NAME "The Google APIS C++ Proto Library")
set(GOOGLE_CLOUD_CPP_PC_DESCRIPTION
"Provides C++ APIs to access Google Cloud Platforms.")
# This list is for backwards compatibility purposes only. DO NOT add new
# libraries to it.
string(
JOIN
" "
GOOGLE_CLOUD_CPP_PC_REQUIRES
"google_cloud_cpp_bigtable_protos"
"google_cloud_cpp_cloud_bigquery_protos"
"google_cloud_cpp_iam_protos"
"google_cloud_cpp_pubsub_protos"
"google_cloud_cpp_storage_protos"
"google_cloud_cpp_logging_protos"
"google_cloud_cpp_iam_v1_iam_policy_protos"
"google_cloud_cpp_iam_v1_options_protos"
"google_cloud_cpp_iam_v1_policy_protos"
"google_cloud_cpp_longrunning_operations_protos"
"google_cloud_cpp_api_auth_protos"
"google_cloud_cpp_api_annotations_protos"
"google_cloud_cpp_api_client_protos"
"google_cloud_cpp_api_field_behavior_protos"
"google_cloud_cpp_api_http_protos"
"google_cloud_cpp_rpc_status_protos"
"google_cloud_cpp_rpc_error_details_protos"
"google_cloud_cpp_type_expr_protos"
"grpc++"
"grpc"
"openssl"
"protobuf"
"zlib"
"libcares")
set(GOOGLE_CLOUD_CPP_PC_LIBS "")
google_cloud_cpp_set_pkgconfig_paths()
configure_file("${PROJECT_SOURCE_DIR}/cmake/templates/config.pc.in"
"googleapis.pc" @ONLY)
install(
FILES "${CMAKE_CURRENT_BINARY_DIR}/googleapis.pc"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
COMPONENT google_cloud_cpp_development)
# Create and install the CMake configuration files.
# include(CMakePackageConfigHelpers)
# configure_file("${CMAKE_CURRENT_LIST_DIR}/config.cmake.in"
# "google_cloud_cpp_googleapis-config.cmake" @ONLY)
# write_basic_package_version_file(
# "google_cloud_cpp_googleapis-config-version.cmake"
# VERSION ${PROJECT_VERSION}
# COMPATIBILITY ExactVersion)
# Export the CMake targets to make it easy to create configuration files.
# install(
# EXPORT googleapis-targets
# DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/google_cloud_cpp_googleapis"
# COMPONENT google_cloud_cpp_development)
# install(
# FILES
# "${CMAKE_CURRENT_BINARY_DIR}/google_cloud_cpp_googleapis-config.cmake"
# "${CMAKE_CURRENT_BINARY_DIR}/google_cloud_cpp_googleapis-config-version.cmake"
# "${PROJECT_SOURCE_DIR}/cmake/FindgRPC.cmake"
# "${PROJECT_SOURCE_DIR}/cmake/CompileProtos.cmake"
# DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/google_cloud_cpp_googleapis"
# COMPONENT google_cloud_cpp_development)

View File

@ -1,447 +0,0 @@
# ~~~
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ~~~
# File copied from google-cloud-cpp/google-cloud-cpp/google_cloud_cpp_common.cmake with minor modifications.
set(GOOGLE_CLOUD_CPP_COMMON_DIR "${GOOGLE_CLOUD_CPP_DIR}/google/cloud")
# Generate the version information from the CMake values.
# configure_file(${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/version_info.h.in
# ${CMAKE_CURRENT_SOURCE_DIR}/internal/version_info.h)
# Create the file that captures build information. Having access to the compiler
# and build flags at runtime allows us to print better benchmark results.
string(TOUPPER "${CMAKE_BUILD_TYPE}" GOOGLE_CLOUD_CPP_BUILD_TYPE_UPPER)
configure_file(${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/build_info.cc.in internal/build_info.cc)
# the client library
add_library(
google_cloud_cpp_common # cmake-format: sort
${CMAKE_CURRENT_BINARY_DIR}/internal/build_info.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/access_token.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/access_token.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/backoff_policy.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/common_options.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/credentials.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/credentials.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/experimental_tag.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/future.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/future_generic.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/future_void.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/idempotency.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/absl_str_cat_quiet.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/absl_str_join_quiet.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/absl_str_replace_quiet.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/algorithm.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/api_client_header.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/api_client_header.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/attributes.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/auth_header_error.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/auth_header_error.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/backoff_policy.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/backoff_policy.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/base64_transforms.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/base64_transforms.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/big_endian.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/build_info.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/call_context.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/clock.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/compiler_info.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/compiler_info.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/compute_engine_util.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/compute_engine_util.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/credentials_impl.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/credentials_impl.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/debug_future_status.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/debug_future_status.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/debug_string.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/debug_string.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/detect_gcp.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/detect_gcp_impl.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/detect_gcp_impl.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/diagnostics_pop.inc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/diagnostics_push.inc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/disable_deprecation_warnings.inc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/disable_msvc_crt_secure_warnings.inc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/error_context.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/error_context.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/filesystem.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/filesystem.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/format_time_point.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/format_time_point.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/future_base.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/future_coroutines.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/future_fwd.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/future_impl.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/future_impl.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/future_then_impl.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/future_then_impl.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/getenv.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/getenv.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/group_options.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/invocation_id_generator.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/invocation_id_generator.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/invoke_result.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/ios_flags_saver.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/log_impl.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/log_impl.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/make_status.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/make_status.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/noexcept_action.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/noexcept_action.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/non_constructible.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/opentelemetry.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/opentelemetry.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/opentelemetry_context.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/opentelemetry_context.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/pagination_range.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/parse_rfc3339.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/parse_rfc3339.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/populate_common_options.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/populate_common_options.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/port_platform.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/random.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/random.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/retry_info.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/retry_loop_helpers.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/retry_loop_helpers.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/retry_policy_impl.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/retry_policy_impl.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/service_endpoint.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/service_endpoint.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/sha256_hash.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/sha256_hash.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/sha256_hmac.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/sha256_hmac.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/sha256_type.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/status_payload_keys.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/status_payload_keys.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/status_utils.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/status_utils.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/strerror.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/strerror.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/subject_token.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/subject_token.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/throw_delegate.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/throw_delegate.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/timer_queue.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/timer_queue.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/trace_propagator.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/trace_propagator.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/traced_stream_range.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/tuple.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/type_list.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/type_traits.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/url_encode.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/url_encode.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/user_agent_prefix.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/user_agent_prefix.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/utility.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/version_info.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/kms_key_name.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/kms_key_name.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/location.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/location.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/log.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/log.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/no_await_tag.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/opentelemetry_options.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/optional.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/options.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/options.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/polling_policy.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/project.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/project.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/retry_policy.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/rpc_metadata.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/status.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/status.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/status_or.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/stream_range.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/terminate_handler.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/terminate_handler.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/tracing_options.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/tracing_options.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/universe_domain_options.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/version.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/version.h)
target_link_libraries(
google_cloud_cpp_common
PUBLIC absl::base
absl::memory
absl::optional
absl::span
absl::str_format
absl::time
absl::variant
Threads::Threads)
if (WIN32)
target_compile_definitions(google_cloud_cpp_common
PRIVATE WIN32_LEAN_AND_MEAN)
target_link_libraries(google_cloud_cpp_common PUBLIC bcrypt)
else ()
target_link_libraries(google_cloud_cpp_common PUBLIC OpenSSL::Crypto ch_contrib::re2)
endif ()
google_cloud_cpp_add_common_options(google_cloud_cpp_common)
target_include_directories(
google_cloud_cpp_common PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
$<INSTALL_INTERFACE:include>)
# We're putting generated code into ${PROJECT_BINARY_DIR} (e.g. compiled
# protobufs or build info), so we need it on the include path, however we don't
# want it checked by linters so we mark it as SYSTEM.
target_include_directories(google_cloud_cpp_common SYSTEM
PUBLIC $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>)
target_compile_options(google_cloud_cpp_common
PUBLIC ${GOOGLE_CLOUD_CPP_EXCEPTIONS_FLAG})
set_target_properties(
google_cloud_cpp_common
PROPERTIES EXPORT_NAME "google-cloud-cpp::common"
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR})
add_library(google-cloud-cpp::common ALIAS google_cloud_cpp_common)
#create_bazel_config(google_cloud_cpp_common YEAR 2018)
# # Export the CMake targets to make it easy to create configuration files.
# install(
# EXPORT google_cloud_cpp_common-targets
# DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/google_cloud_cpp_common"
# COMPONENT google_cloud_cpp_development)
# # Install the libraries and headers in the locations determined by
# # GNUInstallDirs
# install(
# TARGETS google_cloud_cpp_common
# EXPORT google_cloud_cpp_common-targets
# RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
# COMPONENT google_cloud_cpp_runtime
# LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
# COMPONENT google_cloud_cpp_runtime
# NAMELINK_COMPONENT google_cloud_cpp_development
# ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
# COMPONENT google_cloud_cpp_development)
#google_cloud_cpp_install_headers(google_cloud_cpp_common include/google/cloud)
# google_cloud_cpp_add_pkgconfig(
# "common"
# "Google Cloud C++ Client Library Common Components"
# "Common Components used by the Google Cloud C++ Client Libraries."
# "absl_optional"
# "absl_span"
# "absl_strings"
# "absl_time"
# "absl_time_zone"
# "absl_variant"
# "${GOOGLE_CLOUD_CPP_OPENTELEMETRY_API}"
# NON_WIN32_REQUIRES
# openssl
# WIN32_LIBS
# bcrypt)
# Create and install the CMake configuration files.
# configure_file("config.cmake.in" "google_cloud_cpp_common-config.cmake" @ONLY)
# write_basic_package_version_file(
# "google_cloud_cpp_common-config-version.cmake"
# VERSION ${PROJECT_VERSION}
# COMPATIBILITY ExactVersion)
# install(
# FILES
# "${CMAKE_CURRENT_BINARY_DIR}/google_cloud_cpp_common-config.cmake"
# "${CMAKE_CURRENT_BINARY_DIR}/google_cloud_cpp_common-config-version.cmake"
# DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/google_cloud_cpp_common"
# COMPONENT google_cloud_cpp_development)
# if (GOOGLE_CLOUD_CPP_WITH_MOCKS)
# # Create a header-only library for the mocks. We use a CMake `INTERFACE`
# # library for these, a regular library would not work on macOS (where the
# # library needs at least one .o file).
# add_library(google_cloud_cpp_mocks INTERFACE)
# set(google_cloud_cpp_mocks_hdrs
# # cmake-format: sort
# mocks/current_options.h mocks/mock_async_streaming_read_write_rpc.h
# mocks/mock_stream_range.h)
# export_list_to_bazel("google_cloud_cpp_mocks.bzl"
# "google_cloud_cpp_mocks_hdrs" YEAR "2022")
# target_link_libraries(
# google_cloud_cpp_mocks INTERFACE google-cloud-cpp::common GTest::gmock
# GTest::gtest)
# set_target_properties(google_cloud_cpp_mocks
# PROPERTIES EXPORT_NAME google-cloud-cpp::mocks)
# target_include_directories(
# google_cloud_cpp_mocks
# INTERFACE $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
# $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}>
# $<INSTALL_INTERFACE:include>)
# target_compile_options(google_cloud_cpp_mocks
# INTERFACE ${GOOGLE_CLOUD_CPP_EXCEPTIONS_FLAG})
# add_library(google-cloud-cpp::mocks ALIAS google_cloud_cpp_mocks)
# install(
# FILES ${google_cloud_cpp_mocks_hdrs}
# DESTINATION "include/google/cloud/mocks"
# COMPONENT google_cloud_cpp_development)
# # Export the CMake targets to make it easy to create configuration files.
# install(
# EXPORT google_cloud_cpp_mocks-targets
# DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/google_cloud_cpp_mocks"
# COMPONENT google_cloud_cpp_development)
# install(
# TARGETS google_cloud_cpp_mocks
# EXPORT google_cloud_cpp_mocks-targets
# COMPONENT google_cloud_cpp_development)
# google_cloud_cpp_add_pkgconfig(
# "mocks" "Google Cloud C++ Testing Library"
# "Helpers for testing the Google Cloud C++ Client Libraries"
# "google_cloud_cpp_common" "gmock")
# # Create and install the CMake configuration files.
# configure_file("mocks-config.cmake.in"
# "google_cloud_cpp_mocks-config.cmake" @ONLY)
# write_basic_package_version_file(
# "google_cloud_cpp_mocks-config-version.cmake"
# VERSION ${PROJECT_VERSION}
# COMPATIBILITY ExactVersion)
# install(
# FILES
# "${CMAKE_CURRENT_BINARY_DIR}/google_cloud_cpp_mocks-config.cmake"
# "${CMAKE_CURRENT_BINARY_DIR}/google_cloud_cpp_mocks-config-version.cmake"
# DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/google_cloud_cpp_mocks"
# COMPONENT google_cloud_cpp_development)
# endif ()
# if (BUILD_TESTING)
# include(FindBenchmarkWithWorkarounds)
# set(google_cloud_cpp_common_unit_tests
# # cmake-format: sort
# access_token_test.cc
# common_options_test.cc
# future_coroutines_test.cc
# future_generic_test.cc
# future_generic_then_test.cc
# future_void_test.cc
# future_void_then_test.cc
# internal/algorithm_test.cc
# internal/api_client_header_test.cc
# internal/backoff_policy_test.cc
# internal/base64_transforms_test.cc
# internal/big_endian_test.cc
# internal/call_context_test.cc
# internal/clock_test.cc
# internal/compiler_info_test.cc
# internal/compute_engine_util_test.cc
# internal/credentials_impl_test.cc
# internal/debug_future_status_test.cc
# internal/debug_string_test.cc
# internal/detect_gcp_test.cc
# internal/error_context_test.cc
# internal/filesystem_test.cc
# internal/format_time_point_test.cc
# internal/future_impl_test.cc
# internal/future_then_impl_test.cc
# internal/group_options_test.cc
# internal/invocation_id_generator_test.cc
# internal/invoke_result_test.cc
# internal/log_impl_test.cc
# internal/make_status_test.cc
# internal/noexcept_action_test.cc
# internal/opentelemetry_context_test.cc
# internal/opentelemetry_test.cc
# internal/pagination_range_test.cc
# internal/parse_rfc3339_test.cc
# internal/populate_common_options_test.cc
# internal/random_test.cc
# internal/retry_loop_helpers_test.cc
# internal/retry_policy_impl_test.cc
# internal/service_endpoint_test.cc
# internal/sha256_hash_test.cc
# internal/sha256_hmac_test.cc
# internal/status_payload_keys_test.cc
# internal/status_utils_test.cc
# internal/strerror_test.cc
# internal/subject_token_test.cc
# internal/throw_delegate_test.cc
# internal/timer_queue_test.cc
# internal/trace_propagator_test.cc
# internal/traced_stream_range_test.cc
# internal/tuple_test.cc
# internal/type_list_test.cc
# internal/url_encode_test.cc
# internal/user_agent_prefix_test.cc
# internal/utility_test.cc
# kms_key_name_test.cc
# location_test.cc
# log_test.cc
# mocks/current_options_test.cc
# mocks/mock_stream_range_test.cc
# options_test.cc
# polling_policy_test.cc
# project_test.cc
# status_or_test.cc
# status_test.cc
# stream_range_test.cc
# terminate_handler_test.cc
# tracing_options_test.cc)
# # Export the list of unit tests so the Bazel BUILD file can pick it up.
# export_list_to_bazel("google_cloud_cpp_common_unit_tests.bzl"
# "google_cloud_cpp_common_unit_tests" YEAR "2018")
# foreach (fname ${google_cloud_cpp_common_unit_tests})
# google_cloud_cpp_add_executable(target "common" "${fname}")
# target_link_libraries(
# ${target}
# PRIVATE google_cloud_cpp_testing
# google-cloud-cpp::common
# google-cloud-cpp::mocks
# absl::variant
# GTest::gmock_main
# GTest::gmock
# GTest::gtest)
# google_cloud_cpp_add_common_options(${target})
# add_test(NAME ${target} COMMAND ${target})
# endforeach ()
# set(google_cloud_cpp_common_benchmarks # cmake-format: sort
# options_benchmark.cc)
# # Export the list of benchmarks to a .bzl file so we do not need to maintain
# # the list in two places.
# export_list_to_bazel("google_cloud_cpp_common_benchmarks.bzl"
# "google_cloud_cpp_common_benchmarks" YEAR "2020")
# # Generate a target for each benchmark.
# foreach (fname ${google_cloud_cpp_common_benchmarks})
# google_cloud_cpp_add_executable(target "common" "${fname}")
# add_test(NAME ${target} COMMAND ${target})
# target_link_libraries(${target} PRIVATE google-cloud-cpp::common
# benchmark::benchmark_main)
# google_cloud_cpp_add_common_options(${target})
# endforeach ()
# endif ()
# if (BUILD_TESTING AND GOOGLE_CLOUD_CPP_ENABLE_CXX_EXCEPTIONS)
# google_cloud_cpp_add_samples_relative("common" "samples/")
# endif ()

View File

@ -1,350 +0,0 @@
# ~~~
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ~~~
# File copied from google-cloud-cpp/google-cloud-cpp/google_cloud_cpp_grpc_utils.cmake with minor modifications.
set(GOOGLE_CLOUD_CPP_COMMON_DIR "${GOOGLE_CLOUD_CPP_DIR}/google/cloud")
# the library
add_library(
google_cloud_cpp_grpc_utils # cmake-format: sort
${GOOGLE_CLOUD_CPP_COMMON_DIR}/async_operation.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/async_streaming_read_write_rpc.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/background_threads.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/completion_queue.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/completion_queue.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/connection_options.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/connection_options.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/grpc_error_delegate.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/grpc_error_delegate.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/grpc_options.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/grpc_options.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/grpc_utils/async_operation.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/grpc_utils/completion_queue.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/grpc_utils/grpc_error_delegate.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/grpc_utils/version.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/iam_updater.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/async_connection_ready.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/async_connection_ready.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/async_long_running_operation.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/async_polling_loop.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/async_polling_loop.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/async_read_stream_impl.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/async_read_write_stream_auth.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/async_read_write_stream_impl.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/async_read_write_stream_logging.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/async_read_write_stream_timeout.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/async_read_write_stream_tracing.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/async_retry_loop.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/async_retry_unary_rpc.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/async_rpc_details.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/async_streaming_read_rpc.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/async_streaming_read_rpc_auth.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/async_streaming_read_rpc_impl.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/async_streaming_read_rpc_logging.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/async_streaming_read_rpc_timeout.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/async_streaming_read_rpc_tracing.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/async_streaming_write_rpc.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/async_streaming_write_rpc_auth.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/async_streaming_write_rpc_impl.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/async_streaming_write_rpc_logging.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/async_streaming_write_rpc_timeout.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/async_streaming_write_rpc_tracing.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/background_threads_impl.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/background_threads_impl.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/completion_queue_impl.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/debug_string_protobuf.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/debug_string_protobuf.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/debug_string_status.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/debug_string_status.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/default_completion_queue_impl.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/default_completion_queue_impl.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/extract_long_running_result.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/extract_long_running_result.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/grpc_access_token_authentication.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/grpc_access_token_authentication.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/grpc_api_key_authentication.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/grpc_api_key_authentication.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/grpc_async_access_token_cache.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/grpc_async_access_token_cache.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/grpc_channel_credentials_authentication.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/grpc_channel_credentials_authentication.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/grpc_impersonate_service_account.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/grpc_impersonate_service_account.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/grpc_metadata_view.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/grpc_opentelemetry.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/grpc_opentelemetry.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/grpc_request_metadata.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/grpc_request_metadata.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/grpc_service_account_authentication.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/grpc_service_account_authentication.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/log_wrapper.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/log_wrapper.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/minimal_iam_credentials_stub.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/minimal_iam_credentials_stub.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/populate_grpc_options.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/populate_grpc_options.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/resumable_streaming_read_rpc.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/retry_loop.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/routing_matcher.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/setup_context.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/streaming_read_rpc.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/streaming_read_rpc.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/streaming_read_rpc_logging.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/streaming_read_rpc_tracing.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/streaming_write_rpc.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/streaming_write_rpc_impl.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/streaming_write_rpc_impl.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/streaming_write_rpc_logging.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/streaming_write_rpc_tracing.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/time_utils.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/time_utils.h
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/unified_grpc_credentials.cc
${GOOGLE_CLOUD_CPP_COMMON_DIR}/internal/unified_grpc_credentials.h)
target_link_libraries(
google_cloud_cpp_grpc_utils
PUBLIC absl::function_ref
absl::memory
absl::time
absl::variant
google-cloud-cpp::iam_credentials_v1_iamcredentials_protos
google-cloud-cpp::iam_v1_policy_protos
google-cloud-cpp::longrunning_operations_protos
google-cloud-cpp::iam_v1_iam_policy_protos
google-cloud-cpp::rpc_error_details_protos
google-cloud-cpp::rpc_status_protos
google-cloud-cpp::common
gRPC::grpc++
gRPC::grpc)
google_cloud_cpp_add_common_options(google_cloud_cpp_grpc_utils)
target_include_directories(
google_cloud_cpp_grpc_utils PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
$<INSTALL_INTERFACE:include>)
target_compile_options(google_cloud_cpp_grpc_utils
PUBLIC ${GOOGLE_CLOUD_CPP_EXCEPTIONS_FLAG})
set_target_properties(
google_cloud_cpp_grpc_utils
PROPERTIES EXPORT_NAME "google-cloud-cpp::grpc_utils"
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR})
add_library(google-cloud-cpp::grpc_utils ALIAS google_cloud_cpp_grpc_utils)
#create_bazel_config(google_cloud_cpp_grpc_utils YEAR 2019)
# # Install the libraries and headers in the locations determined by
# # GNUInstallDirs
# install(
# TARGETS
# EXPORT grpc_utils-targets
# RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
# LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
# ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
# COMPONENT google_cloud_cpp_development)
# # Export the CMake targets to make it easy to create configuration files.
# install(
# EXPORT grpc_utils-targets
# DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/google_cloud_cpp_grpc_utils"
# COMPONENT google_cloud_cpp_development)
# install(
# TARGETS google_cloud_cpp_grpc_utils
# EXPORT grpc_utils-targets
# RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
# COMPONENT google_cloud_cpp_runtime
# LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
# COMPONENT google_cloud_cpp_runtime
# NAMELINK_COMPONENT google_cloud_cpp_development
# ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
# COMPONENT google_cloud_cpp_development)
# google_cloud_cpp_install_headers(google_cloud_cpp_grpc_utils
# include/google/cloud)
# google_cloud_cpp_add_pkgconfig(
# grpc_utils
# "gRPC Utilities for the Google Cloud C++ Client Library"
# "Provides gRPC Utilities for the Google Cloud C++ Client Library."
# "google_cloud_cpp_common"
# "google_cloud_cpp_iam_credentials_v1_iamcredentials_protos"
# "google_cloud_cpp_iam_v1_policy_protos"
# "google_cloud_cpp_iam_v1_iam_policy_protos"
# "google_cloud_cpp_longrunning_operations_protos"
# "google_cloud_cpp_rpc_status_protos"
# "absl_function_ref"
# "absl_strings"
# "absl_time"
# "absl_time_zone"
# "absl_variant"
# "openssl")
# # Create and install the CMake configuration files.
# configure_file("grpc_utils/config.cmake.in"
# "google_cloud_cpp_grpc_utils-config.cmake" @ONLY)
# write_basic_package_version_file(
# "google_cloud_cpp_grpc_utils-config-version.cmake"
# VERSION ${PROJECT_VERSION}
# COMPATIBILITY ExactVersion)
# install(
# FILES
# "${CMAKE_CURRENT_BINARY_DIR}/google_cloud_cpp_grpc_utils-config.cmake"
# "${CMAKE_CURRENT_BINARY_DIR}/google_cloud_cpp_grpc_utils-config-version.cmake"
# DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/google_cloud_cpp_grpc_utils"
# COMPONENT google_cloud_cpp_development)
# function (google_cloud_cpp_grpc_utils_add_test fname labels)
# google_cloud_cpp_add_executable(target "common" "${fname}")
# target_link_libraries(
# ${target}
# PRIVATE google-cloud-cpp::grpc_utils
# google_cloud_cpp_testing_grpc
# google_cloud_cpp_testing
# google-cloud-cpp::common
# absl::variant
# GTest::gmock_main
# GTest::gmock
# GTest::gtest
# gRPC::grpc++
# gRPC::grpc)
# google_cloud_cpp_add_common_options(${target})
# add_test(NAME ${target} COMMAND ${target})
# set_tests_properties(${target} PROPERTIES LABELS "${labels}")
# endfunction ()
# if (BUILD_TESTING)
# include(FindBenchmarkWithWorkarounds)
# # List the unit tests, then setup the targets and dependencies.
# set(google_cloud_cpp_grpc_utils_unit_tests
# # cmake-format: sort
# completion_queue_test.cc
# connection_options_test.cc
# grpc_error_delegate_test.cc
# grpc_options_test.cc
# internal/async_connection_ready_test.cc
# internal/async_long_running_operation_test.cc
# internal/async_polling_loop_test.cc
# internal/async_read_write_stream_auth_test.cc
# internal/async_read_write_stream_impl_test.cc
# internal/async_read_write_stream_logging_test.cc
# internal/async_read_write_stream_timeout_test.cc
# internal/async_read_write_stream_tracing_test.cc
# internal/async_retry_loop_test.cc
# internal/async_retry_unary_rpc_test.cc
# internal/async_streaming_read_rpc_auth_test.cc
# internal/async_streaming_read_rpc_impl_test.cc
# internal/async_streaming_read_rpc_logging_test.cc
# internal/async_streaming_read_rpc_timeout_test.cc
# internal/async_streaming_read_rpc_tracing_test.cc
# internal/async_streaming_write_rpc_auth_test.cc
# internal/async_streaming_write_rpc_impl_test.cc
# internal/async_streaming_write_rpc_logging_test.cc
# internal/async_streaming_write_rpc_timeout_test.cc
# internal/async_streaming_write_rpc_tracing_test.cc
# internal/background_threads_impl_test.cc
# internal/debug_string_protobuf_test.cc
# internal/debug_string_status_test.cc
# internal/extract_long_running_result_test.cc
# internal/grpc_access_token_authentication_test.cc
# internal/grpc_async_access_token_cache_test.cc
# internal/grpc_channel_credentials_authentication_test.cc
# internal/grpc_opentelemetry_test.cc
# internal/grpc_request_metadata_test.cc
# internal/grpc_service_account_authentication_test.cc
# internal/log_wrapper_test.cc
# internal/minimal_iam_credentials_stub_test.cc
# internal/populate_grpc_options_test.cc
# internal/resumable_streaming_read_rpc_test.cc
# internal/retry_loop_test.cc
# internal/routing_matcher_test.cc
# internal/streaming_read_rpc_logging_test.cc
# internal/streaming_read_rpc_test.cc
# internal/streaming_read_rpc_tracing_test.cc
# internal/streaming_write_rpc_logging_test.cc
# internal/streaming_write_rpc_test.cc
# internal/streaming_write_rpc_tracing_test.cc
# internal/time_utils_test.cc
# internal/unified_grpc_credentials_test.cc)
# # List the unit tests, then setup the targets and dependencies.
# set(google_cloud_cpp_grpc_utils_integration_tests
# # cmake-format: sort
# internal/grpc_impersonate_service_account_integration_test.cc)
# # Export the list of unit and integration tests so the Bazel BUILD file can
# # pick them up.
# export_list_to_bazel("google_cloud_cpp_grpc_utils_unit_tests.bzl"
# "google_cloud_cpp_grpc_utils_unit_tests" YEAR "2019")
# export_list_to_bazel(
# "google_cloud_cpp_grpc_utils_integration_tests.bzl"
# "google_cloud_cpp_grpc_utils_integration_tests" YEAR "2021")
# foreach (fname ${google_cloud_cpp_grpc_utils_unit_tests})
# google_cloud_cpp_grpc_utils_add_test("${fname}" "")
# endforeach ()
# # TODO(#12485) - remove dependency on bigtable in this integration test.
# if (NOT bigtable IN_LIST GOOGLE_CLOUD_CPP_ENABLE)
# list(REMOVE_ITEM google_cloud_cpp_grpc_utils_integration_tests
# "internal/grpc_impersonate_service_account_integration_test.cc")
# endif ()
# foreach (fname ${google_cloud_cpp_grpc_utils_integration_tests})
# google_cloud_cpp_add_executable(target "common" "${fname}")
# target_link_libraries(
# ${target}
# PRIVATE google-cloud-cpp::grpc_utils
# google_cloud_cpp_testing_grpc
# google_cloud_cpp_testing
# google-cloud-cpp::common
# google-cloud-cpp::iam_credentials_v1_iamcredentials_protos
# absl::variant
# GTest::gmock_main
# GTest::gmock
# GTest::gtest
# gRPC::grpc++
# gRPC::grpc)
# google_cloud_cpp_add_common_options(${target})
# add_test(NAME ${target} COMMAND ${target})
# set_tests_properties(${target} PROPERTIES LABELS
# "integration-test-production")
# # TODO(12485) - remove dep on bigtable_protos
# if (bigtable IN_LIST GOOGLE_CLOUD_CPP_ENABLE)
# target_link_libraries(${target}
# PRIVATE google-cloud-cpp::bigtable_protos)
# endif ()
# endforeach ()
# set(google_cloud_cpp_grpc_utils_benchmarks # cmake-format: sortable
# completion_queue_benchmark.cc)
# # Export the list of benchmarks to a .bzl file so we do not need to maintain
# # the list in two places.
# export_list_to_bazel("google_cloud_cpp_grpc_utils_benchmarks.bzl"
# "google_cloud_cpp_grpc_utils_benchmarks" YEAR "2020")
# # Generate a target for each benchmark.
# foreach (fname ${google_cloud_cpp_grpc_utils_benchmarks})
# google_cloud_cpp_add_executable(target "common" "${fname}")
# add_test(NAME ${target} COMMAND ${target})
# target_link_libraries(
# ${target}
# PRIVATE google-cloud-cpp::grpc_utils google-cloud-cpp::common
# benchmark::benchmark_main)
# google_cloud_cpp_add_common_options(${target})
# endforeach ()
# endif ()

1
contrib/jwt-cpp vendored

@ -1 +0,0 @@
Subproject commit a6927cb8140858c34e05d1a954626b9849fbcdfc

View File

@ -1,23 +0,0 @@
set(ENABLE_JWT_CPP_DEFAULT OFF)
if(ENABLE_LIBRARIES AND CLICKHOUSE_CLOUD)
set(ENABLE_JWT_CPP_DEFAULT ON)
endif()
option(ENABLE_JWT_CPP "Enable jwt-cpp library" ${ENABLE_JWT_CPP_DEFAULT})
if (NOT ENABLE_JWT_CPP)
message(STATUS "Not using jwt-cpp")
return()
endif()
if(ENABLE_JWT_CPP)
if(NOT TARGET OpenSSL::Crypto)
message (${RECONFIGURE_MESSAGE_LEVEL} "Can't use jwt-cpp without OpenSSL")
endif()
endif()
set (JWT_CPP_INCLUDE_DIR "${ClickHouse_SOURCE_DIR}/contrib/jwt-cpp/include")
add_library (_jwt-cpp INTERFACE)
target_include_directories(_jwt-cpp SYSTEM BEFORE INTERFACE ${JWT_CPP_INCLUDE_DIR})
add_library(ch_contrib::jwt-cpp ALIAS _jwt-cpp)

View File

@ -24,7 +24,7 @@ git config --file .gitmodules --get-regexp '.*path' | sed 's/[^ ]* //' | xargs -
# We don't want to depend on any third-party CMake files. # We don't want to depend on any third-party CMake files.
# To check it, find and delete them. # To check it, find and delete them.
grep -o -P '"contrib/[^"]+"' .gitmodules | grep -o -P '"contrib/[^"]+"' .gitmodules |
grep -v -P 'contrib/(llvm-project|google-protobuf|grpc|abseil-cpp|corrosion|aws-crt-cpp|google-cloud-cpp)' | grep -v -P 'contrib/(llvm-project|google-protobuf|grpc|abseil-cpp|corrosion|aws-crt-cpp)' |
xargs -I@ find @ \ xargs -I@ find @ \
-'(' -name 'CMakeLists.txt' -or -name '*.cmake' -')' -and -not -name '*.h.cmake' \ -'(' -name 'CMakeLists.txt' -or -name '*.cmake' -')' -and -not -name '*.h.cmake' \
-delete -delete

View File

@ -31,8 +31,8 @@ COPY entrypoint.sh /entrypoint.sh
ARG TARGETARCH ARG TARGETARCH
RUN arch=${TARGETARCH:-amd64} \ RUN arch=${TARGETARCH:-amd64} \
&& case $arch in \ && case $arch in \
amd64) mkdir -p /lib64 && ln -sf /lib/ld-2.35.so /lib64/ld-linux-x86-64.so.2 ;; \ amd64) mkdir -p /lib64 && ln -sf /lib/ld-2.31.so /lib64/ld-linux-x86-64.so.2 ;; \
arm64) ln -sf /lib/ld-2.35.so /lib/ld-linux-aarch64.so.1 ;; \ arm64) ln -sf /lib/ld-2.31.so /lib/ld-linux-aarch64.so.1 ;; \
esac esac
# lts / testing / prestable / etc # lts / testing / prestable / etc
@ -86,8 +86,7 @@ RUN arch=${TARGETARCH:-amd64} \
ARG DEFAULT_CONFIG_DIR="/etc/clickhouse-keeper" ARG DEFAULT_CONFIG_DIR="/etc/clickhouse-keeper"
ARG DEFAULT_DATA_DIR="/var/lib/clickhouse-keeper" ARG DEFAULT_DATA_DIR="/var/lib/clickhouse-keeper"
ARG DEFAULT_LOG_DIR="/var/log/clickhouse-keeper" ARG DEFAULT_LOG_DIR="/var/log/clickhouse-keeper"
RUN clickhouse-keeper --version \ RUN mkdir -p "${DEFAULT_DATA_DIR}" "${DEFAULT_LOG_DIR}" "${DEFAULT_CONFIG_DIR}" \
&& mkdir -p "${DEFAULT_DATA_DIR}" "${DEFAULT_LOG_DIR}" "${DEFAULT_CONFIG_DIR}" \
&& chown clickhouse:clickhouse "${DEFAULT_DATA_DIR}" \ && chown clickhouse:clickhouse "${DEFAULT_DATA_DIR}" \
&& chown root:clickhouse "${DEFAULT_LOG_DIR}" \ && chown root:clickhouse "${DEFAULT_LOG_DIR}" \
&& chmod ugo+Xrw -R "${DEFAULT_DATA_DIR}" "${DEFAULT_LOG_DIR}" "${DEFAULT_CONFIG_DIR}" && chmod ugo+Xrw -R "${DEFAULT_DATA_DIR}" "${DEFAULT_LOG_DIR}" "${DEFAULT_CONFIG_DIR}"

View File

@ -258,7 +258,7 @@ CREATE TABLE table_with_asterisk (name String, value UInt32)
- [s3_truncate_on_insert](/docs/en/operations/settings/settings.md#s3_truncate_on_insert) - allows to truncate file before insert into it. Disabled by default. - [s3_truncate_on_insert](/docs/en/operations/settings/settings.md#s3_truncate_on_insert) - allows to truncate file before insert into it. Disabled by default.
- [s3_create_new_file_on_insert](/docs/en/operations/settings/settings.md#s3_create_new_file_on_insert) - allows to create a new file on each insert if format has suffix. Disabled by default. - [s3_create_new_file_on_insert](/docs/en/operations/settings/settings.md#s3_create_new_file_on_insert) - allows to create a new file on each insert if format has suffix. Disabled by default.
- [s3_skip_empty_files](/docs/en/operations/settings/settings.md#s3_skip_empty_files) - allows to skip empty files while reading. Enabled by default. - [s3_skip_empty_files](/docs/en/operations/settings/settings.md#s3_skip_empty_files) - allows to skip empty files while reading. Disabled by default.
## S3-related Settings {#settings} ## S3-related Settings {#settings}

View File

@ -3286,17 +3286,3 @@ Use the legacy MongoDB integration implementation. Deprecated.
Type: Bool Type: Bool
Default value: `true`. Default value: `true`.
## allowed_feature_tier
Controls if the user can change settings related to the different feature tiers.
0 - Changes to any setting are allowed (experimental, beta, production).
1 - Only changes to beta and production feature settings are allowed. Changes to experimental settings are rejected.
2 - Only changes to production settings are allowed. Changes to experimental or beta settings are rejected.
This is equivalent to setting a readonly constraint on all EXPERIMENTAL / BETA features.
```
Type: UInt32
Default value: `0` (all settings can be changed).

View File

@ -49,4 +49,4 @@ LIMIT 2
**See Also** **See Also**
- [DeltaLake engine](/docs/en/engines/table-engines/integrations/deltalake.md) - [DeltaLake engine](/docs/en/engines/table-engines/integrations/deltalake.md)
- [DeltaLake cluster table function](/docs/en/sql-reference/table-functions/deltalakeCluster.md)

View File

@ -1,30 +0,0 @@
---
slug: /en/sql-reference/table-functions/deltalakeCluster
sidebar_position: 46
sidebar_label: deltaLakeCluster
title: "deltaLakeCluster Table Function"
---
This is an extension to the [deltaLake](/docs/en/sql-reference/table-functions/deltalake.md) table function.
Allows processing files from [Delta Lake](https://github.com/delta-io/delta) tables in Amazon S3 in parallel from many nodes in a specified cluster. On initiator it creates a connection to all nodes in the cluster and dispatches each file dynamically. On the worker node it asks the initiator about the next task to process and processes it. This is repeated until all tasks are finished.
**Syntax**
``` sql
deltaLakeCluster(cluster_name, url [,aws_access_key_id, aws_secret_access_key] [,format] [,structure] [,compression])
```
**Arguments**
- `cluster_name` — Name of a cluster that is used to build a set of addresses and connection parameters to remote and local servers.
- Description of all other arguments coincides with description of arguments in equivalent [deltaLake](/docs/en/sql-reference/table-functions/deltalake.md) table function.
**Returned value**
A table with the specified structure for reading data from cluster in the specified Delta Lake table in S3.
**See Also**
- [deltaLake engine](/docs/en/engines/table-engines/integrations/deltalake.md)
- [deltaLake table function](/docs/en/sql-reference/table-functions/deltalake.md)

View File

@ -29,4 +29,4 @@ A table with the specified structure for reading data in the specified Hudi tabl
**See Also** **See Also**
- [Hudi engine](/docs/en/engines/table-engines/integrations/hudi.md) - [Hudi engine](/docs/en/engines/table-engines/integrations/hudi.md)
- [Hudi cluster table function](/docs/en/sql-reference/table-functions/hudiCluster.md)

View File

@ -1,30 +0,0 @@
---
slug: /en/sql-reference/table-functions/hudiCluster
sidebar_position: 86
sidebar_label: hudiCluster
title: "hudiCluster Table Function"
---
This is an extension to the [hudi](/docs/en/sql-reference/table-functions/hudi.md) table function.
Allows processing files from Apache [Hudi](https://hudi.apache.org/) tables in Amazon S3 in parallel from many nodes in a specified cluster. On initiator it creates a connection to all nodes in the cluster and dispatches each file dynamically. On the worker node it asks the initiator about the next task to process and processes it. This is repeated until all tasks are finished.
**Syntax**
``` sql
hudiCluster(cluster_name, url [,aws_access_key_id, aws_secret_access_key] [,format] [,structure] [,compression])
```
**Arguments**
- `cluster_name` — Name of a cluster that is used to build a set of addresses and connection parameters to remote and local servers.
- Description of all other arguments coincides with description of arguments in equivalent [hudi](/docs/en/sql-reference/table-functions/hudi.md) table function.
**Returned value**
A table with the specified structure for reading data from cluster in the specified Hudi table in S3.
**See Also**
- [Hudi engine](/docs/en/engines/table-engines/integrations/hudi.md)
- [Hudi table function](/docs/en/sql-reference/table-functions/hudi.md)

View File

@ -72,4 +72,3 @@ Table function `iceberg` is an alias to `icebergS3` now.
**See Also** **See Also**
- [Iceberg engine](/docs/en/engines/table-engines/integrations/iceberg.md) - [Iceberg engine](/docs/en/engines/table-engines/integrations/iceberg.md)
- [Iceberg cluster table function](/docs/en/sql-reference/table-functions/icebergCluster.md)

View File

@ -1,43 +0,0 @@
---
slug: /en/sql-reference/table-functions/icebergCluster
sidebar_position: 91
sidebar_label: icebergCluster
title: "icebergCluster Table Function"
---
This is an extension to the [iceberg](/docs/en/sql-reference/table-functions/iceberg.md) table function.
Allows processing files from Apache [Iceberg](https://iceberg.apache.org/) in parallel from many nodes in a specified cluster. On initiator it creates a connection to all nodes in the cluster and dispatches each file dynamically. On the worker node it asks the initiator about the next task to process and processes it. This is repeated until all tasks are finished.
**Syntax**
``` sql
icebergS3Cluster(cluster_name, url [, NOSIGN | access_key_id, secret_access_key, [session_token]] [,format] [,compression_method])
icebergS3Cluster(cluster_name, named_collection[, option=value [,..]])
icebergAzureCluster(cluster_name, connection_string|storage_account_url, container_name, blobpath, [,account_name], [,account_key] [,format] [,compression_method])
icebergAzureCluster(cluster_name, named_collection[, option=value [,..]])
icebergHDFSCluster(cluster_name, path_to_table, [,format] [,compression_method])
icebergHDFSCluster(cluster_name, named_collection[, option=value [,..]])
```
**Arguments**
- `cluster_name` — Name of a cluster that is used to build a set of addresses and connection parameters to remote and local servers.
- Description of all other arguments coincides with description of arguments in equivalent [iceberg](/docs/en/sql-reference/table-functions/iceberg.md) table function.
**Returned value**
A table with the specified structure for reading data from cluster in the specified Iceberg table.
**Examples**
```sql
SELECT * FROM icebergS3Cluster('cluster_simple', 'http://test.s3.amazonaws.com/clickhouse-bucket/test_table', 'test', 'test')
```
**See Also**
- [Iceberg engine](/docs/en/engines/table-engines/integrations/iceberg.md)
- [Iceberg table function](/docs/en/sql-reference/table-functions/iceberg.md)

View File

@ -317,7 +317,7 @@ SELECT * from s3('s3://data/path/date=*/country=*/code=*/*.parquet') where _date
- [s3_truncate_on_insert](/docs/en/operations/settings/settings.md#s3_truncate_on_insert) - allows to truncate file before insert into it. Disabled by default. - [s3_truncate_on_insert](/docs/en/operations/settings/settings.md#s3_truncate_on_insert) - allows to truncate file before insert into it. Disabled by default.
- [s3_create_new_file_on_insert](/docs/en/operations/settings/settings.md#s3_create_new_file_on_insert) - allows to create a new file on each insert if format has suffix. Disabled by default. - [s3_create_new_file_on_insert](/docs/en/operations/settings/settings.md#s3_create_new_file_on_insert) - allows to create a new file on each insert if format has suffix. Disabled by default.
- [s3_skip_empty_files](/docs/en/operations/settings/settings.md#s3_skip_empty_files) - allows to skip empty files while reading. Enabled by default. - [s3_skip_empty_files](/docs/en/operations/settings/settings.md#s3_skip_empty_files) - allows to skip empty files while reading. Disabled by default.
**See Also** **See Also**

View File

@ -79,7 +79,6 @@ namespace Setting
namespace ServerSetting namespace ServerSetting
{ {
extern const ServerSettingsUInt32 allowed_feature_tier;
extern const ServerSettingsDouble cache_size_to_ram_max_ratio; extern const ServerSettingsDouble cache_size_to_ram_max_ratio;
extern const ServerSettingsUInt64 compiled_expression_cache_elements_size; extern const ServerSettingsUInt64 compiled_expression_cache_elements_size;
extern const ServerSettingsUInt64 compiled_expression_cache_size; extern const ServerSettingsUInt64 compiled_expression_cache_size;
@ -790,9 +789,6 @@ void LocalServer::processConfig()
/// Initialize a dummy query cache. /// Initialize a dummy query cache.
global_context->setQueryCache(0, 0, 0, 0); global_context->setQueryCache(0, 0, 0, 0);
/// Initialize allowed tiers
global_context->getAccessControl().setAllowTierSettings(server_settings[ServerSetting::allowed_feature_tier]);
#if USE_EMBEDDED_COMPILER #if USE_EMBEDDED_COMPILER
size_t compiled_expression_cache_max_size_in_bytes = server_settings[ServerSetting::compiled_expression_cache_size]; size_t compiled_expression_cache_max_size_in_bytes = server_settings[ServerSetting::compiled_expression_cache_size];
size_t compiled_expression_cache_max_elements = server_settings[ServerSetting::compiled_expression_cache_elements_size]; size_t compiled_expression_cache_max_elements = server_settings[ServerSetting::compiled_expression_cache_elements_size];

View File

@ -166,7 +166,6 @@ namespace MergeTreeSetting
namespace ServerSetting namespace ServerSetting
{ {
extern const ServerSettingsUInt32 allowed_feature_tier;
extern const ServerSettingsUInt32 asynchronous_heavy_metrics_update_period_s; extern const ServerSettingsUInt32 asynchronous_heavy_metrics_update_period_s;
extern const ServerSettingsUInt32 asynchronous_metrics_update_period_s; extern const ServerSettingsUInt32 asynchronous_metrics_update_period_s;
extern const ServerSettingsBool asynchronous_metrics_enable_heavy_metrics; extern const ServerSettingsBool asynchronous_metrics_enable_heavy_metrics;
@ -1772,7 +1771,6 @@ try
global_context->setMaxDictionaryNumToWarn(new_server_settings[ServerSetting::max_dictionary_num_to_warn]); global_context->setMaxDictionaryNumToWarn(new_server_settings[ServerSetting::max_dictionary_num_to_warn]);
global_context->setMaxDatabaseNumToWarn(new_server_settings[ServerSetting::max_database_num_to_warn]); global_context->setMaxDatabaseNumToWarn(new_server_settings[ServerSetting::max_database_num_to_warn]);
global_context->setMaxPartNumToWarn(new_server_settings[ServerSetting::max_part_num_to_warn]); global_context->setMaxPartNumToWarn(new_server_settings[ServerSetting::max_part_num_to_warn]);
global_context->getAccessControl().setAllowTierSettings(new_server_settings[ServerSetting::allowed_feature_tier]);
/// Only for system.server_settings /// Only for system.server_settings
global_context->setConfigReloaderInterval(new_server_settings[ServerSetting::config_reload_interval_ms]); global_context->setConfigReloaderInterval(new_server_settings[ServerSetting::config_reload_interval_ms]);
@ -2163,12 +2161,9 @@ try
/// Check sanity of MergeTreeSettings on server startup /// Check sanity of MergeTreeSettings on server startup
{ {
/// All settings can be changed in the global config
bool allowed_experimental = true;
bool allowed_beta = true;
size_t background_pool_tasks = global_context->getMergeMutateExecutor()->getMaxTasksCount(); size_t background_pool_tasks = global_context->getMergeMutateExecutor()->getMaxTasksCount();
global_context->getMergeTreeSettings().sanityCheck(background_pool_tasks, allowed_experimental, allowed_beta); global_context->getMergeTreeSettings().sanityCheck(background_pool_tasks);
global_context->getReplicatedMergeTreeSettings().sanityCheck(background_pool_tasks, allowed_experimental, allowed_beta); global_context->getReplicatedMergeTreeSettings().sanityCheck(background_pool_tasks);
} }
/// try set up encryption. There are some errors in config, error will be printed and server wouldn't start. /// try set up encryption. There are some errors in config, error will be printed and server wouldn't start.
CompressionCodecEncrypted::Configuration::instance().load(config(), "encryption_codecs"); CompressionCodecEncrypted::Configuration::instance().load(config(), "encryption_codecs");

View File

@ -876,28 +876,4 @@ void AccessControl::allowAllSettings()
custom_settings_prefixes->registerPrefixes({""}); custom_settings_prefixes->registerPrefixes({""});
} }
void AccessControl::setAllowTierSettings(UInt32 value)
{
allow_experimental_tier_settings = value == 0;
allow_beta_tier_settings = value <= 1;
}
UInt32 AccessControl::getAllowTierSettings() const
{
if (allow_experimental_tier_settings)
return 0;
if (allow_beta_tier_settings)
return 1;
return 2;
}
bool AccessControl::getAllowExperimentalTierSettings() const
{
return allow_experimental_tier_settings;
}
bool AccessControl::getAllowBetaTierSettings() const
{
return allow_beta_tier_settings;
}
} }

View File

@ -243,11 +243,6 @@ public:
/// Allow all setting names - this can be used in clients to pass-through unknown settings to the server. /// Allow all setting names - this can be used in clients to pass-through unknown settings to the server.
void allowAllSettings(); void allowAllSettings();
void setAllowTierSettings(UInt32 value);
UInt32 getAllowTierSettings() const;
bool getAllowExperimentalTierSettings() const;
bool getAllowBetaTierSettings() const;
private: private:
class ContextAccessCache; class ContextAccessCache;
class CustomSettingsPrefixes; class CustomSettingsPrefixes;
@ -277,8 +272,6 @@ private:
std::atomic_bool table_engines_require_grant = false; std::atomic_bool table_engines_require_grant = false;
std::atomic_int bcrypt_workfactor = 12; std::atomic_int bcrypt_workfactor = 12;
std::atomic<AuthenticationType> default_password_type = AuthenticationType::SHA256_PASSWORD; std::atomic<AuthenticationType> default_password_type = AuthenticationType::SHA256_PASSWORD;
std::atomic_bool allow_experimental_tier_settings = true;
std::atomic_bool allow_beta_tier_settings = true;
}; };
} }

View File

@ -9,12 +9,6 @@
namespace DB namespace DB
{ {
namespace ErrorCodes
{
extern const int INVALID_GRANT;
}
namespace namespace
{ {
void formatOptions(bool grant_option, bool is_partial_revoke, String & result) void formatOptions(bool grant_option, bool is_partial_revoke, String & result)
@ -217,43 +211,18 @@ AccessRightsElement::AccessRightsElement(
{ {
} }
AccessFlags AccessRightsElement::getGrantableFlags() const void AccessRightsElement::eraseNonGrantable()
{ {
if (isGlobalWithParameter() && !anyParameter()) if (isGlobalWithParameter() && !anyParameter())
return access_flags & AccessFlags::allFlagsGrantableOnGlobalWithParameterLevel(); access_flags &= AccessFlags::allFlagsGrantableOnGlobalWithParameterLevel();
else if (!anyColumn()) else if (!anyColumn())
return access_flags & AccessFlags::allFlagsGrantableOnColumnLevel(); access_flags &= AccessFlags::allFlagsGrantableOnColumnLevel();
else if (!anyTable()) else if (!anyTable())
return access_flags & AccessFlags::allFlagsGrantableOnTableLevel(); access_flags &= AccessFlags::allFlagsGrantableOnTableLevel();
else if (!anyDatabase()) else if (!anyDatabase())
return access_flags & AccessFlags::allFlagsGrantableOnDatabaseLevel(); access_flags &= AccessFlags::allFlagsGrantableOnDatabaseLevel();
else else
return access_flags & AccessFlags::allFlagsGrantableOnGlobalLevel(); access_flags &= AccessFlags::allFlagsGrantableOnGlobalLevel();
}
void AccessRightsElement::throwIfNotGrantable() const
{
if (empty())
return;
auto grantable_flags = getGrantableFlags();
if (grantable_flags)
return;
if (!anyColumn())
throw Exception(ErrorCodes::INVALID_GRANT, "{} cannot be granted on the column level", access_flags.toString());
if (!anyTable())
throw Exception(ErrorCodes::INVALID_GRANT, "{} cannot be granted on the table level", access_flags.toString());
if (!anyDatabase())
throw Exception(ErrorCodes::INVALID_GRANT, "{} cannot be granted on the database level", access_flags.toString());
if (!anyParameter())
throw Exception(ErrorCodes::INVALID_GRANT, "{} cannot be granted on the global with parameter level", access_flags.toString());
throw Exception(ErrorCodes::INVALID_GRANT, "{} cannot be granted", access_flags.toString());
}
void AccessRightsElement::eraseNotGrantable()
{
access_flags = getGrantableFlags();
} }
void AccessRightsElement::replaceEmptyDatabase(const String & current_database) void AccessRightsElement::replaceEmptyDatabase(const String & current_database)
@ -282,17 +251,11 @@ bool AccessRightsElements::sameOptions() const
return (size() < 2) || std::all_of(std::next(begin()), end(), [this](const AccessRightsElement & e) { return e.sameOptions(front()); }); return (size() < 2) || std::all_of(std::next(begin()), end(), [this](const AccessRightsElement & e) { return e.sameOptions(front()); });
} }
void AccessRightsElements::throwIfNotGrantable() const void AccessRightsElements::eraseNonGrantable()
{
for (const auto & element : *this)
element.throwIfNotGrantable();
}
void AccessRightsElements::eraseNotGrantable()
{ {
std::erase_if(*this, [](AccessRightsElement & element) std::erase_if(*this, [](AccessRightsElement & element)
{ {
element.eraseNotGrantable(); element.eraseNonGrantable();
return element.empty(); return element.empty();
}); });
} }
@ -306,45 +269,4 @@ void AccessRightsElements::replaceEmptyDatabase(const String & current_database)
String AccessRightsElements::toString() const { return toStringImpl(*this, true); } String AccessRightsElements::toString() const { return toStringImpl(*this, true); }
String AccessRightsElements::toStringWithoutOptions() const { return toStringImpl(*this, false); } String AccessRightsElements::toStringWithoutOptions() const { return toStringImpl(*this, false); }
void AccessRightsElements::formatElementsWithoutOptions(WriteBuffer & buffer, bool hilite) const
{
bool no_output = true;
for (size_t i = 0; i != size(); ++i)
{
const auto & element = (*this)[i];
auto keywords = element.access_flags.toKeywords();
if (keywords.empty() || (!element.anyColumn() && element.columns.empty()))
continue;
for (const auto & keyword : keywords)
{
if (!std::exchange(no_output, false))
buffer << ", ";
buffer << (hilite ? IAST::hilite_keyword : "") << keyword << (hilite ? IAST::hilite_none : "");
if (!element.anyColumn())
element.formatColumnNames(buffer);
}
bool next_element_on_same_db_and_table = false;
if (i != size() - 1)
{
const auto & next_element = (*this)[i + 1];
if (element.sameDatabaseAndTableAndParameter(next_element))
{
next_element_on_same_db_and_table = true;
}
}
if (!next_element_on_same_db_and_table)
{
buffer << " ";
element.formatONClause(buffer, hilite);
}
}
if (no_output)
buffer << (hilite ? IAST::hilite_keyword : "") << "USAGE ON " << (hilite ? IAST::hilite_none : "") << "*.*";
}
} }

View File

@ -79,14 +79,8 @@ struct AccessRightsElement
return (grant_option == other.grant_option) && (is_partial_revoke == other.is_partial_revoke); return (grant_option == other.grant_option) && (is_partial_revoke == other.is_partial_revoke);
} }
/// Returns only those flags which can be granted.
AccessFlags getGrantableFlags() const;
/// Throws an exception if some flags can't be granted.
void throwIfNotGrantable() const;
/// Resets flags which cannot be granted. /// Resets flags which cannot be granted.
void eraseNotGrantable(); void eraseNonGrantable();
bool isEmptyDatabase() const { return database.empty() and !anyDatabase(); } bool isEmptyDatabase() const { return database.empty() and !anyDatabase(); }
@ -116,11 +110,8 @@ public:
bool sameDatabaseAndTable() const; bool sameDatabaseAndTable() const;
bool sameOptions() const; bool sameOptions() const;
/// Throws an exception if some flags can't be granted.
void throwIfNotGrantable() const;
/// Resets flags which cannot be granted. /// Resets flags which cannot be granted.
void eraseNotGrantable(); void eraseNonGrantable();
/// If the database is empty, replaces it with `current_database`. Otherwise does nothing. /// If the database is empty, replaces it with `current_database`. Otherwise does nothing.
void replaceEmptyDatabase(const String & current_database); void replaceEmptyDatabase(const String & current_database);
@ -128,7 +119,6 @@ public:
/// Returns a human-readable representation like "GRANT SELECT, UPDATE(x, y) ON db.table". /// Returns a human-readable representation like "GRANT SELECT, UPDATE(x, y) ON db.table".
String toString() const; String toString() const;
String toStringWithoutOptions() const; String toStringWithoutOptions() const;
void formatElementsWithoutOptions(WriteBuffer & buffer, bool hilite) const;
}; };
} }

View File

@ -397,35 +397,13 @@ SettingsConstraints::Checker SettingsConstraints::getChecker(const Settings & cu
/** The `readonly` value is understood as follows: /** The `readonly` value is understood as follows:
* 0 - no read-only restrictions. * 0 - no read-only restrictions.
* 1 - only read requests, as well as changing settings with `changeable_in_readonly` flag. * 1 - only read requests, as well as changing settings with `changable_in_readonly` flag.
* 2 - only read requests, as well as changing settings, except for the `readonly` setting. * 2 - only read requests, as well as changing settings, except for the `readonly` setting.
*/ */
if (current_settings[Setting::readonly] > 1 && resolved_name == "readonly") if (current_settings[Setting::readonly] > 1 && resolved_name == "readonly")
return Checker(PreformattedMessage::create("Cannot modify 'readonly' setting in readonly mode"), ErrorCodes::READONLY); return Checker(PreformattedMessage::create("Cannot modify 'readonly' setting in readonly mode"), ErrorCodes::READONLY);
if (access_control)
{
bool allowed_experimental = access_control->getAllowExperimentalTierSettings();
bool allowed_beta = access_control->getAllowBetaTierSettings();
if (!allowed_experimental || !allowed_beta)
{
auto setting_tier = current_settings.getTier(resolved_name);
if (setting_tier == SettingsTierType::EXPERIMENTAL && !allowed_experimental)
return Checker(
PreformattedMessage::create(
"Cannot modify setting '{}'. Changes to EXPERIMENTAL settings are disabled in the server config ('allowed_feature_tier')",
setting_name),
ErrorCodes::READONLY);
if (setting_tier == SettingsTierType::BETA && !allowed_beta)
return Checker(
PreformattedMessage::create(
"Cannot modify setting '{}'. Changes to BETA settings are disabled in the server config ('allowed_feature_tier')",
setting_name),
ErrorCodes::READONLY);
}
}
auto it = constraints.find(resolved_name); auto it = constraints.find(resolved_name);
if (current_settings[Setting::readonly] == 1) if (current_settings[Setting::readonly] == 1)
{ {

View File

@ -40,7 +40,7 @@ class AccessControl;
* <const/> * <const/>
* </force_index_by_date> * </force_index_by_date>
* <max_threads> * <max_threads>
* <changeable_in_readonly/> * <changable_in_readonly/>
* </max_threads> * </max_threads>
* </constraints> * </constraints>
* </user_profile> * </user_profile>
@ -50,7 +50,7 @@ class AccessControl;
* If a setting cannot be change due to the read-only mode this class throws an exception. * If a setting cannot be change due to the read-only mode this class throws an exception.
* The value of `readonly` is understood as follows: * The value of `readonly` is understood as follows:
* 0 - not read-only mode, no additional checks. * 0 - not read-only mode, no additional checks.
* 1 - only read queries, as well as changing settings with <changeable_in_readonly/> flag. * 1 - only read queries, as well as changing settings with <changable_in_readonly/> flag.
* 2 - only read queries and you can change the settings, except for the `readonly` setting. * 2 - only read queries and you can change the settings, except for the `readonly` setting.
* *
*/ */

View File

@ -79,14 +79,6 @@ public:
"Illegal type {} of second argument of aggregate function {} because the values of that data type are not comparable", "Illegal type {} of second argument of aggregate function {} because the values of that data type are not comparable",
type_val->getName(), type_val->getName(),
getName()); getName());
if (isDynamic(this->type_val) || isVariant(this->type_val))
throw Exception(
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
"Illegal type {} of argument of aggregate function {} because the column of that type can contain values with different "
"data types. Consider using typed subcolumns or cast column to a specific data type",
this->type_val->getName(),
getName());
} }
void create(AggregateDataPtr __restrict place) const override /// NOLINT void create(AggregateDataPtr __restrict place) const override /// NOLINT

View File

@ -35,14 +35,6 @@ public:
"Illegal type {} of argument of aggregate function {} because the values of that data type are not comparable", "Illegal type {} of argument of aggregate function {} because the values of that data type are not comparable",
this->result_type->getName(), this->result_type->getName(),
getName()); getName());
if (isDynamic(this->result_type) || isVariant(this->result_type))
throw Exception(
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
"Illegal type {} of argument of aggregate function {} because the column of that type can contain values with different "
"data types. Consider using typed subcolumns or cast column to a specific data type",
this->result_type->getName(),
getName());
} }
String getName() const override String getName() const override

View File

@ -63,14 +63,6 @@ public:
"Illegal type {} for combinator {} because the values of that data type are not comparable", "Illegal type {} for combinator {} because the values of that data type are not comparable",
arguments[key_col]->getName(), arguments[key_col]->getName(),
getName()); getName());
if (isDynamic(arguments[key_col]) || isVariant(arguments[key_col]))
throw Exception(
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
"Illegal type {} of argument of aggregate function {} because the column of that type can contain values with different "
"data types. Consider using typed subcolumns or cast column to a specific data type",
arguments[key_col]->getName(),
getName());
} }
String getName() const override String getName() const override

View File

@ -4,7 +4,6 @@
#include <Core/Settings.h> #include <Core/Settings.h>
#include <Functions/grouping.h> #include <Functions/grouping.h>
#include <Functions/IFunctionAdaptors.h>
#include <Interpreters/Context.h> #include <Interpreters/Context.h>

View File

@ -13,7 +13,6 @@
#include <DataTypes/getLeastSupertype.h> #include <DataTypes/getLeastSupertype.h>
#include <Functions/FunctionFactory.h> #include <Functions/FunctionFactory.h>
#include <Functions/IFunctionAdaptors.h>
#include <Functions/UserDefined/UserDefinedExecutableFunctionFactory.h> #include <Functions/UserDefined/UserDefinedExecutableFunctionFactory.h>
#include <Functions/UserDefined/UserDefinedSQLFunctionFactory.h> #include <Functions/UserDefined/UserDefinedSQLFunctionFactory.h>
#include <Functions/grouping.h> #include <Functions/grouping.h>

View File

@ -639,10 +639,6 @@ if (TARGET ch_rust::skim)
dbms_target_link_libraries(PUBLIC ch_rust::skim) dbms_target_link_libraries(PUBLIC ch_rust::skim)
endif() endif()
if (TARGET ch_contrib::google_cloud_cpp)
dbms_target_link_libraries(PUBLIC ch_contrib::google_cloud_cpp)
endif()
if (ENABLE_TESTS) if (ENABLE_TESTS)
macro (grep_gtest_sources BASE_DIR DST_VAR) macro (grep_gtest_sources BASE_DIR DST_VAR)
# Cold match files that are not in tests/ directories # Cold match files that are not in tests/ directories

View File

@ -8,10 +8,6 @@
#include <Common/assert_cast.h> #include <Common/assert_cast.h>
#include <Common/iota.h> #include <Common/iota.h>
#include <Core/DecimalFunctions.h>
#include <Core/TypeId.h>
#include <base/TypeName.h>
#include <base/sort.h> #include <base/sort.h>
#include <IO/WriteHelpers.h> #include <IO/WriteHelpers.h>
@ -34,19 +30,6 @@ namespace ErrorCodes
extern const int NOT_IMPLEMENTED; extern const int NOT_IMPLEMENTED;
} }
template <is_decimal T>
const char * ColumnDecimal<T>::getFamilyName() const
{
return TypeName<T>.data();
}
template <is_decimal T>
TypeIndex ColumnDecimal<T>::getDataType() const
{
return TypeToTypeIndex<T>;
}
template <is_decimal T> template <is_decimal T>
#if !defined(DEBUG_OR_SANITIZER_BUILD) #if !defined(DEBUG_OR_SANITIZER_BUILD)
int ColumnDecimal<T>::compareAt(size_t n, size_t m, const IColumn & rhs_, int) const int ColumnDecimal<T>::compareAt(size_t n, size_t m, const IColumn & rhs_, int) const
@ -63,12 +46,6 @@ int ColumnDecimal<T>::doCompareAt(size_t n, size_t m, const IColumn & rhs_, int)
return decimalLess<T>(b, a, other.scale, scale) ? 1 : (decimalLess<T>(a, b, scale, other.scale) ? -1 : 0); return decimalLess<T>(b, a, other.scale, scale) ? 1 : (decimalLess<T>(a, b, scale, other.scale) ? -1 : 0);
} }
template <is_decimal T>
Float64 ColumnDecimal<T>::getFloat64(size_t n) const
{
return DecimalUtils::convertTo<Float64>(data[n], scale);
}
template <is_decimal T> template <is_decimal T>
const char * ColumnDecimal<T>::deserializeAndInsertFromArena(const char * pos) const char * ColumnDecimal<T>::deserializeAndInsertFromArena(const char * pos)
{ {

View File

@ -1,9 +1,14 @@
#pragma once #pragma once
#include <base/sort.h>
#include <base/TypeName.h>
#include <Core/Field.h>
#include <Core/DecimalFunctions.h>
#include <Core/TypeId.h>
#include <Common/typeid_cast.h>
#include <Columns/ColumnFixedSizeHelper.h> #include <Columns/ColumnFixedSizeHelper.h>
#include <Columns/IColumn.h> #include <Columns/IColumn.h>
#include <Columns/IColumnImpl.h> #include <Columns/IColumnImpl.h>
#include <Core/Field.h>
namespace DB namespace DB
@ -34,8 +39,8 @@ private:
{} {}
public: public:
const char * getFamilyName() const override; const char * getFamilyName() const override { return TypeName<T>.data(); }
TypeIndex getDataType() const override; TypeIndex getDataType() const override { return TypeToTypeIndex<T>; }
bool isNumeric() const override { return false; } bool isNumeric() const override { return false; }
bool canBeInsideNullable() const override { return true; } bool canBeInsideNullable() const override { return true; }
@ -93,7 +98,7 @@ public:
return StringRef(reinterpret_cast<const char *>(&data[n]), sizeof(data[n])); return StringRef(reinterpret_cast<const char *>(&data[n]), sizeof(data[n]));
} }
Float64 getFloat64(size_t n) const final; Float64 getFloat64(size_t n) const final { return DecimalUtils::convertTo<Float64>(data[n], scale); }
const char * deserializeAndInsertFromArena(const char * pos) override; const char * deserializeAndInsertFromArena(const char * pos) override;
const char * skipSerializedInArena(const char * pos) const override; const char * skipSerializedInArena(const char * pos) const override;

View File

@ -347,7 +347,7 @@ ColumnWithTypeAndName ColumnFunction::reduce() const
if (is_function_compiled) if (is_function_compiled)
ProfileEvents::increment(ProfileEvents::CompiledFunctionExecute); ProfileEvents::increment(ProfileEvents::CompiledFunctionExecute);
res.column = function->execute(columns, res.type, elements_size, /* dry_run = */ false); res.column = function->execute(columns, res.type, elements_size);
if (res.column->getDataType() != res.type->getColumnType()) if (res.column->getDataType() != res.type->getColumnType())
throw Exception( throw Exception(
ErrorCodes::LOGICAL_ERROR, ErrorCodes::LOGICAL_ERROR,

View File

@ -32,8 +32,6 @@
# include <emmintrin.h> # include <emmintrin.h>
#endif #endif
#include "config.h"
#if USE_MULTITARGET_CODE #if USE_MULTITARGET_CODE
# include <immintrin.h> # include <immintrin.h>
#endif #endif
@ -660,7 +658,7 @@ inline void doFilterAligned(const UInt8 *& filt_pos, const UInt8 *& filt_end_ali
reinterpret_cast<void *>(&res_data[current_offset]), mask & KMASK); reinterpret_cast<void *>(&res_data[current_offset]), mask & KMASK);
current_offset += std::popcount(mask & KMASK); current_offset += std::popcount(mask & KMASK);
/// prepare mask for next iter, if ELEMENTS_PER_VEC = 64, no next iter /// prepare mask for next iter, if ELEMENTS_PER_VEC = 64, no next iter
if constexpr (ELEMENTS_PER_VEC < 64) if (ELEMENTS_PER_VEC < 64)
{ {
mask >>= ELEMENTS_PER_VEC; mask >>= ELEMENTS_PER_VEC;
} }
@ -994,151 +992,6 @@ ColumnPtr ColumnVector<T>::createWithOffsets(const IColumn::Offsets & offsets, c
return res; return res;
} }
DECLARE_DEFAULT_CODE(
template <typename Container, typename Type> void vectorIndexImpl(
const Container & data, const PaddedPODArray<Type> & indexes, size_t limit, Container & res_data)
{
for (size_t i = 0; i < limit; ++i)
res_data[i] = data[indexes[i]];
}
);
DECLARE_AVX512VBMI_SPECIFIC_CODE(
template <typename Container, typename Type>
void vectorIndexImpl(const Container & data, const PaddedPODArray<Type> & indexes, size_t limit, Container & res_data)
{
static constexpr UInt64 MASK64 = 0xffffffffffffffff;
const size_t limit64 = limit & ~63;
size_t pos = 0;
size_t data_size = data.size();
auto data_pos = reinterpret_cast<const UInt8 *>(data.data());
auto indexes_pos = reinterpret_cast<const UInt8 *>(indexes.data());
auto res_pos = reinterpret_cast<UInt8 *>(res_data.data());
if (limit == 0)
return; /// nothing to do, just return
if (data_size <= 64)
{
/// one single mask load for table size <= 64
__mmask64 last_mask = MASK64 >> (64 - data_size);
__m512i table1 = _mm512_maskz_loadu_epi8(last_mask, data_pos);
/// 64 bytes table lookup using one single permutexvar_epi8
while (pos < limit64)
{
__m512i vidx = _mm512_loadu_epi8(indexes_pos + pos);
__m512i out = _mm512_permutexvar_epi8(vidx, table1);
_mm512_storeu_epi8(res_pos + pos, out);
pos += 64;
}
/// tail handling
if (limit > limit64)
{
__mmask64 tail_mask = MASK64 >> (limit64 + 64 - limit);
__m512i vidx = _mm512_maskz_loadu_epi8(tail_mask, indexes_pos + pos);
__m512i out = _mm512_permutexvar_epi8(vidx, table1);
_mm512_mask_storeu_epi8(res_pos + pos, tail_mask, out);
}
}
else if (data_size <= 128)
{
/// table size (64, 128] requires 2 zmm load
__mmask64 last_mask = MASK64 >> (128 - data_size);
__m512i table1 = _mm512_loadu_epi8(data_pos);
__m512i table2 = _mm512_maskz_loadu_epi8(last_mask, data_pos + 64);
/// 128 bytes table lookup using one single permute2xvar_epi8
while (pos < limit64)
{
__m512i vidx = _mm512_loadu_epi8(indexes_pos + pos);
__m512i out = _mm512_permutex2var_epi8(table1, vidx, table2);
_mm512_storeu_epi8(res_pos + pos, out);
pos += 64;
}
if (limit > limit64)
{
__mmask64 tail_mask = MASK64 >> (limit64 + 64 - limit);
__m512i vidx = _mm512_maskz_loadu_epi8(tail_mask, indexes_pos + pos);
__m512i out = _mm512_permutex2var_epi8(table1, vidx, table2);
_mm512_mask_storeu_epi8(res_pos + pos, tail_mask, out);
}
}
else
{
if (data_size > 256)
{
/// byte index will not exceed 256 boundary.
data_size = 256;
}
__m512i table1 = _mm512_loadu_epi8(data_pos);
__m512i table2 = _mm512_loadu_epi8(data_pos + 64);
__m512i table3, table4;
if (data_size <= 192)
{
/// only 3 tables need to load if size <= 192
__mmask64 last_mask = MASK64 >> (192 - data_size);
table3 = _mm512_maskz_loadu_epi8(last_mask, data_pos + 128);
table4 = _mm512_setzero_si512();
}
else
{
__mmask64 last_mask = MASK64 >> (256 - data_size);
table3 = _mm512_loadu_epi8(data_pos + 128);
table4 = _mm512_maskz_loadu_epi8(last_mask, data_pos + 192);
}
/// 256 bytes table lookup can use: 2 permute2xvar_epi8 plus 1 blender with MSB
while (pos < limit64)
{
__m512i vidx = _mm512_loadu_epi8(indexes_pos + pos);
__m512i tmp1 = _mm512_permutex2var_epi8(table1, vidx, table2);
__m512i tmp2 = _mm512_permutex2var_epi8(table3, vidx, table4);
__mmask64 msb = _mm512_movepi8_mask(vidx);
__m512i out = _mm512_mask_blend_epi8(msb, tmp1, tmp2);
_mm512_storeu_epi8(res_pos + pos, out);
pos += 64;
}
if (limit > limit64)
{
__mmask64 tail_mask = MASK64 >> (limit64 + 64 - limit);
__m512i vidx = _mm512_maskz_loadu_epi8(tail_mask, indexes_pos + pos);
__m512i tmp1 = _mm512_permutex2var_epi8(table1, vidx, table2);
__m512i tmp2 = _mm512_permutex2var_epi8(table3, vidx, table4);
__mmask64 msb = _mm512_movepi8_mask(vidx);
__m512i out = _mm512_mask_blend_epi8(msb, tmp1, tmp2);
_mm512_mask_storeu_epi8(res_pos + pos, tail_mask, out);
}
}
}
);
template <typename T>
template <typename Type>
ColumnPtr ColumnVector<T>::indexImpl(const PaddedPODArray<Type> & indexes, size_t limit) const
{
chassert(limit <= indexes.size());
auto res = this->create(limit);
typename Self::Container & res_data = res->getData();
#if USE_MULTITARGET_CODE
if constexpr (sizeof(T) == 1 && sizeof(Type) == 1)
{
/// VBMI optimization only applicable for (U)Int8 types
if (isArchSupported(TargetArch::AVX512VBMI))
{
TargetSpecific::AVX512VBMI::vectorIndexImpl<Container, Type>(data, indexes, limit, res_data);
return res;
}
}
#endif
TargetSpecific::Default::vectorIndexImpl<Container, Type>(data, indexes, limit, res_data);
return res;
}
/// Explicit template instantiations - to avoid code bloat in headers. /// Explicit template instantiations - to avoid code bloat in headers.
template class ColumnVector<UInt8>; template class ColumnVector<UInt8>;
template class ColumnVector<UInt16>; template class ColumnVector<UInt16>;
@ -1159,17 +1012,4 @@ template class ColumnVector<UUID>;
template class ColumnVector<IPv4>; template class ColumnVector<IPv4>;
template class ColumnVector<IPv6>; template class ColumnVector<IPv6>;
INSTANTIATE_INDEX_TEMPLATE_IMPL(ColumnVector)
/// Used by ColumnVariant.cpp
template ColumnPtr ColumnVector<UInt8>::indexImpl<UInt16>(const PaddedPODArray<UInt16> & indexes, size_t limit) const;
template ColumnPtr ColumnVector<UInt8>::indexImpl<UInt32>(const PaddedPODArray<UInt32> & indexes, size_t limit) const;
template ColumnPtr ColumnVector<UInt8>::indexImpl<UInt64>(const PaddedPODArray<UInt64> & indexes, size_t limit) const;
template ColumnPtr ColumnVector<UInt64>::indexImpl<UInt8>(const PaddedPODArray<UInt8> & indexes, size_t limit) const;
template ColumnPtr ColumnVector<UInt64>::indexImpl<UInt16>(const PaddedPODArray<UInt16> & indexes, size_t limit) const;
template ColumnPtr ColumnVector<UInt64>::indexImpl<UInt32>(const PaddedPODArray<UInt32> & indexes, size_t limit) const;
#if defined(OS_DARWIN)
template ColumnPtr ColumnVector<UInt8>::indexImpl<size_t>(const PaddedPODArray<size_t> & indexes, size_t limit) const;
template ColumnPtr ColumnVector<UInt64>::indexImpl<size_t>(const PaddedPODArray<size_t> & indexes, size_t limit) const;
#endif
} }

View File

@ -13,6 +13,10 @@
#include "config.h" #include "config.h"
#if USE_MULTITARGET_CODE
# include <immintrin.h>
#endif
namespace DB namespace DB
{ {
@ -316,6 +320,151 @@ protected:
Container data; Container data;
}; };
DECLARE_DEFAULT_CODE(
template <typename Container, typename Type>
inline void vectorIndexImpl(const Container & data, const PaddedPODArray<Type> & indexes, size_t limit, Container & res_data)
{
for (size_t i = 0; i < limit; ++i)
res_data[i] = data[indexes[i]];
}
);
DECLARE_AVX512VBMI_SPECIFIC_CODE(
template <typename Container, typename Type>
inline void vectorIndexImpl(const Container & data, const PaddedPODArray<Type> & indexes, size_t limit, Container & res_data)
{
static constexpr UInt64 MASK64 = 0xffffffffffffffff;
const size_t limit64 = limit & ~63;
size_t pos = 0;
size_t data_size = data.size();
auto data_pos = reinterpret_cast<const UInt8 *>(data.data());
auto indexes_pos = reinterpret_cast<const UInt8 *>(indexes.data());
auto res_pos = reinterpret_cast<UInt8 *>(res_data.data());
if (limit == 0)
return; /// nothing to do, just return
if (data_size <= 64)
{
/// one single mask load for table size <= 64
__mmask64 last_mask = MASK64 >> (64 - data_size);
__m512i table1 = _mm512_maskz_loadu_epi8(last_mask, data_pos);
/// 64 bytes table lookup using one single permutexvar_epi8
while (pos < limit64)
{
__m512i vidx = _mm512_loadu_epi8(indexes_pos + pos);
__m512i out = _mm512_permutexvar_epi8(vidx, table1);
_mm512_storeu_epi8(res_pos + pos, out);
pos += 64;
}
/// tail handling
if (limit > limit64)
{
__mmask64 tail_mask = MASK64 >> (limit64 + 64 - limit);
__m512i vidx = _mm512_maskz_loadu_epi8(tail_mask, indexes_pos + pos);
__m512i out = _mm512_permutexvar_epi8(vidx, table1);
_mm512_mask_storeu_epi8(res_pos + pos, tail_mask, out);
}
}
else if (data_size <= 128)
{
/// table size (64, 128] requires 2 zmm load
__mmask64 last_mask = MASK64 >> (128 - data_size);
__m512i table1 = _mm512_loadu_epi8(data_pos);
__m512i table2 = _mm512_maskz_loadu_epi8(last_mask, data_pos + 64);
/// 128 bytes table lookup using one single permute2xvar_epi8
while (pos < limit64)
{
__m512i vidx = _mm512_loadu_epi8(indexes_pos + pos);
__m512i out = _mm512_permutex2var_epi8(table1, vidx, table2);
_mm512_storeu_epi8(res_pos + pos, out);
pos += 64;
}
if (limit > limit64)
{
__mmask64 tail_mask = MASK64 >> (limit64 + 64 - limit);
__m512i vidx = _mm512_maskz_loadu_epi8(tail_mask, indexes_pos + pos);
__m512i out = _mm512_permutex2var_epi8(table1, vidx, table2);
_mm512_mask_storeu_epi8(res_pos + pos, tail_mask, out);
}
}
else
{
if (data_size > 256)
{
/// byte index will not exceed 256 boundary.
data_size = 256;
}
__m512i table1 = _mm512_loadu_epi8(data_pos);
__m512i table2 = _mm512_loadu_epi8(data_pos + 64);
__m512i table3, table4;
if (data_size <= 192)
{
/// only 3 tables need to load if size <= 192
__mmask64 last_mask = MASK64 >> (192 - data_size);
table3 = _mm512_maskz_loadu_epi8(last_mask, data_pos + 128);
table4 = _mm512_setzero_si512();
}
else
{
__mmask64 last_mask = MASK64 >> (256 - data_size);
table3 = _mm512_loadu_epi8(data_pos + 128);
table4 = _mm512_maskz_loadu_epi8(last_mask, data_pos + 192);
}
/// 256 bytes table lookup can use: 2 permute2xvar_epi8 plus 1 blender with MSB
while (pos < limit64)
{
__m512i vidx = _mm512_loadu_epi8(indexes_pos + pos);
__m512i tmp1 = _mm512_permutex2var_epi8(table1, vidx, table2);
__m512i tmp2 = _mm512_permutex2var_epi8(table3, vidx, table4);
__mmask64 msb = _mm512_movepi8_mask(vidx);
__m512i out = _mm512_mask_blend_epi8(msb, tmp1, tmp2);
_mm512_storeu_epi8(res_pos + pos, out);
pos += 64;
}
if (limit > limit64)
{
__mmask64 tail_mask = MASK64 >> (limit64 + 64 - limit);
__m512i vidx = _mm512_maskz_loadu_epi8(tail_mask, indexes_pos + pos);
__m512i tmp1 = _mm512_permutex2var_epi8(table1, vidx, table2);
__m512i tmp2 = _mm512_permutex2var_epi8(table3, vidx, table4);
__mmask64 msb = _mm512_movepi8_mask(vidx);
__m512i out = _mm512_mask_blend_epi8(msb, tmp1, tmp2);
_mm512_mask_storeu_epi8(res_pos + pos, tail_mask, out);
}
}
}
);
template <typename T>
template <typename Type>
ColumnPtr ColumnVector<T>::indexImpl(const PaddedPODArray<Type> & indexes, size_t limit) const
{
assert(limit <= indexes.size());
auto res = this->create(limit);
typename Self::Container & res_data = res->getData();
#if USE_MULTITARGET_CODE
if constexpr (sizeof(T) == 1 && sizeof(Type) == 1)
{
/// VBMI optimization only applicable for (U)Int8 types
if (isArchSupported(TargetArch::AVX512VBMI))
{
TargetSpecific::AVX512VBMI::vectorIndexImpl<Container, Type>(data, indexes, limit, res_data);
return res;
}
}
#endif
TargetSpecific::Default::vectorIndexImpl<Container, Type>(data, indexes, limit, res_data);
return res;
}
template <class TCol> template <class TCol>
concept is_col_vector = std::is_same_v<TCol, ColumnVector<typename TCol::ValueType>>; concept is_col_vector = std::is_same_v<TCol, ColumnVector<typename TCol::ValueType>>;

View File

@ -142,10 +142,4 @@ ColumnPtr permuteImpl(const Column & column, const IColumn::Permutation & perm,
template ColumnPtr Column::indexImpl<UInt16>(const PaddedPODArray<UInt16> & indexes, size_t limit) const; \ template ColumnPtr Column::indexImpl<UInt16>(const PaddedPODArray<UInt16> & indexes, size_t limit) const; \
template ColumnPtr Column::indexImpl<UInt32>(const PaddedPODArray<UInt32> & indexes, size_t limit) const; \ template ColumnPtr Column::indexImpl<UInt32>(const PaddedPODArray<UInt32> & indexes, size_t limit) const; \
template ColumnPtr Column::indexImpl<UInt64>(const PaddedPODArray<UInt64> & indexes, size_t limit) const; template ColumnPtr Column::indexImpl<UInt64>(const PaddedPODArray<UInt64> & indexes, size_t limit) const;
#define INSTANTIATE_INDEX_TEMPLATE_IMPL(ColumnTemplate) \
template ColumnPtr ColumnTemplate<UInt8>::indexImpl<UInt8>(const PaddedPODArray<UInt8> & indexes, size_t limit) const; \
template ColumnPtr ColumnTemplate<UInt16>::indexImpl<UInt16>(const PaddedPODArray<UInt16> & indexes, size_t limit) const; \
template ColumnPtr ColumnTemplate<UInt32>::indexImpl<UInt32>(const PaddedPODArray<UInt32> & indexes, size_t limit) const; \
template ColumnPtr ColumnTemplate<UInt64>::indexImpl<UInt64>(const PaddedPODArray<UInt64> & indexes, size_t limit) const;
} }

View File

@ -131,7 +131,6 @@ public:
const char * getTypeName(std::string_view name) const; const char * getTypeName(std::string_view name) const;
const char * getDescription(std::string_view name) const; const char * getDescription(std::string_view name) const;
SettingsTierType getTier(std::string_view name) const;
/// Checks if it's possible to assign a field to a specified value and throws an exception if not. /// Checks if it's possible to assign a field to a specified value and throws an exception if not.
/// This function doesn't change the fields, it performs check only. /// This function doesn't change the fields, it performs check only.
@ -381,18 +380,6 @@ const char * BaseSettings<TTraits>::getDescription(std::string_view name) const
BaseSettingsHelpers::throwSettingNotFound(name); BaseSettingsHelpers::throwSettingNotFound(name);
} }
template <typename TTraits>
SettingsTierType BaseSettings<TTraits>::getTier(std::string_view name) const
{
name = TTraits::resolveName(name);
const auto & accessor = Traits::Accessor::instance();
if (size_t index = accessor.find(name); index != static_cast<size_t>(-1))
return accessor.getTier(index);
if (tryGetCustomSetting(name))
return SettingsTierType::PRODUCTION;
BaseSettingsHelpers::throwSettingNotFound(name);
}
template <typename TTraits> template <typename TTraits>
void BaseSettings<TTraits>::checkCanSet(std::string_view name, const Field & value) void BaseSettings<TTraits>::checkCanSet(std::string_view name, const Field & value)
{ {

View File

@ -2,7 +2,6 @@
#include <base/arithmeticOverflow.h> #include <base/arithmeticOverflow.h>
#include <Core/Block.h> #include <Core/Block.h>
#include <Core/DecimalFunctions.h>
#include <Core/AccurateComparison.h> #include <Core/AccurateComparison.h>
#include <Core/callOnTypeIndex.h> #include <Core/callOnTypeIndex.h>
#include <DataTypes/DataTypesNumber.h> #include <DataTypes/DataTypesNumber.h>
@ -53,8 +52,8 @@ struct DecCompareInt
using TypeB = Type; using TypeB = Type;
}; };
template <typename A, typename B, template <typename, typename> typename Operation> template <typename A, typename B, template <typename, typename> typename Operation, bool _check_overflow = true,
requires is_decimal<A> || is_decimal<B> bool _actual = is_decimal<A> || is_decimal<B>>
class DecimalComparison class DecimalComparison
{ {
public: public:
@ -66,17 +65,20 @@ public:
using ArrayA = typename ColVecA::Container; using ArrayA = typename ColVecA::Container;
using ArrayB = typename ColVecB::Container; using ArrayB = typename ColVecB::Container;
static ColumnPtr apply(const ColumnWithTypeAndName & col_left, const ColumnWithTypeAndName & col_right, bool check_overflow) static ColumnPtr apply(const ColumnWithTypeAndName & col_left, const ColumnWithTypeAndName & col_right)
{ {
ColumnPtr c_res; if constexpr (_actual)
Shift shift = getScales<A, B>(col_left.type, col_right.type); {
ColumnPtr c_res;
Shift shift = getScales<A, B>(col_left.type, col_right.type);
if (check_overflow) return applyWithScale(col_left.column, col_right.column, shift);
return applyWithScale<true>(col_left.column, col_right.column, shift); }
return applyWithScale<false>(col_left.column, col_right.column, shift); else
return nullptr;
} }
static bool compare(A a, B b, UInt32 scale_a, UInt32 scale_b, bool check_overflow) static bool compare(A a, B b, UInt32 scale_a, UInt32 scale_b)
{ {
static const UInt32 max_scale = DecimalUtils::max_precision<Decimal256>; static const UInt32 max_scale = DecimalUtils::max_precision<Decimal256>;
if (scale_a > max_scale || scale_b > max_scale) if (scale_a > max_scale || scale_b > max_scale)
@ -88,9 +90,7 @@ public:
if (scale_a > scale_b) if (scale_a > scale_b)
shift.b = static_cast<CompareInt>(DecimalUtils::scaleMultiplier<A>(scale_a - scale_b)); shift.b = static_cast<CompareInt>(DecimalUtils::scaleMultiplier<A>(scale_a - scale_b));
if (check_overflow) return applyWithScale(a, b, shift);
return applyWithScale<true>(a, b, shift);
return applyWithScale<false>(a, b, shift);
} }
private: private:
@ -104,14 +104,14 @@ private:
bool right() const { return b != 1; } bool right() const { return b != 1; }
}; };
template <bool check_overflow, typename T, typename U> template <typename T, typename U>
static auto applyWithScale(T a, U b, const Shift & shift) static auto applyWithScale(T a, U b, const Shift & shift)
{ {
if (shift.left()) if (shift.left())
return apply<check_overflow, true, false>(a, b, shift.a); return apply<true, false>(a, b, shift.a);
if (shift.right()) if (shift.right())
return apply<check_overflow, false, true>(a, b, shift.b); return apply<false, true>(a, b, shift.b);
return apply<check_overflow, false, false>(a, b, 1); return apply<false, false>(a, b, 1);
} }
template <typename T, typename U> template <typename T, typename U>
@ -125,8 +125,8 @@ private:
if (decimal0 && decimal1) if (decimal0 && decimal1)
{ {
auto result_type = DecimalUtils::binaryOpResult<false, false>(*decimal0, *decimal1); auto result_type = DecimalUtils::binaryOpResult<false, false>(*decimal0, *decimal1);
shift.a = static_cast<CompareInt>(result_type.scaleFactorFor(DecimalUtils::DataTypeDecimalTrait<T>{decimal0->getPrecision(), decimal0->getScale()}, false).value); shift.a = static_cast<CompareInt>(result_type.scaleFactorFor(decimal0->getTrait(), false).value);
shift.b = static_cast<CompareInt>(result_type.scaleFactorFor(DecimalUtils::DataTypeDecimalTrait<U>{decimal1->getPrecision(), decimal1->getScale()}, false).value); shift.b = static_cast<CompareInt>(result_type.scaleFactorFor(decimal1->getTrait(), false).value);
} }
else if (decimal0) else if (decimal0)
shift.b = static_cast<CompareInt>(decimal0->getScaleMultiplier().value); shift.b = static_cast<CompareInt>(decimal0->getScaleMultiplier().value);
@ -158,63 +158,66 @@ private:
return shift; return shift;
} }
template <bool check_overflow, bool scale_left, bool scale_right> template <bool scale_left, bool scale_right>
static ColumnPtr apply(const ColumnPtr & c0, const ColumnPtr & c1, CompareInt scale) static ColumnPtr apply(const ColumnPtr & c0, const ColumnPtr & c1, CompareInt scale)
{ {
auto c_res = ColumnUInt8::create(); auto c_res = ColumnUInt8::create();
bool c0_is_const = isColumnConst(*c0); if constexpr (_actual)
bool c1_is_const = isColumnConst(*c1);
if (c0_is_const && c1_is_const)
{ {
const ColumnConst & c0_const = checkAndGetColumnConst<ColVecA>(*c0); bool c0_is_const = isColumnConst(*c0);
const ColumnConst & c1_const = checkAndGetColumnConst<ColVecB>(*c1); bool c1_is_const = isColumnConst(*c1);
A a = c0_const.template getValue<A>(); if (c0_is_const && c1_is_const)
B b = c1_const.template getValue<B>();
UInt8 res = apply<check_overflow, scale_left, scale_right>(a, b, scale);
return DataTypeUInt8().createColumnConst(c0->size(), toField(res));
}
ColumnUInt8::Container & vec_res = c_res->getData();
vec_res.resize(c0->size());
if (c0_is_const)
{
const ColumnConst & c0_const = checkAndGetColumnConst<ColVecA>(*c0);
A a = c0_const.template getValue<A>();
if (const ColVecB * c1_vec = checkAndGetColumn<ColVecB>(c1.get()))
constantVector<check_overflow, scale_left, scale_right>(a, c1_vec->getData(), vec_res, scale);
else
throw Exception(ErrorCodes::LOGICAL_ERROR, "Wrong column in Decimal comparison");
}
else if (c1_is_const)
{
const ColumnConst & c1_const = checkAndGetColumnConst<ColVecB>(*c1);
B b = c1_const.template getValue<B>();
if (const ColVecA * c0_vec = checkAndGetColumn<ColVecA>(c0.get()))
vectorConstant<check_overflow, scale_left, scale_right>(c0_vec->getData(), b, vec_res, scale);
else
throw Exception(ErrorCodes::LOGICAL_ERROR, "Wrong column in Decimal comparison");
}
else
{
if (const ColVecA * c0_vec = checkAndGetColumn<ColVecA>(c0.get()))
{ {
const ColumnConst & c0_const = checkAndGetColumnConst<ColVecA>(*c0);
const ColumnConst & c1_const = checkAndGetColumnConst<ColVecB>(*c1);
A a = c0_const.template getValue<A>();
B b = c1_const.template getValue<B>();
UInt8 res = apply<scale_left, scale_right>(a, b, scale);
return DataTypeUInt8().createColumnConst(c0->size(), toField(res));
}
ColumnUInt8::Container & vec_res = c_res->getData();
vec_res.resize(c0->size());
if (c0_is_const)
{
const ColumnConst & c0_const = checkAndGetColumnConst<ColVecA>(*c0);
A a = c0_const.template getValue<A>();
if (const ColVecB * c1_vec = checkAndGetColumn<ColVecB>(c1.get())) if (const ColVecB * c1_vec = checkAndGetColumn<ColVecB>(c1.get()))
vectorVector<check_overflow, scale_left, scale_right>(c0_vec->getData(), c1_vec->getData(), vec_res, scale); constantVector<scale_left, scale_right>(a, c1_vec->getData(), vec_res, scale);
else
throw Exception(ErrorCodes::LOGICAL_ERROR, "Wrong column in Decimal comparison");
}
else if (c1_is_const)
{
const ColumnConst & c1_const = checkAndGetColumnConst<ColVecB>(*c1);
B b = c1_const.template getValue<B>();
if (const ColVecA * c0_vec = checkAndGetColumn<ColVecA>(c0.get()))
vectorConstant<scale_left, scale_right>(c0_vec->getData(), b, vec_res, scale);
else else
throw Exception(ErrorCodes::LOGICAL_ERROR, "Wrong column in Decimal comparison"); throw Exception(ErrorCodes::LOGICAL_ERROR, "Wrong column in Decimal comparison");
} }
else else
throw Exception(ErrorCodes::LOGICAL_ERROR, "Wrong column in Decimal comparison"); {
if (const ColVecA * c0_vec = checkAndGetColumn<ColVecA>(c0.get()))
{
if (const ColVecB * c1_vec = checkAndGetColumn<ColVecB>(c1.get()))
vectorVector<scale_left, scale_right>(c0_vec->getData(), c1_vec->getData(), vec_res, scale);
else
throw Exception(ErrorCodes::LOGICAL_ERROR, "Wrong column in Decimal comparison");
}
else
throw Exception(ErrorCodes::LOGICAL_ERROR, "Wrong column in Decimal comparison");
}
} }
return c_res; return c_res;
} }
template <bool check_overflow, bool scale_left, bool scale_right> template <bool scale_left, bool scale_right>
static NO_INLINE UInt8 apply(A a, B b, CompareInt scale [[maybe_unused]]) static NO_INLINE UInt8 apply(A a, B b, CompareInt scale [[maybe_unused]])
{ {
CompareInt x; CompareInt x;
@ -229,7 +232,7 @@ private:
else else
y = static_cast<CompareInt>(b); y = static_cast<CompareInt>(b);
if constexpr (check_overflow) if constexpr (_check_overflow)
{ {
bool overflow = false; bool overflow = false;
@ -261,8 +264,9 @@ private:
return Op::apply(x, y); return Op::apply(x, y);
} }
template <bool check_overflow, bool scale_left, bool scale_right> template <bool scale_left, bool scale_right>
static void NO_INLINE vectorVector(const ArrayA & a, const ArrayB & b, PaddedPODArray<UInt8> & c, CompareInt scale) static void NO_INLINE vectorVector(const ArrayA & a, const ArrayB & b, PaddedPODArray<UInt8> & c,
CompareInt scale)
{ {
size_t size = a.size(); size_t size = a.size();
const A * a_pos = a.data(); const A * a_pos = a.data();
@ -272,14 +276,14 @@ private:
while (a_pos < a_end) while (a_pos < a_end)
{ {
*c_pos = apply<check_overflow, scale_left, scale_right>(*a_pos, *b_pos, scale); *c_pos = apply<scale_left, scale_right>(*a_pos, *b_pos, scale);
++a_pos; ++a_pos;
++b_pos; ++b_pos;
++c_pos; ++c_pos;
} }
} }
template <bool check_overflow, bool scale_left, bool scale_right> template <bool scale_left, bool scale_right>
static void NO_INLINE vectorConstant(const ArrayA & a, B b, PaddedPODArray<UInt8> & c, CompareInt scale) static void NO_INLINE vectorConstant(const ArrayA & a, B b, PaddedPODArray<UInt8> & c, CompareInt scale)
{ {
size_t size = a.size(); size_t size = a.size();
@ -289,13 +293,13 @@ private:
while (a_pos < a_end) while (a_pos < a_end)
{ {
*c_pos = apply<check_overflow, scale_left, scale_right>(*a_pos, b, scale); *c_pos = apply<scale_left, scale_right>(*a_pos, b, scale);
++a_pos; ++a_pos;
++c_pos; ++c_pos;
} }
} }
template <bool check_overflow, bool scale_left, bool scale_right> template <bool scale_left, bool scale_right>
static void NO_INLINE constantVector(A a, const ArrayB & b, PaddedPODArray<UInt8> & c, CompareInt scale) static void NO_INLINE constantVector(A a, const ArrayB & b, PaddedPODArray<UInt8> & c, CompareInt scale)
{ {
size_t size = b.size(); size_t size = b.size();
@ -305,7 +309,7 @@ private:
while (b_pos < b_end) while (b_pos < b_end)
{ {
*c_pos = apply<check_overflow, scale_left, scale_right>(a, *b_pos, scale); *c_pos = apply<scale_left, scale_right>(a, *b_pos, scale);
++b_pos; ++b_pos;
++c_pos; ++c_pos;
} }

View File

@ -529,25 +529,22 @@ Field Field::restoreFromDump(std::string_view dump_)
template <typename T> template <typename T>
bool decimalEqual(T x, T y, UInt32 x_scale, UInt32 y_scale) bool decimalEqual(T x, T y, UInt32 x_scale, UInt32 y_scale)
{ {
bool check_overflow = true;
using Comparator = DecimalComparison<T, T, EqualsOp>; using Comparator = DecimalComparison<T, T, EqualsOp>;
return Comparator::compare(x, y, x_scale, y_scale, check_overflow); return Comparator::compare(x, y, x_scale, y_scale);
} }
template <typename T> template <typename T>
bool decimalLess(T x, T y, UInt32 x_scale, UInt32 y_scale) bool decimalLess(T x, T y, UInt32 x_scale, UInt32 y_scale)
{ {
bool check_overflow = true;
using Comparator = DecimalComparison<T, T, LessOp>; using Comparator = DecimalComparison<T, T, LessOp>;
return Comparator::compare(x, y, x_scale, y_scale, check_overflow); return Comparator::compare(x, y, x_scale, y_scale);
} }
template <typename T> template <typename T>
bool decimalLessOrEqual(T x, T y, UInt32 x_scale, UInt32 y_scale) bool decimalLessOrEqual(T x, T y, UInt32 x_scale, UInt32 y_scale)
{ {
bool check_overflow = true;
using Comparator = DecimalComparison<T, T, LessOrEqualsOp>; using Comparator = DecimalComparison<T, T, LessOrEqualsOp>;
return Comparator::compare(x, y, x_scale, y_scale, check_overflow); return Comparator::compare(x, y, x_scale, y_scale);
} }

View File

@ -863,9 +863,6 @@ template <> struct Field::EnumToType<Field::Types::AggregateFunctionState> { usi
template <> struct Field::EnumToType<Field::Types::CustomType> { using Type = CustomType; }; template <> struct Field::EnumToType<Field::Types::CustomType> { using Type = CustomType; };
template <> struct Field::EnumToType<Field::Types::Bool> { using Type = UInt64; }; template <> struct Field::EnumToType<Field::Types::Bool> { using Type = UInt64; };
/// Use it to prevent inclusion of magic_enum in headers, which is very expensive for the compiler
std::string_view fieldTypeToString(Field::Types::Which type);
constexpr bool isInt64OrUInt64FieldType(Field::Types::Which t) constexpr bool isInt64OrUInt64FieldType(Field::Types::Which t)
{ {
return t == Field::Types::Int64 return t == Field::Types::Int64
@ -889,7 +886,7 @@ auto & Field::safeGet()
if (target != which && if (target != which &&
!(which == Field::Types::Bool && (target == Field::Types::UInt64 || target == Field::Types::Int64)) && !(which == Field::Types::Bool && (target == Field::Types::UInt64 || target == Field::Types::Int64)) &&
!(isInt64OrUInt64FieldType(which) && isInt64OrUInt64FieldType(target))) !(isInt64OrUInt64FieldType(which) && isInt64OrUInt64FieldType(target)))
throw Exception(ErrorCodes::BAD_GET, "Bad get: has {}, requested {}", getTypeName(), fieldTypeToString(target)); throw Exception(ErrorCodes::BAD_GET, "Bad get: has {}, requested {}", getTypeName(), target);
return get<T>(); return get<T>();
} }
@ -1005,6 +1002,8 @@ void readQuoted(DecimalField<T> & x, ReadBuffer & buf);
void writeFieldText(const Field & x, WriteBuffer & buf); void writeFieldText(const Field & x, WriteBuffer & buf);
String toString(const Field & x); String toString(const Field & x);
std::string_view fieldTypeToString(Field::Types::Which type);
} }
template <> template <>

View File

@ -1,4 +1,3 @@
#include <Access/AccessControl.h>
#include <Core/BackgroundSchedulePool.h> #include <Core/BackgroundSchedulePool.h>
#include <Core/BaseSettings.h> #include <Core/BaseSettings.h>
#include <Core/BaseSettingsFwdMacrosImpl.h> #include <Core/BaseSettingsFwdMacrosImpl.h>
@ -27,8 +26,6 @@ extern const Metric BackgroundMessageBrokerSchedulePoolSize;
namespace DB namespace DB
{ {
// clang-format off
#define LIST_OF_SERVER_SETTINGS(DECLARE, ALIAS) \ #define LIST_OF_SERVER_SETTINGS(DECLARE, ALIAS) \
DECLARE(Bool, show_addresses_in_stack_traces, true, "If it is set true will show addresses in stack traces", 0) \ DECLARE(Bool, show_addresses_in_stack_traces, true, "If it is set true will show addresses in stack traces", 0) \
DECLARE(Bool, shutdown_wait_unfinished_queries, false, "If set true ClickHouse will wait for running queries finish before shutdown.", 0) \ DECLARE(Bool, shutdown_wait_unfinished_queries, false, "If set true ClickHouse will wait for running queries finish before shutdown.", 0) \
@ -204,11 +201,7 @@ namespace DB
DECLARE(UInt64, load_marks_threadpool_pool_size, 50, "Size of background pool for marks loading", 0) \ DECLARE(UInt64, load_marks_threadpool_pool_size, 50, "Size of background pool for marks loading", 0) \
DECLARE(UInt64, load_marks_threadpool_queue_size, 1000000, "Number of tasks which is possible to push into prefetches pool", 0) \ DECLARE(UInt64, load_marks_threadpool_queue_size, 1000000, "Number of tasks which is possible to push into prefetches pool", 0) \
DECLARE(UInt64, threadpool_writer_pool_size, 100, "Size of background pool for write requests to object storages", 0) \ DECLARE(UInt64, threadpool_writer_pool_size, 100, "Size of background pool for write requests to object storages", 0) \
DECLARE(UInt64, threadpool_writer_queue_size, 1000000, "Number of tasks which is possible to push into background pool for write requests to object storages", 0) \ DECLARE(UInt64, threadpool_writer_queue_size, 1000000, "Number of tasks which is possible to push into background pool for write requests to object storages", 0)
DECLARE(UInt32, allowed_feature_tier, 0, "0 - All feature tiers allowed (experimental, beta, production). 1 - Only beta and production feature tiers allowed. 2 - Only production feature tier allowed", 0) \
// clang-format on
/// If you add a setting which can be updated at runtime, please update 'changeable_settings' map in dumpToSystemServerSettingsColumns below /// If you add a setting which can be updated at runtime, please update 'changeable_settings' map in dumpToSystemServerSettingsColumns below
@ -291,42 +284,40 @@ void ServerSettings::dumpToSystemServerSettingsColumns(ServerSettingColumnsParam
/// with new the setting values but the settings themselves are not stored between re-loads. As a result, if one wants to know the /// with new the setting values but the settings themselves are not stored between re-loads. As a result, if one wants to know the
/// current setting values, one needs to ask the components directly. /// current setting values, one needs to ask the components directly.
std::unordered_map<String, std::pair<String, ChangeableWithoutRestart>> changeable_settings std::unordered_map<String, std::pair<String, ChangeableWithoutRestart>> changeable_settings
= { = {{"max_server_memory_usage", {std::to_string(total_memory_tracker.getHardLimit()), ChangeableWithoutRestart::Yes}},
{"max_server_memory_usage", {std::to_string(total_memory_tracker.getHardLimit()), ChangeableWithoutRestart::Yes}},
{"max_table_size_to_drop", {std::to_string(context->getMaxTableSizeToDrop()), ChangeableWithoutRestart::Yes}}, {"max_table_size_to_drop", {std::to_string(context->getMaxTableSizeToDrop()), ChangeableWithoutRestart::Yes}},
{"max_partition_size_to_drop", {std::to_string(context->getMaxPartitionSizeToDrop()), ChangeableWithoutRestart::Yes}}, {"max_partition_size_to_drop", {std::to_string(context->getMaxPartitionSizeToDrop()), ChangeableWithoutRestart::Yes}},
{"max_concurrent_queries", {std::to_string(context->getProcessList().getMaxSize()), ChangeableWithoutRestart::Yes}}, {"max_concurrent_queries", {std::to_string(context->getProcessList().getMaxSize()), ChangeableWithoutRestart::Yes}},
{"max_concurrent_insert_queries", {"max_concurrent_insert_queries",
{std::to_string(context->getProcessList().getMaxInsertQueriesAmount()), ChangeableWithoutRestart::Yes}}, {std::to_string(context->getProcessList().getMaxInsertQueriesAmount()), ChangeableWithoutRestart::Yes}},
{"max_concurrent_select_queries", {"max_concurrent_select_queries",
{std::to_string(context->getProcessList().getMaxSelectQueriesAmount()), ChangeableWithoutRestart::Yes}}, {std::to_string(context->getProcessList().getMaxSelectQueriesAmount()), ChangeableWithoutRestart::Yes}},
{"max_waiting_queries", {std::to_string(context->getProcessList().getMaxWaitingQueriesAmount()), ChangeableWithoutRestart::Yes}}, {"max_waiting_queries", {std::to_string(context->getProcessList().getMaxWaitingQueriesAmount()), ChangeableWithoutRestart::Yes}},
{"background_buffer_flush_schedule_pool_size", {"background_buffer_flush_schedule_pool_size",
{std::to_string(CurrentMetrics::get(CurrentMetrics::BackgroundBufferFlushSchedulePoolSize)), ChangeableWithoutRestart::IncreaseOnly}}, {std::to_string(CurrentMetrics::get(CurrentMetrics::BackgroundBufferFlushSchedulePoolSize)),
{"background_schedule_pool_size", ChangeableWithoutRestart::IncreaseOnly}},
{std::to_string(CurrentMetrics::get(CurrentMetrics::BackgroundSchedulePoolSize)), ChangeableWithoutRestart::IncreaseOnly}}, {"background_schedule_pool_size",
{"background_message_broker_schedule_pool_size", {std::to_string(CurrentMetrics::get(CurrentMetrics::BackgroundSchedulePoolSize)), ChangeableWithoutRestart::IncreaseOnly}},
{std::to_string(CurrentMetrics::get(CurrentMetrics::BackgroundMessageBrokerSchedulePoolSize)), ChangeableWithoutRestart::IncreaseOnly}}, {"background_message_broker_schedule_pool_size",
{"background_distributed_schedule_pool_size", {std::to_string(CurrentMetrics::get(CurrentMetrics::BackgroundMessageBrokerSchedulePoolSize)),
{std::to_string(CurrentMetrics::get(CurrentMetrics::BackgroundDistributedSchedulePoolSize)), ChangeableWithoutRestart::IncreaseOnly}}, ChangeableWithoutRestart::IncreaseOnly}},
{"background_distributed_schedule_pool_size",
{std::to_string(CurrentMetrics::get(CurrentMetrics::BackgroundDistributedSchedulePoolSize)),
ChangeableWithoutRestart::IncreaseOnly}},
{"mark_cache_size", {std::to_string(context->getMarkCache()->maxSizeInBytes()), ChangeableWithoutRestart::Yes}}, {"mark_cache_size", {std::to_string(context->getMarkCache()->maxSizeInBytes()), ChangeableWithoutRestart::Yes}},
{"uncompressed_cache_size", {std::to_string(context->getUncompressedCache()->maxSizeInBytes()), ChangeableWithoutRestart::Yes}}, {"uncompressed_cache_size", {std::to_string(context->getUncompressedCache()->maxSizeInBytes()), ChangeableWithoutRestart::Yes}},
{"index_mark_cache_size", {std::to_string(context->getIndexMarkCache()->maxSizeInBytes()), ChangeableWithoutRestart::Yes}}, {"index_mark_cache_size", {std::to_string(context->getIndexMarkCache()->maxSizeInBytes()), ChangeableWithoutRestart::Yes}},
{"index_uncompressed_cache_size", {"index_uncompressed_cache_size",
{std::to_string(context->getIndexUncompressedCache()->maxSizeInBytes()), ChangeableWithoutRestart::Yes}}, {std::to_string(context->getIndexUncompressedCache()->maxSizeInBytes()), ChangeableWithoutRestart::Yes}},
{"mmap_cache_size", {std::to_string(context->getMMappedFileCache()->maxSizeInBytes()), ChangeableWithoutRestart::Yes}}, {"mmap_cache_size", {std::to_string(context->getMMappedFileCache()->maxSizeInBytes()), ChangeableWithoutRestart::Yes}},
{"merge_workload", {context->getMergeWorkload(), ChangeableWithoutRestart::Yes}}, {"merge_workload", {context->getMergeWorkload(), ChangeableWithoutRestart::Yes}},
{"mutation_workload", {context->getMutationWorkload(), ChangeableWithoutRestart::Yes}}, {"mutation_workload", {context->getMutationWorkload(), ChangeableWithoutRestart::Yes}},
{"config_reload_interval_ms", {std::to_string(context->getConfigReloaderInterval()), ChangeableWithoutRestart::Yes}}, {"config_reload_interval_ms", {std::to_string(context->getConfigReloaderInterval()), ChangeableWithoutRestart::Yes}}};
{"allowed_feature_tier",
{std::to_string(context->getAccessControl().getAllowTierSettings()), ChangeableWithoutRestart::Yes}},
};
if (context->areBackgroundExecutorsInitialized()) if (context->areBackgroundExecutorsInitialized())
{ {

View File

@ -433,7 +433,7 @@ Possible values:
- 0 `INSERT` query appends new data to the end of the file. - 0 `INSERT` query appends new data to the end of the file.
- 1 `INSERT` query creates a new file. - 1 `INSERT` query creates a new file.
)", 0) \ )", 0) \
DECLARE(Bool, s3_skip_empty_files, true, R"( DECLARE(Bool, s3_skip_empty_files, false, R"(
Enables or disables skipping empty files in [S3](../../engines/table-engines/integrations/s3.md) engine tables. Enables or disables skipping empty files in [S3](../../engines/table-engines/integrations/s3.md) engine tables.
Possible values: Possible values:
@ -4323,7 +4323,7 @@ Disable limit on kafka_num_consumers that depends on the number of available CPU
)", 0) \ )", 0) \
DECLARE(Bool, allow_experimental_kafka_offsets_storage_in_keeper, false, R"( DECLARE(Bool, allow_experimental_kafka_offsets_storage_in_keeper, false, R"(
Allow experimental feature to store Kafka related offsets in ClickHouse Keeper. When enabled a ClickHouse Keeper path and replica name can be specified to the Kafka table engine. As a result instead of the regular Kafka engine, a new type of storage engine will be used that stores the committed offsets primarily in ClickHouse Keeper Allow experimental feature to store Kafka related offsets in ClickHouse Keeper. When enabled a ClickHouse Keeper path and replica name can be specified to the Kafka table engine. As a result instead of the regular Kafka engine, a new type of storage engine will be used that stores the committed offsets primarily in ClickHouse Keeper
)", EXPERIMENTAL) \ )", 0) \
DECLARE(Bool, enable_software_prefetch_in_aggregation, true, R"( DECLARE(Bool, enable_software_prefetch_in_aggregation, true, R"(
Enable use of software prefetch in aggregation Enable use of software prefetch in aggregation
)", 0) \ )", 0) \
@ -5656,10 +5656,10 @@ Build local plan for local replica
\ \
DECLARE(Bool, allow_experimental_analyzer, true, R"( DECLARE(Bool, allow_experimental_analyzer, true, R"(
Allow new query analyzer. Allow new query analyzer.
)", IMPORTANT) ALIAS(enable_analyzer) \ )", IMPORTANT | BETA) ALIAS(enable_analyzer) \
DECLARE(Bool, analyzer_compatibility_join_using_top_level_identifier, false, R"( DECLARE(Bool, analyzer_compatibility_join_using_top_level_identifier, false, R"(
Force to resolve identifier in JOIN USING from projection (for example, in `SELECT a + 1 AS b FROM t1 JOIN t2 USING (b)` join will be performed by `t1.a + 1 = t2.b`, rather then `t1.b = t2.b`). Force to resolve identifier in JOIN USING from projection (for example, in `SELECT a + 1 AS b FROM t1 JOIN t2 USING (b)` join will be performed by `t1.a + 1 = t2.b`, rather then `t1.b = t2.b`).
)", 0) \ )", BETA) \
\ \
DECLARE(Timezone, session_timezone, "", R"( DECLARE(Timezone, session_timezone, "", R"(
Sets the implicit time zone of the current session or query. Sets the implicit time zone of the current session or query.
@ -5770,7 +5770,7 @@ Possible values:
- 0 the [TimeSeries](../../engines/table-engines/integrations/time-series.md) table engine is disabled. - 0 the [TimeSeries](../../engines/table-engines/integrations/time-series.md) table engine is disabled.
- 1 the [TimeSeries](../../engines/table-engines/integrations/time-series.md) table engine is enabled. - 1 the [TimeSeries](../../engines/table-engines/integrations/time-series.md) table engine is enabled.
)", EXPERIMENTAL) \ )", 0) \
DECLARE(Bool, allow_experimental_vector_similarity_index, false, R"( DECLARE(Bool, allow_experimental_vector_similarity_index, false, R"(
Allow experimental vector similarity index Allow experimental vector similarity index
)", EXPERIMENTAL) \ )", EXPERIMENTAL) \
@ -5843,7 +5843,7 @@ If it is set to true, allow to use experimental full-text index.
\ \
DECLARE(Bool, allow_experimental_join_condition, false, R"( DECLARE(Bool, allow_experimental_join_condition, false, R"(
Support join with inequal conditions which involve columns from both left and right table. e.g. t1.y < t2.y. Support join with inequal conditions which involve columns from both left and right table. e.g. t1.y < t2.y.
)", EXPERIMENTAL) \ )", 0) \
\ \
DECLARE(Bool, allow_experimental_live_view, false, R"( DECLARE(Bool, allow_experimental_live_view, false, R"(
Allows creation of a deprecated LIVE VIEW. Allows creation of a deprecated LIVE VIEW.
@ -5852,7 +5852,7 @@ Possible values:
- 0 Working with live views is disabled. - 0 Working with live views is disabled.
- 1 Working with live views is enabled. - 1 Working with live views is enabled.
)", EXPERIMENTAL) \ )", 0) \
DECLARE(Seconds, live_view_heartbeat_interval, 15, R"( DECLARE(Seconds, live_view_heartbeat_interval, 15, R"(
The heartbeat interval in seconds to indicate live query is alive. The heartbeat interval in seconds to indicate live query is alive.
)", EXPERIMENTAL) \ )", EXPERIMENTAL) \
@ -6206,11 +6206,6 @@ bool Settings::isChanged(std::string_view name) const
return impl->isChanged(name); return impl->isChanged(name);
} }
SettingsTierType Settings::getTier(std::string_view name) const
{
return impl->getTier(name);
}
bool Settings::tryGet(std::string_view name, Field & value) const bool Settings::tryGet(std::string_view name, Field & value) const
{ {
return impl->tryGet(name, value); return impl->tryGet(name, value);

View File

@ -4,7 +4,6 @@
#include <Core/Field.h> #include <Core/Field.h>
#include <Core/SettingsEnums.h> #include <Core/SettingsEnums.h>
#include <Core/SettingsFields.h> #include <Core/SettingsFields.h>
#include <Core/SettingsTierType.h>
#include <Core/SettingsWriteFormat.h> #include <Core/SettingsWriteFormat.h>
#include <base/types.h> #include <base/types.h>
#include <Common/SettingsChanges.h> #include <Common/SettingsChanges.h>
@ -118,7 +117,6 @@ struct Settings
/// General API as needed /// General API as needed
bool has(std::string_view name) const; bool has(std::string_view name) const;
bool isChanged(std::string_view name) const; bool isChanged(std::string_view name) const;
SettingsTierType getTier(std::string_view name) const;
bool tryGet(std::string_view name, Field & value) const; bool tryGet(std::string_view name, Field & value) const;
Field get(std::string_view name) const; Field get(std::string_view name) const;

View File

@ -87,7 +87,6 @@ static std::initializer_list<std::pair<ClickHouseVersion, SettingsChangesHistory
{"filesystem_cache_skip_download_if_exceeds_per_query_cache_write_limit", 1, 1, "Rename of setting skip_download_if_exceeds_query_cache_limit"}, {"filesystem_cache_skip_download_if_exceeds_per_query_cache_write_limit", 1, 1, "Rename of setting skip_download_if_exceeds_query_cache_limit"},
{"filesystem_cache_prefer_bigger_buffer_size", true, true, "New setting"}, {"filesystem_cache_prefer_bigger_buffer_size", true, true, "New setting"},
{"read_in_order_use_virtual_row", false, false, "Use virtual row while reading in order of primary key or its monotonic function fashion. It is useful when searching over multiple parts as only relevant ones are touched."}, {"read_in_order_use_virtual_row", false, false, "Use virtual row while reading in order of primary key or its monotonic function fashion. It is useful when searching over multiple parts as only relevant ones are touched."},
{"s3_skip_empty_files", false, true, "We hope it will provide better UX"},
{"filesystem_cache_boundary_alignment", 0, 0, "New setting"}, {"filesystem_cache_boundary_alignment", 0, 0, "New setting"},
{"push_external_roles_in_interserver_queries", false, false, "New setting."}, {"push_external_roles_in_interserver_queries", false, false, "New setting."},
} }

View File

@ -87,77 +87,6 @@ static bool callOnBasicType(TypeIndex number, F && f)
return false; return false;
} }
template <typename T, bool _int, bool _float, bool _decimal, bool _datetime, typename F>
static bool callOnBasicTypeSecondArg(TypeIndex number, F && f)
{
if constexpr (_int)
{
switch (number)
{
case TypeIndex::UInt8: return f(TypePair<UInt8, T>());
case TypeIndex::UInt16: return f(TypePair<UInt16, T>());
case TypeIndex::UInt32: return f(TypePair<UInt32, T>());
case TypeIndex::UInt64: return f(TypePair<UInt64, T>());
case TypeIndex::UInt128: return f(TypePair<UInt128, T>());
case TypeIndex::UInt256: return f(TypePair<UInt256, T>());
case TypeIndex::Int8: return f(TypePair<Int8, T>());
case TypeIndex::Int16: return f(TypePair<Int16, T>());
case TypeIndex::Int32: return f(TypePair<Int32, T>());
case TypeIndex::Int64: return f(TypePair<Int64, T>());
case TypeIndex::Int128: return f(TypePair<Int128, T>());
case TypeIndex::Int256: return f(TypePair<Int256, T>());
case TypeIndex::Enum8: return f(TypePair<Int8, T>());
case TypeIndex::Enum16: return f(TypePair<Int16, T>());
default:
break;
}
}
if constexpr (_decimal)
{
switch (number)
{
case TypeIndex::Decimal32: return f(TypePair<Decimal32, T>());
case TypeIndex::Decimal64: return f(TypePair<Decimal64, T>());
case TypeIndex::Decimal128: return f(TypePair<Decimal128, T>());
case TypeIndex::Decimal256: return f(TypePair<Decimal256, T>());
default:
break;
}
}
if constexpr (_float)
{
switch (number)
{
case TypeIndex::BFloat16: return f(TypePair<BFloat16, T>());
case TypeIndex::Float32: return f(TypePair<Float32, T>());
case TypeIndex::Float64: return f(TypePair<Float64, T>());
default:
break;
}
}
if constexpr (_datetime)
{
switch (number)
{
case TypeIndex::Date: return f(TypePair<UInt16, T>());
case TypeIndex::Date32: return f(TypePair<Int32, T>());
case TypeIndex::DateTime: return f(TypePair<UInt32, T>());
case TypeIndex::DateTime64: return f(TypePair<DateTime64, T>());
default:
break;
}
}
return false;
}
/// Unroll template using TypeIndex /// Unroll template using TypeIndex
template <bool _int, bool _float, bool _decimal, bool _datetime, typename F> template <bool _int, bool _float, bool _decimal, bool _datetime, typename F>
static inline bool callOnBasicTypes(TypeIndex type_num1, TypeIndex type_num2, F && f) static inline bool callOnBasicTypes(TypeIndex type_num1, TypeIndex type_num2, F && f)

View File

@ -1,8 +1,7 @@
#include <type_traits>
#include <Core/DecimalFunctions.h>
#include <Core/Settings.h> #include <Core/Settings.h>
#include <DataTypes/DataTypeDecimalBase.h> #include <DataTypes/DataTypeDecimalBase.h>
#include <Interpreters/Context.h> #include <Interpreters/Context.h>
#include <type_traits>
namespace DB namespace DB
{ {
@ -15,12 +14,6 @@ namespace ErrorCodes
{ {
} }
template <is_decimal T>
constexpr size_t DataTypeDecimalBase<T>::maxPrecision()
{
return DecimalUtils::max_precision<T>;
}
bool decimalCheckComparisonOverflow(ContextPtr context) bool decimalCheckComparisonOverflow(ContextPtr context)
{ {
return context->getSettingsRef()[Setting::decimal_check_overflow]; return context->getSettingsRef()[Setting::decimal_check_overflow];
@ -48,18 +41,6 @@ T DataTypeDecimalBase<T>::getScaleMultiplier(UInt32 scale_)
return DecimalUtils::scaleMultiplier<typename T::NativeType>(scale_); return DecimalUtils::scaleMultiplier<typename T::NativeType>(scale_);
} }
template <is_decimal T>
T DataTypeDecimalBase<T>::wholePart(T x) const
{
return DecimalUtils::getWholePart(x, scale);
}
template <is_decimal T>
T DataTypeDecimalBase<T>::fractionalPart(T x) const
{
return DecimalUtils::getFractionalPart(x, scale);
}
/// Explicit template instantiations. /// Explicit template instantiations.
template class DataTypeDecimalBase<Decimal32>; template class DataTypeDecimalBase<Decimal32>;

View File

@ -3,10 +3,11 @@
#include <cmath> #include <cmath>
#include <type_traits> #include <type_traits>
#include <Columns/ColumnDecimal.h>
#include <Core/TypeId.h> #include <Core/TypeId.h>
#include <DataTypes/DataTypesNumber.h> #include <Core/DecimalFunctions.h>
#include <Columns/ColumnDecimal.h>
#include <DataTypes/IDataType.h> #include <DataTypes/IDataType.h>
#include <DataTypes/DataTypesNumber.h>
#include <Interpreters/Context_fwd.h> #include <Interpreters/Context_fwd.h>
@ -63,7 +64,7 @@ public:
static constexpr bool is_parametric = true; static constexpr bool is_parametric = true;
static constexpr size_t maxPrecision(); static constexpr size_t maxPrecision() { return DecimalUtils::max_precision<T>; }
DataTypeDecimalBase(UInt32 precision_, UInt32 scale_) DataTypeDecimalBase(UInt32 precision_, UInt32 scale_)
: precision(precision_), : precision(precision_),
@ -103,8 +104,15 @@ public:
UInt32 getScale() const { return scale; } UInt32 getScale() const { return scale; }
T getScaleMultiplier() const { return getScaleMultiplier(scale); } T getScaleMultiplier() const { return getScaleMultiplier(scale); }
T wholePart(T x) const; T wholePart(T x) const
T fractionalPart(T x) const; {
return DecimalUtils::getWholePart(x, scale);
}
T fractionalPart(T x) const
{
return DecimalUtils::getFractionalPart(x, scale);
}
T maxWholeValue() const { return getScaleMultiplier(precision - scale) - T(1); } T maxWholeValue() const { return getScaleMultiplier(precision - scale) - T(1); }
@ -139,6 +147,11 @@ public:
static T getScaleMultiplier(UInt32 scale); static T getScaleMultiplier(UInt32 scale);
DecimalUtils::DataTypeDecimalTrait<T> getTrait() const
{
return {precision, scale};
}
protected: protected:
const UInt32 precision; const UInt32 precision;
const UInt32 scale; const UInt32 scale;
@ -154,35 +167,50 @@ inline const DataTypeDecimalBase<T> * checkDecimalBase(const IDataType & data_ty
return nullptr; return nullptr;
} }
template <> constexpr size_t DataTypeDecimalBase<Decimal32>::maxPrecision() { return 9; }; template <bool is_multiply, bool is_division, typename T, typename U, template <typename> typename DecimalType>
template <> constexpr size_t DataTypeDecimalBase<Decimal64>::maxPrecision() { return 18; }; inline auto decimalResultType(const DecimalType<T> & tx, const DecimalType<U> & ty)
template <> constexpr size_t DataTypeDecimalBase<DateTime64>::maxPrecision() { return 18; }; {
template <> constexpr size_t DataTypeDecimalBase<Decimal128>::maxPrecision() { return 38; }; const auto result_trait = DecimalUtils::binaryOpResult<is_multiply, is_division>(tx, ty);
template <> constexpr size_t DataTypeDecimalBase<Decimal256>::maxPrecision() { return 76; }; return DecimalType<typename decltype(result_trait)::FieldType>(result_trait.precision, result_trait.scale);
}
extern template class DataTypeDecimalBase<Decimal32>; template <bool is_multiply, bool is_division, typename T, typename U, template <typename> typename DecimalType>
extern template class DataTypeDecimalBase<Decimal64>; inline DecimalType<T> decimalResultType(const DecimalType<T> & tx, const DataTypeNumber<U> & ty)
extern template class DataTypeDecimalBase<DateTime64>; {
extern template class DataTypeDecimalBase<Decimal128>; const auto result_trait = DecimalUtils::binaryOpResult<is_multiply, is_division>(tx, ty);
extern template class DataTypeDecimalBase<Decimal256>; return DecimalType<typename decltype(result_trait)::FieldType>(result_trait.precision, result_trait.scale);
}
template <bool is_multiply, bool is_division, typename T, typename U, template <typename> typename DecimalType>
inline DecimalType<U> decimalResultType(const DataTypeNumber<T> & tx, const DecimalType<U> & ty)
{
const auto result_trait = DecimalUtils::binaryOpResult<is_multiply, is_division>(tx, ty);
return DecimalType<typename decltype(result_trait)::FieldType>(result_trait.precision, result_trait.scale);
}
template <template <typename> typename DecimalType> template <template <typename> typename DecimalType>
inline DataTypePtr createDecimal(UInt64 precision_value, UInt64 scale_value) inline DataTypePtr createDecimal(UInt64 precision_value, UInt64 scale_value)
{ {
if (precision_value < 1 || precision_value > DataTypeDecimalBase<Decimal256>::maxPrecision()) if (precision_value < DecimalUtils::min_precision || precision_value > DecimalUtils::max_precision<Decimal256>)
throw Exception(ErrorCodes::ARGUMENT_OUT_OF_BOUND, "Wrong precision: it must be between {} and {}, got {}", throw Exception(ErrorCodes::ARGUMENT_OUT_OF_BOUND, "Wrong precision: it must be between {} and {}, got {}",
1, DataTypeDecimalBase<Decimal256>::maxPrecision(), precision_value); DecimalUtils::min_precision, DecimalUtils::max_precision<Decimal256>, precision_value);
if (scale_value > precision_value) if (scale_value > precision_value)
throw Exception(ErrorCodes::ARGUMENT_OUT_OF_BOUND, "Negative scales and scales larger than precision are not supported"); throw Exception(ErrorCodes::ARGUMENT_OUT_OF_BOUND, "Negative scales and scales larger than precision are not supported");
if (precision_value <= DataTypeDecimalBase<Decimal32>::maxPrecision()) if (precision_value <= DecimalUtils::max_precision<Decimal32>)
return std::make_shared<DecimalType<Decimal32>>(precision_value, scale_value); return std::make_shared<DecimalType<Decimal32>>(precision_value, scale_value);
if (precision_value <= DataTypeDecimalBase<Decimal64>::maxPrecision()) if (precision_value <= DecimalUtils::max_precision<Decimal64>)
return std::make_shared<DecimalType<Decimal64>>(precision_value, scale_value); return std::make_shared<DecimalType<Decimal64>>(precision_value, scale_value);
if (precision_value <= DataTypeDecimalBase<Decimal128>::maxPrecision()) if (precision_value <= DecimalUtils::max_precision<Decimal128>)
return std::make_shared<DecimalType<Decimal128>>(precision_value, scale_value); return std::make_shared<DecimalType<Decimal128>>(precision_value, scale_value);
return std::make_shared<DecimalType<Decimal256>>(precision_value, scale_value); return std::make_shared<DecimalType<Decimal256>>(precision_value, scale_value);
} }
extern template class DataTypeDecimalBase<Decimal32>;
extern template class DataTypeDecimalBase<Decimal64>;
extern template class DataTypeDecimalBase<Decimal128>;
extern template class DataTypeDecimalBase<Decimal256>;
extern template class DataTypeDecimalBase<DateTime64>;
} }

View File

@ -111,13 +111,13 @@ DataTypePtr convertMySQLDataType(MultiEnum<MySQLDataTypesSupport> type_support,
} }
else if (type_support.isSet(MySQLDataTypesSupport::DECIMAL) && (type_name == "numeric" || type_name == "decimal")) else if (type_support.isSet(MySQLDataTypesSupport::DECIMAL) && (type_name == "numeric" || type_name == "decimal"))
{ {
if (precision <= DataTypeDecimalBase<Decimal32>::maxPrecision()) if (precision <= DecimalUtils::max_precision<Decimal32>)
res = std::make_shared<DataTypeDecimal<Decimal32>>(precision, scale); res = std::make_shared<DataTypeDecimal<Decimal32>>(precision, scale);
else if (precision <= DataTypeDecimalBase<Decimal64>::maxPrecision()) else if (precision <= DecimalUtils::max_precision<Decimal64>)
res = std::make_shared<DataTypeDecimal<Decimal64>>(precision, scale); res = std::make_shared<DataTypeDecimal<Decimal64>>(precision, scale);
else if (precision <= DataTypeDecimalBase<Decimal128>::maxPrecision()) else if (precision <= DecimalUtils::max_precision<Decimal128>)
res = std::make_shared<DataTypeDecimal<Decimal128>>(precision, scale); res = std::make_shared<DataTypeDecimal<Decimal128>>(precision, scale);
else if (precision <= DataTypeDecimalBase<Decimal256>::maxPrecision()) else if (precision <= DecimalUtils::max_precision<Decimal256>)
res = std::make_shared<DataTypeDecimal<Decimal256>>(precision, scale); res = std::make_shared<DataTypeDecimal<Decimal256>>(precision, scale);
} }
else if (type_name == "point") else if (type_name == "point")

View File

@ -493,7 +493,7 @@ void buildConfigurationFromFunctionWithKeyValueArguments(
/// We assume that function will not take arguments and will return constant value like tcpPort or hostName /// We assume that function will not take arguments and will return constant value like tcpPort or hostName
/// Such functions will return column with size equal to input_rows_count. /// Such functions will return column with size equal to input_rows_count.
size_t input_rows_count = 1; size_t input_rows_count = 1;
auto result = function->execute({}, function->getResultType(), input_rows_count, /* dry_run = */ false); auto result = function->execute({}, function->getResultType(), input_rows_count);
Field value; Field value;
result->get(0, value); result->get(0, value);

View File

@ -1443,8 +1443,7 @@ public:
auto shared_variant_discr = column_dynamic.getSharedVariantDiscriminator(); auto shared_variant_discr = column_dynamic.getSharedVariantDiscriminator();
auto insert_settings_with_no_type_conversion = insert_settings; auto insert_settings_with_no_type_conversion = insert_settings;
insert_settings_with_no_type_conversion.allow_type_conversion = false; insert_settings_with_no_type_conversion.allow_type_conversion = false;
auto order = SerializationVariant::getVariantsDeserializeTextOrder(assert_cast<const DataTypeVariant &>(*variant_info.variant_type).getVariants()); for (size_t i = 0; i != variant_info.variant_names.size(); ++i)
for (size_t i : order)
{ {
if (i != shared_variant_discr) if (i != shared_variant_discr)
{ {

View File

@ -1308,8 +1308,7 @@ namespace
{ {
if (decimal.value == 0) if (decimal.value == 0)
writeInt(0); writeInt(0);
else if (DecimalComparison<DecimalType, int, EqualsOp>::compare( else if (DecimalComparison<DecimalType, int, EqualsOp>::compare(decimal, 1, scale, 0))
decimal, 1, scale, 0, /* check overflow */ true))
writeInt(1); writeInt(1);
else else
{ {

View File

@ -7,7 +7,6 @@ add_headers_and_sources(clickhouse_functions .)
# This allows less dependency and linker work (specially important when building many example executables) # This allows less dependency and linker work (specially important when building many example executables)
set(DBMS_FUNCTIONS set(DBMS_FUNCTIONS
IFunction.cpp # IFunctionOverloadResolver::getLambdaArgumentTypes, IExecutableFunction::execute... (Many AST visitors, analyzer passes, some storages...) IFunction.cpp # IFunctionOverloadResolver::getLambdaArgumentTypes, IExecutableFunction::execute... (Many AST visitors, analyzer passes, some storages...)
IFunctionAdaptors.cpp # FunctionToFunctionBaseAdaptor (Used by FunctionFactory.cpp)
FunctionDynamicAdaptor.cpp # IFunctionOverloadResolver::getLambdaArgumentTypes, IExecutableFunction::execute... (Many AST visitors, analyzer passes, some storages...) FunctionDynamicAdaptor.cpp # IFunctionOverloadResolver::getLambdaArgumentTypes, IExecutableFunction::execute... (Many AST visitors, analyzer passes, some storages...)
FunctionFactory.cpp # FunctionFactory::instance() (Many AST visitors, analyzer passes, some storages...) FunctionFactory.cpp # FunctionFactory::instance() (Many AST visitors, analyzer passes, some storages...)
FunctionHelpers.cpp # convertConstTupleToConstantElements, checkAndGetColumnConstStringOrFixedString, checkAndGetNestedArrayOffset ...) FunctionHelpers.cpp # convertConstTupleToConstantElements, checkAndGetColumnConstStringOrFixedString, checkAndGetNestedArrayOffset ...)

View File

@ -41,7 +41,6 @@
#include <Functions/FunctionFactory.h> #include <Functions/FunctionFactory.h>
#include <Functions/FunctionHelpers.h> #include <Functions/FunctionHelpers.h>
#include <Functions/IFunction.h> #include <Functions/IFunction.h>
#include <Functions/IFunctionAdaptors.h>
#include <Functions/IsOperation.h> #include <Functions/IsOperation.h>
#include <Functions/castTypeToEither.h> #include <Functions/castTypeToEither.h>
#include <Interpreters/Context.h> #include <Interpreters/Context.h>
@ -232,27 +231,6 @@ public:
namespace impl_ namespace impl_
{ {
template <bool is_multiply, bool is_division, typename T, typename U, template <typename> typename DecimalType>
inline auto decimalResultType(const DecimalType<T> & tx, const DecimalType<U> & ty)
{
const auto result_trait = DecimalUtils::binaryOpResult<is_multiply, is_division>(tx, ty);
return DecimalType<typename decltype(result_trait)::FieldType>(result_trait.precision, result_trait.scale);
}
template <bool is_multiply, bool is_division, typename T, typename U, template <typename> typename DecimalType>
inline DecimalType<T> decimalResultType(const DecimalType<T> & tx, const DataTypeNumber<U> & ty)
{
const auto result_trait = DecimalUtils::binaryOpResult<is_multiply, is_division>(tx, ty);
return DecimalType<typename decltype(result_trait)::FieldType>(result_trait.precision, result_trait.scale);
}
template <bool is_multiply, bool is_division, typename T, typename U, template <typename> typename DecimalType>
inline DecimalType<U> decimalResultType(const DataTypeNumber<T> & tx, const DecimalType<U> & ty)
{
const auto result_trait = DecimalUtils::binaryOpResult<is_multiply, is_division>(tx, ty);
return DecimalType<typename decltype(result_trait)::FieldType>(result_trait.precision, result_trait.scale);
}
/** Arithmetic operations: +, -, *, /, %, /** Arithmetic operations: +, -, *, /, %,
* intDiv (integer division) * intDiv (integer division)
* Bitwise operations: |, &, ^, ~. * Bitwise operations: |, &, ^, ~.
@ -1188,7 +1166,7 @@ class FunctionBinaryArithmetic : public IFunction
new_arguments[1].type = std::make_shared<DataTypeNumber<DataTypeInterval::FieldType>>(); new_arguments[1].type = std::make_shared<DataTypeNumber<DataTypeInterval::FieldType>>();
auto function = function_builder->build(new_arguments); auto function = function_builder->build(new_arguments);
return function->execute(new_arguments, result_type, input_rows_count, /* dry_run = */ false); return function->execute(new_arguments, result_type, input_rows_count);
} }
ColumnPtr executeDateTimeTupleOfIntervalsPlusMinus(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, ColumnPtr executeDateTimeTupleOfIntervalsPlusMinus(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type,
@ -1202,7 +1180,7 @@ class FunctionBinaryArithmetic : public IFunction
auto function = function_builder->build(new_arguments); auto function = function_builder->build(new_arguments);
return function->execute(new_arguments, result_type, input_rows_count, /* dry_run = */ false); return function->execute(new_arguments, result_type, input_rows_count);
} }
ColumnPtr executeIntervalTupleOfIntervalsPlusMinus(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, ColumnPtr executeIntervalTupleOfIntervalsPlusMinus(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type,
@ -1210,7 +1188,7 @@ class FunctionBinaryArithmetic : public IFunction
{ {
auto function = function_builder->build(arguments); auto function = function_builder->build(arguments);
return function->execute(arguments, result_type, input_rows_count, /* dry_run = */ false); return function->execute(arguments, result_type, input_rows_count);
} }
ColumnPtr executeArraysImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t input_rows_count) const ColumnPtr executeArraysImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t input_rows_count) const
@ -1345,7 +1323,7 @@ class FunctionBinaryArithmetic : public IFunction
auto function = function_builder->build(new_arguments); auto function = function_builder->build(new_arguments);
return function->execute(new_arguments, result_type, input_rows_count, /* dry_run = */ false); return function->execute(new_arguments, result_type, input_rows_count);
} }
template <typename T, typename ResultDataType> template <typename T, typename ResultDataType>
@ -2247,7 +2225,7 @@ ColumnPtr executeStringInteger(const ColumnsWithTypeAndName & arguments, const A
/// Special case when the function is plus, minus or multiply, both arguments are tuples. /// Special case when the function is plus, minus or multiply, both arguments are tuples.
if (auto function_builder = getFunctionForTupleArithmetic(arguments[0].type, arguments[1].type, context)) if (auto function_builder = getFunctionForTupleArithmetic(arguments[0].type, arguments[1].type, context))
{ {
return function_builder->build(arguments)->execute(arguments, result_type, input_rows_count, /* dry_run = */ false); return function_builder->build(arguments)->execute(arguments, result_type, input_rows_count);
} }
/// Special case when the function is multiply or divide, one of arguments is Tuple and another is Number. /// Special case when the function is multiply or divide, one of arguments is Tuple and another is Number.

View File

@ -1,5 +1,4 @@
#include <Functions/FunctionFactory.h> #include <Functions/FunctionFactory.h>
#include <Functions/IFunctionAdaptors.h>
#include <Interpreters/Context.h> #include <Interpreters/Context.h>

View File

@ -5,6 +5,7 @@
#include <Common/IFactoryWithAliases.h> #include <Common/IFactoryWithAliases.h>
#include <Common/FunctionDocumentation.h> #include <Common/FunctionDocumentation.h>
#include <Functions/IFunction.h> #include <Functions/IFunction.h>
#include <Functions/IFunctionAdaptors.h>
#include <functional> #include <functional>
#include <memory> #include <memory>

Some files were not shown because too many files have changed in this diff Show More