From c3e81877cacb1530a26333c3d0ab4d34c404d733 Mon Sep 17 00:00:00 2001 From: Arthur Passos Date: Thu, 28 Apr 2022 10:07:47 -0300 Subject: [PATCH 01/72] Make builds and tests possible in Altinity's infrastructure add comment and rename github robot token add clickhouse instance password parameter use Altinity's s3 bucket Use altinityinfra dockerhub images and minor adjustments (#135) Allow CI to be triggered on PR Proper error reporting during docker pull and lowercase version name allow `v22.3.x.y-altinitystable` git tags Download specific MinIO version instead of latest - same as upstream master remove stale chmod More stable CI/CD builds: - Rebuilding all docker images - Reduced number of docker images - Rerunning functional tests even if those were already executed in previous run Added missing dependencies for stateful tests Re-generating _pb2 files on each test run Changed hardcoded docker images name prefixes from `clickhouse/` to `altinityinfra/` Pushing images as :latest too to avoid some test failures reverted back to use clickhouse/jdbc-bridge etc. --- .github/workflows/release_branches.yml | 1622 +---------------- cmake/autogenerated_versions.txt | 6 +- cmake/version.cmake | 2 +- docker/images.json | 83 +- docker/packager/binary/build.sh | 3 + docker/packager/packager | 2 +- docker/test/base/Dockerfile | 4 +- docker/test/codebrowser/Dockerfile | 2 +- docker/test/fasttest/Dockerfile | 2 +- docker/test/fuzzer/Dockerfile | 2 +- docker/test/integration/base/Dockerfile | 2 +- .../integration/mysql_php_client/Dockerfile | 2 +- .../compose/docker_compose_dotnet_client.yml | 2 +- .../runner/compose/docker_compose_keeper.yml | 6 +- .../docker_compose_kerberized_hdfs.yml | 4 +- .../docker_compose_kerberized_kafka.yml | 2 +- .../runner/compose/docker_compose_minio.yml | 6 +- .../docker_compose_mysql_golang_client.yml | 2 +- .../docker_compose_mysql_java_client.yml | 2 +- .../docker_compose_mysql_js_client.yml | 2 +- .../docker_compose_mysql_php_client.yml | 2 +- .../docker_compose_postgresql_java_client.yml | 2 +- docker/test/keeper-jepsen/Dockerfile | 2 +- docker/test/split_build_smoke_test/Dockerfile | 4 +- docker/test/stateful/Dockerfile | 10 +- docker/test/stateful/setup_minio.sh | 11 + docker/test/stateless/Dockerfile | 9 +- docker/test/stateless/setup_minio.sh | 15 +- docker/test/stateless_pytest/Dockerfile | 2 +- docker/test/stress/Dockerfile | 2 +- docker/test/unit/Dockerfile | 2 +- packages/clickhouse-client.yaml | 4 +- packages/clickhouse-common-static-dbg.yaml | 4 +- packages/clickhouse-common-static.yaml | 4 +- packages/clickhouse-keeper-dbg.yaml | 4 +- packages/clickhouse-keeper.yaml | 4 +- packages/clickhouse-server.yaml | 4 +- tests/ci/ast_fuzzer_check.py | 2 +- tests/ci/build_check.py | 24 +- tests/ci/build_report_check.py | 2 + tests/ci/ccache_utils.py | 3 +- tests/ci/ci_config.py | 10 +- tests/ci/clickhouse_helper.py | 6 +- tests/ci/codebrowser_check.py | 2 +- tests/ci/compatibility_check.py | 8 +- tests/ci/docker_images_check.py | 51 +- tests/ci/docker_manifests_merge.py | 4 +- tests/ci/docker_pull_helper.py | 5 +- tests/ci/docker_server.py | 6 +- tests/ci/docker_test.py | 64 +- tests/ci/docs_check.py | 5 +- tests/ci/docs_release.py | 2 +- tests/ci/env_helper.py | 7 +- tests/ci/fast_test_check.py | 4 +- tests/ci/functional_test_check.py | 14 +- tests/ci/get_robot_token.py | 9 +- tests/ci/git_helper.py | 2 +- tests/ci/git_test.py | 6 + tests/ci/integration_test_check.py | 36 +- tests/ci/keeper_jepsen_check.py | 2 +- tests/ci/performance_comparison_check.py | 5 +- tests/ci/release.py | 2 +- tests/ci/run_check.py | 1 + tests/ci/split_build_smoke_check.py | 6 +- tests/ci/stress_check.py | 5 +- tests/ci/style_check.py | 5 +- tests/ci/tests/docker_images.json | 100 +- tests/ci/unit_tests_check.py | 6 +- tests/ci/version_helper.py | 18 +- tests/integration/ci-runner.py | 35 +- tests/integration/helpers/cluster.py | 34 +- tests/integration/helpers/network.py | 8 +- tests/integration/runner | 20 +- .../integration/test_replicated_users/test.py | 2 +- .../test_s3_zero_copy_replication/test.py | 2 + .../integration/test_s3_zero_copy_ttl/test.py | 34 +- tests/integration/test_storage_kafka/test.py | 12 + tests/queries/0_stateless/00900_orc_load.sh | 9 +- .../aes_encryption_env/clickhouse-service.yml | 2 +- .../clickhouse-service.yml | 2 +- .../example_env/clickhouse-service.yml | 2 +- .../clickhouse-service.yml | 2 +- .../kerberos_env/clickhouse-service.yml | 2 +- .../authentication_env/clickhouse-service.yml | 2 +- .../clickhouse-service.yml | 2 +- .../clickhouse-service.yml | 2 +- .../clickhouse-service.yml | 2 +- .../clickhouse-service.yml | 2 +- .../role_mapping_env/clickhouse-service.yml | 2 +- .../map_type_env/clickhouse-service.yml | 2 +- .../rbac/rbac_env/clickhouse-service.yml | 2 +- tests/testflows/runner | 2 +- .../clickhouse-service.yml | 2 +- utils/clickhouse-docker | 4 +- 94 files changed, 525 insertions(+), 1930 deletions(-) diff --git a/.github/workflows/release_branches.yml b/.github/workflows/release_branches.yml index dd97debaf4bd..a01bbad0f5ed 100644 --- a/.github/workflows/release_branches.yml +++ b/.github/workflows/release_branches.yml @@ -5,11 +5,17 @@ env: PYTHONUNBUFFERED: 1 on: # yamllint disable-line rule:truthy + pull_request: + types: + - synchronize + - reopened + - opened + branches: + - '**/22.3*' push: branches: - # 22.1 and 22.10 - - '2[1-9].[1-9][0-9]' - - '2[1-9].[1-9]' + # Anything/22.3 (e.g customizations/22.3) + - '**/22.3*' jobs: DockerHubPushAarch64: @@ -108,9 +114,9 @@ jobs: # shellcheck disable=SC2046 docker rm -f $(docker ps -a -q) ||: sudo rm -fr "$TEMP_PATH" -######################################################################################### -#################################### ORDINARY BUILDS #################################### -######################################################################################### + ######################################################################################### + #################################### ORDINARY BUILDS #################################### + ######################################################################################### BuilderDebRelease: needs: [DockerHubPush] runs-on: [self-hosted, builder] @@ -124,6 +130,7 @@ jobs: CACHES_PATH=${{runner.temp}}/../ccaches CHECK_NAME=ClickHouse build check (actions) BUILD_NAME=package_release + CLICKHOUSE_STABLE_VERSION_SUFFIX=altinitystable EOF - name: Download changed images uses: actions/download-artifact@v2 @@ -133,6 +140,8 @@ jobs: - name: Clear repository run: | sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" + - name: Trust My Directory + run: git config --global --add safe.directory * # https://stackoverflow.com/a/71940133 - name: Check out repository code uses: actions/checkout@v2 with: @@ -159,138 +168,43 @@ jobs: # shellcheck disable=SC2046 docker rm -f $(docker ps -a -q) ||: sudo rm -fr "$TEMP_PATH" "$CACHES_PATH" - BuilderDebAarch64: - needs: [DockerHubPush] - runs-on: [self-hosted, builder] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/build_check - IMAGES_PATH=${{runner.temp}}/images_path - REPO_COPY=${{runner.temp}}/build_check/ClickHouse - CACHES_PATH=${{runner.temp}}/../ccaches - CHECK_NAME=ClickHouse build check (actions) - BUILD_NAME=package_aarch64 - EOF - - name: Download changed images - uses: actions/download-artifact@v2 - with: - name: changed_images - path: ${{ runner.temp }}/images_path - - name: Check out repository code - uses: actions/checkout@v2 - with: - fetch-depth: 0 # otherwise we will have no info about contributors - - name: Build - run: | git -C "$GITHUB_WORKSPACE" submodule sync --recursive git -C "$GITHUB_WORKSPACE" submodule update --depth=1 --recursive --init --jobs=10 - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" && python3 build_check.py "$CHECK_NAME" "$BUILD_NAME" - - name: Upload build URLs to artifacts - uses: actions/upload-artifact@v2 - with: - name: ${{ env.BUILD_URLS }} - path: ${{ runner.temp }}/build_check/${{ env.BUILD_URLS }}.json - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" "$CACHES_PATH" - BuilderDebAsan: - needs: [DockerHubPush] - runs-on: [self-hosted, builder] + ############################################################################################ + ##################################### BUILD REPORTER ####################################### + ############################################################################################ + BuilderReport: + needs: + - BuilderDebRelease + runs-on: [self-hosted, style-checker] steps: - name: Set envs run: | cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/build_check - IMAGES_PATH=${{runner.temp}}/images_path - REPO_COPY=${{runner.temp}}/build_check/ClickHouse - CACHES_PATH=${{runner.temp}}/../ccaches CHECK_NAME=ClickHouse build check (actions) - BUILD_NAME=package_asan + REPORTS_PATH=${{runner.temp}}/reports_dir + REPORTS_PATH=${{runner.temp}}/reports_dir + TEMP_PATH=${{runner.temp}}/report_check + NEEDS_DATA_PATH=${{runner.temp}}/needs.json EOF - - name: Download changed images + - name: Download json reports uses: actions/download-artifact@v2 with: - name: changed_images - path: ${{ env.IMAGES_PATH }} + path: ${{ env.REPORTS_PATH }} - name: Clear repository run: | sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - name: Check out repository code uses: actions/checkout@v2 - with: - fetch-depth: 0 # otherwise we will have no info about contributors - - name: Build + - name: Report Builder run: | - git -C "$GITHUB_WORKSPACE" submodule sync --recursive - git -C "$GITHUB_WORKSPACE" submodule update --depth=1 --recursive --init --jobs=10 sudo rm -fr "$TEMP_PATH" mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" && python3 build_check.py "$CHECK_NAME" "$BUILD_NAME" - - name: Upload build URLs to artifacts - if: ${{ success() || failure() }} - uses: actions/upload-artifact@v2 - with: - name: ${{ env.BUILD_URLS }} - path: ${{ env.TEMP_PATH }}/${{ env.BUILD_URLS }}.json - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" "$CACHES_PATH" - BuilderDebUBsan: - needs: [DockerHubPush] - runs-on: [self-hosted, builder] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/build_check - IMAGES_PATH=${{runner.temp}}/images_path - REPO_COPY=${{runner.temp}}/build_check/ClickHouse - CACHES_PATH=${{runner.temp}}/../ccaches - CHECK_NAME=ClickHouse build check (actions) - BUILD_NAME=package_ubsan + cat > "$NEEDS_DATA_PATH" << 'EOF' + ${{ toJSON(needs) }} EOF - - name: Download changed images - uses: actions/download-artifact@v2 - with: - name: changed_images - path: ${{ env.IMAGES_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - with: - fetch-depth: 0 # otherwise we will have no info about contributors - - name: Build - run: | - git -C "$GITHUB_WORKSPACE" submodule sync --recursive - git -C "$GITHUB_WORKSPACE" submodule update --depth=1 --recursive --init --jobs=10 - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" && python3 build_check.py "$CHECK_NAME" "$BUILD_NAME" - - name: Upload build URLs to artifacts - if: ${{ success() || failure() }} - uses: actions/upload-artifact@v2 - with: - name: ${{ env.BUILD_URLS }} - path: ${{ env.TEMP_PATH }}/${{ env.BUILD_URLS }}.json + cd "$GITHUB_WORKSPACE/tests/ci" + python3 build_report_check.py "$CHECK_NAME" - name: Cleanup if: always() run: | @@ -298,95 +212,39 @@ jobs: docker kill $(docker ps -q) ||: # shellcheck disable=SC2046 docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" "$CACHES_PATH" - BuilderDebTsan: - needs: [DockerHubPush] - runs-on: [self-hosted, builder] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/build_check - IMAGES_PATH=${{runner.temp}}/images_path - REPO_COPY=${{runner.temp}}/build_check/ClickHouse - CACHES_PATH=${{runner.temp}}/../ccaches - CHECK_NAME=ClickHouse build check (actions) - BUILD_NAME=package_tsan - EOF - - name: Download changed images - uses: actions/download-artifact@v2 - with: - name: changed_images - path: ${{ env.IMAGES_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - with: - fetch-depth: 0 # otherwise we will have no info about contributors - - name: Build - run: | - git -C "$GITHUB_WORKSPACE" submodule sync --recursive - git -C "$GITHUB_WORKSPACE" submodule update --depth=1 --recursive --init --jobs=10 sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" && python3 build_check.py "$CHECK_NAME" "$BUILD_NAME" - - name: Upload build URLs to artifacts - if: ${{ success() || failure() }} - uses: actions/upload-artifact@v2 - with: - name: ${{ env.BUILD_URLS }} - path: ${{ env.TEMP_PATH }}/${{ env.BUILD_URLS }}.json - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" "$CACHES_PATH" - BuilderDebMsan: - needs: [DockerHubPush] - runs-on: [self-hosted, builder] + ############################################################################################## + ########################### FUNCTIONAl STATELESS TESTS ####################################### + ############################################################################################## + FunctionalStatelessTestRelease: + needs: [BuilderDebRelease] + runs-on: [self-hosted, func-tester] steps: - name: Set envs run: | cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/build_check - IMAGES_PATH=${{runner.temp}}/images_path - REPO_COPY=${{runner.temp}}/build_check/ClickHouse - CACHES_PATH=${{runner.temp}}/../ccaches - CHECK_NAME=ClickHouse build check (actions) - BUILD_NAME=package_msan + TEMP_PATH=${{runner.temp}}/stateless_debug + REPORTS_PATH=${{runner.temp}}/reports_dir + CHECK_NAME=Stateless tests (release, actions) + REPO_COPY=${{runner.temp}}/stateless_debug/ClickHouse + KILL_TIMEOUT=10800 EOF - - name: Download changed images + - name: Download json reports uses: actions/download-artifact@v2 with: - name: changed_images - path: ${{ env.IMAGES_PATH }} + path: ${{ env.REPORTS_PATH }} - name: Clear repository run: | sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - name: Check out repository code uses: actions/checkout@v2 - with: - fetch-depth: 0 # otherwise we will have no info about contributors - - name: Build + - name: Functional test run: | - git -C "$GITHUB_WORKSPACE" submodule sync --recursive - git -C "$GITHUB_WORKSPACE" submodule update --depth=1 --recursive --init --jobs=10 sudo rm -fr "$TEMP_PATH" mkdir -p "$TEMP_PATH" cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" && python3 build_check.py "$CHECK_NAME" "$BUILD_NAME" - - name: Upload build URLs to artifacts - if: ${{ success() || failure() }} - uses: actions/upload-artifact@v2 - with: - name: ${{ env.BUILD_URLS }} - path: ${{ env.TEMP_PATH }}/${{ env.BUILD_URLS }}.json + cd "$REPO_COPY/tests/ci" + python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - name: Cleanup if: always() run: | @@ -394,284 +252,33 @@ jobs: docker kill $(docker ps -q) ||: # shellcheck disable=SC2046 docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" "$CACHES_PATH" - BuilderDebDebug: - needs: [DockerHubPush] - runs-on: [self-hosted, builder] + sudo rm -fr "$TEMP_PATH" + ############################################################################################## + ############################ FUNCTIONAl STATEFUL TESTS ####################################### + ############################################################################################## + FunctionalStatefulTestRelease: + needs: [BuilderDebRelease] + runs-on: [self-hosted, func-tester] steps: - name: Set envs run: | cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/build_check - IMAGES_PATH=${{runner.temp}}/images_path - REPO_COPY=${{runner.temp}}/build_check/ClickHouse - CACHES_PATH=${{runner.temp}}/../ccaches - CHECK_NAME=ClickHouse build check (actions) - BUILD_NAME=package_debug + TEMP_PATH=${{runner.temp}}/stateful_debug + REPORTS_PATH=${{runner.temp}}/reports_dir + CHECK_NAME=Stateful tests (release, actions) + REPO_COPY=${{runner.temp}}/stateful_debug/ClickHouse + KILL_TIMEOUT=3600 EOF - - name: Download changed images + - name: Download json reports uses: actions/download-artifact@v2 with: - name: changed_images - path: ${{ env.IMAGES_PATH }} + path: ${{ env.REPORTS_PATH }} - name: Clear repository run: | sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - name: Check out repository code uses: actions/checkout@v2 - with: - fetch-depth: 0 # otherwise we will have no info about contributors - - name: Build - run: | - git -C "$GITHUB_WORKSPACE" submodule sync --recursive - git -C "$GITHUB_WORKSPACE" submodule update --depth=1 --recursive --init --jobs=10 - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" && python3 build_check.py "$CHECK_NAME" "$BUILD_NAME" - - name: Upload build URLs to artifacts - if: ${{ success() || failure() }} - uses: actions/upload-artifact@v2 - with: - name: ${{ env.BUILD_URLS }} - path: ${{ env.TEMP_PATH }}/${{ env.BUILD_URLS }}.json - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" "$CACHES_PATH" -############################################################################################ -##################################### BUILD REPORTER ####################################### -############################################################################################ - BuilderReport: - needs: - - BuilderDebRelease - - BuilderDebAarch64 - - BuilderDebAsan - - BuilderDebTsan - - BuilderDebUBsan - - BuilderDebMsan - - BuilderDebDebug - runs-on: [self-hosted, style-checker] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - CHECK_NAME=ClickHouse build check (actions) - REPORTS_PATH=${{runner.temp}}/reports_dir - REPORTS_PATH=${{runner.temp}}/reports_dir - TEMP_PATH=${{runner.temp}}/report_check - NEEDS_DATA_PATH=${{runner.temp}}/needs.json - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Report Builder - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cat > "$NEEDS_DATA_PATH" << 'EOF' - ${{ toJSON(needs) }} - EOF - cd "$GITHUB_WORKSPACE/tests/ci" - python3 build_report_check.py "$CHECK_NAME" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" -############################################################################################## -########################### FUNCTIONAl STATELESS TESTS ####################################### -############################################################################################## - FunctionalStatelessTestRelease: - needs: [BuilderDebRelease] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateless_debug - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateless tests (release, actions) - REPO_COPY=${{runner.temp}}/stateless_debug/ClickHouse - KILL_TIMEOUT=10800 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatelessTestAarch64: - needs: [BuilderDebAarch64] - runs-on: [self-hosted, func-tester-aarch64] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateless_release - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateless tests (aarch64, actions) - REPO_COPY=${{runner.temp}}/stateless_release/ClickHouse - KILL_TIMEOUT=10800 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatelessTestAsan0: - needs: [BuilderDebAsan] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateless_debug - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateless tests (address, actions) - REPO_COPY=${{runner.temp}}/stateless_debug/ClickHouse - KILL_TIMEOUT=10800 - RUN_BY_HASH_NUM=0 - RUN_BY_HASH_TOTAL=2 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatelessTestAsan1: - needs: [BuilderDebAsan] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateless_debug - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateless tests (address, actions) - REPO_COPY=${{runner.temp}}/stateless_debug/ClickHouse - KILL_TIMEOUT=10800 - RUN_BY_HASH_NUM=1 - RUN_BY_HASH_TOTAL=2 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatelessTestTsan0: - needs: [BuilderDebTsan] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateless_tsan - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateless tests (thread, actions) - REPO_COPY=${{runner.temp}}/stateless_tsan/ClickHouse - KILL_TIMEOUT=10800 - RUN_BY_HASH_NUM=0 - RUN_BY_HASH_TOTAL=3 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test + - name: Functional test run: | sudo rm -fr "$TEMP_PATH" mkdir -p "$TEMP_PATH" @@ -686,1073 +293,9 @@ jobs: # shellcheck disable=SC2046 docker rm -f $(docker ps -a -q) ||: sudo rm -fr "$TEMP_PATH" - FunctionalStatelessTestTsan1: - needs: [BuilderDebTsan] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateless_tsan - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateless tests (thread, actions) - REPO_COPY=${{runner.temp}}/stateless_tsan/ClickHouse - KILL_TIMEOUT=10800 - RUN_BY_HASH_NUM=1 - RUN_BY_HASH_TOTAL=3 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatelessTestTsan2: - needs: [BuilderDebTsan] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateless_tsan - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateless tests (thread, actions) - REPO_COPY=${{runner.temp}}/stateless_tsan/ClickHouse - KILL_TIMEOUT=10800 - RUN_BY_HASH_NUM=2 - RUN_BY_HASH_TOTAL=3 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatelessTestUBsan: - needs: [BuilderDebUBsan] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateless_ubsan - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateless tests (ubsan, actions) - REPO_COPY=${{runner.temp}}/stateless_ubsan/ClickHouse - KILL_TIMEOUT=10800 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatelessTestMsan0: - needs: [BuilderDebMsan] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateless_memory - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateless tests (memory, actions) - REPO_COPY=${{runner.temp}}/stateless_memory/ClickHouse - KILL_TIMEOUT=10800 - RUN_BY_HASH_NUM=0 - RUN_BY_HASH_TOTAL=3 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatelessTestMsan1: - needs: [BuilderDebMsan] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateless_memory - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateless tests (memory, actions) - REPO_COPY=${{runner.temp}}/stateless_memory/ClickHouse - KILL_TIMEOUT=10800 - RUN_BY_HASH_NUM=1 - RUN_BY_HASH_TOTAL=3 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatelessTestMsan2: - needs: [BuilderDebMsan] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateless_memory - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateless tests (memory, actions) - REPO_COPY=${{runner.temp}}/stateless_memory/ClickHouse - KILL_TIMEOUT=10800 - RUN_BY_HASH_NUM=2 - RUN_BY_HASH_TOTAL=3 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatelessTestDebug0: - needs: [BuilderDebDebug] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateless_debug - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateless tests (debug, actions) - REPO_COPY=${{runner.temp}}/stateless_debug/ClickHouse - KILL_TIMEOUT=10800 - RUN_BY_HASH_NUM=0 - RUN_BY_HASH_TOTAL=3 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatelessTestDebug1: - needs: [BuilderDebDebug] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateless_debug - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateless tests (debug, actions) - REPO_COPY=${{runner.temp}}/stateless_debug/ClickHouse - KILL_TIMEOUT=10800 - RUN_BY_HASH_NUM=1 - RUN_BY_HASH_TOTAL=3 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatelessTestDebug2: - needs: [BuilderDebDebug] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateless_debug - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateless tests (debug, actions) - REPO_COPY=${{runner.temp}}/stateless_debug/ClickHouse - KILL_TIMEOUT=10800 - RUN_BY_HASH_NUM=2 - RUN_BY_HASH_TOTAL=3 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" -############################################################################################## -############################ FUNCTIONAl STATEFUL TESTS ####################################### -############################################################################################## - FunctionalStatefulTestRelease: - needs: [BuilderDebRelease] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateful_debug - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateful tests (release, actions) - REPO_COPY=${{runner.temp}}/stateful_debug/ClickHouse - KILL_TIMEOUT=3600 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatefulTestAarch64: - needs: [BuilderDebAarch64] - runs-on: [self-hosted, func-tester-aarch64] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateful_release - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateful tests (aarch64, actions) - REPO_COPY=${{runner.temp}}/stateful_release/ClickHouse - KILL_TIMEOUT=3600 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatefulTestAsan: - needs: [BuilderDebAsan] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateful_debug - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateful tests (address, actions) - REPO_COPY=${{runner.temp}}/stateful_debug/ClickHouse - KILL_TIMEOUT=3600 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatefulTestTsan: - needs: [BuilderDebTsan] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateful_tsan - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateful tests (thread, actions) - REPO_COPY=${{runner.temp}}/stateful_tsan/ClickHouse - KILL_TIMEOUT=3600 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatefulTestMsan: - needs: [BuilderDebMsan] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateful_msan - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateful tests (memory, actions) - REPO_COPY=${{runner.temp}}/stateful_msan/ClickHouse - KILL_TIMEOUT=3600 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatefulTestUBsan: - needs: [BuilderDebUBsan] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateful_ubsan - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateful tests (ubsan, actions) - REPO_COPY=${{runner.temp}}/stateful_ubsan/ClickHouse - KILL_TIMEOUT=3600 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatefulTestDebug: - needs: [BuilderDebDebug] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateful_debug - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateful tests (debug, actions) - REPO_COPY=${{runner.temp}}/stateful_debug/ClickHouse - KILL_TIMEOUT=3600 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" -############################################################################################## -######################################### STRESS TESTS ####################################### -############################################################################################## - StressTestAsan: - needs: [BuilderDebAsan] - runs-on: [self-hosted, stress-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stress_thread - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stress test (address, actions) - REPO_COPY=${{runner.temp}}/stress_thread/ClickHouse - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Stress test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 stress_check.py "$CHECK_NAME" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - StressTestTsan: - needs: [BuilderDebTsan] - # func testers have 16 cores + 128 GB memory - # while stress testers have 36 cores + 72 memory - # It would be better to have something like 32 + 128, - # but such servers almost unavailable as spot instances. - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stress_thread - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stress test (thread, actions) - REPO_COPY=${{runner.temp}}/stress_thread/ClickHouse - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Stress test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 stress_check.py "$CHECK_NAME" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - StressTestMsan: - needs: [BuilderDebMsan] - runs-on: [self-hosted, stress-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stress_memory - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stress test (memory, actions) - REPO_COPY=${{runner.temp}}/stress_memory/ClickHouse - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Stress test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 stress_check.py "$CHECK_NAME" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - StressTestUBsan: - needs: [BuilderDebUBsan] - runs-on: [self-hosted, stress-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stress_undefined - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stress test (undefined, actions) - REPO_COPY=${{runner.temp}}/stress_undefined/ClickHouse - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Stress test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 stress_check.py "$CHECK_NAME" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - StressTestDebug: - needs: [BuilderDebDebug] - runs-on: [self-hosted, stress-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stress_debug - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stress test (debug, actions) - REPO_COPY=${{runner.temp}}/stress_debug/ClickHouse - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Stress test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 stress_check.py "$CHECK_NAME" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" -############################################################################################# -############################# INTEGRATION TESTS ############################################# -############################################################################################# - IntegrationTestsAsan0: - needs: [BuilderDebAsan] - runs-on: [self-hosted, stress-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/integration_tests_asan - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Integration tests (asan, actions) - REPO_COPY=${{runner.temp}}/integration_tests_asan/ClickHouse - RUN_BY_HASH_NUM=0 - RUN_BY_HASH_TOTAL=3 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Integration test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 integration_test_check.py "$CHECK_NAME" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - IntegrationTestsAsan1: - needs: [BuilderDebAsan] - runs-on: [self-hosted, stress-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/integration_tests_asan - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Integration tests (asan, actions) - REPO_COPY=${{runner.temp}}/integration_tests_asan/ClickHouse - RUN_BY_HASH_NUM=1 - RUN_BY_HASH_TOTAL=3 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Integration test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 integration_test_check.py "$CHECK_NAME" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - IntegrationTestsAsan2: - needs: [BuilderDebAsan] - runs-on: [self-hosted, stress-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/integration_tests_asan - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Integration tests (asan, actions) - REPO_COPY=${{runner.temp}}/integration_tests_asan/ClickHouse - RUN_BY_HASH_NUM=2 - RUN_BY_HASH_TOTAL=3 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Integration test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 integration_test_check.py "$CHECK_NAME" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - IntegrationTestsTsan0: - needs: [BuilderDebTsan] - runs-on: [self-hosted, stress-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/integration_tests_tsan - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Integration tests (thread, actions) - REPO_COPY=${{runner.temp}}/integration_tests_tsan/ClickHouse - RUN_BY_HASH_NUM=0 - RUN_BY_HASH_TOTAL=4 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Integration test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 integration_test_check.py "$CHECK_NAME" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - IntegrationTestsTsan1: - needs: [BuilderDebTsan] - runs-on: [self-hosted, stress-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/integration_tests_tsan - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Integration tests (thread, actions) - REPO_COPY=${{runner.temp}}/integration_tests_tsan/ClickHouse - RUN_BY_HASH_NUM=1 - RUN_BY_HASH_TOTAL=4 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Integration test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 integration_test_check.py "$CHECK_NAME" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - IntegrationTestsTsan2: - needs: [BuilderDebTsan] - runs-on: [self-hosted, stress-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/integration_tests_tsan - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Integration tests (thread, actions) - REPO_COPY=${{runner.temp}}/integration_tests_tsan/ClickHouse - RUN_BY_HASH_NUM=2 - RUN_BY_HASH_TOTAL=4 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Integration test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 integration_test_check.py "$CHECK_NAME" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - IntegrationTestsTsan3: - needs: [BuilderDebTsan] - runs-on: [self-hosted, stress-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/integration_tests_tsan - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Integration tests (thread, actions) - REPO_COPY=${{runner.temp}}/integration_tests_tsan/ClickHouse - RUN_BY_HASH_NUM=3 - RUN_BY_HASH_TOTAL=4 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Integration test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 integration_test_check.py "$CHECK_NAME" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" + ############################################################################################# + ############################# INTEGRATION TESTS ############################################# + ############################################################################################# IntegrationTestsRelease0: needs: [BuilderDebRelease] runs-on: [self-hosted, stress-tester] @@ -1833,41 +376,10 @@ jobs: needs: - DockerHubPush - BuilderReport - - FunctionalStatelessTestDebug0 - - FunctionalStatelessTestDebug1 - - FunctionalStatelessTestDebug2 - FunctionalStatelessTestRelease - - FunctionalStatelessTestAarch64 - - FunctionalStatelessTestAsan0 - - FunctionalStatelessTestAsan1 - - FunctionalStatelessTestTsan0 - - FunctionalStatelessTestTsan1 - - FunctionalStatelessTestTsan2 - - FunctionalStatelessTestMsan0 - - FunctionalStatelessTestMsan1 - - FunctionalStatelessTestMsan2 - - FunctionalStatelessTestUBsan - - FunctionalStatefulTestDebug - FunctionalStatefulTestRelease - - FunctionalStatefulTestAarch64 - - FunctionalStatefulTestAsan - - FunctionalStatefulTestTsan - - FunctionalStatefulTestMsan - - FunctionalStatefulTestUBsan - - StressTestDebug - - StressTestAsan - - StressTestTsan - - StressTestMsan - - StressTestUBsan - - IntegrationTestsAsan0 - - IntegrationTestsAsan1 - - IntegrationTestsAsan2 - IntegrationTestsRelease0 - IntegrationTestsRelease1 - - IntegrationTestsTsan0 - - IntegrationTestsTsan1 - - IntegrationTestsTsan2 - - IntegrationTestsTsan3 - CompatibilityCheck runs-on: [self-hosted, style-checker] steps: diff --git a/cmake/autogenerated_versions.txt b/cmake/autogenerated_versions.txt index 656c26224146..46af0929d64c 100644 --- a/cmake/autogenerated_versions.txt +++ b/cmake/autogenerated_versions.txt @@ -2,11 +2,11 @@ # NOTE: has nothing common with DBMS_TCP_PROTOCOL_VERSION, # only DBMS_TCP_PROTOCOL_VERSION should be incremented on protocol changes. -SET(VERSION_REVISION 54460) +SET(VERSION_REVISION 6) SET(VERSION_MAJOR 22) SET(VERSION_MINOR 3) SET(VERSION_PATCH 8) SET(VERSION_GITHASH 420bdfa27510fe18e20f881d74ab66cddc44583d) -SET(VERSION_DESCRIBE v22.3.8.28-lts) -SET(VERSION_STRING 22.3.8.28) +SET(VERSION_DESCRIBE v22.3.8.29-altinitystable) +SET(VERSION_STRING 22.3.8.29.altinitystable) # end of autochange diff --git a/cmake/version.cmake b/cmake/version.cmake index acaa772ff2ff..d785da5fe9b1 100644 --- a/cmake/version.cmake +++ b/cmake/version.cmake @@ -19,5 +19,5 @@ set (VERSION_STRING_SHORT "${VERSION_MAJOR}.${VERSION_MINOR}") math (EXPR VERSION_INTEGER "${VERSION_PATCH} + ${VERSION_MINOR}*1000 + ${VERSION_MAJOR}*1000000") if(CLICKHOUSE_OFFICIAL_BUILD) - set(VERSION_OFFICIAL " (official build)") + set(VERSION_OFFICIAL " (altinity build)") endif() diff --git a/docker/images.json b/docker/images.json index 06d689e8f7cc..baf88e9e610e 100644 --- a/docker/images.json +++ b/docker/images.json @@ -1,164 +1,149 @@ { "docker/packager/deb": { - "name": "clickhouse/deb-builder", + "name": "altinityinfra/deb-builder", "dependent": [] }, "docker/packager/binary": { - "name": "clickhouse/binary-builder", + "name": "altinityinfra/binary-builder", "dependent": [ "docker/test/split_build_smoke_test", "docker/test/codebrowser" ] }, "docker/test/compatibility/centos": { - "name": "clickhouse/test-old-centos", + "name": "altinityinfra/test-old-centos", "dependent": [] }, "docker/test/compatibility/ubuntu": { - "name": "clickhouse/test-old-ubuntu", + "name": "altinityinfra/test-old-ubuntu", "dependent": [] }, "docker/test/integration/base": { - "name": "clickhouse/integration-test", + "name": "altinityinfra/integration-test", "dependent": [] }, "docker/test/fuzzer": { - "name": "clickhouse/fuzzer", + "name": "altinityinfra/fuzzer", "dependent": [] }, "docker/test/performance-comparison": { - "name": "clickhouse/performance-comparison", + "name": "altinityinfra/performance-comparison", "dependent": [] }, "docker/test/util": { - "name": "clickhouse/test-util", + "name": "altinityinfra/test-util", "dependent": [ "docker/test/base", "docker/test/fasttest" ] }, "docker/test/stateless": { - "name": "clickhouse/stateless-test", + "name": "altinityinfra/stateless-test", "dependent": [ "docker/test/stateful", "docker/test/unit" ] }, "docker/test/stateful": { - "name": "clickhouse/stateful-test", + "name": "altinityinfra/stateful-test", "dependent": [ "docker/test/stress" ] }, "docker/test/unit": { - "name": "clickhouse/unit-test", + "name": "altinityinfra/unit-test", "dependent": [] }, "docker/test/stress": { - "name": "clickhouse/stress-test", + "name": "altinityinfra/stress-test", "dependent": [] }, "docker/test/split_build_smoke_test": { - "name": "clickhouse/split-build-smoke-test", + "name": "altinityinfra/split-build-smoke-test", "dependent": [] }, "docker/test/codebrowser": { - "name": "clickhouse/codebrowser", + "name": "altinityinfra/codebrowser", "dependent": [] }, "docker/test/integration/runner": { "only_amd64": true, - "name": "clickhouse/integration-tests-runner", + "name": "altinityinfra/integration-tests-runner", "dependent": [] }, "docker/test/testflows/runner": { - "name": "clickhouse/testflows-runner", + "name": "altinityinfra/testflows-runner", "dependent": [] }, "docker/test/fasttest": { - "name": "clickhouse/fasttest", + "name": "altinityinfra/fasttest", "dependent": [] }, "docker/test/style": { - "name": "clickhouse/style-test", + "name": "altinityinfra/style-test", "dependent": [] }, "docker/test/integration/s3_proxy": { - "name": "clickhouse/s3-proxy", + "name": "altinityinfra/s3-proxy", "dependent": [] }, "docker/test/integration/resolver": { - "name": "clickhouse/python-bottle", + "name": "altinityinfra/python-bottle", "dependent": [] }, "docker/test/integration/helper_container": { - "name": "clickhouse/integration-helper", + "name": "altinityinfra/integration-helper", "dependent": [] }, "docker/test/integration/mysql_golang_client": { - "name": "clickhouse/mysql-golang-client", + "name": "altinityinfra/mysql-golang-client", "dependent": [] }, "docker/test/integration/dotnet_client": { - "name": "clickhouse/dotnet-client", + "name": "altinityinfra/dotnet-client", "dependent": [] }, "docker/test/integration/mysql_java_client": { - "name": "clickhouse/mysql-java-client", + "name": "altinityinfra/mysql-java-client", "dependent": [] }, "docker/test/integration/mysql_js_client": { - "name": "clickhouse/mysql-js-client", + "name": "altinityinfra/mysql-js-client", "dependent": [] }, "docker/test/integration/mysql_php_client": { - "name": "clickhouse/mysql-php-client", + "name": "altinityinfra/mysql-php-client", "dependent": [] }, "docker/test/integration/postgresql_java_client": { - "name": "clickhouse/postgresql-java-client", + "name": "altinityinfra/postgresql-java-client", "dependent": [] }, "docker/test/integration/kerberos_kdc": { "only_amd64": true, - "name": "clickhouse/kerberos-kdc", + "name": "altinityinfra/kerberos-kdc", "dependent": [] }, "docker/test/base": { - "name": "clickhouse/test-base", - "dependent": [ + "name": "altinityinfra/test-base", + "dependent": [ "docker/test/stateless", "docker/test/integration/base", "docker/test/fuzzer", "docker/test/keeper-jepsen" - ] + ] }, "docker/test/integration/kerberized_hadoop": { "only_amd64": true, - "name": "clickhouse/kerberized-hadoop", + "name": "altinityinfra/kerberized-hadoop", "dependent": [] }, "docker/test/sqlancer": { - "name": "clickhouse/sqlancer-test", + "name": "altinityinfra/sqlancer-test", "dependent": [] }, "docker/test/keeper-jepsen": { - "name": "clickhouse/keeper-jepsen-test", - "dependent": [] - }, - "docker/docs/builder": { - "name": "clickhouse/docs-builder", - "dependent": [ - "docker/docs/check", - "docker/docs/release" - ] - }, - "docker/docs/check": { - "name": "clickhouse/docs-check", - "dependent": [] - }, - "docker/docs/release": { - "name": "clickhouse/docs-release", + "name": "altinityinfra/keeper-jepsen-test", "dependent": [] } } diff --git a/docker/packager/binary/build.sh b/docker/packager/binary/build.sh index 943b92abdda1..7ed58b3d82a1 100755 --- a/docker/packager/binary/build.sh +++ b/docker/packager/binary/build.sh @@ -19,6 +19,9 @@ ln -sf darwin-x86_64 build/cmake/toolchain/darwin-aarch64 # export CCACHE_LOGFILE=/build/ccache.log # export CCACHE_DEBUG=1 +# https://stackoverflow.com/a/71940133 +git config --global --add safe.directory '*' + mkdir -p build/build_docker cd build/build_docker rm -f CMakeCache.txt diff --git a/docker/packager/packager b/docker/packager/packager index f82d402d613a..6235ee7ad10e 100755 --- a/docker/packager/packager +++ b/docker/packager/packager @@ -331,7 +331,7 @@ if __name__ == "__main__": args.output_dir = os.path.abspath(os.path.join(os.getcwd(), args.output_dir)) image_type = "binary" if args.package_type == "performance" else args.package_type - image_name = "clickhouse/binary-builder" + image_name = "altinityinfra/binary-builder" if not os.path.isabs(args.clickhouse_repo_path): ch_root = os.path.abspath(os.path.join(os.getcwd(), args.clickhouse_repo_path)) diff --git a/docker/test/base/Dockerfile b/docker/test/base/Dockerfile index 6beab2e5bb70..58fa01241e1f 100644 --- a/docker/test/base/Dockerfile +++ b/docker/test/base/Dockerfile @@ -1,7 +1,7 @@ # rebuild in #33610 -# docker build -t clickhouse/test-base . +# docker build -t altinityinfra/test-base . ARG FROM_TAG=latest -FROM clickhouse/test-util:$FROM_TAG +FROM altinityinfra/test-util:$FROM_TAG # ARG for quick switch to a given ubuntu mirror ARG apt_archive="http://archive.ubuntu.com" diff --git a/docker/test/codebrowser/Dockerfile b/docker/test/codebrowser/Dockerfile index 97f3f54ad987..86147635373f 100644 --- a/docker/test/codebrowser/Dockerfile +++ b/docker/test/codebrowser/Dockerfile @@ -2,7 +2,7 @@ # docker build --network=host -t clickhouse/codebrowser . # docker run --volume=path_to_repo:/repo_folder --volume=path_to_result:/test_output clickhouse/codebrowser ARG FROM_TAG=latest -FROM clickhouse/binary-builder:$FROM_TAG +FROM altinityinfra/binary-builder:$FROM_TAG # ARG for quick switch to a given ubuntu mirror ARG apt_archive="http://archive.ubuntu.com" diff --git a/docker/test/fasttest/Dockerfile b/docker/test/fasttest/Dockerfile index 46b74d89e13f..f61b0d11057f 100644 --- a/docker/test/fasttest/Dockerfile +++ b/docker/test/fasttest/Dockerfile @@ -1,7 +1,7 @@ # rebuild in #33610 # docker build -t clickhouse/fasttest . ARG FROM_TAG=latest -FROM clickhouse/test-util:$FROM_TAG +FROM altinityinfra/test-util:$FROM_TAG # ARG for quick switch to a given ubuntu mirror ARG apt_archive="http://archive.ubuntu.com" diff --git a/docker/test/fuzzer/Dockerfile b/docker/test/fuzzer/Dockerfile index eb4b09c173f6..2aec54bd1719 100644 --- a/docker/test/fuzzer/Dockerfile +++ b/docker/test/fuzzer/Dockerfile @@ -1,7 +1,7 @@ # rebuild in #33610 # docker build -t clickhouse/fuzzer . ARG FROM_TAG=latest -FROM clickhouse/test-base:$FROM_TAG +FROM altinityinfra/test-base:$FROM_TAG # ARG for quick switch to a given ubuntu mirror ARG apt_archive="http://archive.ubuntu.com" diff --git a/docker/test/integration/base/Dockerfile b/docker/test/integration/base/Dockerfile index eaf0f01e36d1..4d7740346ed0 100644 --- a/docker/test/integration/base/Dockerfile +++ b/docker/test/integration/base/Dockerfile @@ -1,7 +1,7 @@ # rebuild in #33610 # docker build -t clickhouse/integration-test . ARG FROM_TAG=latest -FROM clickhouse/test-base:$FROM_TAG +FROM altinityinfra/test-base:$FROM_TAG SHELL ["/bin/bash", "-c"] diff --git a/docker/test/integration/mysql_php_client/Dockerfile b/docker/test/integration/mysql_php_client/Dockerfile index 0fb77bf8ffb7..55db4d15a7f3 100644 --- a/docker/test/integration/mysql_php_client/Dockerfile +++ b/docker/test/integration/mysql_php_client/Dockerfile @@ -1,7 +1,7 @@ # docker build -t clickhouse/mysql-php-client . # MySQL PHP client docker container -FROM php:7.3-cli +FROM php:8.0.18-cli COPY ./client.crt client.crt COPY ./client.key client.key diff --git a/docker/test/integration/runner/compose/docker_compose_dotnet_client.yml b/docker/test/integration/runner/compose/docker_compose_dotnet_client.yml index b63dac51522c..e5746fa209fb 100644 --- a/docker/test/integration/runner/compose/docker_compose_dotnet_client.yml +++ b/docker/test/integration/runner/compose/docker_compose_dotnet_client.yml @@ -1,6 +1,6 @@ version: '2.3' services: dotnet1: - image: clickhouse/dotnet-client:${DOCKER_DOTNET_CLIENT_TAG:-latest} + image: altinityinfra/dotnet-client:${DOCKER_DOTNET_CLIENT_TAG:-latest} # to keep container running command: sleep infinity diff --git a/docker/test/integration/runner/compose/docker_compose_keeper.yml b/docker/test/integration/runner/compose/docker_compose_keeper.yml index 134ffbff1f74..375003d5e14f 100644 --- a/docker/test/integration/runner/compose/docker_compose_keeper.yml +++ b/docker/test/integration/runner/compose/docker_compose_keeper.yml @@ -1,7 +1,7 @@ version: '2.3' services: zoo1: - image: ${image:-clickhouse/integration-test} + image: ${image:-altinityinfra/integration-test} restart: always user: ${user:-} volumes: @@ -31,7 +31,7 @@ services: - inet6 - rotate zoo2: - image: ${image:-clickhouse/integration-test} + image: ${image:-altinityinfra/integration-test} restart: always user: ${user:-} volumes: @@ -61,7 +61,7 @@ services: - inet6 - rotate zoo3: - image: ${image:-clickhouse/integration-test} + image: ${image:-altinityinfra/integration-test} restart: always user: ${user:-} volumes: diff --git a/docker/test/integration/runner/compose/docker_compose_kerberized_hdfs.yml b/docker/test/integration/runner/compose/docker_compose_kerberized_hdfs.yml index e1b4d393169a..365821b3f5ea 100644 --- a/docker/test/integration/runner/compose/docker_compose_kerberized_hdfs.yml +++ b/docker/test/integration/runner/compose/docker_compose_kerberized_hdfs.yml @@ -4,7 +4,7 @@ services: kerberizedhdfs1: cap_add: - DAC_READ_SEARCH - image: clickhouse/kerberized-hadoop:${DOCKER_KERBERIZED_HADOOP_TAG:-latest} + image: altinityinfra/kerberized-hadoop:${DOCKER_KERBERIZED_HADOOP_TAG:-latest} hostname: kerberizedhdfs1 restart: always volumes: @@ -22,7 +22,7 @@ services: entrypoint: /etc/bootstrap.sh -d hdfskerberos: - image: clickhouse/kerberos-kdc:${DOCKER_KERBEROS_KDC_TAG:-latest} + image: altinityinfra/kerberos-kdc:${DOCKER_KERBEROS_KDC_TAG:-latest} hostname: hdfskerberos volumes: - ${KERBERIZED_HDFS_DIR}/secrets:/tmp/keytab diff --git a/docker/test/integration/runner/compose/docker_compose_kerberized_kafka.yml b/docker/test/integration/runner/compose/docker_compose_kerberized_kafka.yml index d57e4e4d5bea..8dbdd9c74c0c 100644 --- a/docker/test/integration/runner/compose/docker_compose_kerberized_kafka.yml +++ b/docker/test/integration/runner/compose/docker_compose_kerberized_kafka.yml @@ -50,7 +50,7 @@ services: - label:disable kafka_kerberos: - image: clickhouse/kerberos-kdc:${DOCKER_KERBEROS_KDC_TAG:-latest} + image: altinityinfra/kerberos-kdc:${DOCKER_KERBEROS_KDC_TAG:-latest} hostname: kafka_kerberos volumes: - ${KERBERIZED_KAFKA_DIR}/secrets:/tmp/keytab diff --git a/docker/test/integration/runner/compose/docker_compose_minio.yml b/docker/test/integration/runner/compose/docker_compose_minio.yml index 6e8c826b2346..438f3486e177 100644 --- a/docker/test/integration/runner/compose/docker_compose_minio.yml +++ b/docker/test/integration/runner/compose/docker_compose_minio.yml @@ -21,14 +21,14 @@ services: # HTTP proxies for Minio. proxy1: - image: clickhouse/s3-proxy + image: altinityinfra/s3-proxy expose: - "8080" # Redirect proxy port - "80" # Reverse proxy port - "443" # Reverse proxy port (secure) proxy2: - image: clickhouse/s3-proxy + image: altinityinfra/s3-proxy expose: - "8080" - "80" @@ -36,7 +36,7 @@ services: # Empty container to run proxy resolver. resolver: - image: clickhouse/python-bottle + image: altinityinfra/python-bottle expose: - "8080" tty: true diff --git a/docker/test/integration/runner/compose/docker_compose_mysql_golang_client.yml b/docker/test/integration/runner/compose/docker_compose_mysql_golang_client.yml index 56cc04105740..09154b584244 100644 --- a/docker/test/integration/runner/compose/docker_compose_mysql_golang_client.yml +++ b/docker/test/integration/runner/compose/docker_compose_mysql_golang_client.yml @@ -1,6 +1,6 @@ version: '2.3' services: golang1: - image: clickhouse/mysql-golang-client:${DOCKER_MYSQL_GOLANG_CLIENT_TAG:-latest} + image: altinityinfra/mysql-golang-client:${DOCKER_MYSQL_GOLANG_CLIENT_TAG:-latest} # to keep container running command: sleep infinity diff --git a/docker/test/integration/runner/compose/docker_compose_mysql_java_client.yml b/docker/test/integration/runner/compose/docker_compose_mysql_java_client.yml index eb5ffb01baa2..a84cef915df2 100644 --- a/docker/test/integration/runner/compose/docker_compose_mysql_java_client.yml +++ b/docker/test/integration/runner/compose/docker_compose_mysql_java_client.yml @@ -1,6 +1,6 @@ version: '2.3' services: java1: - image: clickhouse/mysql-java-client:${DOCKER_MYSQL_JAVA_CLIENT_TAG:-latest} + image: altinityinfra/mysql-java-client:${DOCKER_MYSQL_JAVA_CLIENT_TAG:-latest} # to keep container running command: sleep infinity diff --git a/docker/test/integration/runner/compose/docker_compose_mysql_js_client.yml b/docker/test/integration/runner/compose/docker_compose_mysql_js_client.yml index 90939449c5f3..b46eb2706c47 100644 --- a/docker/test/integration/runner/compose/docker_compose_mysql_js_client.yml +++ b/docker/test/integration/runner/compose/docker_compose_mysql_js_client.yml @@ -1,6 +1,6 @@ version: '2.3' services: mysqljs1: - image: clickhouse/mysql-js-client:${DOCKER_MYSQL_JS_CLIENT_TAG:-latest} + image: altinityinfra/mysql-js-client:${DOCKER_MYSQL_JS_CLIENT_TAG:-latest} # to keep container running command: sleep infinity diff --git a/docker/test/integration/runner/compose/docker_compose_mysql_php_client.yml b/docker/test/integration/runner/compose/docker_compose_mysql_php_client.yml index 408b8ff089a9..662783a00a1f 100644 --- a/docker/test/integration/runner/compose/docker_compose_mysql_php_client.yml +++ b/docker/test/integration/runner/compose/docker_compose_mysql_php_client.yml @@ -1,6 +1,6 @@ version: '2.3' services: php1: - image: clickhouse/mysql-php-client:${DOCKER_MYSQL_PHP_CLIENT_TAG:-latest} + image: altinityinfra/mysql-php-client:${DOCKER_MYSQL_PHP_CLIENT_TAG:-latest} # to keep container running command: sleep infinity diff --git a/docker/test/integration/runner/compose/docker_compose_postgresql_java_client.yml b/docker/test/integration/runner/compose/docker_compose_postgresql_java_client.yml index 904bfffdfd5b..5c8673ae3eeb 100644 --- a/docker/test/integration/runner/compose/docker_compose_postgresql_java_client.yml +++ b/docker/test/integration/runner/compose/docker_compose_postgresql_java_client.yml @@ -1,6 +1,6 @@ version: '2.2' services: java: - image: clickhouse/postgresql-java-client:${DOCKER_POSTGRESQL_JAVA_CLIENT_TAG:-latest} + image: altinityinfra/postgresql-java-client:${DOCKER_POSTGRESQL_JAVA_CLIENT_TAG:-latest} # to keep container running command: sleep infinity diff --git a/docker/test/keeper-jepsen/Dockerfile b/docker/test/keeper-jepsen/Dockerfile index a794e076ec02..b93b07189012 100644 --- a/docker/test/keeper-jepsen/Dockerfile +++ b/docker/test/keeper-jepsen/Dockerfile @@ -1,7 +1,7 @@ # rebuild in #33610 # docker build -t clickhouse/keeper-jepsen-test . ARG FROM_TAG=latest -FROM clickhouse/test-base:$FROM_TAG +FROM altinityinfra/test-base:$FROM_TAG ENV DEBIAN_FRONTEND=noninteractive ENV CLOJURE_VERSION=1.10.3.814 diff --git a/docker/test/split_build_smoke_test/Dockerfile b/docker/test/split_build_smoke_test/Dockerfile index 5f84eb42216c..cb41859fb118 100644 --- a/docker/test/split_build_smoke_test/Dockerfile +++ b/docker/test/split_build_smoke_test/Dockerfile @@ -1,7 +1,7 @@ # rebuild in #33610 -# docker build -t clickhouse/split-build-smoke-test . +# docker build -t altinityinfra/split-build-smoke-test . ARG FROM_TAG=latest -FROM clickhouse/binary-builder:$FROM_TAG +FROM altinityinfra/binary-builder:$FROM_TAG COPY run.sh /run.sh COPY process_split_build_smoke_test_result.py / diff --git a/docker/test/stateful/Dockerfile b/docker/test/stateful/Dockerfile index 543cf113b2b2..a68168b1271f 100644 --- a/docker/test/stateful/Dockerfile +++ b/docker/test/stateful/Dockerfile @@ -1,13 +1,16 @@ # rebuild in #33610 # docker build -t clickhouse/stateful-test . ARG FROM_TAG=latest -FROM clickhouse/stateless-test:$FROM_TAG +# TODO consider replacing clickhouse with altinityinfra dockerhub account +FROM altinityinfra/stateless-test:$FROM_TAG RUN apt-get update -y \ && env DEBIAN_FRONTEND=noninteractive \ apt-get install --yes --no-install-recommends \ python3-requests \ - llvm-9 + llvm-9 \ + rpm2cpio \ + cpio COPY s3downloader /s3downloader @@ -17,8 +20,7 @@ ENV EXPORT_S3_STORAGE_POLICIES=1 # Download Minio-related binaries RUN arch=${TARGETARCH:-amd64} \ - && wget "https://dl.min.io/server/minio/release/linux-${arch}/minio" \ - && chmod +x ./minio \ + && wget "https://dl.min.io/server/minio/release/linux-${arch}/archive/minio-20220103182258.0.0.x86_64.rpm" \ && wget "https://dl.min.io/client/mc/release/linux-${arch}/mc" \ && chmod +x ./mc ENV MINIO_ROOT_USER="clickhouse" diff --git a/docker/test/stateful/setup_minio.sh b/docker/test/stateful/setup_minio.sh index 5758d905197b..d077dea920c6 100755 --- a/docker/test/stateful/setup_minio.sh +++ b/docker/test/stateful/setup_minio.sh @@ -9,6 +9,10 @@ set -e -x -a -u +rpm2cpio ./minio-20220103182258.0.0.x86_64.rpm | cpio -i --make-directories +find -name minio +cp ./usr/local/bin/minio ./ + ls -lha mkdir -p ./minio_data @@ -27,12 +31,19 @@ fi MINIO_ROOT_USER=${MINIO_ROOT_USER:-clickhouse} MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD:-clickhouse} +./minio --version ./minio server --address ":11111" ./minio_data & +i=0 while ! curl -v --silent http://localhost:11111 2>&1 | grep AccessDenied do + if [[ $i == 60 ]]; then + echo "Failed to setup minio" + exit 0 + fi echo "Trying to connect to minio" sleep 1 + i=$((i + 1)) done lsof -i :11111 diff --git a/docker/test/stateless/Dockerfile b/docker/test/stateless/Dockerfile index 68c08c23b3ff..975a1da8d36e 100644 --- a/docker/test/stateless/Dockerfile +++ b/docker/test/stateless/Dockerfile @@ -1,7 +1,7 @@ # rebuild in #33610 # docker build -t clickhouse/stateless-test . ARG FROM_TAG=latest -FROM clickhouse/test-base:$FROM_TAG +FROM altinityinfra/test-base:$FROM_TAG ARG odbc_driver_url="https://github.com/ClickHouse/clickhouse-odbc/releases/download/v1.1.4.20200302/clickhouse-odbc-1.1.4-Linux.tar.gz" @@ -32,7 +32,9 @@ RUN apt-get update -y \ mysql-client=8.0* \ postgresql-client \ sqlite3 \ - awscli + awscli \ + rpm2cpio \ + cpio RUN pip3 install numpy scipy pandas Jinja2 @@ -53,8 +55,7 @@ ARG TARGETARCH # Download Minio-related binaries RUN arch=${TARGETARCH:-amd64} \ - && wget "https://dl.min.io/server/minio/release/linux-${arch}/minio" \ - && chmod +x ./minio \ + && wget "https://dl.min.io/server/minio/release/linux-${arch}/archive/minio-20220103182258.0.0.x86_64.rpm" \ && wget "https://dl.min.io/client/mc/release/linux-${arch}/mc" \ && chmod +x ./mc diff --git a/docker/test/stateless/setup_minio.sh b/docker/test/stateless/setup_minio.sh index df27b21b05b7..031a54639e92 100755 --- a/docker/test/stateless/setup_minio.sh +++ b/docker/test/stateless/setup_minio.sh @@ -16,21 +16,32 @@ if [ ! -f ./minio ]; then BINARY_TYPE=$(uname -s | tr '[:upper:]' '[:lower:]') - wget "https://dl.min.io/server/minio/release/${BINARY_TYPE}-amd64/minio" \ - && chmod +x ./minio \ + wget "https://dl.min.io/server/minio/release/${BINARY_TYPE}-amd64/archive/minio-20220103182258.0.0.x86_64.rpm" \ && wget "https://dl.min.io/client/mc/release/${BINARY_TYPE}-amd64/mc" \ && chmod +x ./mc fi +rpm2cpio ./minio-20220103182258.0.0.x86_64.rpm | cpio -i --make-directories +find -name minio +cp ./usr/local/bin/minio ./ + MINIO_ROOT_USER=${MINIO_ROOT_USER:-clickhouse} MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD:-clickhouse} +./minio --version + ./minio server --address ":11111" ./minio_data & +i=0 while ! curl -v --silent http://localhost:11111 2>&1 | grep AccessDenied do + if [[ $i == 60 ]]; then + echo "Failed to setup minio" + exit 0 + fi echo "Trying to connect to minio" sleep 1 + i=$((i + 1)) done lsof -i :11111 diff --git a/docker/test/stateless_pytest/Dockerfile b/docker/test/stateless_pytest/Dockerfile index 789ee0e9b308..c148b6212417 100644 --- a/docker/test/stateless_pytest/Dockerfile +++ b/docker/test/stateless_pytest/Dockerfile @@ -1,7 +1,7 @@ # rebuild in #33610 # docker build -t clickhouse/stateless-pytest . ARG FROM_TAG=latest -FROM clickhouse/test-base:$FROM_TAG +FROM altinityinfra/test-base:$FROM_TAG RUN apt-get update -y && \ apt-get install -y --no-install-recommends \ diff --git a/docker/test/stress/Dockerfile b/docker/test/stress/Dockerfile index 393508fd551b..4f6834fff737 100644 --- a/docker/test/stress/Dockerfile +++ b/docker/test/stress/Dockerfile @@ -1,7 +1,7 @@ # rebuild in #33610 # docker build -t clickhouse/stress-test . ARG FROM_TAG=latest -FROM clickhouse/stateful-test:$FROM_TAG +FROM altinityinfra/stateful-test:$FROM_TAG RUN apt-get update -y \ && env DEBIAN_FRONTEND=noninteractive \ diff --git a/docker/test/unit/Dockerfile b/docker/test/unit/Dockerfile index b75bfb6661cc..378341ab8b69 100644 --- a/docker/test/unit/Dockerfile +++ b/docker/test/unit/Dockerfile @@ -1,7 +1,7 @@ # rebuild in #33610 # docker build -t clickhouse/unit-test . ARG FROM_TAG=latest -FROM clickhouse/stateless-test:$FROM_TAG +FROM altinityinfra/stateless-test:$FROM_TAG RUN apt-get install gdb diff --git a/packages/clickhouse-client.yaml b/packages/clickhouse-client.yaml index 6d1233e7c7a5..efb509e622e6 100644 --- a/packages/clickhouse-client.yaml +++ b/packages/clickhouse-client.yaml @@ -4,8 +4,8 @@ name: "clickhouse-client" arch: "${DEB_ARCH}" # amd64, arm64 platform: "linux" version: "${CLICKHOUSE_VERSION_STRING}" -vendor: "ClickHouse Inc." -homepage: "https://clickhouse.com" +vendor: "Altinity Inc." +homepage: "https://altinity.com/" license: "Apache" section: "database" priority: "optional" diff --git a/packages/clickhouse-common-static-dbg.yaml b/packages/clickhouse-common-static-dbg.yaml index 12a1594bd301..78f74ed15cb8 100644 --- a/packages/clickhouse-common-static-dbg.yaml +++ b/packages/clickhouse-common-static-dbg.yaml @@ -4,8 +4,8 @@ name: "clickhouse-common-static-dbg" arch: "${DEB_ARCH}" # amd64, arm64 platform: "linux" version: "${CLICKHOUSE_VERSION_STRING}" -vendor: "ClickHouse Inc." -homepage: "https://clickhouse.com" +vendor: "Altinity Inc." +homepage: "https://altinity.com/" license: "Apache" section: "database" priority: "optional" diff --git a/packages/clickhouse-common-static.yaml b/packages/clickhouse-common-static.yaml index 269d4318e5e0..07dee0d326bb 100644 --- a/packages/clickhouse-common-static.yaml +++ b/packages/clickhouse-common-static.yaml @@ -4,8 +4,8 @@ name: "clickhouse-common-static" arch: "${DEB_ARCH}" # amd64, arm64 platform: "linux" version: "${CLICKHOUSE_VERSION_STRING}" -vendor: "ClickHouse Inc." -homepage: "https://clickhouse.com" +vendor: "Altinity Inc." +homepage: "https://altinity.com/" license: "Apache" section: "database" priority: "optional" diff --git a/packages/clickhouse-keeper-dbg.yaml b/packages/clickhouse-keeper-dbg.yaml index 2c70b7ad4aa4..55bf5da74f20 100644 --- a/packages/clickhouse-keeper-dbg.yaml +++ b/packages/clickhouse-keeper-dbg.yaml @@ -4,8 +4,8 @@ name: "clickhouse-keeper-dbg" arch: "${DEB_ARCH}" # amd64, arm64 platform: "linux" version: "${CLICKHOUSE_VERSION_STRING}" -vendor: "ClickHouse Inc." -homepage: "https://clickhouse.com" +vendor: "Altinity Inc." +homepage: "https://altinity.com/" license: "Apache" section: "database" priority: "optional" diff --git a/packages/clickhouse-keeper.yaml b/packages/clickhouse-keeper.yaml index e99ac30f9443..c8c0b1ad2b33 100644 --- a/packages/clickhouse-keeper.yaml +++ b/packages/clickhouse-keeper.yaml @@ -4,8 +4,8 @@ name: "clickhouse-keeper" arch: "${DEB_ARCH}" # amd64, arm64 platform: "linux" version: "${CLICKHOUSE_VERSION_STRING}" -vendor: "ClickHouse Inc." -homepage: "https://clickhouse.com" +vendor: "Altinity Inc." +homepage: "https://altinity.com/" license: "Apache" section: "database" priority: "optional" diff --git a/packages/clickhouse-server.yaml b/packages/clickhouse-server.yaml index 289956897549..e437deba8790 100644 --- a/packages/clickhouse-server.yaml +++ b/packages/clickhouse-server.yaml @@ -4,8 +4,8 @@ name: "clickhouse-server" arch: "${DEB_ARCH}" # amd64, arm64 platform: "linux" version: "${CLICKHOUSE_VERSION_STRING}" -vendor: "ClickHouse Inc." -homepage: "https://clickhouse.com" +vendor: "Altinity Inc." +homepage: "https://altinity.com/" license: "Apache" section: "database" priority: "optional" diff --git a/tests/ci/ast_fuzzer_check.py b/tests/ci/ast_fuzzer_check.py index 94f5eff51d7e..d3c871207896 100644 --- a/tests/ci/ast_fuzzer_check.py +++ b/tests/ci/ast_fuzzer_check.py @@ -24,7 +24,7 @@ from stopwatch import Stopwatch from rerun_helper import RerunHelper -IMAGE_NAME = "clickhouse/fuzzer" +IMAGE_NAME = "altinityinfra/fuzzer" def get_run_command(pr_number, sha, download_url, workspace_path, image): diff --git a/tests/ci/build_check.py b/tests/ci/build_check.py index 4c77f77a7021..cb8d03a83553 100644 --- a/tests/ci/build_check.py +++ b/tests/ci/build_check.py @@ -8,7 +8,7 @@ import time from typing import List, Optional, Tuple -from env_helper import REPO_COPY, TEMP_PATH, CACHES_PATH, IMAGES_PATH, GITHUB_JOB +from env_helper import REPO_COPY, TEMP_PATH, CACHES_PATH, IMAGES_PATH, S3_BUILDS_BUCKET, GITHUB_JOB, CLICKHOUSE_STABLE_VERSION_SUFFIX from s3_helper import S3Helper from pr_info import PRInfo from version_helper import ( @@ -21,7 +21,7 @@ from docker_pull_helper import get_image_with_version from tee_popen import TeePopen -IMAGE_NAME = "clickhouse/binary-builder" +IMAGE_NAME = "altinityinfra/binary-builder" def get_build_config(build_check_name: str, build_name: str) -> BuildConfig: @@ -230,12 +230,12 @@ def main(): log_url = "" for url in build_results: if "build_log.log" in url: - log_url = "https://s3.amazonaws.com/clickhouse-builds/" + url.replace( + log_url = f"https://s3.amazonaws.com/{S3_BUILDS_BUCKET}/" + url.replace( "+", "%2B" ).replace(" ", "%20") else: build_urls.append( - "https://s3.amazonaws.com/clickhouse-builds/" + f"https://s3.amazonaws.com/{S3_BUILDS_BUCKET}/" + url.replace("+", "%2B").replace(" ", "%20") ) success = len(build_urls) > 0 @@ -259,15 +259,19 @@ def main(): logging.info("Got version from repo %s", version.string) - official_flag = pr_info.number == 0 - version_type = "testing" - if "release" in pr_info.labels or "release-lts" in pr_info.labels: - version_type = "stable" - official_flag = True + official_flag = True + version._flavour = version_type = CLICKHOUSE_STABLE_VERSION_SUFFIX + # TODO (vnemkov): right now we'll use simplified version management: + # only update git hash and explicitly set stable version suffix. + # official_flag = pr_info.number == 0 + # version_type = "testing" + # if "release" in pr_info.labels or "release-lts" in pr_info.labels: + # version_type = CLICKHOUSE_STABLE_VERSION_SUFFIX + # official_flag = True update_version_local(version, version_type) - logging.info("Updated local files with version") + logging.info(f"Updated local files with version : {version.string} / {version.describe}") logging.info("Build short name %s", build_name) diff --git a/tests/ci/build_report_check.py b/tests/ci/build_report_check.py index dbf5adfe1747..a25307ffc446 100644 --- a/tests/ci/build_report_check.py +++ b/tests/ci/build_report_check.py @@ -286,6 +286,8 @@ def main(): if some_builds_are_missing: addition = f"({len(build_reports)} of {required_builds} builds are OK)" + description = f"{ok_builds}/{total_builds} builds are OK {addition}" + description = f"{ok_groups}/{total_groups} artifact groups are OK {addition}" commit = get_commit(gh, pr_info.sha) diff --git a/tests/ci/ccache_utils.py b/tests/ci/ccache_utils.py index 7b0b0f01aa3b..734818f6ec6e 100644 --- a/tests/ci/ccache_utils.py +++ b/tests/ci/ccache_utils.py @@ -5,6 +5,7 @@ import sys import os import shutil +from env_helper import S3_BUILDS_BUCKET from pathlib import Path import requests @@ -71,7 +72,7 @@ def get_ccache_if_not_exists( for obj in objects: if ccache_name in obj: logging.info("Found ccache on path %s", obj) - url = "https://s3.amazonaws.com/clickhouse-builds/" + obj + url = f"https://s3.amazonaws.com/{S3_BUILDS_BUCKET}/" + obj compressed_cache = os.path.join(temp_path, os.path.basename(obj)) dowload_file_with_progress(url, compressed_cache) diff --git a/tests/ci/ci_config.py b/tests/ci/ci_config.py index 74dbe65911c8..08a608570a4a 100644 --- a/tests/ci/ci_config.py +++ b/tests/ci/ci_config.py @@ -188,15 +188,7 @@ }, "builds_report_config": { "ClickHouse build check (actions)": [ - "package_release", - "performance", - "package_aarch64", - "package_asan", - "package_ubsan", - "package_tsan", - "package_msan", - "package_debug", - "binary_release", + "package_release" ], "ClickHouse special build check (actions)": [ "binary_tidy", diff --git a/tests/ci/clickhouse_helper.py b/tests/ci/clickhouse_helper.py index 7ccbcb4a47e1..bfe2ed0e72b6 100644 --- a/tests/ci/clickhouse_helper.py +++ b/tests/ci/clickhouse_helper.py @@ -15,7 +15,9 @@ def __init__(self, url=None): "X-ClickHouse-User": get_parameter_from_ssm( "clickhouse-test-stat-login2" ), - "X-ClickHouse-Key": "", + "X-ClickHouse-Key": get_parameter_from_ssm( + "clickhouse-test-stat-password" + ), } @staticmethod @@ -117,7 +119,7 @@ def prepare_tests_results_for_clickhouse( check_name, ): - pull_request_url = "https://github.com/ClickHouse/ClickHouse/commits/master" + pull_request_url = "https://github.com/Altinity/ClickHouse/commits/master" base_ref = "master" head_ref = "master" base_repo = pr_info.repo_full_name diff --git a/tests/ci/codebrowser_check.py b/tests/ci/codebrowser_check.py index 48c92e9f6acc..3a245005cb40 100644 --- a/tests/ci/codebrowser_check.py +++ b/tests/ci/codebrowser_check.py @@ -40,7 +40,7 @@ def get_run_command(repo_path, output_path, image): if not os.path.exists(temp_path): os.makedirs(temp_path) - docker_image = get_image_with_version(IMAGES_PATH, "clickhouse/codebrowser") + docker_image = get_image_with_version(IMAGES_PATH, "altinityinfra/codebrowser") s3_helper = S3Helper("https://s3.amazonaws.com") result_path = os.path.join(temp_path, "result_path") diff --git a/tests/ci/compatibility_check.py b/tests/ci/compatibility_check.py index d546fabf2316..2351ef0c60a1 100644 --- a/tests/ci/compatibility_check.py +++ b/tests/ci/compatibility_check.py @@ -24,8 +24,8 @@ from stopwatch import Stopwatch from rerun_helper import RerunHelper -IMAGE_UBUNTU = "clickhouse/test-old-ubuntu" -IMAGE_CENTOS = "clickhouse/test-old-centos" +IMAGE_UBUNTU = "altinityinfra/test-old-ubuntu" +IMAGE_CENTOS = "altinityinfra/test-old-centos" MAX_GLIBC_VERSION = "2.4" DOWNLOAD_RETRIES_COUNT = 5 CHECK_NAME = "Compatibility check (actions)" @@ -197,4 +197,8 @@ def url_filter(url): report_url, CHECK_NAME, ) + ch_helper.insert_events_into(db="gh-data", table="checks", events=prepared_events) + + if state == "error": + sys.exit(1) diff --git a/tests/ci/docker_images_check.py b/tests/ci/docker_images_check.py index 8185229dfd69..9d5173648e35 100644 --- a/tests/ci/docker_images_check.py +++ b/tests/ci/docker_images_check.py @@ -106,22 +106,23 @@ def get_changed_docker_images( str(files_changed), ) - changed_images = [] - - for dockerfile_dir, image_description in images_dict.items(): - for f in files_changed: - if f.startswith(dockerfile_dir): - name = image_description["name"] - only_amd64 = image_description.get("only_amd64", False) - logging.info( - "Found changed file '%s' which affects " - "docker image '%s' with path '%s'", - f, - name, - dockerfile_dir, - ) - changed_images.append(DockerImage(dockerfile_dir, name, only_amd64)) - break + # Rebuild all images + changed_images = [DockerImage(dockerfile_dir, image_description["name"], image_description.get("only_amd64", False)) for dockerfile_dir, image_description in images_dict.items()] + + # for dockerfile_dir, image_description in images_dict.items(): + # for f in files_changed: + # if f.startswith(dockerfile_dir): + # name = image_description["name"] + # only_amd64 = image_description.get("only_amd64", False) + # logging.info( + # "Found changed file '%s' which affects " + # "docker image '%s' with path '%s'", + # f, + # name, + # dockerfile_dir, + # ) + # changed_images.append(DockerImage(dockerfile_dir, name, only_amd64)) + # break # The order is important: dependents should go later than bases, so that # they are built with updated base versions. @@ -253,6 +254,19 @@ def build_and_push_one_image( f"--tag {image.repo}:{version_string} " f"{cache_from} " f"--cache-to type=inline,mode=max " + # FIXME: many tests utilize packages without specifying version, hence docker pulls :latest + # this will fail multiple jobs are going to be executed on different machines and + # push different images as latest. + # To fix it we may: + # - require jobs to be executed on same machine images were built (no parallelism) + # - change all the test's code (mostly docker-compose files in integration tests) + # that depend on said images and push version somehow into docker-compose. + # (and that is lots of work and many potential conflicts with upstream) + # - tag and push all images as :latest and then just pray that collisions are infrequent. + # and if even if collision happens, image is not that different and would still properly work. + # (^^^ CURRENT SOLUTION ^^^) But this is just a numbers game, it will blow up at some point. + # - do something crazy + f"--tag {image.repo}:latest " f"{push_arg}" f"--progress plain {image.full_path}" ) @@ -261,6 +275,7 @@ def build_and_push_one_image( retcode = proc.wait() if retcode != 0: + logging.error("Building image {} failed with error: {}\n{}".format(image, retcode, ''.join(list(open(build_log, 'rt'))))) return False, build_log logging.info("Processing of %s successfully finished", image.repo) @@ -407,8 +422,8 @@ def main(): if args.push: subprocess.check_output( # pylint: disable=unexpected-keyword-arg - "docker login --username 'robotclickhouse' --password-stdin", - input=get_parameter_from_ssm("dockerhub_robot_password"), + "docker login --username 'altinityinfra' --password-stdin", + input=get_parameter_from_ssm("dockerhub-password"), encoding="utf-8", shell=True, ) diff --git a/tests/ci/docker_manifests_merge.py b/tests/ci/docker_manifests_merge.py index 8bd50819877c..0d061bb0db33 100644 --- a/tests/ci/docker_manifests_merge.py +++ b/tests/ci/docker_manifests_merge.py @@ -173,8 +173,8 @@ def main(): args = parse_args() if args.push: subprocess.check_output( # pylint: disable=unexpected-keyword-arg - "docker login --username 'robotclickhouse' --password-stdin", - input=get_parameter_from_ssm("dockerhub_robot_password"), + "docker login --username 'altinityinfra' --password-stdin", + input=get_parameter_from_ssm("dockerhub-password"), encoding="utf-8", shell=True, ) diff --git a/tests/ci/docker_pull_helper.py b/tests/ci/docker_pull_helper.py index 54d48c588050..c1c0637411d2 100644 --- a/tests/ci/docker_pull_helper.py +++ b/tests/ci/docker_pull_helper.py @@ -5,6 +5,7 @@ import time import subprocess import logging +import traceback class DockerImage: @@ -48,6 +49,7 @@ def get_images_with_versions(reports_path, required_image, pull=True): docker_images.append(docker_image) if pull: + latest_error = None for docker_image in docker_images: for i in range(10): try: @@ -60,7 +62,8 @@ def get_images_with_versions(reports_path, required_image, pull=True): break except Exception as ex: time.sleep(i * 3) - logging.info("Got execption pulling docker %s", ex) + logging.info("Got exception pulling docker %s", ex) + latest_error = traceback.format_exc() else: raise Exception( f"Cannot pull dockerhub for image docker pull {docker_image} because of {latest_error}" diff --git a/tests/ci/docker_server.py b/tests/ci/docker_server.py index 0ddafc4b5821..972182929800 100644 --- a/tests/ci/docker_server.py +++ b/tests/ci/docker_server.py @@ -70,7 +70,7 @@ def parse_args() -> argparse.Namespace: parser.add_argument( "--image-repo", type=str, - default="clickhouse/clickhouse-server", + default="altinityinfra/clickhouse-server", help="image name on docker hub", ) parser.add_argument( @@ -298,8 +298,8 @@ def main(): if args.push: subprocess.check_output( # pylint: disable=unexpected-keyword-arg - "docker login --username 'robotclickhouse' --password-stdin", - input=get_parameter_from_ssm("dockerhub_robot_password"), + "docker login --username 'altinityinfra' --password-stdin", + input=get_parameter_from_ssm("dockerhub-password"), encoding="utf-8", shell=True, ) diff --git a/tests/ci/docker_test.py b/tests/ci/docker_test.py index 32df6d5f1d07..95ac61a0c1ee 100644 --- a/tests/ci/docker_test.py +++ b/tests/ci/docker_test.py @@ -37,61 +37,61 @@ def test_get_changed_docker_images(self): self.maxDiff = None expected = sorted( [ - di.DockerImage("docker/test/base", "clickhouse/test-base", False), - di.DockerImage("docker/docs/builder", "clickhouse/docs-builder", True), + di.DockerImage("docker/test/base", "altinityinfra/test-base", False), + di.DockerImage("docker/docs/builder", "altinityinfra/docs-builder", True), di.DockerImage( "docker/test/stateless", - "clickhouse/stateless-test", + "altinityinfra/stateless-test", False, - "clickhouse/test-base", + "altinityinfra/test-base", ), di.DockerImage( "docker/test/integration/base", - "clickhouse/integration-test", + "altinityinfra/integration-test", False, - "clickhouse/test-base", - ), - di.DockerImage( - "docker/test/fuzzer", - "clickhouse/fuzzer", - False, - "clickhouse/test-base", + "altinityinfra/test-base", ), + # di.DockerImage( + # "docker/test/fuzzer", + # "altinityinfra/fuzzer", + # False, + # "altinityinfra/test-base", + # ), di.DockerImage( "docker/test/keeper-jepsen", - "clickhouse/keeper-jepsen-test", - False, - "clickhouse/test-base", - ), - di.DockerImage( - "docker/docs/check", - "clickhouse/docs-check", - False, - "clickhouse/docs-builder", - ), - di.DockerImage( - "docker/docs/release", - "clickhouse/docs-release", + "altinityinfra/keeper-jepsen-test", False, - "clickhouse/docs-builder", + "altinityinfra/test-base", ), + # di.DockerImage( + # "docker/docs/check", + # "altinityinfra/docs-check", + # False, + # "altinityinfra/docs-builder", + # ), + # di.DockerImage( + # "docker/docs/release", + # "altinityinfra/docs-release", + # False, + # "altinityinfra/docs-builder", + # ), di.DockerImage( "docker/test/stateful", - "clickhouse/stateful-test", + "altinityinfra/stateful-test", False, - "clickhouse/stateless-test", + "altinityinfra/stateless-test", ), di.DockerImage( "docker/test/unit", - "clickhouse/unit-test", + "altinityinfra/unit-test", False, - "clickhouse/stateless-test", + "altinityinfra/stateless-test", ), di.DockerImage( "docker/test/stress", - "clickhouse/stress-test", + "altinityinfra/stress-test", False, - "clickhouse/stateful-test", + "altinityinfra/stateful-test", ), ] ) diff --git a/tests/ci/docs_check.py b/tests/ci/docs_check.py index 58678b160a4f..10168c9a77e8 100644 --- a/tests/ci/docs_check.py +++ b/tests/ci/docs_check.py @@ -50,7 +50,7 @@ if not os.path.exists(temp_path): os.makedirs(temp_path) - docker_image = get_image_with_version(temp_path, "clickhouse/docs-check") + docker_image = get_image_with_version(temp_path, "altinityinfra/docs-check") test_output = os.path.join(temp_path, "docs_check_log") if not os.path.exists(test_output): @@ -114,4 +114,7 @@ report_url, NAME, ) + ch_helper.insert_events_into(db="gh-data", table="checks", events=prepared_events) + if status == "error": + sys.exit(1) diff --git a/tests/ci/docs_release.py b/tests/ci/docs_release.py index b6d47326f9b8..99ee9e0910d7 100644 --- a/tests/ci/docs_release.py +++ b/tests/ci/docs_release.py @@ -34,7 +34,7 @@ if not os.path.exists(temp_path): os.makedirs(temp_path) - docker_image = get_image_with_version(temp_path, "clickhouse/docs-release") + docker_image = get_image_with_version(temp_path, "altinityinfra/docs-release") test_output = os.path.join(temp_path, "docs_release_log") if not os.path.exists(test_output): diff --git a/tests/ci/env_helper.py b/tests/ci/env_helper.py index dd081523db11..d2ef7f397a27 100644 --- a/tests/ci/env_helper.py +++ b/tests/ci/env_helper.py @@ -11,7 +11,7 @@ CLOUDFLARE_TOKEN = os.getenv("CLOUDFLARE_TOKEN") GITHUB_EVENT_PATH = os.getenv("GITHUB_EVENT_PATH", "") GITHUB_JOB = os.getenv("GITHUB_JOB", "local") -GITHUB_REPOSITORY = os.getenv("GITHUB_REPOSITORY", "ClickHouse/ClickHouse") +GITHUB_REPOSITORY = os.getenv("GITHUB_REPOSITORY", "Altinity/ClickHouse") GITHUB_RUN_ID = os.getenv("GITHUB_RUN_ID", "0") GITHUB_SERVER_URL = os.getenv("GITHUB_SERVER_URL", "https://github.com") GITHUB_WORKSPACE = os.getenv("GITHUB_WORKSPACE", git_root) @@ -20,5 +20,6 @@ REPORTS_PATH = os.getenv("REPORTS_PATH", p.abspath(p.join(module_dir, "./reports"))) REPO_COPY = os.getenv("REPO_COPY", git_root) RUNNER_TEMP = os.getenv("RUNNER_TEMP", p.abspath(p.join(module_dir, "./tmp"))) -S3_BUILDS_BUCKET = os.getenv("S3_BUILDS_BUCKET", "clickhouse-builds") -S3_TEST_REPORTS_BUCKET = os.getenv("S3_TEST_REPORTS_BUCKET", "clickhouse-test-reports") +S3_BUILDS_BUCKET = os.getenv("S3_BUILDS_BUCKET", "altinity-build-artifacts") +S3_TEST_REPORTS_BUCKET = os.getenv("S3_TEST_REPORTS_BUCKET", "altinity-build-artifacts") +CLICKHOUSE_STABLE_VERSION_SUFFIX = os.getenv("CLICKHOUSE_STABLE_VERSION_SUFFIX", "stable") diff --git a/tests/ci/fast_test_check.py b/tests/ci/fast_test_check.py index 64e045947864..df5464a7dace 100644 --- a/tests/ci/fast_test_check.py +++ b/tests/ci/fast_test_check.py @@ -98,7 +98,7 @@ def process_results(result_folder): logging.info("Check is already finished according to github status, exiting") sys.exit(0) - docker_image = get_image_with_version(temp_path, "clickhouse/fasttest") + docker_image = get_image_with_version(temp_path, "altinityinfra/fasttest") s3_helper = S3Helper("https://s3.amazonaws.com") @@ -208,7 +208,7 @@ def process_results(result_folder): # Refuse other checks to run if fast test failed if state != "success": - if "force-tests" in pr_info.labels: + if "force-tests" in pr_info.labels and state != "error": print("'force-tests' enabled, will report success") else: sys.exit(1) diff --git a/tests/ci/functional_test_check.py b/tests/ci/functional_test_check.py index 6113bfdf0cdf..2b8a52b4db43 100644 --- a/tests/ci/functional_test_check.py +++ b/tests/ci/functional_test_check.py @@ -48,9 +48,9 @@ def get_additional_envs(check_name, run_by_hash_num, run_by_hash_total): def get_image_name(check_name): if "stateless" in check_name.lower(): - return "clickhouse/stateless-test" + return "altinityinfra/stateless-test" if "stateful" in check_name.lower(): - return "clickhouse/stateful-test" + return "altinityinfra/stateful-test" else: raise Exception(f"Cannot deduce image name based on check name {check_name}") @@ -190,10 +190,12 @@ def process_results(result_folder, server_log_path): run_by_hash_total = 0 check_name_with_group = check_name - rerun_helper = RerunHelper(gh, pr_info, check_name_with_group) - if rerun_helper.is_already_finished_by_status(): - logging.info("Check is already finished according to github status, exiting") - sys.exit(0) + # Always re-run, even if it finished in previous run. + # gh = Github(get_best_robot_token()) + # rerun_helper = RerunHelper(gh, pr_info, check_name_with_group) + # if rerun_helper.is_already_finished_by_status(): + # logging.info("Check is already finished according to github status, exiting") + # sys.exit(0) if not os.path.exists(temp_path): os.makedirs(temp_path) diff --git a/tests/ci/get_robot_token.py b/tests/ci/get_robot_token.py index cb79d9ae01ac..10d742083f56 100644 --- a/tests/ci/get_robot_token.py +++ b/tests/ci/get_robot_token.py @@ -9,7 +9,14 @@ def get_parameter_from_ssm(name, decrypt=True, client=None): return client.get_parameter(Name=name, WithDecryption=decrypt)["Parameter"]["Value"] -def get_best_robot_token(token_prefix_env_name="github_robot_token_", total_tokens=4): +# Original CI code uses the "_original" version of this method. Each robot token is rate limited +# and the original implementation selects the "best one". To make it simpler and iterate faster, +# we are using only one robot and keeping the method signature. In the future we might reconsider +# having multiple robot tokens +def get_best_robot_token(token_prefix_env_name="github_robot_token", total_tokens=4): + return get_parameter_from_ssm(token_prefix_env_name) + +def get_best_robot_token_original(token_prefix_env_name="github_robot_token_", total_tokens=4): client = boto3.client("ssm", region_name="us-east-1") tokens = {} for i in range(1, total_tokens + 1): diff --git a/tests/ci/git_helper.py b/tests/ci/git_helper.py index 50414ffb470d..18d0cbf3840c 100644 --- a/tests/ci/git_helper.py +++ b/tests/ci/git_helper.py @@ -9,7 +9,7 @@ # \A and \Z match only start and end of the whole string RELEASE_BRANCH_REGEXP = r"\A\d+[.]\d+\Z" TAG_REGEXP = ( - r"\Av\d{2}[.][1-9]\d*[.][1-9]\d*[.][1-9]\d*-(testing|prestable|stable|lts)\Z" + r"\Av\d{2}[.][1-9]\d*[.][1-9]\d*[.][1-9]\d*-(testing|prestable|stable|lts|altinitystable)\Z" ) SHA_REGEXP = r"\A([0-9]|[a-f]){40}\Z" diff --git a/tests/ci/git_test.py b/tests/ci/git_test.py index 785c9b62ccef..69371af40b34 100644 --- a/tests/ci/git_test.py +++ b/tests/ci/git_test.py @@ -57,6 +57,9 @@ def test_tags(self): with self.assertRaises(Exception): setattr(self.git, tag_attr, tag) + def check_tag(self): + self.git.check_tag("v21.12.333.4567-altinitystable") + def test_tweak(self): self.git.commits_since_tag = 0 self.assertEqual(self.git.tweak, 1) @@ -66,3 +69,6 @@ def test_tweak(self): self.assertEqual(self.git.tweak, 22224) self.git.commits_since_tag = 0 self.assertEqual(self.git.tweak, 22222) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/ci/integration_test_check.py b/tests/ci/integration_test_check.py index 4ee0c8349746..24d5eda5477c 100644 --- a/tests/ci/integration_test_check.py +++ b/tests/ci/integration_test_check.py @@ -30,17 +30,17 @@ # When update, update # integration/ci-runner.py:ClickhouseIntegrationTestsRunner.get_images_names too IMAGES = [ - "clickhouse/integration-tests-runner", - "clickhouse/mysql-golang-client", - "clickhouse/mysql-java-client", - "clickhouse/mysql-js-client", - "clickhouse/mysql-php-client", - "clickhouse/postgresql-java-client", - "clickhouse/integration-test", - "clickhouse/kerberos-kdc", - "clickhouse/kerberized-hadoop", - "clickhouse/integration-helper", - "clickhouse/dotnet-client", + "altinityinfra/integration-tests-runner", + "altinityinfra/mysql-golang-client", + "altinityinfra/mysql-java-client", + "altinityinfra/mysql-js-client", + "altinityinfra/mysql-php-client", + "altinityinfra/postgresql-java-client", + "altinityinfra/integration-test", + "altinityinfra/kerberos-kdc", + "altinityinfra/kerberized-hadoop", + "altinityinfra/integration-helper", + "altinityinfra/dotnet-client", ] @@ -146,10 +146,12 @@ def process_results(result_folder): gh = Github(get_best_robot_token()) - rerun_helper = RerunHelper(gh, pr_info, check_name_with_group) - if rerun_helper.is_already_finished_by_status(): - logging.info("Check is already finished according to github status, exiting") - sys.exit(0) + # Always re-run, even if it finished in previous run. + # gh = Github(get_best_robot_token()) + # rerun_helper = RerunHelper(gh, pr_info, check_name_with_group) + # if rerun_helper.is_already_finished_by_status(): + # logging.info("Check is already finished according to github status, exiting") + # sys.exit(0) images = get_images_with_versions(reports_path, IMAGES) images_with_versions = {i.name: i.version for i in images} @@ -233,4 +235,8 @@ def process_results(result_folder): report_url, check_name_with_group, ) + ch_helper.insert_events_into(db="gh-data", table="checks", events=prepared_events) + + if state == "error": + sys.exit(1) diff --git a/tests/ci/keeper_jepsen_check.py b/tests/ci/keeper_jepsen_check.py index b0ec1e7ba8bf..98af355ab6d1 100644 --- a/tests/ci/keeper_jepsen_check.py +++ b/tests/ci/keeper_jepsen_check.py @@ -26,7 +26,7 @@ JEPSEN_GROUP_NAME = "jepsen_group" DESIRED_INSTANCE_COUNT = 3 -IMAGE_NAME = "clickhouse/keeper-jepsen-test" +IMAGE_NAME = "altinityinfra/keeper-jepsen-test" CHECK_NAME = "ClickHouse Keeper Jepsen (actions)" diff --git a/tests/ci/performance_comparison_check.py b/tests/ci/performance_comparison_check.py index c6ce86b2ce10..bd0ca267ddd8 100644 --- a/tests/ci/performance_comparison_check.py +++ b/tests/ci/performance_comparison_check.py @@ -20,7 +20,7 @@ from tee_popen import TeePopen from rerun_helper import RerunHelper -IMAGE_NAME = "clickhouse/performance-comparison" +IMAGE_NAME = "altinityinfra/performance-comparison" def get_run_command( @@ -217,3 +217,6 @@ def __exit__(self, exc_type, exc_val, exc_tb): post_commit_status( gh, pr_info.sha, check_name_with_group, message, status, report_url ) + + if status == "error": + sys.exit(1) diff --git a/tests/ci/release.py b/tests/ci/release.py index 89182dc7428e..2fdd4c8b1742 100755 --- a/tests/ci/release.py +++ b/tests/ci/release.py @@ -400,7 +400,7 @@ def parse_args() -> argparse.Namespace: ) parser.add_argument( "--repo", - default="ClickHouse/ClickHouse", + default="Altinity/ClickHouse", help="repository to create the release", ) parser.add_argument( diff --git a/tests/ci/run_check.py b/tests/ci/run_check.py index 9c7ba13f8e4d..ef96dc03e18d 100644 --- a/tests/ci/run_check.py +++ b/tests/ci/run_check.py @@ -78,6 +78,7 @@ "ilejn", # Arenadata, responsible for Kerberized Kafka "thomoco", # ClickHouse "BoloniniD", # Seasoned contributor, HSE + "arthurpassos" # Altinity ] } diff --git a/tests/ci/split_build_smoke_check.py b/tests/ci/split_build_smoke_check.py index 41ba6c2fedb3..39561a311875 100644 --- a/tests/ci/split_build_smoke_check.py +++ b/tests/ci/split_build_smoke_check.py @@ -20,7 +20,7 @@ from rerun_helper import RerunHelper -DOCKER_IMAGE = "clickhouse/split-build-smoke-test" +DOCKER_IMAGE = "altinityinfra/split-build-smoke-test" DOWNLOAD_RETRIES_COUNT = 5 RESULT_LOG_NAME = "run.log" CHECK_NAME = "Split build smoke test (actions)" @@ -147,4 +147,8 @@ def get_run_command(build_path, result_folder, server_log_folder, docker_image): report_url, CHECK_NAME, ) + ch_helper.insert_events_into(db="gh-data", table="checks", events=prepared_events) + + if state == "error": + sys.exit(1) diff --git a/tests/ci/stress_check.py b/tests/ci/stress_check.py index b95bf4b8aba0..7c110daf03e4 100644 --- a/tests/ci/stress_check.py +++ b/tests/ci/stress_check.py @@ -114,7 +114,7 @@ def process_results(result_folder, server_log_path, run_log_path): logging.info("Check is already finished according to github status, exiting") sys.exit(0) - docker_image = get_image_with_version(reports_path, "clickhouse/stress-test") + docker_image = get_image_with_version(reports_path, "altinityinfra/stress-test") packages_path = os.path.join(temp_path, "packages") if not os.path.exists(packages_path): @@ -175,3 +175,6 @@ def process_results(result_folder, server_log_path, run_log_path): check_name, ) ch_helper.insert_events_into(db="gh-data", table="checks", events=prepared_events) + + if state == "error": + sys.exit(1) diff --git a/tests/ci/style_check.py b/tests/ci/style_check.py index 1b3037217c83..2260c4d7f924 100644 --- a/tests/ci/style_check.py +++ b/tests/ci/style_check.py @@ -83,7 +83,7 @@ def process_result(result_folder): if not os.path.exists(temp_path): os.makedirs(temp_path) - docker_image = get_image_with_version(temp_path, "clickhouse/style-test") + docker_image = get_image_with_version(temp_path, "altinityinfra/style-test") s3_helper = S3Helper("https://s3.amazonaws.com") cmd = ( @@ -118,3 +118,6 @@ def process_result(result_folder): NAME, ) ch_helper.insert_events_into(db="gh-data", table="checks", events=prepared_events) + + if state == "error": + sys.exit(1) diff --git a/tests/ci/tests/docker_images.json b/tests/ci/tests/docker_images.json index ca5c516bccba..53ad258f6ec9 100644 --- a/tests/ci/tests/docker_images.json +++ b/tests/ci/tests/docker_images.json @@ -1,10 +1,10 @@ { "docker/packager/deb": { - "name": "clickhouse/deb-builder", + "name": "altinityinfra/deb-builder", "dependent": [] }, "docker/packager/binary": { - "name": "clickhouse/binary-builder", + "name": "altinityinfra/binary-builder", "dependent": [ "docker/test/split_build_smoke_test", "docker/test/pvs", @@ -12,156 +12,112 @@ ] }, "docker/test/compatibility/centos": { - "name": "clickhouse/test-old-centos", + "name": "altinityinfra/test-old-centos", "dependent": [] }, "docker/test/compatibility/ubuntu": { - "name": "clickhouse/test-old-ubuntu", + "name": "altinityinfra/test-old-ubuntu", "dependent": [] }, "docker/test/integration/base": { - "name": "clickhouse/integration-test", - "dependent": [] - }, - "docker/test/fuzzer": { - "name": "clickhouse/fuzzer", - "dependent": [] - }, - "docker/test/performance-comparison": { - "name": "clickhouse/performance-comparison", - "dependent": [] - }, - "docker/test/pvs": { - "name": "clickhouse/pvs-test", + "name": "altinityinfra/integration-test", "dependent": [] }, "docker/test/util": { - "name": "clickhouse/test-util", + "name": "altinityinfra/test-util", "dependent": [ "docker/test/base", "docker/test/fasttest" ] }, "docker/test/stateless": { - "name": "clickhouse/stateless-test", + "name": "altinityinfra/stateless-test", "dependent": [ "docker/test/stateful", "docker/test/unit" ] }, "docker/test/stateful": { - "name": "clickhouse/stateful-test", + "name": "altinityinfra/stateful-test", "dependent": [ "docker/test/stress" ] }, "docker/test/unit": { - "name": "clickhouse/unit-test", - "dependent": [] - }, - "docker/test/stress": { - "name": "clickhouse/stress-test", - "dependent": [] - }, - "docker/test/split_build_smoke_test": { - "name": "clickhouse/split-build-smoke-test", - "dependent": [] - }, - "docker/test/codebrowser": { - "name": "clickhouse/codebrowser", + "name": "altinityinfra/unit-test", "dependent": [] }, "docker/test/integration/runner": { - "name": "clickhouse/integration-tests-runner", + "name": "altinityinfra/integration-tests-runner", "dependent": [] }, "docker/test/testflows/runner": { - "name": "clickhouse/testflows-runner", + "name": "altinityinfra/testflows-runner", "dependent": [] }, "docker/test/fasttest": { - "name": "clickhouse/fasttest", - "dependent": [] - }, - "docker/test/style": { - "name": "clickhouse/style-test", + "name": "altinityinfra/fasttest", "dependent": [] }, "docker/test/integration/s3_proxy": { - "name": "clickhouse/s3-proxy", + "name": "altinityinfra/s3-proxy", "dependent": [] }, "docker/test/integration/resolver": { - "name": "clickhouse/python-bottle", + "name": "altinityinfra/python-bottle", "dependent": [] }, "docker/test/integration/helper_container": { - "name": "clickhouse/integration-helper", + "name": "altinityinfra/integration-helper", "dependent": [] }, "docker/test/integration/mysql_golang_client": { - "name": "clickhouse/mysql-golang-client", + "name": "altinityinfra/mysql-golang-client", "dependent": [] }, "docker/test/integration/dotnet_client": { - "name": "clickhouse/dotnet-client", + "name": "altinityinfra/dotnet-client", "dependent": [] }, "docker/test/integration/mysql_java_client": { - "name": "clickhouse/mysql-java-client", + "name": "altinityinfra/mysql-java-client", "dependent": [] }, "docker/test/integration/mysql_js_client": { - "name": "clickhouse/mysql-js-client", + "name": "altinityinfra/mysql-js-client", "dependent": [] }, "docker/test/integration/mysql_php_client": { - "name": "clickhouse/mysql-php-client", + "name": "altinityinfra/mysql-php-client", "dependent": [] }, "docker/test/integration/postgresql_java_client": { - "name": "clickhouse/postgresql-java-client", + "name": "altinityinfra/postgresql-java-client", "dependent": [] }, "docker/test/integration/kerberos_kdc": { - "name": "clickhouse/kerberos-kdc", + "name": "altinityinfra/kerberos-kdc", "dependent": [] }, "docker/test/base": { - "name": "clickhouse/test-base", - "dependent": [ + "name": "altinityinfra/test-base", + "dependent": [ "docker/test/stateless", "docker/test/integration/base", "docker/test/fuzzer", "docker/test/keeper-jepsen" - ] + ] }, "docker/test/integration/kerberized_hadoop": { - "name": "clickhouse/kerberized-hadoop", + "name": "altinityinfra/kerberized-hadoop", "dependent": [] }, "docker/test/sqlancer": { - "name": "clickhouse/sqlancer-test", + "name": "altinityinfra/sqlancer-test", "dependent": [] }, "docker/test/keeper-jepsen": { - "name": "clickhouse/keeper-jepsen-test", - "dependent": [] - }, - "docker/docs/builder": { - "name": "clickhouse/docs-builder", - "only_amd64": true, - "dependent": [ - "docker/docs/check", - "docker/docs/release" - ] - }, - "docker/docs/check": { - "name": "clickhouse/docs-check", - "dependent": [] - }, - "docker/docs/release": { - "name": "clickhouse/docs-release", + "name": "altinityinfra/keeper-jepsen-test", "dependent": [] } } diff --git a/tests/ci/unit_tests_check.py b/tests/ci/unit_tests_check.py index 84c4faa822db..b2a1f837faa7 100644 --- a/tests/ci/unit_tests_check.py +++ b/tests/ci/unit_tests_check.py @@ -25,7 +25,7 @@ from tee_popen import TeePopen -IMAGE_NAME = "clickhouse/unit-test" +IMAGE_NAME = "altinityinfra/unit-test" def get_test_name(line): @@ -173,4 +173,8 @@ def process_result(result_folder): report_url, check_name, ) + ch_helper.insert_events_into(db="gh-data", table="checks", events=prepared_events) + + if state == "error": + sys.exit(1) diff --git a/tests/ci/version_helper.py b/tests/ci/version_helper.py index 9c67191e4c3c..f8e93c582ce9 100755 --- a/tests/ci/version_helper.py +++ b/tests/ci/version_helper.py @@ -48,6 +48,7 @@ def __init__( revision: Union[int, str], git: Git, tweak: str = None, + flavour: str = None, ): self._major = int(major) self._minor = int(minor) @@ -58,6 +59,7 @@ def __init__( if tweak is not None: self._tweak = int(tweak) self._describe = "" + self._flavour = flavour def update(self, part: str) -> "ClickHouseVersion": """If part is valid, returns a new version""" @@ -107,9 +109,12 @@ def describe(self): @property def string(self): - return ".".join( + version_as_string = ".".join( (str(self.major), str(self.minor), str(self.patch), str(self.tweak)) ) + if self._flavour: + version_as_string = f"{version_as_string}.{self._flavour}" + return version_as_string def as_dict(self) -> VERSIONS: return { @@ -129,7 +134,10 @@ def as_tuple(self) -> Tuple[int, int, int, int]: def with_description(self, version_type): if version_type not in VersionType.VALID: raise ValueError(f"version type {version_type} not in {VersionType.VALID}") - self._describe = f"v{self.string}-{version_type}" + if version_type == self._flavour: + self._describe = f"v{self.string}" + else: + self._describe = f"v{self.string}-{version_type}" def __eq__(self, other) -> bool: if not isinstance(self, type(other)): @@ -157,7 +165,7 @@ def __le__(self, other: "ClickHouseVersion") -> bool: class VersionType: LTS = "lts" PRESTABLE = "prestable" - STABLE = "stable" + STABLE = "altinitystable" TESTING = "testing" VALID = (TESTING, PRESTABLE, STABLE, LTS) @@ -205,6 +213,8 @@ def get_version_from_repo( versions["patch"], versions["revision"], git, + # Explicitly use tweak value from version file + tweak=versions["revision"] ) @@ -278,7 +288,7 @@ def update_contributors( cfd.write(content) -def update_version_local(version, version_type="testing"): +def update_version_local(version : ClickHouseVersion, version_type="testing"): update_contributors() version.with_description(version_type) update_cmake_version(version) diff --git a/tests/integration/ci-runner.py b/tests/integration/ci-runner.py index a301869319df..da8346f55165 100755 --- a/tests/integration/ci-runner.py +++ b/tests/integration/ci-runner.py @@ -98,6 +98,7 @@ def get_counters(fname): # Lines like: # [gw0] [ 7%] ERROR test_mysql_protocol/test.py::test_golang_client + # [gw3] [ 40%] PASSED test_replicated_users/test.py::test_rename_replicated[QUOTA] state = line_arr[-2] test_name = line_arr[-1] @@ -255,17 +256,17 @@ def shuffle_test_groups(self): @staticmethod def get_images_names(): return [ - "clickhouse/dotnet-client", - "clickhouse/integration-helper", - "clickhouse/integration-test", - "clickhouse/integration-tests-runner", - "clickhouse/kerberized-hadoop", - "clickhouse/kerberos-kdc", - "clickhouse/mysql-golang-client", - "clickhouse/mysql-java-client", - "clickhouse/mysql-js-client", - "clickhouse/mysql-php-client", - "clickhouse/postgresql-java-client", + "altinityinfra/dotnet-client", + "altinityinfra/integration-helper", + "altinityinfra/integration-test", + "altinityinfra/integration-tests-runner", + "altinityinfra/kerberized-hadoop", + "altinityinfra/kerberos-kdc", + "altinityinfra/mysql-golang-client", + "altinityinfra/mysql-java-client", + "altinityinfra/mysql-js-client", + "altinityinfra/mysql-php-client", + "altinityinfra/postgresql-java-client", ] def _can_run_with(self, path, opt): @@ -462,7 +463,7 @@ def _get_runner_image_cmd(self, repo_path): "--docker-image-version", ): for img in self.get_images_names(): - if img == "clickhouse/integration-tests-runner": + if img == "altinityinfra/integration-tests-runner": runner_version = self.get_image_version(img) logging.info( "Can run with custom docker image version %s", runner_version @@ -905,6 +906,16 @@ def run_impl(self, repo_path, build_path): if "(memory)" in self.params["context_name"]: result_state = "success" + for res in test_result: + # It's not easy to parse output of pytest + # Especially when test names may contain spaces + # Do not allow it to avoid obscure failures + if " " not in res[0]: + continue + logging.warning("Found invalid test name with space: %s", res[0]) + status_text = "Found test with invalid name, see main log" + result_state = "failure" + return result_state, status_text, test_result, [] diff --git a/tests/integration/helpers/cluster.py b/tests/integration/helpers/cluster.py index d0b5e892f5b7..a32321c8dc20 100644 --- a/tests/integration/helpers/cluster.py +++ b/tests/integration/helpers/cluster.py @@ -16,21 +16,29 @@ import urllib.parse import shlex import urllib3 - -from cassandra.policies import RoundRobinPolicy -import cassandra.cluster -import psycopg2 -import pymongo -import pymysql import requests -from confluent_kafka.avro.cached_schema_registry_client import ( - CachedSchemaRegistryClient, -) + +try: + # Please, add modules that required for specific tests only here. + # So contributors will be able to run most tests locally + # without installing tons of unneeded packages that may be not so easy to install. + from cassandra.policies import RoundRobinPolicy + import cassandra.cluster + import psycopg2 + from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT + import pymongo + import pymysql + from confluent_kafka.avro.cached_schema_registry_client import ( + CachedSchemaRegistryClient, + ) + import meilisearch +except Exception as e: + logging.warning(f"Cannot import some modules, some tests may not work: {e}") + from dict2xml import dict2xml from kazoo.client import KazooClient from kazoo.exceptions import KazooException from minio import Minio -from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT from helpers.test_tools import assert_eq_with_retry, exec_query_with_retry from helpers import pytest_xdist_logging_to_separate_files @@ -689,7 +697,7 @@ def setup_keeper_cmd(self, instance, env_variables, docker_compose_yml_dir): binary_path = binary_path[: -len("-server")] env_variables["keeper_binary"] = binary_path - env_variables["image"] = "clickhouse/integration-test:" + self.docker_base_tag + env_variables["image"] = "altinityinfra/integration-test:" + self.docker_base_tag env_variables["user"] = str(os.getuid()) env_variables["keeper_fs"] = "bind" for i in range(1, 4): @@ -1169,7 +1177,7 @@ def add_instance( with_hive=False, hostname=None, env_variables=None, - image="clickhouse/integration-test", + image="altinityinfra/integration-test", tag=None, stay_alive=False, ipv4_address=None, @@ -2643,7 +2651,7 @@ def __init__( copy_common_configs=True, hostname=None, env_variables=None, - image="clickhouse/integration-test", + image="altinityinfra/integration-test", tag="latest", stay_alive=False, ipv4_address=None, diff --git a/tests/integration/helpers/network.py b/tests/integration/helpers/network.py index 63fb2065f9df..c3829e160e2b 100644 --- a/tests/integration/helpers/network.py +++ b/tests/integration/helpers/network.py @@ -248,7 +248,7 @@ def _ensure_container(self): time.sleep(i) image = subprocess.check_output( - "docker images -q clickhouse/integration-helper 2>/dev/null", shell=True + "docker images -q altinityinfra/integration-helper 2>/dev/null", shell=True ) if not image.strip(): print("No network image helper, will try download") @@ -257,16 +257,16 @@ def _ensure_container(self): for i in range(5): try: subprocess.check_call( # STYLE_CHECK_ALLOW_SUBPROCESS_CHECK_CALL - "docker pull clickhouse/integration-helper", shell=True + "docker pull altinityinfra/integration-helper", shell=True ) break except: time.sleep(i) else: - raise Exception("Cannot pull clickhouse/integration-helper image") + raise Exception("Cannot pull altinityinfra/integration-helper image") self._container = self._docker_client.containers.run( - "clickhouse/integration-helper", + "altinityinfra/integration-helper", auto_remove=True, command=("sleep %s" % self.container_exit_timeout), # /run/xtables.lock passed inside for correct iptables --wait diff --git a/tests/integration/runner b/tests/integration/runner index 5a168eeea250..8666258d4851 100755 --- a/tests/integration/runner +++ b/tests/integration/runner @@ -19,7 +19,7 @@ CONFIG_DIR_IN_REPO = "programs/server" INTEGRATION_DIR_IN_REPO = "tests/integration" SRC_DIR_IN_REPO = "src" -DIND_INTEGRATION_TESTS_IMAGE_NAME = "clickhouse/integration-tests-runner" +DIND_INTEGRATION_TESTS_IMAGE_NAME = "altinityinfra/integration-tests-runner" def check_args_and_update_paths(args): if args.clickhouse_root: @@ -226,23 +226,23 @@ if __name__ == "__main__": if args.docker_compose_images_tags is not None: for img_tag in args.docker_compose_images_tags: [image, tag] = img_tag.split(":") - if image == "clickhouse/mysql-golang-client": + if image == "altinityinfra/mysql-golang-client": env_tags += "-e {}={} ".format("DOCKER_MYSQL_GOLANG_CLIENT_TAG", tag) - elif image == "clickhouse/dotnet-client": + elif image == "altinityinfra/dotnet-client": env_tags += "-e {}={} ".format("DOCKER_DOTNET_CLIENT_TAG", tag) - elif image == "clickhouse/mysql-java-client": + elif image == "altinityinfra/mysql-java-client": env_tags += "-e {}={} ".format("DOCKER_MYSQL_JAVA_CLIENT_TAG", tag) - elif image == "clickhouse/mysql-js-client": + elif image == "altinityinfra/mysql-js-client": env_tags += "-e {}={} ".format("DOCKER_MYSQL_JS_CLIENT_TAG", tag) - elif image == "clickhouse/mysql-php-client": + elif image == "altinityinfra/mysql-php-client": env_tags += "-e {}={} ".format("DOCKER_MYSQL_PHP_CLIENT_TAG", tag) - elif image == "clickhouse/postgresql-java-client": + elif image == "altinityinfra/postgresql-java-client": env_tags += "-e {}={} ".format("DOCKER_POSTGRESQL_JAVA_CLIENT_TAG", tag) - elif image == "clickhouse/integration-test": + elif image == "altinityinfra/integration-test": env_tags += "-e {}={} ".format("DOCKER_BASE_TAG", tag) - elif image == "clickhouse/kerberized-hadoop": + elif image == "altinityinfra/kerberized-hadoop": env_tags += "-e {}={} ".format("DOCKER_KERBERIZED_HADOOP_TAG", tag) - elif image == "clickhouse/kerberos-kdc": + elif image == "altinityinfra/kerberos-kdc": env_tags += "-e {}={} ".format("DOCKER_KERBEROS_KDC_TAG", tag) else: logging.info("Unknown image %s" % (image)) diff --git a/tests/integration/test_replicated_users/test.py b/tests/integration/test_replicated_users/test.py index add45d262e63..56383f0d2dfb 100644 --- a/tests/integration/test_replicated_users/test.py +++ b/tests/integration/test_replicated_users/test.py @@ -41,7 +41,7 @@ class Entity: def get_entity_id(entity): - return entity.keyword + return entity.keyword.replace(" ", "_") @pytest.mark.parametrize("entity", entities, ids=get_entity_id) diff --git a/tests/integration/test_s3_zero_copy_replication/test.py b/tests/integration/test_s3_zero_copy_replication/test.py index d7aa4feb1d2f..1ce1047ebec9 100644 --- a/tests/integration/test_s3_zero_copy_replication/test.py +++ b/tests/integration/test_s3_zero_copy_replication/test.py @@ -361,6 +361,8 @@ def test_s3_zero_copy_with_ttl_delete(cluster, large_data, iterations): ) node1.query("OPTIMIZE TABLE ttl_delete_test FINAL") + + node1.query("SYSTEM SYNC REPLICA ttl_delete_test") node2.query("SYSTEM SYNC REPLICA ttl_delete_test") if large_data: diff --git a/tests/integration/test_s3_zero_copy_ttl/test.py b/tests/integration/test_s3_zero_copy_ttl/test.py index 14b4664fcc14..9a782aacef6b 100644 --- a/tests/integration/test_s3_zero_copy_ttl/test.py +++ b/tests/integration/test_s3_zero_copy_ttl/test.py @@ -68,19 +68,27 @@ def test_ttl_move_and_s3(started_cluster): assert node1.query("SELECT COUNT() FROM s3_test_with_ttl") == "30\n" assert node2.query("SELECT COUNT() FROM s3_test_with_ttl") == "30\n" - time.sleep(5) + for attempt in reversed(range(5)): + time.sleep(5) - print( - node1.query( - "SELECT * FROM system.parts WHERE table = 's3_test_with_ttl' FORMAT Vertical" + print( + node1.query( + "SELECT * FROM system.parts WHERE table = 's3_test_with_ttl' FORMAT Vertical" + ) ) - ) - - minio = cluster.minio_client - objects = minio.list_objects(cluster.minio_bucket, "data/", recursive=True) - counter = 0 - for obj in objects: - print("Objectname:", obj.object_name, "metadata:", obj.metadata) - counter += 1 - print("Total objects", counter) + + minio = cluster.minio_client + objects = minio.list_objects(cluster.minio_bucket, "data/", recursive=True) + counter = 0 + for obj in objects: + print(f"Objectname: {obj.object_name}, metadata: {obj.metadata}") + counter += 1 + + print(f"Total objects: {counter}") + + if counter == 300: + break + + print(f"Attempts remaining: {attempt}") + assert counter == 300 diff --git a/tests/integration/test_storage_kafka/test.py b/tests/integration/test_storage_kafka/test.py index a27b5a134e49..45a944b8d93b 100644 --- a/tests/integration/test_storage_kafka/test.py +++ b/tests/integration/test_storage_kafka/test.py @@ -30,12 +30,24 @@ from kafka.protocol.group import MemberAssignment from kafka.admin import NewTopic +from pathlib import Path +from helpers.cluster import run_and_check # protoc --version # libprotoc 3.0.0 # # to create kafka_pb2.py # protoc --python_out=. kafka.proto +# Regenerate _pb2 files on each run, to make sure test doesn't depend installed protobuf version +proto_dir = Path(__file__).parent / "clickhouse_path/format_schemas" +gen_dir = Path(__file__).parent +gen_dir.mkdir(exist_ok=True) +run_and_check( + f"python3 -m grpc_tools.protoc -I{proto_dir!s} --python_out={gen_dir!s} --grpc_python_out={gen_dir!s} \ + {proto_dir!s}/kafka.proto", + shell=True, +) + from . import kafka_pb2 from . import social_pb2 from . import message_with_repeated_pb2 diff --git a/tests/queries/0_stateless/00900_orc_load.sh b/tests/queries/0_stateless/00900_orc_load.sh index b3f2c39e5d2c..62149fa554e1 100755 --- a/tests/queries/0_stateless/00900_orc_load.sh +++ b/tests/queries/0_stateless/00900_orc_load.sh @@ -5,16 +5,13 @@ CUR_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) # shellcheck source=../shell_config.sh . "$CUR_DIR"/../shell_config.sh -DATA_FILE=$CUR_DIR/data_orc/test.orc - ${CLICKHOUSE_CLIENT} --query="DROP TABLE IF EXISTS orc_load" ${CLICKHOUSE_CLIENT} --query="CREATE TABLE orc_load (int Int32, smallint Int8, bigint Int64, float Float32, double Float64, date Date, y String, datetime64 DateTime64(3)) ENGINE = Memory" ${CLICKHOUSE_CLIENT} --query="insert into orc_load values (0, 0, 0, 0, 0, '2019-01-01', 'test1', toDateTime64('2019-01-01 02:03:04.567', 3)), (2147483647, -1, 9223372036854775806, 123.345345, 345345.3453451212, '2019-01-01', 'test2', toDateTime64('2019-01-01 02:03:04.567', 3))" -${CLICKHOUSE_CLIENT} --query="select * from orc_load FORMAT ORC" > $DATA_FILE +${CLICKHOUSE_CLIENT} --query="select * from orc_load FORMAT ORC" > "${CLICKHOUSE_TMP}"/test.orc ${CLICKHOUSE_CLIENT} --query="truncate table orc_load" -cat "$DATA_FILE" | ${CLICKHOUSE_CLIENT} -q "insert into orc_load format ORC" -timeout 3 ${CLICKHOUSE_CLIENT} -q "insert into orc_load format ORC" < $DATA_FILE +cat "${CLICKHOUSE_TMP}"/test.orc | ${CLICKHOUSE_CLIENT} -q "insert into orc_load format ORC" +timeout 3 ${CLICKHOUSE_CLIENT} -q "insert into orc_load format ORC" < "${CLICKHOUSE_TMP}"/test.orc ${CLICKHOUSE_CLIENT} --query="select * from orc_load" ${CLICKHOUSE_CLIENT} --query="drop table orc_load" -rm -rf "$DATA_FILE" diff --git a/tests/testflows/aes_encryption/aes_encryption_env/clickhouse-service.yml b/tests/testflows/aes_encryption/aes_encryption_env/clickhouse-service.yml index 0c9352dbc0b6..74a56b63aabc 100644 --- a/tests/testflows/aes_encryption/aes_encryption_env/clickhouse-service.yml +++ b/tests/testflows/aes_encryption/aes_encryption_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: clickhouse/integration-test + image: altinityinfra/integration-test expose: - "9000" - "9009" diff --git a/tests/testflows/datetime64_extended_range/datetime64_extended_range_env/clickhouse-service.yml b/tests/testflows/datetime64_extended_range/datetime64_extended_range_env/clickhouse-service.yml index 0c9352dbc0b6..74a56b63aabc 100644 --- a/tests/testflows/datetime64_extended_range/datetime64_extended_range_env/clickhouse-service.yml +++ b/tests/testflows/datetime64_extended_range/datetime64_extended_range_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: clickhouse/integration-test + image: altinityinfra/integration-test expose: - "9000" - "9009" diff --git a/tests/testflows/example/example_env/clickhouse-service.yml b/tests/testflows/example/example_env/clickhouse-service.yml index 0c9352dbc0b6..74a56b63aabc 100644 --- a/tests/testflows/example/example_env/clickhouse-service.yml +++ b/tests/testflows/example/example_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: clickhouse/integration-test + image: altinityinfra/integration-test expose: - "9000" - "9009" diff --git a/tests/testflows/extended_precision_data_types/extended-precision-data-type_env/clickhouse-service.yml b/tests/testflows/extended_precision_data_types/extended-precision-data-type_env/clickhouse-service.yml index afb31f77c94c..9162d06bf27d 100644 --- a/tests/testflows/extended_precision_data_types/extended-precision-data-type_env/clickhouse-service.yml +++ b/tests/testflows/extended_precision_data_types/extended-precision-data-type_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: clickhouse/integration-test + image: altinityinfra/integration-test expose: - "9000" - "9009" diff --git a/tests/testflows/kerberos/kerberos_env/clickhouse-service.yml b/tests/testflows/kerberos/kerberos_env/clickhouse-service.yml index 45b975db00d7..7671684f6ee0 100644 --- a/tests/testflows/kerberos/kerberos_env/clickhouse-service.yml +++ b/tests/testflows/kerberos/kerberos_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: clickhouse/integration-test + image: altinityinfra/integration-test expose: - "9000" - "9009" diff --git a/tests/testflows/ldap/authentication/authentication_env/clickhouse-service.yml b/tests/testflows/ldap/authentication/authentication_env/clickhouse-service.yml index 74661f6fa04b..f8cc0a62c67c 100644 --- a/tests/testflows/ldap/authentication/authentication_env/clickhouse-service.yml +++ b/tests/testflows/ldap/authentication/authentication_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: clickhouse/integration-test + image: altinityinfra/integration-test init: true expose: - "9000" diff --git a/tests/testflows/ldap/authentication/ldap_authentication_env/clickhouse-service.yml b/tests/testflows/ldap/authentication/ldap_authentication_env/clickhouse-service.yml index 0c9352dbc0b6..74a56b63aabc 100644 --- a/tests/testflows/ldap/authentication/ldap_authentication_env/clickhouse-service.yml +++ b/tests/testflows/ldap/authentication/ldap_authentication_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: clickhouse/integration-test + image: altinityinfra/integration-test expose: - "9000" - "9009" diff --git a/tests/testflows/ldap/external_user_directory/external_user_directory_env/clickhouse-service.yml b/tests/testflows/ldap/external_user_directory/external_user_directory_env/clickhouse-service.yml index 74661f6fa04b..f8cc0a62c67c 100644 --- a/tests/testflows/ldap/external_user_directory/external_user_directory_env/clickhouse-service.yml +++ b/tests/testflows/ldap/external_user_directory/external_user_directory_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: clickhouse/integration-test + image: altinityinfra/integration-test init: true expose: - "9000" diff --git a/tests/testflows/ldap/external_user_directory/ldap_external_user_directory_env/clickhouse-service.yml b/tests/testflows/ldap/external_user_directory/ldap_external_user_directory_env/clickhouse-service.yml index 0c9352dbc0b6..74a56b63aabc 100644 --- a/tests/testflows/ldap/external_user_directory/ldap_external_user_directory_env/clickhouse-service.yml +++ b/tests/testflows/ldap/external_user_directory/ldap_external_user_directory_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: clickhouse/integration-test + image: altinityinfra/integration-test expose: - "9000" - "9009" diff --git a/tests/testflows/ldap/role_mapping/ldap_role_mapping_env/clickhouse-service.yml b/tests/testflows/ldap/role_mapping/ldap_role_mapping_env/clickhouse-service.yml index 0c9352dbc0b6..74a56b63aabc 100644 --- a/tests/testflows/ldap/role_mapping/ldap_role_mapping_env/clickhouse-service.yml +++ b/tests/testflows/ldap/role_mapping/ldap_role_mapping_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: clickhouse/integration-test + image: altinityinfra/integration-test expose: - "9000" - "9009" diff --git a/tests/testflows/ldap/role_mapping/role_mapping_env/clickhouse-service.yml b/tests/testflows/ldap/role_mapping/role_mapping_env/clickhouse-service.yml index 7ff0139ab9be..3fe80bfce343 100644 --- a/tests/testflows/ldap/role_mapping/role_mapping_env/clickhouse-service.yml +++ b/tests/testflows/ldap/role_mapping/role_mapping_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: clickhouse/integration-test + image: altinityinfra/integration-test init: true expose: - "9000" diff --git a/tests/testflows/map_type/map_type_env/clickhouse-service.yml b/tests/testflows/map_type/map_type_env/clickhouse-service.yml index afb31f77c94c..9162d06bf27d 100755 --- a/tests/testflows/map_type/map_type_env/clickhouse-service.yml +++ b/tests/testflows/map_type/map_type_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: clickhouse/integration-test + image: altinityinfra/integration-test expose: - "9000" - "9009" diff --git a/tests/testflows/rbac/rbac_env/clickhouse-service.yml b/tests/testflows/rbac/rbac_env/clickhouse-service.yml index c808372d7e94..4634f3b8721f 100755 --- a/tests/testflows/rbac/rbac_env/clickhouse-service.yml +++ b/tests/testflows/rbac/rbac_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: clickhouse/integration-test + image: altinityinfra/integration-test init: true expose: - "9000" diff --git a/tests/testflows/runner b/tests/testflows/runner index 0208512762ce..1cf2a784ca0e 100755 --- a/tests/testflows/runner +++ b/tests/testflows/runner @@ -14,7 +14,7 @@ DEFAULT_CLICKHOUSE_ROOT = os.path.abspath(os.path.join(CUR_FILE_DIR, "../../")) CURRENT_WORK_DIR = os.getcwd() CONTAINER_NAME = "clickhouse_testflows_tests" -DIND_TESTFLOWS_TESTS_IMAGE_NAME = "clickhouse/testflows-runner" +DIND_TESTFLOWS_TESTS_IMAGE_NAME = "altinityinfra/testflows-runner" def check_args_and_update_paths(args): if not os.path.isabs(args.binary): diff --git a/tests/testflows/window_functions/window_functions_env/clickhouse-service.yml b/tests/testflows/window_functions/window_functions_env/clickhouse-service.yml index afb31f77c94c..9162d06bf27d 100755 --- a/tests/testflows/window_functions/window_functions_env/clickhouse-service.yml +++ b/tests/testflows/window_functions/window_functions_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: clickhouse/integration-test + image: altinityinfra/integration-test expose: - "9000" - "9009" diff --git a/utils/clickhouse-docker b/utils/clickhouse-docker index cfe515f1de54..34b637f0eaad 100755 --- a/utils/clickhouse-docker +++ b/utils/clickhouse-docker @@ -26,11 +26,11 @@ then # https://stackoverflow.com/a/39454426/1555175 wget -nv https://registry.hub.docker.com/v1/repositories/clickhouse/clickhouse-server/tags -O - | sed -e 's/[][]//g' -e 's/"//g' -e 's/ //g' | tr '}' '\n' | awk -F: '{print $3}' else - docker pull clickhouse/clickhouse-server:${param} + docker pull altinityinfra/clickhouse-server:${param} tmp_dir=$(mktemp -d -t ci-XXXXXXXXXX) # older version require /nonexistent folder to exist to run clickhouse client :D chmod 777 ${tmp_dir} set -e - containerid=`docker run -v${tmp_dir}:/nonexistent -d clickhouse/clickhouse-server:${param}` + containerid=`docker run -v${tmp_dir}:/nonexistent -d altinityinfra/clickhouse-server:${param}` set +e while : do From 97720dbdc97d9cf281142d508031857cf53037ea Mon Sep 17 00:00:00 2001 From: Dmitry Novik Date: Tue, 7 Jun 2022 17:20:43 +0200 Subject: [PATCH 02/72] Merge pull request #36944 from excitoon-favorites/better_exp_smooth --- src/Processors/Transforms/WindowTransform.cpp | 327 ++++++++++-------- .../02020_exponential_smoothing.reference | 112 +++--- .../02020_exponential_smoothing.sql | 92 ++++- 3 files changed, 338 insertions(+), 193 deletions(-) diff --git a/src/Processors/Transforms/WindowTransform.cpp b/src/Processors/Transforms/WindowTransform.cpp index b81ed099915f..2a2fed1cc078 100644 --- a/src/Processors/Transforms/WindowTransform.cpp +++ b/src/Processors/Transforms/WindowTransform.cpp @@ -1,5 +1,7 @@ #include +#include + #include #include #include @@ -14,6 +16,7 @@ #include #include + namespace DB { @@ -1538,65 +1541,21 @@ struct WindowFunctionDenseRank final : public WindowFunction namespace recurrent_detail { - template T getLastValueFromInputColumn(const WindowTransform * /*transform*/, size_t /*function_index*/, size_t /*column_index*/) + template T getValue(const WindowTransform * /*transform*/, size_t /*function_index*/, size_t /*column_index*/, RowNumber /*row*/) { - throw Exception(ErrorCodes::NOT_IMPLEMENTED, "getLastValueFromInputColumn() is not implemented for {} type", typeid(T).name()); + throw Exception(ErrorCodes::NOT_IMPLEMENTED, "recurrent_detail::getValue() is not implemented for {} type", typeid(T).name()); } - template<> Float64 getLastValueFromInputColumn(const WindowTransform * transform, size_t function_index, size_t column_index) + template<> Float64 getValue(const WindowTransform * transform, size_t function_index, size_t column_index, RowNumber row) { const auto & workspace = transform->workspaces[function_index]; - auto current_row = transform->current_row; - - if (current_row.row == 0) - { - if (current_row.block > 0) - { - const auto & column = transform->blockAt(current_row.block - 1).input_columns[workspace.argument_column_indices[column_index]]; - return column->getFloat64(column->size() - 1); - } - } - else - { - const auto & column = transform->blockAt(current_row.block).input_columns[workspace.argument_column_indices[column_index]]; - return column->getFloat64(current_row.row - 1); - } - - return 0; - } - - template T getLastValueFromState(const WindowTransform * /*transform*/, size_t /*function_index*/, size_t /*data_index*/) - { - throw Exception(ErrorCodes::NOT_IMPLEMENTED, "getLastValueFromInputColumn() is not implemented for {} type", typeid(T).name()); - } - - template<> Float64 getLastValueFromState(const WindowTransform * transform, size_t function_index, size_t data_index) - { - const auto & workspace = transform->workspaces[function_index]; - if (workspace.aggregate_function_state.data() == nullptr) - { - return 0.0; - } - else - { - return static_cast(static_cast(workspace.aggregate_function_state.data()))[data_index]; - } - } - - template void setValueToState(const WindowTransform * /*transform*/, size_t /*function_index*/, T /*value*/, size_t /*data_index*/) - { - throw Exception(ErrorCodes::NOT_IMPLEMENTED, "setValueToState() is not implemented for {} type", typeid(T).name()); - } - - template<> void setValueToState(const WindowTransform * transform, size_t function_index, Float64 value, size_t data_index) - { - const auto & workspace = transform->workspaces[function_index]; - static_cast(static_cast(workspace.aggregate_function_state.data()))[data_index] = value; + const auto & column = transform->blockAt(row.block).input_columns[workspace.argument_column_indices[column_index]]; + return column->getFloat64(row.row); } template void setValueToOutputColumn(const WindowTransform * /*transform*/, size_t /*function_index*/, T /*value*/) { - throw Exception(ErrorCodes::NOT_IMPLEMENTED, "setValueToOutputColumn() is not implemented for {} type", typeid(T).name()); + throw Exception(ErrorCodes::NOT_IMPLEMENTED, "recurrent_detail::setValueToOutputColumn() is not implemented for {} type", typeid(T).name()); } template<> void setValueToOutputColumn(const WindowTransform * transform, size_t function_index, Float64 value) @@ -1607,82 +1566,77 @@ namespace recurrent_detail assert_cast(to).getData().push_back(value); } +} - template T getCurrentValueFromInputColumn(const WindowTransform * /*transform*/, size_t /*function_index*/, size_t /*column_index*/) +struct WindowFunctionHelpers +{ + template + static T getValue(const WindowTransform * transform, size_t function_index, size_t column_index, RowNumber row) { - throw Exception(ErrorCodes::NOT_IMPLEMENTED, "getCurrentValueFromInputColumn() is not implemented for {} type", typeid(T).name()); + return recurrent_detail::getValue(transform, function_index, column_index, row); } - template<> Float64 getCurrentValueFromInputColumn(const WindowTransform * transform, size_t function_index, size_t column_index) + template + static void setValueToOutputColumn(const WindowTransform * transform, size_t function_index, T value) { - const auto & workspace = transform->workspaces[function_index]; - auto current_row = transform->current_row; - const auto & current_block = transform->blockAt(current_row); - - return (*current_block.input_columns[workspace.argument_column_indices[column_index]]).getFloat64(transform->current_row.row); + recurrent_detail::setValueToOutputColumn(transform, function_index, value); } -} +}; -template -struct RecurrentWindowFunction : public WindowFunction +template +struct StatefulWindowFunction : public WindowFunction { - RecurrentWindowFunction(const std::string & name_, + StatefulWindowFunction(const std::string & name_, const DataTypes & argument_types_, const Array & parameters_) : WindowFunction(name_, argument_types_, parameters_) { } - size_t sizeOfData() const override { return sizeof(Float64)*state_size; } + size_t sizeOfData() const override { return sizeof(State); } size_t alignOfData() const override { return 1; } void create(AggregateDataPtr __restrict place) const override { - auto * const state = static_cast(static_cast(place)); - for (size_t i = 0; i < state_size; ++i) - state[i] = 0.0; + new (place) State(); } - template - static T getLastValueFromInputColumn(const WindowTransform * transform, size_t function_index, size_t column_index) + void destroy(AggregateDataPtr __restrict place) const noexcept override { - return recurrent_detail::getLastValueFromInputColumn(transform, function_index, column_index); + auto * const state = static_cast(static_cast(place)); + state->~State(); } - template - static T getLastValueFromState(const WindowTransform * transform, size_t function_index, size_t data_index) + State & getState(const WindowFunctionWorkspace & workspace) { - return recurrent_detail::getLastValueFromState(transform, function_index, data_index); - } - - template - static void setValueToState(const WindowTransform * transform, size_t function_index, T value, size_t data_index) - { - recurrent_detail::setValueToState(transform, function_index, value, data_index); + return *static_cast(static_cast(workspace.aggregate_function_state.data())); } +}; - template - static void setValueToOutputColumn(const WindowTransform * transform, size_t function_index, T value) - { - recurrent_detail::setValueToOutputColumn(transform, function_index, value); - } +struct ExponentialTimeDecayedSumState +{ + RowNumber previous_frame_start; + RowNumber previous_frame_end; + Float64 previous_time; + Float64 previous_sum; +}; - template - static T getCurrentValueFromInputColumn(const WindowTransform * transform, size_t function_index, size_t column_index) - { - return recurrent_detail::getCurrentValueFromInputColumn(transform, function_index, column_index); - } +struct ExponentialTimeDecayedAvgState +{ + RowNumber previous_frame_start; + RowNumber previous_frame_end; + Float64 previous_time; + Float64 previous_sum; + Float64 previous_count; }; -struct WindowFunctionExponentialTimeDecayedSum final : public RecurrentWindowFunction<1> +struct WindowFunctionExponentialTimeDecayedSum final : public StatefulWindowFunction { static constexpr size_t ARGUMENT_VALUE = 0; static constexpr size_t ARGUMENT_TIME = 1; - static constexpr size_t STATE_SUM = 0; - WindowFunctionExponentialTimeDecayedSum(const std::string & name_, const DataTypes & argument_types_, const Array & parameters_) - : RecurrentWindowFunction(name_, argument_types_, parameters_) + : StatefulWindowFunction(name_, argument_types_, parameters_) { if (parameters_.size() != 1) { @@ -1724,33 +1678,60 @@ struct WindowFunctionExponentialTimeDecayedSum final : public RecurrentWindowFun void windowInsertResultInto(const WindowTransform * transform, size_t function_index) override { - Float64 last_sum = getLastValueFromState(transform, function_index, STATE_SUM); - Float64 last_t = getLastValueFromInputColumn(transform, function_index, ARGUMENT_TIME); + const auto & workspace = transform->workspaces[function_index]; + auto & state = getState(workspace); + + Float64 result = 0; + Float64 curr_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, transform->current_row); - Float64 x = getCurrentValueFromInputColumn(transform, function_index, ARGUMENT_VALUE); - Float64 t = getCurrentValueFromInputColumn(transform, function_index, ARGUMENT_TIME); + if (state.previous_frame_start <= transform->frame_start + && transform->frame_start < state.previous_frame_end + && state.previous_frame_end <= transform->frame_end) + { + for (RowNumber i = state.previous_frame_start; i < transform->frame_start; transform->advanceRowNumber(i)) + { + Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + result -= std::exp((prev_t - curr_t) / decay_length) * prev_val; + } + result += std::exp((state.previous_time - curr_t) / decay_length) * state.previous_sum; + for (RowNumber i = state.previous_frame_end; i < transform->frame_end; transform->advanceRowNumber(i)) + { + Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + result += std::exp((prev_t - curr_t) / decay_length) * prev_val; + } + } + else + { + for (RowNumber i = transform->frame_start; i < transform->frame_end; transform->advanceRowNumber(i)) + { + Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + result += std::exp((prev_t - curr_t) / decay_length) * prev_val; + } + } - Float64 c = exp((last_t - t) / decay_length); - Float64 result = x + c * last_sum; + state.previous_sum = result; + state.previous_time = curr_t; + state.previous_frame_start = transform->frame_start; + state.previous_frame_end = transform->frame_end; - setValueToOutputColumn(transform, function_index, result); - setValueToState(transform, function_index, result, STATE_SUM); + WindowFunctionHelpers::setValueToOutputColumn(transform, function_index, result); } private: Float64 decay_length; }; -struct WindowFunctionExponentialTimeDecayedMax final : public RecurrentWindowFunction<1> +struct WindowFunctionExponentialTimeDecayedMax final : public WindowFunction { static constexpr size_t ARGUMENT_VALUE = 0; static constexpr size_t ARGUMENT_TIME = 1; - static constexpr size_t STATE_MAX = 0; - WindowFunctionExponentialTimeDecayedMax(const std::string & name_, const DataTypes & argument_types_, const Array & parameters_) - : RecurrentWindowFunction(name_, argument_types_, parameters_) + : WindowFunction(name_, argument_types_, parameters_) { if (parameters_.size() != 1) { @@ -1792,32 +1773,35 @@ struct WindowFunctionExponentialTimeDecayedMax final : public RecurrentWindowFun void windowInsertResultInto(const WindowTransform * transform, size_t function_index) override { - Float64 last_max = getLastValueFromState(transform, function_index, STATE_MAX); - Float64 last_t = getLastValueFromInputColumn(transform, function_index, ARGUMENT_TIME); + Float64 result = std::numeric_limits::lowest(); + Float64 curr_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, transform->current_row); - Float64 x = getCurrentValueFromInputColumn(transform, function_index, ARGUMENT_VALUE); - Float64 t = getCurrentValueFromInputColumn(transform, function_index, ARGUMENT_TIME); + for (RowNumber i = transform->frame_start; i < transform->frame_end; transform->advanceRowNumber(i)) + { + Float64 value = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); + Float64 t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); - Float64 c = exp((last_t - t) / decay_length); - Float64 result = std::max(x, c * last_max); + /// Avoiding extra calls to `exp` and multiplications. + if (value > result || t > curr_t || result < 0) + { + result = std::max(std::exp((t - curr_t) / decay_length) * value, result); + } + } - setValueToOutputColumn(transform, function_index, result); - setValueToState(transform, function_index, result, STATE_MAX); + WindowFunctionHelpers::setValueToOutputColumn(transform, function_index, result); } private: Float64 decay_length; }; -struct WindowFunctionExponentialTimeDecayedCount final : public RecurrentWindowFunction<1> +struct WindowFunctionExponentialTimeDecayedCount final : public StatefulWindowFunction { static constexpr size_t ARGUMENT_TIME = 0; - static constexpr size_t STATE_COUNT = 0; - WindowFunctionExponentialTimeDecayedCount(const std::string & name_, const DataTypes & argument_types_, const Array & parameters_) - : RecurrentWindowFunction(name_, argument_types_, parameters_) + : StatefulWindowFunction(name_, argument_types_, parameters_) { if (parameters_.size() != 1) { @@ -1851,33 +1835,57 @@ struct WindowFunctionExponentialTimeDecayedCount final : public RecurrentWindowF void windowInsertResultInto(const WindowTransform * transform, size_t function_index) override { - Float64 last_count = getLastValueFromState(transform, function_index, STATE_COUNT); - Float64 last_t = getLastValueFromInputColumn(transform, function_index, ARGUMENT_TIME); + const auto & workspace = transform->workspaces[function_index]; + auto & state = getState(workspace); + + Float64 result = 0; + Float64 curr_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, transform->current_row); - Float64 t = getCurrentValueFromInputColumn(transform, function_index, ARGUMENT_TIME); + if (state.previous_frame_start <= transform->frame_start + && transform->frame_start < state.previous_frame_end + && state.previous_frame_end <= transform->frame_end) + { + for (RowNumber i = state.previous_frame_start; i < transform->frame_start; transform->advanceRowNumber(i)) + { + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + result -= std::exp((prev_t - curr_t) / decay_length); + } + result += std::exp((state.previous_time - curr_t) / decay_length) * state.previous_sum; + for (RowNumber i = state.previous_frame_end; i < transform->frame_end; transform->advanceRowNumber(i)) + { + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + result += std::exp((prev_t - curr_t) / decay_length); + } + } + else + { + for (RowNumber i = transform->frame_start; i < transform->frame_end; transform->advanceRowNumber(i)) + { + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + result += std::exp((prev_t - curr_t) / decay_length); + } + } - Float64 c = exp((last_t - t) / decay_length); - Float64 result = c * last_count + 1.0; + state.previous_sum = result; + state.previous_time = curr_t; + state.previous_frame_start = transform->frame_start; + state.previous_frame_end = transform->frame_end; - setValueToOutputColumn(transform, function_index, result); - setValueToState(transform, function_index, result, STATE_COUNT); + WindowFunctionHelpers::setValueToOutputColumn(transform, function_index, result); } private: Float64 decay_length; }; -struct WindowFunctionExponentialTimeDecayedAvg final : public RecurrentWindowFunction<2> +struct WindowFunctionExponentialTimeDecayedAvg final : public StatefulWindowFunction { static constexpr size_t ARGUMENT_VALUE = 0; static constexpr size_t ARGUMENT_TIME = 1; - static constexpr size_t STATE_SUM = 0; - static constexpr size_t STATE_COUNT = 1; - WindowFunctionExponentialTimeDecayedAvg(const std::string & name_, const DataTypes & argument_types_, const Array & parameters_) - : RecurrentWindowFunction(name_, argument_types_, parameters_) + : StatefulWindowFunction(name_, argument_types_, parameters_) { if (parameters_.size() != 1) { @@ -1919,21 +1927,60 @@ struct WindowFunctionExponentialTimeDecayedAvg final : public RecurrentWindowFun void windowInsertResultInto(const WindowTransform * transform, size_t function_index) override { - Float64 last_sum = getLastValueFromState(transform, function_index, STATE_SUM); - Float64 last_count = getLastValueFromState(transform, function_index, STATE_COUNT); - Float64 last_t = getLastValueFromInputColumn(transform, function_index, ARGUMENT_TIME); + const auto & workspace = transform->workspaces[function_index]; + auto & state = getState(workspace); + + Float64 count = 0; + Float64 sum = 0; + Float64 curr_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, transform->current_row); + + if (state.previous_frame_start <= transform->frame_start + && transform->frame_start < state.previous_frame_end + && state.previous_frame_end <= transform->frame_end) + { + for (RowNumber i = state.previous_frame_start; i < transform->frame_start; transform->advanceRowNumber(i)) + { + Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + Float64 decay = std::exp((prev_t - curr_t) / decay_length); + sum -= decay * prev_val; + count -= decay; + } - Float64 x = getCurrentValueFromInputColumn(transform, function_index, ARGUMENT_VALUE); - Float64 t = getCurrentValueFromInputColumn(transform, function_index, ARGUMENT_TIME); + { + Float64 decay = std::exp((state.previous_time - curr_t) / decay_length); + sum += decay * state.previous_sum; + count += decay * state.previous_count; + } + + for (RowNumber i = state.previous_frame_end; i < transform->frame_end; transform->advanceRowNumber(i)) + { + Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + Float64 decay = std::exp((prev_t - curr_t) / decay_length); + sum += decay * prev_val; + count += decay; + } + } + else + { + for (RowNumber i = transform->frame_start; i < transform->frame_end; transform->advanceRowNumber(i)) + { + Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + Float64 decay = std::exp((prev_t - curr_t) / decay_length); + sum += decay * prev_val; + count += decay; + } + } - Float64 c = exp((last_t - t) / decay_length); - Float64 new_sum = c * last_sum + x; - Float64 new_count = c * last_count + 1.0; - Float64 result = new_sum / new_count; + state.previous_sum = sum; + state.previous_count = count; + state.previous_time = curr_t; + state.previous_frame_start = transform->frame_start; + state.previous_frame_end = transform->frame_end; - setValueToOutputColumn(transform, function_index, result); - setValueToState(transform, function_index, new_sum, STATE_SUM); - setValueToState(transform, function_index, new_count, STATE_COUNT); + WindowFunctionHelpers::setValueToOutputColumn(transform, function_index, sum/count); } private: diff --git a/tests/queries/0_stateless/02020_exponential_smoothing.reference b/tests/queries/0_stateless/02020_exponential_smoothing.reference index b3c234206783..334d32e1c163 100644 --- a/tests/queries/0_stateless/02020_exponential_smoothing.reference +++ b/tests/queries/0_stateless/02020_exponential_smoothing.reference @@ -1,13 +1,14 @@ +exponentialMovingAverage 1 0 0.5 0 1 0.25 0 2 0.125 -0 3 0.0625 -0 4 0.03125 -0 5 0.015625 -0 6 0.0078125 -0 7 0.00390625 -0 8 0.001953125 -0 9 0.0009765625 +0 3 0.062 +0 4 0.031 +0 5 0.016 +0 6 0.008 +0 7 0.004 +0 8 0.002 +0 9 0.001 1 0 0.067 0 1 0.062 0 2 0.058 @@ -128,16 +129,17 @@ 0 47 0.129 ██████▍ 0 48 0.065 ███▏ 0 49 0.032 █▌ +exponentialTimeDecayedSum 1 0 1 -0 1 0.36787944117144233 -0 2 0.1353352832366127 -0 3 0.04978706836786395 -0 4 0.018315638888734186 -0 5 0.00673794699908547 -0 6 0.0024787521766663594 -0 7 0.0009118819655545166 -0 8 0.00033546262790251196 -0 9 0.0001234098040866796 +0 1 0.368 +0 2 0.135 +0 3 0.05 +0 4 0.018 +0 5 0.007 +0 6 0.002 +0 7 0.001 +0 8 0 +0 9 0 1 0 1 0 1 0.905 0 2 0.819 @@ -258,16 +260,17 @@ 0 47 0.136 ██████▋ 0 48 0.05 ██▌ 0 49 0.018 ▊ +exponentialTimeDecayedMax 1 0 1 -0 1 0.36787944117144233 -0 2 0.1353352832366127 -0 3 0.04978706836786395 -0 4 0.018315638888734186 -0 5 0.00673794699908547 -0 6 0.0024787521766663594 -0 7 0.0009118819655545166 -0 8 0.00033546262790251196 -0 9 0.0001234098040866796 +0 1 0.368 +0 2 0.135 +0 3 0.05 +0 4 0.018 +0 5 0.007 +0 6 0.002 +0 7 0.001 +0 8 0 +0 9 0 1 0 1 0 1 0.905 0 2 0.819 @@ -388,16 +391,17 @@ 0 47 0.135 ██████▋ 0 48 0.05 ██▍ 0 49 0.018 ▊ +exponentialTimeDecayedCount 1 0 1 -0 1 1.3678794411714423 -0 2 1.5032147244080551 -0 3 1.553001792775919 -0 4 1.5713174316646532 -0 5 1.5780553786637386 -0 6 1.5805341308404048 -0 7 1.5814460128059595 -0 8 1.581781475433862 -0 9 1.5819048852379487 +0 1 1.368 +0 2 1.503 +0 3 1.553 +0 4 1.571 +0 5 1.578 +0 6 1.581 +0 7 1.581 +0 8 1.582 +0 9 1.582 1 0 1 0 1 1.905 0 2 2.724 @@ -518,16 +522,17 @@ 0 47 10.422 ██████████████████████████ 0 48 10.43 ██████████████████████████ 0 49 10.438 ██████████████████████████ +exponentialTimeDecayedAvg 1 0 1 -0 1 0.2689414213699951 -0 2 0.09003057317038046 -0 3 0.032058603280084995 -0 4 0.01165623095603961 -0 5 0.004269778545282112 -0 6 0.0015683003158864733 -0 7 0.000576612769687006 -0 8 0.00021207899644323433 -0 9 0.00007801341612780745 +0 1 0.269 +0 2 0.09 +0 3 0.032 +0 4 0.012 +0 5 0.004 +0 6 0.002 +0 7 0.001 +0 8 0 +0 9 0 1 0 1 0 1 0.475 0 2 0.301 @@ -648,3 +653,24 @@ 0 47 0.206 ████████████████████▋ 0 48 0.201 ████████████████████ 0 49 0.196 ███████████████████▌ +Check `exponentialTimeDecayed.*` supports sliding windows +2 1 3.010050167084 2 3.030251507111 0.993333444442 +1 2 7.060905027605 4.080805360107 4.02030134086 1.756312382816 +0 3 12.091654548833 5.101006700134 5.000500014167 2.418089094006 +4 4 11.050650848754 5.050250835421 5.000500014167 2.209909172572 +5 5 9.970249502081 5 5.000500014167 1.993850509716 +1 6 20.07305726224 10.202013400268 5.000500014167 4.014210020072 +0 7 15.991544871125 10.100501670842 3.98029867414 4.017674596889 +10 8 10.980198673307 10 2.970248507056 3.696727276261 +Check `exponentialTimeDecayedMax` works with negative values +2 1 -1.010050167084 +1 2 -1 +10 3 -0.990049833749 +4 4 -0.980198673307 +5 5 -1.010050167084 +1 6 -1 +10 7 -0.990049833749 +10 8 -0.980198673307 +10 9 -9.801986733068 +9.81 10 -9.801986733068 +9.9 11 -9.712388869079 diff --git a/tests/queries/0_stateless/02020_exponential_smoothing.sql b/tests/queries/0_stateless/02020_exponential_smoothing.sql index a39b09a883da..462081b12d6c 100644 --- a/tests/queries/0_stateless/02020_exponential_smoothing.sql +++ b/tests/queries/0_stateless/02020_exponential_smoothing.sql @@ -1,5 +1,6 @@ --- exponentialMovingAverage -SELECT number = 0 AS value, number AS time, exponentialMovingAverage(1)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10); +SELECT 'exponentialMovingAverage'; + +SELECT value, time, round(exp_smooth, 3) FROM (SELECT number = 0 AS value, number AS time, exponentialMovingAverage(1)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10)); SELECT value, time, round(exp_smooth, 3) FROM (SELECT number = 0 AS value, number AS time, exponentialMovingAverage(10)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10)); SELECT number AS value, number AS time, exponentialMovingAverage(1)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10); @@ -32,8 +33,9 @@ FROM FROM numbers(50) ); --- exponentialTimeDecayedSum -SELECT number = 0 AS value, number AS time, exponentialTimeDecayedSum(1)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10); +SELECT 'exponentialTimeDecayedSum'; + +SELECT value, time, round(exp_smooth, 3) FROM (SELECT number = 0 AS value, number AS time, exponentialTimeDecayedSum(1)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10)); SELECT value, time, round(exp_smooth, 3) FROM (SELECT number = 0 AS value, number AS time, exponentialTimeDecayedSum(10)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10)); SELECT number AS value, number AS time, exponentialTimeDecayedSum(1)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10); @@ -66,8 +68,9 @@ FROM FROM numbers(50) ); --- exponentialTimeDecayedMax -SELECT number = 0 AS value, number AS time, exponentialTimeDecayedMax(1)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10); +SELECT 'exponentialTimeDecayedMax'; + +SELECT value, time, round(exp_smooth, 3) FROM (SELECT number = 0 AS value, number AS time, exponentialTimeDecayedMax(1)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10)); SELECT value, time, round(exp_smooth, 3) FROM (SELECT number = 0 AS value, number AS time, exponentialTimeDecayedMax(10)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10)); SELECT number AS value, number AS time, exponentialTimeDecayedMax(1)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10); @@ -100,8 +103,9 @@ FROM FROM numbers(50) ); --- exponentialTimeDecayedCount -SELECT number = 0 AS value, number AS time, exponentialTimeDecayedCount(1)(time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10); +SELECT 'exponentialTimeDecayedCount'; + +SELECT value, time, round(exp_smooth, 3) FROM (SELECT number = 0 AS value, number AS time, exponentialTimeDecayedCount(1)(time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10)); SELECT value, time, round(exp_smooth, 3) FROM (SELECT number = 0 AS value, number AS time, exponentialTimeDecayedCount(10)(time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10)); SELECT number AS value, number AS time, exponentialTimeDecayedCount(1)(time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10); @@ -134,8 +138,9 @@ FROM FROM numbers(50) ); --- exponentialTimeDecayedAvg -SELECT number = 0 AS value, number AS time, exponentialTimeDecayedAvg(1)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10); +SELECT 'exponentialTimeDecayedAvg'; + +SELECT value, time, round(exp_smooth, 3) FROM (SELECT number = 0 AS value, number AS time, exponentialTimeDecayedAvg(1)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10)); SELECT value, time, round(exp_smooth, 3) FROM (SELECT number = 0 AS value, number AS time, exponentialTimeDecayedAvg(10)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10)); SELECT number AS value, number AS time, exponentialTimeDecayedAvg(1)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10); @@ -167,3 +172,70 @@ FROM exponentialTimeDecayedAvg(100)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(50) ); + +SELECT 'Check `exponentialTimeDecayed.*` supports sliding windows'; + +SELECT + x, + t, + round(sum, 12), + round(max, 12), + round(count, 12), + round(avg, 12) +FROM +( + SELECT + d[1] AS x, + d[2] AS t, + exponentialTimeDecayedSum(100)(x, t) OVER w AS sum, + exponentialTimeDecayedMax(100)(x, t) OVER w AS max, + exponentialTimeDecayedCount(100)(t) OVER w AS count, + exponentialTimeDecayedAvg(100)(x, t) OVER w AS avg + FROM + ( + SELECT [[2, 1], [1, 2], [0, 3], [4, 4], [5, 5], [1, 6], [0, 7], [10, 8]] AS d + ) + ARRAY JOIN d + WINDOW w AS (ORDER BY 1 ASC Rows BETWEEN 2 PRECEDING AND 2 FOLLOWING) +); + +SELECT + x, + t, + round(sum, 12), + round(max, 12), + round(count, 12), + round(avg, 12) +FROM +( + SELECT + sin(number) AS x, + number AS t, + exponentialTimeDecayedSum(100)(x, t) OVER w AS sum, + exponentialTimeDecayedMax(100)(x, t) OVER w AS max, + exponentialTimeDecayedCount(100)(t) OVER w AS count, + exponentialTimeDecayedAvg(100)(x, t) OVER w AS avg + FROM numbers(1000000) + WINDOW w AS (ORDER BY 1 ASC Rows BETWEEN 2 PRECEDING AND 2 FOLLOWING) +) +FORMAT `Null`; + +SELECT 'Check `exponentialTimeDecayedMax` works with negative values'; + +SELECT + x, + t, + round(max, 12) +FROM +( + SELECT + d[1] AS x, + d[2] AS t, + exponentialTimeDecayedMax(100)(-x, t) OVER w AS max + FROM + ( + SELECT [[2, 1], [1, 2], [10, 3], [4, 4], [5, 5], [1, 6], [10, 7], [10, 8], [10, 9], [9.81, 10], [9.9, 11]] AS d + ) + ARRAY JOIN d + WINDOW w AS (ORDER BY 1 ASC Rows BETWEEN 2 PRECEDING AND 2 FOLLOWING) +); From 1f52a06ce2d5fdb2480107ef45f539b21af64ed3 Mon Sep 17 00:00:00 2001 From: Dmitry Novik Date: Mon, 20 Jun 2022 20:03:26 +0200 Subject: [PATCH 03/72] Merge pull request #34632 from excitoon-favorites/optimizedprocessing --- src/Core/Settings.h | 1 + src/Interpreters/ExpressionAnalyzer.cpp | 14 +- src/Interpreters/ExpressionAnalyzer.h | 1 + src/Interpreters/InterpreterSelectQuery.cpp | 8 +- src/Interpreters/InterpreterSelectQuery.h | 3 + src/Interpreters/WindowDescription.h | 4 + .../QueryPlan/Optimizations/Optimizations.h | 9 +- ...reuseStorageOrderingForWindowFunctions.cpp | 122 ++++++++++++++++++ .../QueryPlan/ReadFromMergeTree.cpp | 26 +++- src/Processors/QueryPlan/ReadFromMergeTree.h | 7 + src/Processors/QueryPlan/SortingStep.cpp | 6 + src/Processors/QueryPlan/SortingStep.h | 2 + src/Processors/QueryPlan/WindowStep.cpp | 5 + src/Processors/QueryPlan/WindowStep.h | 2 + ...ns_optimize_read_in_window_order.reference | 12 ++ ...mizations_optimize_read_in_window_order.sh | 36 ++++++ ...timize_read_in_window_order_long.reference | 4 + ...ions_optimize_read_in_window_order_long.sh | 35 +++++ 18 files changed, 287 insertions(+), 10 deletions(-) create mode 100644 src/Processors/QueryPlan/Optimizations/reuseStorageOrderingForWindowFunctions.cpp create mode 100644 tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.reference create mode 100755 tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.sh create mode 100644 tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order_long.reference create mode 100755 tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order_long.sh diff --git a/src/Core/Settings.h b/src/Core/Settings.h index 0c69b864d528..b40d88f769f0 100644 --- a/src/Core/Settings.h +++ b/src/Core/Settings.h @@ -396,6 +396,7 @@ class IColumn; M(Bool, parallel_view_processing, false, "Enables pushing to attached views concurrently instead of sequentially.", 0) \ M(Bool, enable_unaligned_array_join, false, "Allow ARRAY JOIN with multiple arrays that have different sizes. When this settings is enabled, arrays will be resized to the longest one.", 0) \ M(Bool, optimize_read_in_order, true, "Enable ORDER BY optimization for reading data in corresponding order in MergeTree tables.", 0) \ + M(Bool, optimize_read_in_window_order, true, "Enable ORDER BY optimization in window clause for reading data in corresponding order in MergeTree tables.", 0) \ M(Bool, optimize_aggregation_in_order, false, "Enable GROUP BY optimization for aggregating data in corresponding order in MergeTree tables.", 0) \ M(UInt64, aggregation_in_order_max_block_bytes, 50000000, "Maximal size of block in bytes accumulated during aggregation in order of primary key. Lower block size allows to parallelize more final merge stage of aggregation.", 0) \ M(UInt64, read_in_order_two_level_merge_threshold, 100, "Minimal number of parts to read to run preliminary merge step during multithread reading in order of primary key.", 0) \ diff --git a/src/Interpreters/ExpressionAnalyzer.cpp b/src/Interpreters/ExpressionAnalyzer.cpp index 841d7bc567f9..2cf2a17fe1f3 100644 --- a/src/Interpreters/ExpressionAnalyzer.cpp +++ b/src/Interpreters/ExpressionAnalyzer.cpp @@ -607,7 +607,7 @@ void ExpressionAnalyzer::makeAggregateDescriptions(ActionsDAGPtr & actions, Aggr } } -void makeWindowDescriptionFromAST(const Context & context, +void ExpressionAnalyzer::makeWindowDescriptionFromAST(const Context & context_, const WindowDescriptions & existing_descriptions, WindowDescription & desc, const IAST * ast) { @@ -676,6 +676,10 @@ void makeWindowDescriptionFromAST(const Context & context, desc.partition_by.push_back(SortColumnDescription( with_alias->getColumnName(), 1 /* direction */, 1 /* nulls_direction */)); + + auto actions_dag = std::make_shared(columns_after_join); + getRootActions(column_ast, false, actions_dag); + desc.partition_by_actions.push_back(std::move(actions_dag)); } } @@ -693,6 +697,10 @@ void makeWindowDescriptionFromAST(const Context & context, order_by_element.children.front()->getColumnName(), order_by_element.direction, order_by_element.nulls_direction)); + + auto actions_dag = std::make_shared(columns_after_join); + getRootActions(column_ast, false, actions_dag); + desc.order_by_actions.push_back(std::move(actions_dag)); } } @@ -719,14 +727,14 @@ void makeWindowDescriptionFromAST(const Context & context, if (definition.frame_end_type == WindowFrame::BoundaryType::Offset) { auto [value, _] = evaluateConstantExpression(definition.frame_end_offset, - context.shared_from_this()); + context_.shared_from_this()); desc.frame.end_offset = value; } if (definition.frame_begin_type == WindowFrame::BoundaryType::Offset) { auto [value, _] = evaluateConstantExpression(definition.frame_begin_offset, - context.shared_from_this()); + context_.shared_from_this()); desc.frame.begin_offset = value; } } diff --git a/src/Interpreters/ExpressionAnalyzer.h b/src/Interpreters/ExpressionAnalyzer.h index 8736f6548e34..52a9f9d9ff4d 100644 --- a/src/Interpreters/ExpressionAnalyzer.h +++ b/src/Interpreters/ExpressionAnalyzer.h @@ -132,6 +132,7 @@ class ExpressionAnalyzer : protected ExpressionAnalyzerData, private boost::nonc /// A list of windows for window functions. const WindowDescriptions & windowDescriptions() const { return window_descriptions; } + void makeWindowDescriptionFromAST(const Context & context, const WindowDescriptions & existing_descriptions, WindowDescription & desc, const IAST * ast); void makeWindowDescriptions(ActionsDAGPtr actions); /** diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 35d20be46553..ffa094a82ae5 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -811,7 +811,7 @@ static FillColumnDescription getWithFillDescription(const ASTOrderByElement & or return descr; } -static SortDescription getSortDescription(const ASTSelectQuery & query, ContextPtr context) +SortDescription InterpreterSelectQuery::getSortDescription(const ASTSelectQuery & query, ContextPtr context_) { SortDescription order_descr; order_descr.reserve(query.orderBy()->children.size()); @@ -826,7 +826,7 @@ static SortDescription getSortDescription(const ASTSelectQuery & query, ContextP if (order_by_elem.with_fill) { - FillColumnDescription fill_desc = getWithFillDescription(order_by_elem, context); + FillColumnDescription fill_desc = getWithFillDescription(order_by_elem, context_); order_descr.emplace_back(name, order_by_elem.direction, order_by_elem.nulls_direction, collator, true, fill_desc); } else @@ -885,12 +885,12 @@ static std::pair getLimitLengthAndOffset(const ASTSelectQuery & } -static UInt64 getLimitForSorting(const ASTSelectQuery & query, ContextPtr context) +UInt64 InterpreterSelectQuery::getLimitForSorting(const ASTSelectQuery & query, ContextPtr context_) { /// Partial sort can be done if there is LIMIT but no DISTINCT or LIMIT BY, neither ARRAY JOIN. if (!query.distinct && !query.limitBy() && !query.limit_with_ties && !query.arrayJoinExpressionList().first && query.limitLength()) { - auto [limit_length, limit_offset] = getLimitLengthAndOffset(query, context); + auto [limit_length, limit_offset] = getLimitLengthAndOffset(query, context_); if (limit_length > std::numeric_limits::max() - limit_offset) return 0; diff --git a/src/Interpreters/InterpreterSelectQuery.h b/src/Interpreters/InterpreterSelectQuery.h index aa41d8376013..0eb6ae06e82f 100644 --- a/src/Interpreters/InterpreterSelectQuery.h +++ b/src/Interpreters/InterpreterSelectQuery.h @@ -106,6 +106,9 @@ class InterpreterSelectQuery : public IInterpreterUnionOrSelectQuery Names getRequiredColumns() { return required_columns; } + static SortDescription getSortDescription(const ASTSelectQuery & query, ContextPtr context); + static UInt64 getLimitForSorting(const ASTSelectQuery & query, ContextPtr context); + private: InterpreterSelectQuery( const ASTPtr & query_ptr_, diff --git a/src/Interpreters/WindowDescription.h b/src/Interpreters/WindowDescription.h index bb0130b4d4ee..65c8cb9423c9 100644 --- a/src/Interpreters/WindowDescription.h +++ b/src/Interpreters/WindowDescription.h @@ -7,6 +7,7 @@ #include #include #include +#include namespace DB { @@ -90,6 +91,9 @@ struct WindowDescription // then by ORDER BY. This field holds this combined sort order. SortDescription full_sort_description; + std::vector partition_by_actions; + std::vector order_by_actions; + WindowFrame frame; // The window functions that are calculated for this window. diff --git a/src/Processors/QueryPlan/Optimizations/Optimizations.h b/src/Processors/QueryPlan/Optimizations/Optimizations.h index 10bc62935371..11bdb1c95e50 100644 --- a/src/Processors/QueryPlan/Optimizations/Optimizations.h +++ b/src/Processors/QueryPlan/Optimizations/Optimizations.h @@ -44,16 +44,21 @@ size_t tryMergeExpressions(QueryPlan::Node * parent_node, QueryPlan::Nodes &); /// May split FilterStep and push down only part of it. size_t tryPushDownFilter(QueryPlan::Node * parent_node, QueryPlan::Nodes & nodes); +/// Utilize storage sorting when sorting for window functions. +/// Update information about prefix sort description in SortingStep. +size_t tryReuseStorageOrderingForWindowFunctions(QueryPlan::Node * parent_node, QueryPlan::Nodes & nodes); + inline const auto & getOptimizations() { - static const std::array optimizations = + static const std::array optimizations = {{ {tryLiftUpArrayJoin, "liftUpArrayJoin", &QueryPlanOptimizationSettings::optimize_plan}, {tryPushDownLimit, "pushDownLimit", &QueryPlanOptimizationSettings::optimize_plan}, {trySplitFilter, "splitFilter", &QueryPlanOptimizationSettings::optimize_plan}, {tryMergeExpressions, "mergeExpressions", &QueryPlanOptimizationSettings::optimize_plan}, {tryPushDownFilter, "pushDownFilter", &QueryPlanOptimizationSettings::filter_push_down}, - }}; + {tryReuseStorageOrderingForWindowFunctions, "reuseStorageOrderingForWindowFunctions", &QueryPlanOptimizationSettings::optimize_plan} + }}; return optimizations; } diff --git a/src/Processors/QueryPlan/Optimizations/reuseStorageOrderingForWindowFunctions.cpp b/src/Processors/QueryPlan/Optimizations/reuseStorageOrderingForWindowFunctions.cpp new file mode 100644 index 000000000000..c68ec47edff0 --- /dev/null +++ b/src/Processors/QueryPlan/Optimizations/reuseStorageOrderingForWindowFunctions.cpp @@ -0,0 +1,122 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace DB::QueryPlanOptimizations +{ + +size_t tryReuseStorageOrderingForWindowFunctions(QueryPlan::Node * parent_node, QueryPlan::Nodes & /*nodes*/) +{ + /// Find the following sequence of steps, add InputOrderInfo and apply prefix sort description to + /// SortingStep: + /// WindowStep <- SortingStep <- [Expression] <- [SettingQuotaAndLimits] <- ReadFromMergeTree + + auto * window_node = parent_node; + auto * window = typeid_cast(window_node->step.get()); + if (!window) + return 0; + if (window_node->children.size() != 1) + return 0; + + auto * sorting_node = window_node->children.front(); + auto * sorting = typeid_cast(sorting_node->step.get()); + if (!sorting) + return 0; + if (sorting_node->children.size() != 1) + return 0; + + auto * possible_read_from_merge_tree_node = sorting_node->children.front(); + + if (typeid_cast(possible_read_from_merge_tree_node->step.get())) + { + if (possible_read_from_merge_tree_node->children.size() != 1) + return 0; + + possible_read_from_merge_tree_node = possible_read_from_merge_tree_node->children.front(); + } + + if (typeid_cast(possible_read_from_merge_tree_node->step.get())) + { + if (possible_read_from_merge_tree_node->children.size() != 1) + return 0; + + possible_read_from_merge_tree_node = possible_read_from_merge_tree_node->children.front(); + } + + auto * read_from_merge_tree = typeid_cast(possible_read_from_merge_tree_node->step.get()); + if (!read_from_merge_tree) + { + return 0; + } + + auto context = read_from_merge_tree->getContext(); + if (!context->getSettings().optimize_read_in_window_order) + { + return 0; + } + + const auto & query_info = read_from_merge_tree->getQueryInfo(); + const auto * select_query = query_info.query->as(); + + ManyExpressionActions order_by_elements_actions; + const auto & window_desc = window->getWindowDescription(); + + for (const auto & actions_dag : window_desc.partition_by_actions) + { + order_by_elements_actions.emplace_back( + std::make_shared(actions_dag, ExpressionActionsSettings::fromContext(context, CompileExpressions::yes))); + } + + for (const auto & actions_dag : window_desc.order_by_actions) + { + order_by_elements_actions.emplace_back( + std::make_shared(actions_dag, ExpressionActionsSettings::fromContext(context, CompileExpressions::yes))); + } + + auto order_optimizer = std::make_shared( + *select_query, + order_by_elements_actions, + window->getWindowDescription().full_sort_description, + query_info.syntax_analyzer_result); + + read_from_merge_tree->setQueryInfoOrderOptimizer(order_optimizer); + + /// If we don't have filtration, we can pushdown limit to reading stage for optimizations. + UInt64 limit = (select_query->hasFiltration() || select_query->groupBy()) ? 0 : InterpreterSelectQuery::getLimitForSorting(*select_query, context); + + auto order_info = order_optimizer->getInputOrder( + query_info.projection ? query_info.projection->desc->metadata : read_from_merge_tree->getStorageMetadata(), + context, + limit); + + if (order_info) + { + read_from_merge_tree->setQueryInfoInputOrderInfo(order_info); + sorting->convertToFinishSorting(order_info->order_key_prefix_descr); + } + + return 0; +} + +} diff --git a/src/Processors/QueryPlan/ReadFromMergeTree.cpp b/src/Processors/QueryPlan/ReadFromMergeTree.cpp index 1bfc1ec7306f..abc753c5fa7b 100644 --- a/src/Processors/QueryPlan/ReadFromMergeTree.cpp +++ b/src/Processors/QueryPlan/ReadFromMergeTree.cpp @@ -977,6 +977,30 @@ MergeTreeDataSelectAnalysisResultPtr ReadFromMergeTree::selectRangesToRead( return std::make_shared(MergeTreeDataSelectAnalysisResult{.result = std::move(result)}); } +void ReadFromMergeTree::setQueryInfoOrderOptimizer(std::shared_ptr order_optimizer) +{ + if (query_info.projection) + { + query_info.projection->order_optimizer = order_optimizer; + } + else + { + query_info.order_optimizer = order_optimizer; + } +} + +void ReadFromMergeTree::setQueryInfoInputOrderInfo(InputOrderInfoPtr order_info) +{ + if (query_info.projection) + { + query_info.projection->input_order_info = order_info; + } + else + { + query_info.input_order_info = order_info; + } +} + ReadFromMergeTree::AnalysisResult ReadFromMergeTree::getAnalysisResult() const { auto result_ptr = analyzed_result_ptr ? analyzed_result_ptr : selectRangesToRead(prepared_parts); @@ -1060,7 +1084,7 @@ void ReadFromMergeTree::initializePipeline(QueryPipelineBuilder & pipeline, cons column_names_to_read, result_projection); } - else if ((settings.optimize_read_in_order || settings.optimize_aggregation_in_order) && input_order_info) + else if ((settings.optimize_read_in_order || settings.optimize_aggregation_in_order || settings.optimize_read_in_window_order) && input_order_info) { pipe = spreadMarkRangesAmongStreamsWithOrder( std::move(result.parts_with_ranges), diff --git a/src/Processors/QueryPlan/ReadFromMergeTree.h b/src/Processors/QueryPlan/ReadFromMergeTree.h index 685b99a7bdcb..16333edcaf3b 100644 --- a/src/Processors/QueryPlan/ReadFromMergeTree.h +++ b/src/Processors/QueryPlan/ReadFromMergeTree.h @@ -128,6 +128,13 @@ class ReadFromMergeTree final : public ISourceStep bool sample_factor_column_queried, Poco::Logger * log); + ContextPtr getContext() const { return context; } + const SelectQueryInfo & getQueryInfo() const { return query_info; } + StorageMetadataPtr getStorageMetadata() const { return metadata_for_reading; } + + void setQueryInfoOrderOptimizer(std::shared_ptr read_in_order_optimizer); + void setQueryInfoInputOrderInfo(InputOrderInfoPtr order_info); + private: const MergeTreeReaderSettings reader_settings; diff --git a/src/Processors/QueryPlan/SortingStep.cpp b/src/Processors/QueryPlan/SortingStep.cpp index 32b314b1c505..602680e17182 100644 --- a/src/Processors/QueryPlan/SortingStep.cpp +++ b/src/Processors/QueryPlan/SortingStep.cpp @@ -97,6 +97,12 @@ void SortingStep::updateLimit(size_t limit_) } } +void SortingStep::convertToFinishSorting(SortDescription prefix_description_) +{ + type = Type::FinishSorting; + prefix_description = std::move(prefix_description_); +} + void SortingStep::transformPipeline(QueryPipelineBuilder & pipeline, const BuildQueryPipelineSettings &) { if (type == Type::FinishSorting) diff --git a/src/Processors/QueryPlan/SortingStep.h b/src/Processors/QueryPlan/SortingStep.h index 8e253e71f441..4da98a15f65d 100644 --- a/src/Processors/QueryPlan/SortingStep.h +++ b/src/Processors/QueryPlan/SortingStep.h @@ -49,6 +49,8 @@ class SortingStep : public ITransformingStep /// Add limit or change it to lower value. void updateLimit(size_t limit_); + void convertToFinishSorting(SortDescription prefix_description); + private: enum class Type diff --git a/src/Processors/QueryPlan/WindowStep.cpp b/src/Processors/QueryPlan/WindowStep.cpp index cd4bb5f67307..0916b43b29a5 100644 --- a/src/Processors/QueryPlan/WindowStep.cpp +++ b/src/Processors/QueryPlan/WindowStep.cpp @@ -138,4 +138,9 @@ void WindowStep::describeActions(JSONBuilder::JSONMap & map) const map.add("Functions", std::move(functions_array)); } +const WindowDescription & WindowStep::getWindowDescription() const +{ + return window_description; +} + } diff --git a/src/Processors/QueryPlan/WindowStep.h b/src/Processors/QueryPlan/WindowStep.h index a65b157f4817..9b58cceb972b 100644 --- a/src/Processors/QueryPlan/WindowStep.h +++ b/src/Processors/QueryPlan/WindowStep.h @@ -25,6 +25,8 @@ class WindowStep : public ITransformingStep void describeActions(JSONBuilder::JSONMap & map) const override; void describeActions(FormatSettings & settings) const override; + const WindowDescription & getWindowDescription() const; + private: WindowDescription window_description; std::vector window_functions; diff --git a/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.reference b/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.reference new file mode 100644 index 000000000000..7fcd29b5faf9 --- /dev/null +++ b/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.reference @@ -0,0 +1,12 @@ +Partial sorting plan + optimize_read_in_window_order=0 + Sort description: n ASC, x ASC + optimize_read_in_window_order=1 + Prefix sort description: n ASC + Result sort description: n ASC, x ASC +No sorting plan + optimize_read_in_window_order=0 + Sort description: n ASC, x ASC + optimize_read_in_window_order=1 + Prefix sort description: n ASC, x ASC + Result sort description: n ASC, x ASC diff --git a/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.sh b/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.sh new file mode 100755 index 000000000000..418baea81136 --- /dev/null +++ b/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +# shellcheck source=../shell_config.sh +. "$CURDIR"/../shell_config.sh + +name=test_01655_plan_optimizations_optimize_read_in_window_order + +$CLICKHOUSE_CLIENT -q "drop table if exists ${name}" +$CLICKHOUSE_CLIENT -q "drop table if exists ${name}_n" +$CLICKHOUSE_CLIENT -q "drop table if exists ${name}_n_x" + +$CLICKHOUSE_CLIENT -q "create table ${name} engine=MergeTree order by tuple() as select toInt64((sin(number)+2)*65535)%10 as n, number as x from numbers_mt(100000)" +$CLICKHOUSE_CLIENT -q "create table ${name}_n engine=MergeTree order by n as select * from ${name} order by n" +$CLICKHOUSE_CLIENT -q "create table ${name}_n_x engine=MergeTree order by (n, x) as select * from ${name} order by n, x" + +$CLICKHOUSE_CLIENT -q "optimize table ${name}_n final" +$CLICKHOUSE_CLIENT -q "optimize table ${name}_n_x final" + +echo 'Partial sorting plan' +echo ' optimize_read_in_window_order=0' +$CLICKHOUSE_CLIENT -q "explain plan actions=1, description=1 select n, sum(x) OVER (ORDER BY n, x ROWS BETWEEN 100 PRECEDING AND CURRENT ROW) from ${name}_n SETTINGS optimize_read_in_window_order=0" | grep -i "sort description" + +echo ' optimize_read_in_window_order=1' +$CLICKHOUSE_CLIENT -q "explain plan actions=1, description=1 select n, sum(x) OVER (ORDER BY n, x ROWS BETWEEN 100 PRECEDING AND CURRENT ROW) from ${name}_n SETTINGS optimize_read_in_window_order=1" | grep -i "sort description" + +echo 'No sorting plan' +echo ' optimize_read_in_window_order=0' +$CLICKHOUSE_CLIENT -q "explain plan actions=1, description=1 select n, sum(x) OVER (ORDER BY n, x ROWS BETWEEN 100 PRECEDING AND CURRENT ROW) from ${name}_n_x SETTINGS optimize_read_in_window_order=0" | grep -i "sort description" + +echo ' optimize_read_in_window_order=1' +$CLICKHOUSE_CLIENT -q "explain plan actions=1, description=1 select n, sum(x) OVER (ORDER BY n, x ROWS BETWEEN 100 PRECEDING AND CURRENT ROW) from ${name}_n_x SETTINGS optimize_read_in_window_order=1" | grep -i "sort description" + +$CLICKHOUSE_CLIENT -q "drop table ${name}" +$CLICKHOUSE_CLIENT -q "drop table ${name}_n" +$CLICKHOUSE_CLIENT -q "drop table ${name}_n_x" diff --git a/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order_long.reference b/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order_long.reference new file mode 100644 index 000000000000..b462a5a7baa4 --- /dev/null +++ b/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order_long.reference @@ -0,0 +1,4 @@ +OK +OK +OK +OK diff --git a/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order_long.sh b/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order_long.sh new file mode 100755 index 000000000000..297688a29c32 --- /dev/null +++ b/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order_long.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# Tags: long + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +# shellcheck source=../shell_config.sh +. "$CURDIR"/../shell_config.sh + +name=test_01655_plan_optimizations_optimize_read_in_window_order_long +max_memory_usage=20000000 + +$CLICKHOUSE_CLIENT -q "drop table if exists ${name}" +$CLICKHOUSE_CLIENT -q "drop table if exists ${name}_n" +$CLICKHOUSE_CLIENT -q "drop table if exists ${name}_n_x" + +$CLICKHOUSE_CLIENT -q "create table ${name} engine=MergeTree order by tuple() as select toInt64((sin(number)+2)*65535)%500 as n, number as x from numbers_mt(5000000)" +$CLICKHOUSE_CLIENT -q "create table ${name}_n engine=MergeTree order by n as select * from ${name} order by n" +$CLICKHOUSE_CLIENT -q "create table ${name}_n_x engine=MergeTree order by (n, x) as select * from ${name} order by n, x" + +$CLICKHOUSE_CLIENT -q "optimize table ${name}_n final" +$CLICKHOUSE_CLIENT -q "optimize table ${name}_n_x final" + +$CLICKHOUSE_CLIENT -q "select n, sum(x) OVER (ORDER BY n, x ROWS BETWEEN 100 PRECEDING AND CURRENT ROW) from ${name}_n SETTINGS optimize_read_in_window_order=0, max_memory_usage=$max_memory_usage, max_threads=1 format Null" 2>&1 | grep -F -q "MEMORY_LIMIT_EXCEEDED" && echo 'OK' || echo 'FAIL' +$CLICKHOUSE_CLIENT -q "select n, sum(x) OVER (ORDER BY n, x ROWS BETWEEN 100 PRECEDING AND CURRENT ROW) from ${name}_n SETTINGS optimize_read_in_window_order=1, max_memory_usage=$max_memory_usage, max_threads=1 format Null" + +$CLICKHOUSE_CLIENT -q "select n, sum(x) OVER (ORDER BY n, x ROWS BETWEEN 100 PRECEDING AND CURRENT ROW) from ${name}_n_x SETTINGS optimize_read_in_window_order=0, max_memory_usage=$max_memory_usage, max_threads=1 format Null" 2>&1 | grep -F -q "MEMORY_LIMIT_EXCEEDED" && echo 'OK' || echo 'FAIL' +$CLICKHOUSE_CLIENT -q "select n, sum(x) OVER (ORDER BY n, x ROWS BETWEEN 100 PRECEDING AND CURRENT ROW) from ${name}_n_x SETTINGS optimize_read_in_window_order=1, max_memory_usage=$max_memory_usage, max_threads=1 format Null" + +$CLICKHOUSE_CLIENT -q "select n, sum(x) OVER (PARTITION BY n ORDER BY x ROWS BETWEEN 100 PRECEDING AND CURRENT ROW) from ${name}_n_x SETTINGS optimize_read_in_window_order=0, max_memory_usage=$max_memory_usage, max_threads=1 format Null" 2>&1 | grep -F -q "MEMORY_LIMIT_EXCEEDED" && echo 'OK' || echo 'FAIL' +$CLICKHOUSE_CLIENT -q "select n, sum(x) OVER (PARTITION BY n ORDER BY x ROWS BETWEEN 100 PRECEDING AND CURRENT ROW) from ${name}_n_x SETTINGS optimize_read_in_window_order=1, max_memory_usage=$max_memory_usage, max_threads=1 format Null" + +$CLICKHOUSE_CLIENT -q "select n, sum(x) OVER (PARTITION BY n+x%2 ORDER BY n, x ROWS BETWEEN 100 PRECEDING AND CURRENT ROW) from ${name}_n_x SETTINGS optimize_read_in_window_order=1, max_memory_usage=$max_memory_usage, max_threads=1 format Null" 2>&1 | grep -F -q "MEMORY_LIMIT_EXCEEDED" && echo 'OK' || echo 'FAIL' + +$CLICKHOUSE_CLIENT -q "drop table ${name}" +$CLICKHOUSE_CLIENT -q "drop table ${name}_n" +$CLICKHOUSE_CLIENT -q "drop table ${name}_n_x" From be4c872c7c2962b6083132bae14280fcb8f64d43 Mon Sep 17 00:00:00 2001 From: alesapin Date: Mon, 13 Jun 2022 13:39:01 +0200 Subject: [PATCH 04/72] Merge pull request #37659 from frew/master Support `batch_delete` capability for GCS --- src/Disks/S3/DiskS3.cpp | 37 +++++++++++++++++++++++++-------- src/Disks/S3/DiskS3.h | 3 +++ src/Disks/S3/S3Capabilities.cpp | 15 +++++++++++++ src/Disks/S3/S3Capabilities.h | 27 ++++++++++++++++++++++++ src/Disks/S3/registerDiskS3.cpp | 2 ++ 5 files changed, 75 insertions(+), 9 deletions(-) create mode 100644 src/Disks/S3/S3Capabilities.cpp create mode 100644 src/Disks/S3/S3Capabilities.h diff --git a/src/Disks/S3/DiskS3.cpp b/src/Disks/S3/DiskS3.cpp index e46620d9d1f0..bf5d08370902 100644 --- a/src/Disks/S3/DiskS3.cpp +++ b/src/Disks/S3/DiskS3.cpp @@ -35,6 +35,7 @@ #include #include +#include #include #include #include @@ -155,12 +156,14 @@ DiskS3::DiskS3( DiskPtr metadata_disk_, FileCachePtr cache_, ContextPtr context_, + const S3Capabilities & s3_capabilities_, SettingsPtr settings_, GetDiskSettings settings_getter_) : IDiskRemote(name_, s3_root_path_, metadata_disk_, std::move(cache_), "DiskS3", settings_->thread_pool_size) , bucket(std::move(bucket_)) , current_settings(std::move(settings_)) , settings_getter(settings_getter_) + , s3_capabilities(s3_capabilities_) , context(context_) { } @@ -180,15 +183,31 @@ void DiskS3::removeFromRemoteFS(RemoteFSPathKeeperPtr fs_paths_keeper) s3_paths_keeper->removePaths([&](S3PathKeeper::Chunk && chunk) { String keys = S3PathKeeper::getChunkKeys(chunk); - LOG_TRACE(log, "Remove AWS keys {}", keys); - Aws::S3::Model::Delete delkeys; - delkeys.SetObjects(chunk); - Aws::S3::Model::DeleteObjectsRequest request; - request.SetBucket(bucket); - request.SetDelete(delkeys); - auto outcome = settings->client->DeleteObjects(request); - // Do not throw here, continue deleting other chunks - logIfError(outcome, [&](){return "Can't remove AWS keys: " + keys;}); + if (!s3_capabilities.support_batch_delete) + { + LOG_TRACE(log, "Remove AWS keys {} one by one", keys); + for (const auto & obj : chunk) + { + Aws::S3::Model::DeleteObjectRequest request; + request.SetBucket(bucket); + request.SetKey(obj.GetKey()); + auto outcome = settings->client->DeleteObject(request); + // Do not throw here, continue deleting other keys and chunks + logIfError(outcome, [&](){return "Can't remove AWS key: " + obj.GetKey();}); + } + } + else + { + LOG_TRACE(log, "Remove AWS keys {}", keys); + Aws::S3::Model::Delete delkeys; + delkeys.SetObjects(chunk); + Aws::S3::Model::DeleteObjectsRequest request; + request.SetBucket(bucket); + request.SetDelete(delkeys); + auto outcome = settings->client->DeleteObjects(request); + // Do not throw here, continue deleting other chunks + logIfError(outcome, [&](){return "Can't remove AWS keys: " + keys;}); + } }); } diff --git a/src/Disks/S3/DiskS3.h b/src/Disks/S3/DiskS3.h index 2de1600d906f..4d24fd071acc 100644 --- a/src/Disks/S3/DiskS3.h +++ b/src/Disks/S3/DiskS3.h @@ -9,6 +9,7 @@ #include #include "Disks/DiskFactory.h" #include "Disks/Executor.h" +#include #include #include @@ -76,6 +77,7 @@ class DiskS3 final : public IDiskRemote DiskPtr metadata_disk_, FileCachePtr cache_, ContextPtr context_, + const S3Capabilities & s3_capabilities_, SettingsPtr settings_, GetDiskSettings settings_getter_); @@ -166,6 +168,7 @@ class DiskS3 final : public IDiskRemote MultiVersion current_settings; /// Gets disk settings from context. GetDiskSettings settings_getter; + const S3Capabilities s3_capabilities; std::atomic revision_counter = 0; static constexpr UInt64 LATEST_REVISION = std::numeric_limits::max(); diff --git a/src/Disks/S3/S3Capabilities.cpp b/src/Disks/S3/S3Capabilities.cpp new file mode 100644 index 000000000000..f96f0b5539a6 --- /dev/null +++ b/src/Disks/S3/S3Capabilities.cpp @@ -0,0 +1,15 @@ +#include + +namespace DB +{ + +S3Capabilities getCapabilitiesFromConfig(const Poco::Util::AbstractConfiguration & config, const std::string & config_prefix) +{ + return S3Capabilities + { + .support_batch_delete = config.getBool(config_prefix + ".support_batch_delete", true), + .support_proxy = config.getBool(config_prefix + ".support_proxy", config.has(config_prefix + ".proxy")), + }; +} + +} diff --git a/src/Disks/S3/S3Capabilities.h b/src/Disks/S3/S3Capabilities.h new file mode 100644 index 000000000000..46e647da89e5 --- /dev/null +++ b/src/Disks/S3/S3Capabilities.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +namespace DB +{ + +/// Supported/unsupported features by different S3 implementations +/// Can be useful only for almost compatible with AWS S3 versions. +struct S3Capabilities +{ + /// Google S3 implementation doesn't support batch delete + /// TODO: possibly we have to use Google SDK https://github.com/googleapis/google-cloud-cpp/tree/main/google/cloud/storage + /// because looks like it miss a lot of features like: + /// 1) batch delete + /// 2) list_v2 + /// 3) multipart upload works differently + bool support_batch_delete{true}; + + /// Y.Cloud S3 implementation support proxy for connection + bool support_proxy{false}; +}; + +S3Capabilities getCapabilitiesFromConfig(const Poco::Util::AbstractConfiguration & config, const std::string & config_prefix); + +} diff --git a/src/Disks/S3/registerDiskS3.cpp b/src/Disks/S3/registerDiskS3.cpp index 2b5fe3c5a81b..96630ad72624 100644 --- a/src/Disks/S3/registerDiskS3.cpp +++ b/src/Disks/S3/registerDiskS3.cpp @@ -187,6 +187,7 @@ void registerDiskS3(DiskFactory & factory) auto [metadata_path, metadata_disk] = prepareForLocalMetadata(name, config, config_prefix, context); FileCachePtr cache = getCachePtrForDisk(name, config, config_prefix, context); + S3Capabilities s3_capabilities = getCapabilitiesFromConfig(config, config_prefix); std::shared_ptr s3disk = std::make_shared( name, @@ -195,6 +196,7 @@ void registerDiskS3(DiskFactory & factory) metadata_disk, std::move(cache), context, + s3_capabilities, getSettings(config, config_prefix, context), getSettings); From 600808b0789390db32b411163aa9aa1714f05df7 Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Tue, 12 Jul 2022 19:09:55 +0200 Subject: [PATCH 05/72] Merge pull request #37882 from excitoon-favorites/nodeleteobjects Fixes for objects removal in `S3ObjectStorage` --- src/Disks/IDiskRemote.h | 3 +- src/Disks/S3/DiskS3.cpp | 47 --------- src/Disks/S3/DiskS3.h | 57 ++++++++++- src/Disks/S3/registerDiskS3.cpp | 97 ++++++++++++++++++- .../configs/config.d/storage_conf.xml | 14 +++ .../s3_mocks/no_delete_objects.py | 92 ++++++++++++++++++ tests/integration/test_merge_tree_s3/test.py | 14 ++- 7 files changed, 272 insertions(+), 52 deletions(-) create mode 100644 tests/integration/test_merge_tree_s3/s3_mocks/no_delete_objects.py diff --git a/src/Disks/IDiskRemote.h b/src/Disks/IDiskRemote.h index 82e76b8f68d4..4c91400c94c5 100644 --- a/src/Disks/IDiskRemote.h +++ b/src/Disks/IDiskRemote.h @@ -165,9 +165,10 @@ friend class DiskRemoteReservation; DiskPtr metadata_disk; FileCachePtr cache; -private: +public: void removeMetadata(const String & path, RemoteFSPathKeeperPtr fs_paths_keeper); +private: void removeMetadataRecursive(const String & path, RemoteFSPathKeeperPtr fs_paths_keeper); bool tryReserve(UInt64 bytes); diff --git a/src/Disks/S3/DiskS3.cpp b/src/Disks/S3/DiskS3.cpp index bf5d08370902..723b1e7373c1 100644 --- a/src/Disks/S3/DiskS3.cpp +++ b/src/Disks/S3/DiskS3.cpp @@ -11,7 +11,6 @@ #include #include -#include #include #include @@ -58,52 +57,6 @@ namespace ErrorCodes extern const int LOGICAL_ERROR; } -/// Helper class to collect keys into chunks of maximum size (to prepare batch requests to AWS API) -/// see https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html -class S3PathKeeper : public RemoteFSPathKeeper -{ -public: - using Chunk = Aws::Vector; - using Chunks = std::list; - - explicit S3PathKeeper(size_t chunk_limit_) : RemoteFSPathKeeper(chunk_limit_) {} - - void addPath(const String & path) override - { - if (chunks.empty() || chunks.back().size() >= chunk_limit) - { - /// add one more chunk - chunks.push_back(Chunks::value_type()); - chunks.back().reserve(chunk_limit); - } - Aws::S3::Model::ObjectIdentifier obj; - obj.SetKey(path); - chunks.back().push_back(obj); - } - - void removePaths(Fn auto && remove_chunk_func) - { - for (auto & chunk : chunks) - remove_chunk_func(std::move(chunk)); - } - - static String getChunkKeys(const Chunk & chunk) - { - String res; - for (const auto & obj : chunk) - { - const auto & key = obj.GetKey(); - if (!res.empty()) - res.append(", "); - res.append(key.c_str(), key.size()); - } - return res; - } - -private: - Chunks chunks; -}; - template void throwIfError(Aws::Utils::Outcome & response) { diff --git a/src/Disks/S3/DiskS3.h b/src/Disks/S3/DiskS3.h index 4d24fd071acc..473dc6a7a751 100644 --- a/src/Disks/S3/DiskS3.h +++ b/src/Disks/S3/DiskS3.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "Disks/DiskFactory.h" #include "Disks/Executor.h" #include @@ -14,6 +15,7 @@ #include #include #include +#include #include #include @@ -121,6 +123,8 @@ class DiskS3 final : public IDiskRemote void applyNewSettings(const Poco::Util::AbstractConfiguration & config, ContextPtr context, const String &, const DisksMap &) override; + void setCapabilitiesSupportBatchDelete(bool value) { s3_capabilities.support_batch_delete = value; } + private: void createFileOperationObject(const String & operation_name, UInt64 revision, const ObjectMetadata & metadata); /// Converts revision to binary string with leading zeroes (64 bit). @@ -168,7 +172,7 @@ class DiskS3 final : public IDiskRemote MultiVersion current_settings; /// Gets disk settings from context. GetDiskSettings settings_getter; - const S3Capabilities s3_capabilities; + S3Capabilities s3_capabilities; std::atomic revision_counter = 0; static constexpr UInt64 LATEST_REVISION = std::numeric_limits::max(); @@ -190,6 +194,57 @@ class DiskS3 final : public IDiskRemote ContextPtr context; }; +/// Helper class to collect keys into chunks of maximum size (to prepare batch requests to AWS API) +/// see https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html +class S3PathKeeper : public RemoteFSPathKeeper +{ +public: + using Chunk = Aws::Vector; + using Chunks = std::list; + + explicit S3PathKeeper(size_t chunk_limit_) : RemoteFSPathKeeper(chunk_limit_) {} + + void addPath(const String & path) override + { + if (chunks.empty() || chunks.back().size() >= chunk_limit) + { + /// add one more chunk + chunks.push_back(Chunks::value_type()); + chunks.back().reserve(chunk_limit); + } + Aws::S3::Model::ObjectIdentifier obj; + obj.SetKey(path); + chunks.back().push_back(obj); + } + + void removePaths(Fn auto && remove_chunk_func) + { + for (auto & chunk : chunks) + remove_chunk_func(std::move(chunk)); + } + + Chunks getChunks() const + { + return chunks; + } + + static String getChunkKeys(const Chunk & chunk) + { + String res; + for (const auto & obj : chunk) + { + const auto & key = obj.GetKey(); + if (!res.empty()) + res.append(", "); + res.append(key.c_str(), key.size()); + } + return res; + } + +private: + Chunks chunks; +}; + } #endif diff --git a/src/Disks/S3/registerDiskS3.cpp b/src/Disks/S3/registerDiskS3.cpp index 96630ad72624..474378f4f3e3 100644 --- a/src/Disks/S3/registerDiskS3.cpp +++ b/src/Disks/S3/registerDiskS3.cpp @@ -9,6 +9,8 @@ #if USE_AWS_S3 #include +#include +#include #include #include "DiskS3.h" #include "Disks/DiskCacheWrapper.h" @@ -21,6 +23,7 @@ #include "Disks/RemoteDisksCommon.h" #include + namespace DB { namespace ErrorCodes @@ -46,7 +49,79 @@ void checkReadAccess(const String & disk_name, IDisk & disk) throw Exception("No read access to S3 bucket in disk " + disk_name, ErrorCodes::PATH_ACCESS_DENIED); } -void checkRemoveAccess(IDisk & disk) { disk.removeFile("test_acl"); } +void checkRemoveAccess(IDisk & disk) +{ + disk.removeFile("test_acl"); +} + +bool checkBatchRemoveIsMissing(DiskS3 & disk, std::unique_ptr settings, const String & bucket) +{ + const String path = "_test_remove_objects_capability"; + try + { + auto file = disk.writeFile(path, DBMS_DEFAULT_BUFFER_SIZE, WriteMode::Rewrite); + file->write("test", 4); + file->finalize(); + } + catch (...) + { + try + { + disk.removeFile(path); + } + catch (...) + { + } + return false; /// We don't have write access, therefore no information about batch remove. + } + + /// See `IDiskRemote::removeSharedFile`. + auto fs_paths_keeper = std::dynamic_pointer_cast(disk.createFSPathKeeper()); + disk.removeMetadata(path, fs_paths_keeper); + + auto fs_paths_keeper_copy = std::dynamic_pointer_cast(disk.createFSPathKeeper()); + for (const auto & chunk : fs_paths_keeper->getChunks()) + for (const auto & obj : chunk) + fs_paths_keeper_copy->addPath(obj.GetKey()); + + try + { + /// See `DiskS3::removeFromRemoteFS`. + fs_paths_keeper->removePaths([&](S3PathKeeper::Chunk && chunk) + { + String keys = S3PathKeeper::getChunkKeys(chunk); + LOG_TRACE(&Poco::Logger::get("registerDiskS3"), "Remove AWS keys {}", keys); + Aws::S3::Model::Delete delkeys; + delkeys.SetObjects(chunk); + Aws::S3::Model::DeleteObjectsRequest request; + request.SetBucket(bucket); + request.SetDelete(delkeys); + auto outcome = settings->client->DeleteObjects(request); + if (!outcome.IsSuccess()) + { + const auto & err = outcome.GetError(); + throw Exception(err.GetMessage(), static_cast(err.GetErrorType())); + } + }); + return false; + } + catch (const Exception &) + { + fs_paths_keeper_copy->removePaths([&](S3PathKeeper::Chunk && chunk) + { + String keys = S3PathKeeper::getChunkKeys(chunk); + LOG_TRACE(&Poco::Logger::get("registerDiskS3"), "Remove AWS keys {} one by one", keys); + for (const auto & obj : chunk) + { + Aws::S3::Model::DeleteObjectRequest request; + request.SetBucket(bucket); + request.SetKey(obj.GetKey()); + settings->client->DeleteObject(request); + } + }); + return true; + } +} std::shared_ptr getProxyResolverConfiguration( const String & prefix, const Poco::Util::AbstractConfiguration & proxy_resolver_config) @@ -200,8 +275,26 @@ void registerDiskS3(DiskFactory & factory) getSettings(config, config_prefix, context), getSettings); + bool skip_access_check = config.getBool(config_prefix + ".skip_access_check", false); + + if (!skip_access_check) + { + /// If `support_batch_delete` is turned on (default), check and possibly switch it off. + if (s3_capabilities.support_batch_delete && checkBatchRemoveIsMissing(*std::dynamic_pointer_cast(s3disk), getSettings(config, config_prefix, context), uri.bucket)) + { + LOG_WARNING( + &Poco::Logger::get("registerDiskS3"), + "Storage for disk {} does not support batch delete operations, " + "so `s3_capabilities.support_batch_delete` was automatically turned off during the access check. " + "To remove this message set `s3_capabilities.support_batch_delete` for the disk to `false`.", + name + ); + std::dynamic_pointer_cast(s3disk)->setCapabilitiesSupportBatchDelete(false); + } + } + /// This code is used only to check access to the corresponding disk. - if (!config.getBool(config_prefix + ".skip_access_check", false)) + if (!skip_access_check) { checkWriteAccess(*s3disk); checkReadAccess(name, *s3disk); diff --git a/tests/integration/test_merge_tree_s3/configs/config.d/storage_conf.xml b/tests/integration/test_merge_tree_s3/configs/config.d/storage_conf.xml index 2f1b8275a0bb..a6e2d29c5d57 100644 --- a/tests/integration/test_merge_tree_s3/configs/config.d/storage_conf.xml +++ b/tests/integration/test_merge_tree_s3/configs/config.d/storage_conf.xml @@ -15,6 +15,13 @@ minio123 10 + + s3 + http://resolver:8082/root/data/ + minio + minio123 + 10 + local / @@ -46,6 +53,13 @@ + + +
+ no_delete_objects_s3 +
+
+
diff --git a/tests/integration/test_merge_tree_s3/s3_mocks/no_delete_objects.py b/tests/integration/test_merge_tree_s3/s3_mocks/no_delete_objects.py new file mode 100644 index 000000000000..111f3a490c2b --- /dev/null +++ b/tests/integration/test_merge_tree_s3/s3_mocks/no_delete_objects.py @@ -0,0 +1,92 @@ +import http.client +import http.server +import random +import socketserver +import sys +import urllib.parse + + +UPSTREAM_HOST = "minio1:9001" +random.seed("No delete objects/1.0") + + +def request(command, url, headers={}, data=None): + """Mini-requests.""" + + class Dummy: + pass + + parts = urllib.parse.urlparse(url) + c = http.client.HTTPConnection(parts.hostname, parts.port) + c.request( + command, + urllib.parse.urlunparse(parts._replace(scheme="", netloc="")), + headers=headers, + body=data, + ) + r = c.getresponse() + result = Dummy() + result.status_code = r.status + result.headers = r.headers + result.content = r.read() + return result + + +class RequestHandler(http.server.BaseHTTPRequestHandler): + def do_GET(self): + if self.path == "/": + self.send_response(200) + self.send_header("Content-Type", "text/plain") + self.end_headers() + self.wfile.write(b"OK") + else: + self.do_HEAD() + + def do_PUT(self): + self.do_HEAD() + + def do_DELETE(self): + self.do_HEAD() + + def do_POST(self): + query = urllib.parse.urlparse(self.path).query + params = urllib.parse.parse_qs(query, keep_blank_values=True) + if "delete" in params: + self.send_response(501) + self.send_header("Content-Type", "application/xml") + self.end_headers() + self.wfile.write( + b""" + + NotImplemented + Ima GCP and I can't do `DeleteObjects` request for ya. See https://issuetracker.google.com/issues/162653700 . + RESOURCE + REQUEST_ID +""" + ) + else: + self.do_HEAD() + + def do_HEAD(self): + content_length = self.headers.get("Content-Length") + data = self.rfile.read(int(content_length)) if content_length else None + r = request( + self.command, + f"http://{UPSTREAM_HOST}{self.path}", + headers=self.headers, + data=data, + ) + self.send_response(r.status_code) + for k, v in r.headers.items(): + self.send_header(k, v) + self.end_headers() + self.wfile.write(r.content) + self.wfile.close() + + +class ThreadedHTTPServer(socketserver.ThreadingMixIn, http.server.HTTPServer): + """Handle requests in a separate thread.""" + + +httpd = ThreadedHTTPServer(("0.0.0.0", int(sys.argv[1])), RequestHandler) +httpd.serve_forever() diff --git a/tests/integration/test_merge_tree_s3/test.py b/tests/integration/test_merge_tree_s3/test.py index b7ef3ce3ef2e..b04b02246132 100644 --- a/tests/integration/test_merge_tree_s3/test.py +++ b/tests/integration/test_merge_tree_s3/test.py @@ -67,7 +67,10 @@ def create_table(node, table_name, **additional_settings): def run_s3_mocks(cluster): logging.info("Starting s3 mocks") - mocks = (("unstable_proxy.py", "resolver", "8081"),) + mocks = ( + ("unstable_proxy.py", "resolver", "8081"), + ("no_delete_objects.py", "resolver", "8082"), + ) for mock_filename, container, port in mocks: container_id = cluster.get_container_id(container) current_dir = os.path.dirname(__file__) @@ -602,6 +605,15 @@ def restart_disk(): thread.join() +@pytest.mark.parametrize("node_name", ["node"]) +def test_s3_no_delete_objects(cluster, node_name): + node = cluster.instances[node_name] + create_table( + node, "s3_test_no_delete_objects", storage_policy="no_delete_objects_s3" + ) + node.query("DROP TABLE s3_test_no_delete_objects SYNC") + + @pytest.mark.parametrize("node_name", ["node"]) def test_s3_disk_reads_on_unstable_connection(cluster, node_name): node = cluster.instances[node_name] From ae554fc9d554e62a37dab44375e376620ed45612 Mon Sep 17 00:00:00 2001 From: Vasily Nemkov Date: Thu, 14 Jul 2022 20:54:40 +0300 Subject: [PATCH 06/72] Disabled upstream's release workflow Starting a release from release_branches.yml Fixed error in build_report_check.py --- .github/workflows/release.yml | 128 ++++++++++++------------- .github/workflows/release_branches.yml | 11 ++- tests/ci/build_report_check.py | 2 - 3 files changed, 71 insertions(+), 70 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b2ba6e51c774..29f036323a7e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,66 +1,66 @@ -name: ReleaseWorkflow -# - Gets artifacts from S3 -# - Sends it to JFROG Artifactory -# - Adds them to the release assets +# name: ReleaseWorkflow +# # - Gets artifacts from S3 +# # - Sends it to JFROG Artifactory +# # - Adds them to the release assets -on: # yamllint disable-line rule:truthy - release: - types: - - published +# on: # yamllint disable-line rule:truthy +# release: +# types: +# - published -jobs: - ReleasePublish: - runs-on: [self-hosted, style-checker] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - JFROG_API_KEY=${{ secrets.JFROG_KEY_API_PACKAGES }} - TEMP_PATH=${{runner.temp}}/release_packages - REPO_COPY=${{runner.temp}}/release_packages/ClickHouse - EOF - - name: Check out repository code - uses: actions/checkout@v2 - with: - # Always use the most recent script version - ref: master - - name: Download packages and push to Artifactory - run: | - rm -rf "$TEMP_PATH" && mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY" - python3 ./tests/ci/push_to_artifactory.py --release "${{ github.ref }}" \ - --commit '${{ github.sha }}' --all - - name: Upload packages to release assets - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ${{runner.temp}}/push_to_artifactory/* - overwrite: true - tag: ${{ github.ref }} - file_glob: true - ############################################################################################ - ##################################### Docker images ####################################### - ############################################################################################ - DockerServerImages: - runs-on: [self-hosted, style-checker] - steps: - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - with: - fetch-depth: 0 # otherwise we will have no version info - - name: Check docker clickhouse/clickhouse-server building - run: | - cd "$GITHUB_WORKSPACE/tests/ci" - python3 docker_server.py --release-type auto --version "${{ github.ref }}" - python3 docker_server.py --release-type auto --version "${{ github.ref }}" --no-ubuntu \ - --image-repo clickhouse/clickhouse-keeper --image-path docker/keeper - - name: Cleanup - if: always() - run: | - docker kill "$(docker ps -q)" ||: - docker rm -f "$(docker ps -a -q)" ||: - sudo rm -fr "$TEMP_PATH" +# jobs: +# ReleasePublish: +# runs-on: [self-hosted, style-checker] +# steps: +# - name: Set envs +# run: | +# cat >> "$GITHUB_ENV" << 'EOF' +# JFROG_API_KEY=${{ secrets.JFROG_KEY_API_PACKAGES }} +# TEMP_PATH=${{runner.temp}}/release_packages +# REPO_COPY=${{runner.temp}}/release_packages/ClickHouse +# EOF +# - name: Check out repository code +# uses: actions/checkout@v2 +# with: +# # Always use the most recent script version +# ref: master +# - name: Download packages and push to Artifactory +# run: | +# rm -rf "$TEMP_PATH" && mkdir -p "$TEMP_PATH" +# cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" +# cd "$REPO_COPY" +# python3 ./tests/ci/push_to_artifactory.py --release "${{ github.ref }}" \ +# --commit '${{ github.sha }}' --all +# - name: Upload packages to release assets +# uses: svenstaro/upload-release-action@v2 +# with: +# repo_token: ${{ secrets.GITHUB_TOKEN }} +# file: ${{runner.temp}}/push_to_artifactory/* +# overwrite: true +# tag: ${{ github.ref }} +# file_glob: true +# ############################################################################################ +# ##################################### Docker images ####################################### +# ############################################################################################ +# DockerServerImages: +# runs-on: [self-hosted, style-checker] +# steps: +# - name: Clear repository +# run: | +# sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" +# - name: Check out repository code +# uses: actions/checkout@v2 +# with: +# fetch-depth: 0 # otherwise we will have no version info +# - name: Check docker clickhouse/clickhouse-server building +# run: | +# cd "$GITHUB_WORKSPACE/tests/ci" +# python3 docker_server.py --release-type auto --version "${{ github.ref }}" +# python3 docker_server.py --release-type auto --version "${{ github.ref }}" --no-ubuntu \ +# --image-repo clickhouse/clickhouse-keeper --image-path docker/keeper +# - name: Cleanup +# if: always() +# run: | +# docker kill "$(docker ps -q)" ||: +# docker rm -f "$(docker ps -a -q)" ||: +# sudo rm -fr "$TEMP_PATH" diff --git a/.github/workflows/release_branches.yml b/.github/workflows/release_branches.yml index a01bbad0f5ed..866ff2298cb7 100644 --- a/.github/workflows/release_branches.yml +++ b/.github/workflows/release_branches.yml @@ -12,10 +12,13 @@ on: # yamllint disable-line rule:truthy - opened branches: - '**/22.3*' - push: - branches: - # Anything/22.3 (e.g customizations/22.3) - - '**/22.3*' + release: + types: + - published + # push: + # branches: + # # Anything/22.3 (e.g customizations/22.3) + # - '**/22.3*' jobs: DockerHubPushAarch64: diff --git a/tests/ci/build_report_check.py b/tests/ci/build_report_check.py index a25307ffc446..dbf5adfe1747 100644 --- a/tests/ci/build_report_check.py +++ b/tests/ci/build_report_check.py @@ -286,8 +286,6 @@ def main(): if some_builds_are_missing: addition = f"({len(build_reports)} of {required_builds} builds are OK)" - description = f"{ok_builds}/{total_builds} builds are OK {addition}" - description = f"{ok_groups}/{total_groups} artifact groups are OK {addition}" commit = get_commit(gh, pr_info.sha) From 1180077dd6766540d130694b4c3971f961fe3d37 Mon Sep 17 00:00:00 2001 From: Vasily Nemkov Date: Fri, 15 Jul 2022 21:35:03 +0300 Subject: [PATCH 07/72] Changed version to 22.3.8.40.altinitystable --- cmake/autogenerated_versions.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/autogenerated_versions.txt b/cmake/autogenerated_versions.txt index 46af0929d64c..5d502df6821d 100644 --- a/cmake/autogenerated_versions.txt +++ b/cmake/autogenerated_versions.txt @@ -2,11 +2,11 @@ # NOTE: has nothing common with DBMS_TCP_PROTOCOL_VERSION, # only DBMS_TCP_PROTOCOL_VERSION should be incremented on protocol changes. -SET(VERSION_REVISION 6) +SET(VERSION_REVISION 40) SET(VERSION_MAJOR 22) SET(VERSION_MINOR 3) SET(VERSION_PATCH 8) SET(VERSION_GITHASH 420bdfa27510fe18e20f881d74ab66cddc44583d) -SET(VERSION_DESCRIBE v22.3.8.29-altinitystable) -SET(VERSION_STRING 22.3.8.29.altinitystable) +SET(VERSION_DESCRIBE v22.3.8.40-altinitystable) +SET(VERSION_STRING 22.3.8.40.altinitystable) # end of autochange From 3efe7da7378403a08fac3ca25f03827db8a24800 Mon Sep 17 00:00:00 2001 From: Vasily Nemkov Date: Thu, 28 Jul 2022 15:47:38 +0300 Subject: [PATCH 08/72] Proper version with proper VERSION_REVISION Reverted VERSION_REVISION to value that was there before changes from Altinity's size Added VERSION_TWEAK, VERSION_FLAVOUR with proper values Utilizing tweak and flavour in version from autogenerated_versions.txt --- cmake/autogenerated_versions.txt | 6 ++++-- tests/ci/version_helper.py | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cmake/autogenerated_versions.txt b/cmake/autogenerated_versions.txt index 5d502df6821d..88969d959166 100644 --- a/cmake/autogenerated_versions.txt +++ b/cmake/autogenerated_versions.txt @@ -2,11 +2,13 @@ # NOTE: has nothing common with DBMS_TCP_PROTOCOL_VERSION, # only DBMS_TCP_PROTOCOL_VERSION should be incremented on protocol changes. -SET(VERSION_REVISION 40) +SET(VERSION_REVISION 54460) SET(VERSION_MAJOR 22) SET(VERSION_MINOR 3) SET(VERSION_PATCH 8) SET(VERSION_GITHASH 420bdfa27510fe18e20f881d74ab66cddc44583d) -SET(VERSION_DESCRIBE v22.3.8.40-altinitystable) +SET(VERSION_TWEAK 40) +SET(VERSION_FLAVOUR altinitystable) +SET(VERSION_DESCRIBE v22.3.8.40.altinitystable) SET(VERSION_STRING 22.3.8.40.altinitystable) # end of autochange diff --git a/tests/ci/version_helper.py b/tests/ci/version_helper.py index f8e93c582ce9..a82fcd97ea76 100755 --- a/tests/ci/version_helper.py +++ b/tests/ci/version_helper.py @@ -214,7 +214,8 @@ def get_version_from_repo( versions["revision"], git, # Explicitly use tweak value from version file - tweak=versions["revision"] + tweak=versions.get("tweak", versions["revision"]), + flavour=versions["flavour"] ) From daf4a231f312983201c2a0f04f62fb1be280491d Mon Sep 17 00:00:00 2001 From: Vitaliy Zakaznikov Date: Fri, 29 Jul 2022 10:31:29 -0400 Subject: [PATCH 09/72] Trying again to work on TestFlowsTestsRelease job. --- .github/workflows/release_branches.yml | 329 +++++++++++++------------ 1 file changed, 165 insertions(+), 164 deletions(-) diff --git a/.github/workflows/release_branches.yml b/.github/workflows/release_branches.yml index 866ff2298cb7..cc473dca41af 100644 --- a/.github/workflows/release_branches.yml +++ b/.github/workflows/release_branches.yml @@ -11,7 +11,8 @@ on: # yamllint disable-line rule:truthy - reopened - opened branches: - - '**/22.3*' + #- '**/22.3*' + - '**/ci-enable-testflows-again-customizations_22.3' release: types: - published @@ -216,173 +217,173 @@ jobs: # shellcheck disable=SC2046 docker rm -f $(docker ps -a -q) ||: sudo rm -fr "$TEMP_PATH" - ############################################################################################## - ########################### FUNCTIONAl STATELESS TESTS ####################################### - ############################################################################################## - FunctionalStatelessTestRelease: - needs: [BuilderDebRelease] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateless_debug - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateless tests (release, actions) - REPO_COPY=${{runner.temp}}/stateless_debug/ClickHouse - KILL_TIMEOUT=10800 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - ############################################################################################## - ############################ FUNCTIONAl STATEFUL TESTS ####################################### - ############################################################################################## - FunctionalStatefulTestRelease: - needs: [BuilderDebRelease] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateful_debug - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateful tests (release, actions) - REPO_COPY=${{runner.temp}}/stateful_debug/ClickHouse - KILL_TIMEOUT=3600 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - ############################################################################################# - ############################# INTEGRATION TESTS ############################################# - ############################################################################################# - IntegrationTestsRelease0: - needs: [BuilderDebRelease] - runs-on: [self-hosted, stress-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/integration_tests_release - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Integration tests (release, actions) - REPO_COPY=${{runner.temp}}/integration_tests_release/ClickHouse - RUN_BY_HASH_NUM=0 - RUN_BY_HASH_TOTAL=2 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Integration test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 integration_test_check.py "$CHECK_NAME" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - IntegrationTestsRelease1: - needs: [BuilderDebRelease] - runs-on: [self-hosted, stress-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/integration_tests_release - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Integration tests (release, actions) - REPO_COPY=${{runner.temp}}/integration_tests_release/ClickHouse - RUN_BY_HASH_NUM=1 - RUN_BY_HASH_TOTAL=2 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Integration test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 integration_test_check.py "$CHECK_NAME" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" +# ############################################################################################## +# ########################### FUNCTIONAl STATELESS TESTS ####################################### +# ############################################################################################## +# FunctionalStatelessTestRelease: +# needs: [BuilderDebRelease] +# runs-on: [self-hosted, func-tester] +# steps: +# - name: Set envs +# run: | +# cat >> "$GITHUB_ENV" << 'EOF' +# TEMP_PATH=${{runner.temp}}/stateless_debug +# REPORTS_PATH=${{runner.temp}}/reports_dir +# CHECK_NAME=Stateless tests (release, actions) +# REPO_COPY=${{runner.temp}}/stateless_debug/ClickHouse +# KILL_TIMEOUT=10800 +# EOF +# - name: Download json reports +# uses: actions/download-artifact@v2 +# with: +# path: ${{ env.REPORTS_PATH }} +# - name: Clear repository +# run: | +# sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" +# - name: Check out repository code +# uses: actions/checkout@v2 +# - name: Functional test +# run: | +# sudo rm -fr "$TEMP_PATH" +# mkdir -p "$TEMP_PATH" +# cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" +# cd "$REPO_COPY/tests/ci" +# python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" +# - name: Cleanup +# if: always() +# run: | +# # shellcheck disable=SC2046 +# docker kill $(docker ps -q) ||: +# # shellcheck disable=SC2046 +# docker rm -f $(docker ps -a -q) ||: +# sudo rm -fr "$TEMP_PATH" +# ############################################################################################## +# ############################ FUNCTIONAl STATEFUL TESTS ####################################### +# ############################################################################################## +# FunctionalStatefulTestRelease: +# needs: [BuilderDebRelease] +# runs-on: [self-hosted, func-tester] +# steps: +# - name: Set envs +# run: | +# cat >> "$GITHUB_ENV" << 'EOF' +# TEMP_PATH=${{runner.temp}}/stateful_debug +# REPORTS_PATH=${{runner.temp}}/reports_dir +# CHECK_NAME=Stateful tests (release, actions) +# REPO_COPY=${{runner.temp}}/stateful_debug/ClickHouse +# KILL_TIMEOUT=3600 +# EOF +# - name: Download json reports +# uses: actions/download-artifact@v2 +# with: +# path: ${{ env.REPORTS_PATH }} +# - name: Clear repository +# run: | +# sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" +# - name: Check out repository code +# uses: actions/checkout@v2 +# - name: Functional test +# run: | +# sudo rm -fr "$TEMP_PATH" +# mkdir -p "$TEMP_PATH" +# cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" +# cd "$REPO_COPY/tests/ci" +# python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" +# - name: Cleanup +# if: always() +# run: | +# # shellcheck disable=SC2046 +# docker kill $(docker ps -q) ||: +# # shellcheck disable=SC2046 +# docker rm -f $(docker ps -a -q) ||: +# sudo rm -fr "$TEMP_PATH" +# ############################################################################################# +# ############################# INTEGRATION TESTS ############################################# +# ############################################################################################# +# IntegrationTestsRelease0: +# needs: [BuilderDebRelease] +# runs-on: [self-hosted, stress-tester] +# steps: +# - name: Set envs +# run: | +# cat >> "$GITHUB_ENV" << 'EOF' +# TEMP_PATH=${{runner.temp}}/integration_tests_release +# REPORTS_PATH=${{runner.temp}}/reports_dir +# CHECK_NAME=Integration tests (release, actions) +# REPO_COPY=${{runner.temp}}/integration_tests_release/ClickHouse +# RUN_BY_HASH_NUM=0 +# RUN_BY_HASH_TOTAL=2 +# EOF +# - name: Download json reports +# uses: actions/download-artifact@v2 +# with: +# path: ${{ env.REPORTS_PATH }} +# - name: Clear repository +# run: | +# sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" +# - name: Check out repository code +# uses: actions/checkout@v2 +# - name: Integration test +# run: | +# sudo rm -fr "$TEMP_PATH" +# mkdir -p "$TEMP_PATH" +# cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" +# cd "$REPO_COPY/tests/ci" +# python3 integration_test_check.py "$CHECK_NAME" +# - name: Cleanup +# if: always() +# run: | +# # shellcheck disable=SC2046 +# docker kill $(docker ps -q) ||: +# # shellcheck disable=SC2046 +# docker rm -f $(docker ps -a -q) ||: +# sudo rm -fr "$TEMP_PATH" +# IntegrationTestsRelease1: +# needs: [BuilderDebRelease] +# runs-on: [self-hosted, stress-tester] +# steps: +# - name: Set envs +# run: | +# cat >> "$GITHUB_ENV" << 'EOF' +# TEMP_PATH=${{runner.temp}}/integration_tests_release +# REPORTS_PATH=${{runner.temp}}/reports_dir +# CHECK_NAME=Integration tests (release, actions) +# REPO_COPY=${{runner.temp}}/integration_tests_release/ClickHouse +# RUN_BY_HASH_NUM=1 +# RUN_BY_HASH_TOTAL=2 +# EOF +# - name: Download json reports +# uses: actions/download-artifact@v2 +# with: +# path: ${{ env.REPORTS_PATH }} +# - name: Clear repository +# run: | +# sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" +# - name: Check out repository code +# uses: actions/checkout@v2 +# - name: Integration test +# run: | +# sudo rm -fr "$TEMP_PATH" +# mkdir -p "$TEMP_PATH" +# cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" +# cd "$REPO_COPY/tests/ci" +# python3 integration_test_check.py "$CHECK_NAME" +# - name: Cleanup +# if: always() +# run: | +# # shellcheck disable=SC2046 +# docker kill $(docker ps -q) ||: +# # shellcheck disable=SC2046 +# docker rm -f $(docker ps -a -q) ||: +# sudo rm -fr "$TEMP_PATH" FinishCheck: needs: - DockerHubPush - BuilderReport - - FunctionalStatelessTestRelease - - FunctionalStatefulTestRelease - - IntegrationTestsRelease0 - - IntegrationTestsRelease1 +# - FunctionalStatelessTestRelease +# - FunctionalStatefulTestRelease +# - IntegrationTestsRelease0 +# - IntegrationTestsRelease1 - CompatibilityCheck runs-on: [self-hosted, style-checker] steps: From 7c485b73652450d8aab07e72516b519a26f7aa61 Mon Sep 17 00:00:00 2001 From: Vitaliy Zakaznikov Date: Fri, 29 Jul 2022 10:35:59 -0400 Subject: [PATCH 10/72] Updating on trigger. --- .github/workflows/release_branches.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release_branches.yml b/.github/workflows/release_branches.yml index cc473dca41af..00baa7f9656e 100644 --- a/.github/workflows/release_branches.yml +++ b/.github/workflows/release_branches.yml @@ -12,14 +12,14 @@ on: # yamllint disable-line rule:truthy - opened branches: #- '**/22.3*' - - '**/ci-enable-testflows-again-customizations_22.3' release: types: - published - # push: - # branches: - # # Anything/22.3 (e.g customizations/22.3) - # - '**/22.3*' + push: + branches: + # Anything/22.3 (e.g customizations/22.3) + #- '**/22.3*' + - '**/ci-enable-testflows-again-customizations_22.3' jobs: DockerHubPushAarch64: From f796d9ca05a6e1a7537def2cabc76b0490e44093 Mon Sep 17 00:00:00 2001 From: Arthur Passos Date: Thu, 28 Apr 2022 10:07:47 -0300 Subject: [PATCH 11/72] Make builds and tests possible in Altinity's infrastructure add comment and rename github robot token add clickhouse instance password parameter use Altinity's s3 bucket Use altinityinfra dockerhub images and minor adjustments (#135) Allow CI to be triggered on PR Proper error reporting during docker pull and lowercase version name allow `v22.3.x.y-altinitystable` git tags Download specific MinIO version instead of latest - same as upstream master remove stale chmod More stable CI/CD builds: - Rebuilding all docker images - Reduced number of docker images - Rerunning functional tests even if those were already executed in previous run Added missing dependencies for stateful tests Re-generating _pb2 files on each test run Changed hardcoded docker images name prefixes from `clickhouse/` to `altinityinfra/` Pushing images as :latest too to avoid some test failures reverted back to use clickhouse/jdbc-bridge etc. --- .github/workflows/release_branches.yml | 1654 +---------------- cmake/autogenerated_versions.txt | 2 +- cmake/version.cmake | 2 +- docker/images.json | 83 +- docker/packager/binary/build.sh | 3 + docker/packager/packager | 2 +- docker/test/base/Dockerfile | 4 +- docker/test/codebrowser/Dockerfile | 2 +- docker/test/fasttest/Dockerfile | 2 +- docker/test/fuzzer/Dockerfile | 2 +- docker/test/integration/base/Dockerfile | 2 +- .../integration/mysql_php_client/Dockerfile | 2 +- .../compose/docker_compose_dotnet_client.yml | 2 +- .../runner/compose/docker_compose_keeper.yml | 6 +- .../docker_compose_kerberized_hdfs.yml | 4 +- .../docker_compose_kerberized_kafka.yml | 2 +- .../runner/compose/docker_compose_minio.yml | 6 +- .../docker_compose_mysql_golang_client.yml | 2 +- .../docker_compose_mysql_java_client.yml | 2 +- .../docker_compose_mysql_js_client.yml | 2 +- .../docker_compose_mysql_php_client.yml | 2 +- .../docker_compose_postgresql_java_client.yml | 2 +- docker/test/keeper-jepsen/Dockerfile | 2 +- docker/test/split_build_smoke_test/Dockerfile | 4 +- docker/test/stateful/Dockerfile | 10 +- docker/test/stateful/setup_minio.sh | 11 + docker/test/stateless/Dockerfile | 9 +- docker/test/stateless/setup_minio.sh | 15 +- docker/test/stateless_pytest/Dockerfile | 2 +- docker/test/stress/Dockerfile | 2 +- docker/test/unit/Dockerfile | 2 +- packages/clickhouse-client.yaml | 4 +- packages/clickhouse-common-static-dbg.yaml | 4 +- packages/clickhouse-common-static.yaml | 4 +- packages/clickhouse-keeper-dbg.yaml | 4 +- packages/clickhouse-keeper.yaml | 4 +- packages/clickhouse-server.yaml | 4 +- tests/ci/ast_fuzzer_check.py | 2 +- tests/ci/build_check.py | 24 +- tests/ci/build_report_check.py | 2 + tests/ci/ccache_utils.py | 3 +- tests/ci/ci_config.py | 10 +- tests/ci/clickhouse_helper.py | 6 +- tests/ci/codebrowser_check.py | 2 +- tests/ci/compatibility_check.py | 8 +- tests/ci/docker_images_check.py | 51 +- tests/ci/docker_manifests_merge.py | 4 +- tests/ci/docker_pull_helper.py | 5 +- tests/ci/docker_server.py | 6 +- tests/ci/docker_test.py | 64 +- tests/ci/docs_check.py | 5 +- tests/ci/docs_release.py | 2 +- tests/ci/env_helper.py | 7 +- tests/ci/fast_test_check.py | 4 +- tests/ci/functional_test_check.py | 14 +- tests/ci/get_robot_token.py | 9 +- tests/ci/git_helper.py | 2 +- tests/ci/git_test.py | 6 + tests/ci/integration_test_check.py | 36 +- tests/ci/keeper_jepsen_check.py | 2 +- tests/ci/performance_comparison_check.py | 5 +- tests/ci/release.py | 2 +- tests/ci/run_check.py | 1 + tests/ci/split_build_smoke_check.py | 6 +- tests/ci/stress_check.py | 5 +- tests/ci/style_check.py | 5 +- tests/ci/tests/docker_images.json | 100 +- tests/ci/unit_tests_check.py | 6 +- tests/ci/version_helper.py | 18 +- tests/integration/ci-runner.py | 35 +- tests/integration/helpers/cluster.py | 34 +- tests/integration/helpers/network.py | 8 +- tests/integration/runner | 20 +- .../integration/test_replicated_users/test.py | 2 +- .../test_s3_zero_copy_replication/test.py | 2 + .../integration/test_s3_zero_copy_ttl/test.py | 34 +- tests/integration/test_storage_kafka/test.py | 12 + tests/queries/0_stateless/00900_orc_load.sh | 9 +- .../aes_encryption_env/clickhouse-service.yml | 2 +- .../clickhouse-service.yml | 2 +- .../example_env/clickhouse-service.yml | 2 +- .../clickhouse-service.yml | 2 +- .../kerberos_env/clickhouse-service.yml | 2 +- .../authentication_env/clickhouse-service.yml | 2 +- .../clickhouse-service.yml | 2 +- .../clickhouse-service.yml | 2 +- .../clickhouse-service.yml | 2 +- .../clickhouse-service.yml | 2 +- .../role_mapping_env/clickhouse-service.yml | 2 +- .../map_type_env/clickhouse-service.yml | 2 +- .../rbac/rbac_env/clickhouse-service.yml | 2 +- tests/testflows/runner | 2 +- .../clickhouse-service.yml | 2 +- utils/clickhouse-docker | 4 +- 94 files changed, 539 insertions(+), 1944 deletions(-) diff --git a/.github/workflows/release_branches.yml b/.github/workflows/release_branches.yml index 272aabf86286..ec78e7715a4c 100644 --- a/.github/workflows/release_branches.yml +++ b/.github/workflows/release_branches.yml @@ -5,11 +5,17 @@ env: PYTHONUNBUFFERED: 1 on: # yamllint disable-line rule:truthy + pull_request: + types: + - synchronize + - reopened + - opened + branches: + - '**/22.3*' push: branches: - # 22.1 and 22.10 - - '2[1-9].[1-9][0-9]' - - '2[1-9].[1-9]' + # Anything/22.3 (e.g customizations/22.3) + - '**/22.3*' jobs: DockerHubPushAarch64: @@ -108,9 +114,9 @@ jobs: # shellcheck disable=SC2046 docker rm -f $(docker ps -a -q) ||: sudo rm -fr "$TEMP_PATH" -######################################################################################### -#################################### ORDINARY BUILDS #################################### -######################################################################################### + ######################################################################################### + #################################### ORDINARY BUILDS #################################### + ######################################################################################### BuilderDebRelease: needs: [DockerHubPush] runs-on: [self-hosted, builder] @@ -124,6 +130,7 @@ jobs: CACHES_PATH=${{runner.temp}}/../ccaches CHECK_NAME=ClickHouse build check (actions) BUILD_NAME=package_release + CLICKHOUSE_STABLE_VERSION_SUFFIX=altinitystable EOF - name: Download changed images uses: actions/download-artifact@v2 @@ -133,6 +140,8 @@ jobs: - name: Clear repository run: | sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" + - name: Trust My Directory + run: git config --global --add safe.directory * # https://stackoverflow.com/a/71940133 - name: Check out repository code uses: actions/checkout@v2 with: @@ -159,90 +168,30 @@ jobs: # shellcheck disable=SC2046 docker rm -f $(docker ps -a -q) ||: sudo rm -fr "$TEMP_PATH" "$CACHES_PATH" - BuilderDebAarch64: - needs: [DockerHubPush] - runs-on: [self-hosted, builder] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/build_check - IMAGES_PATH=${{runner.temp}}/images_path - REPO_COPY=${{runner.temp}}/build_check/ClickHouse - CACHES_PATH=${{runner.temp}}/../ccaches - CHECK_NAME=ClickHouse build check (actions) - BUILD_NAME=package_aarch64 - EOF - - name: Download changed images - uses: actions/download-artifact@v2 - with: - name: changed_images - path: ${{ runner.temp }}/images_path - - name: Check out repository code - uses: actions/checkout@v2 - with: - fetch-depth: 0 # otherwise we will have no info about contributors - - name: Build - run: | git -C "$GITHUB_WORKSPACE" submodule sync --recursive git -C "$GITHUB_WORKSPACE" submodule update --depth=1 --recursive --init --jobs=10 - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" && python3 build_check.py "$CHECK_NAME" "$BUILD_NAME" - - name: Upload build URLs to artifacts - uses: actions/upload-artifact@v2 - with: - name: ${{ env.BUILD_URLS }} - path: ${{ runner.temp }}/build_check/${{ env.BUILD_URLS }}.json - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" "$CACHES_PATH" - BuilderDebAsan: - needs: [DockerHubPush] - runs-on: [self-hosted, builder] + ############################################################################################ +##################################### Docker images ####################################### +############################################################################################ + DockerServerImages: + needs: + - BuilderDebRelease + - BuilderDebAarch64 + runs-on: [self-hosted, style-checker] steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/build_check - IMAGES_PATH=${{runner.temp}}/images_path - REPO_COPY=${{runner.temp}}/build_check/ClickHouse - CACHES_PATH=${{runner.temp}}/../ccaches - CHECK_NAME=ClickHouse build check (actions) - BUILD_NAME=package_asan - EOF - - name: Download changed images - uses: actions/download-artifact@v2 - with: - name: changed_images - path: ${{ env.IMAGES_PATH }} - name: Clear repository run: | sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - name: Check out repository code uses: actions/checkout@v2 with: - fetch-depth: 0 # otherwise we will have no info about contributors - - name: Build + fetch-depth: 0 # It MUST BE THE SAME for all dependencies and the job itself + - name: Check docker clickhouse/clickhouse-server building run: | - git -C "$GITHUB_WORKSPACE" submodule sync --recursive - git -C "$GITHUB_WORKSPACE" submodule update --depth=1 --recursive --init --jobs=10 - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" && python3 build_check.py "$CHECK_NAME" "$BUILD_NAME" - - name: Upload build URLs to artifacts - if: ${{ success() || failure() }} - uses: actions/upload-artifact@v2 - with: - name: ${{ env.BUILD_URLS }} - path: ${{ env.TEMP_PATH }}/${{ env.BUILD_URLS }}.json + cd "$GITHUB_WORKSPACE/tests/ci" + python3 docker_server.py --release-type head --no-push + python3 docker_server.py --release-type head --no-push --no-ubuntu \ + --image-repo clickhouse/clickhouse-keeper --image-path docker/keeper - name: Cleanup if: always() run: | @@ -250,95 +199,42 @@ jobs: docker kill $(docker ps -q) ||: # shellcheck disable=SC2046 docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" "$CACHES_PATH" - BuilderDebUBsan: - needs: [DockerHubPush] - runs-on: [self-hosted, builder] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/build_check - IMAGES_PATH=${{runner.temp}}/images_path - REPO_COPY=${{runner.temp}}/build_check/ClickHouse - CACHES_PATH=${{runner.temp}}/../ccaches - CHECK_NAME=ClickHouse build check (actions) - BUILD_NAME=package_ubsan - EOF - - name: Download changed images - uses: actions/download-artifact@v2 - with: - name: changed_images - path: ${{ env.IMAGES_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - with: - fetch-depth: 0 # otherwise we will have no info about contributors - - name: Build - run: | - git -C "$GITHUB_WORKSPACE" submodule sync --recursive - git -C "$GITHUB_WORKSPACE" submodule update --depth=1 --recursive --init --jobs=10 sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" && python3 build_check.py "$CHECK_NAME" "$BUILD_NAME" - - name: Upload build URLs to artifacts - if: ${{ success() || failure() }} - uses: actions/upload-artifact@v2 - with: - name: ${{ env.BUILD_URLS }} - path: ${{ env.TEMP_PATH }}/${{ env.BUILD_URLS }}.json - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" "$CACHES_PATH" - BuilderDebTsan: - needs: [DockerHubPush] - runs-on: [self-hosted, builder] +############################################################################################ + ##################################### BUILD REPORTER ####################################### + ############################################################################################ + BuilderReport: + needs: + - BuilderDebRelease + runs-on: [self-hosted, style-checker] steps: - name: Set envs run: | cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/build_check - IMAGES_PATH=${{runner.temp}}/images_path - REPO_COPY=${{runner.temp}}/build_check/ClickHouse - CACHES_PATH=${{runner.temp}}/../ccaches CHECK_NAME=ClickHouse build check (actions) - BUILD_NAME=package_tsan + REPORTS_PATH=${{runner.temp}}/reports_dir + REPORTS_PATH=${{runner.temp}}/reports_dir + TEMP_PATH=${{runner.temp}}/report_check + NEEDS_DATA_PATH=${{runner.temp}}/needs.json EOF - - name: Download changed images + - name: Download json reports uses: actions/download-artifact@v2 with: - name: changed_images - path: ${{ env.IMAGES_PATH }} + path: ${{ env.REPORTS_PATH }} - name: Clear repository run: | sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - name: Check out repository code uses: actions/checkout@v2 - with: - fetch-depth: 0 # otherwise we will have no info about contributors - - name: Build + - name: Report Builder run: | - git -C "$GITHUB_WORKSPACE" submodule sync --recursive - git -C "$GITHUB_WORKSPACE" submodule update --depth=1 --recursive --init --jobs=10 sudo rm -fr "$TEMP_PATH" mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" && python3 build_check.py "$CHECK_NAME" "$BUILD_NAME" - - name: Upload build URLs to artifacts - if: ${{ success() || failure() }} - uses: actions/upload-artifact@v2 - with: - name: ${{ env.BUILD_URLS }} - path: ${{ env.TEMP_PATH }}/${{ env.BUILD_URLS }}.json + cat > "$NEEDS_DATA_PATH" << 'EOF' + ${{ toJSON(needs) }} + EOF + cd "$GITHUB_WORKSPACE/tests/ci" + python3 build_report_check.py "$CHECK_NAME" - name: Cleanup if: always() run: | @@ -346,47 +242,39 @@ jobs: docker kill $(docker ps -q) ||: # shellcheck disable=SC2046 docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" "$CACHES_PATH" - BuilderDebMsan: - needs: [DockerHubPush] - runs-on: [self-hosted, builder] + sudo rm -fr "$TEMP_PATH" + ############################################################################################## + ########################### FUNCTIONAl STATELESS TESTS ####################################### + ############################################################################################## + FunctionalStatelessTestRelease: + needs: [BuilderDebRelease] + runs-on: [self-hosted, func-tester] steps: - name: Set envs run: | cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/build_check - IMAGES_PATH=${{runner.temp}}/images_path - REPO_COPY=${{runner.temp}}/build_check/ClickHouse - CACHES_PATH=${{runner.temp}}/../ccaches - CHECK_NAME=ClickHouse build check (actions) - BUILD_NAME=package_msan + TEMP_PATH=${{runner.temp}}/stateless_debug + REPORTS_PATH=${{runner.temp}}/reports_dir + CHECK_NAME=Stateless tests (release, actions) + REPO_COPY=${{runner.temp}}/stateless_debug/ClickHouse + KILL_TIMEOUT=10800 EOF - - name: Download changed images + - name: Download json reports uses: actions/download-artifact@v2 with: - name: changed_images - path: ${{ env.IMAGES_PATH }} + path: ${{ env.REPORTS_PATH }} - name: Clear repository run: | sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - name: Check out repository code uses: actions/checkout@v2 - with: - fetch-depth: 0 # otherwise we will have no info about contributors - - name: Build + - name: Functional test run: | - git -C "$GITHUB_WORKSPACE" submodule sync --recursive - git -C "$GITHUB_WORKSPACE" submodule update --depth=1 --recursive --init --jobs=10 sudo rm -fr "$TEMP_PATH" mkdir -p "$TEMP_PATH" cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" && python3 build_check.py "$CHECK_NAME" "$BUILD_NAME" - - name: Upload build URLs to artifacts - if: ${{ success() || failure() }} - uses: actions/upload-artifact@v2 - with: - name: ${{ env.BUILD_URLS }} - path: ${{ env.TEMP_PATH }}/${{ env.BUILD_URLS }}.json + cd "$REPO_COPY/tests/ci" + python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - name: Cleanup if: always() run: | @@ -394,314 +282,33 @@ jobs: docker kill $(docker ps -q) ||: # shellcheck disable=SC2046 docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" "$CACHES_PATH" - BuilderDebDebug: - needs: [DockerHubPush] - runs-on: [self-hosted, builder] + sudo rm -fr "$TEMP_PATH" + ############################################################################################## + ############################ FUNCTIONAl STATEFUL TESTS ####################################### + ############################################################################################## + FunctionalStatefulTestRelease: + needs: [BuilderDebRelease] + runs-on: [self-hosted, func-tester] steps: - name: Set envs run: | cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/build_check - IMAGES_PATH=${{runner.temp}}/images_path - REPO_COPY=${{runner.temp}}/build_check/ClickHouse - CACHES_PATH=${{runner.temp}}/../ccaches - CHECK_NAME=ClickHouse build check (actions) - BUILD_NAME=package_debug + TEMP_PATH=${{runner.temp}}/stateful_debug + REPORTS_PATH=${{runner.temp}}/reports_dir + CHECK_NAME=Stateful tests (release, actions) + REPO_COPY=${{runner.temp}}/stateful_debug/ClickHouse + KILL_TIMEOUT=3600 EOF - - name: Download changed images + - name: Download json reports uses: actions/download-artifact@v2 with: - name: changed_images - path: ${{ env.IMAGES_PATH }} + path: ${{ env.REPORTS_PATH }} - name: Clear repository run: | sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - name: Check out repository code uses: actions/checkout@v2 - with: - fetch-depth: 0 # otherwise we will have no info about contributors - - name: Build - run: | - git -C "$GITHUB_WORKSPACE" submodule sync --recursive - git -C "$GITHUB_WORKSPACE" submodule update --depth=1 --recursive --init --jobs=10 - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" && python3 build_check.py "$CHECK_NAME" "$BUILD_NAME" - - name: Upload build URLs to artifacts - if: ${{ success() || failure() }} - uses: actions/upload-artifact@v2 - with: - name: ${{ env.BUILD_URLS }} - path: ${{ env.TEMP_PATH }}/${{ env.BUILD_URLS }}.json - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" "$CACHES_PATH" -############################################################################################ -##################################### Docker images ####################################### -############################################################################################ - DockerServerImages: - needs: - - BuilderDebRelease - - BuilderDebAarch64 - runs-on: [self-hosted, style-checker] - steps: - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - with: - fetch-depth: 0 # It MUST BE THE SAME for all dependencies and the job itself - - name: Check docker clickhouse/clickhouse-server building - run: | - cd "$GITHUB_WORKSPACE/tests/ci" - python3 docker_server.py --release-type head --no-push - python3 docker_server.py --release-type head --no-push --no-ubuntu \ - --image-repo clickhouse/clickhouse-keeper --image-path docker/keeper - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" -############################################################################################ -##################################### BUILD REPORTER ####################################### -############################################################################################ - BuilderReport: - needs: - - BuilderDebRelease - - BuilderDebAarch64 - - BuilderDebAsan - - BuilderDebTsan - - BuilderDebUBsan - - BuilderDebMsan - - BuilderDebDebug - runs-on: [self-hosted, style-checker] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - CHECK_NAME=ClickHouse build check (actions) - REPORTS_PATH=${{runner.temp}}/reports_dir - REPORTS_PATH=${{runner.temp}}/reports_dir - TEMP_PATH=${{runner.temp}}/report_check - NEEDS_DATA_PATH=${{runner.temp}}/needs.json - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Report Builder - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cat > "$NEEDS_DATA_PATH" << 'EOF' - ${{ toJSON(needs) }} - EOF - cd "$GITHUB_WORKSPACE/tests/ci" - python3 build_report_check.py "$CHECK_NAME" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" -############################################################################################## -########################### FUNCTIONAl STATELESS TESTS ####################################### -############################################################################################## - FunctionalStatelessTestRelease: - needs: [BuilderDebRelease] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateless_debug - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateless tests (release, actions) - REPO_COPY=${{runner.temp}}/stateless_debug/ClickHouse - KILL_TIMEOUT=10800 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatelessTestAarch64: - needs: [BuilderDebAarch64] - runs-on: [self-hosted, func-tester-aarch64] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateless_release - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateless tests (aarch64, actions) - REPO_COPY=${{runner.temp}}/stateless_release/ClickHouse - KILL_TIMEOUT=10800 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatelessTestAsan0: - needs: [BuilderDebAsan] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateless_debug - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateless tests (address, actions) - REPO_COPY=${{runner.temp}}/stateless_debug/ClickHouse - KILL_TIMEOUT=10800 - RUN_BY_HASH_NUM=0 - RUN_BY_HASH_TOTAL=2 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatelessTestAsan1: - needs: [BuilderDebAsan] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateless_debug - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateless tests (address, actions) - REPO_COPY=${{runner.temp}}/stateless_debug/ClickHouse - KILL_TIMEOUT=10800 - RUN_BY_HASH_NUM=1 - RUN_BY_HASH_TOTAL=2 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatelessTestTsan0: - needs: [BuilderDebTsan] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateless_tsan - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateless tests (thread, actions) - REPO_COPY=${{runner.temp}}/stateless_tsan/ClickHouse - KILL_TIMEOUT=10800 - RUN_BY_HASH_NUM=0 - RUN_BY_HASH_TOTAL=3 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test + - name: Functional test run: | sudo rm -fr "$TEMP_PATH" mkdir -p "$TEMP_PATH" @@ -716,1073 +323,9 @@ jobs: # shellcheck disable=SC2046 docker rm -f $(docker ps -a -q) ||: sudo rm -fr "$TEMP_PATH" - FunctionalStatelessTestTsan1: - needs: [BuilderDebTsan] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateless_tsan - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateless tests (thread, actions) - REPO_COPY=${{runner.temp}}/stateless_tsan/ClickHouse - KILL_TIMEOUT=10800 - RUN_BY_HASH_NUM=1 - RUN_BY_HASH_TOTAL=3 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatelessTestTsan2: - needs: [BuilderDebTsan] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateless_tsan - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateless tests (thread, actions) - REPO_COPY=${{runner.temp}}/stateless_tsan/ClickHouse - KILL_TIMEOUT=10800 - RUN_BY_HASH_NUM=2 - RUN_BY_HASH_TOTAL=3 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatelessTestUBsan: - needs: [BuilderDebUBsan] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateless_ubsan - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateless tests (ubsan, actions) - REPO_COPY=${{runner.temp}}/stateless_ubsan/ClickHouse - KILL_TIMEOUT=10800 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatelessTestMsan0: - needs: [BuilderDebMsan] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateless_memory - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateless tests (memory, actions) - REPO_COPY=${{runner.temp}}/stateless_memory/ClickHouse - KILL_TIMEOUT=10800 - RUN_BY_HASH_NUM=0 - RUN_BY_HASH_TOTAL=3 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatelessTestMsan1: - needs: [BuilderDebMsan] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateless_memory - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateless tests (memory, actions) - REPO_COPY=${{runner.temp}}/stateless_memory/ClickHouse - KILL_TIMEOUT=10800 - RUN_BY_HASH_NUM=1 - RUN_BY_HASH_TOTAL=3 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatelessTestMsan2: - needs: [BuilderDebMsan] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateless_memory - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateless tests (memory, actions) - REPO_COPY=${{runner.temp}}/stateless_memory/ClickHouse - KILL_TIMEOUT=10800 - RUN_BY_HASH_NUM=2 - RUN_BY_HASH_TOTAL=3 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatelessTestDebug0: - needs: [BuilderDebDebug] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateless_debug - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateless tests (debug, actions) - REPO_COPY=${{runner.temp}}/stateless_debug/ClickHouse - KILL_TIMEOUT=10800 - RUN_BY_HASH_NUM=0 - RUN_BY_HASH_TOTAL=3 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatelessTestDebug1: - needs: [BuilderDebDebug] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateless_debug - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateless tests (debug, actions) - REPO_COPY=${{runner.temp}}/stateless_debug/ClickHouse - KILL_TIMEOUT=10800 - RUN_BY_HASH_NUM=1 - RUN_BY_HASH_TOTAL=3 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatelessTestDebug2: - needs: [BuilderDebDebug] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateless_debug - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateless tests (debug, actions) - REPO_COPY=${{runner.temp}}/stateless_debug/ClickHouse - KILL_TIMEOUT=10800 - RUN_BY_HASH_NUM=2 - RUN_BY_HASH_TOTAL=3 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" -############################################################################################## -############################ FUNCTIONAl STATEFUL TESTS ####################################### -############################################################################################## - FunctionalStatefulTestRelease: - needs: [BuilderDebRelease] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateful_debug - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateful tests (release, actions) - REPO_COPY=${{runner.temp}}/stateful_debug/ClickHouse - KILL_TIMEOUT=3600 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatefulTestAarch64: - needs: [BuilderDebAarch64] - runs-on: [self-hosted, func-tester-aarch64] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateful_release - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateful tests (aarch64, actions) - REPO_COPY=${{runner.temp}}/stateful_release/ClickHouse - KILL_TIMEOUT=3600 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatefulTestAsan: - needs: [BuilderDebAsan] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateful_debug - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateful tests (address, actions) - REPO_COPY=${{runner.temp}}/stateful_debug/ClickHouse - KILL_TIMEOUT=3600 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatefulTestTsan: - needs: [BuilderDebTsan] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateful_tsan - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateful tests (thread, actions) - REPO_COPY=${{runner.temp}}/stateful_tsan/ClickHouse - KILL_TIMEOUT=3600 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatefulTestMsan: - needs: [BuilderDebMsan] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateful_msan - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateful tests (memory, actions) - REPO_COPY=${{runner.temp}}/stateful_msan/ClickHouse - KILL_TIMEOUT=3600 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatefulTestUBsan: - needs: [BuilderDebUBsan] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateful_ubsan - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateful tests (ubsan, actions) - REPO_COPY=${{runner.temp}}/stateful_ubsan/ClickHouse - KILL_TIMEOUT=3600 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - FunctionalStatefulTestDebug: - needs: [BuilderDebDebug] - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stateful_debug - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stateful tests (debug, actions) - REPO_COPY=${{runner.temp}}/stateful_debug/ClickHouse - KILL_TIMEOUT=3600 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Functional test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 functional_test_check.py "$CHECK_NAME" "$KILL_TIMEOUT" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" -############################################################################################## -######################################### STRESS TESTS ####################################### -############################################################################################## - StressTestAsan: - needs: [BuilderDebAsan] - runs-on: [self-hosted, stress-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stress_thread - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stress test (address, actions) - REPO_COPY=${{runner.temp}}/stress_thread/ClickHouse - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Stress test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 stress_check.py "$CHECK_NAME" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - StressTestTsan: - needs: [BuilderDebTsan] - # func testers have 16 cores + 128 GB memory - # while stress testers have 36 cores + 72 memory - # It would be better to have something like 32 + 128, - # but such servers almost unavailable as spot instances. - runs-on: [self-hosted, func-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stress_thread - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stress test (thread, actions) - REPO_COPY=${{runner.temp}}/stress_thread/ClickHouse - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Stress test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 stress_check.py "$CHECK_NAME" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - StressTestMsan: - needs: [BuilderDebMsan] - runs-on: [self-hosted, stress-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stress_memory - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stress test (memory, actions) - REPO_COPY=${{runner.temp}}/stress_memory/ClickHouse - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Stress test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 stress_check.py "$CHECK_NAME" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - StressTestUBsan: - needs: [BuilderDebUBsan] - runs-on: [self-hosted, stress-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stress_undefined - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stress test (undefined, actions) - REPO_COPY=${{runner.temp}}/stress_undefined/ClickHouse - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Stress test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 stress_check.py "$CHECK_NAME" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - StressTestDebug: - needs: [BuilderDebDebug] - runs-on: [self-hosted, stress-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/stress_debug - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Stress test (debug, actions) - REPO_COPY=${{runner.temp}}/stress_debug/ClickHouse - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Stress test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 stress_check.py "$CHECK_NAME" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" -############################################################################################# -############################# INTEGRATION TESTS ############################################# -############################################################################################# - IntegrationTestsAsan0: - needs: [BuilderDebAsan] - runs-on: [self-hosted, stress-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/integration_tests_asan - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Integration tests (asan, actions) - REPO_COPY=${{runner.temp}}/integration_tests_asan/ClickHouse - RUN_BY_HASH_NUM=0 - RUN_BY_HASH_TOTAL=3 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Integration test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 integration_test_check.py "$CHECK_NAME" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - IntegrationTestsAsan1: - needs: [BuilderDebAsan] - runs-on: [self-hosted, stress-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/integration_tests_asan - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Integration tests (asan, actions) - REPO_COPY=${{runner.temp}}/integration_tests_asan/ClickHouse - RUN_BY_HASH_NUM=1 - RUN_BY_HASH_TOTAL=3 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Integration test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 integration_test_check.py "$CHECK_NAME" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - IntegrationTestsAsan2: - needs: [BuilderDebAsan] - runs-on: [self-hosted, stress-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/integration_tests_asan - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Integration tests (asan, actions) - REPO_COPY=${{runner.temp}}/integration_tests_asan/ClickHouse - RUN_BY_HASH_NUM=2 - RUN_BY_HASH_TOTAL=3 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Integration test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 integration_test_check.py "$CHECK_NAME" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - IntegrationTestsTsan0: - needs: [BuilderDebTsan] - runs-on: [self-hosted, stress-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/integration_tests_tsan - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Integration tests (thread, actions) - REPO_COPY=${{runner.temp}}/integration_tests_tsan/ClickHouse - RUN_BY_HASH_NUM=0 - RUN_BY_HASH_TOTAL=4 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Integration test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 integration_test_check.py "$CHECK_NAME" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - IntegrationTestsTsan1: - needs: [BuilderDebTsan] - runs-on: [self-hosted, stress-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/integration_tests_tsan - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Integration tests (thread, actions) - REPO_COPY=${{runner.temp}}/integration_tests_tsan/ClickHouse - RUN_BY_HASH_NUM=1 - RUN_BY_HASH_TOTAL=4 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Integration test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 integration_test_check.py "$CHECK_NAME" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - IntegrationTestsTsan2: - needs: [BuilderDebTsan] - runs-on: [self-hosted, stress-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/integration_tests_tsan - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Integration tests (thread, actions) - REPO_COPY=${{runner.temp}}/integration_tests_tsan/ClickHouse - RUN_BY_HASH_NUM=2 - RUN_BY_HASH_TOTAL=4 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Integration test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 integration_test_check.py "$CHECK_NAME" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" - IntegrationTestsTsan3: - needs: [BuilderDebTsan] - runs-on: [self-hosted, stress-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/integration_tests_tsan - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=Integration tests (thread, actions) - REPO_COPY=${{runner.temp}}/integration_tests_tsan/ClickHouse - RUN_BY_HASH_NUM=3 - RUN_BY_HASH_TOTAL=4 - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: Integration test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 integration_test_check.py "$CHECK_NAME" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" + ############################################################################################# + ############################# INTEGRATION TESTS ############################################# + ############################################################################################# IntegrationTestsRelease0: needs: [BuilderDebRelease] runs-on: [self-hosted, stress-tester] @@ -1864,41 +407,10 @@ jobs: - DockerHubPush - DockerServerImages - BuilderReport - - FunctionalStatelessTestDebug0 - - FunctionalStatelessTestDebug1 - - FunctionalStatelessTestDebug2 - FunctionalStatelessTestRelease - - FunctionalStatelessTestAarch64 - - FunctionalStatelessTestAsan0 - - FunctionalStatelessTestAsan1 - - FunctionalStatelessTestTsan0 - - FunctionalStatelessTestTsan1 - - FunctionalStatelessTestTsan2 - - FunctionalStatelessTestMsan0 - - FunctionalStatelessTestMsan1 - - FunctionalStatelessTestMsan2 - - FunctionalStatelessTestUBsan - - FunctionalStatefulTestDebug - FunctionalStatefulTestRelease - - FunctionalStatefulTestAarch64 - - FunctionalStatefulTestAsan - - FunctionalStatefulTestTsan - - FunctionalStatefulTestMsan - - FunctionalStatefulTestUBsan - - StressTestDebug - - StressTestAsan - - StressTestTsan - - StressTestMsan - - StressTestUBsan - - IntegrationTestsAsan0 - - IntegrationTestsAsan1 - - IntegrationTestsAsan2 - IntegrationTestsRelease0 - IntegrationTestsRelease1 - - IntegrationTestsTsan0 - - IntegrationTestsTsan1 - - IntegrationTestsTsan2 - - IntegrationTestsTsan3 - CompatibilityCheck runs-on: [self-hosted, style-checker] steps: diff --git a/cmake/autogenerated_versions.txt b/cmake/autogenerated_versions.txt index 305902936cdf..7a1f566cf86c 100644 --- a/cmake/autogenerated_versions.txt +++ b/cmake/autogenerated_versions.txt @@ -2,7 +2,7 @@ # NOTE: has nothing common with DBMS_TCP_PROTOCOL_VERSION, # only DBMS_TCP_PROTOCOL_VERSION should be incremented on protocol changes. -SET(VERSION_REVISION 54460) +SET(VERSION_REVISION 6) SET(VERSION_MAJOR 22) SET(VERSION_MINOR 3) SET(VERSION_PATCH 10) diff --git a/cmake/version.cmake b/cmake/version.cmake index acaa772ff2ff..d785da5fe9b1 100644 --- a/cmake/version.cmake +++ b/cmake/version.cmake @@ -19,5 +19,5 @@ set (VERSION_STRING_SHORT "${VERSION_MAJOR}.${VERSION_MINOR}") math (EXPR VERSION_INTEGER "${VERSION_PATCH} + ${VERSION_MINOR}*1000 + ${VERSION_MAJOR}*1000000") if(CLICKHOUSE_OFFICIAL_BUILD) - set(VERSION_OFFICIAL " (official build)") + set(VERSION_OFFICIAL " (altinity build)") endif() diff --git a/docker/images.json b/docker/images.json index 06d689e8f7cc..baf88e9e610e 100644 --- a/docker/images.json +++ b/docker/images.json @@ -1,164 +1,149 @@ { "docker/packager/deb": { - "name": "clickhouse/deb-builder", + "name": "altinityinfra/deb-builder", "dependent": [] }, "docker/packager/binary": { - "name": "clickhouse/binary-builder", + "name": "altinityinfra/binary-builder", "dependent": [ "docker/test/split_build_smoke_test", "docker/test/codebrowser" ] }, "docker/test/compatibility/centos": { - "name": "clickhouse/test-old-centos", + "name": "altinityinfra/test-old-centos", "dependent": [] }, "docker/test/compatibility/ubuntu": { - "name": "clickhouse/test-old-ubuntu", + "name": "altinityinfra/test-old-ubuntu", "dependent": [] }, "docker/test/integration/base": { - "name": "clickhouse/integration-test", + "name": "altinityinfra/integration-test", "dependent": [] }, "docker/test/fuzzer": { - "name": "clickhouse/fuzzer", + "name": "altinityinfra/fuzzer", "dependent": [] }, "docker/test/performance-comparison": { - "name": "clickhouse/performance-comparison", + "name": "altinityinfra/performance-comparison", "dependent": [] }, "docker/test/util": { - "name": "clickhouse/test-util", + "name": "altinityinfra/test-util", "dependent": [ "docker/test/base", "docker/test/fasttest" ] }, "docker/test/stateless": { - "name": "clickhouse/stateless-test", + "name": "altinityinfra/stateless-test", "dependent": [ "docker/test/stateful", "docker/test/unit" ] }, "docker/test/stateful": { - "name": "clickhouse/stateful-test", + "name": "altinityinfra/stateful-test", "dependent": [ "docker/test/stress" ] }, "docker/test/unit": { - "name": "clickhouse/unit-test", + "name": "altinityinfra/unit-test", "dependent": [] }, "docker/test/stress": { - "name": "clickhouse/stress-test", + "name": "altinityinfra/stress-test", "dependent": [] }, "docker/test/split_build_smoke_test": { - "name": "clickhouse/split-build-smoke-test", + "name": "altinityinfra/split-build-smoke-test", "dependent": [] }, "docker/test/codebrowser": { - "name": "clickhouse/codebrowser", + "name": "altinityinfra/codebrowser", "dependent": [] }, "docker/test/integration/runner": { "only_amd64": true, - "name": "clickhouse/integration-tests-runner", + "name": "altinityinfra/integration-tests-runner", "dependent": [] }, "docker/test/testflows/runner": { - "name": "clickhouse/testflows-runner", + "name": "altinityinfra/testflows-runner", "dependent": [] }, "docker/test/fasttest": { - "name": "clickhouse/fasttest", + "name": "altinityinfra/fasttest", "dependent": [] }, "docker/test/style": { - "name": "clickhouse/style-test", + "name": "altinityinfra/style-test", "dependent": [] }, "docker/test/integration/s3_proxy": { - "name": "clickhouse/s3-proxy", + "name": "altinityinfra/s3-proxy", "dependent": [] }, "docker/test/integration/resolver": { - "name": "clickhouse/python-bottle", + "name": "altinityinfra/python-bottle", "dependent": [] }, "docker/test/integration/helper_container": { - "name": "clickhouse/integration-helper", + "name": "altinityinfra/integration-helper", "dependent": [] }, "docker/test/integration/mysql_golang_client": { - "name": "clickhouse/mysql-golang-client", + "name": "altinityinfra/mysql-golang-client", "dependent": [] }, "docker/test/integration/dotnet_client": { - "name": "clickhouse/dotnet-client", + "name": "altinityinfra/dotnet-client", "dependent": [] }, "docker/test/integration/mysql_java_client": { - "name": "clickhouse/mysql-java-client", + "name": "altinityinfra/mysql-java-client", "dependent": [] }, "docker/test/integration/mysql_js_client": { - "name": "clickhouse/mysql-js-client", + "name": "altinityinfra/mysql-js-client", "dependent": [] }, "docker/test/integration/mysql_php_client": { - "name": "clickhouse/mysql-php-client", + "name": "altinityinfra/mysql-php-client", "dependent": [] }, "docker/test/integration/postgresql_java_client": { - "name": "clickhouse/postgresql-java-client", + "name": "altinityinfra/postgresql-java-client", "dependent": [] }, "docker/test/integration/kerberos_kdc": { "only_amd64": true, - "name": "clickhouse/kerberos-kdc", + "name": "altinityinfra/kerberos-kdc", "dependent": [] }, "docker/test/base": { - "name": "clickhouse/test-base", - "dependent": [ + "name": "altinityinfra/test-base", + "dependent": [ "docker/test/stateless", "docker/test/integration/base", "docker/test/fuzzer", "docker/test/keeper-jepsen" - ] + ] }, "docker/test/integration/kerberized_hadoop": { "only_amd64": true, - "name": "clickhouse/kerberized-hadoop", + "name": "altinityinfra/kerberized-hadoop", "dependent": [] }, "docker/test/sqlancer": { - "name": "clickhouse/sqlancer-test", + "name": "altinityinfra/sqlancer-test", "dependent": [] }, "docker/test/keeper-jepsen": { - "name": "clickhouse/keeper-jepsen-test", - "dependent": [] - }, - "docker/docs/builder": { - "name": "clickhouse/docs-builder", - "dependent": [ - "docker/docs/check", - "docker/docs/release" - ] - }, - "docker/docs/check": { - "name": "clickhouse/docs-check", - "dependent": [] - }, - "docker/docs/release": { - "name": "clickhouse/docs-release", + "name": "altinityinfra/keeper-jepsen-test", "dependent": [] } } diff --git a/docker/packager/binary/build.sh b/docker/packager/binary/build.sh index 943b92abdda1..7ed58b3d82a1 100755 --- a/docker/packager/binary/build.sh +++ b/docker/packager/binary/build.sh @@ -19,6 +19,9 @@ ln -sf darwin-x86_64 build/cmake/toolchain/darwin-aarch64 # export CCACHE_LOGFILE=/build/ccache.log # export CCACHE_DEBUG=1 +# https://stackoverflow.com/a/71940133 +git config --global --add safe.directory '*' + mkdir -p build/build_docker cd build/build_docker rm -f CMakeCache.txt diff --git a/docker/packager/packager b/docker/packager/packager index f82d402d613a..6235ee7ad10e 100755 --- a/docker/packager/packager +++ b/docker/packager/packager @@ -331,7 +331,7 @@ if __name__ == "__main__": args.output_dir = os.path.abspath(os.path.join(os.getcwd(), args.output_dir)) image_type = "binary" if args.package_type == "performance" else args.package_type - image_name = "clickhouse/binary-builder" + image_name = "altinityinfra/binary-builder" if not os.path.isabs(args.clickhouse_repo_path): ch_root = os.path.abspath(os.path.join(os.getcwd(), args.clickhouse_repo_path)) diff --git a/docker/test/base/Dockerfile b/docker/test/base/Dockerfile index 6beab2e5bb70..58fa01241e1f 100644 --- a/docker/test/base/Dockerfile +++ b/docker/test/base/Dockerfile @@ -1,7 +1,7 @@ # rebuild in #33610 -# docker build -t clickhouse/test-base . +# docker build -t altinityinfra/test-base . ARG FROM_TAG=latest -FROM clickhouse/test-util:$FROM_TAG +FROM altinityinfra/test-util:$FROM_TAG # ARG for quick switch to a given ubuntu mirror ARG apt_archive="http://archive.ubuntu.com" diff --git a/docker/test/codebrowser/Dockerfile b/docker/test/codebrowser/Dockerfile index 97f3f54ad987..86147635373f 100644 --- a/docker/test/codebrowser/Dockerfile +++ b/docker/test/codebrowser/Dockerfile @@ -2,7 +2,7 @@ # docker build --network=host -t clickhouse/codebrowser . # docker run --volume=path_to_repo:/repo_folder --volume=path_to_result:/test_output clickhouse/codebrowser ARG FROM_TAG=latest -FROM clickhouse/binary-builder:$FROM_TAG +FROM altinityinfra/binary-builder:$FROM_TAG # ARG for quick switch to a given ubuntu mirror ARG apt_archive="http://archive.ubuntu.com" diff --git a/docker/test/fasttest/Dockerfile b/docker/test/fasttest/Dockerfile index 46b74d89e13f..f61b0d11057f 100644 --- a/docker/test/fasttest/Dockerfile +++ b/docker/test/fasttest/Dockerfile @@ -1,7 +1,7 @@ # rebuild in #33610 # docker build -t clickhouse/fasttest . ARG FROM_TAG=latest -FROM clickhouse/test-util:$FROM_TAG +FROM altinityinfra/test-util:$FROM_TAG # ARG for quick switch to a given ubuntu mirror ARG apt_archive="http://archive.ubuntu.com" diff --git a/docker/test/fuzzer/Dockerfile b/docker/test/fuzzer/Dockerfile index eb4b09c173f6..2aec54bd1719 100644 --- a/docker/test/fuzzer/Dockerfile +++ b/docker/test/fuzzer/Dockerfile @@ -1,7 +1,7 @@ # rebuild in #33610 # docker build -t clickhouse/fuzzer . ARG FROM_TAG=latest -FROM clickhouse/test-base:$FROM_TAG +FROM altinityinfra/test-base:$FROM_TAG # ARG for quick switch to a given ubuntu mirror ARG apt_archive="http://archive.ubuntu.com" diff --git a/docker/test/integration/base/Dockerfile b/docker/test/integration/base/Dockerfile index 9b6318a5426b..ff5c4b2982b5 100644 --- a/docker/test/integration/base/Dockerfile +++ b/docker/test/integration/base/Dockerfile @@ -1,7 +1,7 @@ # rebuild in #33610 # docker build -t clickhouse/integration-test . ARG FROM_TAG=latest -FROM clickhouse/test-base:$FROM_TAG +FROM altinityinfra/test-base:$FROM_TAG SHELL ["/bin/bash", "-c"] diff --git a/docker/test/integration/mysql_php_client/Dockerfile b/docker/test/integration/mysql_php_client/Dockerfile index 0fb77bf8ffb7..55db4d15a7f3 100644 --- a/docker/test/integration/mysql_php_client/Dockerfile +++ b/docker/test/integration/mysql_php_client/Dockerfile @@ -1,7 +1,7 @@ # docker build -t clickhouse/mysql-php-client . # MySQL PHP client docker container -FROM php:7.3-cli +FROM php:8.0.18-cli COPY ./client.crt client.crt COPY ./client.key client.key diff --git a/docker/test/integration/runner/compose/docker_compose_dotnet_client.yml b/docker/test/integration/runner/compose/docker_compose_dotnet_client.yml index b63dac51522c..e5746fa209fb 100644 --- a/docker/test/integration/runner/compose/docker_compose_dotnet_client.yml +++ b/docker/test/integration/runner/compose/docker_compose_dotnet_client.yml @@ -1,6 +1,6 @@ version: '2.3' services: dotnet1: - image: clickhouse/dotnet-client:${DOCKER_DOTNET_CLIENT_TAG:-latest} + image: altinityinfra/dotnet-client:${DOCKER_DOTNET_CLIENT_TAG:-latest} # to keep container running command: sleep infinity diff --git a/docker/test/integration/runner/compose/docker_compose_keeper.yml b/docker/test/integration/runner/compose/docker_compose_keeper.yml index 134ffbff1f74..375003d5e14f 100644 --- a/docker/test/integration/runner/compose/docker_compose_keeper.yml +++ b/docker/test/integration/runner/compose/docker_compose_keeper.yml @@ -1,7 +1,7 @@ version: '2.3' services: zoo1: - image: ${image:-clickhouse/integration-test} + image: ${image:-altinityinfra/integration-test} restart: always user: ${user:-} volumes: @@ -31,7 +31,7 @@ services: - inet6 - rotate zoo2: - image: ${image:-clickhouse/integration-test} + image: ${image:-altinityinfra/integration-test} restart: always user: ${user:-} volumes: @@ -61,7 +61,7 @@ services: - inet6 - rotate zoo3: - image: ${image:-clickhouse/integration-test} + image: ${image:-altinityinfra/integration-test} restart: always user: ${user:-} volumes: diff --git a/docker/test/integration/runner/compose/docker_compose_kerberized_hdfs.yml b/docker/test/integration/runner/compose/docker_compose_kerberized_hdfs.yml index e1b4d393169a..365821b3f5ea 100644 --- a/docker/test/integration/runner/compose/docker_compose_kerberized_hdfs.yml +++ b/docker/test/integration/runner/compose/docker_compose_kerberized_hdfs.yml @@ -4,7 +4,7 @@ services: kerberizedhdfs1: cap_add: - DAC_READ_SEARCH - image: clickhouse/kerberized-hadoop:${DOCKER_KERBERIZED_HADOOP_TAG:-latest} + image: altinityinfra/kerberized-hadoop:${DOCKER_KERBERIZED_HADOOP_TAG:-latest} hostname: kerberizedhdfs1 restart: always volumes: @@ -22,7 +22,7 @@ services: entrypoint: /etc/bootstrap.sh -d hdfskerberos: - image: clickhouse/kerberos-kdc:${DOCKER_KERBEROS_KDC_TAG:-latest} + image: altinityinfra/kerberos-kdc:${DOCKER_KERBEROS_KDC_TAG:-latest} hostname: hdfskerberos volumes: - ${KERBERIZED_HDFS_DIR}/secrets:/tmp/keytab diff --git a/docker/test/integration/runner/compose/docker_compose_kerberized_kafka.yml b/docker/test/integration/runner/compose/docker_compose_kerberized_kafka.yml index d57e4e4d5bea..8dbdd9c74c0c 100644 --- a/docker/test/integration/runner/compose/docker_compose_kerberized_kafka.yml +++ b/docker/test/integration/runner/compose/docker_compose_kerberized_kafka.yml @@ -50,7 +50,7 @@ services: - label:disable kafka_kerberos: - image: clickhouse/kerberos-kdc:${DOCKER_KERBEROS_KDC_TAG:-latest} + image: altinityinfra/kerberos-kdc:${DOCKER_KERBEROS_KDC_TAG:-latest} hostname: kafka_kerberos volumes: - ${KERBERIZED_KAFKA_DIR}/secrets:/tmp/keytab diff --git a/docker/test/integration/runner/compose/docker_compose_minio.yml b/docker/test/integration/runner/compose/docker_compose_minio.yml index 6e8c826b2346..438f3486e177 100644 --- a/docker/test/integration/runner/compose/docker_compose_minio.yml +++ b/docker/test/integration/runner/compose/docker_compose_minio.yml @@ -21,14 +21,14 @@ services: # HTTP proxies for Minio. proxy1: - image: clickhouse/s3-proxy + image: altinityinfra/s3-proxy expose: - "8080" # Redirect proxy port - "80" # Reverse proxy port - "443" # Reverse proxy port (secure) proxy2: - image: clickhouse/s3-proxy + image: altinityinfra/s3-proxy expose: - "8080" - "80" @@ -36,7 +36,7 @@ services: # Empty container to run proxy resolver. resolver: - image: clickhouse/python-bottle + image: altinityinfra/python-bottle expose: - "8080" tty: true diff --git a/docker/test/integration/runner/compose/docker_compose_mysql_golang_client.yml b/docker/test/integration/runner/compose/docker_compose_mysql_golang_client.yml index 56cc04105740..09154b584244 100644 --- a/docker/test/integration/runner/compose/docker_compose_mysql_golang_client.yml +++ b/docker/test/integration/runner/compose/docker_compose_mysql_golang_client.yml @@ -1,6 +1,6 @@ version: '2.3' services: golang1: - image: clickhouse/mysql-golang-client:${DOCKER_MYSQL_GOLANG_CLIENT_TAG:-latest} + image: altinityinfra/mysql-golang-client:${DOCKER_MYSQL_GOLANG_CLIENT_TAG:-latest} # to keep container running command: sleep infinity diff --git a/docker/test/integration/runner/compose/docker_compose_mysql_java_client.yml b/docker/test/integration/runner/compose/docker_compose_mysql_java_client.yml index eb5ffb01baa2..a84cef915df2 100644 --- a/docker/test/integration/runner/compose/docker_compose_mysql_java_client.yml +++ b/docker/test/integration/runner/compose/docker_compose_mysql_java_client.yml @@ -1,6 +1,6 @@ version: '2.3' services: java1: - image: clickhouse/mysql-java-client:${DOCKER_MYSQL_JAVA_CLIENT_TAG:-latest} + image: altinityinfra/mysql-java-client:${DOCKER_MYSQL_JAVA_CLIENT_TAG:-latest} # to keep container running command: sleep infinity diff --git a/docker/test/integration/runner/compose/docker_compose_mysql_js_client.yml b/docker/test/integration/runner/compose/docker_compose_mysql_js_client.yml index 90939449c5f3..b46eb2706c47 100644 --- a/docker/test/integration/runner/compose/docker_compose_mysql_js_client.yml +++ b/docker/test/integration/runner/compose/docker_compose_mysql_js_client.yml @@ -1,6 +1,6 @@ version: '2.3' services: mysqljs1: - image: clickhouse/mysql-js-client:${DOCKER_MYSQL_JS_CLIENT_TAG:-latest} + image: altinityinfra/mysql-js-client:${DOCKER_MYSQL_JS_CLIENT_TAG:-latest} # to keep container running command: sleep infinity diff --git a/docker/test/integration/runner/compose/docker_compose_mysql_php_client.yml b/docker/test/integration/runner/compose/docker_compose_mysql_php_client.yml index 408b8ff089a9..662783a00a1f 100644 --- a/docker/test/integration/runner/compose/docker_compose_mysql_php_client.yml +++ b/docker/test/integration/runner/compose/docker_compose_mysql_php_client.yml @@ -1,6 +1,6 @@ version: '2.3' services: php1: - image: clickhouse/mysql-php-client:${DOCKER_MYSQL_PHP_CLIENT_TAG:-latest} + image: altinityinfra/mysql-php-client:${DOCKER_MYSQL_PHP_CLIENT_TAG:-latest} # to keep container running command: sleep infinity diff --git a/docker/test/integration/runner/compose/docker_compose_postgresql_java_client.yml b/docker/test/integration/runner/compose/docker_compose_postgresql_java_client.yml index 904bfffdfd5b..5c8673ae3eeb 100644 --- a/docker/test/integration/runner/compose/docker_compose_postgresql_java_client.yml +++ b/docker/test/integration/runner/compose/docker_compose_postgresql_java_client.yml @@ -1,6 +1,6 @@ version: '2.2' services: java: - image: clickhouse/postgresql-java-client:${DOCKER_POSTGRESQL_JAVA_CLIENT_TAG:-latest} + image: altinityinfra/postgresql-java-client:${DOCKER_POSTGRESQL_JAVA_CLIENT_TAG:-latest} # to keep container running command: sleep infinity diff --git a/docker/test/keeper-jepsen/Dockerfile b/docker/test/keeper-jepsen/Dockerfile index a794e076ec02..b93b07189012 100644 --- a/docker/test/keeper-jepsen/Dockerfile +++ b/docker/test/keeper-jepsen/Dockerfile @@ -1,7 +1,7 @@ # rebuild in #33610 # docker build -t clickhouse/keeper-jepsen-test . ARG FROM_TAG=latest -FROM clickhouse/test-base:$FROM_TAG +FROM altinityinfra/test-base:$FROM_TAG ENV DEBIAN_FRONTEND=noninteractive ENV CLOJURE_VERSION=1.10.3.814 diff --git a/docker/test/split_build_smoke_test/Dockerfile b/docker/test/split_build_smoke_test/Dockerfile index 5f84eb42216c..cb41859fb118 100644 --- a/docker/test/split_build_smoke_test/Dockerfile +++ b/docker/test/split_build_smoke_test/Dockerfile @@ -1,7 +1,7 @@ # rebuild in #33610 -# docker build -t clickhouse/split-build-smoke-test . +# docker build -t altinityinfra/split-build-smoke-test . ARG FROM_TAG=latest -FROM clickhouse/binary-builder:$FROM_TAG +FROM altinityinfra/binary-builder:$FROM_TAG COPY run.sh /run.sh COPY process_split_build_smoke_test_result.py / diff --git a/docker/test/stateful/Dockerfile b/docker/test/stateful/Dockerfile index 543cf113b2b2..a68168b1271f 100644 --- a/docker/test/stateful/Dockerfile +++ b/docker/test/stateful/Dockerfile @@ -1,13 +1,16 @@ # rebuild in #33610 # docker build -t clickhouse/stateful-test . ARG FROM_TAG=latest -FROM clickhouse/stateless-test:$FROM_TAG +# TODO consider replacing clickhouse with altinityinfra dockerhub account +FROM altinityinfra/stateless-test:$FROM_TAG RUN apt-get update -y \ && env DEBIAN_FRONTEND=noninteractive \ apt-get install --yes --no-install-recommends \ python3-requests \ - llvm-9 + llvm-9 \ + rpm2cpio \ + cpio COPY s3downloader /s3downloader @@ -17,8 +20,7 @@ ENV EXPORT_S3_STORAGE_POLICIES=1 # Download Minio-related binaries RUN arch=${TARGETARCH:-amd64} \ - && wget "https://dl.min.io/server/minio/release/linux-${arch}/minio" \ - && chmod +x ./minio \ + && wget "https://dl.min.io/server/minio/release/linux-${arch}/archive/minio-20220103182258.0.0.x86_64.rpm" \ && wget "https://dl.min.io/client/mc/release/linux-${arch}/mc" \ && chmod +x ./mc ENV MINIO_ROOT_USER="clickhouse" diff --git a/docker/test/stateful/setup_minio.sh b/docker/test/stateful/setup_minio.sh index 5758d905197b..d077dea920c6 100755 --- a/docker/test/stateful/setup_minio.sh +++ b/docker/test/stateful/setup_minio.sh @@ -9,6 +9,10 @@ set -e -x -a -u +rpm2cpio ./minio-20220103182258.0.0.x86_64.rpm | cpio -i --make-directories +find -name minio +cp ./usr/local/bin/minio ./ + ls -lha mkdir -p ./minio_data @@ -27,12 +31,19 @@ fi MINIO_ROOT_USER=${MINIO_ROOT_USER:-clickhouse} MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD:-clickhouse} +./minio --version ./minio server --address ":11111" ./minio_data & +i=0 while ! curl -v --silent http://localhost:11111 2>&1 | grep AccessDenied do + if [[ $i == 60 ]]; then + echo "Failed to setup minio" + exit 0 + fi echo "Trying to connect to minio" sleep 1 + i=$((i + 1)) done lsof -i :11111 diff --git a/docker/test/stateless/Dockerfile b/docker/test/stateless/Dockerfile index 68c08c23b3ff..975a1da8d36e 100644 --- a/docker/test/stateless/Dockerfile +++ b/docker/test/stateless/Dockerfile @@ -1,7 +1,7 @@ # rebuild in #33610 # docker build -t clickhouse/stateless-test . ARG FROM_TAG=latest -FROM clickhouse/test-base:$FROM_TAG +FROM altinityinfra/test-base:$FROM_TAG ARG odbc_driver_url="https://github.com/ClickHouse/clickhouse-odbc/releases/download/v1.1.4.20200302/clickhouse-odbc-1.1.4-Linux.tar.gz" @@ -32,7 +32,9 @@ RUN apt-get update -y \ mysql-client=8.0* \ postgresql-client \ sqlite3 \ - awscli + awscli \ + rpm2cpio \ + cpio RUN pip3 install numpy scipy pandas Jinja2 @@ -53,8 +55,7 @@ ARG TARGETARCH # Download Minio-related binaries RUN arch=${TARGETARCH:-amd64} \ - && wget "https://dl.min.io/server/minio/release/linux-${arch}/minio" \ - && chmod +x ./minio \ + && wget "https://dl.min.io/server/minio/release/linux-${arch}/archive/minio-20220103182258.0.0.x86_64.rpm" \ && wget "https://dl.min.io/client/mc/release/linux-${arch}/mc" \ && chmod +x ./mc diff --git a/docker/test/stateless/setup_minio.sh b/docker/test/stateless/setup_minio.sh index df27b21b05b7..031a54639e92 100755 --- a/docker/test/stateless/setup_minio.sh +++ b/docker/test/stateless/setup_minio.sh @@ -16,21 +16,32 @@ if [ ! -f ./minio ]; then BINARY_TYPE=$(uname -s | tr '[:upper:]' '[:lower:]') - wget "https://dl.min.io/server/minio/release/${BINARY_TYPE}-amd64/minio" \ - && chmod +x ./minio \ + wget "https://dl.min.io/server/minio/release/${BINARY_TYPE}-amd64/archive/minio-20220103182258.0.0.x86_64.rpm" \ && wget "https://dl.min.io/client/mc/release/${BINARY_TYPE}-amd64/mc" \ && chmod +x ./mc fi +rpm2cpio ./minio-20220103182258.0.0.x86_64.rpm | cpio -i --make-directories +find -name minio +cp ./usr/local/bin/minio ./ + MINIO_ROOT_USER=${MINIO_ROOT_USER:-clickhouse} MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD:-clickhouse} +./minio --version + ./minio server --address ":11111" ./minio_data & +i=0 while ! curl -v --silent http://localhost:11111 2>&1 | grep AccessDenied do + if [[ $i == 60 ]]; then + echo "Failed to setup minio" + exit 0 + fi echo "Trying to connect to minio" sleep 1 + i=$((i + 1)) done lsof -i :11111 diff --git a/docker/test/stateless_pytest/Dockerfile b/docker/test/stateless_pytest/Dockerfile index 789ee0e9b308..c148b6212417 100644 --- a/docker/test/stateless_pytest/Dockerfile +++ b/docker/test/stateless_pytest/Dockerfile @@ -1,7 +1,7 @@ # rebuild in #33610 # docker build -t clickhouse/stateless-pytest . ARG FROM_TAG=latest -FROM clickhouse/test-base:$FROM_TAG +FROM altinityinfra/test-base:$FROM_TAG RUN apt-get update -y && \ apt-get install -y --no-install-recommends \ diff --git a/docker/test/stress/Dockerfile b/docker/test/stress/Dockerfile index 393508fd551b..4f6834fff737 100644 --- a/docker/test/stress/Dockerfile +++ b/docker/test/stress/Dockerfile @@ -1,7 +1,7 @@ # rebuild in #33610 # docker build -t clickhouse/stress-test . ARG FROM_TAG=latest -FROM clickhouse/stateful-test:$FROM_TAG +FROM altinityinfra/stateful-test:$FROM_TAG RUN apt-get update -y \ && env DEBIAN_FRONTEND=noninteractive \ diff --git a/docker/test/unit/Dockerfile b/docker/test/unit/Dockerfile index b75bfb6661cc..378341ab8b69 100644 --- a/docker/test/unit/Dockerfile +++ b/docker/test/unit/Dockerfile @@ -1,7 +1,7 @@ # rebuild in #33610 # docker build -t clickhouse/unit-test . ARG FROM_TAG=latest -FROM clickhouse/stateless-test:$FROM_TAG +FROM altinityinfra/stateless-test:$FROM_TAG RUN apt-get install gdb diff --git a/packages/clickhouse-client.yaml b/packages/clickhouse-client.yaml index 6d1233e7c7a5..efb509e622e6 100644 --- a/packages/clickhouse-client.yaml +++ b/packages/clickhouse-client.yaml @@ -4,8 +4,8 @@ name: "clickhouse-client" arch: "${DEB_ARCH}" # amd64, arm64 platform: "linux" version: "${CLICKHOUSE_VERSION_STRING}" -vendor: "ClickHouse Inc." -homepage: "https://clickhouse.com" +vendor: "Altinity Inc." +homepage: "https://altinity.com/" license: "Apache" section: "database" priority: "optional" diff --git a/packages/clickhouse-common-static-dbg.yaml b/packages/clickhouse-common-static-dbg.yaml index 12a1594bd301..78f74ed15cb8 100644 --- a/packages/clickhouse-common-static-dbg.yaml +++ b/packages/clickhouse-common-static-dbg.yaml @@ -4,8 +4,8 @@ name: "clickhouse-common-static-dbg" arch: "${DEB_ARCH}" # amd64, arm64 platform: "linux" version: "${CLICKHOUSE_VERSION_STRING}" -vendor: "ClickHouse Inc." -homepage: "https://clickhouse.com" +vendor: "Altinity Inc." +homepage: "https://altinity.com/" license: "Apache" section: "database" priority: "optional" diff --git a/packages/clickhouse-common-static.yaml b/packages/clickhouse-common-static.yaml index 269d4318e5e0..07dee0d326bb 100644 --- a/packages/clickhouse-common-static.yaml +++ b/packages/clickhouse-common-static.yaml @@ -4,8 +4,8 @@ name: "clickhouse-common-static" arch: "${DEB_ARCH}" # amd64, arm64 platform: "linux" version: "${CLICKHOUSE_VERSION_STRING}" -vendor: "ClickHouse Inc." -homepage: "https://clickhouse.com" +vendor: "Altinity Inc." +homepage: "https://altinity.com/" license: "Apache" section: "database" priority: "optional" diff --git a/packages/clickhouse-keeper-dbg.yaml b/packages/clickhouse-keeper-dbg.yaml index 2c70b7ad4aa4..55bf5da74f20 100644 --- a/packages/clickhouse-keeper-dbg.yaml +++ b/packages/clickhouse-keeper-dbg.yaml @@ -4,8 +4,8 @@ name: "clickhouse-keeper-dbg" arch: "${DEB_ARCH}" # amd64, arm64 platform: "linux" version: "${CLICKHOUSE_VERSION_STRING}" -vendor: "ClickHouse Inc." -homepage: "https://clickhouse.com" +vendor: "Altinity Inc." +homepage: "https://altinity.com/" license: "Apache" section: "database" priority: "optional" diff --git a/packages/clickhouse-keeper.yaml b/packages/clickhouse-keeper.yaml index e99ac30f9443..c8c0b1ad2b33 100644 --- a/packages/clickhouse-keeper.yaml +++ b/packages/clickhouse-keeper.yaml @@ -4,8 +4,8 @@ name: "clickhouse-keeper" arch: "${DEB_ARCH}" # amd64, arm64 platform: "linux" version: "${CLICKHOUSE_VERSION_STRING}" -vendor: "ClickHouse Inc." -homepage: "https://clickhouse.com" +vendor: "Altinity Inc." +homepage: "https://altinity.com/" license: "Apache" section: "database" priority: "optional" diff --git a/packages/clickhouse-server.yaml b/packages/clickhouse-server.yaml index 289956897549..e437deba8790 100644 --- a/packages/clickhouse-server.yaml +++ b/packages/clickhouse-server.yaml @@ -4,8 +4,8 @@ name: "clickhouse-server" arch: "${DEB_ARCH}" # amd64, arm64 platform: "linux" version: "${CLICKHOUSE_VERSION_STRING}" -vendor: "ClickHouse Inc." -homepage: "https://clickhouse.com" +vendor: "Altinity Inc." +homepage: "https://altinity.com/" license: "Apache" section: "database" priority: "optional" diff --git a/tests/ci/ast_fuzzer_check.py b/tests/ci/ast_fuzzer_check.py index 94f5eff51d7e..d3c871207896 100644 --- a/tests/ci/ast_fuzzer_check.py +++ b/tests/ci/ast_fuzzer_check.py @@ -24,7 +24,7 @@ from stopwatch import Stopwatch from rerun_helper import RerunHelper -IMAGE_NAME = "clickhouse/fuzzer" +IMAGE_NAME = "altinityinfra/fuzzer" def get_run_command(pr_number, sha, download_url, workspace_path, image): diff --git a/tests/ci/build_check.py b/tests/ci/build_check.py index 4c77f77a7021..cb8d03a83553 100644 --- a/tests/ci/build_check.py +++ b/tests/ci/build_check.py @@ -8,7 +8,7 @@ import time from typing import List, Optional, Tuple -from env_helper import REPO_COPY, TEMP_PATH, CACHES_PATH, IMAGES_PATH, GITHUB_JOB +from env_helper import REPO_COPY, TEMP_PATH, CACHES_PATH, IMAGES_PATH, S3_BUILDS_BUCKET, GITHUB_JOB, CLICKHOUSE_STABLE_VERSION_SUFFIX from s3_helper import S3Helper from pr_info import PRInfo from version_helper import ( @@ -21,7 +21,7 @@ from docker_pull_helper import get_image_with_version from tee_popen import TeePopen -IMAGE_NAME = "clickhouse/binary-builder" +IMAGE_NAME = "altinityinfra/binary-builder" def get_build_config(build_check_name: str, build_name: str) -> BuildConfig: @@ -230,12 +230,12 @@ def main(): log_url = "" for url in build_results: if "build_log.log" in url: - log_url = "https://s3.amazonaws.com/clickhouse-builds/" + url.replace( + log_url = f"https://s3.amazonaws.com/{S3_BUILDS_BUCKET}/" + url.replace( "+", "%2B" ).replace(" ", "%20") else: build_urls.append( - "https://s3.amazonaws.com/clickhouse-builds/" + f"https://s3.amazonaws.com/{S3_BUILDS_BUCKET}/" + url.replace("+", "%2B").replace(" ", "%20") ) success = len(build_urls) > 0 @@ -259,15 +259,19 @@ def main(): logging.info("Got version from repo %s", version.string) - official_flag = pr_info.number == 0 - version_type = "testing" - if "release" in pr_info.labels or "release-lts" in pr_info.labels: - version_type = "stable" - official_flag = True + official_flag = True + version._flavour = version_type = CLICKHOUSE_STABLE_VERSION_SUFFIX + # TODO (vnemkov): right now we'll use simplified version management: + # only update git hash and explicitly set stable version suffix. + # official_flag = pr_info.number == 0 + # version_type = "testing" + # if "release" in pr_info.labels or "release-lts" in pr_info.labels: + # version_type = CLICKHOUSE_STABLE_VERSION_SUFFIX + # official_flag = True update_version_local(version, version_type) - logging.info("Updated local files with version") + logging.info(f"Updated local files with version : {version.string} / {version.describe}") logging.info("Build short name %s", build_name) diff --git a/tests/ci/build_report_check.py b/tests/ci/build_report_check.py index dbf5adfe1747..a25307ffc446 100644 --- a/tests/ci/build_report_check.py +++ b/tests/ci/build_report_check.py @@ -286,6 +286,8 @@ def main(): if some_builds_are_missing: addition = f"({len(build_reports)} of {required_builds} builds are OK)" + description = f"{ok_builds}/{total_builds} builds are OK {addition}" + description = f"{ok_groups}/{total_groups} artifact groups are OK {addition}" commit = get_commit(gh, pr_info.sha) diff --git a/tests/ci/ccache_utils.py b/tests/ci/ccache_utils.py index 7b0b0f01aa3b..734818f6ec6e 100644 --- a/tests/ci/ccache_utils.py +++ b/tests/ci/ccache_utils.py @@ -5,6 +5,7 @@ import sys import os import shutil +from env_helper import S3_BUILDS_BUCKET from pathlib import Path import requests @@ -71,7 +72,7 @@ def get_ccache_if_not_exists( for obj in objects: if ccache_name in obj: logging.info("Found ccache on path %s", obj) - url = "https://s3.amazonaws.com/clickhouse-builds/" + obj + url = f"https://s3.amazonaws.com/{S3_BUILDS_BUCKET}/" + obj compressed_cache = os.path.join(temp_path, os.path.basename(obj)) dowload_file_with_progress(url, compressed_cache) diff --git a/tests/ci/ci_config.py b/tests/ci/ci_config.py index 74dbe65911c8..08a608570a4a 100644 --- a/tests/ci/ci_config.py +++ b/tests/ci/ci_config.py @@ -188,15 +188,7 @@ }, "builds_report_config": { "ClickHouse build check (actions)": [ - "package_release", - "performance", - "package_aarch64", - "package_asan", - "package_ubsan", - "package_tsan", - "package_msan", - "package_debug", - "binary_release", + "package_release" ], "ClickHouse special build check (actions)": [ "binary_tidy", diff --git a/tests/ci/clickhouse_helper.py b/tests/ci/clickhouse_helper.py index 7ccbcb4a47e1..bfe2ed0e72b6 100644 --- a/tests/ci/clickhouse_helper.py +++ b/tests/ci/clickhouse_helper.py @@ -15,7 +15,9 @@ def __init__(self, url=None): "X-ClickHouse-User": get_parameter_from_ssm( "clickhouse-test-stat-login2" ), - "X-ClickHouse-Key": "", + "X-ClickHouse-Key": get_parameter_from_ssm( + "clickhouse-test-stat-password" + ), } @staticmethod @@ -117,7 +119,7 @@ def prepare_tests_results_for_clickhouse( check_name, ): - pull_request_url = "https://github.com/ClickHouse/ClickHouse/commits/master" + pull_request_url = "https://github.com/Altinity/ClickHouse/commits/master" base_ref = "master" head_ref = "master" base_repo = pr_info.repo_full_name diff --git a/tests/ci/codebrowser_check.py b/tests/ci/codebrowser_check.py index 48c92e9f6acc..3a245005cb40 100644 --- a/tests/ci/codebrowser_check.py +++ b/tests/ci/codebrowser_check.py @@ -40,7 +40,7 @@ def get_run_command(repo_path, output_path, image): if not os.path.exists(temp_path): os.makedirs(temp_path) - docker_image = get_image_with_version(IMAGES_PATH, "clickhouse/codebrowser") + docker_image = get_image_with_version(IMAGES_PATH, "altinityinfra/codebrowser") s3_helper = S3Helper("https://s3.amazonaws.com") result_path = os.path.join(temp_path, "result_path") diff --git a/tests/ci/compatibility_check.py b/tests/ci/compatibility_check.py index d546fabf2316..2351ef0c60a1 100644 --- a/tests/ci/compatibility_check.py +++ b/tests/ci/compatibility_check.py @@ -24,8 +24,8 @@ from stopwatch import Stopwatch from rerun_helper import RerunHelper -IMAGE_UBUNTU = "clickhouse/test-old-ubuntu" -IMAGE_CENTOS = "clickhouse/test-old-centos" +IMAGE_UBUNTU = "altinityinfra/test-old-ubuntu" +IMAGE_CENTOS = "altinityinfra/test-old-centos" MAX_GLIBC_VERSION = "2.4" DOWNLOAD_RETRIES_COUNT = 5 CHECK_NAME = "Compatibility check (actions)" @@ -197,4 +197,8 @@ def url_filter(url): report_url, CHECK_NAME, ) + ch_helper.insert_events_into(db="gh-data", table="checks", events=prepared_events) + + if state == "error": + sys.exit(1) diff --git a/tests/ci/docker_images_check.py b/tests/ci/docker_images_check.py index 8185229dfd69..9d5173648e35 100644 --- a/tests/ci/docker_images_check.py +++ b/tests/ci/docker_images_check.py @@ -106,22 +106,23 @@ def get_changed_docker_images( str(files_changed), ) - changed_images = [] - - for dockerfile_dir, image_description in images_dict.items(): - for f in files_changed: - if f.startswith(dockerfile_dir): - name = image_description["name"] - only_amd64 = image_description.get("only_amd64", False) - logging.info( - "Found changed file '%s' which affects " - "docker image '%s' with path '%s'", - f, - name, - dockerfile_dir, - ) - changed_images.append(DockerImage(dockerfile_dir, name, only_amd64)) - break + # Rebuild all images + changed_images = [DockerImage(dockerfile_dir, image_description["name"], image_description.get("only_amd64", False)) for dockerfile_dir, image_description in images_dict.items()] + + # for dockerfile_dir, image_description in images_dict.items(): + # for f in files_changed: + # if f.startswith(dockerfile_dir): + # name = image_description["name"] + # only_amd64 = image_description.get("only_amd64", False) + # logging.info( + # "Found changed file '%s' which affects " + # "docker image '%s' with path '%s'", + # f, + # name, + # dockerfile_dir, + # ) + # changed_images.append(DockerImage(dockerfile_dir, name, only_amd64)) + # break # The order is important: dependents should go later than bases, so that # they are built with updated base versions. @@ -253,6 +254,19 @@ def build_and_push_one_image( f"--tag {image.repo}:{version_string} " f"{cache_from} " f"--cache-to type=inline,mode=max " + # FIXME: many tests utilize packages without specifying version, hence docker pulls :latest + # this will fail multiple jobs are going to be executed on different machines and + # push different images as latest. + # To fix it we may: + # - require jobs to be executed on same machine images were built (no parallelism) + # - change all the test's code (mostly docker-compose files in integration tests) + # that depend on said images and push version somehow into docker-compose. + # (and that is lots of work and many potential conflicts with upstream) + # - tag and push all images as :latest and then just pray that collisions are infrequent. + # and if even if collision happens, image is not that different and would still properly work. + # (^^^ CURRENT SOLUTION ^^^) But this is just a numbers game, it will blow up at some point. + # - do something crazy + f"--tag {image.repo}:latest " f"{push_arg}" f"--progress plain {image.full_path}" ) @@ -261,6 +275,7 @@ def build_and_push_one_image( retcode = proc.wait() if retcode != 0: + logging.error("Building image {} failed with error: {}\n{}".format(image, retcode, ''.join(list(open(build_log, 'rt'))))) return False, build_log logging.info("Processing of %s successfully finished", image.repo) @@ -407,8 +422,8 @@ def main(): if args.push: subprocess.check_output( # pylint: disable=unexpected-keyword-arg - "docker login --username 'robotclickhouse' --password-stdin", - input=get_parameter_from_ssm("dockerhub_robot_password"), + "docker login --username 'altinityinfra' --password-stdin", + input=get_parameter_from_ssm("dockerhub-password"), encoding="utf-8", shell=True, ) diff --git a/tests/ci/docker_manifests_merge.py b/tests/ci/docker_manifests_merge.py index 8bd50819877c..0d061bb0db33 100644 --- a/tests/ci/docker_manifests_merge.py +++ b/tests/ci/docker_manifests_merge.py @@ -173,8 +173,8 @@ def main(): args = parse_args() if args.push: subprocess.check_output( # pylint: disable=unexpected-keyword-arg - "docker login --username 'robotclickhouse' --password-stdin", - input=get_parameter_from_ssm("dockerhub_robot_password"), + "docker login --username 'altinityinfra' --password-stdin", + input=get_parameter_from_ssm("dockerhub-password"), encoding="utf-8", shell=True, ) diff --git a/tests/ci/docker_pull_helper.py b/tests/ci/docker_pull_helper.py index 54d48c588050..c1c0637411d2 100644 --- a/tests/ci/docker_pull_helper.py +++ b/tests/ci/docker_pull_helper.py @@ -5,6 +5,7 @@ import time import subprocess import logging +import traceback class DockerImage: @@ -48,6 +49,7 @@ def get_images_with_versions(reports_path, required_image, pull=True): docker_images.append(docker_image) if pull: + latest_error = None for docker_image in docker_images: for i in range(10): try: @@ -60,7 +62,8 @@ def get_images_with_versions(reports_path, required_image, pull=True): break except Exception as ex: time.sleep(i * 3) - logging.info("Got execption pulling docker %s", ex) + logging.info("Got exception pulling docker %s", ex) + latest_error = traceback.format_exc() else: raise Exception( f"Cannot pull dockerhub for image docker pull {docker_image} because of {latest_error}" diff --git a/tests/ci/docker_server.py b/tests/ci/docker_server.py index 4fbb1faee04a..4a03b49c5aa6 100644 --- a/tests/ci/docker_server.py +++ b/tests/ci/docker_server.py @@ -71,7 +71,7 @@ def parse_args() -> argparse.Namespace: parser.add_argument( "--image-repo", type=str, - default="clickhouse/clickhouse-server", + default="altinityinfra/clickhouse-server", help="image name on docker hub", ) parser.add_argument( @@ -308,8 +308,8 @@ def main(): if args.push: subprocess.check_output( # pylint: disable=unexpected-keyword-arg - "docker login --username 'robotclickhouse' --password-stdin", - input=get_parameter_from_ssm("dockerhub_robot_password"), + "docker login --username 'altinityinfra' --password-stdin", + input=get_parameter_from_ssm("dockerhub-password"), encoding="utf-8", shell=True, ) diff --git a/tests/ci/docker_test.py b/tests/ci/docker_test.py index 32df6d5f1d07..95ac61a0c1ee 100644 --- a/tests/ci/docker_test.py +++ b/tests/ci/docker_test.py @@ -37,61 +37,61 @@ def test_get_changed_docker_images(self): self.maxDiff = None expected = sorted( [ - di.DockerImage("docker/test/base", "clickhouse/test-base", False), - di.DockerImage("docker/docs/builder", "clickhouse/docs-builder", True), + di.DockerImage("docker/test/base", "altinityinfra/test-base", False), + di.DockerImage("docker/docs/builder", "altinityinfra/docs-builder", True), di.DockerImage( "docker/test/stateless", - "clickhouse/stateless-test", + "altinityinfra/stateless-test", False, - "clickhouse/test-base", + "altinityinfra/test-base", ), di.DockerImage( "docker/test/integration/base", - "clickhouse/integration-test", + "altinityinfra/integration-test", False, - "clickhouse/test-base", - ), - di.DockerImage( - "docker/test/fuzzer", - "clickhouse/fuzzer", - False, - "clickhouse/test-base", + "altinityinfra/test-base", ), + # di.DockerImage( + # "docker/test/fuzzer", + # "altinityinfra/fuzzer", + # False, + # "altinityinfra/test-base", + # ), di.DockerImage( "docker/test/keeper-jepsen", - "clickhouse/keeper-jepsen-test", - False, - "clickhouse/test-base", - ), - di.DockerImage( - "docker/docs/check", - "clickhouse/docs-check", - False, - "clickhouse/docs-builder", - ), - di.DockerImage( - "docker/docs/release", - "clickhouse/docs-release", + "altinityinfra/keeper-jepsen-test", False, - "clickhouse/docs-builder", + "altinityinfra/test-base", ), + # di.DockerImage( + # "docker/docs/check", + # "altinityinfra/docs-check", + # False, + # "altinityinfra/docs-builder", + # ), + # di.DockerImage( + # "docker/docs/release", + # "altinityinfra/docs-release", + # False, + # "altinityinfra/docs-builder", + # ), di.DockerImage( "docker/test/stateful", - "clickhouse/stateful-test", + "altinityinfra/stateful-test", False, - "clickhouse/stateless-test", + "altinityinfra/stateless-test", ), di.DockerImage( "docker/test/unit", - "clickhouse/unit-test", + "altinityinfra/unit-test", False, - "clickhouse/stateless-test", + "altinityinfra/stateless-test", ), di.DockerImage( "docker/test/stress", - "clickhouse/stress-test", + "altinityinfra/stress-test", False, - "clickhouse/stateful-test", + "altinityinfra/stateful-test", ), ] ) diff --git a/tests/ci/docs_check.py b/tests/ci/docs_check.py index 58678b160a4f..10168c9a77e8 100644 --- a/tests/ci/docs_check.py +++ b/tests/ci/docs_check.py @@ -50,7 +50,7 @@ if not os.path.exists(temp_path): os.makedirs(temp_path) - docker_image = get_image_with_version(temp_path, "clickhouse/docs-check") + docker_image = get_image_with_version(temp_path, "altinityinfra/docs-check") test_output = os.path.join(temp_path, "docs_check_log") if not os.path.exists(test_output): @@ -114,4 +114,7 @@ report_url, NAME, ) + ch_helper.insert_events_into(db="gh-data", table="checks", events=prepared_events) + if status == "error": + sys.exit(1) diff --git a/tests/ci/docs_release.py b/tests/ci/docs_release.py index b6d47326f9b8..99ee9e0910d7 100644 --- a/tests/ci/docs_release.py +++ b/tests/ci/docs_release.py @@ -34,7 +34,7 @@ if not os.path.exists(temp_path): os.makedirs(temp_path) - docker_image = get_image_with_version(temp_path, "clickhouse/docs-release") + docker_image = get_image_with_version(temp_path, "altinityinfra/docs-release") test_output = os.path.join(temp_path, "docs_release_log") if not os.path.exists(test_output): diff --git a/tests/ci/env_helper.py b/tests/ci/env_helper.py index dd081523db11..d2ef7f397a27 100644 --- a/tests/ci/env_helper.py +++ b/tests/ci/env_helper.py @@ -11,7 +11,7 @@ CLOUDFLARE_TOKEN = os.getenv("CLOUDFLARE_TOKEN") GITHUB_EVENT_PATH = os.getenv("GITHUB_EVENT_PATH", "") GITHUB_JOB = os.getenv("GITHUB_JOB", "local") -GITHUB_REPOSITORY = os.getenv("GITHUB_REPOSITORY", "ClickHouse/ClickHouse") +GITHUB_REPOSITORY = os.getenv("GITHUB_REPOSITORY", "Altinity/ClickHouse") GITHUB_RUN_ID = os.getenv("GITHUB_RUN_ID", "0") GITHUB_SERVER_URL = os.getenv("GITHUB_SERVER_URL", "https://github.com") GITHUB_WORKSPACE = os.getenv("GITHUB_WORKSPACE", git_root) @@ -20,5 +20,6 @@ REPORTS_PATH = os.getenv("REPORTS_PATH", p.abspath(p.join(module_dir, "./reports"))) REPO_COPY = os.getenv("REPO_COPY", git_root) RUNNER_TEMP = os.getenv("RUNNER_TEMP", p.abspath(p.join(module_dir, "./tmp"))) -S3_BUILDS_BUCKET = os.getenv("S3_BUILDS_BUCKET", "clickhouse-builds") -S3_TEST_REPORTS_BUCKET = os.getenv("S3_TEST_REPORTS_BUCKET", "clickhouse-test-reports") +S3_BUILDS_BUCKET = os.getenv("S3_BUILDS_BUCKET", "altinity-build-artifacts") +S3_TEST_REPORTS_BUCKET = os.getenv("S3_TEST_REPORTS_BUCKET", "altinity-build-artifacts") +CLICKHOUSE_STABLE_VERSION_SUFFIX = os.getenv("CLICKHOUSE_STABLE_VERSION_SUFFIX", "stable") diff --git a/tests/ci/fast_test_check.py b/tests/ci/fast_test_check.py index 64e045947864..df5464a7dace 100644 --- a/tests/ci/fast_test_check.py +++ b/tests/ci/fast_test_check.py @@ -98,7 +98,7 @@ def process_results(result_folder): logging.info("Check is already finished according to github status, exiting") sys.exit(0) - docker_image = get_image_with_version(temp_path, "clickhouse/fasttest") + docker_image = get_image_with_version(temp_path, "altinityinfra/fasttest") s3_helper = S3Helper("https://s3.amazonaws.com") @@ -208,7 +208,7 @@ def process_results(result_folder): # Refuse other checks to run if fast test failed if state != "success": - if "force-tests" in pr_info.labels: + if "force-tests" in pr_info.labels and state != "error": print("'force-tests' enabled, will report success") else: sys.exit(1) diff --git a/tests/ci/functional_test_check.py b/tests/ci/functional_test_check.py index 6113bfdf0cdf..2b8a52b4db43 100644 --- a/tests/ci/functional_test_check.py +++ b/tests/ci/functional_test_check.py @@ -48,9 +48,9 @@ def get_additional_envs(check_name, run_by_hash_num, run_by_hash_total): def get_image_name(check_name): if "stateless" in check_name.lower(): - return "clickhouse/stateless-test" + return "altinityinfra/stateless-test" if "stateful" in check_name.lower(): - return "clickhouse/stateful-test" + return "altinityinfra/stateful-test" else: raise Exception(f"Cannot deduce image name based on check name {check_name}") @@ -190,10 +190,12 @@ def process_results(result_folder, server_log_path): run_by_hash_total = 0 check_name_with_group = check_name - rerun_helper = RerunHelper(gh, pr_info, check_name_with_group) - if rerun_helper.is_already_finished_by_status(): - logging.info("Check is already finished according to github status, exiting") - sys.exit(0) + # Always re-run, even if it finished in previous run. + # gh = Github(get_best_robot_token()) + # rerun_helper = RerunHelper(gh, pr_info, check_name_with_group) + # if rerun_helper.is_already_finished_by_status(): + # logging.info("Check is already finished according to github status, exiting") + # sys.exit(0) if not os.path.exists(temp_path): os.makedirs(temp_path) diff --git a/tests/ci/get_robot_token.py b/tests/ci/get_robot_token.py index cb79d9ae01ac..10d742083f56 100644 --- a/tests/ci/get_robot_token.py +++ b/tests/ci/get_robot_token.py @@ -9,7 +9,14 @@ def get_parameter_from_ssm(name, decrypt=True, client=None): return client.get_parameter(Name=name, WithDecryption=decrypt)["Parameter"]["Value"] -def get_best_robot_token(token_prefix_env_name="github_robot_token_", total_tokens=4): +# Original CI code uses the "_original" version of this method. Each robot token is rate limited +# and the original implementation selects the "best one". To make it simpler and iterate faster, +# we are using only one robot and keeping the method signature. In the future we might reconsider +# having multiple robot tokens +def get_best_robot_token(token_prefix_env_name="github_robot_token", total_tokens=4): + return get_parameter_from_ssm(token_prefix_env_name) + +def get_best_robot_token_original(token_prefix_env_name="github_robot_token_", total_tokens=4): client = boto3.client("ssm", region_name="us-east-1") tokens = {} for i in range(1, total_tokens + 1): diff --git a/tests/ci/git_helper.py b/tests/ci/git_helper.py index 50414ffb470d..18d0cbf3840c 100644 --- a/tests/ci/git_helper.py +++ b/tests/ci/git_helper.py @@ -9,7 +9,7 @@ # \A and \Z match only start and end of the whole string RELEASE_BRANCH_REGEXP = r"\A\d+[.]\d+\Z" TAG_REGEXP = ( - r"\Av\d{2}[.][1-9]\d*[.][1-9]\d*[.][1-9]\d*-(testing|prestable|stable|lts)\Z" + r"\Av\d{2}[.][1-9]\d*[.][1-9]\d*[.][1-9]\d*-(testing|prestable|stable|lts|altinitystable)\Z" ) SHA_REGEXP = r"\A([0-9]|[a-f]){40}\Z" diff --git a/tests/ci/git_test.py b/tests/ci/git_test.py index 785c9b62ccef..69371af40b34 100644 --- a/tests/ci/git_test.py +++ b/tests/ci/git_test.py @@ -57,6 +57,9 @@ def test_tags(self): with self.assertRaises(Exception): setattr(self.git, tag_attr, tag) + def check_tag(self): + self.git.check_tag("v21.12.333.4567-altinitystable") + def test_tweak(self): self.git.commits_since_tag = 0 self.assertEqual(self.git.tweak, 1) @@ -66,3 +69,6 @@ def test_tweak(self): self.assertEqual(self.git.tweak, 22224) self.git.commits_since_tag = 0 self.assertEqual(self.git.tweak, 22222) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/ci/integration_test_check.py b/tests/ci/integration_test_check.py index 4ee0c8349746..24d5eda5477c 100644 --- a/tests/ci/integration_test_check.py +++ b/tests/ci/integration_test_check.py @@ -30,17 +30,17 @@ # When update, update # integration/ci-runner.py:ClickhouseIntegrationTestsRunner.get_images_names too IMAGES = [ - "clickhouse/integration-tests-runner", - "clickhouse/mysql-golang-client", - "clickhouse/mysql-java-client", - "clickhouse/mysql-js-client", - "clickhouse/mysql-php-client", - "clickhouse/postgresql-java-client", - "clickhouse/integration-test", - "clickhouse/kerberos-kdc", - "clickhouse/kerberized-hadoop", - "clickhouse/integration-helper", - "clickhouse/dotnet-client", + "altinityinfra/integration-tests-runner", + "altinityinfra/mysql-golang-client", + "altinityinfra/mysql-java-client", + "altinityinfra/mysql-js-client", + "altinityinfra/mysql-php-client", + "altinityinfra/postgresql-java-client", + "altinityinfra/integration-test", + "altinityinfra/kerberos-kdc", + "altinityinfra/kerberized-hadoop", + "altinityinfra/integration-helper", + "altinityinfra/dotnet-client", ] @@ -146,10 +146,12 @@ def process_results(result_folder): gh = Github(get_best_robot_token()) - rerun_helper = RerunHelper(gh, pr_info, check_name_with_group) - if rerun_helper.is_already_finished_by_status(): - logging.info("Check is already finished according to github status, exiting") - sys.exit(0) + # Always re-run, even if it finished in previous run. + # gh = Github(get_best_robot_token()) + # rerun_helper = RerunHelper(gh, pr_info, check_name_with_group) + # if rerun_helper.is_already_finished_by_status(): + # logging.info("Check is already finished according to github status, exiting") + # sys.exit(0) images = get_images_with_versions(reports_path, IMAGES) images_with_versions = {i.name: i.version for i in images} @@ -233,4 +235,8 @@ def process_results(result_folder): report_url, check_name_with_group, ) + ch_helper.insert_events_into(db="gh-data", table="checks", events=prepared_events) + + if state == "error": + sys.exit(1) diff --git a/tests/ci/keeper_jepsen_check.py b/tests/ci/keeper_jepsen_check.py index b0ec1e7ba8bf..98af355ab6d1 100644 --- a/tests/ci/keeper_jepsen_check.py +++ b/tests/ci/keeper_jepsen_check.py @@ -26,7 +26,7 @@ JEPSEN_GROUP_NAME = "jepsen_group" DESIRED_INSTANCE_COUNT = 3 -IMAGE_NAME = "clickhouse/keeper-jepsen-test" +IMAGE_NAME = "altinityinfra/keeper-jepsen-test" CHECK_NAME = "ClickHouse Keeper Jepsen (actions)" diff --git a/tests/ci/performance_comparison_check.py b/tests/ci/performance_comparison_check.py index c6ce86b2ce10..bd0ca267ddd8 100644 --- a/tests/ci/performance_comparison_check.py +++ b/tests/ci/performance_comparison_check.py @@ -20,7 +20,7 @@ from tee_popen import TeePopen from rerun_helper import RerunHelper -IMAGE_NAME = "clickhouse/performance-comparison" +IMAGE_NAME = "altinityinfra/performance-comparison" def get_run_command( @@ -217,3 +217,6 @@ def __exit__(self, exc_type, exc_val, exc_tb): post_commit_status( gh, pr_info.sha, check_name_with_group, message, status, report_url ) + + if status == "error": + sys.exit(1) diff --git a/tests/ci/release.py b/tests/ci/release.py index 89182dc7428e..2fdd4c8b1742 100755 --- a/tests/ci/release.py +++ b/tests/ci/release.py @@ -400,7 +400,7 @@ def parse_args() -> argparse.Namespace: ) parser.add_argument( "--repo", - default="ClickHouse/ClickHouse", + default="Altinity/ClickHouse", help="repository to create the release", ) parser.add_argument( diff --git a/tests/ci/run_check.py b/tests/ci/run_check.py index 9c7ba13f8e4d..ef96dc03e18d 100644 --- a/tests/ci/run_check.py +++ b/tests/ci/run_check.py @@ -78,6 +78,7 @@ "ilejn", # Arenadata, responsible for Kerberized Kafka "thomoco", # ClickHouse "BoloniniD", # Seasoned contributor, HSE + "arthurpassos" # Altinity ] } diff --git a/tests/ci/split_build_smoke_check.py b/tests/ci/split_build_smoke_check.py index 41ba6c2fedb3..39561a311875 100644 --- a/tests/ci/split_build_smoke_check.py +++ b/tests/ci/split_build_smoke_check.py @@ -20,7 +20,7 @@ from rerun_helper import RerunHelper -DOCKER_IMAGE = "clickhouse/split-build-smoke-test" +DOCKER_IMAGE = "altinityinfra/split-build-smoke-test" DOWNLOAD_RETRIES_COUNT = 5 RESULT_LOG_NAME = "run.log" CHECK_NAME = "Split build smoke test (actions)" @@ -147,4 +147,8 @@ def get_run_command(build_path, result_folder, server_log_folder, docker_image): report_url, CHECK_NAME, ) + ch_helper.insert_events_into(db="gh-data", table="checks", events=prepared_events) + + if state == "error": + sys.exit(1) diff --git a/tests/ci/stress_check.py b/tests/ci/stress_check.py index b95bf4b8aba0..7c110daf03e4 100644 --- a/tests/ci/stress_check.py +++ b/tests/ci/stress_check.py @@ -114,7 +114,7 @@ def process_results(result_folder, server_log_path, run_log_path): logging.info("Check is already finished according to github status, exiting") sys.exit(0) - docker_image = get_image_with_version(reports_path, "clickhouse/stress-test") + docker_image = get_image_with_version(reports_path, "altinityinfra/stress-test") packages_path = os.path.join(temp_path, "packages") if not os.path.exists(packages_path): @@ -175,3 +175,6 @@ def process_results(result_folder, server_log_path, run_log_path): check_name, ) ch_helper.insert_events_into(db="gh-data", table="checks", events=prepared_events) + + if state == "error": + sys.exit(1) diff --git a/tests/ci/style_check.py b/tests/ci/style_check.py index 1b3037217c83..2260c4d7f924 100644 --- a/tests/ci/style_check.py +++ b/tests/ci/style_check.py @@ -83,7 +83,7 @@ def process_result(result_folder): if not os.path.exists(temp_path): os.makedirs(temp_path) - docker_image = get_image_with_version(temp_path, "clickhouse/style-test") + docker_image = get_image_with_version(temp_path, "altinityinfra/style-test") s3_helper = S3Helper("https://s3.amazonaws.com") cmd = ( @@ -118,3 +118,6 @@ def process_result(result_folder): NAME, ) ch_helper.insert_events_into(db="gh-data", table="checks", events=prepared_events) + + if state == "error": + sys.exit(1) diff --git a/tests/ci/tests/docker_images.json b/tests/ci/tests/docker_images.json index ca5c516bccba..53ad258f6ec9 100644 --- a/tests/ci/tests/docker_images.json +++ b/tests/ci/tests/docker_images.json @@ -1,10 +1,10 @@ { "docker/packager/deb": { - "name": "clickhouse/deb-builder", + "name": "altinityinfra/deb-builder", "dependent": [] }, "docker/packager/binary": { - "name": "clickhouse/binary-builder", + "name": "altinityinfra/binary-builder", "dependent": [ "docker/test/split_build_smoke_test", "docker/test/pvs", @@ -12,156 +12,112 @@ ] }, "docker/test/compatibility/centos": { - "name": "clickhouse/test-old-centos", + "name": "altinityinfra/test-old-centos", "dependent": [] }, "docker/test/compatibility/ubuntu": { - "name": "clickhouse/test-old-ubuntu", + "name": "altinityinfra/test-old-ubuntu", "dependent": [] }, "docker/test/integration/base": { - "name": "clickhouse/integration-test", - "dependent": [] - }, - "docker/test/fuzzer": { - "name": "clickhouse/fuzzer", - "dependent": [] - }, - "docker/test/performance-comparison": { - "name": "clickhouse/performance-comparison", - "dependent": [] - }, - "docker/test/pvs": { - "name": "clickhouse/pvs-test", + "name": "altinityinfra/integration-test", "dependent": [] }, "docker/test/util": { - "name": "clickhouse/test-util", + "name": "altinityinfra/test-util", "dependent": [ "docker/test/base", "docker/test/fasttest" ] }, "docker/test/stateless": { - "name": "clickhouse/stateless-test", + "name": "altinityinfra/stateless-test", "dependent": [ "docker/test/stateful", "docker/test/unit" ] }, "docker/test/stateful": { - "name": "clickhouse/stateful-test", + "name": "altinityinfra/stateful-test", "dependent": [ "docker/test/stress" ] }, "docker/test/unit": { - "name": "clickhouse/unit-test", - "dependent": [] - }, - "docker/test/stress": { - "name": "clickhouse/stress-test", - "dependent": [] - }, - "docker/test/split_build_smoke_test": { - "name": "clickhouse/split-build-smoke-test", - "dependent": [] - }, - "docker/test/codebrowser": { - "name": "clickhouse/codebrowser", + "name": "altinityinfra/unit-test", "dependent": [] }, "docker/test/integration/runner": { - "name": "clickhouse/integration-tests-runner", + "name": "altinityinfra/integration-tests-runner", "dependent": [] }, "docker/test/testflows/runner": { - "name": "clickhouse/testflows-runner", + "name": "altinityinfra/testflows-runner", "dependent": [] }, "docker/test/fasttest": { - "name": "clickhouse/fasttest", - "dependent": [] - }, - "docker/test/style": { - "name": "clickhouse/style-test", + "name": "altinityinfra/fasttest", "dependent": [] }, "docker/test/integration/s3_proxy": { - "name": "clickhouse/s3-proxy", + "name": "altinityinfra/s3-proxy", "dependent": [] }, "docker/test/integration/resolver": { - "name": "clickhouse/python-bottle", + "name": "altinityinfra/python-bottle", "dependent": [] }, "docker/test/integration/helper_container": { - "name": "clickhouse/integration-helper", + "name": "altinityinfra/integration-helper", "dependent": [] }, "docker/test/integration/mysql_golang_client": { - "name": "clickhouse/mysql-golang-client", + "name": "altinityinfra/mysql-golang-client", "dependent": [] }, "docker/test/integration/dotnet_client": { - "name": "clickhouse/dotnet-client", + "name": "altinityinfra/dotnet-client", "dependent": [] }, "docker/test/integration/mysql_java_client": { - "name": "clickhouse/mysql-java-client", + "name": "altinityinfra/mysql-java-client", "dependent": [] }, "docker/test/integration/mysql_js_client": { - "name": "clickhouse/mysql-js-client", + "name": "altinityinfra/mysql-js-client", "dependent": [] }, "docker/test/integration/mysql_php_client": { - "name": "clickhouse/mysql-php-client", + "name": "altinityinfra/mysql-php-client", "dependent": [] }, "docker/test/integration/postgresql_java_client": { - "name": "clickhouse/postgresql-java-client", + "name": "altinityinfra/postgresql-java-client", "dependent": [] }, "docker/test/integration/kerberos_kdc": { - "name": "clickhouse/kerberos-kdc", + "name": "altinityinfra/kerberos-kdc", "dependent": [] }, "docker/test/base": { - "name": "clickhouse/test-base", - "dependent": [ + "name": "altinityinfra/test-base", + "dependent": [ "docker/test/stateless", "docker/test/integration/base", "docker/test/fuzzer", "docker/test/keeper-jepsen" - ] + ] }, "docker/test/integration/kerberized_hadoop": { - "name": "clickhouse/kerberized-hadoop", + "name": "altinityinfra/kerberized-hadoop", "dependent": [] }, "docker/test/sqlancer": { - "name": "clickhouse/sqlancer-test", + "name": "altinityinfra/sqlancer-test", "dependent": [] }, "docker/test/keeper-jepsen": { - "name": "clickhouse/keeper-jepsen-test", - "dependent": [] - }, - "docker/docs/builder": { - "name": "clickhouse/docs-builder", - "only_amd64": true, - "dependent": [ - "docker/docs/check", - "docker/docs/release" - ] - }, - "docker/docs/check": { - "name": "clickhouse/docs-check", - "dependent": [] - }, - "docker/docs/release": { - "name": "clickhouse/docs-release", + "name": "altinityinfra/keeper-jepsen-test", "dependent": [] } } diff --git a/tests/ci/unit_tests_check.py b/tests/ci/unit_tests_check.py index 84c4faa822db..b2a1f837faa7 100644 --- a/tests/ci/unit_tests_check.py +++ b/tests/ci/unit_tests_check.py @@ -25,7 +25,7 @@ from tee_popen import TeePopen -IMAGE_NAME = "clickhouse/unit-test" +IMAGE_NAME = "altinityinfra/unit-test" def get_test_name(line): @@ -173,4 +173,8 @@ def process_result(result_folder): report_url, check_name, ) + ch_helper.insert_events_into(db="gh-data", table="checks", events=prepared_events) + + if state == "error": + sys.exit(1) diff --git a/tests/ci/version_helper.py b/tests/ci/version_helper.py index 9c67191e4c3c..f8e93c582ce9 100755 --- a/tests/ci/version_helper.py +++ b/tests/ci/version_helper.py @@ -48,6 +48,7 @@ def __init__( revision: Union[int, str], git: Git, tweak: str = None, + flavour: str = None, ): self._major = int(major) self._minor = int(minor) @@ -58,6 +59,7 @@ def __init__( if tweak is not None: self._tweak = int(tweak) self._describe = "" + self._flavour = flavour def update(self, part: str) -> "ClickHouseVersion": """If part is valid, returns a new version""" @@ -107,9 +109,12 @@ def describe(self): @property def string(self): - return ".".join( + version_as_string = ".".join( (str(self.major), str(self.minor), str(self.patch), str(self.tweak)) ) + if self._flavour: + version_as_string = f"{version_as_string}.{self._flavour}" + return version_as_string def as_dict(self) -> VERSIONS: return { @@ -129,7 +134,10 @@ def as_tuple(self) -> Tuple[int, int, int, int]: def with_description(self, version_type): if version_type not in VersionType.VALID: raise ValueError(f"version type {version_type} not in {VersionType.VALID}") - self._describe = f"v{self.string}-{version_type}" + if version_type == self._flavour: + self._describe = f"v{self.string}" + else: + self._describe = f"v{self.string}-{version_type}" def __eq__(self, other) -> bool: if not isinstance(self, type(other)): @@ -157,7 +165,7 @@ def __le__(self, other: "ClickHouseVersion") -> bool: class VersionType: LTS = "lts" PRESTABLE = "prestable" - STABLE = "stable" + STABLE = "altinitystable" TESTING = "testing" VALID = (TESTING, PRESTABLE, STABLE, LTS) @@ -205,6 +213,8 @@ def get_version_from_repo( versions["patch"], versions["revision"], git, + # Explicitly use tweak value from version file + tweak=versions["revision"] ) @@ -278,7 +288,7 @@ def update_contributors( cfd.write(content) -def update_version_local(version, version_type="testing"): +def update_version_local(version : ClickHouseVersion, version_type="testing"): update_contributors() version.with_description(version_type) update_cmake_version(version) diff --git a/tests/integration/ci-runner.py b/tests/integration/ci-runner.py index a301869319df..da8346f55165 100755 --- a/tests/integration/ci-runner.py +++ b/tests/integration/ci-runner.py @@ -98,6 +98,7 @@ def get_counters(fname): # Lines like: # [gw0] [ 7%] ERROR test_mysql_protocol/test.py::test_golang_client + # [gw3] [ 40%] PASSED test_replicated_users/test.py::test_rename_replicated[QUOTA] state = line_arr[-2] test_name = line_arr[-1] @@ -255,17 +256,17 @@ def shuffle_test_groups(self): @staticmethod def get_images_names(): return [ - "clickhouse/dotnet-client", - "clickhouse/integration-helper", - "clickhouse/integration-test", - "clickhouse/integration-tests-runner", - "clickhouse/kerberized-hadoop", - "clickhouse/kerberos-kdc", - "clickhouse/mysql-golang-client", - "clickhouse/mysql-java-client", - "clickhouse/mysql-js-client", - "clickhouse/mysql-php-client", - "clickhouse/postgresql-java-client", + "altinityinfra/dotnet-client", + "altinityinfra/integration-helper", + "altinityinfra/integration-test", + "altinityinfra/integration-tests-runner", + "altinityinfra/kerberized-hadoop", + "altinityinfra/kerberos-kdc", + "altinityinfra/mysql-golang-client", + "altinityinfra/mysql-java-client", + "altinityinfra/mysql-js-client", + "altinityinfra/mysql-php-client", + "altinityinfra/postgresql-java-client", ] def _can_run_with(self, path, opt): @@ -462,7 +463,7 @@ def _get_runner_image_cmd(self, repo_path): "--docker-image-version", ): for img in self.get_images_names(): - if img == "clickhouse/integration-tests-runner": + if img == "altinityinfra/integration-tests-runner": runner_version = self.get_image_version(img) logging.info( "Can run with custom docker image version %s", runner_version @@ -905,6 +906,16 @@ def run_impl(self, repo_path, build_path): if "(memory)" in self.params["context_name"]: result_state = "success" + for res in test_result: + # It's not easy to parse output of pytest + # Especially when test names may contain spaces + # Do not allow it to avoid obscure failures + if " " not in res[0]: + continue + logging.warning("Found invalid test name with space: %s", res[0]) + status_text = "Found test with invalid name, see main log" + result_state = "failure" + return result_state, status_text, test_result, [] diff --git a/tests/integration/helpers/cluster.py b/tests/integration/helpers/cluster.py index d0b5e892f5b7..a32321c8dc20 100644 --- a/tests/integration/helpers/cluster.py +++ b/tests/integration/helpers/cluster.py @@ -16,21 +16,29 @@ import urllib.parse import shlex import urllib3 - -from cassandra.policies import RoundRobinPolicy -import cassandra.cluster -import psycopg2 -import pymongo -import pymysql import requests -from confluent_kafka.avro.cached_schema_registry_client import ( - CachedSchemaRegistryClient, -) + +try: + # Please, add modules that required for specific tests only here. + # So contributors will be able to run most tests locally + # without installing tons of unneeded packages that may be not so easy to install. + from cassandra.policies import RoundRobinPolicy + import cassandra.cluster + import psycopg2 + from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT + import pymongo + import pymysql + from confluent_kafka.avro.cached_schema_registry_client import ( + CachedSchemaRegistryClient, + ) + import meilisearch +except Exception as e: + logging.warning(f"Cannot import some modules, some tests may not work: {e}") + from dict2xml import dict2xml from kazoo.client import KazooClient from kazoo.exceptions import KazooException from minio import Minio -from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT from helpers.test_tools import assert_eq_with_retry, exec_query_with_retry from helpers import pytest_xdist_logging_to_separate_files @@ -689,7 +697,7 @@ def setup_keeper_cmd(self, instance, env_variables, docker_compose_yml_dir): binary_path = binary_path[: -len("-server")] env_variables["keeper_binary"] = binary_path - env_variables["image"] = "clickhouse/integration-test:" + self.docker_base_tag + env_variables["image"] = "altinityinfra/integration-test:" + self.docker_base_tag env_variables["user"] = str(os.getuid()) env_variables["keeper_fs"] = "bind" for i in range(1, 4): @@ -1169,7 +1177,7 @@ def add_instance( with_hive=False, hostname=None, env_variables=None, - image="clickhouse/integration-test", + image="altinityinfra/integration-test", tag=None, stay_alive=False, ipv4_address=None, @@ -2643,7 +2651,7 @@ def __init__( copy_common_configs=True, hostname=None, env_variables=None, - image="clickhouse/integration-test", + image="altinityinfra/integration-test", tag="latest", stay_alive=False, ipv4_address=None, diff --git a/tests/integration/helpers/network.py b/tests/integration/helpers/network.py index 63fb2065f9df..c3829e160e2b 100644 --- a/tests/integration/helpers/network.py +++ b/tests/integration/helpers/network.py @@ -248,7 +248,7 @@ def _ensure_container(self): time.sleep(i) image = subprocess.check_output( - "docker images -q clickhouse/integration-helper 2>/dev/null", shell=True + "docker images -q altinityinfra/integration-helper 2>/dev/null", shell=True ) if not image.strip(): print("No network image helper, will try download") @@ -257,16 +257,16 @@ def _ensure_container(self): for i in range(5): try: subprocess.check_call( # STYLE_CHECK_ALLOW_SUBPROCESS_CHECK_CALL - "docker pull clickhouse/integration-helper", shell=True + "docker pull altinityinfra/integration-helper", shell=True ) break except: time.sleep(i) else: - raise Exception("Cannot pull clickhouse/integration-helper image") + raise Exception("Cannot pull altinityinfra/integration-helper image") self._container = self._docker_client.containers.run( - "clickhouse/integration-helper", + "altinityinfra/integration-helper", auto_remove=True, command=("sleep %s" % self.container_exit_timeout), # /run/xtables.lock passed inside for correct iptables --wait diff --git a/tests/integration/runner b/tests/integration/runner index 5a168eeea250..8666258d4851 100755 --- a/tests/integration/runner +++ b/tests/integration/runner @@ -19,7 +19,7 @@ CONFIG_DIR_IN_REPO = "programs/server" INTEGRATION_DIR_IN_REPO = "tests/integration" SRC_DIR_IN_REPO = "src" -DIND_INTEGRATION_TESTS_IMAGE_NAME = "clickhouse/integration-tests-runner" +DIND_INTEGRATION_TESTS_IMAGE_NAME = "altinityinfra/integration-tests-runner" def check_args_and_update_paths(args): if args.clickhouse_root: @@ -226,23 +226,23 @@ if __name__ == "__main__": if args.docker_compose_images_tags is not None: for img_tag in args.docker_compose_images_tags: [image, tag] = img_tag.split(":") - if image == "clickhouse/mysql-golang-client": + if image == "altinityinfra/mysql-golang-client": env_tags += "-e {}={} ".format("DOCKER_MYSQL_GOLANG_CLIENT_TAG", tag) - elif image == "clickhouse/dotnet-client": + elif image == "altinityinfra/dotnet-client": env_tags += "-e {}={} ".format("DOCKER_DOTNET_CLIENT_TAG", tag) - elif image == "clickhouse/mysql-java-client": + elif image == "altinityinfra/mysql-java-client": env_tags += "-e {}={} ".format("DOCKER_MYSQL_JAVA_CLIENT_TAG", tag) - elif image == "clickhouse/mysql-js-client": + elif image == "altinityinfra/mysql-js-client": env_tags += "-e {}={} ".format("DOCKER_MYSQL_JS_CLIENT_TAG", tag) - elif image == "clickhouse/mysql-php-client": + elif image == "altinityinfra/mysql-php-client": env_tags += "-e {}={} ".format("DOCKER_MYSQL_PHP_CLIENT_TAG", tag) - elif image == "clickhouse/postgresql-java-client": + elif image == "altinityinfra/postgresql-java-client": env_tags += "-e {}={} ".format("DOCKER_POSTGRESQL_JAVA_CLIENT_TAG", tag) - elif image == "clickhouse/integration-test": + elif image == "altinityinfra/integration-test": env_tags += "-e {}={} ".format("DOCKER_BASE_TAG", tag) - elif image == "clickhouse/kerberized-hadoop": + elif image == "altinityinfra/kerberized-hadoop": env_tags += "-e {}={} ".format("DOCKER_KERBERIZED_HADOOP_TAG", tag) - elif image == "clickhouse/kerberos-kdc": + elif image == "altinityinfra/kerberos-kdc": env_tags += "-e {}={} ".format("DOCKER_KERBEROS_KDC_TAG", tag) else: logging.info("Unknown image %s" % (image)) diff --git a/tests/integration/test_replicated_users/test.py b/tests/integration/test_replicated_users/test.py index add45d262e63..56383f0d2dfb 100644 --- a/tests/integration/test_replicated_users/test.py +++ b/tests/integration/test_replicated_users/test.py @@ -41,7 +41,7 @@ class Entity: def get_entity_id(entity): - return entity.keyword + return entity.keyword.replace(" ", "_") @pytest.mark.parametrize("entity", entities, ids=get_entity_id) diff --git a/tests/integration/test_s3_zero_copy_replication/test.py b/tests/integration/test_s3_zero_copy_replication/test.py index d7aa4feb1d2f..1ce1047ebec9 100644 --- a/tests/integration/test_s3_zero_copy_replication/test.py +++ b/tests/integration/test_s3_zero_copy_replication/test.py @@ -361,6 +361,8 @@ def test_s3_zero_copy_with_ttl_delete(cluster, large_data, iterations): ) node1.query("OPTIMIZE TABLE ttl_delete_test FINAL") + + node1.query("SYSTEM SYNC REPLICA ttl_delete_test") node2.query("SYSTEM SYNC REPLICA ttl_delete_test") if large_data: diff --git a/tests/integration/test_s3_zero_copy_ttl/test.py b/tests/integration/test_s3_zero_copy_ttl/test.py index 14b4664fcc14..9a782aacef6b 100644 --- a/tests/integration/test_s3_zero_copy_ttl/test.py +++ b/tests/integration/test_s3_zero_copy_ttl/test.py @@ -68,19 +68,27 @@ def test_ttl_move_and_s3(started_cluster): assert node1.query("SELECT COUNT() FROM s3_test_with_ttl") == "30\n" assert node2.query("SELECT COUNT() FROM s3_test_with_ttl") == "30\n" - time.sleep(5) + for attempt in reversed(range(5)): + time.sleep(5) - print( - node1.query( - "SELECT * FROM system.parts WHERE table = 's3_test_with_ttl' FORMAT Vertical" + print( + node1.query( + "SELECT * FROM system.parts WHERE table = 's3_test_with_ttl' FORMAT Vertical" + ) ) - ) - - minio = cluster.minio_client - objects = minio.list_objects(cluster.minio_bucket, "data/", recursive=True) - counter = 0 - for obj in objects: - print("Objectname:", obj.object_name, "metadata:", obj.metadata) - counter += 1 - print("Total objects", counter) + + minio = cluster.minio_client + objects = minio.list_objects(cluster.minio_bucket, "data/", recursive=True) + counter = 0 + for obj in objects: + print(f"Objectname: {obj.object_name}, metadata: {obj.metadata}") + counter += 1 + + print(f"Total objects: {counter}") + + if counter == 300: + break + + print(f"Attempts remaining: {attempt}") + assert counter == 300 diff --git a/tests/integration/test_storage_kafka/test.py b/tests/integration/test_storage_kafka/test.py index a27b5a134e49..45a944b8d93b 100644 --- a/tests/integration/test_storage_kafka/test.py +++ b/tests/integration/test_storage_kafka/test.py @@ -30,12 +30,24 @@ from kafka.protocol.group import MemberAssignment from kafka.admin import NewTopic +from pathlib import Path +from helpers.cluster import run_and_check # protoc --version # libprotoc 3.0.0 # # to create kafka_pb2.py # protoc --python_out=. kafka.proto +# Regenerate _pb2 files on each run, to make sure test doesn't depend installed protobuf version +proto_dir = Path(__file__).parent / "clickhouse_path/format_schemas" +gen_dir = Path(__file__).parent +gen_dir.mkdir(exist_ok=True) +run_and_check( + f"python3 -m grpc_tools.protoc -I{proto_dir!s} --python_out={gen_dir!s} --grpc_python_out={gen_dir!s} \ + {proto_dir!s}/kafka.proto", + shell=True, +) + from . import kafka_pb2 from . import social_pb2 from . import message_with_repeated_pb2 diff --git a/tests/queries/0_stateless/00900_orc_load.sh b/tests/queries/0_stateless/00900_orc_load.sh index b3f2c39e5d2c..62149fa554e1 100755 --- a/tests/queries/0_stateless/00900_orc_load.sh +++ b/tests/queries/0_stateless/00900_orc_load.sh @@ -5,16 +5,13 @@ CUR_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) # shellcheck source=../shell_config.sh . "$CUR_DIR"/../shell_config.sh -DATA_FILE=$CUR_DIR/data_orc/test.orc - ${CLICKHOUSE_CLIENT} --query="DROP TABLE IF EXISTS orc_load" ${CLICKHOUSE_CLIENT} --query="CREATE TABLE orc_load (int Int32, smallint Int8, bigint Int64, float Float32, double Float64, date Date, y String, datetime64 DateTime64(3)) ENGINE = Memory" ${CLICKHOUSE_CLIENT} --query="insert into orc_load values (0, 0, 0, 0, 0, '2019-01-01', 'test1', toDateTime64('2019-01-01 02:03:04.567', 3)), (2147483647, -1, 9223372036854775806, 123.345345, 345345.3453451212, '2019-01-01', 'test2', toDateTime64('2019-01-01 02:03:04.567', 3))" -${CLICKHOUSE_CLIENT} --query="select * from orc_load FORMAT ORC" > $DATA_FILE +${CLICKHOUSE_CLIENT} --query="select * from orc_load FORMAT ORC" > "${CLICKHOUSE_TMP}"/test.orc ${CLICKHOUSE_CLIENT} --query="truncate table orc_load" -cat "$DATA_FILE" | ${CLICKHOUSE_CLIENT} -q "insert into orc_load format ORC" -timeout 3 ${CLICKHOUSE_CLIENT} -q "insert into orc_load format ORC" < $DATA_FILE +cat "${CLICKHOUSE_TMP}"/test.orc | ${CLICKHOUSE_CLIENT} -q "insert into orc_load format ORC" +timeout 3 ${CLICKHOUSE_CLIENT} -q "insert into orc_load format ORC" < "${CLICKHOUSE_TMP}"/test.orc ${CLICKHOUSE_CLIENT} --query="select * from orc_load" ${CLICKHOUSE_CLIENT} --query="drop table orc_load" -rm -rf "$DATA_FILE" diff --git a/tests/testflows/aes_encryption/aes_encryption_env/clickhouse-service.yml b/tests/testflows/aes_encryption/aes_encryption_env/clickhouse-service.yml index 0c9352dbc0b6..74a56b63aabc 100644 --- a/tests/testflows/aes_encryption/aes_encryption_env/clickhouse-service.yml +++ b/tests/testflows/aes_encryption/aes_encryption_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: clickhouse/integration-test + image: altinityinfra/integration-test expose: - "9000" - "9009" diff --git a/tests/testflows/datetime64_extended_range/datetime64_extended_range_env/clickhouse-service.yml b/tests/testflows/datetime64_extended_range/datetime64_extended_range_env/clickhouse-service.yml index 0c9352dbc0b6..74a56b63aabc 100644 --- a/tests/testflows/datetime64_extended_range/datetime64_extended_range_env/clickhouse-service.yml +++ b/tests/testflows/datetime64_extended_range/datetime64_extended_range_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: clickhouse/integration-test + image: altinityinfra/integration-test expose: - "9000" - "9009" diff --git a/tests/testflows/example/example_env/clickhouse-service.yml b/tests/testflows/example/example_env/clickhouse-service.yml index 0c9352dbc0b6..74a56b63aabc 100644 --- a/tests/testflows/example/example_env/clickhouse-service.yml +++ b/tests/testflows/example/example_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: clickhouse/integration-test + image: altinityinfra/integration-test expose: - "9000" - "9009" diff --git a/tests/testflows/extended_precision_data_types/extended-precision-data-type_env/clickhouse-service.yml b/tests/testflows/extended_precision_data_types/extended-precision-data-type_env/clickhouse-service.yml index afb31f77c94c..9162d06bf27d 100644 --- a/tests/testflows/extended_precision_data_types/extended-precision-data-type_env/clickhouse-service.yml +++ b/tests/testflows/extended_precision_data_types/extended-precision-data-type_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: clickhouse/integration-test + image: altinityinfra/integration-test expose: - "9000" - "9009" diff --git a/tests/testflows/kerberos/kerberos_env/clickhouse-service.yml b/tests/testflows/kerberos/kerberos_env/clickhouse-service.yml index 45b975db00d7..7671684f6ee0 100644 --- a/tests/testflows/kerberos/kerberos_env/clickhouse-service.yml +++ b/tests/testflows/kerberos/kerberos_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: clickhouse/integration-test + image: altinityinfra/integration-test expose: - "9000" - "9009" diff --git a/tests/testflows/ldap/authentication/authentication_env/clickhouse-service.yml b/tests/testflows/ldap/authentication/authentication_env/clickhouse-service.yml index 74661f6fa04b..f8cc0a62c67c 100644 --- a/tests/testflows/ldap/authentication/authentication_env/clickhouse-service.yml +++ b/tests/testflows/ldap/authentication/authentication_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: clickhouse/integration-test + image: altinityinfra/integration-test init: true expose: - "9000" diff --git a/tests/testflows/ldap/authentication/ldap_authentication_env/clickhouse-service.yml b/tests/testflows/ldap/authentication/ldap_authentication_env/clickhouse-service.yml index 0c9352dbc0b6..74a56b63aabc 100644 --- a/tests/testflows/ldap/authentication/ldap_authentication_env/clickhouse-service.yml +++ b/tests/testflows/ldap/authentication/ldap_authentication_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: clickhouse/integration-test + image: altinityinfra/integration-test expose: - "9000" - "9009" diff --git a/tests/testflows/ldap/external_user_directory/external_user_directory_env/clickhouse-service.yml b/tests/testflows/ldap/external_user_directory/external_user_directory_env/clickhouse-service.yml index 74661f6fa04b..f8cc0a62c67c 100644 --- a/tests/testflows/ldap/external_user_directory/external_user_directory_env/clickhouse-service.yml +++ b/tests/testflows/ldap/external_user_directory/external_user_directory_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: clickhouse/integration-test + image: altinityinfra/integration-test init: true expose: - "9000" diff --git a/tests/testflows/ldap/external_user_directory/ldap_external_user_directory_env/clickhouse-service.yml b/tests/testflows/ldap/external_user_directory/ldap_external_user_directory_env/clickhouse-service.yml index 0c9352dbc0b6..74a56b63aabc 100644 --- a/tests/testflows/ldap/external_user_directory/ldap_external_user_directory_env/clickhouse-service.yml +++ b/tests/testflows/ldap/external_user_directory/ldap_external_user_directory_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: clickhouse/integration-test + image: altinityinfra/integration-test expose: - "9000" - "9009" diff --git a/tests/testflows/ldap/role_mapping/ldap_role_mapping_env/clickhouse-service.yml b/tests/testflows/ldap/role_mapping/ldap_role_mapping_env/clickhouse-service.yml index 0c9352dbc0b6..74a56b63aabc 100644 --- a/tests/testflows/ldap/role_mapping/ldap_role_mapping_env/clickhouse-service.yml +++ b/tests/testflows/ldap/role_mapping/ldap_role_mapping_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: clickhouse/integration-test + image: altinityinfra/integration-test expose: - "9000" - "9009" diff --git a/tests/testflows/ldap/role_mapping/role_mapping_env/clickhouse-service.yml b/tests/testflows/ldap/role_mapping/role_mapping_env/clickhouse-service.yml index 7ff0139ab9be..3fe80bfce343 100644 --- a/tests/testflows/ldap/role_mapping/role_mapping_env/clickhouse-service.yml +++ b/tests/testflows/ldap/role_mapping/role_mapping_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: clickhouse/integration-test + image: altinityinfra/integration-test init: true expose: - "9000" diff --git a/tests/testflows/map_type/map_type_env/clickhouse-service.yml b/tests/testflows/map_type/map_type_env/clickhouse-service.yml index afb31f77c94c..9162d06bf27d 100755 --- a/tests/testflows/map_type/map_type_env/clickhouse-service.yml +++ b/tests/testflows/map_type/map_type_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: clickhouse/integration-test + image: altinityinfra/integration-test expose: - "9000" - "9009" diff --git a/tests/testflows/rbac/rbac_env/clickhouse-service.yml b/tests/testflows/rbac/rbac_env/clickhouse-service.yml index c808372d7e94..4634f3b8721f 100755 --- a/tests/testflows/rbac/rbac_env/clickhouse-service.yml +++ b/tests/testflows/rbac/rbac_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: clickhouse/integration-test + image: altinityinfra/integration-test init: true expose: - "9000" diff --git a/tests/testflows/runner b/tests/testflows/runner index 0208512762ce..1cf2a784ca0e 100755 --- a/tests/testflows/runner +++ b/tests/testflows/runner @@ -14,7 +14,7 @@ DEFAULT_CLICKHOUSE_ROOT = os.path.abspath(os.path.join(CUR_FILE_DIR, "../../")) CURRENT_WORK_DIR = os.getcwd() CONTAINER_NAME = "clickhouse_testflows_tests" -DIND_TESTFLOWS_TESTS_IMAGE_NAME = "clickhouse/testflows-runner" +DIND_TESTFLOWS_TESTS_IMAGE_NAME = "altinityinfra/testflows-runner" def check_args_and_update_paths(args): if not os.path.isabs(args.binary): diff --git a/tests/testflows/window_functions/window_functions_env/clickhouse-service.yml b/tests/testflows/window_functions/window_functions_env/clickhouse-service.yml index afb31f77c94c..9162d06bf27d 100755 --- a/tests/testflows/window_functions/window_functions_env/clickhouse-service.yml +++ b/tests/testflows/window_functions/window_functions_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: clickhouse/integration-test + image: altinityinfra/integration-test expose: - "9000" - "9009" diff --git a/utils/clickhouse-docker b/utils/clickhouse-docker index cfe515f1de54..34b637f0eaad 100755 --- a/utils/clickhouse-docker +++ b/utils/clickhouse-docker @@ -26,11 +26,11 @@ then # https://stackoverflow.com/a/39454426/1555175 wget -nv https://registry.hub.docker.com/v1/repositories/clickhouse/clickhouse-server/tags -O - | sed -e 's/[][]//g' -e 's/"//g' -e 's/ //g' | tr '}' '\n' | awk -F: '{print $3}' else - docker pull clickhouse/clickhouse-server:${param} + docker pull altinityinfra/clickhouse-server:${param} tmp_dir=$(mktemp -d -t ci-XXXXXXXXXX) # older version require /nonexistent folder to exist to run clickhouse client :D chmod 777 ${tmp_dir} set -e - containerid=`docker run -v${tmp_dir}:/nonexistent -d clickhouse/clickhouse-server:${param}` + containerid=`docker run -v${tmp_dir}:/nonexistent -d altinityinfra/clickhouse-server:${param}` set +e while : do From 09087c6186b81e8a61ab1ebf19d4a1ac32fd80b2 Mon Sep 17 00:00:00 2001 From: Dmitry Novik Date: Tue, 7 Jun 2022 17:20:43 +0200 Subject: [PATCH 12/72] Merge pull request #36944 from excitoon-favorites/better_exp_smooth --- src/Processors/Transforms/WindowTransform.cpp | 327 ++++++++++-------- .../02020_exponential_smoothing.reference | 112 +++--- .../02020_exponential_smoothing.sql | 92 ++++- 3 files changed, 338 insertions(+), 193 deletions(-) diff --git a/src/Processors/Transforms/WindowTransform.cpp b/src/Processors/Transforms/WindowTransform.cpp index b81ed099915f..2a2fed1cc078 100644 --- a/src/Processors/Transforms/WindowTransform.cpp +++ b/src/Processors/Transforms/WindowTransform.cpp @@ -1,5 +1,7 @@ #include +#include + #include #include #include @@ -14,6 +16,7 @@ #include #include + namespace DB { @@ -1538,65 +1541,21 @@ struct WindowFunctionDenseRank final : public WindowFunction namespace recurrent_detail { - template T getLastValueFromInputColumn(const WindowTransform * /*transform*/, size_t /*function_index*/, size_t /*column_index*/) + template T getValue(const WindowTransform * /*transform*/, size_t /*function_index*/, size_t /*column_index*/, RowNumber /*row*/) { - throw Exception(ErrorCodes::NOT_IMPLEMENTED, "getLastValueFromInputColumn() is not implemented for {} type", typeid(T).name()); + throw Exception(ErrorCodes::NOT_IMPLEMENTED, "recurrent_detail::getValue() is not implemented for {} type", typeid(T).name()); } - template<> Float64 getLastValueFromInputColumn(const WindowTransform * transform, size_t function_index, size_t column_index) + template<> Float64 getValue(const WindowTransform * transform, size_t function_index, size_t column_index, RowNumber row) { const auto & workspace = transform->workspaces[function_index]; - auto current_row = transform->current_row; - - if (current_row.row == 0) - { - if (current_row.block > 0) - { - const auto & column = transform->blockAt(current_row.block - 1).input_columns[workspace.argument_column_indices[column_index]]; - return column->getFloat64(column->size() - 1); - } - } - else - { - const auto & column = transform->blockAt(current_row.block).input_columns[workspace.argument_column_indices[column_index]]; - return column->getFloat64(current_row.row - 1); - } - - return 0; - } - - template T getLastValueFromState(const WindowTransform * /*transform*/, size_t /*function_index*/, size_t /*data_index*/) - { - throw Exception(ErrorCodes::NOT_IMPLEMENTED, "getLastValueFromInputColumn() is not implemented for {} type", typeid(T).name()); - } - - template<> Float64 getLastValueFromState(const WindowTransform * transform, size_t function_index, size_t data_index) - { - const auto & workspace = transform->workspaces[function_index]; - if (workspace.aggregate_function_state.data() == nullptr) - { - return 0.0; - } - else - { - return static_cast(static_cast(workspace.aggregate_function_state.data()))[data_index]; - } - } - - template void setValueToState(const WindowTransform * /*transform*/, size_t /*function_index*/, T /*value*/, size_t /*data_index*/) - { - throw Exception(ErrorCodes::NOT_IMPLEMENTED, "setValueToState() is not implemented for {} type", typeid(T).name()); - } - - template<> void setValueToState(const WindowTransform * transform, size_t function_index, Float64 value, size_t data_index) - { - const auto & workspace = transform->workspaces[function_index]; - static_cast(static_cast(workspace.aggregate_function_state.data()))[data_index] = value; + const auto & column = transform->blockAt(row.block).input_columns[workspace.argument_column_indices[column_index]]; + return column->getFloat64(row.row); } template void setValueToOutputColumn(const WindowTransform * /*transform*/, size_t /*function_index*/, T /*value*/) { - throw Exception(ErrorCodes::NOT_IMPLEMENTED, "setValueToOutputColumn() is not implemented for {} type", typeid(T).name()); + throw Exception(ErrorCodes::NOT_IMPLEMENTED, "recurrent_detail::setValueToOutputColumn() is not implemented for {} type", typeid(T).name()); } template<> void setValueToOutputColumn(const WindowTransform * transform, size_t function_index, Float64 value) @@ -1607,82 +1566,77 @@ namespace recurrent_detail assert_cast(to).getData().push_back(value); } +} - template T getCurrentValueFromInputColumn(const WindowTransform * /*transform*/, size_t /*function_index*/, size_t /*column_index*/) +struct WindowFunctionHelpers +{ + template + static T getValue(const WindowTransform * transform, size_t function_index, size_t column_index, RowNumber row) { - throw Exception(ErrorCodes::NOT_IMPLEMENTED, "getCurrentValueFromInputColumn() is not implemented for {} type", typeid(T).name()); + return recurrent_detail::getValue(transform, function_index, column_index, row); } - template<> Float64 getCurrentValueFromInputColumn(const WindowTransform * transform, size_t function_index, size_t column_index) + template + static void setValueToOutputColumn(const WindowTransform * transform, size_t function_index, T value) { - const auto & workspace = transform->workspaces[function_index]; - auto current_row = transform->current_row; - const auto & current_block = transform->blockAt(current_row); - - return (*current_block.input_columns[workspace.argument_column_indices[column_index]]).getFloat64(transform->current_row.row); + recurrent_detail::setValueToOutputColumn(transform, function_index, value); } -} +}; -template -struct RecurrentWindowFunction : public WindowFunction +template +struct StatefulWindowFunction : public WindowFunction { - RecurrentWindowFunction(const std::string & name_, + StatefulWindowFunction(const std::string & name_, const DataTypes & argument_types_, const Array & parameters_) : WindowFunction(name_, argument_types_, parameters_) { } - size_t sizeOfData() const override { return sizeof(Float64)*state_size; } + size_t sizeOfData() const override { return sizeof(State); } size_t alignOfData() const override { return 1; } void create(AggregateDataPtr __restrict place) const override { - auto * const state = static_cast(static_cast(place)); - for (size_t i = 0; i < state_size; ++i) - state[i] = 0.0; + new (place) State(); } - template - static T getLastValueFromInputColumn(const WindowTransform * transform, size_t function_index, size_t column_index) + void destroy(AggregateDataPtr __restrict place) const noexcept override { - return recurrent_detail::getLastValueFromInputColumn(transform, function_index, column_index); + auto * const state = static_cast(static_cast(place)); + state->~State(); } - template - static T getLastValueFromState(const WindowTransform * transform, size_t function_index, size_t data_index) + State & getState(const WindowFunctionWorkspace & workspace) { - return recurrent_detail::getLastValueFromState(transform, function_index, data_index); - } - - template - static void setValueToState(const WindowTransform * transform, size_t function_index, T value, size_t data_index) - { - recurrent_detail::setValueToState(transform, function_index, value, data_index); + return *static_cast(static_cast(workspace.aggregate_function_state.data())); } +}; - template - static void setValueToOutputColumn(const WindowTransform * transform, size_t function_index, T value) - { - recurrent_detail::setValueToOutputColumn(transform, function_index, value); - } +struct ExponentialTimeDecayedSumState +{ + RowNumber previous_frame_start; + RowNumber previous_frame_end; + Float64 previous_time; + Float64 previous_sum; +}; - template - static T getCurrentValueFromInputColumn(const WindowTransform * transform, size_t function_index, size_t column_index) - { - return recurrent_detail::getCurrentValueFromInputColumn(transform, function_index, column_index); - } +struct ExponentialTimeDecayedAvgState +{ + RowNumber previous_frame_start; + RowNumber previous_frame_end; + Float64 previous_time; + Float64 previous_sum; + Float64 previous_count; }; -struct WindowFunctionExponentialTimeDecayedSum final : public RecurrentWindowFunction<1> +struct WindowFunctionExponentialTimeDecayedSum final : public StatefulWindowFunction { static constexpr size_t ARGUMENT_VALUE = 0; static constexpr size_t ARGUMENT_TIME = 1; - static constexpr size_t STATE_SUM = 0; - WindowFunctionExponentialTimeDecayedSum(const std::string & name_, const DataTypes & argument_types_, const Array & parameters_) - : RecurrentWindowFunction(name_, argument_types_, parameters_) + : StatefulWindowFunction(name_, argument_types_, parameters_) { if (parameters_.size() != 1) { @@ -1724,33 +1678,60 @@ struct WindowFunctionExponentialTimeDecayedSum final : public RecurrentWindowFun void windowInsertResultInto(const WindowTransform * transform, size_t function_index) override { - Float64 last_sum = getLastValueFromState(transform, function_index, STATE_SUM); - Float64 last_t = getLastValueFromInputColumn(transform, function_index, ARGUMENT_TIME); + const auto & workspace = transform->workspaces[function_index]; + auto & state = getState(workspace); + + Float64 result = 0; + Float64 curr_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, transform->current_row); - Float64 x = getCurrentValueFromInputColumn(transform, function_index, ARGUMENT_VALUE); - Float64 t = getCurrentValueFromInputColumn(transform, function_index, ARGUMENT_TIME); + if (state.previous_frame_start <= transform->frame_start + && transform->frame_start < state.previous_frame_end + && state.previous_frame_end <= transform->frame_end) + { + for (RowNumber i = state.previous_frame_start; i < transform->frame_start; transform->advanceRowNumber(i)) + { + Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + result -= std::exp((prev_t - curr_t) / decay_length) * prev_val; + } + result += std::exp((state.previous_time - curr_t) / decay_length) * state.previous_sum; + for (RowNumber i = state.previous_frame_end; i < transform->frame_end; transform->advanceRowNumber(i)) + { + Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + result += std::exp((prev_t - curr_t) / decay_length) * prev_val; + } + } + else + { + for (RowNumber i = transform->frame_start; i < transform->frame_end; transform->advanceRowNumber(i)) + { + Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + result += std::exp((prev_t - curr_t) / decay_length) * prev_val; + } + } - Float64 c = exp((last_t - t) / decay_length); - Float64 result = x + c * last_sum; + state.previous_sum = result; + state.previous_time = curr_t; + state.previous_frame_start = transform->frame_start; + state.previous_frame_end = transform->frame_end; - setValueToOutputColumn(transform, function_index, result); - setValueToState(transform, function_index, result, STATE_SUM); + WindowFunctionHelpers::setValueToOutputColumn(transform, function_index, result); } private: Float64 decay_length; }; -struct WindowFunctionExponentialTimeDecayedMax final : public RecurrentWindowFunction<1> +struct WindowFunctionExponentialTimeDecayedMax final : public WindowFunction { static constexpr size_t ARGUMENT_VALUE = 0; static constexpr size_t ARGUMENT_TIME = 1; - static constexpr size_t STATE_MAX = 0; - WindowFunctionExponentialTimeDecayedMax(const std::string & name_, const DataTypes & argument_types_, const Array & parameters_) - : RecurrentWindowFunction(name_, argument_types_, parameters_) + : WindowFunction(name_, argument_types_, parameters_) { if (parameters_.size() != 1) { @@ -1792,32 +1773,35 @@ struct WindowFunctionExponentialTimeDecayedMax final : public RecurrentWindowFun void windowInsertResultInto(const WindowTransform * transform, size_t function_index) override { - Float64 last_max = getLastValueFromState(transform, function_index, STATE_MAX); - Float64 last_t = getLastValueFromInputColumn(transform, function_index, ARGUMENT_TIME); + Float64 result = std::numeric_limits::lowest(); + Float64 curr_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, transform->current_row); - Float64 x = getCurrentValueFromInputColumn(transform, function_index, ARGUMENT_VALUE); - Float64 t = getCurrentValueFromInputColumn(transform, function_index, ARGUMENT_TIME); + for (RowNumber i = transform->frame_start; i < transform->frame_end; transform->advanceRowNumber(i)) + { + Float64 value = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); + Float64 t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); - Float64 c = exp((last_t - t) / decay_length); - Float64 result = std::max(x, c * last_max); + /// Avoiding extra calls to `exp` and multiplications. + if (value > result || t > curr_t || result < 0) + { + result = std::max(std::exp((t - curr_t) / decay_length) * value, result); + } + } - setValueToOutputColumn(transform, function_index, result); - setValueToState(transform, function_index, result, STATE_MAX); + WindowFunctionHelpers::setValueToOutputColumn(transform, function_index, result); } private: Float64 decay_length; }; -struct WindowFunctionExponentialTimeDecayedCount final : public RecurrentWindowFunction<1> +struct WindowFunctionExponentialTimeDecayedCount final : public StatefulWindowFunction { static constexpr size_t ARGUMENT_TIME = 0; - static constexpr size_t STATE_COUNT = 0; - WindowFunctionExponentialTimeDecayedCount(const std::string & name_, const DataTypes & argument_types_, const Array & parameters_) - : RecurrentWindowFunction(name_, argument_types_, parameters_) + : StatefulWindowFunction(name_, argument_types_, parameters_) { if (parameters_.size() != 1) { @@ -1851,33 +1835,57 @@ struct WindowFunctionExponentialTimeDecayedCount final : public RecurrentWindowF void windowInsertResultInto(const WindowTransform * transform, size_t function_index) override { - Float64 last_count = getLastValueFromState(transform, function_index, STATE_COUNT); - Float64 last_t = getLastValueFromInputColumn(transform, function_index, ARGUMENT_TIME); + const auto & workspace = transform->workspaces[function_index]; + auto & state = getState(workspace); + + Float64 result = 0; + Float64 curr_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, transform->current_row); - Float64 t = getCurrentValueFromInputColumn(transform, function_index, ARGUMENT_TIME); + if (state.previous_frame_start <= transform->frame_start + && transform->frame_start < state.previous_frame_end + && state.previous_frame_end <= transform->frame_end) + { + for (RowNumber i = state.previous_frame_start; i < transform->frame_start; transform->advanceRowNumber(i)) + { + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + result -= std::exp((prev_t - curr_t) / decay_length); + } + result += std::exp((state.previous_time - curr_t) / decay_length) * state.previous_sum; + for (RowNumber i = state.previous_frame_end; i < transform->frame_end; transform->advanceRowNumber(i)) + { + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + result += std::exp((prev_t - curr_t) / decay_length); + } + } + else + { + for (RowNumber i = transform->frame_start; i < transform->frame_end; transform->advanceRowNumber(i)) + { + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + result += std::exp((prev_t - curr_t) / decay_length); + } + } - Float64 c = exp((last_t - t) / decay_length); - Float64 result = c * last_count + 1.0; + state.previous_sum = result; + state.previous_time = curr_t; + state.previous_frame_start = transform->frame_start; + state.previous_frame_end = transform->frame_end; - setValueToOutputColumn(transform, function_index, result); - setValueToState(transform, function_index, result, STATE_COUNT); + WindowFunctionHelpers::setValueToOutputColumn(transform, function_index, result); } private: Float64 decay_length; }; -struct WindowFunctionExponentialTimeDecayedAvg final : public RecurrentWindowFunction<2> +struct WindowFunctionExponentialTimeDecayedAvg final : public StatefulWindowFunction { static constexpr size_t ARGUMENT_VALUE = 0; static constexpr size_t ARGUMENT_TIME = 1; - static constexpr size_t STATE_SUM = 0; - static constexpr size_t STATE_COUNT = 1; - WindowFunctionExponentialTimeDecayedAvg(const std::string & name_, const DataTypes & argument_types_, const Array & parameters_) - : RecurrentWindowFunction(name_, argument_types_, parameters_) + : StatefulWindowFunction(name_, argument_types_, parameters_) { if (parameters_.size() != 1) { @@ -1919,21 +1927,60 @@ struct WindowFunctionExponentialTimeDecayedAvg final : public RecurrentWindowFun void windowInsertResultInto(const WindowTransform * transform, size_t function_index) override { - Float64 last_sum = getLastValueFromState(transform, function_index, STATE_SUM); - Float64 last_count = getLastValueFromState(transform, function_index, STATE_COUNT); - Float64 last_t = getLastValueFromInputColumn(transform, function_index, ARGUMENT_TIME); + const auto & workspace = transform->workspaces[function_index]; + auto & state = getState(workspace); + + Float64 count = 0; + Float64 sum = 0; + Float64 curr_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, transform->current_row); + + if (state.previous_frame_start <= transform->frame_start + && transform->frame_start < state.previous_frame_end + && state.previous_frame_end <= transform->frame_end) + { + for (RowNumber i = state.previous_frame_start; i < transform->frame_start; transform->advanceRowNumber(i)) + { + Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + Float64 decay = std::exp((prev_t - curr_t) / decay_length); + sum -= decay * prev_val; + count -= decay; + } - Float64 x = getCurrentValueFromInputColumn(transform, function_index, ARGUMENT_VALUE); - Float64 t = getCurrentValueFromInputColumn(transform, function_index, ARGUMENT_TIME); + { + Float64 decay = std::exp((state.previous_time - curr_t) / decay_length); + sum += decay * state.previous_sum; + count += decay * state.previous_count; + } + + for (RowNumber i = state.previous_frame_end; i < transform->frame_end; transform->advanceRowNumber(i)) + { + Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + Float64 decay = std::exp((prev_t - curr_t) / decay_length); + sum += decay * prev_val; + count += decay; + } + } + else + { + for (RowNumber i = transform->frame_start; i < transform->frame_end; transform->advanceRowNumber(i)) + { + Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + Float64 decay = std::exp((prev_t - curr_t) / decay_length); + sum += decay * prev_val; + count += decay; + } + } - Float64 c = exp((last_t - t) / decay_length); - Float64 new_sum = c * last_sum + x; - Float64 new_count = c * last_count + 1.0; - Float64 result = new_sum / new_count; + state.previous_sum = sum; + state.previous_count = count; + state.previous_time = curr_t; + state.previous_frame_start = transform->frame_start; + state.previous_frame_end = transform->frame_end; - setValueToOutputColumn(transform, function_index, result); - setValueToState(transform, function_index, new_sum, STATE_SUM); - setValueToState(transform, function_index, new_count, STATE_COUNT); + WindowFunctionHelpers::setValueToOutputColumn(transform, function_index, sum/count); } private: diff --git a/tests/queries/0_stateless/02020_exponential_smoothing.reference b/tests/queries/0_stateless/02020_exponential_smoothing.reference index b3c234206783..334d32e1c163 100644 --- a/tests/queries/0_stateless/02020_exponential_smoothing.reference +++ b/tests/queries/0_stateless/02020_exponential_smoothing.reference @@ -1,13 +1,14 @@ +exponentialMovingAverage 1 0 0.5 0 1 0.25 0 2 0.125 -0 3 0.0625 -0 4 0.03125 -0 5 0.015625 -0 6 0.0078125 -0 7 0.00390625 -0 8 0.001953125 -0 9 0.0009765625 +0 3 0.062 +0 4 0.031 +0 5 0.016 +0 6 0.008 +0 7 0.004 +0 8 0.002 +0 9 0.001 1 0 0.067 0 1 0.062 0 2 0.058 @@ -128,16 +129,17 @@ 0 47 0.129 ██████▍ 0 48 0.065 ███▏ 0 49 0.032 █▌ +exponentialTimeDecayedSum 1 0 1 -0 1 0.36787944117144233 -0 2 0.1353352832366127 -0 3 0.04978706836786395 -0 4 0.018315638888734186 -0 5 0.00673794699908547 -0 6 0.0024787521766663594 -0 7 0.0009118819655545166 -0 8 0.00033546262790251196 -0 9 0.0001234098040866796 +0 1 0.368 +0 2 0.135 +0 3 0.05 +0 4 0.018 +0 5 0.007 +0 6 0.002 +0 7 0.001 +0 8 0 +0 9 0 1 0 1 0 1 0.905 0 2 0.819 @@ -258,16 +260,17 @@ 0 47 0.136 ██████▋ 0 48 0.05 ██▌ 0 49 0.018 ▊ +exponentialTimeDecayedMax 1 0 1 -0 1 0.36787944117144233 -0 2 0.1353352832366127 -0 3 0.04978706836786395 -0 4 0.018315638888734186 -0 5 0.00673794699908547 -0 6 0.0024787521766663594 -0 7 0.0009118819655545166 -0 8 0.00033546262790251196 -0 9 0.0001234098040866796 +0 1 0.368 +0 2 0.135 +0 3 0.05 +0 4 0.018 +0 5 0.007 +0 6 0.002 +0 7 0.001 +0 8 0 +0 9 0 1 0 1 0 1 0.905 0 2 0.819 @@ -388,16 +391,17 @@ 0 47 0.135 ██████▋ 0 48 0.05 ██▍ 0 49 0.018 ▊ +exponentialTimeDecayedCount 1 0 1 -0 1 1.3678794411714423 -0 2 1.5032147244080551 -0 3 1.553001792775919 -0 4 1.5713174316646532 -0 5 1.5780553786637386 -0 6 1.5805341308404048 -0 7 1.5814460128059595 -0 8 1.581781475433862 -0 9 1.5819048852379487 +0 1 1.368 +0 2 1.503 +0 3 1.553 +0 4 1.571 +0 5 1.578 +0 6 1.581 +0 7 1.581 +0 8 1.582 +0 9 1.582 1 0 1 0 1 1.905 0 2 2.724 @@ -518,16 +522,17 @@ 0 47 10.422 ██████████████████████████ 0 48 10.43 ██████████████████████████ 0 49 10.438 ██████████████████████████ +exponentialTimeDecayedAvg 1 0 1 -0 1 0.2689414213699951 -0 2 0.09003057317038046 -0 3 0.032058603280084995 -0 4 0.01165623095603961 -0 5 0.004269778545282112 -0 6 0.0015683003158864733 -0 7 0.000576612769687006 -0 8 0.00021207899644323433 -0 9 0.00007801341612780745 +0 1 0.269 +0 2 0.09 +0 3 0.032 +0 4 0.012 +0 5 0.004 +0 6 0.002 +0 7 0.001 +0 8 0 +0 9 0 1 0 1 0 1 0.475 0 2 0.301 @@ -648,3 +653,24 @@ 0 47 0.206 ████████████████████▋ 0 48 0.201 ████████████████████ 0 49 0.196 ███████████████████▌ +Check `exponentialTimeDecayed.*` supports sliding windows +2 1 3.010050167084 2 3.030251507111 0.993333444442 +1 2 7.060905027605 4.080805360107 4.02030134086 1.756312382816 +0 3 12.091654548833 5.101006700134 5.000500014167 2.418089094006 +4 4 11.050650848754 5.050250835421 5.000500014167 2.209909172572 +5 5 9.970249502081 5 5.000500014167 1.993850509716 +1 6 20.07305726224 10.202013400268 5.000500014167 4.014210020072 +0 7 15.991544871125 10.100501670842 3.98029867414 4.017674596889 +10 8 10.980198673307 10 2.970248507056 3.696727276261 +Check `exponentialTimeDecayedMax` works with negative values +2 1 -1.010050167084 +1 2 -1 +10 3 -0.990049833749 +4 4 -0.980198673307 +5 5 -1.010050167084 +1 6 -1 +10 7 -0.990049833749 +10 8 -0.980198673307 +10 9 -9.801986733068 +9.81 10 -9.801986733068 +9.9 11 -9.712388869079 diff --git a/tests/queries/0_stateless/02020_exponential_smoothing.sql b/tests/queries/0_stateless/02020_exponential_smoothing.sql index a39b09a883da..462081b12d6c 100644 --- a/tests/queries/0_stateless/02020_exponential_smoothing.sql +++ b/tests/queries/0_stateless/02020_exponential_smoothing.sql @@ -1,5 +1,6 @@ --- exponentialMovingAverage -SELECT number = 0 AS value, number AS time, exponentialMovingAverage(1)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10); +SELECT 'exponentialMovingAverage'; + +SELECT value, time, round(exp_smooth, 3) FROM (SELECT number = 0 AS value, number AS time, exponentialMovingAverage(1)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10)); SELECT value, time, round(exp_smooth, 3) FROM (SELECT number = 0 AS value, number AS time, exponentialMovingAverage(10)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10)); SELECT number AS value, number AS time, exponentialMovingAverage(1)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10); @@ -32,8 +33,9 @@ FROM FROM numbers(50) ); --- exponentialTimeDecayedSum -SELECT number = 0 AS value, number AS time, exponentialTimeDecayedSum(1)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10); +SELECT 'exponentialTimeDecayedSum'; + +SELECT value, time, round(exp_smooth, 3) FROM (SELECT number = 0 AS value, number AS time, exponentialTimeDecayedSum(1)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10)); SELECT value, time, round(exp_smooth, 3) FROM (SELECT number = 0 AS value, number AS time, exponentialTimeDecayedSum(10)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10)); SELECT number AS value, number AS time, exponentialTimeDecayedSum(1)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10); @@ -66,8 +68,9 @@ FROM FROM numbers(50) ); --- exponentialTimeDecayedMax -SELECT number = 0 AS value, number AS time, exponentialTimeDecayedMax(1)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10); +SELECT 'exponentialTimeDecayedMax'; + +SELECT value, time, round(exp_smooth, 3) FROM (SELECT number = 0 AS value, number AS time, exponentialTimeDecayedMax(1)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10)); SELECT value, time, round(exp_smooth, 3) FROM (SELECT number = 0 AS value, number AS time, exponentialTimeDecayedMax(10)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10)); SELECT number AS value, number AS time, exponentialTimeDecayedMax(1)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10); @@ -100,8 +103,9 @@ FROM FROM numbers(50) ); --- exponentialTimeDecayedCount -SELECT number = 0 AS value, number AS time, exponentialTimeDecayedCount(1)(time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10); +SELECT 'exponentialTimeDecayedCount'; + +SELECT value, time, round(exp_smooth, 3) FROM (SELECT number = 0 AS value, number AS time, exponentialTimeDecayedCount(1)(time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10)); SELECT value, time, round(exp_smooth, 3) FROM (SELECT number = 0 AS value, number AS time, exponentialTimeDecayedCount(10)(time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10)); SELECT number AS value, number AS time, exponentialTimeDecayedCount(1)(time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10); @@ -134,8 +138,9 @@ FROM FROM numbers(50) ); --- exponentialTimeDecayedAvg -SELECT number = 0 AS value, number AS time, exponentialTimeDecayedAvg(1)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10); +SELECT 'exponentialTimeDecayedAvg'; + +SELECT value, time, round(exp_smooth, 3) FROM (SELECT number = 0 AS value, number AS time, exponentialTimeDecayedAvg(1)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10)); SELECT value, time, round(exp_smooth, 3) FROM (SELECT number = 0 AS value, number AS time, exponentialTimeDecayedAvg(10)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10)); SELECT number AS value, number AS time, exponentialTimeDecayedAvg(1)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(10); @@ -167,3 +172,70 @@ FROM exponentialTimeDecayedAvg(100)(value, time) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS exp_smooth FROM numbers(50) ); + +SELECT 'Check `exponentialTimeDecayed.*` supports sliding windows'; + +SELECT + x, + t, + round(sum, 12), + round(max, 12), + round(count, 12), + round(avg, 12) +FROM +( + SELECT + d[1] AS x, + d[2] AS t, + exponentialTimeDecayedSum(100)(x, t) OVER w AS sum, + exponentialTimeDecayedMax(100)(x, t) OVER w AS max, + exponentialTimeDecayedCount(100)(t) OVER w AS count, + exponentialTimeDecayedAvg(100)(x, t) OVER w AS avg + FROM + ( + SELECT [[2, 1], [1, 2], [0, 3], [4, 4], [5, 5], [1, 6], [0, 7], [10, 8]] AS d + ) + ARRAY JOIN d + WINDOW w AS (ORDER BY 1 ASC Rows BETWEEN 2 PRECEDING AND 2 FOLLOWING) +); + +SELECT + x, + t, + round(sum, 12), + round(max, 12), + round(count, 12), + round(avg, 12) +FROM +( + SELECT + sin(number) AS x, + number AS t, + exponentialTimeDecayedSum(100)(x, t) OVER w AS sum, + exponentialTimeDecayedMax(100)(x, t) OVER w AS max, + exponentialTimeDecayedCount(100)(t) OVER w AS count, + exponentialTimeDecayedAvg(100)(x, t) OVER w AS avg + FROM numbers(1000000) + WINDOW w AS (ORDER BY 1 ASC Rows BETWEEN 2 PRECEDING AND 2 FOLLOWING) +) +FORMAT `Null`; + +SELECT 'Check `exponentialTimeDecayedMax` works with negative values'; + +SELECT + x, + t, + round(max, 12) +FROM +( + SELECT + d[1] AS x, + d[2] AS t, + exponentialTimeDecayedMax(100)(-x, t) OVER w AS max + FROM + ( + SELECT [[2, 1], [1, 2], [10, 3], [4, 4], [5, 5], [1, 6], [10, 7], [10, 8], [10, 9], [9.81, 10], [9.9, 11]] AS d + ) + ARRAY JOIN d + WINDOW w AS (ORDER BY 1 ASC Rows BETWEEN 2 PRECEDING AND 2 FOLLOWING) +); From 6f773824d1ada83bd7dbba465a280eb32bef9ba2 Mon Sep 17 00:00:00 2001 From: Dmitry Novik Date: Mon, 20 Jun 2022 20:03:26 +0200 Subject: [PATCH 13/72] Merge pull request #34632 from excitoon-favorites/optimizedprocessing --- src/Core/Settings.h | 1 + src/Interpreters/ExpressionAnalyzer.cpp | 14 +- src/Interpreters/ExpressionAnalyzer.h | 1 + src/Interpreters/InterpreterSelectQuery.cpp | 8 +- src/Interpreters/InterpreterSelectQuery.h | 3 + src/Interpreters/WindowDescription.h | 4 + .../QueryPlan/Optimizations/Optimizations.h | 9 +- ...reuseStorageOrderingForWindowFunctions.cpp | 122 ++++++++++++++++++ .../QueryPlan/ReadFromMergeTree.cpp | 26 +++- src/Processors/QueryPlan/ReadFromMergeTree.h | 7 + src/Processors/QueryPlan/SortingStep.cpp | 6 + src/Processors/QueryPlan/SortingStep.h | 2 + src/Processors/QueryPlan/WindowStep.cpp | 5 + src/Processors/QueryPlan/WindowStep.h | 2 + ...ns_optimize_read_in_window_order.reference | 12 ++ ...mizations_optimize_read_in_window_order.sh | 36 ++++++ ...timize_read_in_window_order_long.reference | 4 + ...ions_optimize_read_in_window_order_long.sh | 35 +++++ 18 files changed, 287 insertions(+), 10 deletions(-) create mode 100644 src/Processors/QueryPlan/Optimizations/reuseStorageOrderingForWindowFunctions.cpp create mode 100644 tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.reference create mode 100755 tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.sh create mode 100644 tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order_long.reference create mode 100755 tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order_long.sh diff --git a/src/Core/Settings.h b/src/Core/Settings.h index 0c69b864d528..b40d88f769f0 100644 --- a/src/Core/Settings.h +++ b/src/Core/Settings.h @@ -396,6 +396,7 @@ class IColumn; M(Bool, parallel_view_processing, false, "Enables pushing to attached views concurrently instead of sequentially.", 0) \ M(Bool, enable_unaligned_array_join, false, "Allow ARRAY JOIN with multiple arrays that have different sizes. When this settings is enabled, arrays will be resized to the longest one.", 0) \ M(Bool, optimize_read_in_order, true, "Enable ORDER BY optimization for reading data in corresponding order in MergeTree tables.", 0) \ + M(Bool, optimize_read_in_window_order, true, "Enable ORDER BY optimization in window clause for reading data in corresponding order in MergeTree tables.", 0) \ M(Bool, optimize_aggregation_in_order, false, "Enable GROUP BY optimization for aggregating data in corresponding order in MergeTree tables.", 0) \ M(UInt64, aggregation_in_order_max_block_bytes, 50000000, "Maximal size of block in bytes accumulated during aggregation in order of primary key. Lower block size allows to parallelize more final merge stage of aggregation.", 0) \ M(UInt64, read_in_order_two_level_merge_threshold, 100, "Minimal number of parts to read to run preliminary merge step during multithread reading in order of primary key.", 0) \ diff --git a/src/Interpreters/ExpressionAnalyzer.cpp b/src/Interpreters/ExpressionAnalyzer.cpp index e3191fefa004..61fd9c0baf20 100644 --- a/src/Interpreters/ExpressionAnalyzer.cpp +++ b/src/Interpreters/ExpressionAnalyzer.cpp @@ -607,7 +607,7 @@ void ExpressionAnalyzer::makeAggregateDescriptions(ActionsDAGPtr & actions, Aggr } } -void makeWindowDescriptionFromAST(const Context & context, +void ExpressionAnalyzer::makeWindowDescriptionFromAST(const Context & context_, const WindowDescriptions & existing_descriptions, WindowDescription & desc, const IAST * ast) { @@ -676,6 +676,10 @@ void makeWindowDescriptionFromAST(const Context & context, desc.partition_by.push_back(SortColumnDescription( with_alias->getColumnName(), 1 /* direction */, 1 /* nulls_direction */)); + + auto actions_dag = std::make_shared(columns_after_join); + getRootActions(column_ast, false, actions_dag); + desc.partition_by_actions.push_back(std::move(actions_dag)); } } @@ -693,6 +697,10 @@ void makeWindowDescriptionFromAST(const Context & context, order_by_element.children.front()->getColumnName(), order_by_element.direction, order_by_element.nulls_direction)); + + auto actions_dag = std::make_shared(columns_after_join); + getRootActions(column_ast, false, actions_dag); + desc.order_by_actions.push_back(std::move(actions_dag)); } } @@ -719,14 +727,14 @@ void makeWindowDescriptionFromAST(const Context & context, if (definition.frame_end_type == WindowFrame::BoundaryType::Offset) { auto [value, _] = evaluateConstantExpression(definition.frame_end_offset, - context.shared_from_this()); + context_.shared_from_this()); desc.frame.end_offset = value; } if (definition.frame_begin_type == WindowFrame::BoundaryType::Offset) { auto [value, _] = evaluateConstantExpression(definition.frame_begin_offset, - context.shared_from_this()); + context_.shared_from_this()); desc.frame.begin_offset = value; } } diff --git a/src/Interpreters/ExpressionAnalyzer.h b/src/Interpreters/ExpressionAnalyzer.h index a034dd573931..6e3bc3f81b47 100644 --- a/src/Interpreters/ExpressionAnalyzer.h +++ b/src/Interpreters/ExpressionAnalyzer.h @@ -132,6 +132,7 @@ class ExpressionAnalyzer : protected ExpressionAnalyzerData, private boost::nonc /// A list of windows for window functions. const WindowDescriptions & windowDescriptions() const { return window_descriptions; } + void makeWindowDescriptionFromAST(const Context & context, const WindowDescriptions & existing_descriptions, WindowDescription & desc, const IAST * ast); void makeWindowDescriptions(ActionsDAGPtr actions); /** diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 35d20be46553..ffa094a82ae5 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -811,7 +811,7 @@ static FillColumnDescription getWithFillDescription(const ASTOrderByElement & or return descr; } -static SortDescription getSortDescription(const ASTSelectQuery & query, ContextPtr context) +SortDescription InterpreterSelectQuery::getSortDescription(const ASTSelectQuery & query, ContextPtr context_) { SortDescription order_descr; order_descr.reserve(query.orderBy()->children.size()); @@ -826,7 +826,7 @@ static SortDescription getSortDescription(const ASTSelectQuery & query, ContextP if (order_by_elem.with_fill) { - FillColumnDescription fill_desc = getWithFillDescription(order_by_elem, context); + FillColumnDescription fill_desc = getWithFillDescription(order_by_elem, context_); order_descr.emplace_back(name, order_by_elem.direction, order_by_elem.nulls_direction, collator, true, fill_desc); } else @@ -885,12 +885,12 @@ static std::pair getLimitLengthAndOffset(const ASTSelectQuery & } -static UInt64 getLimitForSorting(const ASTSelectQuery & query, ContextPtr context) +UInt64 InterpreterSelectQuery::getLimitForSorting(const ASTSelectQuery & query, ContextPtr context_) { /// Partial sort can be done if there is LIMIT but no DISTINCT or LIMIT BY, neither ARRAY JOIN. if (!query.distinct && !query.limitBy() && !query.limit_with_ties && !query.arrayJoinExpressionList().first && query.limitLength()) { - auto [limit_length, limit_offset] = getLimitLengthAndOffset(query, context); + auto [limit_length, limit_offset] = getLimitLengthAndOffset(query, context_); if (limit_length > std::numeric_limits::max() - limit_offset) return 0; diff --git a/src/Interpreters/InterpreterSelectQuery.h b/src/Interpreters/InterpreterSelectQuery.h index aa41d8376013..0eb6ae06e82f 100644 --- a/src/Interpreters/InterpreterSelectQuery.h +++ b/src/Interpreters/InterpreterSelectQuery.h @@ -106,6 +106,9 @@ class InterpreterSelectQuery : public IInterpreterUnionOrSelectQuery Names getRequiredColumns() { return required_columns; } + static SortDescription getSortDescription(const ASTSelectQuery & query, ContextPtr context); + static UInt64 getLimitForSorting(const ASTSelectQuery & query, ContextPtr context); + private: InterpreterSelectQuery( const ASTPtr & query_ptr_, diff --git a/src/Interpreters/WindowDescription.h b/src/Interpreters/WindowDescription.h index bb0130b4d4ee..65c8cb9423c9 100644 --- a/src/Interpreters/WindowDescription.h +++ b/src/Interpreters/WindowDescription.h @@ -7,6 +7,7 @@ #include #include #include +#include namespace DB { @@ -90,6 +91,9 @@ struct WindowDescription // then by ORDER BY. This field holds this combined sort order. SortDescription full_sort_description; + std::vector partition_by_actions; + std::vector order_by_actions; + WindowFrame frame; // The window functions that are calculated for this window. diff --git a/src/Processors/QueryPlan/Optimizations/Optimizations.h b/src/Processors/QueryPlan/Optimizations/Optimizations.h index 10bc62935371..11bdb1c95e50 100644 --- a/src/Processors/QueryPlan/Optimizations/Optimizations.h +++ b/src/Processors/QueryPlan/Optimizations/Optimizations.h @@ -44,16 +44,21 @@ size_t tryMergeExpressions(QueryPlan::Node * parent_node, QueryPlan::Nodes &); /// May split FilterStep and push down only part of it. size_t tryPushDownFilter(QueryPlan::Node * parent_node, QueryPlan::Nodes & nodes); +/// Utilize storage sorting when sorting for window functions. +/// Update information about prefix sort description in SortingStep. +size_t tryReuseStorageOrderingForWindowFunctions(QueryPlan::Node * parent_node, QueryPlan::Nodes & nodes); + inline const auto & getOptimizations() { - static const std::array optimizations = + static const std::array optimizations = {{ {tryLiftUpArrayJoin, "liftUpArrayJoin", &QueryPlanOptimizationSettings::optimize_plan}, {tryPushDownLimit, "pushDownLimit", &QueryPlanOptimizationSettings::optimize_plan}, {trySplitFilter, "splitFilter", &QueryPlanOptimizationSettings::optimize_plan}, {tryMergeExpressions, "mergeExpressions", &QueryPlanOptimizationSettings::optimize_plan}, {tryPushDownFilter, "pushDownFilter", &QueryPlanOptimizationSettings::filter_push_down}, - }}; + {tryReuseStorageOrderingForWindowFunctions, "reuseStorageOrderingForWindowFunctions", &QueryPlanOptimizationSettings::optimize_plan} + }}; return optimizations; } diff --git a/src/Processors/QueryPlan/Optimizations/reuseStorageOrderingForWindowFunctions.cpp b/src/Processors/QueryPlan/Optimizations/reuseStorageOrderingForWindowFunctions.cpp new file mode 100644 index 000000000000..c68ec47edff0 --- /dev/null +++ b/src/Processors/QueryPlan/Optimizations/reuseStorageOrderingForWindowFunctions.cpp @@ -0,0 +1,122 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace DB::QueryPlanOptimizations +{ + +size_t tryReuseStorageOrderingForWindowFunctions(QueryPlan::Node * parent_node, QueryPlan::Nodes & /*nodes*/) +{ + /// Find the following sequence of steps, add InputOrderInfo and apply prefix sort description to + /// SortingStep: + /// WindowStep <- SortingStep <- [Expression] <- [SettingQuotaAndLimits] <- ReadFromMergeTree + + auto * window_node = parent_node; + auto * window = typeid_cast(window_node->step.get()); + if (!window) + return 0; + if (window_node->children.size() != 1) + return 0; + + auto * sorting_node = window_node->children.front(); + auto * sorting = typeid_cast(sorting_node->step.get()); + if (!sorting) + return 0; + if (sorting_node->children.size() != 1) + return 0; + + auto * possible_read_from_merge_tree_node = sorting_node->children.front(); + + if (typeid_cast(possible_read_from_merge_tree_node->step.get())) + { + if (possible_read_from_merge_tree_node->children.size() != 1) + return 0; + + possible_read_from_merge_tree_node = possible_read_from_merge_tree_node->children.front(); + } + + if (typeid_cast(possible_read_from_merge_tree_node->step.get())) + { + if (possible_read_from_merge_tree_node->children.size() != 1) + return 0; + + possible_read_from_merge_tree_node = possible_read_from_merge_tree_node->children.front(); + } + + auto * read_from_merge_tree = typeid_cast(possible_read_from_merge_tree_node->step.get()); + if (!read_from_merge_tree) + { + return 0; + } + + auto context = read_from_merge_tree->getContext(); + if (!context->getSettings().optimize_read_in_window_order) + { + return 0; + } + + const auto & query_info = read_from_merge_tree->getQueryInfo(); + const auto * select_query = query_info.query->as(); + + ManyExpressionActions order_by_elements_actions; + const auto & window_desc = window->getWindowDescription(); + + for (const auto & actions_dag : window_desc.partition_by_actions) + { + order_by_elements_actions.emplace_back( + std::make_shared(actions_dag, ExpressionActionsSettings::fromContext(context, CompileExpressions::yes))); + } + + for (const auto & actions_dag : window_desc.order_by_actions) + { + order_by_elements_actions.emplace_back( + std::make_shared(actions_dag, ExpressionActionsSettings::fromContext(context, CompileExpressions::yes))); + } + + auto order_optimizer = std::make_shared( + *select_query, + order_by_elements_actions, + window->getWindowDescription().full_sort_description, + query_info.syntax_analyzer_result); + + read_from_merge_tree->setQueryInfoOrderOptimizer(order_optimizer); + + /// If we don't have filtration, we can pushdown limit to reading stage for optimizations. + UInt64 limit = (select_query->hasFiltration() || select_query->groupBy()) ? 0 : InterpreterSelectQuery::getLimitForSorting(*select_query, context); + + auto order_info = order_optimizer->getInputOrder( + query_info.projection ? query_info.projection->desc->metadata : read_from_merge_tree->getStorageMetadata(), + context, + limit); + + if (order_info) + { + read_from_merge_tree->setQueryInfoInputOrderInfo(order_info); + sorting->convertToFinishSorting(order_info->order_key_prefix_descr); + } + + return 0; +} + +} diff --git a/src/Processors/QueryPlan/ReadFromMergeTree.cpp b/src/Processors/QueryPlan/ReadFromMergeTree.cpp index 1bfc1ec7306f..abc753c5fa7b 100644 --- a/src/Processors/QueryPlan/ReadFromMergeTree.cpp +++ b/src/Processors/QueryPlan/ReadFromMergeTree.cpp @@ -977,6 +977,30 @@ MergeTreeDataSelectAnalysisResultPtr ReadFromMergeTree::selectRangesToRead( return std::make_shared(MergeTreeDataSelectAnalysisResult{.result = std::move(result)}); } +void ReadFromMergeTree::setQueryInfoOrderOptimizer(std::shared_ptr order_optimizer) +{ + if (query_info.projection) + { + query_info.projection->order_optimizer = order_optimizer; + } + else + { + query_info.order_optimizer = order_optimizer; + } +} + +void ReadFromMergeTree::setQueryInfoInputOrderInfo(InputOrderInfoPtr order_info) +{ + if (query_info.projection) + { + query_info.projection->input_order_info = order_info; + } + else + { + query_info.input_order_info = order_info; + } +} + ReadFromMergeTree::AnalysisResult ReadFromMergeTree::getAnalysisResult() const { auto result_ptr = analyzed_result_ptr ? analyzed_result_ptr : selectRangesToRead(prepared_parts); @@ -1060,7 +1084,7 @@ void ReadFromMergeTree::initializePipeline(QueryPipelineBuilder & pipeline, cons column_names_to_read, result_projection); } - else if ((settings.optimize_read_in_order || settings.optimize_aggregation_in_order) && input_order_info) + else if ((settings.optimize_read_in_order || settings.optimize_aggregation_in_order || settings.optimize_read_in_window_order) && input_order_info) { pipe = spreadMarkRangesAmongStreamsWithOrder( std::move(result.parts_with_ranges), diff --git a/src/Processors/QueryPlan/ReadFromMergeTree.h b/src/Processors/QueryPlan/ReadFromMergeTree.h index 685b99a7bdcb..16333edcaf3b 100644 --- a/src/Processors/QueryPlan/ReadFromMergeTree.h +++ b/src/Processors/QueryPlan/ReadFromMergeTree.h @@ -128,6 +128,13 @@ class ReadFromMergeTree final : public ISourceStep bool sample_factor_column_queried, Poco::Logger * log); + ContextPtr getContext() const { return context; } + const SelectQueryInfo & getQueryInfo() const { return query_info; } + StorageMetadataPtr getStorageMetadata() const { return metadata_for_reading; } + + void setQueryInfoOrderOptimizer(std::shared_ptr read_in_order_optimizer); + void setQueryInfoInputOrderInfo(InputOrderInfoPtr order_info); + private: const MergeTreeReaderSettings reader_settings; diff --git a/src/Processors/QueryPlan/SortingStep.cpp b/src/Processors/QueryPlan/SortingStep.cpp index 32b314b1c505..602680e17182 100644 --- a/src/Processors/QueryPlan/SortingStep.cpp +++ b/src/Processors/QueryPlan/SortingStep.cpp @@ -97,6 +97,12 @@ void SortingStep::updateLimit(size_t limit_) } } +void SortingStep::convertToFinishSorting(SortDescription prefix_description_) +{ + type = Type::FinishSorting; + prefix_description = std::move(prefix_description_); +} + void SortingStep::transformPipeline(QueryPipelineBuilder & pipeline, const BuildQueryPipelineSettings &) { if (type == Type::FinishSorting) diff --git a/src/Processors/QueryPlan/SortingStep.h b/src/Processors/QueryPlan/SortingStep.h index 8e253e71f441..4da98a15f65d 100644 --- a/src/Processors/QueryPlan/SortingStep.h +++ b/src/Processors/QueryPlan/SortingStep.h @@ -49,6 +49,8 @@ class SortingStep : public ITransformingStep /// Add limit or change it to lower value. void updateLimit(size_t limit_); + void convertToFinishSorting(SortDescription prefix_description); + private: enum class Type diff --git a/src/Processors/QueryPlan/WindowStep.cpp b/src/Processors/QueryPlan/WindowStep.cpp index cd4bb5f67307..0916b43b29a5 100644 --- a/src/Processors/QueryPlan/WindowStep.cpp +++ b/src/Processors/QueryPlan/WindowStep.cpp @@ -138,4 +138,9 @@ void WindowStep::describeActions(JSONBuilder::JSONMap & map) const map.add("Functions", std::move(functions_array)); } +const WindowDescription & WindowStep::getWindowDescription() const +{ + return window_description; +} + } diff --git a/src/Processors/QueryPlan/WindowStep.h b/src/Processors/QueryPlan/WindowStep.h index a65b157f4817..9b58cceb972b 100644 --- a/src/Processors/QueryPlan/WindowStep.h +++ b/src/Processors/QueryPlan/WindowStep.h @@ -25,6 +25,8 @@ class WindowStep : public ITransformingStep void describeActions(JSONBuilder::JSONMap & map) const override; void describeActions(FormatSettings & settings) const override; + const WindowDescription & getWindowDescription() const; + private: WindowDescription window_description; std::vector window_functions; diff --git a/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.reference b/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.reference new file mode 100644 index 000000000000..7fcd29b5faf9 --- /dev/null +++ b/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.reference @@ -0,0 +1,12 @@ +Partial sorting plan + optimize_read_in_window_order=0 + Sort description: n ASC, x ASC + optimize_read_in_window_order=1 + Prefix sort description: n ASC + Result sort description: n ASC, x ASC +No sorting plan + optimize_read_in_window_order=0 + Sort description: n ASC, x ASC + optimize_read_in_window_order=1 + Prefix sort description: n ASC, x ASC + Result sort description: n ASC, x ASC diff --git a/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.sh b/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.sh new file mode 100755 index 000000000000..418baea81136 --- /dev/null +++ b/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +# shellcheck source=../shell_config.sh +. "$CURDIR"/../shell_config.sh + +name=test_01655_plan_optimizations_optimize_read_in_window_order + +$CLICKHOUSE_CLIENT -q "drop table if exists ${name}" +$CLICKHOUSE_CLIENT -q "drop table if exists ${name}_n" +$CLICKHOUSE_CLIENT -q "drop table if exists ${name}_n_x" + +$CLICKHOUSE_CLIENT -q "create table ${name} engine=MergeTree order by tuple() as select toInt64((sin(number)+2)*65535)%10 as n, number as x from numbers_mt(100000)" +$CLICKHOUSE_CLIENT -q "create table ${name}_n engine=MergeTree order by n as select * from ${name} order by n" +$CLICKHOUSE_CLIENT -q "create table ${name}_n_x engine=MergeTree order by (n, x) as select * from ${name} order by n, x" + +$CLICKHOUSE_CLIENT -q "optimize table ${name}_n final" +$CLICKHOUSE_CLIENT -q "optimize table ${name}_n_x final" + +echo 'Partial sorting plan' +echo ' optimize_read_in_window_order=0' +$CLICKHOUSE_CLIENT -q "explain plan actions=1, description=1 select n, sum(x) OVER (ORDER BY n, x ROWS BETWEEN 100 PRECEDING AND CURRENT ROW) from ${name}_n SETTINGS optimize_read_in_window_order=0" | grep -i "sort description" + +echo ' optimize_read_in_window_order=1' +$CLICKHOUSE_CLIENT -q "explain plan actions=1, description=1 select n, sum(x) OVER (ORDER BY n, x ROWS BETWEEN 100 PRECEDING AND CURRENT ROW) from ${name}_n SETTINGS optimize_read_in_window_order=1" | grep -i "sort description" + +echo 'No sorting plan' +echo ' optimize_read_in_window_order=0' +$CLICKHOUSE_CLIENT -q "explain plan actions=1, description=1 select n, sum(x) OVER (ORDER BY n, x ROWS BETWEEN 100 PRECEDING AND CURRENT ROW) from ${name}_n_x SETTINGS optimize_read_in_window_order=0" | grep -i "sort description" + +echo ' optimize_read_in_window_order=1' +$CLICKHOUSE_CLIENT -q "explain plan actions=1, description=1 select n, sum(x) OVER (ORDER BY n, x ROWS BETWEEN 100 PRECEDING AND CURRENT ROW) from ${name}_n_x SETTINGS optimize_read_in_window_order=1" | grep -i "sort description" + +$CLICKHOUSE_CLIENT -q "drop table ${name}" +$CLICKHOUSE_CLIENT -q "drop table ${name}_n" +$CLICKHOUSE_CLIENT -q "drop table ${name}_n_x" diff --git a/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order_long.reference b/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order_long.reference new file mode 100644 index 000000000000..b462a5a7baa4 --- /dev/null +++ b/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order_long.reference @@ -0,0 +1,4 @@ +OK +OK +OK +OK diff --git a/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order_long.sh b/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order_long.sh new file mode 100755 index 000000000000..297688a29c32 --- /dev/null +++ b/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order_long.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# Tags: long + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +# shellcheck source=../shell_config.sh +. "$CURDIR"/../shell_config.sh + +name=test_01655_plan_optimizations_optimize_read_in_window_order_long +max_memory_usage=20000000 + +$CLICKHOUSE_CLIENT -q "drop table if exists ${name}" +$CLICKHOUSE_CLIENT -q "drop table if exists ${name}_n" +$CLICKHOUSE_CLIENT -q "drop table if exists ${name}_n_x" + +$CLICKHOUSE_CLIENT -q "create table ${name} engine=MergeTree order by tuple() as select toInt64((sin(number)+2)*65535)%500 as n, number as x from numbers_mt(5000000)" +$CLICKHOUSE_CLIENT -q "create table ${name}_n engine=MergeTree order by n as select * from ${name} order by n" +$CLICKHOUSE_CLIENT -q "create table ${name}_n_x engine=MergeTree order by (n, x) as select * from ${name} order by n, x" + +$CLICKHOUSE_CLIENT -q "optimize table ${name}_n final" +$CLICKHOUSE_CLIENT -q "optimize table ${name}_n_x final" + +$CLICKHOUSE_CLIENT -q "select n, sum(x) OVER (ORDER BY n, x ROWS BETWEEN 100 PRECEDING AND CURRENT ROW) from ${name}_n SETTINGS optimize_read_in_window_order=0, max_memory_usage=$max_memory_usage, max_threads=1 format Null" 2>&1 | grep -F -q "MEMORY_LIMIT_EXCEEDED" && echo 'OK' || echo 'FAIL' +$CLICKHOUSE_CLIENT -q "select n, sum(x) OVER (ORDER BY n, x ROWS BETWEEN 100 PRECEDING AND CURRENT ROW) from ${name}_n SETTINGS optimize_read_in_window_order=1, max_memory_usage=$max_memory_usage, max_threads=1 format Null" + +$CLICKHOUSE_CLIENT -q "select n, sum(x) OVER (ORDER BY n, x ROWS BETWEEN 100 PRECEDING AND CURRENT ROW) from ${name}_n_x SETTINGS optimize_read_in_window_order=0, max_memory_usage=$max_memory_usage, max_threads=1 format Null" 2>&1 | grep -F -q "MEMORY_LIMIT_EXCEEDED" && echo 'OK' || echo 'FAIL' +$CLICKHOUSE_CLIENT -q "select n, sum(x) OVER (ORDER BY n, x ROWS BETWEEN 100 PRECEDING AND CURRENT ROW) from ${name}_n_x SETTINGS optimize_read_in_window_order=1, max_memory_usage=$max_memory_usage, max_threads=1 format Null" + +$CLICKHOUSE_CLIENT -q "select n, sum(x) OVER (PARTITION BY n ORDER BY x ROWS BETWEEN 100 PRECEDING AND CURRENT ROW) from ${name}_n_x SETTINGS optimize_read_in_window_order=0, max_memory_usage=$max_memory_usage, max_threads=1 format Null" 2>&1 | grep -F -q "MEMORY_LIMIT_EXCEEDED" && echo 'OK' || echo 'FAIL' +$CLICKHOUSE_CLIENT -q "select n, sum(x) OVER (PARTITION BY n ORDER BY x ROWS BETWEEN 100 PRECEDING AND CURRENT ROW) from ${name}_n_x SETTINGS optimize_read_in_window_order=1, max_memory_usage=$max_memory_usage, max_threads=1 format Null" + +$CLICKHOUSE_CLIENT -q "select n, sum(x) OVER (PARTITION BY n+x%2 ORDER BY n, x ROWS BETWEEN 100 PRECEDING AND CURRENT ROW) from ${name}_n_x SETTINGS optimize_read_in_window_order=1, max_memory_usage=$max_memory_usage, max_threads=1 format Null" 2>&1 | grep -F -q "MEMORY_LIMIT_EXCEEDED" && echo 'OK' || echo 'FAIL' + +$CLICKHOUSE_CLIENT -q "drop table ${name}" +$CLICKHOUSE_CLIENT -q "drop table ${name}_n" +$CLICKHOUSE_CLIENT -q "drop table ${name}_n_x" From a4fc3da6dd820031e4c4d05a5a828f5550d4363b Mon Sep 17 00:00:00 2001 From: Vasily Nemkov Date: Thu, 14 Jul 2022 20:54:40 +0300 Subject: [PATCH 14/72] Disabled upstream's release workflow Starting a release from release_branches.yml Fixed error in build_report_check.py --- .github/workflows/release.yml | 128 ++++++++++++------------- .github/workflows/release_branches.yml | 11 ++- tests/ci/build_report_check.py | 2 - 3 files changed, 71 insertions(+), 70 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b2ba6e51c774..29f036323a7e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,66 +1,66 @@ -name: ReleaseWorkflow -# - Gets artifacts from S3 -# - Sends it to JFROG Artifactory -# - Adds them to the release assets +# name: ReleaseWorkflow +# # - Gets artifacts from S3 +# # - Sends it to JFROG Artifactory +# # - Adds them to the release assets -on: # yamllint disable-line rule:truthy - release: - types: - - published +# on: # yamllint disable-line rule:truthy +# release: +# types: +# - published -jobs: - ReleasePublish: - runs-on: [self-hosted, style-checker] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - JFROG_API_KEY=${{ secrets.JFROG_KEY_API_PACKAGES }} - TEMP_PATH=${{runner.temp}}/release_packages - REPO_COPY=${{runner.temp}}/release_packages/ClickHouse - EOF - - name: Check out repository code - uses: actions/checkout@v2 - with: - # Always use the most recent script version - ref: master - - name: Download packages and push to Artifactory - run: | - rm -rf "$TEMP_PATH" && mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY" - python3 ./tests/ci/push_to_artifactory.py --release "${{ github.ref }}" \ - --commit '${{ github.sha }}' --all - - name: Upload packages to release assets - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ${{runner.temp}}/push_to_artifactory/* - overwrite: true - tag: ${{ github.ref }} - file_glob: true - ############################################################################################ - ##################################### Docker images ####################################### - ############################################################################################ - DockerServerImages: - runs-on: [self-hosted, style-checker] - steps: - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - with: - fetch-depth: 0 # otherwise we will have no version info - - name: Check docker clickhouse/clickhouse-server building - run: | - cd "$GITHUB_WORKSPACE/tests/ci" - python3 docker_server.py --release-type auto --version "${{ github.ref }}" - python3 docker_server.py --release-type auto --version "${{ github.ref }}" --no-ubuntu \ - --image-repo clickhouse/clickhouse-keeper --image-path docker/keeper - - name: Cleanup - if: always() - run: | - docker kill "$(docker ps -q)" ||: - docker rm -f "$(docker ps -a -q)" ||: - sudo rm -fr "$TEMP_PATH" +# jobs: +# ReleasePublish: +# runs-on: [self-hosted, style-checker] +# steps: +# - name: Set envs +# run: | +# cat >> "$GITHUB_ENV" << 'EOF' +# JFROG_API_KEY=${{ secrets.JFROG_KEY_API_PACKAGES }} +# TEMP_PATH=${{runner.temp}}/release_packages +# REPO_COPY=${{runner.temp}}/release_packages/ClickHouse +# EOF +# - name: Check out repository code +# uses: actions/checkout@v2 +# with: +# # Always use the most recent script version +# ref: master +# - name: Download packages and push to Artifactory +# run: | +# rm -rf "$TEMP_PATH" && mkdir -p "$TEMP_PATH" +# cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" +# cd "$REPO_COPY" +# python3 ./tests/ci/push_to_artifactory.py --release "${{ github.ref }}" \ +# --commit '${{ github.sha }}' --all +# - name: Upload packages to release assets +# uses: svenstaro/upload-release-action@v2 +# with: +# repo_token: ${{ secrets.GITHUB_TOKEN }} +# file: ${{runner.temp}}/push_to_artifactory/* +# overwrite: true +# tag: ${{ github.ref }} +# file_glob: true +# ############################################################################################ +# ##################################### Docker images ####################################### +# ############################################################################################ +# DockerServerImages: +# runs-on: [self-hosted, style-checker] +# steps: +# - name: Clear repository +# run: | +# sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" +# - name: Check out repository code +# uses: actions/checkout@v2 +# with: +# fetch-depth: 0 # otherwise we will have no version info +# - name: Check docker clickhouse/clickhouse-server building +# run: | +# cd "$GITHUB_WORKSPACE/tests/ci" +# python3 docker_server.py --release-type auto --version "${{ github.ref }}" +# python3 docker_server.py --release-type auto --version "${{ github.ref }}" --no-ubuntu \ +# --image-repo clickhouse/clickhouse-keeper --image-path docker/keeper +# - name: Cleanup +# if: always() +# run: | +# docker kill "$(docker ps -q)" ||: +# docker rm -f "$(docker ps -a -q)" ||: +# sudo rm -fr "$TEMP_PATH" diff --git a/.github/workflows/release_branches.yml b/.github/workflows/release_branches.yml index ec78e7715a4c..d0175ae02166 100644 --- a/.github/workflows/release_branches.yml +++ b/.github/workflows/release_branches.yml @@ -12,10 +12,13 @@ on: # yamllint disable-line rule:truthy - opened branches: - '**/22.3*' - push: - branches: - # Anything/22.3 (e.g customizations/22.3) - - '**/22.3*' + release: + types: + - published + # push: + # branches: + # # Anything/22.3 (e.g customizations/22.3) + # - '**/22.3*' jobs: DockerHubPushAarch64: diff --git a/tests/ci/build_report_check.py b/tests/ci/build_report_check.py index a25307ffc446..dbf5adfe1747 100644 --- a/tests/ci/build_report_check.py +++ b/tests/ci/build_report_check.py @@ -286,8 +286,6 @@ def main(): if some_builds_are_missing: addition = f"({len(build_reports)} of {required_builds} builds are OK)" - description = f"{ok_builds}/{total_builds} builds are OK {addition}" - description = f"{ok_groups}/{total_groups} artifact groups are OK {addition}" commit = get_commit(gh, pr_info.sha) From 9f933a0aec1cf8bc9a73e08cf304f3793164aed7 Mon Sep 17 00:00:00 2001 From: alesapin Date: Mon, 13 Jun 2022 13:39:01 +0200 Subject: [PATCH 15/72] Merge pull request #37659 from frew/master Support `batch_delete` capability for GCS --- src/Disks/S3/DiskS3.cpp | 37 +++++++++++++++++++++++++-------- src/Disks/S3/DiskS3.h | 3 +++ src/Disks/S3/S3Capabilities.cpp | 15 +++++++++++++ src/Disks/S3/S3Capabilities.h | 27 ++++++++++++++++++++++++ src/Disks/S3/registerDiskS3.cpp | 2 ++ 5 files changed, 75 insertions(+), 9 deletions(-) create mode 100644 src/Disks/S3/S3Capabilities.cpp create mode 100644 src/Disks/S3/S3Capabilities.h diff --git a/src/Disks/S3/DiskS3.cpp b/src/Disks/S3/DiskS3.cpp index e46620d9d1f0..bf5d08370902 100644 --- a/src/Disks/S3/DiskS3.cpp +++ b/src/Disks/S3/DiskS3.cpp @@ -35,6 +35,7 @@ #include #include +#include #include #include #include @@ -155,12 +156,14 @@ DiskS3::DiskS3( DiskPtr metadata_disk_, FileCachePtr cache_, ContextPtr context_, + const S3Capabilities & s3_capabilities_, SettingsPtr settings_, GetDiskSettings settings_getter_) : IDiskRemote(name_, s3_root_path_, metadata_disk_, std::move(cache_), "DiskS3", settings_->thread_pool_size) , bucket(std::move(bucket_)) , current_settings(std::move(settings_)) , settings_getter(settings_getter_) + , s3_capabilities(s3_capabilities_) , context(context_) { } @@ -180,15 +183,31 @@ void DiskS3::removeFromRemoteFS(RemoteFSPathKeeperPtr fs_paths_keeper) s3_paths_keeper->removePaths([&](S3PathKeeper::Chunk && chunk) { String keys = S3PathKeeper::getChunkKeys(chunk); - LOG_TRACE(log, "Remove AWS keys {}", keys); - Aws::S3::Model::Delete delkeys; - delkeys.SetObjects(chunk); - Aws::S3::Model::DeleteObjectsRequest request; - request.SetBucket(bucket); - request.SetDelete(delkeys); - auto outcome = settings->client->DeleteObjects(request); - // Do not throw here, continue deleting other chunks - logIfError(outcome, [&](){return "Can't remove AWS keys: " + keys;}); + if (!s3_capabilities.support_batch_delete) + { + LOG_TRACE(log, "Remove AWS keys {} one by one", keys); + for (const auto & obj : chunk) + { + Aws::S3::Model::DeleteObjectRequest request; + request.SetBucket(bucket); + request.SetKey(obj.GetKey()); + auto outcome = settings->client->DeleteObject(request); + // Do not throw here, continue deleting other keys and chunks + logIfError(outcome, [&](){return "Can't remove AWS key: " + obj.GetKey();}); + } + } + else + { + LOG_TRACE(log, "Remove AWS keys {}", keys); + Aws::S3::Model::Delete delkeys; + delkeys.SetObjects(chunk); + Aws::S3::Model::DeleteObjectsRequest request; + request.SetBucket(bucket); + request.SetDelete(delkeys); + auto outcome = settings->client->DeleteObjects(request); + // Do not throw here, continue deleting other chunks + logIfError(outcome, [&](){return "Can't remove AWS keys: " + keys;}); + } }); } diff --git a/src/Disks/S3/DiskS3.h b/src/Disks/S3/DiskS3.h index 2de1600d906f..4d24fd071acc 100644 --- a/src/Disks/S3/DiskS3.h +++ b/src/Disks/S3/DiskS3.h @@ -9,6 +9,7 @@ #include #include "Disks/DiskFactory.h" #include "Disks/Executor.h" +#include #include #include @@ -76,6 +77,7 @@ class DiskS3 final : public IDiskRemote DiskPtr metadata_disk_, FileCachePtr cache_, ContextPtr context_, + const S3Capabilities & s3_capabilities_, SettingsPtr settings_, GetDiskSettings settings_getter_); @@ -166,6 +168,7 @@ class DiskS3 final : public IDiskRemote MultiVersion current_settings; /// Gets disk settings from context. GetDiskSettings settings_getter; + const S3Capabilities s3_capabilities; std::atomic revision_counter = 0; static constexpr UInt64 LATEST_REVISION = std::numeric_limits::max(); diff --git a/src/Disks/S3/S3Capabilities.cpp b/src/Disks/S3/S3Capabilities.cpp new file mode 100644 index 000000000000..f96f0b5539a6 --- /dev/null +++ b/src/Disks/S3/S3Capabilities.cpp @@ -0,0 +1,15 @@ +#include + +namespace DB +{ + +S3Capabilities getCapabilitiesFromConfig(const Poco::Util::AbstractConfiguration & config, const std::string & config_prefix) +{ + return S3Capabilities + { + .support_batch_delete = config.getBool(config_prefix + ".support_batch_delete", true), + .support_proxy = config.getBool(config_prefix + ".support_proxy", config.has(config_prefix + ".proxy")), + }; +} + +} diff --git a/src/Disks/S3/S3Capabilities.h b/src/Disks/S3/S3Capabilities.h new file mode 100644 index 000000000000..46e647da89e5 --- /dev/null +++ b/src/Disks/S3/S3Capabilities.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +namespace DB +{ + +/// Supported/unsupported features by different S3 implementations +/// Can be useful only for almost compatible with AWS S3 versions. +struct S3Capabilities +{ + /// Google S3 implementation doesn't support batch delete + /// TODO: possibly we have to use Google SDK https://github.com/googleapis/google-cloud-cpp/tree/main/google/cloud/storage + /// because looks like it miss a lot of features like: + /// 1) batch delete + /// 2) list_v2 + /// 3) multipart upload works differently + bool support_batch_delete{true}; + + /// Y.Cloud S3 implementation support proxy for connection + bool support_proxy{false}; +}; + +S3Capabilities getCapabilitiesFromConfig(const Poco::Util::AbstractConfiguration & config, const std::string & config_prefix); + +} diff --git a/src/Disks/S3/registerDiskS3.cpp b/src/Disks/S3/registerDiskS3.cpp index 2b5fe3c5a81b..96630ad72624 100644 --- a/src/Disks/S3/registerDiskS3.cpp +++ b/src/Disks/S3/registerDiskS3.cpp @@ -187,6 +187,7 @@ void registerDiskS3(DiskFactory & factory) auto [metadata_path, metadata_disk] = prepareForLocalMetadata(name, config, config_prefix, context); FileCachePtr cache = getCachePtrForDisk(name, config, config_prefix, context); + S3Capabilities s3_capabilities = getCapabilitiesFromConfig(config, config_prefix); std::shared_ptr s3disk = std::make_shared( name, @@ -195,6 +196,7 @@ void registerDiskS3(DiskFactory & factory) metadata_disk, std::move(cache), context, + s3_capabilities, getSettings(config, config_prefix, context), getSettings); From e336a8b8c30152af6b712b2e980612c33900d6cd Mon Sep 17 00:00:00 2001 From: Robert Schulze Date: Tue, 12 Jul 2022 19:09:55 +0200 Subject: [PATCH 16/72] Merge pull request #37882 from excitoon-favorites/nodeleteobjects Fixes for objects removal in `S3ObjectStorage` --- src/Disks/IDiskRemote.h | 3 +- src/Disks/S3/DiskS3.cpp | 47 --------- src/Disks/S3/DiskS3.h | 57 ++++++++++- src/Disks/S3/registerDiskS3.cpp | 97 ++++++++++++++++++- .../configs/config.d/storage_conf.xml | 14 +++ .../s3_mocks/no_delete_objects.py | 92 ++++++++++++++++++ tests/integration/test_merge_tree_s3/test.py | 14 ++- 7 files changed, 272 insertions(+), 52 deletions(-) create mode 100644 tests/integration/test_merge_tree_s3/s3_mocks/no_delete_objects.py diff --git a/src/Disks/IDiskRemote.h b/src/Disks/IDiskRemote.h index 82e76b8f68d4..4c91400c94c5 100644 --- a/src/Disks/IDiskRemote.h +++ b/src/Disks/IDiskRemote.h @@ -165,9 +165,10 @@ friend class DiskRemoteReservation; DiskPtr metadata_disk; FileCachePtr cache; -private: +public: void removeMetadata(const String & path, RemoteFSPathKeeperPtr fs_paths_keeper); +private: void removeMetadataRecursive(const String & path, RemoteFSPathKeeperPtr fs_paths_keeper); bool tryReserve(UInt64 bytes); diff --git a/src/Disks/S3/DiskS3.cpp b/src/Disks/S3/DiskS3.cpp index bf5d08370902..723b1e7373c1 100644 --- a/src/Disks/S3/DiskS3.cpp +++ b/src/Disks/S3/DiskS3.cpp @@ -11,7 +11,6 @@ #include #include -#include #include #include @@ -58,52 +57,6 @@ namespace ErrorCodes extern const int LOGICAL_ERROR; } -/// Helper class to collect keys into chunks of maximum size (to prepare batch requests to AWS API) -/// see https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html -class S3PathKeeper : public RemoteFSPathKeeper -{ -public: - using Chunk = Aws::Vector; - using Chunks = std::list; - - explicit S3PathKeeper(size_t chunk_limit_) : RemoteFSPathKeeper(chunk_limit_) {} - - void addPath(const String & path) override - { - if (chunks.empty() || chunks.back().size() >= chunk_limit) - { - /// add one more chunk - chunks.push_back(Chunks::value_type()); - chunks.back().reserve(chunk_limit); - } - Aws::S3::Model::ObjectIdentifier obj; - obj.SetKey(path); - chunks.back().push_back(obj); - } - - void removePaths(Fn auto && remove_chunk_func) - { - for (auto & chunk : chunks) - remove_chunk_func(std::move(chunk)); - } - - static String getChunkKeys(const Chunk & chunk) - { - String res; - for (const auto & obj : chunk) - { - const auto & key = obj.GetKey(); - if (!res.empty()) - res.append(", "); - res.append(key.c_str(), key.size()); - } - return res; - } - -private: - Chunks chunks; -}; - template void throwIfError(Aws::Utils::Outcome & response) { diff --git a/src/Disks/S3/DiskS3.h b/src/Disks/S3/DiskS3.h index 4d24fd071acc..473dc6a7a751 100644 --- a/src/Disks/S3/DiskS3.h +++ b/src/Disks/S3/DiskS3.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "Disks/DiskFactory.h" #include "Disks/Executor.h" #include @@ -14,6 +15,7 @@ #include #include #include +#include #include #include @@ -121,6 +123,8 @@ class DiskS3 final : public IDiskRemote void applyNewSettings(const Poco::Util::AbstractConfiguration & config, ContextPtr context, const String &, const DisksMap &) override; + void setCapabilitiesSupportBatchDelete(bool value) { s3_capabilities.support_batch_delete = value; } + private: void createFileOperationObject(const String & operation_name, UInt64 revision, const ObjectMetadata & metadata); /// Converts revision to binary string with leading zeroes (64 bit). @@ -168,7 +172,7 @@ class DiskS3 final : public IDiskRemote MultiVersion current_settings; /// Gets disk settings from context. GetDiskSettings settings_getter; - const S3Capabilities s3_capabilities; + S3Capabilities s3_capabilities; std::atomic revision_counter = 0; static constexpr UInt64 LATEST_REVISION = std::numeric_limits::max(); @@ -190,6 +194,57 @@ class DiskS3 final : public IDiskRemote ContextPtr context; }; +/// Helper class to collect keys into chunks of maximum size (to prepare batch requests to AWS API) +/// see https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html +class S3PathKeeper : public RemoteFSPathKeeper +{ +public: + using Chunk = Aws::Vector; + using Chunks = std::list; + + explicit S3PathKeeper(size_t chunk_limit_) : RemoteFSPathKeeper(chunk_limit_) {} + + void addPath(const String & path) override + { + if (chunks.empty() || chunks.back().size() >= chunk_limit) + { + /// add one more chunk + chunks.push_back(Chunks::value_type()); + chunks.back().reserve(chunk_limit); + } + Aws::S3::Model::ObjectIdentifier obj; + obj.SetKey(path); + chunks.back().push_back(obj); + } + + void removePaths(Fn auto && remove_chunk_func) + { + for (auto & chunk : chunks) + remove_chunk_func(std::move(chunk)); + } + + Chunks getChunks() const + { + return chunks; + } + + static String getChunkKeys(const Chunk & chunk) + { + String res; + for (const auto & obj : chunk) + { + const auto & key = obj.GetKey(); + if (!res.empty()) + res.append(", "); + res.append(key.c_str(), key.size()); + } + return res; + } + +private: + Chunks chunks; +}; + } #endif diff --git a/src/Disks/S3/registerDiskS3.cpp b/src/Disks/S3/registerDiskS3.cpp index 96630ad72624..474378f4f3e3 100644 --- a/src/Disks/S3/registerDiskS3.cpp +++ b/src/Disks/S3/registerDiskS3.cpp @@ -9,6 +9,8 @@ #if USE_AWS_S3 #include +#include +#include #include #include "DiskS3.h" #include "Disks/DiskCacheWrapper.h" @@ -21,6 +23,7 @@ #include "Disks/RemoteDisksCommon.h" #include + namespace DB { namespace ErrorCodes @@ -46,7 +49,79 @@ void checkReadAccess(const String & disk_name, IDisk & disk) throw Exception("No read access to S3 bucket in disk " + disk_name, ErrorCodes::PATH_ACCESS_DENIED); } -void checkRemoveAccess(IDisk & disk) { disk.removeFile("test_acl"); } +void checkRemoveAccess(IDisk & disk) +{ + disk.removeFile("test_acl"); +} + +bool checkBatchRemoveIsMissing(DiskS3 & disk, std::unique_ptr settings, const String & bucket) +{ + const String path = "_test_remove_objects_capability"; + try + { + auto file = disk.writeFile(path, DBMS_DEFAULT_BUFFER_SIZE, WriteMode::Rewrite); + file->write("test", 4); + file->finalize(); + } + catch (...) + { + try + { + disk.removeFile(path); + } + catch (...) + { + } + return false; /// We don't have write access, therefore no information about batch remove. + } + + /// See `IDiskRemote::removeSharedFile`. + auto fs_paths_keeper = std::dynamic_pointer_cast(disk.createFSPathKeeper()); + disk.removeMetadata(path, fs_paths_keeper); + + auto fs_paths_keeper_copy = std::dynamic_pointer_cast(disk.createFSPathKeeper()); + for (const auto & chunk : fs_paths_keeper->getChunks()) + for (const auto & obj : chunk) + fs_paths_keeper_copy->addPath(obj.GetKey()); + + try + { + /// See `DiskS3::removeFromRemoteFS`. + fs_paths_keeper->removePaths([&](S3PathKeeper::Chunk && chunk) + { + String keys = S3PathKeeper::getChunkKeys(chunk); + LOG_TRACE(&Poco::Logger::get("registerDiskS3"), "Remove AWS keys {}", keys); + Aws::S3::Model::Delete delkeys; + delkeys.SetObjects(chunk); + Aws::S3::Model::DeleteObjectsRequest request; + request.SetBucket(bucket); + request.SetDelete(delkeys); + auto outcome = settings->client->DeleteObjects(request); + if (!outcome.IsSuccess()) + { + const auto & err = outcome.GetError(); + throw Exception(err.GetMessage(), static_cast(err.GetErrorType())); + } + }); + return false; + } + catch (const Exception &) + { + fs_paths_keeper_copy->removePaths([&](S3PathKeeper::Chunk && chunk) + { + String keys = S3PathKeeper::getChunkKeys(chunk); + LOG_TRACE(&Poco::Logger::get("registerDiskS3"), "Remove AWS keys {} one by one", keys); + for (const auto & obj : chunk) + { + Aws::S3::Model::DeleteObjectRequest request; + request.SetBucket(bucket); + request.SetKey(obj.GetKey()); + settings->client->DeleteObject(request); + } + }); + return true; + } +} std::shared_ptr getProxyResolverConfiguration( const String & prefix, const Poco::Util::AbstractConfiguration & proxy_resolver_config) @@ -200,8 +275,26 @@ void registerDiskS3(DiskFactory & factory) getSettings(config, config_prefix, context), getSettings); + bool skip_access_check = config.getBool(config_prefix + ".skip_access_check", false); + + if (!skip_access_check) + { + /// If `support_batch_delete` is turned on (default), check and possibly switch it off. + if (s3_capabilities.support_batch_delete && checkBatchRemoveIsMissing(*std::dynamic_pointer_cast(s3disk), getSettings(config, config_prefix, context), uri.bucket)) + { + LOG_WARNING( + &Poco::Logger::get("registerDiskS3"), + "Storage for disk {} does not support batch delete operations, " + "so `s3_capabilities.support_batch_delete` was automatically turned off during the access check. " + "To remove this message set `s3_capabilities.support_batch_delete` for the disk to `false`.", + name + ); + std::dynamic_pointer_cast(s3disk)->setCapabilitiesSupportBatchDelete(false); + } + } + /// This code is used only to check access to the corresponding disk. - if (!config.getBool(config_prefix + ".skip_access_check", false)) + if (!skip_access_check) { checkWriteAccess(*s3disk); checkReadAccess(name, *s3disk); diff --git a/tests/integration/test_merge_tree_s3/configs/config.d/storage_conf.xml b/tests/integration/test_merge_tree_s3/configs/config.d/storage_conf.xml index 2f1b8275a0bb..a6e2d29c5d57 100644 --- a/tests/integration/test_merge_tree_s3/configs/config.d/storage_conf.xml +++ b/tests/integration/test_merge_tree_s3/configs/config.d/storage_conf.xml @@ -15,6 +15,13 @@ minio123 10 + + s3 + http://resolver:8082/root/data/ + minio + minio123 + 10 + local / @@ -46,6 +53,13 @@
+ + +
+ no_delete_objects_s3 +
+
+
diff --git a/tests/integration/test_merge_tree_s3/s3_mocks/no_delete_objects.py b/tests/integration/test_merge_tree_s3/s3_mocks/no_delete_objects.py new file mode 100644 index 000000000000..111f3a490c2b --- /dev/null +++ b/tests/integration/test_merge_tree_s3/s3_mocks/no_delete_objects.py @@ -0,0 +1,92 @@ +import http.client +import http.server +import random +import socketserver +import sys +import urllib.parse + + +UPSTREAM_HOST = "minio1:9001" +random.seed("No delete objects/1.0") + + +def request(command, url, headers={}, data=None): + """Mini-requests.""" + + class Dummy: + pass + + parts = urllib.parse.urlparse(url) + c = http.client.HTTPConnection(parts.hostname, parts.port) + c.request( + command, + urllib.parse.urlunparse(parts._replace(scheme="", netloc="")), + headers=headers, + body=data, + ) + r = c.getresponse() + result = Dummy() + result.status_code = r.status + result.headers = r.headers + result.content = r.read() + return result + + +class RequestHandler(http.server.BaseHTTPRequestHandler): + def do_GET(self): + if self.path == "/": + self.send_response(200) + self.send_header("Content-Type", "text/plain") + self.end_headers() + self.wfile.write(b"OK") + else: + self.do_HEAD() + + def do_PUT(self): + self.do_HEAD() + + def do_DELETE(self): + self.do_HEAD() + + def do_POST(self): + query = urllib.parse.urlparse(self.path).query + params = urllib.parse.parse_qs(query, keep_blank_values=True) + if "delete" in params: + self.send_response(501) + self.send_header("Content-Type", "application/xml") + self.end_headers() + self.wfile.write( + b""" + + NotImplemented + Ima GCP and I can't do `DeleteObjects` request for ya. See https://issuetracker.google.com/issues/162653700 . + RESOURCE + REQUEST_ID +""" + ) + else: + self.do_HEAD() + + def do_HEAD(self): + content_length = self.headers.get("Content-Length") + data = self.rfile.read(int(content_length)) if content_length else None + r = request( + self.command, + f"http://{UPSTREAM_HOST}{self.path}", + headers=self.headers, + data=data, + ) + self.send_response(r.status_code) + for k, v in r.headers.items(): + self.send_header(k, v) + self.end_headers() + self.wfile.write(r.content) + self.wfile.close() + + +class ThreadedHTTPServer(socketserver.ThreadingMixIn, http.server.HTTPServer): + """Handle requests in a separate thread.""" + + +httpd = ThreadedHTTPServer(("0.0.0.0", int(sys.argv[1])), RequestHandler) +httpd.serve_forever() diff --git a/tests/integration/test_merge_tree_s3/test.py b/tests/integration/test_merge_tree_s3/test.py index b7ef3ce3ef2e..b04b02246132 100644 --- a/tests/integration/test_merge_tree_s3/test.py +++ b/tests/integration/test_merge_tree_s3/test.py @@ -67,7 +67,10 @@ def create_table(node, table_name, **additional_settings): def run_s3_mocks(cluster): logging.info("Starting s3 mocks") - mocks = (("unstable_proxy.py", "resolver", "8081"),) + mocks = ( + ("unstable_proxy.py", "resolver", "8081"), + ("no_delete_objects.py", "resolver", "8082"), + ) for mock_filename, container, port in mocks: container_id = cluster.get_container_id(container) current_dir = os.path.dirname(__file__) @@ -602,6 +605,15 @@ def restart_disk(): thread.join() +@pytest.mark.parametrize("node_name", ["node"]) +def test_s3_no_delete_objects(cluster, node_name): + node = cluster.instances[node_name] + create_table( + node, "s3_test_no_delete_objects", storage_policy="no_delete_objects_s3" + ) + node.query("DROP TABLE s3_test_no_delete_objects SYNC") + + @pytest.mark.parametrize("node_name", ["node"]) def test_s3_disk_reads_on_unstable_connection(cluster, node_name): node = cluster.instances[node_name] From 7719068cfd064a5de612b1961c28591059ea83b3 Mon Sep 17 00:00:00 2001 From: Vasily Nemkov Date: Thu, 28 Jul 2022 15:47:38 +0300 Subject: [PATCH 17/72] Proper version with proper VERSION_REVISION Reverted VERSION_REVISION to value that was there before changes from Altinity's size Added VERSION_TWEAK, VERSION_FLAVOUR with proper values Utilizing tweak and flavour in version from autogenerated_versions.txt --- cmake/autogenerated_versions.txt | 8 +++++--- tests/ci/version_helper.py | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/cmake/autogenerated_versions.txt b/cmake/autogenerated_versions.txt index 7a1f566cf86c..4379b55821e2 100644 --- a/cmake/autogenerated_versions.txt +++ b/cmake/autogenerated_versions.txt @@ -2,11 +2,13 @@ # NOTE: has nothing common with DBMS_TCP_PROTOCOL_VERSION, # only DBMS_TCP_PROTOCOL_VERSION should be incremented on protocol changes. -SET(VERSION_REVISION 6) +SET(VERSION_REVISION 54460) SET(VERSION_MAJOR 22) SET(VERSION_MINOR 3) SET(VERSION_PATCH 10) SET(VERSION_GITHASH 7976930b82eed26e8728897d530e044774e0cded) -SET(VERSION_DESCRIBE v22.3.10.19-lts) -SET(VERSION_STRING 22.3.10.19) +SET(VERSION_TWEAK 20) +SET(VERSION_FLAVOUR altinitystable) +SET(VERSION_DESCRIBE v22.3.10.20-altinitystable) +SET(VERSION_STRING 22.3.10.20.altinitystable) # end of autochange diff --git a/tests/ci/version_helper.py b/tests/ci/version_helper.py index f8e93c582ce9..a82fcd97ea76 100755 --- a/tests/ci/version_helper.py +++ b/tests/ci/version_helper.py @@ -214,7 +214,8 @@ def get_version_from_repo( versions["revision"], git, # Explicitly use tweak value from version file - tweak=versions["revision"] + tweak=versions.get("tweak", versions["revision"]), + flavour=versions["flavour"] ) From c06ae64d57eb05dbc556a70b902a2bb021d27bc2 Mon Sep 17 00:00:00 2001 From: Vasily Nemkov Date: Thu, 4 Aug 2022 13:37:45 +0300 Subject: [PATCH 18/72] Version 22.3.10.23.altinitystable --- cmake/autogenerated_versions.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/autogenerated_versions.txt b/cmake/autogenerated_versions.txt index 4379b55821e2..3c235b85c3c7 100644 --- a/cmake/autogenerated_versions.txt +++ b/cmake/autogenerated_versions.txt @@ -7,8 +7,8 @@ SET(VERSION_MAJOR 22) SET(VERSION_MINOR 3) SET(VERSION_PATCH 10) SET(VERSION_GITHASH 7976930b82eed26e8728897d530e044774e0cded) -SET(VERSION_TWEAK 20) +SET(VERSION_TWEAK 23) SET(VERSION_FLAVOUR altinitystable) -SET(VERSION_DESCRIBE v22.3.10.20-altinitystable) -SET(VERSION_STRING 22.3.10.20.altinitystable) +SET(VERSION_DESCRIBE v22.3.10.23-altinitystable) +SET(VERSION_STRING 22.3.10.23.altinitystable) # end of autochange From 5d32a5457da0c738d0950026c76a7f5cdb1b1953 Mon Sep 17 00:00:00 2001 From: Vasily Nemkov Date: Thu, 4 Aug 2022 13:46:55 +0300 Subject: [PATCH 19/72] Removed dependecny on BuilderDebAarch64 --- .github/workflows/release_branches.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release_branches.yml b/.github/workflows/release_branches.yml index d0175ae02166..8ce8b2ccd427 100644 --- a/.github/workflows/release_branches.yml +++ b/.github/workflows/release_branches.yml @@ -179,7 +179,7 @@ jobs: DockerServerImages: needs: - BuilderDebRelease - - BuilderDebAarch64 + # - BuilderDebAarch64 - currently we do not build aarch images runs-on: [self-hosted, style-checker] steps: - name: Clear repository From 75dfee052926b0d4737376c1f34ef5a4a5c6450f Mon Sep 17 00:00:00 2001 From: Vladimir Chebotaryov Date: Sun, 31 Jul 2022 23:36:20 +0300 Subject: [PATCH 20/72] Fixed using `column_after_join` for handling `WINDOW` expressions in `ExpressionAnalyzer`, shall be `aggregated_columns`. --- src/Interpreters/ExpressionAnalyzer.cpp | 4 ++-- .../reuseStorageOrderingForWindowFunctions.cpp | 2 +- ...optimizations_optimize_read_in_window_order.reference | 9 +++++++++ ...5_plan_optimizations_optimize_read_in_window_order.sh | 9 +++++++++ 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/Interpreters/ExpressionAnalyzer.cpp b/src/Interpreters/ExpressionAnalyzer.cpp index 61fd9c0baf20..bc2745ce19d2 100644 --- a/src/Interpreters/ExpressionAnalyzer.cpp +++ b/src/Interpreters/ExpressionAnalyzer.cpp @@ -677,7 +677,7 @@ void ExpressionAnalyzer::makeWindowDescriptionFromAST(const Context & context_, with_alias->getColumnName(), 1 /* direction */, 1 /* nulls_direction */)); - auto actions_dag = std::make_shared(columns_after_join); + auto actions_dag = std::make_shared(aggregated_columns); getRootActions(column_ast, false, actions_dag); desc.partition_by_actions.push_back(std::move(actions_dag)); } @@ -698,7 +698,7 @@ void ExpressionAnalyzer::makeWindowDescriptionFromAST(const Context & context_, order_by_element.direction, order_by_element.nulls_direction)); - auto actions_dag = std::make_shared(columns_after_join); + auto actions_dag = std::make_shared(aggregated_columns); getRootActions(column_ast, false, actions_dag); desc.order_by_actions.push_back(std::move(actions_dag)); } diff --git a/src/Processors/QueryPlan/Optimizations/reuseStorageOrderingForWindowFunctions.cpp b/src/Processors/QueryPlan/Optimizations/reuseStorageOrderingForWindowFunctions.cpp index c68ec47edff0..547e29106a4f 100644 --- a/src/Processors/QueryPlan/Optimizations/reuseStorageOrderingForWindowFunctions.cpp +++ b/src/Processors/QueryPlan/Optimizations/reuseStorageOrderingForWindowFunctions.cpp @@ -30,7 +30,7 @@ size_t tryReuseStorageOrderingForWindowFunctions(QueryPlan::Node * parent_node, { /// Find the following sequence of steps, add InputOrderInfo and apply prefix sort description to /// SortingStep: - /// WindowStep <- SortingStep <- [Expression] <- [SettingQuotaAndLimits] <- ReadFromMergeTree + /// WindowStep <- SortingStep <- [Expression] <- ReadFromMergeTree auto * window_node = parent_node; auto * window = typeid_cast(window_node->step.get()); diff --git a/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.reference b/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.reference index 7fcd29b5faf9..00eb03bd5f02 100644 --- a/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.reference +++ b/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.reference @@ -10,3 +10,12 @@ No sorting plan optimize_read_in_window_order=1 Prefix sort description: n ASC, x ASC Result sort description: n ASC, x ASC +Complex ORDER BY + optimize_read_in_window_order=0 +3 3 1 +4 5 2 +5 7 3 + optimize_read_in_window_order=1 +3 3 1 +4 5 2 +5 7 3 diff --git a/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.sh b/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.sh index 418baea81136..328d181fadd7 100755 --- a/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.sh +++ b/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.sh @@ -31,6 +31,15 @@ $CLICKHOUSE_CLIENT -q "explain plan actions=1, description=1 select n, sum(x) OV echo ' optimize_read_in_window_order=1' $CLICKHOUSE_CLIENT -q "explain plan actions=1, description=1 select n, sum(x) OVER (ORDER BY n, x ROWS BETWEEN 100 PRECEDING AND CURRENT ROW) from ${name}_n_x SETTINGS optimize_read_in_window_order=1" | grep -i "sort description" +echo 'Complex ORDER BY' +$CLICKHOUSE_CLIENT -q "CREATE TABLE ${name}_complex (unique1 Int32, unique2 Int32, ten Int32) ENGINE=MergeTree ORDER BY tuple() SETTINGS index_granularity = 8192" +$CLICKHOUSE_CLIENT -q "INSERT INTO ${name}_complex VALUES (1, 2, 3), (2, 3, 4), (3, 4, 5)" +echo ' optimize_read_in_window_order=0' +$CLICKHOUSE_CLIENT -q "SELECT ten, sum(unique1) + sum(unique2) AS res, rank() OVER (ORDER BY sum(unique1) + sum(unique2) ASC) AS rank FROM ${name}_complex GROUP BY ten ORDER BY ten ASC SETTINGS optimize_read_in_window_order=0" +echo ' optimize_read_in_window_order=1' +$CLICKHOUSE_CLIENT -q "SELECT ten, sum(unique1) + sum(unique2) AS res, rank() OVER (ORDER BY sum(unique1) + sum(unique2) ASC) AS rank FROM ${name}_complex GROUP BY ten ORDER BY ten ASC SETTINGS optimize_read_in_window_order=1" + $CLICKHOUSE_CLIENT -q "drop table ${name}" $CLICKHOUSE_CLIENT -q "drop table ${name}_n" $CLICKHOUSE_CLIENT -q "drop table ${name}_n_x" +$CLICKHOUSE_CLIENT -q "drop table ${name}_complex" From c076916c6a98e63189e500332097719960901dc8 Mon Sep 17 00:00:00 2001 From: Vladimir Chebotaryov Date: Tue, 26 Jul 2022 09:05:31 +0300 Subject: [PATCH 21/72] Fixed point of origin for exponential decay window functions to the last value in window. --- src/Processors/Transforms/WindowTransform.cpp | 224 ++++++++++-------- src/Processors/Transforms/WindowTransform.h | 14 ++ .../02020_exponential_smoothing.reference | 34 +-- 3 files changed, 150 insertions(+), 122 deletions(-) diff --git a/src/Processors/Transforms/WindowTransform.cpp b/src/Processors/Transforms/WindowTransform.cpp index 2a2fed1cc078..b7e02c27adf0 100644 --- a/src/Processors/Transforms/WindowTransform.cpp +++ b/src/Processors/Transforms/WindowTransform.cpp @@ -965,9 +965,6 @@ void WindowTransform::updateAggregationState() } } } - - prev_frame_start = frame_start; - prev_frame_end = frame_end; } void WindowTransform::writeOutCurrentRow() @@ -1209,6 +1206,9 @@ void WindowTransform::appendChunk(Chunk & chunk) return; } + prev_frame_start = frame_start; + prev_frame_end = frame_end; + // Move to the next row. The frame will have to be recalculated. // The peer group start is updated at the beginning of the loop, // because current_row might now be past-the-end. @@ -1614,16 +1614,12 @@ struct StatefulWindowFunction : public WindowFunction struct ExponentialTimeDecayedSumState { - RowNumber previous_frame_start; - RowNumber previous_frame_end; Float64 previous_time; Float64 previous_sum; }; struct ExponentialTimeDecayedAvgState { - RowNumber previous_frame_start; - RowNumber previous_frame_end; Float64 previous_time; Float64 previous_sum; Float64 previous_count; @@ -1682,40 +1678,43 @@ struct WindowFunctionExponentialTimeDecayedSum final : public StatefulWindowFunc auto & state = getState(workspace); Float64 result = 0; - Float64 curr_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, transform->current_row); - if (state.previous_frame_start <= transform->frame_start - && transform->frame_start < state.previous_frame_end - && state.previous_frame_end <= transform->frame_end) + if (transform->frame_start < transform->frame_end) { - for (RowNumber i = state.previous_frame_start; i < transform->frame_start; transform->advanceRowNumber(i)) - { - Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); - Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); - result -= std::exp((prev_t - curr_t) / decay_length) * prev_val; - } - result += std::exp((state.previous_time - curr_t) / decay_length) * state.previous_sum; - for (RowNumber i = state.previous_frame_end; i < transform->frame_end; transform->advanceRowNumber(i)) + RowNumber frame_back = transform->prevRowNumber(transform->frame_end); + Float64 back_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, frame_back); + + if (transform->prev_frame_start <= transform->frame_start + && transform->frame_start < transform->prev_frame_end + && transform->prev_frame_end <= transform->frame_end) { - Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); - Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); - result += std::exp((prev_t - curr_t) / decay_length) * prev_val; + for (RowNumber i = transform->prev_frame_start; i < transform->frame_start; transform->advanceRowNumber(i)) + { + Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + result -= std::exp((prev_t - back_t) / decay_length) * prev_val; + } + result += std::exp((state.previous_time - back_t) / decay_length) * state.previous_sum; + for (RowNumber i = transform->prev_frame_end; i < transform->frame_end; transform->advanceRowNumber(i)) + { + Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + result += std::exp((prev_t - back_t) / decay_length) * prev_val; + } } - } - else - { - for (RowNumber i = transform->frame_start; i < transform->frame_end; transform->advanceRowNumber(i)) + else { - Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); - Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); - result += std::exp((prev_t - curr_t) / decay_length) * prev_val; + for (RowNumber i = transform->frame_start; i < transform->frame_end; transform->advanceRowNumber(i)) + { + Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + result += std::exp((prev_t - back_t) / decay_length) * prev_val; + } } - } - state.previous_sum = result; - state.previous_time = curr_t; - state.previous_frame_start = transform->frame_start; - state.previous_frame_end = transform->frame_end; + state.previous_sum = result; + state.previous_time = back_t; + } WindowFunctionHelpers::setValueToOutputColumn(transform, function_index, result); } @@ -1773,18 +1772,24 @@ struct WindowFunctionExponentialTimeDecayedMax final : public WindowFunction void windowInsertResultInto(const WindowTransform * transform, size_t function_index) override { - Float64 result = std::numeric_limits::lowest(); - Float64 curr_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, transform->current_row); + Float64 result = std::numeric_limits::quiet_NaN(); - for (RowNumber i = transform->frame_start; i < transform->frame_end; transform->advanceRowNumber(i)) + if (transform->frame_start < transform->frame_end) { - Float64 value = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); - Float64 t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + result = std::numeric_limits::lowest(); + RowNumber frame_back = transform->prevRowNumber(transform->frame_end); + Float64 back_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, frame_back); - /// Avoiding extra calls to `exp` and multiplications. - if (value > result || t > curr_t || result < 0) + for (RowNumber i = transform->frame_start; i < transform->frame_end; transform->advanceRowNumber(i)) { - result = std::max(std::exp((t - curr_t) / decay_length) * value, result); + Float64 value = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); + Float64 t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + + /// Avoiding extra calls to `exp` and multiplications. + if (value > result || t > back_t || result < 0) + { + result = std::max(std::exp((t - back_t) / decay_length) * value, result); + } } } @@ -1839,37 +1844,40 @@ struct WindowFunctionExponentialTimeDecayedCount final : public StatefulWindowFu auto & state = getState(workspace); Float64 result = 0; - Float64 curr_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, transform->current_row); - if (state.previous_frame_start <= transform->frame_start - && transform->frame_start < state.previous_frame_end - && state.previous_frame_end <= transform->frame_end) + if (transform->frame_start < transform->frame_end) { - for (RowNumber i = state.previous_frame_start; i < transform->frame_start; transform->advanceRowNumber(i)) - { - Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); - result -= std::exp((prev_t - curr_t) / decay_length); - } - result += std::exp((state.previous_time - curr_t) / decay_length) * state.previous_sum; - for (RowNumber i = state.previous_frame_end; i < transform->frame_end; transform->advanceRowNumber(i)) + RowNumber frame_back = transform->prevRowNumber(transform->frame_end); + Float64 back_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, frame_back); + + if (transform->prev_frame_start <= transform->frame_start + && transform->frame_start < transform->prev_frame_end + && transform->prev_frame_end <= transform->frame_end) { - Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); - result += std::exp((prev_t - curr_t) / decay_length); + for (RowNumber i = transform->prev_frame_start; i < transform->frame_start; transform->advanceRowNumber(i)) + { + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + result -= std::exp((prev_t - back_t) / decay_length); + } + result += std::exp((state.previous_time - back_t) / decay_length) * state.previous_sum; + for (RowNumber i = transform->prev_frame_end; i < transform->frame_end; transform->advanceRowNumber(i)) + { + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + result += std::exp((prev_t - back_t) / decay_length); + } } - } - else - { - for (RowNumber i = transform->frame_start; i < transform->frame_end; transform->advanceRowNumber(i)) + else { - Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); - result += std::exp((prev_t - curr_t) / decay_length); + for (RowNumber i = transform->frame_start; i < transform->frame_end; transform->advanceRowNumber(i)) + { + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + result += std::exp((prev_t - back_t) / decay_length); + } } - } - state.previous_sum = result; - state.previous_time = curr_t; - state.previous_frame_start = transform->frame_start; - state.previous_frame_end = transform->frame_end; + state.previous_sum = result; + state.previous_time = back_t; + } WindowFunctionHelpers::setValueToOutputColumn(transform, function_index, result); } @@ -1932,55 +1940,61 @@ struct WindowFunctionExponentialTimeDecayedAvg final : public StatefulWindowFunc Float64 count = 0; Float64 sum = 0; - Float64 curr_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, transform->current_row); + Float64 result = std::numeric_limits::quiet_NaN(); - if (state.previous_frame_start <= transform->frame_start - && transform->frame_start < state.previous_frame_end - && state.previous_frame_end <= transform->frame_end) + if (transform->frame_start < transform->frame_end) { - for (RowNumber i = state.previous_frame_start; i < transform->frame_start; transform->advanceRowNumber(i)) - { - Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); - Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); - Float64 decay = std::exp((prev_t - curr_t) / decay_length); - sum -= decay * prev_val; - count -= decay; - } + RowNumber frame_back = transform->prevRowNumber(transform->frame_end); + Float64 back_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, frame_back); + if (transform->prev_frame_start <= transform->frame_start + && transform->frame_start < transform->prev_frame_end + && transform->prev_frame_end <= transform->frame_end) { - Float64 decay = std::exp((state.previous_time - curr_t) / decay_length); - sum += decay * state.previous_sum; - count += decay * state.previous_count; - } + for (RowNumber i = transform->prev_frame_start; i < transform->frame_start; transform->advanceRowNumber(i)) + { + Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + Float64 decay = std::exp((prev_t - back_t) / decay_length); + sum -= decay * prev_val; + count -= decay; + } - for (RowNumber i = state.previous_frame_end; i < transform->frame_end; transform->advanceRowNumber(i)) - { - Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); - Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); - Float64 decay = std::exp((prev_t - curr_t) / decay_length); - sum += decay * prev_val; - count += decay; + { + Float64 decay = std::exp((state.previous_time - back_t) / decay_length); + sum += decay * state.previous_sum; + count += decay * state.previous_count; + } + + for (RowNumber i = transform->prev_frame_end; i < transform->frame_end; transform->advanceRowNumber(i)) + { + Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + Float64 decay = std::exp((prev_t - back_t) / decay_length); + sum += decay * prev_val; + count += decay; + } } - } - else - { - for (RowNumber i = transform->frame_start; i < transform->frame_end; transform->advanceRowNumber(i)) + else { - Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); - Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); - Float64 decay = std::exp((prev_t - curr_t) / decay_length); - sum += decay * prev_val; - count += decay; + for (RowNumber i = transform->frame_start; i < transform->frame_end; transform->advanceRowNumber(i)) + { + Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + Float64 decay = std::exp((prev_t - back_t) / decay_length); + sum += decay * prev_val; + count += decay; + } } - } - state.previous_sum = sum; - state.previous_count = count; - state.previous_time = curr_t; - state.previous_frame_start = transform->frame_start; - state.previous_frame_end = transform->frame_end; + state.previous_sum = sum; + state.previous_count = count; + state.previous_time = back_t; - WindowFunctionHelpers::setValueToOutputColumn(transform, function_index, sum/count); + result = sum/count; + } + + WindowFunctionHelpers::setValueToOutputColumn(transform, function_index, result); } private: diff --git a/src/Processors/Transforms/WindowTransform.h b/src/Processors/Transforms/WindowTransform.h index d536c8780d21..5bedfa1fb29a 100644 --- a/src/Processors/Transforms/WindowTransform.h +++ b/src/Processors/Transforms/WindowTransform.h @@ -198,6 +198,13 @@ class WindowTransform final : public IProcessor ++x.block; } + RowNumber nextRowNumber(const RowNumber & x) const + { + RowNumber result = x; + advanceRowNumber(result); + return result; + } + void retreatRowNumber(RowNumber & x) const { if (x.row > 0) @@ -219,6 +226,13 @@ class WindowTransform final : public IProcessor #endif } + RowNumber prevRowNumber(const RowNumber & x) const + { + RowNumber result = x; + retreatRowNumber(result); + return result; + } + auto moveRowNumber(const RowNumber & _x, int64_t offset) const; auto moveRowNumberNoCheck(const RowNumber & _x, int64_t offset) const; diff --git a/tests/queries/0_stateless/02020_exponential_smoothing.reference b/tests/queries/0_stateless/02020_exponential_smoothing.reference index 334d32e1c163..5481bfe80f8d 100644 --- a/tests/queries/0_stateless/02020_exponential_smoothing.reference +++ b/tests/queries/0_stateless/02020_exponential_smoothing.reference @@ -654,23 +654,23 @@ exponentialTimeDecayedAvg 0 48 0.201 ████████████████████ 0 49 0.196 ███████████████████▌ Check `exponentialTimeDecayed.*` supports sliding windows -2 1 3.010050167084 2 3.030251507111 0.993333444442 -1 2 7.060905027605 4.080805360107 4.02030134086 1.756312382816 -0 3 12.091654548833 5.101006700134 5.000500014167 2.418089094006 -4 4 11.050650848754 5.050250835421 5.000500014167 2.209909172572 -5 5 9.970249502081 5 5.000500014167 1.993850509716 -1 6 20.07305726224 10.202013400268 5.000500014167 4.014210020072 -0 7 15.991544871125 10.100501670842 3.98029867414 4.017674596889 +2 1 2.950447180363 1.960397346614 2.970248507056 0.993333444442 +1 2 6.921089740404 4 3.940694040604 1.756312382816 +0 3 11.85222374685 5 4.901483479757 2.418089094006 +4 4 10.831833301125 4.950249168746 4.901483479757 2.209909172572 +5 5 9.772825334477 4.900993366534 4.901483479757 1.993850509716 +1 6 19.675584097659 10 4.901483479757 4.014210020072 +0 7 15.832426341049 10 3.940694040604 4.017674596889 10 8 10.980198673307 10 2.970248507056 3.696727276261 Check `exponentialTimeDecayedMax` works with negative values -2 1 -1.010050167084 -1 2 -1 -10 3 -0.990049833749 -4 4 -0.980198673307 -5 5 -1.010050167084 -1 6 -1 -10 7 -0.990049833749 -10 8 -0.980198673307 -10 9 -9.801986733068 -9.81 10 -9.801986733068 +2 1 -0.990049833749 +1 2 -0.980198673307 +10 3 -0.970445533549 +4 4 -0.960789439152 +5 5 -0.990049833749 +1 6 -0.980198673307 +10 7 -0.970445533549 +10 8 -0.960789439152 +10 9 -9.607894391523 +9.81 10 -9.704455335485 9.9 11 -9.712388869079 From 99ff344cee324f800d17309d58ea9218575e2026 Mon Sep 17 00:00:00 2001 From: Vladimir Chebotaryov Date: Thu, 4 Aug 2022 18:08:32 +0300 Subject: [PATCH 22/72] Fixed tests on Debug build type. --- src/Processors/Transforms/WindowTransform.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Processors/Transforms/WindowTransform.h b/src/Processors/Transforms/WindowTransform.h index 5bedfa1fb29a..dedc8c9941cc 100644 --- a/src/Processors/Transforms/WindowTransform.h +++ b/src/Processors/Transforms/WindowTransform.h @@ -207,6 +207,10 @@ class WindowTransform final : public IProcessor void retreatRowNumber(RowNumber & x) const { +#ifndef NDEBUG + auto original_x = x; +#endif + if (x.row > 0) { --x.row; @@ -220,9 +224,9 @@ class WindowTransform final : public IProcessor x.row = blockAt(x).rows - 1; #ifndef NDEBUG - auto xx = x; - advanceRowNumber(xx); - assert(xx == x); + auto advanced_retreated_x = x; + advanceRowNumber(advanced_retreated_x); + assert(advanced_retreated_x == original_x); #endif } From eef37613732c9446ee17fa93278643b1d25eb5ea Mon Sep 17 00:00:00 2001 From: Vasily Nemkov Date: Fri, 5 Aug 2022 15:52:47 +0300 Subject: [PATCH 23/72] attempt to fix DockerServerImages --- tests/ci/version_helper.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ci/version_helper.py b/tests/ci/version_helper.py index a82fcd97ea76..8451b3dc4288 100755 --- a/tests/ci/version_helper.py +++ b/tests/ci/version_helper.py @@ -172,9 +172,9 @@ class VersionType: def validate_version(version: str): parts = version.split(".") - if len(parts) != 4: + if len(parts) < 4: raise ValueError(f"{version} does not contain 4 parts") - for part in parts: + for part in parts[:4]: int(part) @@ -222,7 +222,7 @@ def get_version_from_repo( def get_version_from_string(version: str) -> ClickHouseVersion: validate_version(version) parts = version.split(".") - return ClickHouseVersion(parts[0], parts[1], parts[2], -1, git, parts[3]) + return ClickHouseVersion(parts[0], parts[1], parts[2], -1, git, parts[3], parts[4] if len(parts) >= 4 else None) def get_version_from_tag(tag: str) -> ClickHouseVersion: From d83ed293ca4bb7525af1e419b7594b9d8fa983ce Mon Sep 17 00:00:00 2001 From: Vasily Nemkov Date: Wed, 10 Aug 2022 12:32:20 +0300 Subject: [PATCH 24/72] Revert "Merge pull request #39761 from ClickHouse/backport/22.3/39687" This reverts commit 201daa11686033f06dffff0c5a6cda03e86cbdb5, reversing changes made to d7208cac195a3bf19a978f5cb864bb114c269d4f. Reverts encrypted disk changes that introduce bugs --- src/IO/FileEncryptionCommon.h | 1 - src/IO/ReadBufferFromEncryptedFile.cpp | 8 +-- src/IO/tests/gtest_file_encryption.cpp | 51 ------------------- tests/integration/test_encrypted_disk/test.py | 18 ------- 4 files changed, 4 insertions(+), 74 deletions(-) diff --git a/src/IO/FileEncryptionCommon.h b/src/IO/FileEncryptionCommon.h index 496c9e66b206..bb6c8d14893e 100644 --- a/src/IO/FileEncryptionCommon.h +++ b/src/IO/FileEncryptionCommon.h @@ -80,7 +80,6 @@ class Encryptor /// the initialization vector is increased by an index of the current block /// and the index of the current block is calculated from this offset. void setOffset(size_t offset_) { offset = offset_; } - size_t getOffset() const { return offset; } /// Encrypts some data. /// Also the function moves `offset` by `size` (for successive encryptions). diff --git a/src/IO/ReadBufferFromEncryptedFile.cpp b/src/IO/ReadBufferFromEncryptedFile.cpp index c1a87283917c..7aec6dcde02b 100644 --- a/src/IO/ReadBufferFromEncryptedFile.cpp +++ b/src/IO/ReadBufferFromEncryptedFile.cpp @@ -21,6 +21,7 @@ ReadBufferFromEncryptedFile::ReadBufferFromEncryptedFile( , encryptor(header_.algorithm, key_, header_.init_vector) { offset = offset_; + encryptor.setOffset(offset_); need_seek = true; } @@ -59,6 +60,9 @@ off_t ReadBufferFromEncryptedFile::seek(off_t off, int whence) assert(!hasPendingData()); } + /// The encryptor always needs to know what the current offset is. + encryptor.setOffset(new_pos); + return new_pos; } @@ -90,10 +94,6 @@ bool ReadBufferFromEncryptedFile::nextImpl() /// The used cipher algorithms generate the same number of bytes in output as it were in input, /// so after deciphering the numbers of bytes will be still `bytes_read`. working_buffer.resize(bytes_read); - - /// The decryptor needs to know what the current offset is (because it's used in the decryption algorithm). - encryptor.setOffset(offset); - encryptor.decrypt(encrypted_buffer.data(), bytes_read, working_buffer.begin()); pos = working_buffer.begin(); diff --git a/src/IO/tests/gtest_file_encryption.cpp b/src/IO/tests/gtest_file_encryption.cpp index cae40afbb385..3a114f94ee05 100644 --- a/src/IO/tests/gtest_file_encryption.cpp +++ b/src/IO/tests/gtest_file_encryption.cpp @@ -4,13 +4,6 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include using namespace DB; @@ -217,48 +210,4 @@ INSTANTIATE_TEST_SUITE_P(All, }) ); -TEST(FileEncryptionPositionUpdateTest, Decryption) -{ - String tmp_path = std::filesystem::current_path() / "test_offset_update"; - if (std::filesystem::exists(tmp_path)) - std::filesystem::remove(tmp_path); - - String key = "1234567812345678"; - FileEncryption::Header header; - header.algorithm = Algorithm::AES_128_CTR; - header.key_id = 1; - header.key_hash = calculateKeyHash(key); - header.init_vector = InitVector::random(); - - auto lwb = std::make_unique(tmp_path); - WriteBufferFromEncryptedFile wb(10, std::move(lwb), key, header); - auto data = getRandomASCIIString(20); - wb.write(data.data(), data.size()); - wb.finalize(); - - auto lrb = std::make_unique(tmp_path); - ReadBufferFromEncryptedFile rb(10, std::move(lrb), key, header); - rb.ignore(5); - rb.ignore(5); - rb.ignore(5); - ASSERT_EQ(rb.getPosition(), 15); - - String res; - readStringUntilEOF(res, rb); - ASSERT_EQ(res, data.substr(15)); - res.clear(); - - rb.seek(0, SEEK_SET); - ASSERT_EQ(rb.getPosition(), 0); - res.resize(5); - rb.read(res.data(), res.size()); - ASSERT_EQ(res, data.substr(0, 5)); - res.clear(); - - rb.seek(1, SEEK_CUR); - ASSERT_EQ(rb.getPosition(), 6); - readStringUntilEOF(res, rb); - ASSERT_EQ(res, data.substr(6)); -} - #endif diff --git a/tests/integration/test_encrypted_disk/test.py b/tests/integration/test_encrypted_disk/test.py index 17a30676f7f7..4e6d1db9e99f 100644 --- a/tests/integration/test_encrypted_disk/test.py +++ b/tests/integration/test_encrypted_disk/test.py @@ -252,21 +252,3 @@ def make_storage_policy_with_keys(policy_name, keys): # Detach the part encrypted with the wrong key and check that another part containing "(2,'data'),(3,'data')" still can be read. node.query("ALTER TABLE encrypted_test DETACH PART '{}'".format(FIRST_PART_NAME)) assert node.query(select_query) == "(2,'data'),(3,'data')" - - -def test_read_in_order(): - node.query( - "CREATE TABLE encrypted_test(`a` UInt64, `b` String(150)) ENGINE = MergeTree() ORDER BY (a, b) SETTINGS storage_policy='encrypted_policy'" - ) - - node.query( - "INSERT INTO encrypted_test SELECT * FROM generateRandom('a UInt64, b FixedString(150)') LIMIT 100000" - ) - - node.query( - "SELECT * FROM encrypted_test ORDER BY a, b SETTINGS optimize_read_in_order=1 FORMAT Null" - ) - - node.query( - "SELECT * FROM encrypted_test ORDER BY a, b SETTINGS optimize_read_in_order=0 FORMAT Null" - ) From 569eb8205dfd7517ad5bd58b6e66db12f0b21a81 Mon Sep 17 00:00:00 2001 From: Vasily Nemkov Date: Wed, 10 Aug 2022 22:59:22 +0300 Subject: [PATCH 25/72] Revert "Merge pull request #27531 from abel-cheng/with-constants" This reverts commit a020fe3b1bcd74d361be38bbc331b3c33dd83864, reversing changes made to 8c06abee739f4cf30fbdfe1b08ed4af566e46a9f. Reverted to fix #37812 missing columns in case of using constants in CTE --- src/Interpreters/QueryNormalizer.cpp | 3 -- .../RequiredSourceColumnsVisitor.cpp | 11 ------ ...use_constants_in_with_and_select.reference | 5 --- ...02006_use_constants_in_with_and_select.sql | 36 ------------------- 4 files changed, 55 deletions(-) delete mode 100644 tests/queries/0_stateless/02006_use_constants_in_with_and_select.reference delete mode 100644 tests/queries/0_stateless/02006_use_constants_in_with_and_select.sql diff --git a/src/Interpreters/QueryNormalizer.cpp b/src/Interpreters/QueryNormalizer.cpp index 7c820622c379..ea61ade2b49d 100644 --- a/src/Interpreters/QueryNormalizer.cpp +++ b/src/Interpreters/QueryNormalizer.cpp @@ -256,9 +256,6 @@ void QueryNormalizer::visit(ASTPtr & ast, Data & data) visit(*node_select, ast, data); else if (auto * node_param = ast->as()) throw Exception("Query parameter " + backQuote(node_param->name) + " was not set", ErrorCodes::UNKNOWN_QUERY_PARAMETER); - else if (auto * node_function = ast->as()) - if (node_function->parameters) - visit(node_function->parameters, data); /// If we replace the root of the subtree, we will be called again for the new root, in case the alias is replaced by an alias. if (ast.get() != initial_ast.get()) diff --git a/src/Interpreters/RequiredSourceColumnsVisitor.cpp b/src/Interpreters/RequiredSourceColumnsVisitor.cpp index 21ec94a6917d..2f2a68656bc4 100644 --- a/src/Interpreters/RequiredSourceColumnsVisitor.cpp +++ b/src/Interpreters/RequiredSourceColumnsVisitor.cpp @@ -123,17 +123,6 @@ void RequiredSourceColumnsMatcher::visit(const ASTSelectQuery & select, const AS data.addColumnAliasIfAny(*node); } - if (const auto & with = select.with()) - { - for (auto & node : with->children) - { - if (const auto * identifier = node->as()) - data.addColumnIdentifier(*identifier); - else - data.addColumnAliasIfAny(*node); - } - } - std::vector out; for (const auto & node : select.children) { diff --git a/tests/queries/0_stateless/02006_use_constants_in_with_and_select.reference b/tests/queries/0_stateless/02006_use_constants_in_with_and_select.reference deleted file mode 100644 index bbf008ffdf20..000000000000 --- a/tests/queries/0_stateless/02006_use_constants_in_with_and_select.reference +++ /dev/null @@ -1,5 +0,0 @@ -1 [1] -[1] -99.9 -0.1 99.9 -[99.9] diff --git a/tests/queries/0_stateless/02006_use_constants_in_with_and_select.sql b/tests/queries/0_stateless/02006_use_constants_in_with_and_select.sql deleted file mode 100644 index daca6c5d0c79..000000000000 --- a/tests/queries/0_stateless/02006_use_constants_in_with_and_select.sql +++ /dev/null @@ -1,36 +0,0 @@ -SELECT - 1 AS max_size, - groupArray(max_size)(col) -FROM - (SELECT col FROM ( - SELECT 1 AS col - UNION ALL - SELECT 2 - ) ORDER BY col); - -WITH 1 AS max_size -SELECT groupArray(max_size)(col) -FROM - (SELECT col FROM ( - SELECT 1 as col - UNION ALL - SELECT 2 - ) ORDER BY col); - -WITH 0.1 AS level -SELECT quantile(level)(number) -FROM numbers(1000); - -SELECT 0.1 AS level, quantile(level)(number) -FROM numbers(1000); - -WITH - 0.1 AS level, - 1 AS max_size -SELECT groupArray(max_size)(col) -FROM - ( - SELECT quantile(level)(number) AS col - FROM numbers(1000) - ); - From 55819675475943325ee5f76515d065534bb767e9 Mon Sep 17 00:00:00 2001 From: Vitaliy Zakaznikov Date: Thu, 18 Aug 2022 08:40:15 -0400 Subject: [PATCH 26/72] Starting to add TestFlowsTestRelease job. --- .github/workflows/release_branches.yml | 43 ++++++ tests/ci/testflows_test_check.py | 182 ++++++++++++++++++++++ tests/testflows/ci-runner.py | 205 +++++++++++++++++++++++++ 3 files changed, 430 insertions(+) create mode 100644 tests/ci/testflows_test_check.py create mode 100755 tests/testflows/ci-runner.py diff --git a/.github/workflows/release_branches.yml b/.github/workflows/release_branches.yml index 00baa7f9656e..1fa4d2fb8d6f 100644 --- a/.github/workflows/release_branches.yml +++ b/.github/workflows/release_branches.yml @@ -376,6 +376,48 @@ jobs: # # shellcheck disable=SC2046 # docker rm -f $(docker ps -a -q) ||: # sudo rm -fr "$TEMP_PATH" +############################################################################################# +############################### TESTFLOWS TESTS ############################################# +############################################################################################# +TestFlowsTestsRelease: + needs: [BuilderDebRelease] + runs-on: [self-hosted, stress-tester] + steps: + - name: Set envs + run: | + cat >> "$GITHUB_ENV" << 'EOF' + TEMP_PATH=${{runner.temp}}/testflows_tests_release + REPORTS_PATH=${{runner.temp}}/reports_dir + CHECK_NAME=TestFlows tests (release, actions) + REPO_COPY=${{runner.temp}}/testflows_tests_release/ClickHouse + EOF + - name: Download json reports + uses: actions/download-artifact@v2 + with: + path: ${{ env.REPORTS_PATH }} + - name: Clear repository + run: | + sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" + - name: Check out repository code + uses: actions/checkout@v2 + - name: TestFlows test + run: | + sudo rm -fr "$TEMP_PATH" + mkdir -p "$TEMP_PATH" + cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" + cd "$REPO_COPY/tests/ci" + python3 testflows_test_check.py "$CHECK_NAME" + - name: Cleanup + if: always() + run: | + # shellcheck disable=SC2046 + docker kill $(docker ps -q) ||: + # shellcheck disable=SC2046 + docker rm -f $(docker ps -a -q) ||: + sudo rm -fr "$TEMP_PATH" +########################################################################################### +################################ FINISH CHECK ############################################# +############################################################################################# FinishCheck: needs: - DockerHubPush @@ -384,6 +426,7 @@ jobs: # - FunctionalStatefulTestRelease # - IntegrationTestsRelease0 # - IntegrationTestsRelease1 + - TestFlowsTestsRelease - CompatibilityCheck runs-on: [self-hosted, style-checker] steps: diff --git a/tests/ci/testflows_test_check.py b/tests/ci/testflows_test_check.py new file mode 100644 index 000000000000..5299f61065bc --- /dev/null +++ b/tests/ci/testflows_test_check.py @@ -0,0 +1,182 @@ +#!/usr/bin/env python3 + +import os +import logging +import sys +import json +import subprocess +import csv + +from github import Github + +from env_helper import TEMP_PATH, REPO_COPY, REPORTS_PATH +from s3_helper import S3Helper +from get_robot_token import get_best_robot_token +from pr_info import PRInfo +from build_download_helper import download_all_deb_packages +from upload_result_helper import upload_results +from commit_status_helper import post_commit_status +from clickhouse_helper import ( + ClickHouseHelper, + mark_flaky_tests, + prepare_tests_results_for_clickhouse, +) +from stopwatch import Stopwatch +from rerun_helper import RerunHelper +from tee_popen import TeePopen + + +def get_json_params_dict(check_name, pr_info): + return { + "context_name": check_name, + "commit": pr_info.sha, + "pull_request": pr_info.number, + "pr_info": {"changed_files": list(pr_info.changed_files)}, + } + + +def get_env_for_runner(build_path, repo_path, result_path, work_path): + binary_path = os.path.join(build_path, "clickhouse") + odbc_bridge_path = os.path.join(build_path, "clickhouse-odbc-bridge") + library_bridge_path = os.path.join(build_path, "clickhouse-library-bridge") + + my_env = os.environ.copy() + my_env["CLICKHOUSE_TESTS_BUILD_PATH"] = build_path + my_env["CLICKHOUSE_TESTS_SERVER_BIN_PATH"] = binary_path + my_env["CLICKHOUSE_TESTS_CLIENT_BIN_PATH"] = binary_path + my_env["CLICKHOUSE_TESTS_ODBC_BRIDGE_BIN_PATH"] = odbc_bridge_path + my_env["CLICKHOUSE_TESTS_LIBRARY_BRIDGE_BIN_PATH"] = library_bridge_path + my_env["CLICKHOUSE_TESTS_REPO_PATH"] = repo_path + my_env["CLICKHOUSE_TESTS_RESULT_PATH"] = result_path + my_env["CLICKHOUSE_TESTS_BASE_CONFIG_DIR"] = f"{repo_path}/programs/server" + my_env["CLICKHOUSE_TESTS_JSON_PARAMS_PATH"] = os.path.join(work_path, "params.json") + my_env["CLICKHOUSE_TESTS_RUNNER_RESTART_DOCKER"] = "0" + + return my_env + + +def process_results(result_folder): + test_results = [] + additional_files = [] + # Just upload all files from result_folder. + # If task provides processed results, then it's responsible for content of result_folder. + if os.path.exists(result_folder): + test_files = [ + f + for f in os.listdir(result_folder) + if os.path.isfile(os.path.join(result_folder, f)) + ] + additional_files = [os.path.join(result_folder, f) for f in test_files] + + status = [] + status_path = os.path.join(result_folder, "check_status.tsv") + if os.path.exists(status_path): + logging.info("Found check_status.tsv") + with open(status_path, "r", encoding="utf-8") as status_file: + status = list(csv.reader(status_file, delimiter="\t")) + + if len(status) != 1 or len(status[0]) != 2: + logging.info("Files in result folder %s", os.listdir(result_folder)) + return "error", "Invalid check_status.tsv", test_results, additional_files + state, description = status[0][0], status[0][1] + + results_path = os.path.join(result_folder, "test_results.tsv") + if os.path.exists(results_path): + logging.info("Found test_results.tsv") + with open(results_path, "r", encoding="utf-8") as results_file: + test_results = list(csv.reader(results_file, delimiter="\t")) + if len(test_results) == 0: + return "error", "Empty test_results.tsv", test_results, additional_files + + return state, description, test_results, additional_files + + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO) + + stopwatch = Stopwatch() + + temp_path = TEMP_PATH + repo_path = REPO_COPY + reports_path = REPORTS_PATH + + check_name = sys.argv[1] + + pr_info = PRInfo(need_changed_files=False) + + gh = Github(get_best_robot_token()) + + rerun_helper = RerunHelper(gh, pr_info, check_name) + if rerun_helper.is_already_finished_by_status(): + logging.info("Check is already finished according to github status, exiting") + sys.exit(0) + + result_path = os.path.join(temp_path, "output_dir") + if not os.path.exists(result_path): + os.makedirs(result_path) + + work_path = os.path.join(temp_path, "workdir") + if not os.path.exists(work_path): + os.makedirs(work_path) + + build_path = os.path.join(temp_path, "build") + if not os.path.exists(build_path): + os.makedirs(build_path) + + download_all_deb_packages(check_name, reports_path, build_path) + + my_env = get_env_for_runner(build_path, repo_path, result_path, work_path) + + json_path = os.path.join(work_path, "params.json") + with open(json_path, "w", encoding="utf-8") as json_params: + json_params.write( + json.dumps( + get_json_params_dict( + check_name, + pr_info, + ) + ) + ) + + output_path_log = os.path.join(result_path, "main_script_log.txt") + + runner_path = os.path.join(repo_path, "tests/testflows", "ci-runner.py") + run_command = f"sudo -E {runner_path} | tee {output_path_log}" + + with TeePopen(run_command, output_path_log, my_env) as process: + retcode = process.wait() + if retcode == 0: + logging.info("Run tests successfully") + else: + logging.info("Some tests failed") + + subprocess.check_call(f"sudo chown -R ubuntu:ubuntu {temp_path}", shell=True) + + state, description, test_results, additional_logs = process_results(result_path) + + ch_helper = ClickHouseHelper() + mark_flaky_tests(ch_helper, check_name, test_results) + + s3_helper = S3Helper("https://s3.amazonaws.com") + report_url = upload_results( + s3_helper, + pr_info.number, + pr_info.sha, + test_results, + [output_path_log] + additional_logs, + check_name, + False, + ) + print(f"::notice ::Report url: {report_url}") + post_commit_status(gh, pr_info.sha, check_name, description, state, report_url) + + prepared_events = prepare_tests_results_for_clickhouse( + pr_info, + test_results, + state, + stopwatch.duration_seconds, + stopwatch.start_time_str, + report_url, + check_name, + ) + ch_helper.insert_events_into(db="gh-data", table="checks", events=prepared_events) diff --git a/tests/testflows/ci-runner.py b/tests/testflows/ci-runner.py new file mode 100755 index 000000000000..828555b8460a --- /dev/null +++ b/tests/testflows/ci-runner.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python3 + +from collections import defaultdict +import csv +import json +import logging +import os +import shutil +import subprocess +import time + + +CLICKHOUSE_BINARY_PATH = "usr/bin/clickhouse" +CLICKHOUSE_ODBC_BRIDGE_BINARY_PATH = "usr/bin/clickhouse-odbc-bridge" +CLICKHOUSE_LIBRARY_BRIDGE_BINARY_PATH = "usr/bin/clickhouse-library-bridge" + +MAX_TIME_IN_SANDBOX = 20 * 60 # 20 minutes +TASK_TIMEOUT = 8 * 60 * 60 # 8 hours + + +def clear_ip_tables_and_restart_daemons(): + logging.info( + "Dump iptables after run %s", + subprocess.check_output("sudo iptables -L", shell=True), + ) + try: + logging.info("Killing all alive docker containers") + subprocess.check_output( + "timeout -s 9 10m docker kill $(docker ps -q)", shell=True + ) + except subprocess.CalledProcessError as err: + logging.info("docker kill excepted: " + str(err)) + + try: + logging.info("Removing all docker containers") + subprocess.check_output( + "timeout -s 9 10m docker rm $(docker ps -a -q) --force", shell=True + ) + except subprocess.CalledProcessError as err: + logging.info("docker rm excepted: " + str(err)) + + # don't restart docker if it's disabled + if os.environ.get("CLICKHOUSE_TESTS_RUNNER_RESTART_DOCKER", "1") == "1": + try: + logging.info("Stopping docker daemon") + subprocess.check_output("service docker stop", shell=True) + except subprocess.CalledProcessError as err: + logging.info("docker stop excepted: " + str(err)) + + try: + for i in range(200): + try: + logging.info("Restarting docker %s", i) + subprocess.check_output("service docker start", shell=True) + subprocess.check_output("docker ps", shell=True) + break + except subprocess.CalledProcessError as err: + time.sleep(0.5) + logging.info("Waiting docker to start, current %s", str(err)) + else: + raise Exception("Docker daemon doesn't responding") + except subprocess.CalledProcessError as err: + logging.info("Can't reload docker: " + str(err)) + + iptables_iter = 0 + try: + for i in range(1000): + iptables_iter = i + # when rules will be empty, it will raise exception + subprocess.check_output("sudo iptables -D DOCKER-USER 1", shell=True) + except subprocess.CalledProcessError as err: + logging.info( + "All iptables rules cleared, " + + str(iptables_iter) + + "iterations, last error: " + + str(err) + ) + + +class ClickhouseTestFlowsTestsRunner: + def __init__(self, result_path, params): + self.result_path = result_path + self.params = params + self.start_time = time.time() + self.soft_deadline_time = self.start_time + (TASK_TIMEOUT - MAX_TIME_IN_SANDBOX) + + def path(self): + return self.result_path + + def base_path(self): + return os.path.join(str(self.result_path), "../") + + def _can_run_with(self, path, opt): + with open(path, "r") as script: + for line in script: + if opt in line: + return True + return False + + def _install_clickhouse(self, debs_path): + for package in ( + "clickhouse-common-static_", + "clickhouse-server_", + "clickhouse-client", + "clickhouse-common-static-dbg_", + ): # order matters + logging.info("Installing package %s", package) + for f in os.listdir(debs_path): + if package in f: + full_path = os.path.join(debs_path, f) + logging.info("Package found in %s", full_path) + log_name = "install_" + f + ".log" + log_path = os.path.join(str(self.path()), log_name) + with open(log_path, "w") as log: + cmd = "dpkg -x {} .".format(full_path) + logging.info("Executing installation cmd %s", cmd) + retcode = subprocess.Popen( + cmd, shell=True, stderr=log, stdout=log + ).wait() + if retcode == 0: + logging.info("Installation of %s successful", full_path) + else: + raise Exception("Installation of %s failed", full_path) + break + else: + raise Exception("Package with {} not found".format(package)) + + logging.info("All packages installed") + os.chmod(CLICKHOUSE_BINARY_PATH, 0o777) + os.chmod(CLICKHOUSE_ODBC_BRIDGE_BINARY_PATH, 0o777) + os.chmod(CLICKHOUSE_LIBRARY_BRIDGE_BINARY_PATH, 0o777) + shutil.copy( + CLICKHOUSE_BINARY_PATH, os.getenv("CLICKHOUSE_TESTS_SERVER_BIN_PATH") + ) + shutil.copy( + CLICKHOUSE_ODBC_BRIDGE_BINARY_PATH, + os.getenv("CLICKHOUSE_TESTS_ODBC_BRIDGE_BIN_PATH"), + ) + shutil.copy( + CLICKHOUSE_LIBRARY_BRIDGE_BINARY_PATH, + os.getenv("CLICKHOUSE_TESTS_LIBRARY_BRIDGE_BIN_PATH"), + ) + + def _compress_logs(self, dir, relpaths, result_path): + subprocess.check_call( # STYLE_CHECK_ALLOW_SUBPROCESS_CHECK_CALL + "tar czf {} -C {} {}".format(result_path, dir, " ".join(relpaths)), + shell=True, + ) + + def _get_runner_image_cmd(self, repo_path): + return "clickhouse/testflows-runner" + + def run_impl(self, repo_path, build_path): + self._install_clickhouse(build_path) + logging.info( + "Dump iptables before run %s", + subprocess.check_output("sudo iptables -L", shell=True), + ) + + test_results = [] + result_state = "success" + failed_sum = 0 + passed_sum = 0 + other_sum = 0 + # FIXME: implement running testflows image and collection of counts + status_text = "fail: {}, passed: {}, other: {}".format( + failed_sum, passed_sum, other_sum + ) + + if self.soft_deadline_time < time.time(): + status_text = "Timeout, " + status_text + result_state = "failure" + + return result_state, status_text, test_results, [] + + +def write_results(results_file, status_file, results, status): + with open(results_file, "w") as f: + out = csv.writer(f, delimiter="\t") + out.writerows(results) + with open(status_file, "w") as f: + out = csv.writer(f, delimiter="\t") + out.writerow(status) + + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO, format="%(asctime)s %(message)s") + + repo_path = os.environ.get("CLICKHOUSE_TESTS_REPO_PATH") + build_path = os.environ.get("CLICKHOUSE_TESTS_BUILD_PATH") + result_path = os.environ.get("CLICKHOUSE_TESTS_RESULT_PATH") + params_path = os.environ.get("CLICKHOUSE_TESTS_JSON_PARAMS_PATH") + + params = json.loads(open(params_path, "r").read()) + runner = ClickhouseTestFlowsTestsRunner(result_path, params) + + logging.info("Running tests") + state, description, test_results, _ = runner.run_impl(repo_path, build_path) + logging.info("Tests finished") + + status = (state, description) + out_results_file = os.path.join(str(runner.path()), "test_results.tsv") + out_status_file = os.path.join(str(runner.path()), "check_status.tsv") + write_results(out_results_file, out_status_file, test_results, status) + logging.info("Results written") From 94b1d23cf526bc2a7600453d30ec118401d51111 Mon Sep 17 00:00:00 2001 From: Vitaliy Zakaznikov Date: Thu, 18 Aug 2022 08:47:21 -0400 Subject: [PATCH 27/72] Fixing indentation. --- .github/workflows/release_branches.yml | 84 +++++++++++++------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/.github/workflows/release_branches.yml b/.github/workflows/release_branches.yml index 1fa4d2fb8d6f..13611a878546 100644 --- a/.github/workflows/release_branches.yml +++ b/.github/workflows/release_branches.yml @@ -376,48 +376,48 @@ jobs: # # shellcheck disable=SC2046 # docker rm -f $(docker ps -a -q) ||: # sudo rm -fr "$TEMP_PATH" -############################################################################################# -############################### TESTFLOWS TESTS ############################################# -############################################################################################# -TestFlowsTestsRelease: - needs: [BuilderDebRelease] - runs-on: [self-hosted, stress-tester] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - TEMP_PATH=${{runner.temp}}/testflows_tests_release - REPORTS_PATH=${{runner.temp}}/reports_dir - CHECK_NAME=TestFlows tests (release, actions) - REPO_COPY=${{runner.temp}}/testflows_tests_release/ClickHouse - EOF - - name: Download json reports - uses: actions/download-artifact@v2 - with: - path: ${{ env.REPORTS_PATH }} - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - - name: TestFlows test - run: | - sudo rm -fr "$TEMP_PATH" - mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY/tests/ci" - python3 testflows_test_check.py "$CHECK_NAME" - - name: Cleanup - if: always() - run: | - # shellcheck disable=SC2046 - docker kill $(docker ps -q) ||: - # shellcheck disable=SC2046 - docker rm -f $(docker ps -a -q) ||: - sudo rm -fr "$TEMP_PATH" -########################################################################################### -################################ FINISH CHECK ############################################# -############################################################################################# + ############################################################################################# + ############################### TESTFLOWS TESTS ############################################# + ############################################################################################# + TestFlowsTestsRelease: + needs: [BuilderDebRelease] + runs-on: [self-hosted, stress-tester] + steps: + - name: Set envs + run: | + cat >> "$GITHUB_ENV" << 'EOF' + TEMP_PATH=${{runner.temp}}/testflows_tests_release + REPORTS_PATH=${{runner.temp}}/reports_dir + CHECK_NAME=TestFlows tests (release, actions) + REPO_COPY=${{runner.temp}}/testflows_tests_release/ClickHouse + EOF + - name: Download json reports + uses: actions/download-artifact@v2 + with: + path: ${{ env.REPORTS_PATH }} + - name: Clear repository + run: | + sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" + - name: Check out repository code + uses: actions/checkout@v2 + - name: TestFlows test + run: | + sudo rm -fr "$TEMP_PATH" + mkdir -p "$TEMP_PATH" + cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" + cd "$REPO_COPY/tests/ci" + python3 testflows_test_check.py "$CHECK_NAME" + - name: Cleanup + if: always() + run: | + # shellcheck disable=SC2046 + docker kill $(docker ps -q) ||: + # shellcheck disable=SC2046 + docker rm -f $(docker ps -a -q) ||: + sudo rm -fr "$TEMP_PATH" + ########################################################################################### + ################################ FINISH CHECK ############################################# + ########################################################################################### FinishCheck: needs: - DockerHubPush From f9fcba01d8ddaf156c9c83d39937ace3338906c3 Mon Sep 17 00:00:00 2001 From: Arthur Passos Date: Thu, 18 Aug 2022 15:28:43 -0300 Subject: [PATCH 28/72] Use gh-data.checks table instead of default.checks for docker_server --- tests/ci/docker_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ci/docker_server.py b/tests/ci/docker_server.py index 4a03b49c5aa6..83c4a0089cdf 100644 --- a/tests/ci/docker_server.py +++ b/tests/ci/docker_server.py @@ -357,7 +357,7 @@ def main(): NAME, ) ch_helper = ClickHouseHelper() - ch_helper.insert_events_into(db="default", table="checks", events=prepared_events) + ch_helper.insert_events_into(db="gh-data", table="checks", events=prepared_events) if __name__ == "__main__": From 3b2ea31a4056b479fec7ada40731f6c32e47003b Mon Sep 17 00:00:00 2001 From: Vitaliy Zakaznikov Date: Mon, 22 Aug 2022 14:56:18 -0400 Subject: [PATCH 29/72] Updating ci_config.py to include correct testflows check name. --- tests/ci/ci_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ci/ci_config.py b/tests/ci/ci_config.py index 08a608570a4a..8e3f8d6a68cc 100644 --- a/tests/ci/ci_config.py +++ b/tests/ci/ci_config.py @@ -299,7 +299,7 @@ "Split build smoke test (actions)": { "required_build": "binary_splitted", }, - "Testflows check (actions)": { + "TestFlows tests (release, actions)": { "required_build": "package_release", }, "Unit tests (release-gcc, actions)": { From ef82cae17ca240cbdc80bd9d09594b7d7f44d4f7 Mon Sep 17 00:00:00 2001 From: Vitaliy Zakaznikov Date: Mon, 22 Aug 2022 17:48:50 -0400 Subject: [PATCH 30/72] Attempting to run tests/testflows/runner. --- tests/testflows/ci-runner.py | 103 ++++++++++++++++++++++++----------- 1 file changed, 72 insertions(+), 31 deletions(-) diff --git a/tests/testflows/ci-runner.py b/tests/testflows/ci-runner.py index 828555b8460a..7ba7f727bf5b 100755 --- a/tests/testflows/ci-runner.py +++ b/tests/testflows/ci-runner.py @@ -79,10 +79,13 @@ def clear_ip_tables_and_restart_daemons(): class ClickhouseTestFlowsTestsRunner: def __init__(self, result_path, params): + self.image_versions = self.params["docker_images_with_versions"] self.result_path = result_path self.params = params self.start_time = time.time() - self.soft_deadline_time = self.start_time + (TASK_TIMEOUT - MAX_TIME_IN_SANDBOX) + self.disable_net_host = ( + "disable_net_host" in self.params and self.params["disable_net_host"] + ) def path(self): return self.result_path @@ -147,8 +150,55 @@ def _compress_logs(self, dir, relpaths, result_path): shell=True, ) + def _get_runner_opts(self): + result = [] + if self.disable_net_host: + result.append("--disable-net-host") + return " ".join(result) + + @staticmethod + def get_images_names(): + return [ + "altinityinfra/testflows-runner", + ] + + def get_image_version(self, name: str): + if name in self.image_versions: + return self.image_versions[name] + logging.warn( + "Cannot find image %s in params list %s", name, self.image_versions + ) + return "latest" + + def get_image_with_version(self, name): + if name in self.image_versions: + return name + ":" + self.image_versions[name] + logging.warn( + "Cannot find image %s in params list %s", name, self.image_versions + ) + if ":" not in name: + return name + ":latest" + return name + def _get_runner_image_cmd(self, repo_path): - return "clickhouse/testflows-runner" + image_cmd = "" + if self._can_run_with( + os.path.join(repo_path, "tests/testflows", "runner"), + "--docker-image-version", + ): + for img in self.get_images_names(): + if img == "altinityinfra/testflows-runner": + runner_version = self.get_image_version(img) + logging.info( + "Can run with custom docker image version %s", runner_version + ) + image_cmd += " --docker-image-version={} ".format(runner_version) + else: + logging.info(f"Cannot run with custom docker compose image version :( for {img}") + else: + image_cmd = "" + logging.info("Cannot run with custom docker image version :(") + return image_cmd def run_impl(self, repo_path, build_path): self._install_clickhouse(build_path) @@ -157,30 +207,27 @@ def run_impl(self, repo_path, build_path): subprocess.check_output("sudo iptables -L", shell=True), ) - test_results = [] - result_state = "success" - failed_sum = 0 - passed_sum = 0 - other_sum = 0 - # FIXME: implement running testflows image and collection of counts - status_text = "fail: {}, passed: {}, other: {}".format( - failed_sum, passed_sum, other_sum - ) - - if self.soft_deadline_time < time.time(): - status_text = "Timeout, " + status_text - result_state = "failure" + image_cmd = self._get_runner_image_cmd(repo_path) - return result_state, status_text, test_results, [] + cmd = (f"cd {repo_path}/tests/testflows && timeout -s 9 10h " + f"./runner {self._get_runner_opts()} {image_cmd}") + log_basename = ".log" + log_path = os.path.join(repo_path, "tests/testflows", log_basename) + with open(log_path, "w") as log: + logging.info("Executing cmd: %s", cmd) + retcode = subprocess.Popen( + cmd, shell=True, stderr=log, stdout=log + ).wait() + if retcode == 0: + logging.info("Run successfully") + else: + logging.info("Some tests failed") -def write_results(results_file, status_file, results, status): - with open(results_file, "w") as f: - out = csv.writer(f, delimiter="\t") - out.writerows(results) - with open(status_file, "w") as f: - out = csv.writer(f, delimiter="\t") - out.writerow(status) + log_result_path = os.path.join( + str(self.path()), "testflows_run" + log_basename + ) + shutil.copy(log_path, log_result_path) if __name__ == "__main__": @@ -195,11 +242,5 @@ def write_results(results_file, status_file, results, status): runner = ClickhouseTestFlowsTestsRunner(result_path, params) logging.info("Running tests") - state, description, test_results, _ = runner.run_impl(repo_path, build_path) - logging.info("Tests finished") - - status = (state, description) - out_results_file = os.path.join(str(runner.path()), "test_results.tsv") - out_status_file = os.path.join(str(runner.path()), "check_status.tsv") - write_results(out_results_file, out_status_file, test_results, status) - logging.info("Results written") + runner.run_impl(repo_path, build_path) + logging.info("Tests finished") \ No newline at end of file From 53c1fbe9b36aa4a1d3d96d178ead9d592e83a908 Mon Sep 17 00:00:00 2001 From: Vitaliy Zakaznikov Date: Mon, 22 Aug 2022 20:19:25 -0400 Subject: [PATCH 31/72] Fixing error in tests/testflows/ci-runner.py --- tests/testflows/ci-runner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testflows/ci-runner.py b/tests/testflows/ci-runner.py index 7ba7f727bf5b..0f7382ae599b 100755 --- a/tests/testflows/ci-runner.py +++ b/tests/testflows/ci-runner.py @@ -79,13 +79,13 @@ def clear_ip_tables_and_restart_daemons(): class ClickhouseTestFlowsTestsRunner: def __init__(self, result_path, params): - self.image_versions = self.params["docker_images_with_versions"] self.result_path = result_path self.params = params self.start_time = time.time() self.disable_net_host = ( "disable_net_host" in self.params and self.params["disable_net_host"] ) + self.image_versions = self.params["docker_images_with_versions"] def path(self): return self.result_path From 529b165c32a914ada6a9555bc7da93c83033b316 Mon Sep 17 00:00:00 2001 From: Vitaliy Zakaznikov Date: Tue, 23 Aug 2022 08:51:18 -0400 Subject: [PATCH 32/72] Adding code to handle images with versions. --- tests/ci/testflows_test_check.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/tests/ci/testflows_test_check.py b/tests/ci/testflows_test_check.py index 5299f61065bc..6e0ef96d60e4 100644 --- a/tests/ci/testflows_test_check.py +++ b/tests/ci/testflows_test_check.py @@ -15,6 +15,7 @@ from pr_info import PRInfo from build_download_helper import download_all_deb_packages from upload_result_helper import upload_results +from docker_pull_helper import get_images_with_versions from commit_status_helper import post_commit_status from clickhouse_helper import ( ClickHouseHelper, @@ -25,13 +26,20 @@ from rerun_helper import RerunHelper from tee_popen import TeePopen +# When update, update +# testflows/ci-runner.py:ClickhouseTestFlowsTestsRunner.get_images_names too +IMAGES = [ + "altinityinfra/testflows-runner", +] -def get_json_params_dict(check_name, pr_info): + +def get_json_params_dict(check_name, pr_info, docker_images): return { "context_name": check_name, "commit": pr_info.sha, "pull_request": pr_info.number, "pr_info": {"changed_files": list(pr_info.changed_files)}, + "docker_images_with_versions": docker_images, } @@ -111,6 +119,8 @@ def process_results(result_folder): logging.info("Check is already finished according to github status, exiting") sys.exit(0) + images = get_images_with_versions(reports_path, IMAGES) + images_with_versions = {i.name: i.version for i in images} result_path = os.path.join(temp_path, "output_dir") if not os.path.exists(result_path): os.makedirs(result_path) @@ -130,12 +140,7 @@ def process_results(result_folder): json_path = os.path.join(work_path, "params.json") with open(json_path, "w", encoding="utf-8") as json_params: json_params.write( - json.dumps( - get_json_params_dict( - check_name, - pr_info, - ) - ) + json.dumps(get_json_params_dict(check_name, pr_info, images_with_versions)) ) output_path_log = os.path.join(result_path, "main_script_log.txt") From f882f75589267740fddd55a9f2706e4304aa9a51 Mon Sep 17 00:00:00 2001 From: Vitaliy Zakaznikov Date: Tue, 23 Aug 2022 10:09:36 -0400 Subject: [PATCH 33/72] Updating testflows version to 1.9.15 --- docker/test/testflows/runner/Dockerfile | 2 +- tests/testflows/regression.py | 76 ++++++++++++------------- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/docker/test/testflows/runner/Dockerfile b/docker/test/testflows/runner/Dockerfile index bfc3ed5e39fb..3e82288088b2 100644 --- a/docker/test/testflows/runner/Dockerfile +++ b/docker/test/testflows/runner/Dockerfile @@ -38,7 +38,7 @@ RUN apt-get update \ ENV TZ=Europe/Moscow RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone -RUN pip3 install urllib3 testflows==1.7.20 docker-compose==1.29.2 docker==5.0.0 dicttoxml kazoo tzlocal==2.1 pytz python-dateutil numpy +RUN pip3 install urllib3 testflows==1.9.15 docker-compose==1.29.2 docker==5.0.0 dicttoxml kazoo tzlocal==2.1 pytz python-dateutil numpy ENV DOCKER_CHANNEL stable ENV DOCKER_VERSION 20.10.6 diff --git a/tests/testflows/regression.py b/tests/testflows/regression.py index bce8274c5cc0..80bbb2bc3f2e 100755 --- a/tests/testflows/regression.py +++ b/tests/testflows/regression.py @@ -31,44 +31,44 @@ def regression( parallel=True, executor=pool, )(**args) - Feature( - test=load("ldap.regression", "regression"), parallel=True, executor=pool - )(**args) - Feature( - test=load("rbac.regression", "regression"), parallel=True, executor=pool - )(**args) - Feature( - test=load("aes_encryption.regression", "regression"), - parallel=True, - executor=pool, - )( - **args - ) # TODO: fix it! - # Feature(test=load("map_type.regression", "regression"), parallel=True, executor=pool)(**args) # TODO: fix it! - Feature( - test=load("window_functions.regression", "regression"), - parallel=True, - executor=pool, - )( - **args - ) # TODO: fix it! - Feature( - test=load("datetime64_extended_range.regression", "regression"), - parallel=True, - executor=pool, - )(**args) - Feature( - test=load("kerberos.regression", "regression"), - parallel=True, - executor=pool, - )(**args) - Feature( - test=load("extended_precision_data_types.regression", "regression"), - parallel=True, - executor=pool, - )( - **args - ) # TODO: fix it! +# Feature( +# test=load("ldap.regression", "regression"), parallel=True, executor=pool +# )(**args) +# Feature( +# test=load("rbac.regression", "regression"), parallel=True, executor=pool +# )(**args) +# Feature( +# test=load("aes_encryption.regression", "regression"), +# parallel=True, +# executor=pool, +# )( +# **args +# ) # TODO: fix it! +# # Feature(test=load("map_type.regression", "regression"), parallel=True, executor=pool)(**args) # TODO: fix it! +# Feature( +# test=load("window_functions.regression", "regression"), +# parallel=True, +# executor=pool, +# )( +# **args +# ) # TODO: fix it! +# Feature( +# test=load("datetime64_extended_range.regression", "regression"), +# parallel=True, +# executor=pool, +# )(**args) +# Feature( +# test=load("kerberos.regression", "regression"), +# parallel=True, +# executor=pool, +# )(**args) +# Feature( +# test=load("extended_precision_data_types.regression", "regression"), +# parallel=True, +# executor=pool, +# )( +# **args +# ) # TODO: fix it! finally: join() From 97051b330d300c3aed0f48f3c31e642e318e8536 Mon Sep 17 00:00:00 2001 From: Vitaliy Zakaznikov Date: Tue, 23 Aug 2022 17:03:19 -0400 Subject: [PATCH 34/72] Trying to fix missing raw.log. --- tests/testflows/ci-runner.py | 40 +++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/tests/testflows/ci-runner.py b/tests/testflows/ci-runner.py index 0f7382ae599b..b0e846a1a3fc 100755 --- a/tests/testflows/ci-runner.py +++ b/tests/testflows/ci-runner.py @@ -194,7 +194,9 @@ def _get_runner_image_cmd(self, repo_path): ) image_cmd += " --docker-image-version={} ".format(runner_version) else: - logging.info(f"Cannot run with custom docker compose image version :( for {img}") + logging.info( + f"Cannot run with custom docker compose image version :( for {img}" + ) else: image_cmd = "" logging.info("Cannot run with custom docker image version :(") @@ -209,25 +211,25 @@ def run_impl(self, repo_path, build_path): image_cmd = self._get_runner_image_cmd(repo_path) - cmd = (f"cd {repo_path}/tests/testflows && timeout -s 9 10h " - f"./runner {self._get_runner_opts()} {image_cmd}") - - log_basename = ".log" - log_path = os.path.join(repo_path, "tests/testflows", log_basename) - with open(log_path, "w") as log: - logging.info("Executing cmd: %s", cmd) - retcode = subprocess.Popen( - cmd, shell=True, stderr=log, stdout=log - ).wait() - if retcode == 0: - logging.info("Run successfully") - else: - logging.info("Some tests failed") + log_path = os.path.join(repo_path, "tests/testflows", "run.log") + test_log_path = os.path.join(repo_path, "tests/testflows", "test.log") - log_result_path = os.path.join( - str(self.path()), "testflows_run" + log_basename + cmd = ( + f"(set -o pipefail && cd {repo_path}/tests/testflows && timeout -s 9 10h " + f"./runner {self._get_runner_opts()} {image_cmd} | tee {log_path})" ) - shutil.copy(log_path, log_result_path) + + logging.info("Executing cmd: %s", cmd) + retcode = subprocess.Popen( + cmd, shell=True, stderr=subprocess.STDOUT, stdout=subprocess.STDOUT + ).wait() + if retcode == 0: + logging.info("Run successfully") + else: + logging.info("Some tests failed") + + shutil.copy(log_path, os.path.join(str(self.path()), "run.log")) + shutil.copy(test_log_path, os.path.join(str(self.path()), "raw.log")) if __name__ == "__main__": @@ -243,4 +245,4 @@ def run_impl(self, repo_path, build_path): logging.info("Running tests") runner.run_impl(repo_path, build_path) - logging.info("Tests finished") \ No newline at end of file + logging.info("Tests finished") From 3472799f3f0e22cc302d7566ef2a09591f09c023 Mon Sep 17 00:00:00 2001 From: Vitaliy Zakaznikov Date: Tue, 23 Aug 2022 18:43:11 -0400 Subject: [PATCH 35/72] Fixing launch command in tests/testflows/ci-runner.py --- tests/testflows/ci-runner.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/testflows/ci-runner.py b/tests/testflows/ci-runner.py index b0e846a1a3fc..e24c0e5ba0cb 100755 --- a/tests/testflows/ci-runner.py +++ b/tests/testflows/ci-runner.py @@ -215,8 +215,8 @@ def run_impl(self, repo_path, build_path): test_log_path = os.path.join(repo_path, "tests/testflows", "test.log") cmd = ( - f"(set -o pipefail && cd {repo_path}/tests/testflows && timeout -s 9 10h " - f"./runner {self._get_runner_opts()} {image_cmd} | tee {log_path})" + f"set -o pipefail && cd {repo_path}/tests/testflows && timeout -s 9 10h " + f"./runner {self._get_runner_opts()} {image_cmd} | tee {log_path}" ) logging.info("Executing cmd: %s", cmd) From c73d74d91d4d133109f2881eb19e7526ecc8133d Mon Sep 17 00:00:00 2001 From: Vitaliy Zakaznikov Date: Tue, 23 Aug 2022 21:13:10 -0400 Subject: [PATCH 36/72] Fixing launch command again in tests/testflows/ci-runner.py --- tests/testflows/ci-runner.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/testflows/ci-runner.py b/tests/testflows/ci-runner.py index e24c0e5ba0cb..4583f37ff36d 100755 --- a/tests/testflows/ci-runner.py +++ b/tests/testflows/ci-runner.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 from collections import defaultdict -import csv +import sys import json import logging import os @@ -215,13 +215,13 @@ def run_impl(self, repo_path, build_path): test_log_path = os.path.join(repo_path, "tests/testflows", "test.log") cmd = ( - f"set -o pipefail && cd {repo_path}/tests/testflows && timeout -s 9 10h " - f"./runner {self._get_runner_opts()} {image_cmd} | tee {log_path}" + f"bash -c \"set -o pipefail && cd {repo_path}/tests/testflows && timeout -s 9 10h " + f"./runner {self._get_runner_opts()} {image_cmd} | tee {log_path}\"" ) logging.info("Executing cmd: %s", cmd) retcode = subprocess.Popen( - cmd, shell=True, stderr=subprocess.STDOUT, stdout=subprocess.STDOUT + cmd, shell=True, stderr=sys.stdout, stdout=sys.stdout ).wait() if retcode == 0: logging.info("Run successfully") From 5458a600b90258d82b823941466184698982ad49 Mon Sep 17 00:00:00 2001 From: Vitaliy Zakaznikov Date: Wed, 24 Aug 2022 18:30:51 -0400 Subject: [PATCH 37/72] Synchronizing open-sourced TestFlows test suites. --- .../_instances/mysql1/database/ca.pem | Bin 0 -> 1112 bytes .../mysql1/database/client-cert.pem | Bin 0 -> 1112 bytes .../_instances/mysql1/database/public_key.pem | Bin 0 -> 452 bytes .../mysql1/database/server-cert.pem | Bin 0 -> 1112 bytes .../aes_encryption_env/clickhouse-service.yml | 18 +- .../aes_encryption_env/docker-compose.yml | 26 +- .../aes_encryption_env/mysql-service.yml | 2 +- .../aes_encryption_env/zookeeper-service.yml | 2 +- .../clickhouse-service.yml | 32 + .../docker-compose.yml | 85 + .../mysql-service.yml | 19 + .../zookeeper-service.yml | 18 + .../configs/clickhouse/common.xml | 6 + .../configs/clickhouse/config.xml | 436 ++ .../configs/clickhouse/users.xml | 133 + tests/testflows/aes_encryption/regression.py | 23 +- .../testflows/aes_encryption/tests/common.py | 1 + .../testflows/aes_encryption/tests/decrypt.py | 18 +- .../aes_encryption/tests/decrypt_mysql.py | 10 +- tests/testflows/ci-runner.py | 4 +- .../configs/clickhouse/common.xml | 6 + .../configs/clickhouse/config.xml | 436 ++ .../configs/clickhouse/users.xml | 133 + .../clickhouse-service.yml | 18 +- .../docker-compose.yml | 27 +- .../zookeeper-service.yml | 2 +- .../clickhouse-service.yml | 29 + .../docker-compose.yml | 62 + .../zookeeper-service.yml | 18 + .../datetime64_extended_range/regression.py | 52 +- .../requirements/requirements.md | 229 +- .../requirements/requirements.py | 672 +- .../datetime64_extended_range/tests/common.py | 99 +- .../tests/date_time_functions.py | 98 +- .../tests/format_conversion.py | 120 + .../tests/generic.py | 17 +- .../tests/non_existent_time.py | 2 +- .../tests/reference_times.py | 1 + .../tests/type_conversion.py | 163 +- .../example/configs/clickhouse/common.xml | 6 + .../example/configs/clickhouse/config.xml | 436 ++ .../example/configs/clickhouse/users.xml | 133 + .../example_env/clickhouse-service.yml | 2 +- .../example/example_env/docker-compose.yml | 2 +- .../example/example_env/zookeeper-service.yml | 2 +- .../example_env_arm64/clickhouse-service.yml | 2 +- .../example_env_arm64/docker-compose.yml | 2 +- .../example_env_arm64/zookeeper-service.yml | 2 +- .../extended_precision_data_types/common.py | 10 +- .../configs/clickhouse/common.xml | 6 + .../configs/clickhouse/config.xml | 448 ++ .../configs/clickhouse/users.xml | 133 + .../clickhouse-service.yml | 28 + .../docker-compose.yml | 30 + .../mysql-service.yml | 19 + .../clickhouse-service.yml | 28 + .../docker-compose.yml | 30 + .../mysql-service.yml | 19 + .../regression.py | 20 +- .../common.py.tests.post22.3.snapshot | 6334 +++++++++++++++++ .../common.py.tests.pre22.3.snapshot | 6286 ++++++++++++++++ .../tests/arithmetic.py | 6 + .../tests/array_tuple_map.py | 19 +- tests/testflows/helpers/argparser.py | 2 +- tests/testflows/helpers/cluster.py | 258 +- tests/testflows/helpers/common.py | 81 +- .../kerberos/configs/clickhouse/common.xml | 6 + .../configs/clickhouse/config.d/logs.xml | 4 +- .../configs/clickhouse/config.d/ports.xml | 4 +- .../configs/clickhouse/config.d/remote.xml | 4 +- .../configs/clickhouse/config.d/ssl.xml | 4 +- .../configs/clickhouse/config.d/storage.xml | 4 +- .../configs/clickhouse/config.d/zookeeper.xml | 4 +- .../kerberos/configs/clickhouse/config.xml | 440 ++ .../kerberos/configs/clickhouse/users.xml | 133 + .../configs/clickhouse1/config.d/kerberos.xml | 4 +- .../configs/clickhouse1/config.d/macros.xml | 4 +- .../clickhouse1/users.d/kerberos-users.xml | 4 +- .../configs/clickhouse2/config.d/kerberos.xml | 4 +- .../configs/clickhouse2/config.d/macros.xml | 4 +- .../configs/clickhouse3/config.d/kerberos.xml | 4 +- .../configs/clickhouse3/config.d/macros.xml | 4 +- .../kerberos_env/clickhouse-service.yml | 7 +- .../kerberos/kerberos_env/docker-compose.yml | 2 +- .../kerberos_env/kerberos-service.yml | 2 +- .../kerberos_env/zookeeper-service.yml | 2 +- .../kerberos_env_arm64/clickhouse-service.yml | 32 + .../kerberos_env_arm64/docker-compose.yml | 80 + .../kerberos_env_arm64/kerberos-service.yml | 26 + .../kerberos_env_arm64/zookeeper-service.yml | 18 + tests/testflows/kerberos/regression.py | 16 +- tests/testflows/kerberos/tests/common.py | 10 +- tests/testflows/kerberos/tests/config.py | 39 +- .../authentication_env/clickhouse-service.yml | 2 +- .../authentication_env/docker-compose.yml | 2 +- .../authentication_env/openldap-service.yml | 4 +- .../authentication_env/zookeeper-service.yml | 2 +- .../docker-compose.yml | 2 +- .../openldap-service.yml | 4 +- .../zookeeper-service.yml | 2 +- .../configs/clickhouse/common.xml | 6 + .../configs/clickhouse/config.d/logs.xml | 4 +- .../configs/clickhouse/config.d/ports.xml | 4 +- .../configs/clickhouse/config.d/remote.xml | 4 +- .../configs/clickhouse/config.d/ssl.xml | 4 +- .../configs/clickhouse/config.d/storage.xml | 4 +- .../configs/clickhouse/config.d/zookeeper.xml | 4 +- .../configs/clickhouse/config.xml | 442 ++ .../configs/clickhouse/users.xml | 133 + .../configs/clickhouse1/config.d/macros.xml | 4 +- .../configs/clickhouse2/config.d/macros.xml | 4 +- .../configs/clickhouse3/config.d/macros.xml | 4 +- .../ldap/authentication/regression.py | 4 +- .../requirements/requirements.md | 8 +- .../requirements/requirements.py | 16 +- .../ldap/authentication/tests/common.py | 4 +- .../authentication/tests/server_config.py | 4 +- .../configs/clickhouse/common.xml | 6 + .../configs/clickhouse/config.d/logs.xml | 4 +- .../configs/clickhouse/config.d/ports.xml | 4 +- .../configs/clickhouse/config.d/remote.xml | 4 +- .../configs/clickhouse/config.d/ssl.xml | 4 +- .../configs/clickhouse/config.d/storage.xml | 4 +- .../configs/clickhouse/config.d/zookeeper.xml | 4 +- .../configs/clickhouse/config.xml | 442 ++ .../configs/clickhouse/users.xml | 133 + .../configs/clickhouse1/config.d/macros.xml | 4 +- .../configs/clickhouse2/config.d/macros.xml | 4 +- .../configs/clickhouse3/config.d/macros.xml | 4 +- .../clickhouse-service.yml | 2 +- .../docker-compose.yml | 2 +- .../openldap-service.yml | 4 +- .../zookeeper-service.yml | 2 +- .../docker-compose.yml | 2 +- .../openldap-service.yml | 4 +- .../zookeeper-service.yml | 2 +- .../external_user_directory/regression.py | 4 +- .../requirements/requirements.md | 8 +- .../requirements/requirements.py | 16 +- .../external_user_directory/tests/common.py | 4 +- .../tests/server_config.py | 4 +- tests/testflows/ldap/regression.py | 4 +- .../configs/clickhouse/common.xml | 6 + .../configs/clickhouse/config.d/logs.xml | 4 +- .../configs/clickhouse/config.d/ports.xml | 4 +- .../configs/clickhouse/config.d/remote.xml | 4 +- .../configs/clickhouse/config.d/ssl.xml | 4 +- .../configs/clickhouse/config.d/storage.xml | 4 +- .../configs/clickhouse/config.d/zookeeper.xml | 4 +- .../configs/clickhouse/config.xml | 442 ++ .../configs/clickhouse/users.d/common.xml | 4 +- .../role_mapping/configs/clickhouse/users.xml | 133 + .../configs/clickhouse1/config.d/macros.xml | 4 +- .../configs/clickhouse2/config.d/macros.xml | 4 +- .../configs/clickhouse3/config.d/macros.xml | 4 +- .../testflows/ldap/role_mapping/regression.py | 4 +- .../role_mapping/requirements/requirements.md | 16 +- .../role_mapping/requirements/requirements.py | 32 +- .../role_mapping_env/clickhouse-service.yml | 2 +- .../role_mapping_env/docker-compose.yml | 2 +- .../role_mapping_env/openldap-service.yml | 4 +- .../role_mapping_env/zookeeper-service.yml | 2 +- .../role_mapping_env_arm64/docker-compose.yml | 2 +- .../openldap-service.yml | 4 +- .../zookeeper-service.yml | 2 +- .../configs/clickhouse/config.d/macros.xml | 0 .../map_type/configs/clickhouse/config.xml | 448 ++ .../map_type/configs/clickhouse/users.xml | 133 + .../map_type_env/clickhouse-service.yml | 13 +- .../map_type/map_type_env/docker-compose.yml | 2 +- .../map_type_env/zookeeper-service.yml | 2 +- .../map_type_env_arm64/clickhouse-service.yml | 30 + .../map_type_env_arm64/docker-compose.yml | 60 + .../map_type_env_arm64/zookeeper-service.yml | 18 + tests/testflows/map_type/regression.py | 38 +- tests/testflows/map_type/tests/feature.py | 28 +- .../rbac/configs/clickhouse/common.xml | 6 + .../configs/clickhouse/config.d/ports.xml | 2 + .../rbac/configs/clickhouse/config.xml | 456 ++ .../rbac/configs/clickhouse/users.xml | 133 + tests/testflows/rbac/rbac_env/Dockerfile | 7 + .../rbac/rbac_env/clickhouse-service.yml | 12 +- .../rbac/rbac_env/docker-compose.yml | 10 +- .../testflows/rbac/rbac_env/mysql-service.yml | 19 + .../rbac/rbac_env/zookeeper-service.yml | 2 +- .../rbac_env_arm64/clickhouse-service.yml | 8 +- .../rbac/rbac_env_arm64/docker-compose.yml | 2 +- .../rbac/rbac_env_arm64/zookeeper-service.yml | 2 +- tests/testflows/rbac/regression.py | 39 + .../rbac/requirements/requirements.md | 250 +- .../rbac/requirements/requirements.py | 919 ++- .../tests/privileges/alter/alter_database.py | 144 + .../privileges/create/create_database.py | 62 +- .../privileges/create/create_dictionary.py | 65 +- .../privileges/create/create_function.py | 134 + .../privileges/create/create_row_policy.py | 224 + .../tests/privileges/create/create_table.py | 53 + .../privileges/create/create_temp_table.py | 65 +- .../tests/privileges/drop/drop_database.py | 67 +- .../tests/privileges/drop/drop_dictionary.py | 46 +- .../tests/privileges/drop/drop_function.py | 139 + .../tests/privileges/drop/drop_replica.py | 146 + .../rbac/tests/privileges/drop/drop_table.py | 18 + .../rbac/tests/privileges/feature.py | 55 + .../tests/privileges/grant_drop_user_race.py | 83 + .../rbac/tests/privileges/introspection.py | 124 + .../rbac/tests/privileges/projections.py | 304 + .../rbac/tests/privileges/race_condition.py | 15 + .../tests/privileges/system/drop_cache.py | 263 + .../rbac/tests/privileges/system/reload.py | 290 +- .../tests/privileges/system/restart_disk.py | 137 + .../privileges/system/restore_replica.py | 143 + .../tests/privileges/system/thread_fuzzer.py | 217 + .../rbac/tests/privileges/table_functions.py | 320 + tests/testflows/regression.py | 76 +- .../configs/clickhouse/config.d/macros.xml | 0 .../configs/clickhouse/config.xml | 448 ++ .../configs/clickhouse/users.xml | 133 + .../testflows/window_functions/regression.py | 60 +- .../requirements/requirements.md | 127 + .../requirements/requirements.py | 346 +- .../window_functions/tests/aggregate_funcs.py | 92 +- .../window_functions/tests/common.py | 202 +- .../window_functions/tests/datatypes.py | 339 + .../window_functions/tests/errors.py | 18 +- .../window_functions/tests/feature.py | 24 + .../testflows/window_functions/tests/misc.py | 2 +- .../tests/non_negative_derivative.py | 357 + .../tests/snapshots/common.py.tests.snapshot | 5569 ++++++++++++++- .../tests/time_decayed_funcs.py | 387 + .../clickhouse-service.yml | 8 +- .../window_functions_env/docker-compose.yml | 2 +- .../zookeeper-service.yml | 2 +- .../clickhouse-service.yml | 30 + .../docker-compose.yml | 60 + .../zookeeper-service.yml | 18 + 236 files changed, 32847 insertions(+), 1469 deletions(-) create mode 100644 tests/testflows/aes_encryption/_instances/mysql1/database/ca.pem create mode 100644 tests/testflows/aes_encryption/_instances/mysql1/database/client-cert.pem create mode 100644 tests/testflows/aes_encryption/_instances/mysql1/database/public_key.pem create mode 100644 tests/testflows/aes_encryption/_instances/mysql1/database/server-cert.pem create mode 100644 tests/testflows/aes_encryption/aes_encryption_env_arm64/clickhouse-service.yml create mode 100644 tests/testflows/aes_encryption/aes_encryption_env_arm64/docker-compose.yml create mode 100644 tests/testflows/aes_encryption/aes_encryption_env_arm64/mysql-service.yml create mode 100644 tests/testflows/aes_encryption/aes_encryption_env_arm64/zookeeper-service.yml create mode 100644 tests/testflows/aes_encryption/configs/clickhouse/common.xml create mode 100644 tests/testflows/aes_encryption/configs/clickhouse/config.xml create mode 100644 tests/testflows/aes_encryption/configs/clickhouse/users.xml create mode 100644 tests/testflows/datetime64_extended_range/configs/clickhouse/common.xml create mode 100644 tests/testflows/datetime64_extended_range/configs/clickhouse/config.xml create mode 100644 tests/testflows/datetime64_extended_range/configs/clickhouse/users.xml create mode 100644 tests/testflows/datetime64_extended_range/datetime64_extended_range_env_arm64/clickhouse-service.yml create mode 100644 tests/testflows/datetime64_extended_range/datetime64_extended_range_env_arm64/docker-compose.yml create mode 100644 tests/testflows/datetime64_extended_range/datetime64_extended_range_env_arm64/zookeeper-service.yml create mode 100644 tests/testflows/datetime64_extended_range/tests/format_conversion.py create mode 100644 tests/testflows/example/configs/clickhouse/common.xml create mode 100644 tests/testflows/example/configs/clickhouse/config.xml create mode 100644 tests/testflows/example/configs/clickhouse/users.xml create mode 100644 tests/testflows/extended_precision_data_types/configs/clickhouse/common.xml create mode 100644 tests/testflows/extended_precision_data_types/configs/clickhouse/config.xml create mode 100644 tests/testflows/extended_precision_data_types/configs/clickhouse/users.xml create mode 100644 tests/testflows/extended_precision_data_types/extended_precision_data_types_env/clickhouse-service.yml create mode 100644 tests/testflows/extended_precision_data_types/extended_precision_data_types_env/docker-compose.yml create mode 100644 tests/testflows/extended_precision_data_types/extended_precision_data_types_env/mysql-service.yml create mode 100644 tests/testflows/extended_precision_data_types/extended_precision_data_types_env_arm64/clickhouse-service.yml create mode 100644 tests/testflows/extended_precision_data_types/extended_precision_data_types_env_arm64/docker-compose.yml create mode 100644 tests/testflows/extended_precision_data_types/extended_precision_data_types_env_arm64/mysql-service.yml create mode 100644 tests/testflows/extended_precision_data_types/snapshots/common.py.tests.post22.3.snapshot create mode 100644 tests/testflows/extended_precision_data_types/snapshots/common.py.tests.pre22.3.snapshot create mode 100644 tests/testflows/kerberos/configs/clickhouse/common.xml create mode 100644 tests/testflows/kerberos/configs/clickhouse/config.xml create mode 100644 tests/testflows/kerberos/configs/clickhouse/users.xml create mode 100644 tests/testflows/kerberos/kerberos_env_arm64/clickhouse-service.yml create mode 100644 tests/testflows/kerberos/kerberos_env_arm64/docker-compose.yml create mode 100644 tests/testflows/kerberos/kerberos_env_arm64/kerberos-service.yml create mode 100644 tests/testflows/kerberos/kerberos_env_arm64/zookeeper-service.yml create mode 100644 tests/testflows/ldap/authentication/configs/clickhouse/common.xml create mode 100644 tests/testflows/ldap/authentication/configs/clickhouse/config.xml create mode 100644 tests/testflows/ldap/authentication/configs/clickhouse/users.xml create mode 100644 tests/testflows/ldap/external_user_directory/configs/clickhouse/common.xml create mode 100644 tests/testflows/ldap/external_user_directory/configs/clickhouse/config.xml create mode 100644 tests/testflows/ldap/external_user_directory/configs/clickhouse/users.xml create mode 100644 tests/testflows/ldap/role_mapping/configs/clickhouse/common.xml create mode 100644 tests/testflows/ldap/role_mapping/configs/clickhouse/config.xml create mode 100644 tests/testflows/ldap/role_mapping/configs/clickhouse/users.xml create mode 100644 tests/testflows/map_type/configs/clickhouse/config.d/macros.xml create mode 100644 tests/testflows/map_type/configs/clickhouse/config.xml create mode 100644 tests/testflows/map_type/configs/clickhouse/users.xml create mode 100755 tests/testflows/map_type/map_type_env_arm64/clickhouse-service.yml create mode 100755 tests/testflows/map_type/map_type_env_arm64/docker-compose.yml create mode 100755 tests/testflows/map_type/map_type_env_arm64/zookeeper-service.yml create mode 100644 tests/testflows/rbac/configs/clickhouse/common.xml create mode 100644 tests/testflows/rbac/configs/clickhouse/config.xml create mode 100644 tests/testflows/rbac/configs/clickhouse/users.xml create mode 100644 tests/testflows/rbac/rbac_env/Dockerfile create mode 100644 tests/testflows/rbac/rbac_env/mysql-service.yml create mode 100644 tests/testflows/rbac/tests/privileges/alter/alter_database.py create mode 100644 tests/testflows/rbac/tests/privileges/create/create_function.py create mode 100644 tests/testflows/rbac/tests/privileges/drop/drop_function.py create mode 100644 tests/testflows/rbac/tests/privileges/drop/drop_replica.py create mode 100644 tests/testflows/rbac/tests/privileges/grant_drop_user_race.py create mode 100644 tests/testflows/rbac/tests/privileges/projections.py create mode 100644 tests/testflows/rbac/tests/privileges/race_condition.py create mode 100644 tests/testflows/rbac/tests/privileges/system/restart_disk.py create mode 100644 tests/testflows/rbac/tests/privileges/system/restore_replica.py create mode 100644 tests/testflows/rbac/tests/privileges/system/thread_fuzzer.py create mode 100644 tests/testflows/rbac/tests/privileges/table_functions.py create mode 100644 tests/testflows/window_functions/configs/clickhouse/config.d/macros.xml create mode 100644 tests/testflows/window_functions/configs/clickhouse/config.xml create mode 100644 tests/testflows/window_functions/configs/clickhouse/users.xml create mode 100644 tests/testflows/window_functions/tests/datatypes.py create mode 100644 tests/testflows/window_functions/tests/non_negative_derivative.py create mode 100644 tests/testflows/window_functions/tests/time_decayed_funcs.py create mode 100755 tests/testflows/window_functions/window_functions_env_arm64/clickhouse-service.yml create mode 100755 tests/testflows/window_functions/window_functions_env_arm64/docker-compose.yml create mode 100755 tests/testflows/window_functions/window_functions_env_arm64/zookeeper-service.yml diff --git a/tests/testflows/aes_encryption/_instances/mysql1/database/ca.pem b/tests/testflows/aes_encryption/_instances/mysql1/database/ca.pem new file mode 100644 index 0000000000000000000000000000000000000000..36ecccedaea80bd521d9acd85aad5b44575cd007 GIT binary patch literal 1112 zcmZvc%g&=X5QS&WQ_LRy3L!wkq8A1m0s(U~gm}j#5E2OFLQHu2p?fi#8B0g9zB*Es z%cc7HS&oKG3;(U7kXVML1A>0TAI`Nbs9kj(oRu0dO9S;H0Of$0PnqvumhYnTDTKDBFDDzcW!og+Zdir5YhS(%~c;0wv3{Ba3k`IN67uT9;3O zL4Y(Z0I;r+58z@5dVRTTzeUxLBbBSxRKt?9EylO{-m^UJ z@R)o*kni}VGm4}5w8TX6U^Hc2v+roMzg79hgey6Wys74-RoY2i>b<6i^%VQQO_R+W z<1+PwZRPDA*oLJ^tp~kVUpS)ArY@xJc%LzK_pW#CxeHux4I!Y8khOUQ1M%ZquRGGq z?djn5*0VA@A%5#QlrAez^75qV7JenTfC|K7O4?Ja0BS1Es90mG@j7ccyfFZzv%q6ad7-Y+Yg#rP=ex-q!EkSKH|Ib7MZ}}Yr z9(kG=28Bt;?hl{BeWs=GH6{%Xg8Tqu2?yw_6=qR6au-d7oR_rmp`qg%F&SC;xb7|su;2Ys? VpPQsf>tDaPKWhSE_}|s>*FT5lWN-ig literal 0 HcmV?d00001 diff --git a/tests/testflows/aes_encryption/_instances/mysql1/database/client-cert.pem b/tests/testflows/aes_encryption/_instances/mysql1/database/client-cert.pem new file mode 100644 index 0000000000000000000000000000000000000000..9f76e77d4a4895a398971d846d90058ce38a05e2 GIT binary patch literal 1112 zcmZXUOV@Kq5XWcjQ_S9Z4?=>)EV{KzCn4^{E(j4sL}+~a@jQ#!OrP`L{Hp78UApSe zpY1?Z$JXD9>iSs23h>n*_=hqvMsQUWaHmT^Fa+hM6bT%C{eux%tlE-Dr zm(t~4?CwgmGgG>v!#wau-|yL3&150H@a0`pPeh1V>N#;7OaT1~Beoj)snU#_>#0E+ z)cz}P@m>{=m@ClRXfDzY4y6B(7)_F7V=|xDib_`byl8u=GC*QBNpMNwRRU|A8t;>R zv}_Brcl`oK%pAjy>V#Ku6lG6uqXBaVa52GepL3O{kRgs@0zi;+J)XJ*Kc>tA zj(Ua;KwD=vpXJ5?&82kU!pL7sjrf_z073q{Y;V*<&k}bHh66 zLb5aqtBl6SGB1tp3xKH_3o*fmi${3Vhp}5Z9RMZt@XO%xdsaRq)163v5nAb+ zuG_>oQhcUz_Mom``#n@Lyk$iVWLx1m-<3o-XgTThM&Pk@F8cO%5ZK}{-x(vrU*Q#%^5hDQ`VK2;>B>Sq2z_})3ztTQ`h8~y z+*9ms?~KutE(7G;cTz1HA@3#@<4!bbWfI%jpv<_xa`m`+2~M)dkhgwO^b#s^Vx6g4 a|6WQzn$vop`_Jn?)bE;5E%g7@@z=kX{$xe~ literal 0 HcmV?d00001 diff --git a/tests/testflows/aes_encryption/_instances/mysql1/database/public_key.pem b/tests/testflows/aes_encryption/_instances/mysql1/database/public_key.pem new file mode 100644 index 0000000000000000000000000000000000000000..776da37dfe8c29cf90894da2a9034efd791c8da2 GIT binary patch literal 452 zcmZXR%W|qf07SFqE3&67AjoSuBOqjik>>zA1O%LD0th&M{WW)+UUl_Zb)C)T6=>~U z|NWT2gS&f2R{pzfIL~#VdjURd>Cn-BZ>%B&fn@`LcOcNgFWnqXmNuOrix-YjSQna=R>=cl;gU9EE*({Z+ksyhnrs` ziY3lC%MKC7RX3BeEoq{A>cP?a5E+j)6RLAG?t)aoPSMn4#AEhspqz{3b}!awu40En zBmmHpf;8!?h8!7xO(Wftek`!CrYKJLZ~cOgCVMnBiul4CZl)kkB4%lAn%m{V@xIUs z+OTCPnLi2t?J{wSJE~SmhEFQZ?(wB==pona>0zKAmvlebap05KRub4Y(g)Q4*4mLJ!7DLRQ+(JkQgailyhy41F?!#!LDd|;7PnA@g zR9|1SK}<~dZyo!gX_z_)@elkGsA(ej(RFYZ3&2eXidhwKVR7rOw~}nR1Oe*}fWU@^ z4fntv#f%|@4EG$nz7QJQ@Q_-{tcx3Hs#8&(N`{=1?ZsE(VdmGI#J5y(;jH@f@Y}<^ zuJ);4Q>di^3eAb4f_upP&`-pv(WEG(zswE)Wt<@_Qp5?6BMdl~!?X^g=zS3)C7$wo zJf)v#a3}3UjV;Vme@vyk&Sw5o>F}Q}*-t*H@5jWQJBzqb6u6{7B6`q~z|2&{@H}$_ zfM!C(0iWYGXy&~zebLBcNzNeKye-&TWf(H66jD=6adrs0gsamLHDv-D@76Badi&jK z&hkni?6lq){U`X4&e?k;oK^<)c-h&SfduP7+`x(w$ywlax3gt@ao@&JVb@!5wRGv) zP+$|(Y4yoQtbRw9cv@zab{V0rr1nx6>Y?m*R4nm8%)aP%?yWvO!;dzGuR%lEXV4Ae4f@IMBM?ZO0OoUkJyYS8Es(-EnaFPK&Cr1Kccw3btYIrQ|IXm!G zm3ViRV^*}>M(Bjz$IjzBlnCptHYL`EiLtkVdZOtXhw=NoRCX6@2Np-TdWIg_j6@tP zDQ~-BW!g$HCTrL-Ta#r06chqLDvW?f!S9`O%rRVWrc(eoVtdPO4(4?{xqx8@GJxjG zKw#dr$JrQ!%%j?}UMQQ#?U7%W29-=B&qcz)(tx>FNS6S}#?jUd+*qSL>Wr zvzwwqsJGfed9x3JiOZ{JxiDJg$yI;KCg0J--pcmK>XlcQ5G&RqHt)E1q>+i&s6rZ9ce4hKuU@gf4moi-5rDIe0u!babSpQWhk)bGcln3!`7+Fcgdg>DOU z#V%#K4J;Lrj2$+Pe9TFc)Y>xS8ol)wpVyxRIs4-t=&w>{nHLSJ@f6v+a)X>Pxi#aJ-_*pnHMWKI?5C-R_6m7~+NExxzYbyJm$f VZobcMa{5R3eG}M4|9>5S{Q>)8V}<|# literal 0 HcmV?d00001 diff --git a/tests/testflows/aes_encryption/aes_encryption_env/clickhouse-service.yml b/tests/testflows/aes_encryption/aes_encryption_env/clickhouse-service.yml index 74a56b63aabc..532ce9185735 100644 --- a/tests/testflows/aes_encryption/aes_encryption_env/clickhouse-service.yml +++ b/tests/testflows/aes_encryption/aes_encryption_env/clickhouse-service.yml @@ -2,25 +2,31 @@ version: '2.3' services: clickhouse: - image: altinityinfra/integration-test + image: ${IMAGE_DEPENDENCY_PROXY}clickhouse/clickhouse-integration-test:28741 + init: true expose: - "9000" - "9009" - "8123" volumes: - - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d:/etc/clickhouse-server/config.d" - - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/users.d/:/etc/clickhouse-server/users.d" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/logs.xml:/etc/clickhouse-server/config.d/logs.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/ports.xml:/etc/clickhouse-server/config.d/ports.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/remote.xml:/etc/clickhouse-server/config.d/remote.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/ssl.xml:/etc/clickhouse-server/config.d/ssl.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/storage.xml:/etc/clickhouse-server/config.d/storage.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/zookeeper.xml:/etc/clickhouse-server/config.d/zookeeper.xml" - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/ssl:/etc/clickhouse-server/ssl" - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.xml:/etc/clickhouse-server/config.xml" - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/users.xml:/etc/clickhouse-server/users.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/common.xml:/etc/clickhouse-server/common.xml" - "${CLICKHOUSE_TESTS_SERVER_BIN_PATH:-/usr/bin/clickhouse}:/usr/bin/clickhouse" - "${CLICKHOUSE_TESTS_ODBC_BRIDGE_BIN_PATH:-/usr/bin/clickhouse-odbc-bridge}:/usr/bin/clickhouse-odbc-bridge" - entrypoint: bash -c "clickhouse server --config-file=/etc/clickhouse-server/config.xml --log-file=/var/log/clickhouse-server/clickhouse-server.log --errorlog-file=/var/log/clickhouse-server/clickhouse-server.err.log" + entrypoint: bash -c "tail -f /dev/null" healthcheck: - test: clickhouse client --query='select 1' + test: echo 1 interval: 10s timeout: 10s - retries: 10 + retries: 3 start_period: 300s cap_add: - SYS_PTRACE diff --git a/tests/testflows/aes_encryption/aes_encryption_env/docker-compose.yml b/tests/testflows/aes_encryption/aes_encryption_env/docker-compose.yml index 124b53bf502b..3224c1fd766e 100644 --- a/tests/testflows/aes_encryption/aes_encryption_env/docker-compose.yml +++ b/tests/testflows/aes_encryption/aes_encryption_env/docker-compose.yml @@ -22,8 +22,12 @@ services: volumes: - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse1/database/:/var/lib/clickhouse/" - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse1/logs/:/var/log/clickhouse-server/" - - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse1/config.d:/etc/clickhouse-server/config.d" - - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse1/users.d:/etc/clickhouse-server/users.d" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/logs.xml:/etc/clickhouse-server/config.d/logs.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/ports.xml:/etc/clickhouse-server/config.d/ports.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/remote.xml:/etc/clickhouse-server/config.d/remote.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/ssl.xml:/etc/clickhouse-server/config.d/ssl.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/storage.xml:/etc/clickhouse-server/config.d/storage.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/zookeeper.xml:/etc/clickhouse-server/config.d/zookeeper.xml" depends_on: zookeeper: condition: service_healthy @@ -36,8 +40,12 @@ services: volumes: - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse2/database/:/var/lib/clickhouse/" - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse2/logs/:/var/log/clickhouse-server/" - - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse2/config.d:/etc/clickhouse-server/config.d" - - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse2/users.d:/etc/clickhouse-server/users.d" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/logs.xml:/etc/clickhouse-server/config.d/logs.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/ports.xml:/etc/clickhouse-server/config.d/ports.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/remote.xml:/etc/clickhouse-server/config.d/remote.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/ssl.xml:/etc/clickhouse-server/config.d/ssl.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/storage.xml:/etc/clickhouse-server/config.d/storage.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/zookeeper.xml:/etc/clickhouse-server/config.d/zookeeper.xml" depends_on: zookeeper: condition: service_healthy @@ -50,8 +58,12 @@ services: volumes: - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse3/database/:/var/lib/clickhouse/" - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse3/logs/:/var/log/clickhouse-server/" - - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse3/config.d:/etc/clickhouse-server/config.d" - - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse3/users.d:/etc/clickhouse-server/users.d" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/logs.xml:/etc/clickhouse-server/config.d/logs.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/ports.xml:/etc/clickhouse-server/config.d/ports.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/remote.xml:/etc/clickhouse-server/config.d/remote.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/ssl.xml:/etc/clickhouse-server/config.d/ssl.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/storage.xml:/etc/clickhouse-server/config.d/storage.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/zookeeper.xml:/etc/clickhouse-server/config.d/zookeeper.xml" depends_on: zookeeper: condition: service_healthy @@ -59,7 +71,7 @@ services: # dummy service which does nothing, but allows to postpone # 'docker-compose up -d' till all dependecies will go healthy all_services_ready: - image: hello-world + image: ${IMAGE_DEPENDENCY_PROXY}hello-world depends_on: mysql1: condition: service_healthy diff --git a/tests/testflows/aes_encryption/aes_encryption_env/mysql-service.yml b/tests/testflows/aes_encryption/aes_encryption_env/mysql-service.yml index 6924bccfad5b..5423b0e0dab4 100644 --- a/tests/testflows/aes_encryption/aes_encryption_env/mysql-service.yml +++ b/tests/testflows/aes_encryption/aes_encryption_env/mysql-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: mysql: - image: mysql:5.7.30 + image: ${IMAGE_DEPENDENCY_PROXY}mysql:5.7.30 restart: always environment: MYSQL_DATABASE: 'db' diff --git a/tests/testflows/aes_encryption/aes_encryption_env/zookeeper-service.yml b/tests/testflows/aes_encryption/aes_encryption_env/zookeeper-service.yml index f27405b97a2c..60c0e4e7de37 100644 --- a/tests/testflows/aes_encryption/aes_encryption_env/zookeeper-service.yml +++ b/tests/testflows/aes_encryption/aes_encryption_env/zookeeper-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: zookeeper: - image: zookeeper:3.6.2 + image: ${IMAGE_DEPENDENCY_PROXY}zookeeper:3.6.2 expose: - "2181" environment: diff --git a/tests/testflows/aes_encryption/aes_encryption_env_arm64/clickhouse-service.yml b/tests/testflows/aes_encryption/aes_encryption_env_arm64/clickhouse-service.yml new file mode 100644 index 000000000000..0923283ac844 --- /dev/null +++ b/tests/testflows/aes_encryption/aes_encryption_env_arm64/clickhouse-service.yml @@ -0,0 +1,32 @@ +version: '2.3' + +services: + clickhouse: + image: registry.gitlab.com/altinity-public/container-images/test/clickhouse-integration-test:21.12 + expose: + - "9000" + - "9009" + - "8123" + volumes: + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/logs.xml:/etc/clickhouse-server/config.d/logs.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/ports.xml:/etc/clickhouse-server/config.d/ports.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/remote.xml:/etc/clickhouse-server/config.d/remote.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/ssl.xml:/etc/clickhouse-server/config.d/ssl.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/storage.xml:/etc/clickhouse-server/config.d/storage.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/zookeeper.xml:/etc/clickhouse-server/config.d/zookeeper.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/ssl:/etc/clickhouse-server/ssl" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.xml:/etc/clickhouse-server/config.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/users.xml:/etc/clickhouse-server/users.xml" + - "${CLICKHOUSE_TESTS_SERVER_BIN_PATH:-/usr/bin/clickhouse}:/usr/bin/clickhouse" + - "${CLICKHOUSE_TESTS_ODBC_BRIDGE_BIN_PATH:-/usr/bin/clickhouse-odbc-bridge}:/usr/bin/clickhouse-odbc-bridge" + entrypoint: bash -c "clickhouse server --config-file=/etc/clickhouse-server/config.xml --log-file=/var/log/clickhouse-server/clickhouse-server.log --errorlog-file=/var/log/clickhouse-server/clickhouse-server.err.log" + healthcheck: + test: clickhouse client --query='select 1' + interval: 10s + timeout: 10s + retries: 10 + start_period: 300s + cap_add: + - SYS_PTRACE + security_opt: + - label:disable diff --git a/tests/testflows/aes_encryption/aes_encryption_env_arm64/docker-compose.yml b/tests/testflows/aes_encryption/aes_encryption_env_arm64/docker-compose.yml new file mode 100644 index 000000000000..3224c1fd766e --- /dev/null +++ b/tests/testflows/aes_encryption/aes_encryption_env_arm64/docker-compose.yml @@ -0,0 +1,85 @@ +version: '2.3' + +services: + zookeeper: + extends: + file: zookeeper-service.yml + service: zookeeper + + mysql1: + extends: + file: mysql-service.yml + service: mysql + hostname: mysql1 + volumes: + - "${CLICKHOUSE_TESTS_DIR}/_instances/mysql1/database:/var/lib/mysql" + + clickhouse1: + extends: + file: clickhouse-service.yml + service: clickhouse + hostname: clickhouse1 + volumes: + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse1/database/:/var/lib/clickhouse/" + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse1/logs/:/var/log/clickhouse-server/" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/logs.xml:/etc/clickhouse-server/config.d/logs.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/ports.xml:/etc/clickhouse-server/config.d/ports.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/remote.xml:/etc/clickhouse-server/config.d/remote.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/ssl.xml:/etc/clickhouse-server/config.d/ssl.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/storage.xml:/etc/clickhouse-server/config.d/storage.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/zookeeper.xml:/etc/clickhouse-server/config.d/zookeeper.xml" + depends_on: + zookeeper: + condition: service_healthy + + clickhouse2: + extends: + file: clickhouse-service.yml + service: clickhouse + hostname: clickhouse2 + volumes: + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse2/database/:/var/lib/clickhouse/" + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse2/logs/:/var/log/clickhouse-server/" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/logs.xml:/etc/clickhouse-server/config.d/logs.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/ports.xml:/etc/clickhouse-server/config.d/ports.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/remote.xml:/etc/clickhouse-server/config.d/remote.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/ssl.xml:/etc/clickhouse-server/config.d/ssl.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/storage.xml:/etc/clickhouse-server/config.d/storage.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/zookeeper.xml:/etc/clickhouse-server/config.d/zookeeper.xml" + depends_on: + zookeeper: + condition: service_healthy + + clickhouse3: + extends: + file: clickhouse-service.yml + service: clickhouse + hostname: clickhouse3 + volumes: + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse3/database/:/var/lib/clickhouse/" + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse3/logs/:/var/log/clickhouse-server/" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/logs.xml:/etc/clickhouse-server/config.d/logs.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/ports.xml:/etc/clickhouse-server/config.d/ports.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/remote.xml:/etc/clickhouse-server/config.d/remote.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/ssl.xml:/etc/clickhouse-server/config.d/ssl.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/storage.xml:/etc/clickhouse-server/config.d/storage.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/zookeeper.xml:/etc/clickhouse-server/config.d/zookeeper.xml" + depends_on: + zookeeper: + condition: service_healthy + + # dummy service which does nothing, but allows to postpone + # 'docker-compose up -d' till all dependecies will go healthy + all_services_ready: + image: ${IMAGE_DEPENDENCY_PROXY}hello-world + depends_on: + mysql1: + condition: service_healthy + clickhouse1: + condition: service_healthy + clickhouse2: + condition: service_healthy + clickhouse3: + condition: service_healthy + zookeeper: + condition: service_healthy diff --git a/tests/testflows/aes_encryption/aes_encryption_env_arm64/mysql-service.yml b/tests/testflows/aes_encryption/aes_encryption_env_arm64/mysql-service.yml new file mode 100644 index 000000000000..fe957e7be0f4 --- /dev/null +++ b/tests/testflows/aes_encryption/aes_encryption_env_arm64/mysql-service.yml @@ -0,0 +1,19 @@ +version: '2.3' + +services: + mysql: + image: ${IMAGE_DEPENDENCY_PROXY}mariadb:10.3 + restart: always + environment: + MYSQL_DATABASE: 'db' + MYSQL_USER: 'user' + MYSQL_PASSWORD: 'password' + MYSQL_ROOT_PASSWORD: 'password' + expose: + - '3306' + healthcheck: + test: mysql -D db -u user --password=password -e "select 1;" + interval: 3s + timeout: 2s + retries: 40 + start_period: 2s diff --git a/tests/testflows/aes_encryption/aes_encryption_env_arm64/zookeeper-service.yml b/tests/testflows/aes_encryption/aes_encryption_env_arm64/zookeeper-service.yml new file mode 100644 index 000000000000..60c0e4e7de37 --- /dev/null +++ b/tests/testflows/aes_encryption/aes_encryption_env_arm64/zookeeper-service.yml @@ -0,0 +1,18 @@ +version: '2.3' + +services: + zookeeper: + image: ${IMAGE_DEPENDENCY_PROXY}zookeeper:3.6.2 + expose: + - "2181" + environment: + ZOO_TICK_TIME: 500 + ZOO_MY_ID: 1 + healthcheck: + test: echo stat | nc localhost 2181 + interval: 3s + timeout: 2s + retries: 5 + start_period: 2s + security_opt: + - label:disable diff --git a/tests/testflows/aes_encryption/configs/clickhouse/common.xml b/tests/testflows/aes_encryption/configs/clickhouse/common.xml new file mode 100644 index 000000000000..31fa972199f5 --- /dev/null +++ b/tests/testflows/aes_encryption/configs/clickhouse/common.xml @@ -0,0 +1,6 @@ + + Europe/Moscow + 0.0.0.0 + /var/lib/clickhouse/ + /var/lib/clickhouse/tmp/ + diff --git a/tests/testflows/aes_encryption/configs/clickhouse/config.xml b/tests/testflows/aes_encryption/configs/clickhouse/config.xml new file mode 100644 index 000000000000..9854f9f990e1 --- /dev/null +++ b/tests/testflows/aes_encryption/configs/clickhouse/config.xml @@ -0,0 +1,436 @@ + + + + + + trace + /var/log/clickhouse-server/clickhouse-server.log + /var/log/clickhouse-server/clickhouse-server.err.log + 1000M + 10 + + + + 8123 + 9000 + + + + + + + + + /etc/clickhouse-server/server.crt + /etc/clickhouse-server/server.key + + /etc/clickhouse-server/dhparam.pem + none + true + true + sslv2,sslv3 + true + + + + true + true + sslv2,sslv3 + true + + + + RejectCertificateHandler + + + + + + + + + 9009 + + + + + + + + + + + + + + + + + + + + 4096 + 3 + + + 100 + + + + + + 8589934592 + + + 5368709120 + + + + /var/lib/clickhouse/ + + + /var/lib/clickhouse/tmp/ + + + /var/lib/clickhouse/user_files/ + + + /var/lib/clickhouse/access/ + + + users.xml + + + default + + + + + + default + + + + + + + + + false + + + + + + + + localhost + 9000 + + + + + + + localhost + 9000 + + + + + localhost + 9000 + + + + + + + localhost + 9440 + 1 + + + + + + + localhost + 9000 + + + + + localhost + 1 + + + + + + + + + + + + + + + + + 3600 + + + + 3600 + + + 60 + + + + + + + + + + system + query_log
+ + toYYYYMM(event_date) + + 7500 +
+ + + + system + trace_log
+ + toYYYYMM(event_date) + 7500 +
+ + + + system + query_thread_log
+ toYYYYMM(event_date) + 7500 +
+ + + + system + part_log
+ toYYYYMM(event_date) + 7500 +
+ + + + + + + + + + + + + + *_dictionary.xml + + + + + + + + + + /clickhouse/task_queue/ddl + + + + + + + + + + + + + + + + click_cost + any + + 0 + 3600 + + + 86400 + 7200 + + + + max + + 0 + 60 + + + 3600 + 300 + + + 86400 + 3600 + + + + + + /var/lib/clickhouse/format_schemas/ + + + +
diff --git a/tests/testflows/aes_encryption/configs/clickhouse/users.xml b/tests/testflows/aes_encryption/configs/clickhouse/users.xml new file mode 100644 index 000000000000..c7d0ecae6931 --- /dev/null +++ b/tests/testflows/aes_encryption/configs/clickhouse/users.xml @@ -0,0 +1,133 @@ + + + + + + + + 10000000000 + + + 0 + + + random + + + + + 1 + + + + + + + + + + + + + ::/0 + + + + default + + + default + + + 1 + + + + + + + + + + + + + + + + + 3600 + + + 0 + 0 + 0 + 0 + 0 + + + + diff --git a/tests/testflows/aes_encryption/regression.py b/tests/testflows/aes_encryption/regression.py index c12aaca861d5..edf46a9f5663 100755 --- a/tests/testflows/aes_encryption/regression.py +++ b/tests/testflows/aes_encryption/regression.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +from gettext import find import os import sys from testflows.core import * @@ -13,8 +14,13 @@ issue_18250 = "https://github.com/ClickHouse/ClickHouse/issues/18250" issue_18251 = "https://github.com/ClickHouse/ClickHouse/issues/18251" issue_24029 = "https://github.com/ClickHouse/ClickHouse/issues/24029" +issue_39987 = "https://github.com/ClickHouse/ClickHouse/issues/39987" xfails = { + # decrypt + "/aes encryption/decrypt/invalid parameters/null in ciphertext": [ + (Fail, issue_39987) + ], # encrypt "encrypt/invalid key or iv length for mode/mode=\"'aes-???-gcm'\", key_len=??, iv_len=12, aad=True/iv is too short": [ (Fail, "known issue") @@ -81,23 +87,30 @@ RQ_SRS008_AES_Functions("1.0"), RQ_SRS008_AES_Functions_DifferentModes("1.0") ) @XFails(xfails) -def regression( - self, local, clickhouse_binary_path, clickhouse_version=None, stress=None -): +def regression(self, local, clickhouse_binary_path, clickhouse_version, stress=None): """ClickHouse AES encryption functions regression module.""" nodes = { "clickhouse": ("clickhouse1", "clickhouse2", "clickhouse3"), } + self.context.clickhouse_version = clickhouse_version + if stress is not None: self.context.stress = stress - self.context.clickhouse_version = clickhouse_version + + from platform import processor as current_cpu + + folder_name = os.path.basename(current_dir()) + if current_cpu() == "aarch64": + env = f"{folder_name}_env_arm64" + else: + env = f"{folder_name}_env" with Cluster( local, clickhouse_binary_path, nodes=nodes, - docker_compose_project_dir=os.path.join(current_dir(), "aes_encryption_env"), + docker_compose_project_dir=os.path.join(current_dir(), env), ) as cluster: self.context.cluster = cluster diff --git a/tests/testflows/aes_encryption/tests/common.py b/tests/testflows/aes_encryption/tests/common.py index f0a10d344114..419996971a1b 100644 --- a/tests/testflows/aes_encryption/tests/common.py +++ b/tests/testflows/aes_encryption/tests/common.py @@ -4,6 +4,7 @@ from testflows._core.testtype import TestSubType from testflows.core.name import basename, parentname from testflows.core import current +from helpers.common import * modes = [ # mode, key_len, iv_len, aad diff --git a/tests/testflows/aes_encryption/tests/decrypt.py b/tests/testflows/aes_encryption/tests/decrypt.py index 1c7d958737c3..f5da0b12d387 100644 --- a/tests/testflows/aes_encryption/tests/decrypt.py +++ b/tests/testflows/aes_encryption/tests/decrypt.py @@ -94,10 +94,11 @@ def invalid_ciphertext(self): no_checks=True, step=By, ) - with Then("exitcode is not zero"): - assert r.exitcode in [198, 36] - with And("exception is present in the output"): - assert "DB::Exception:" in r.output + if check_clickhouse_version("<21.12")(self): + with Then("exitcode is not zero"): + assert r.exitcode in [198, 36] + with And("exception is present in the output"): + assert "DB::Exception:" in r.output @TestScenario @@ -303,6 +304,15 @@ def invalid_parameters(self): message="DB::Exception: Invalid mode: AES-128-ECB", ) + with Example("null in ciphertext"): + decrypt( + ciphertext="[6555, 555, 555, 9223372036854775807, 1048576, NULL]", + key="'0123456789123456'", + mode="'aes-128-ecb'", + exitcode=198, + message="DB::Exception: Failed to decrypt", + ) + @TestOutline(Scenario) @Requirements( diff --git a/tests/testflows/aes_encryption/tests/decrypt_mysql.py b/tests/testflows/aes_encryption/tests/decrypt_mysql.py index 1a8f53464b76..d307d87b90ac 100644 --- a/tests/testflows/aes_encryption/tests/decrypt_mysql.py +++ b/tests/testflows/aes_encryption/tests/decrypt_mysql.py @@ -90,10 +90,12 @@ def invalid_ciphertext(self): no_checks=True, step=By, ) - with Then("exitcode is not zero"): - assert r.exitcode in [198, 36] - with And("exception is present in the output"): - assert "DB::Exception:" in r.output + + if check_clickhouse_version("<21.12")(self): + with Then("exitcode is not zero"): + assert r.exitcode in [198, 36] + with And("exception is present in the output"): + assert "DB::Exception:" in r.output @TestOutline(Scenario) diff --git a/tests/testflows/ci-runner.py b/tests/testflows/ci-runner.py index 4583f37ff36d..b69bf94d1769 100755 --- a/tests/testflows/ci-runner.py +++ b/tests/testflows/ci-runner.py @@ -215,8 +215,8 @@ def run_impl(self, repo_path, build_path): test_log_path = os.path.join(repo_path, "tests/testflows", "test.log") cmd = ( - f"bash -c \"set -o pipefail && cd {repo_path}/tests/testflows && timeout -s 9 10h " - f"./runner {self._get_runner_opts()} {image_cmd} | tee {log_path}\"" + f'bash -c "set -o pipefail && cd {repo_path}/tests/testflows && timeout -s 9 10h ' + f'./runner {self._get_runner_opts()} {image_cmd} | tee {log_path}"' ) logging.info("Executing cmd: %s", cmd) diff --git a/tests/testflows/datetime64_extended_range/configs/clickhouse/common.xml b/tests/testflows/datetime64_extended_range/configs/clickhouse/common.xml new file mode 100644 index 000000000000..31fa972199f5 --- /dev/null +++ b/tests/testflows/datetime64_extended_range/configs/clickhouse/common.xml @@ -0,0 +1,6 @@ + + Europe/Moscow + 0.0.0.0 + /var/lib/clickhouse/ + /var/lib/clickhouse/tmp/ + diff --git a/tests/testflows/datetime64_extended_range/configs/clickhouse/config.xml b/tests/testflows/datetime64_extended_range/configs/clickhouse/config.xml new file mode 100644 index 000000000000..a9a37875273b --- /dev/null +++ b/tests/testflows/datetime64_extended_range/configs/clickhouse/config.xml @@ -0,0 +1,436 @@ + + + + + + trace + /var/log/clickhouse-server/clickhouse-server.log + /var/log/clickhouse-server/clickhouse-server.err.log + 1000M + 10 + + + + 8123 + 9000 + + + + + + + + + /etc/clickhouse-server/server.crt + /etc/clickhouse-server/server.key + + /etc/clickhouse-server/dhparam.pem + none + true + true + sslv2,sslv3 + true + + + + true + true + sslv2,sslv3 + true + + + + RejectCertificateHandler + + + + + + + + + 9009 + + + + + + + + + + + + + + + + + + + + 4096 + 3 + + + 100 + + + + + + 8589934592 + + + 5368709120 + + + + /var/lib/clickhouse/ + + + /var/lib/clickhouse/tmp/ + + + /var/lib/clickhouse/user_files/ + + + /var/lib/clickhouse/access/ + + + users.xml + + + default + + + + + + default + + + + + + + + + false + + + + + + + + localhost + 9000 + + + + + + + localhost + 9000 + + + + + localhost + 9000 + + + + + + + localhost + 9440 + 1 + + + + + + + localhost + 9000 + + + + + localhost + 1 + + + + + + + + + + + + + + + + + 3600 + + + + 3600 + + + 60 + + + + + + + + + + system + query_log
+ + toYYYYMM(event_date) + + 7500 +
+ + + + system + trace_log
+ + toYYYYMM(event_date) + 7500 +
+ + + + system + query_thread_log
+ toYYYYMM(event_date) + 7500 +
+ + + + system + part_log
+ toYYYYMM(event_date) + 7500 +
+ + + + + + + + + + + + + + *_dictionary.xml + + + + + + + + + + /clickhouse/task_queue/ddl + + + + + + + + + + + + + + + + click_cost + any + + 0 + 3600 + + + 86400 + 60 + + + + max + + 0 + 60 + + + 3600 + 300 + + + 86400 + 3600 + + + + + + /var/lib/clickhouse/format_schemas/ + + + +
diff --git a/tests/testflows/datetime64_extended_range/configs/clickhouse/users.xml b/tests/testflows/datetime64_extended_range/configs/clickhouse/users.xml new file mode 100644 index 000000000000..c7d0ecae6931 --- /dev/null +++ b/tests/testflows/datetime64_extended_range/configs/clickhouse/users.xml @@ -0,0 +1,133 @@ + + + + + + + + 10000000000 + + + 0 + + + random + + + + + 1 + + + + + + + + + + + + + ::/0 + + + + default + + + default + + + 1 + + + + + + + + + + + + + + + + + 3600 + + + 0 + 0 + 0 + 0 + 0 + + + + diff --git a/tests/testflows/datetime64_extended_range/datetime64_extended_range_env/clickhouse-service.yml b/tests/testflows/datetime64_extended_range/datetime64_extended_range_env/clickhouse-service.yml index 74a56b63aabc..532ce9185735 100644 --- a/tests/testflows/datetime64_extended_range/datetime64_extended_range_env/clickhouse-service.yml +++ b/tests/testflows/datetime64_extended_range/datetime64_extended_range_env/clickhouse-service.yml @@ -2,25 +2,31 @@ version: '2.3' services: clickhouse: - image: altinityinfra/integration-test + image: ${IMAGE_DEPENDENCY_PROXY}clickhouse/clickhouse-integration-test:28741 + init: true expose: - "9000" - "9009" - "8123" volumes: - - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d:/etc/clickhouse-server/config.d" - - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/users.d/:/etc/clickhouse-server/users.d" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/logs.xml:/etc/clickhouse-server/config.d/logs.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/ports.xml:/etc/clickhouse-server/config.d/ports.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/remote.xml:/etc/clickhouse-server/config.d/remote.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/ssl.xml:/etc/clickhouse-server/config.d/ssl.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/storage.xml:/etc/clickhouse-server/config.d/storage.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/zookeeper.xml:/etc/clickhouse-server/config.d/zookeeper.xml" - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/ssl:/etc/clickhouse-server/ssl" - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.xml:/etc/clickhouse-server/config.xml" - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/users.xml:/etc/clickhouse-server/users.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/common.xml:/etc/clickhouse-server/common.xml" - "${CLICKHOUSE_TESTS_SERVER_BIN_PATH:-/usr/bin/clickhouse}:/usr/bin/clickhouse" - "${CLICKHOUSE_TESTS_ODBC_BRIDGE_BIN_PATH:-/usr/bin/clickhouse-odbc-bridge}:/usr/bin/clickhouse-odbc-bridge" - entrypoint: bash -c "clickhouse server --config-file=/etc/clickhouse-server/config.xml --log-file=/var/log/clickhouse-server/clickhouse-server.log --errorlog-file=/var/log/clickhouse-server/clickhouse-server.err.log" + entrypoint: bash -c "tail -f /dev/null" healthcheck: - test: clickhouse client --query='select 1' + test: echo 1 interval: 10s timeout: 10s - retries: 10 + retries: 3 start_period: 300s cap_add: - SYS_PTRACE diff --git a/tests/testflows/datetime64_extended_range/datetime64_extended_range_env/docker-compose.yml b/tests/testflows/datetime64_extended_range/datetime64_extended_range_env/docker-compose.yml index 665e355d51e5..83f4a2afab1b 100644 --- a/tests/testflows/datetime64_extended_range/datetime64_extended_range_env/docker-compose.yml +++ b/tests/testflows/datetime64_extended_range/datetime64_extended_range_env/docker-compose.yml @@ -14,8 +14,13 @@ services: volumes: - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse1/database/:/var/lib/clickhouse/" - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse1/logs/:/var/log/clickhouse-server/" - - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse1/config.d:/etc/clickhouse-server/config.d" - - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse1/users.d:/etc/clickhouse-server/users.d" + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse1/files/:/var/lib/ch-files/" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/logs.xml:/etc/clickhouse-server/config.d/logs.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/ports.xml:/etc/clickhouse-server/config.d/ports.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/remote.xml:/etc/clickhouse-server/config.d/remote.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/ssl.xml:/etc/clickhouse-server/config.d/ssl.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/storage.xml:/etc/clickhouse-server/config.d/storage.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/zookeeper.xml:/etc/clickhouse-server/config.d/zookeeper.xml" depends_on: zookeeper: condition: service_healthy @@ -28,8 +33,12 @@ services: volumes: - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse2/database/:/var/lib/clickhouse/" - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse2/logs/:/var/log/clickhouse-server/" - - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse2/config.d:/etc/clickhouse-server/config.d" - - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse2/users.d:/etc/clickhouse-server/users.d" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/logs.xml:/etc/clickhouse-server/config.d/logs.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/ports.xml:/etc/clickhouse-server/config.d/ports.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/remote.xml:/etc/clickhouse-server/config.d/remote.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/ssl.xml:/etc/clickhouse-server/config.d/ssl.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/storage.xml:/etc/clickhouse-server/config.d/storage.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/zookeeper.xml:/etc/clickhouse-server/config.d/zookeeper.xml" depends_on: zookeeper: condition: service_healthy @@ -42,14 +51,18 @@ services: volumes: - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse3/database/:/var/lib/clickhouse/" - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse3/logs/:/var/log/clickhouse-server/" - - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse3/config.d:/etc/clickhouse-server/config.d" - - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse3/users.d:/etc/clickhouse-server/users.d" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/logs.xml:/etc/clickhouse-server/config.d/logs.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/ports.xml:/etc/clickhouse-server/config.d/ports.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/remote.xml:/etc/clickhouse-server/config.d/remote.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/ssl.xml:/etc/clickhouse-server/config.d/ssl.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/storage.xml:/etc/clickhouse-server/config.d/storage.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/zookeeper.xml:/etc/clickhouse-server/config.d/zookeeper.xml" depends_on: zookeeper: condition: service_healthy all_services_ready: - image: hello-world + image: ${IMAGE_DEPENDENCY_PROXY}hello-world depends_on: clickhouse1: condition: service_healthy diff --git a/tests/testflows/datetime64_extended_range/datetime64_extended_range_env/zookeeper-service.yml b/tests/testflows/datetime64_extended_range/datetime64_extended_range_env/zookeeper-service.yml index f27405b97a2c..60c0e4e7de37 100644 --- a/tests/testflows/datetime64_extended_range/datetime64_extended_range_env/zookeeper-service.yml +++ b/tests/testflows/datetime64_extended_range/datetime64_extended_range_env/zookeeper-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: zookeeper: - image: zookeeper:3.6.2 + image: ${IMAGE_DEPENDENCY_PROXY}zookeeper:3.6.2 expose: - "2181" environment: diff --git a/tests/testflows/datetime64_extended_range/datetime64_extended_range_env_arm64/clickhouse-service.yml b/tests/testflows/datetime64_extended_range/datetime64_extended_range_env_arm64/clickhouse-service.yml new file mode 100644 index 000000000000..a73d31421c87 --- /dev/null +++ b/tests/testflows/datetime64_extended_range/datetime64_extended_range_env_arm64/clickhouse-service.yml @@ -0,0 +1,29 @@ +version: '2.3' + +services: + clickhouse: + image: registry.gitlab.com/altinity-public/container-images/test/clickhouse-integration-test:21.12 + privileged: true + expose: + - "9000" + - "9009" + - "8123" + volumes: + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d:/etc/clickhouse-server/config.d" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/users.d/:/etc/clickhouse-server/users.d" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/ssl:/etc/clickhouse-server/ssl" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.xml:/etc/clickhouse-server/config.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/users.xml:/etc/clickhouse-server/users.xml" + - "${CLICKHOUSE_TESTS_SERVER_BIN_PATH:-/usr/bin/clickhouse}:/usr/bin/clickhouse" + - "${CLICKHOUSE_TESTS_ODBC_BRIDGE_BIN_PATH:-/usr/bin/clickhouse-odbc-bridge}:/usr/bin/clickhouse-odbc-bridge" + entrypoint: bash -c "clickhouse server --config-file=/etc/clickhouse-server/config.xml --log-file=/var/log/clickhouse-server/clickhouse-server.log --errorlog-file=/var/log/clickhouse-server/clickhouse-server.err.log" + healthcheck: + test: clickhouse client --query='select 1' + interval: 10s + timeout: 10s + retries: 10 + start_period: 300s + cap_add: + - SYS_PTRACE + security_opt: + - label:disable diff --git a/tests/testflows/datetime64_extended_range/datetime64_extended_range_env_arm64/docker-compose.yml b/tests/testflows/datetime64_extended_range/datetime64_extended_range_env_arm64/docker-compose.yml new file mode 100644 index 000000000000..03787c14f449 --- /dev/null +++ b/tests/testflows/datetime64_extended_range/datetime64_extended_range_env_arm64/docker-compose.yml @@ -0,0 +1,62 @@ +version: '2.3' + +services: + zookeeper: + extends: + file: zookeeper-service.yml + service: zookeeper + + clickhouse1: + extends: + file: clickhouse-service.yml + service: clickhouse + hostname: clickhouse1 + volumes: + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse1/database/:/var/lib/clickhouse/" + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse1/logs/:/var/log/clickhouse-server/" + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse1/files/:/var/lib/ch-files/" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse1/config.d:/etc/clickhouse-server/config.d" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse1/users.d:/etc/clickhouse-server/users.d" + depends_on: + zookeeper: + condition: service_healthy + + clickhouse2: + extends: + file: clickhouse-service.yml + service: clickhouse + hostname: clickhouse2 + volumes: + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse2/database/:/var/lib/clickhouse/" + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse2/logs/:/var/log/clickhouse-server/" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse2/config.d:/etc/clickhouse-server/config.d" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse2/users.d:/etc/clickhouse-server/users.d" + depends_on: + zookeeper: + condition: service_healthy + + clickhouse3: + extends: + file: clickhouse-service.yml + service: clickhouse + hostname: clickhouse3 + volumes: + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse3/database/:/var/lib/clickhouse/" + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse3/logs/:/var/log/clickhouse-server/" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse3/config.d:/etc/clickhouse-server/config.d" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse3/users.d:/etc/clickhouse-server/users.d" + depends_on: + zookeeper: + condition: service_healthy + + all_services_ready: + image: ${IMAGE_DEPENDENCY_PROXY}hello-world + depends_on: + clickhouse1: + condition: service_healthy + clickhouse2: + condition: service_healthy + clickhouse3: + condition: service_healthy + zookeeper: + condition: service_healthy diff --git a/tests/testflows/datetime64_extended_range/datetime64_extended_range_env_arm64/zookeeper-service.yml b/tests/testflows/datetime64_extended_range/datetime64_extended_range_env_arm64/zookeeper-service.yml new file mode 100644 index 000000000000..60c0e4e7de37 --- /dev/null +++ b/tests/testflows/datetime64_extended_range/datetime64_extended_range_env_arm64/zookeeper-service.yml @@ -0,0 +1,18 @@ +version: '2.3' + +services: + zookeeper: + image: ${IMAGE_DEPENDENCY_PROXY}zookeeper:3.6.2 + expose: + - "2181" + environment: + ZOO_TICK_TIME: 500 + ZOO_MY_ID: 1 + healthcheck: + test: echo stat | nc localhost 2181 + interval: 3s + timeout: 2s + retries: 5 + start_period: 2s + security_opt: + - label:disable diff --git a/tests/testflows/datetime64_extended_range/regression.py b/tests/testflows/datetime64_extended_range/regression.py index 69c4021df4cb..b1f1e22e2c16 100755 --- a/tests/testflows/datetime64_extended_range/regression.py +++ b/tests/testflows/datetime64_extended_range/regression.py @@ -9,6 +9,7 @@ from helpers.argparser import argparser from datetime64_extended_range.requirements import * from datetime64_extended_range.common import * +from datetime64_extended_range.requirements import RQ_SRS_010_DateTime64_ExtendedRange # cross-outs # https://github.com/ClickHouse/ClickHouse/issues/16581#issuecomment-804360350: 128 and 256-bit types are not supported for now @@ -23,6 +24,7 @@ # https://github.com/ClickHouse/ClickHouse/issues/22930 : toWeek() # https://github.com/ClickHouse/ClickHouse/issues/22948 : toYearWeek() # https://github.com/ClickHouse/ClickHouse/issues/22959 : toUnixTimestamp64*() wrong fractal seconds treatment +# https://github.com/ClickHouse/ClickHouse/issues/34831 : DateTime64 to Arrow format mistreats negative timestamps # For `reference times` test it is unclear how to evaluate correctness - majority of test cases are correct, and ONLY # Juba and Monrovia timezones are damaged - probably, due to wrong DST shifts lookup tables @@ -85,38 +87,52 @@ "type conversion/from unix timestamp64 */:": [ (Fail, "https://github.com/ClickHouse/ClickHouse/issues/22959") ], - "type conversion/to int 8 16 32 64 128 256/:": [ - ( - Fail, - "https://github.com/ClickHouse/ClickHouse/issues/16581#issuecomment-804360350", - ) - ], "reference times/:": [(Fail, "check procedure unclear")], - # need to investigate - "type conversion/to datetime/cast=True": [(Fail, "need to investigate")], - "date time funcs/today": [(Fail, "need to investigate")], + "generic/transform/:": [ + (Fail, "https://github.com/ClickHouse/ClickHouse/issues/32387") + ], + "format conversion/arrow format/:": [ + (Fail, "https://github.com/ClickHouse/ClickHouse/issues/34831") + ], + "type conversion/to datetime/:": [(Fail, "needs to be investigated")], +} + +ffails = { + "type conversion/to date32/:": ( + XFail, + "toDate32 not implemented before 21.10", + (lambda test: check_clickhouse_version("<21.10")(test)), + ) } @TestModule @Name("datetime64 extended range") @ArgumentParser(argparser) -@Specifications(SRS_010_ClickHouse_DateTime64_Extended_Range) +@Specifications(QA_SRS010_ClickHouse_DateTime64_Extended_Range) @Requirements( RQ_SRS_010_DateTime64_ExtendedRange("1.0"), ) @XFails(xfails) -def regression( - self, local, clickhouse_binary_path, clickhouse_version=None, stress=False -): +@FFails(ffails) +def regression(self, local, clickhouse_binary_path, clickhouse_version, stress=False): """ClickHouse DateTime64 Extended Range regression module.""" nodes = { "clickhouse": ("clickhouse1", "clickhouse2", "clickhouse3"), } + self.context.clickhouse_version = clickhouse_version + if stress is not None: self.context.stress = stress - self.context.clickhouse_version = clickhouse_version + + from platform import processor as current_cpu + + folder_name = os.path.basename(current_dir()) + if current_cpu() == "aarch64": + env = f"{folder_name}_env_arm64" + else: + env = f"{folder_name}_env" with Cluster( local, @@ -166,6 +182,14 @@ def regression( parallel=True, executor=pool, ) + Scenario( + run=load( + "datetime64_extended_range.tests.format_conversion", + "format_conversion", + ), + parallel=True, + executor=pool, + ) finally: join() diff --git a/tests/testflows/datetime64_extended_range/requirements/requirements.md b/tests/testflows/datetime64_extended_range/requirements/requirements.md index 659057ebfdc9..1430cb2ddfb5 100644 --- a/tests/testflows/datetime64_extended_range/requirements/requirements.md +++ b/tests/testflows/datetime64_extended_range/requirements/requirements.md @@ -1,6 +1,24 @@ -# SRS-010 ClickHouse DateTime64 Extended Range +# QA-SRS010 ClickHouse DateTime64 Extended Range # Software Requirements Specification +(c) 2020 Altinity LTD. All Rights Reserved. + +**Document status:** Confidential + +**Author:** vzakaznikov, zvonand + +**Date:** August 10, 2020 + +## Approval + +**Status:** - + +**Version:** - + +**Approved by:** - + +**Date:** - + ## Table of Contents * 1 [Revision History](#revision-history) @@ -22,98 +40,101 @@ * 4.1.0.9 [RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime](#rqsrs-010datetime64extendedrangenonexistenttime) * 4.1.0.10 [RQ.SRS-010.DateTime64.ExtendedRange.Comparison](#rqsrs-010datetime64extendedrangecomparison) * 4.1.0.11 [RQ.SRS-010.DateTime64.ExtendedRange.SpecificTimestamps](#rqsrs-010datetime64extendedrangespecifictimestamps) + * 4.1.0.12 [RQ.SRS-010.DateTime64.ExtendedRange.Transform](#rqsrs-010datetime64extendedrangetransform) * 4.2 [Specific](#specific) - * 4.2.0.1 [RQ.SRS-010.DateTime64.ExtendedRange.Start](#rqsrs-010datetime64extendedrangestart) - * 4.2.0.2 [RQ.SRS-010.DateTime64.ExtendedRange.End](#rqsrs-010datetime64extendedrangeend) - * 4.2.0.3 [Non-Existent Time](#non-existent-time) - * 4.2.0.3.1 [RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.InvalidDate](#rqsrs-010datetime64extendedrangenonexistenttimeinvaliddate) - * 4.2.0.3.2 [RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.InvalidTime](#rqsrs-010datetime64extendedrangenonexistenttimeinvalidtime) - * 4.2.0.3.3 [RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.TimeZoneSwitch](#rqsrs-010datetime64extendedrangenonexistenttimetimezoneswitch) - * 4.2.0.3.4 [RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.DaylightSavingTime](#rqsrs-010datetime64extendedrangenonexistenttimedaylightsavingtime) - * 4.2.0.3.5 [RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.DaylightSavingTime.Disappeared](#rqsrs-010datetime64extendedrangenonexistenttimedaylightsavingtimedisappeared) - * 4.2.0.3.6 [RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.LeapSeconds](#rqsrs-010datetime64extendedrangenonexistenttimeleapseconds) - * 4.2.0.4 [Dates And Times Functions](#dates-and-times-functions) - * 4.2.0.4.1 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toTimeZone](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstotimezone) - * 4.2.0.4.2 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toYear](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoyear) - * 4.2.0.4.3 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toQuarter](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoquarter) - * 4.2.0.4.4 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toMonth](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstomonth) - * 4.2.0.4.5 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toDayOfYear](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstodayofyear) - * 4.2.0.4.6 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toDayOfMonth](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstodayofmonth) - * 4.2.0.4.7 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toDayOfWeek](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstodayofweek) - * 4.2.0.4.8 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toHour](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstohour) - * 4.2.0.4.9 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toMinute](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstominute) - * 4.2.0.4.10 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toSecond](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstosecond) - * 4.2.0.4.11 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toUnixTimestamp](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstounixtimestamp) - * 4.2.0.4.12 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfYear](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofyear) - * 4.2.0.4.13 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfISOYear](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofisoyear) - * 4.2.0.4.14 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfQuarter](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofquarter) - * 4.2.0.4.15 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfMonth](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofmonth) - * 4.2.0.4.16 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toMonday](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstomonday) - * 4.2.0.4.17 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfWeek](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofweek) - * 4.2.0.4.18 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfDay](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofday) - * 4.2.0.4.19 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfHour](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofhour) - * 4.2.0.4.20 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfMinute](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofminute) - * 4.2.0.4.21 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfSecond](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofsecond) - * 4.2.0.4.22 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfFiveMinute](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartoffiveminute) - * 4.2.0.4.23 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfTenMinutes](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartoftenminutes) - * 4.2.0.4.24 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfFifteenMinutes](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartoffifteenminutes) - * 4.2.0.4.25 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfInterval](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofinterval) - * 4.2.0.4.26 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toTime](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstotime) - * 4.2.0.4.27 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeYearNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativeyearnum) - * 4.2.0.4.28 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeQuarterNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativequarternum) - * 4.2.0.4.29 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeMonthNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativemonthnum) - * 4.2.0.4.30 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeWeekNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativeweeknum) - * 4.2.0.4.31 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeDayNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativedaynum) - * 4.2.0.4.32 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeHourNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativehournum) - * 4.2.0.4.33 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeMinuteNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativeminutenum) - * 4.2.0.4.34 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeSecondNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativesecondnum) - * 4.2.0.4.35 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toISOYear](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoisoyear) - * 4.2.0.4.36 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toISOWeek](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoisoweek) - * 4.2.0.4.37 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toWeek](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoweek) - * 4.2.0.4.38 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toYearWeek](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoyearweek) - * 4.2.0.4.39 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.now](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsnow) - * 4.2.0.4.40 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.today](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoday) - * 4.2.0.4.41 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.yesterday](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsyesterday) - * 4.2.0.4.42 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.timeSlot](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstimeslot) - * 4.2.0.4.43 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toYYYYMM](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoyyyymm) - * 4.2.0.4.44 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toYYYYMMDD](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoyyyymmdd) - * 4.2.0.4.45 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toYYYYMMDDhhmmss](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoyyyymmddhhmmss) - * 4.2.0.4.46 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addYears](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsaddyears) - * 4.2.0.4.47 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addMonths](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsaddmonths) - * 4.2.0.4.48 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addWeeks](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsaddweeks) - * 4.2.0.4.49 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addDays](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsadddays) - * 4.2.0.4.50 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addHours](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsaddhours) - * 4.2.0.4.51 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addMinutes](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsaddminutes) - * 4.2.0.4.52 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addSeconds](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsaddseconds) - * 4.2.0.4.53 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addQuarters](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsaddquarters) - * 4.2.0.4.54 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractYears](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtractyears) - * 4.2.0.4.55 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractMonths](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtractmonths) - * 4.2.0.4.56 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractWeeks](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtractweeks) - * 4.2.0.4.57 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractDays](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtractdays) - * 4.2.0.4.58 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractHours](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtracthours) - * 4.2.0.4.59 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractMinutes](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtractminutes) - * 4.2.0.4.60 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractSeconds](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtractseconds) - * 4.2.0.4.61 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractQuarters](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtractquarters) - * 4.2.0.4.62 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.dateDiff](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsdatediff) - * 4.2.0.4.63 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.timeSlots](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstimeslots) - * 4.2.0.4.64 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.formatDateTime](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsformatdatetime) + * 4.2.0.1 [RQ.SRS-010.DateTime64.ExtendedRange.ArrowFormat](#rqsrs-010datetime64extendedrangearrowformat) + * 4.2.0.2 [RQ.SRS-010.DateTime64.ExtendedRange.Start](#rqsrs-010datetime64extendedrangestart) + * 4.2.0.3 [RQ.SRS-010.DateTime64.ExtendedRange.End](#rqsrs-010datetime64extendedrangeend) + * 4.2.0.4 [Non-Existent Time](#non-existent-time) + * 4.2.0.4.1 [RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.InvalidDate](#rqsrs-010datetime64extendedrangenonexistenttimeinvaliddate) + * 4.2.0.4.2 [RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.InvalidTime](#rqsrs-010datetime64extendedrangenonexistenttimeinvalidtime) + * 4.2.0.4.3 [RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.TimeZoneSwitch](#rqsrs-010datetime64extendedrangenonexistenttimetimezoneswitch) + * 4.2.0.4.4 [RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.DaylightSavingTime](#rqsrs-010datetime64extendedrangenonexistenttimedaylightsavingtime) + * 4.2.0.4.5 [RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.DaylightSavingTime.Disappeared](#rqsrs-010datetime64extendedrangenonexistenttimedaylightsavingtimedisappeared) + * 4.2.0.4.6 [RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.LeapSeconds](#rqsrs-010datetime64extendedrangenonexistenttimeleapseconds) + * 4.2.0.5 [Dates And Times Functions](#dates-and-times-functions) + * 4.2.0.5.1 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toTimeZone](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstotimezone) + * 4.2.0.5.2 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toYear](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoyear) + * 4.2.0.5.3 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toQuarter](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoquarter) + * 4.2.0.5.4 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toMonth](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstomonth) + * 4.2.0.5.5 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toDayOfYear](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstodayofyear) + * 4.2.0.5.6 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toDayOfMonth](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstodayofmonth) + * 4.2.0.5.7 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toDayOfWeek](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstodayofweek) + * 4.2.0.5.8 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toHour](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstohour) + * 4.2.0.5.9 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toMinute](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstominute) + * 4.2.0.5.10 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toSecond](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstosecond) + * 4.2.0.5.11 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toUnixTimestamp](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstounixtimestamp) + * 4.2.0.5.12 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfYear](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofyear) + * 4.2.0.5.13 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfISOYear](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofisoyear) + * 4.2.0.5.14 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfQuarter](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofquarter) + * 4.2.0.5.15 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfMonth](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofmonth) + * 4.2.0.5.16 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toMonday](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstomonday) + * 4.2.0.5.17 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfWeek](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofweek) + * 4.2.0.5.18 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfDay](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofday) + * 4.2.0.5.19 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfHour](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofhour) + * 4.2.0.5.20 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfMinute](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofminute) + * 4.2.0.5.21 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfSecond](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofsecond) + * 4.2.0.5.22 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfFiveMinute](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartoffiveminute) + * 4.2.0.5.23 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfTenMinutes](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartoftenminutes) + * 4.2.0.5.24 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfFifteenMinutes](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartoffifteenminutes) + * 4.2.0.5.25 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfInterval](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofinterval) + * 4.2.0.5.26 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toTime](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstotime) + * 4.2.0.5.27 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeYearNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativeyearnum) + * 4.2.0.5.28 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeQuarterNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativequarternum) + * 4.2.0.5.29 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeMonthNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativemonthnum) + * 4.2.0.5.30 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeWeekNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativeweeknum) + * 4.2.0.5.31 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeDayNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativedaynum) + * 4.2.0.5.32 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeHourNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativehournum) + * 4.2.0.5.33 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeMinuteNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativeminutenum) + * 4.2.0.5.34 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeSecondNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativesecondnum) + * 4.2.0.5.35 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toISOYear](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoisoyear) + * 4.2.0.5.36 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toISOWeek](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoisoweek) + * 4.2.0.5.37 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toWeek](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoweek) + * 4.2.0.5.38 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toYearWeek](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoyearweek) + * 4.2.0.5.39 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.now](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsnow) + * 4.2.0.5.40 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.today](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoday) + * 4.2.0.5.41 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.yesterday](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsyesterday) + * 4.2.0.5.42 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.timeSlot](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstimeslot) + * 4.2.0.5.43 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toYYYYMM](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoyyyymm) + * 4.2.0.5.44 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toYYYYMMDD](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoyyyymmdd) + * 4.2.0.5.45 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toYYYYMMDDhhmmss](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoyyyymmddhhmmss) + * 4.2.0.5.46 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addYears](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsaddyears) + * 4.2.0.5.47 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addMonths](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsaddmonths) + * 4.2.0.5.48 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addWeeks](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsaddweeks) + * 4.2.0.5.49 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addDays](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsadddays) + * 4.2.0.5.50 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addHours](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsaddhours) + * 4.2.0.5.51 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addMinutes](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsaddminutes) + * 4.2.0.5.52 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addSeconds](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsaddseconds) + * 4.2.0.5.53 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addQuarters](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsaddquarters) + * 4.2.0.5.54 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractYears](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtractyears) + * 4.2.0.5.55 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractMonths](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtractmonths) + * 4.2.0.5.56 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractWeeks](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtractweeks) + * 4.2.0.5.57 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractDays](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtractdays) + * 4.2.0.5.58 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractHours](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtracthours) + * 4.2.0.5.59 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractMinutes](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtractminutes) + * 4.2.0.5.60 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractSeconds](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtractseconds) + * 4.2.0.5.61 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractQuarters](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtractquarters) + * 4.2.0.5.62 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.dateDiff](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsdatediff) + * 4.2.0.5.63 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.timeSlots](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstimeslots) + * 4.2.0.5.64 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.formatDateTime](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsformatdatetime) * 4.2.1 [Type Conversion Functions](#type-conversion-functions) - * 4.2.1.4.1 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toInt(8|16|32|64|128|256)](#rqsrs-010datetime64extendedrangetypeconversionfunctionstoint8163264128256) - * 4.2.1.4.2 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toUInt(8|16|32|64|256)](#rqsrs-010datetime64extendedrangetypeconversionfunctionstouint8163264256) - * 4.2.1.4.3 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toFloat(32|64)](#rqsrs-010datetime64extendedrangetypeconversionfunctionstofloat3264) - * 4.2.1.4.4 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDate](#rqsrs-010datetime64extendedrangetypeconversionfunctionstodate) - * 4.2.1.4.5 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDateTime](#rqsrs-010datetime64extendedrangetypeconversionfunctionstodatetime) - * 4.2.1.4.6 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDateTime64](#rqsrs-010datetime64extendedrangetypeconversionfunctionstodatetime64) - * 4.2.1.4.7 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDateTime64.FromString.MissingTime](#rqsrs-010datetime64extendedrangetypeconversionfunctionstodatetime64fromstringmissingtime) - * 4.2.1.4.8 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDecimal(32|64|128|256)](#rqsrs-010datetime64extendedrangetypeconversionfunctionstodecimal3264128256) - * 4.2.1.4.9 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toString](#rqsrs-010datetime64extendedrangetypeconversionfunctionstostring) - * 4.2.1.4.10 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.CAST(x,T)](#rqsrs-010datetime64extendedrangetypeconversionfunctionscastxt) - * 4.2.1.4.11 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toUnixTimestamp64Milli](#rqsrs-010datetime64extendedrangetypeconversionfunctionstounixtimestamp64milli) - * 4.2.1.4.12 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toUnixTimestamp64Micro](#rqsrs-010datetime64extendedrangetypeconversionfunctionstounixtimestamp64micro) - * 4.2.1.4.13 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toUnixTimestamp64Nano](#rqsrs-010datetime64extendedrangetypeconversionfunctionstounixtimestamp64nano) - * 4.2.1.4.14 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.fromUnixTimestamp64Milli](#rqsrs-010datetime64extendedrangetypeconversionfunctionsfromunixtimestamp64milli) - * 4.2.1.4.15 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.fromUnixTimestamp64Micro](#rqsrs-010datetime64extendedrangetypeconversionfunctionsfromunixtimestamp64micro) - * 4.2.1.4.16 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.fromUnixTimestamp64Nano](#rqsrs-010datetime64extendedrangetypeconversionfunctionsfromunixtimestamp64nano) + * 4.2.1.5.1 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toInt(8|16|32|64|128|256)](#rqsrs-010datetime64extendedrangetypeconversionfunctionstoint8163264128256) + * 4.2.1.5.2 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toUInt(8|16|32|64|256)](#rqsrs-010datetime64extendedrangetypeconversionfunctionstouint8163264256) + * 4.2.1.5.3 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toFloat(32|64)](#rqsrs-010datetime64extendedrangetypeconversionfunctionstofloat3264) + * 4.2.1.5.4 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDate](#rqsrs-010datetime64extendedrangetypeconversionfunctionstodate) + * 4.2.1.5.5 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDate32](#rqsrs-010datetime64extendedrangetypeconversionfunctionstodate32) + * 4.2.1.5.6 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDateTime](#rqsrs-010datetime64extendedrangetypeconversionfunctionstodatetime) + * 4.2.1.5.7 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDateTime64](#rqsrs-010datetime64extendedrangetypeconversionfunctionstodatetime64) + * 4.2.1.5.8 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDateTime64.FromString.MissingTime](#rqsrs-010datetime64extendedrangetypeconversionfunctionstodatetime64fromstringmissingtime) + * 4.2.1.5.9 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDecimal(32|64|128|256)](#rqsrs-010datetime64extendedrangetypeconversionfunctionstodecimal3264128256) + * 4.2.1.5.10 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toString](#rqsrs-010datetime64extendedrangetypeconversionfunctionstostring) + * 4.2.1.5.11 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.CAST(x,T)](#rqsrs-010datetime64extendedrangetypeconversionfunctionscastxt) + * 4.2.1.5.12 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toUnixTimestamp64Milli](#rqsrs-010datetime64extendedrangetypeconversionfunctionstounixtimestamp64milli) + * 4.2.1.5.13 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toUnixTimestamp64Micro](#rqsrs-010datetime64extendedrangetypeconversionfunctionstounixtimestamp64micro) + * 4.2.1.5.14 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toUnixTimestamp64Nano](#rqsrs-010datetime64extendedrangetypeconversionfunctionstounixtimestamp64nano) + * 4.2.1.5.15 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.fromUnixTimestamp64Milli](#rqsrs-010datetime64extendedrangetypeconversionfunctionsfromunixtimestamp64milli) + * 4.2.1.5.16 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.fromUnixTimestamp64Micro](#rqsrs-010datetime64extendedrangetypeconversionfunctionsfromunixtimestamp64micro) + * 4.2.1.5.17 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.fromUnixTimestamp64Nano](#rqsrs-010datetime64extendedrangetypeconversionfunctionsfromunixtimestamp64nano) * 5 [References](#references) ## Revision History @@ -210,9 +231,20 @@ version: 1.0 [9961200,73476000,325666800,354675600,370400400,386125200,388566010,401850000,417574811,496803600,528253200,624423614,636516015,671011200,717555600,752047218,859683600,922582800,1018173600,1035705600,1143334800,1162105223,1174784400,1194156000,1206838823,1224982823,1236495624,1319936400,1319936424,1425798025,1459040400,1509872400,2090451627,2140668000] ``` +##### RQ.SRS-010.DateTime64.ExtendedRange.Transform +version: 1.0 + +[ClickHouse] SHALL support `DateTime64` in [`transform`](https://clickhouse.com/docs/ru/sql-reference/functions/other-functions/#transform). ### Specific +##### RQ.SRS-010.DateTime64.ExtendedRange.ArrowFormat +version: 1.0 + +[ClickHouse] SHALL support exporting of [DateTime64] data type to +[Arrow](https://clickhouse.com/docs/en/interfaces/formats/#data-format-arrow) format and importing +[DateTime64] from corresponding Arrow data types. + ##### RQ.SRS-010.DateTime64.ExtendedRange.Start version: 1.0 @@ -705,6 +737,11 @@ version: 1.0 to the [Date](https://clickhouse.com/docs/en/sql-reference/data-types/date/) type using the [toDate](https://clickhouse.com/docs/en/sql-reference/functions/type-conversion-functions/#todate) function. This function is ONLY supposed to work in NORMAL RANGE. +###### RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDate32 +version: 1.0 + +[ClickHouse] SHALL support correct conversion of the [DateTime64] data type to the [Date32](https://clickhouse.com/docs/en/sql-reference/data-types/date32/) type using the [toDate32](https://clickhouse.com/docs/en/sql-reference/functions/type-conversion-functions/#todate32) function. + ###### RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDateTime version: 1.0 @@ -788,8 +825,8 @@ using the [fromUnixTimestamp64Nano](https://clickhouse.com/docs/en/sql-reference * **DateTime64**: https://clickhouse.com/docs/en/sql-reference/data-types/datetime64/ * **ISO 8601 format**: https://en.wikipedia.org/wiki/ISO_8601 * **ClickHouse:** https://clickhouse.com -* **GitHub Repository:** https://github.com/ClickHouse/ClickHouse/blob/master/tests/testflows/datetime64_extended_range/requirements/requirements.md -* **Revision History:** https://github.com/ClickHouse/ClickHouse/commits/master/tests/testflows/datetime64_extended_range/requirements/requirements.md +* **GitLab Repository:** https://gitlab.com/altinity-qa/documents/qa-srs010-clickhouse-datetime64-extended-range/-/blob/master/QA_SRS010_ClickHouse_DateTime64_Extended_Range.md +* **Revision History:** https://gitlab.com/altinity-qa/documents/qa-srs010-clickhouse-datetime64-extended-range/-/commits/master/QA_SRS010_ClickHouse_DateTime64_Extended_Range.md * **Git:** https://git-scm.com/ [SRS]: #srs @@ -799,7 +836,7 @@ using the [fromUnixTimestamp64Nano](https://clickhouse.com/docs/en/sql-reference [DateTime64]: https://clickhouse.com/docs/en/sql-reference/data-types/datetime64/ [ISO 8601 format]: https://en.wikipedia.org/wiki/ISO_8601 [ClickHouse]: https://clickhouse.com -[GitHub Repository]: https://github.com/ClickHouse/ClickHouse/blob/master/tests/testflows/datetime64_extended_range/requirements/requirements.md -[Revision History]: https://github.com/ClickHouse/ClickHouse/commits/master/tests/testflows/datetime64_extended_range/requirements/requirements.md +[GitLab Repository]: https://gitlab.com/altinity-qa/documents/qa-srs010-clickhouse-datetime64-extended-range/-/blob/master/QA_SRS010_ClickHouse_DateTime64_Extended_Range.md +[Revision History]: https://gitlab.com/altinity-qa/documents/qa-srs010-clickhouse-datetime64-extended-range/-/commits/master/QA_SRS010_ClickHouse_DateTime64_Extended_Range.md [Git]: https://git-scm.com/ -[GitHub]: https://github.com +[GitLab]: https://gitlab.com diff --git a/tests/testflows/datetime64_extended_range/requirements/requirements.py b/tests/testflows/datetime64_extended_range/requirements/requirements.py index 1bbaf3547d92..9ad8d8f38c64 100644 --- a/tests/testflows/datetime64_extended_range/requirements/requirements.py +++ b/tests/testflows/datetime64_extended_range/requirements/requirements.py @@ -1,6 +1,6 @@ # These requirements were auto generated # from software requirements specification (SRS) -# document by TestFlows v1.6.210505.1133630. +# document by TestFlows v1.8.220216.1232941. # Do not edit by hand but re-generate instead # using 'tfs requirements generate' command. from testflows.core import Specification @@ -188,13 +188,46 @@ "[9961200,73476000,325666800,354675600,370400400,386125200,388566010,401850000,417574811,496803600,528253200,624423614,636516015,671011200,717555600,752047218,859683600,922582800,1018173600,1035705600,1143334800,1162105223,1174784400,1194156000,1206838823,1224982823,1236495624,1319936400,1319936424,1425798025,1459040400,1509872400,2090451627,2140668000]\n" "```\n" "\n" - "\n" ), link=None, level=4, num="4.1.0.11", ) +RQ_SRS_010_DateTime64_ExtendedRange_Transform = Requirement( + name="RQ.SRS-010.DateTime64.ExtendedRange.Transform", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL support `DateTime64` in [`transform`](https://clickhouse.com/docs/ru/sql-reference/functions/other-functions/#transform).\n" + "\n" + ), + link=None, + level=4, + num="4.1.0.12", +) + +RQ_SRS_010_DateTime64_ExtendedRange_ArrowFormat = Requirement( + name="RQ.SRS-010.DateTime64.ExtendedRange.ArrowFormat", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL support exporting of [DateTime64] data type to \n" + "[Arrow](https://clickhouse.com/docs/en/interfaces/formats/#data-format-arrow) format and importing \n" + "[DateTime64] from corresponding Arrow data types.\n" + "\n" + ), + link=None, + level=4, + num="4.2.0.1", +) + RQ_SRS_010_DateTime64_ExtendedRange_Start = Requirement( name="RQ.SRS-010.DateTime64.ExtendedRange.Start", version="1.0", @@ -209,7 +242,7 @@ ), link=None, level=4, - num="4.2.0.1", + num="4.2.0.2", ) RQ_SRS_010_DateTime64_ExtendedRange_End = Requirement( @@ -226,7 +259,7 @@ ), link=None, level=4, - num="4.2.0.2", + num="4.2.0.3", ) RQ_SRS_010_DateTime64_ExtendedRange_NonExistentTime_InvalidDate = Requirement( @@ -246,7 +279,7 @@ ), link=None, level=5, - num="4.2.0.3.1", + num="4.2.0.4.1", ) RQ_SRS_010_DateTime64_ExtendedRange_NonExistentTime_InvalidTime = Requirement( @@ -266,7 +299,7 @@ ), link=None, level=5, - num="4.2.0.3.2", + num="4.2.0.4.2", ) RQ_SRS_010_DateTime64_ExtendedRange_NonExistentTime_TimeZoneSwitch = Requirement( @@ -294,7 +327,7 @@ ), link=None, level=5, - num="4.2.0.3.3", + num="4.2.0.4.3", ) RQ_SRS_010_DateTime64_ExtendedRange_NonExistentTime_DaylightSavingTime = Requirement( @@ -318,7 +351,7 @@ ), link=None, level=5, - num="4.2.0.3.4", + num="4.2.0.4.4", ) RQ_SRS_010_DateTime64_ExtendedRange_NonExistentTime_DaylightSavingTime_Disappeared = Requirement( @@ -337,7 +370,7 @@ ), link=None, level=5, - num="4.2.0.3.5", + num="4.2.0.4.5", ) RQ_SRS_010_DateTime64_ExtendedRange_NonExistentTime_LeapSeconds = Requirement( @@ -353,7 +386,7 @@ ), link=None, level=5, - num="4.2.0.3.6", + num="4.2.0.4.6", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toTimeZone = Requirement( @@ -370,7 +403,7 @@ ), link=None, level=5, - num="4.2.0.4.1", + num="4.2.0.5.1", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toYear = Requirement( @@ -387,7 +420,7 @@ ), link=None, level=5, - num="4.2.0.4.2", + num="4.2.0.5.2", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toQuarter = Requirement( @@ -404,7 +437,7 @@ ), link=None, level=5, - num="4.2.0.4.3", + num="4.2.0.5.3", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toMonth = Requirement( @@ -421,7 +454,7 @@ ), link=None, level=5, - num="4.2.0.4.4", + num="4.2.0.5.4", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toDayOfYear = Requirement( @@ -438,7 +471,7 @@ ), link=None, level=5, - num="4.2.0.4.5", + num="4.2.0.5.5", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toDayOfMonth = Requirement( @@ -455,7 +488,7 @@ ), link=None, level=5, - num="4.2.0.4.6", + num="4.2.0.5.6", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toDayOfWeek = Requirement( @@ -472,7 +505,7 @@ ), link=None, level=5, - num="4.2.0.4.7", + num="4.2.0.5.7", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toHour = Requirement( @@ -489,7 +522,7 @@ ), link=None, level=5, - num="4.2.0.4.8", + num="4.2.0.5.8", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toMinute = Requirement( @@ -506,7 +539,7 @@ ), link=None, level=5, - num="4.2.0.4.9", + num="4.2.0.5.9", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toSecond = Requirement( @@ -523,7 +556,7 @@ ), link=None, level=5, - num="4.2.0.4.10", + num="4.2.0.5.10", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toUnixTimestamp = Requirement( @@ -541,7 +574,7 @@ ), link=None, level=5, - num="4.2.0.4.11", + num="4.2.0.5.11", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toStartOfYear = Requirement( @@ -558,7 +591,7 @@ ), link=None, level=5, - num="4.2.0.4.12", + num="4.2.0.5.12", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toStartOfISOYear = Requirement( @@ -575,7 +608,7 @@ ), link=None, level=5, - num="4.2.0.4.13", + num="4.2.0.5.13", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toStartOfQuarter = Requirement( @@ -592,7 +625,7 @@ ), link=None, level=5, - num="4.2.0.4.14", + num="4.2.0.5.14", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toStartOfMonth = Requirement( @@ -609,7 +642,7 @@ ), link=None, level=5, - num="4.2.0.4.15", + num="4.2.0.5.15", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toMonday = Requirement( @@ -626,7 +659,7 @@ ), link=None, level=5, - num="4.2.0.4.16", + num="4.2.0.5.16", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toStartOfWeek = Requirement( @@ -643,7 +676,7 @@ ), link=None, level=5, - num="4.2.0.4.17", + num="4.2.0.5.17", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toStartOfDay = Requirement( @@ -660,7 +693,7 @@ ), link=None, level=5, - num="4.2.0.4.18", + num="4.2.0.5.18", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toStartOfHour = Requirement( @@ -677,7 +710,7 @@ ), link=None, level=5, - num="4.2.0.4.19", + num="4.2.0.5.19", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toStartOfMinute = Requirement( @@ -694,7 +727,7 @@ ), link=None, level=5, - num="4.2.0.4.20", + num="4.2.0.5.20", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toStartOfSecond = Requirement( @@ -711,7 +744,7 @@ ), link=None, level=5, - num="4.2.0.4.21", + num="4.2.0.5.21", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toStartOfFiveMinute = Requirement( @@ -728,7 +761,7 @@ ), link=None, level=5, - num="4.2.0.4.22", + num="4.2.0.5.22", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toStartOfTenMinutes = Requirement( @@ -745,7 +778,7 @@ ), link=None, level=5, - num="4.2.0.4.23", + num="4.2.0.5.23", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toStartOfFifteenMinutes = Requirement( @@ -762,7 +795,7 @@ ), link=None, level=5, - num="4.2.0.4.24", + num="4.2.0.5.24", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toStartOfInterval = Requirement( @@ -780,7 +813,7 @@ ), link=None, level=5, - num="4.2.0.4.25", + num="4.2.0.5.25", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toTime = Requirement( @@ -797,7 +830,7 @@ ), link=None, level=5, - num="4.2.0.4.26", + num="4.2.0.5.26", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toRelativeYearNum = Requirement( @@ -814,7 +847,7 @@ ), link=None, level=5, - num="4.2.0.4.27", + num="4.2.0.5.27", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toRelativeQuarterNum = Requirement( @@ -831,7 +864,7 @@ ), link=None, level=5, - num="4.2.0.4.28", + num="4.2.0.5.28", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toRelativeMonthNum = Requirement( @@ -848,7 +881,7 @@ ), link=None, level=5, - num="4.2.0.4.29", + num="4.2.0.5.29", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toRelativeWeekNum = Requirement( @@ -865,7 +898,7 @@ ), link=None, level=5, - num="4.2.0.4.30", + num="4.2.0.5.30", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toRelativeDayNum = Requirement( @@ -882,7 +915,7 @@ ), link=None, level=5, - num="4.2.0.4.31", + num="4.2.0.5.31", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toRelativeHourNum = Requirement( @@ -899,7 +932,7 @@ ), link=None, level=5, - num="4.2.0.4.32", + num="4.2.0.5.32", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toRelativeMinuteNum = Requirement( @@ -916,7 +949,7 @@ ), link=None, level=5, - num="4.2.0.4.33", + num="4.2.0.5.33", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toRelativeSecondNum = Requirement( @@ -933,7 +966,7 @@ ), link=None, level=5, - num="4.2.0.4.34", + num="4.2.0.5.34", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toISOYear = Requirement( @@ -950,7 +983,7 @@ ), link=None, level=5, - num="4.2.0.4.35", + num="4.2.0.5.35", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toISOWeek = Requirement( @@ -967,7 +1000,7 @@ ), link=None, level=5, - num="4.2.0.4.36", + num="4.2.0.5.36", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toWeek = Requirement( @@ -984,7 +1017,7 @@ ), link=None, level=5, - num="4.2.0.4.37", + num="4.2.0.5.37", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toYearWeek = Requirement( @@ -1001,7 +1034,7 @@ ), link=None, level=5, - num="4.2.0.4.38", + num="4.2.0.5.38", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_now = Requirement( @@ -1018,7 +1051,7 @@ ), link=None, level=5, - num="4.2.0.4.39", + num="4.2.0.5.39", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_today = Requirement( @@ -1035,7 +1068,7 @@ ), link=None, level=5, - num="4.2.0.4.40", + num="4.2.0.5.40", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_yesterday = Requirement( @@ -1052,7 +1085,7 @@ ), link=None, level=5, - num="4.2.0.4.41", + num="4.2.0.5.41", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_timeSlot = Requirement( @@ -1069,7 +1102,7 @@ ), link=None, level=5, - num="4.2.0.4.42", + num="4.2.0.5.42", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toYYYYMM = Requirement( @@ -1086,7 +1119,7 @@ ), link=None, level=5, - num="4.2.0.4.43", + num="4.2.0.5.43", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toYYYYMMDD = Requirement( @@ -1103,7 +1136,7 @@ ), link=None, level=5, - num="4.2.0.4.44", + num="4.2.0.5.44", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_toYYYYMMDDhhmmss = Requirement( @@ -1120,7 +1153,7 @@ ), link=None, level=5, - num="4.2.0.4.45", + num="4.2.0.5.45", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_addYears = Requirement( @@ -1137,7 +1170,7 @@ ), link=None, level=5, - num="4.2.0.4.46", + num="4.2.0.5.46", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_addMonths = Requirement( @@ -1154,7 +1187,7 @@ ), link=None, level=5, - num="4.2.0.4.47", + num="4.2.0.5.47", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_addWeeks = Requirement( @@ -1171,7 +1204,7 @@ ), link=None, level=5, - num="4.2.0.4.48", + num="4.2.0.5.48", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_addDays = Requirement( @@ -1188,7 +1221,7 @@ ), link=None, level=5, - num="4.2.0.4.49", + num="4.2.0.5.49", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_addHours = Requirement( @@ -1205,7 +1238,7 @@ ), link=None, level=5, - num="4.2.0.4.50", + num="4.2.0.5.50", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_addMinutes = Requirement( @@ -1222,7 +1255,7 @@ ), link=None, level=5, - num="4.2.0.4.51", + num="4.2.0.5.51", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_addSeconds = Requirement( @@ -1239,7 +1272,7 @@ ), link=None, level=5, - num="4.2.0.4.52", + num="4.2.0.5.52", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_addQuarters = Requirement( @@ -1256,7 +1289,7 @@ ), link=None, level=5, - num="4.2.0.4.53", + num="4.2.0.5.53", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_subtractYears = Requirement( @@ -1273,7 +1306,7 @@ ), link=None, level=5, - num="4.2.0.4.54", + num="4.2.0.5.54", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_subtractMonths = Requirement( @@ -1290,7 +1323,7 @@ ), link=None, level=5, - num="4.2.0.4.55", + num="4.2.0.5.55", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_subtractWeeks = Requirement( @@ -1307,7 +1340,7 @@ ), link=None, level=5, - num="4.2.0.4.56", + num="4.2.0.5.56", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_subtractDays = Requirement( @@ -1325,7 +1358,7 @@ ), link=None, level=5, - num="4.2.0.4.57", + num="4.2.0.5.57", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_subtractHours = Requirement( @@ -1342,7 +1375,7 @@ ), link=None, level=5, - num="4.2.0.4.58", + num="4.2.0.5.58", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_subtractMinutes = Requirement( @@ -1359,7 +1392,7 @@ ), link=None, level=5, - num="4.2.0.4.59", + num="4.2.0.5.59", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_subtractSeconds = Requirement( @@ -1376,7 +1409,7 @@ ), link=None, level=5, - num="4.2.0.4.60", + num="4.2.0.5.60", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_subtractQuarters = Requirement( @@ -1393,7 +1426,7 @@ ), link=None, level=5, - num="4.2.0.4.61", + num="4.2.0.5.61", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_dateDiff = Requirement( @@ -1410,7 +1443,7 @@ ), link=None, level=5, - num="4.2.0.4.62", + num="4.2.0.5.62", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_timeSlots = Requirement( @@ -1427,7 +1460,7 @@ ), link=None, level=5, - num="4.2.0.4.63", + num="4.2.0.5.63", ) RQ_SRS_010_DateTime64_ExtendedRange_DatesAndTimesFunctions_formatDateTime = Requirement( @@ -1445,7 +1478,7 @@ ), link=None, level=5, - num="4.2.0.4.64", + num="4.2.0.5.64", ) RQ_SRS_010_DateTime64_ExtendedRange_TypeConversionFunctions_toInt_8_16_32_64_128_256_ = Requirement( @@ -1462,7 +1495,7 @@ ), link=None, level=5, - num="4.2.1.4.1", + num="4.2.1.5.1", ) RQ_SRS_010_DateTime64_ExtendedRange_TypeConversionFunctions_toUInt_8_16_32_64_256_ = Requirement( @@ -1479,7 +1512,7 @@ ), link=None, level=5, - num="4.2.1.4.2", + num="4.2.1.5.2", ) RQ_SRS_010_DateTime64_ExtendedRange_TypeConversionFunctions_toFloat_32_64_ = Requirement( @@ -1496,7 +1529,7 @@ ), link=None, level=5, - num="4.2.1.4.3", + num="4.2.1.5.3", ) RQ_SRS_010_DateTime64_ExtendedRange_TypeConversionFunctions_toDate = Requirement( @@ -1514,7 +1547,23 @@ ), link=None, level=5, - num="4.2.1.4.4", + num="4.2.1.5.4", +) + +RQ_SRS_010_DateTime64_ExtendedRange_TypeConversionFunctions_toDate32 = Requirement( + name="RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDate32", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL support correct conversion of the [DateTime64] data type to the [Date32](https://clickhouse.com/docs/en/sql-reference/data-types/date32/) type using the [toDate32](https://clickhouse.com/docs/en/sql-reference/functions/type-conversion-functions/#todate32) function.\n" + "\n" + ), + link=None, + level=5, + num="4.2.1.5.5", ) RQ_SRS_010_DateTime64_ExtendedRange_TypeConversionFunctions_toDateTime = Requirement( @@ -1532,7 +1581,7 @@ ), link=None, level=5, - num="4.2.1.4.5", + num="4.2.1.5.6", ) RQ_SRS_010_DateTime64_ExtendedRange_TypeConversionFunctions_toDateTime64 = Requirement( @@ -1549,7 +1598,7 @@ ), link=None, level=5, - num="4.2.1.4.6", + num="4.2.1.5.7", ) RQ_SRS_010_DateTime64_ExtendedRange_TypeConversionFunctions_toDateTime64_FromString_MissingTime = Requirement( @@ -1568,7 +1617,7 @@ ), link=None, level=5, - num="4.2.1.4.7", + num="4.2.1.5.8", ) RQ_SRS_010_DateTime64_ExtendedRange_TypeConversionFunctions_toDecimal_32_64_128_256_ = Requirement( @@ -1585,7 +1634,7 @@ ), link=None, level=5, - num="4.2.1.4.8", + num="4.2.1.5.9", ) RQ_SRS_010_DateTime64_ExtendedRange_TypeConversionFunctions_toString = Requirement( @@ -1602,7 +1651,7 @@ ), link=None, level=5, - num="4.2.1.4.9", + num="4.2.1.5.10", ) RQ_SRS_010_DateTime64_ExtendedRange_TypeConversionFunctions_CAST_x_T_ = Requirement( @@ -1619,7 +1668,7 @@ ), link=None, level=5, - num="4.2.1.4.10", + num="4.2.1.5.11", ) RQ_SRS_010_DateTime64_ExtendedRange_TypeConversionFunctions_toUnixTimestamp64Milli = Requirement( @@ -1636,7 +1685,7 @@ ), link=None, level=5, - num="4.2.1.4.11", + num="4.2.1.5.12", ) RQ_SRS_010_DateTime64_ExtendedRange_TypeConversionFunctions_toUnixTimestamp64Micro = Requirement( @@ -1653,7 +1702,7 @@ ), link=None, level=5, - num="4.2.1.4.12", + num="4.2.1.5.13", ) RQ_SRS_010_DateTime64_ExtendedRange_TypeConversionFunctions_toUnixTimestamp64Nano = Requirement( @@ -1670,7 +1719,7 @@ ), link=None, level=5, - num="4.2.1.4.13", + num="4.2.1.5.14", ) RQ_SRS_010_DateTime64_ExtendedRange_TypeConversionFunctions_fromUnixTimestamp64Milli = Requirement( @@ -1688,7 +1737,7 @@ ), link=None, level=5, - num="4.2.1.4.14", + num="4.2.1.5.15", ) RQ_SRS_010_DateTime64_ExtendedRange_TypeConversionFunctions_fromUnixTimestamp64Micro = Requirement( @@ -1706,7 +1755,7 @@ ), link=None, level=5, - num="4.2.1.4.15", + num="4.2.1.5.16", ) RQ_SRS_010_DateTime64_ExtendedRange_TypeConversionFunctions_fromUnixTimestamp64Nano = Requirement( @@ -1724,18 +1773,18 @@ ), link=None, level=5, - num="4.2.1.4.16", + num="4.2.1.5.17", ) -SRS_010_ClickHouse_DateTime64_Extended_Range = Specification( - name="SRS-010 ClickHouse DateTime64 Extended Range", +QA_SRS010_ClickHouse_DateTime64_Extended_Range = Specification( + name="QA-SRS010 ClickHouse DateTime64 Extended Range", description=None, - author=None, - date=None, - status=None, - approved_by=None, - approved_date=None, - approved_version=None, + author="vzakaznikov, zvonand", + date="August 10, 2020", + status="-", + approved_by="-", + approved_date="-", + approved_version="-", version=None, group=None, type=None, @@ -1801,443 +1850,458 @@ level=4, num="4.1.0.11", ), + Heading( + name="RQ.SRS-010.DateTime64.ExtendedRange.Transform", + level=4, + num="4.1.0.12", + ), Heading(name="Specific", level=2, num="4.2"), Heading( - name="RQ.SRS-010.DateTime64.ExtendedRange.Start", level=4, num="4.2.0.1" + name="RQ.SRS-010.DateTime64.ExtendedRange.ArrowFormat", + level=4, + num="4.2.0.1", ), - Heading(name="RQ.SRS-010.DateTime64.ExtendedRange.End", level=4, num="4.2.0.2"), - Heading(name="Non-Existent Time", level=4, num="4.2.0.3"), + Heading( + name="RQ.SRS-010.DateTime64.ExtendedRange.Start", level=4, num="4.2.0.2" + ), + Heading(name="RQ.SRS-010.DateTime64.ExtendedRange.End", level=4, num="4.2.0.3"), + Heading(name="Non-Existent Time", level=4, num="4.2.0.4"), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.InvalidDate", level=5, - num="4.2.0.3.1", + num="4.2.0.4.1", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.InvalidTime", level=5, - num="4.2.0.3.2", + num="4.2.0.4.2", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.TimeZoneSwitch", level=5, - num="4.2.0.3.3", + num="4.2.0.4.3", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.DaylightSavingTime", level=5, - num="4.2.0.3.4", + num="4.2.0.4.4", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.DaylightSavingTime.Disappeared", level=5, - num="4.2.0.3.5", + num="4.2.0.4.5", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.LeapSeconds", level=5, - num="4.2.0.3.6", + num="4.2.0.4.6", ), - Heading(name="Dates And Times Functions", level=4, num="4.2.0.4"), + Heading(name="Dates And Times Functions", level=4, num="4.2.0.5"), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toTimeZone", level=5, - num="4.2.0.4.1", + num="4.2.0.5.1", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toYear", level=5, - num="4.2.0.4.2", + num="4.2.0.5.2", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toQuarter", level=5, - num="4.2.0.4.3", + num="4.2.0.5.3", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toMonth", level=5, - num="4.2.0.4.4", + num="4.2.0.5.4", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toDayOfYear", level=5, - num="4.2.0.4.5", + num="4.2.0.5.5", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toDayOfMonth", level=5, - num="4.2.0.4.6", + num="4.2.0.5.6", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toDayOfWeek", level=5, - num="4.2.0.4.7", + num="4.2.0.5.7", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toHour", level=5, - num="4.2.0.4.8", + num="4.2.0.5.8", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toMinute", level=5, - num="4.2.0.4.9", + num="4.2.0.5.9", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toSecond", level=5, - num="4.2.0.4.10", + num="4.2.0.5.10", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toUnixTimestamp", level=5, - num="4.2.0.4.11", + num="4.2.0.5.11", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfYear", level=5, - num="4.2.0.4.12", + num="4.2.0.5.12", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfISOYear", level=5, - num="4.2.0.4.13", + num="4.2.0.5.13", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfQuarter", level=5, - num="4.2.0.4.14", + num="4.2.0.5.14", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfMonth", level=5, - num="4.2.0.4.15", + num="4.2.0.5.15", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toMonday", level=5, - num="4.2.0.4.16", + num="4.2.0.5.16", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfWeek", level=5, - num="4.2.0.4.17", + num="4.2.0.5.17", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfDay", level=5, - num="4.2.0.4.18", + num="4.2.0.5.18", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfHour", level=5, - num="4.2.0.4.19", + num="4.2.0.5.19", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfMinute", level=5, - num="4.2.0.4.20", + num="4.2.0.5.20", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfSecond", level=5, - num="4.2.0.4.21", + num="4.2.0.5.21", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfFiveMinute", level=5, - num="4.2.0.4.22", + num="4.2.0.5.22", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfTenMinutes", level=5, - num="4.2.0.4.23", + num="4.2.0.5.23", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfFifteenMinutes", level=5, - num="4.2.0.4.24", + num="4.2.0.5.24", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfInterval", level=5, - num="4.2.0.4.25", + num="4.2.0.5.25", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toTime", level=5, - num="4.2.0.4.26", + num="4.2.0.5.26", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeYearNum", level=5, - num="4.2.0.4.27", + num="4.2.0.5.27", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeQuarterNum", level=5, - num="4.2.0.4.28", + num="4.2.0.5.28", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeMonthNum", level=5, - num="4.2.0.4.29", + num="4.2.0.5.29", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeWeekNum", level=5, - num="4.2.0.4.30", + num="4.2.0.5.30", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeDayNum", level=5, - num="4.2.0.4.31", + num="4.2.0.5.31", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeHourNum", level=5, - num="4.2.0.4.32", + num="4.2.0.5.32", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeMinuteNum", level=5, - num="4.2.0.4.33", + num="4.2.0.5.33", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeSecondNum", level=5, - num="4.2.0.4.34", + num="4.2.0.5.34", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toISOYear", level=5, - num="4.2.0.4.35", + num="4.2.0.5.35", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toISOWeek", level=5, - num="4.2.0.4.36", + num="4.2.0.5.36", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toWeek", level=5, - num="4.2.0.4.37", + num="4.2.0.5.37", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toYearWeek", level=5, - num="4.2.0.4.38", + num="4.2.0.5.38", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.now", level=5, - num="4.2.0.4.39", + num="4.2.0.5.39", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.today", level=5, - num="4.2.0.4.40", + num="4.2.0.5.40", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.yesterday", level=5, - num="4.2.0.4.41", + num="4.2.0.5.41", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.timeSlot", level=5, - num="4.2.0.4.42", + num="4.2.0.5.42", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toYYYYMM", level=5, - num="4.2.0.4.43", + num="4.2.0.5.43", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toYYYYMMDD", level=5, - num="4.2.0.4.44", + num="4.2.0.5.44", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toYYYYMMDDhhmmss", level=5, - num="4.2.0.4.45", + num="4.2.0.5.45", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addYears", level=5, - num="4.2.0.4.46", + num="4.2.0.5.46", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addMonths", level=5, - num="4.2.0.4.47", + num="4.2.0.5.47", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addWeeks", level=5, - num="4.2.0.4.48", + num="4.2.0.5.48", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addDays", level=5, - num="4.2.0.4.49", + num="4.2.0.5.49", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addHours", level=5, - num="4.2.0.4.50", + num="4.2.0.5.50", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addMinutes", level=5, - num="4.2.0.4.51", + num="4.2.0.5.51", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addSeconds", level=5, - num="4.2.0.4.52", + num="4.2.0.5.52", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addQuarters", level=5, - num="4.2.0.4.53", + num="4.2.0.5.53", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractYears", level=5, - num="4.2.0.4.54", + num="4.2.0.5.54", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractMonths", level=5, - num="4.2.0.4.55", + num="4.2.0.5.55", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractWeeks", level=5, - num="4.2.0.4.56", + num="4.2.0.5.56", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractDays", level=5, - num="4.2.0.4.57", + num="4.2.0.5.57", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractHours", level=5, - num="4.2.0.4.58", + num="4.2.0.5.58", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractMinutes", level=5, - num="4.2.0.4.59", + num="4.2.0.5.59", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractSeconds", level=5, - num="4.2.0.4.60", + num="4.2.0.5.60", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractQuarters", level=5, - num="4.2.0.4.61", + num="4.2.0.5.61", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.dateDiff", level=5, - num="4.2.0.4.62", + num="4.2.0.5.62", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.timeSlots", level=5, - num="4.2.0.4.63", + num="4.2.0.5.63", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.formatDateTime", level=5, - num="4.2.0.4.64", + num="4.2.0.5.64", ), Heading(name="Type Conversion Functions", level=3, num="4.2.1"), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toInt(8|16|32|64|128|256)", level=5, - num="4.2.1.4.1", + num="4.2.1.5.1", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toUInt(8|16|32|64|256)", level=5, - num="4.2.1.4.2", + num="4.2.1.5.2", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toFloat(32|64)", level=5, - num="4.2.1.4.3", + num="4.2.1.5.3", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDate", level=5, - num="4.2.1.4.4", + num="4.2.1.5.4", + ), + Heading( + name="RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDate32", + level=5, + num="4.2.1.5.5", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDateTime", level=5, - num="4.2.1.4.5", + num="4.2.1.5.6", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDateTime64", level=5, - num="4.2.1.4.6", + num="4.2.1.5.7", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDateTime64.FromString.MissingTime", level=5, - num="4.2.1.4.7", + num="4.2.1.5.8", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDecimal(32|64|128|256)", level=5, - num="4.2.1.4.8", + num="4.2.1.5.9", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toString", level=5, - num="4.2.1.4.9", + num="4.2.1.5.10", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.CAST(x,T)", level=5, - num="4.2.1.4.10", + num="4.2.1.5.11", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toUnixTimestamp64Milli", level=5, - num="4.2.1.4.11", + num="4.2.1.5.12", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toUnixTimestamp64Micro", level=5, - num="4.2.1.4.12", + num="4.2.1.5.13", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toUnixTimestamp64Nano", level=5, - num="4.2.1.4.13", + num="4.2.1.5.14", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.fromUnixTimestamp64Milli", level=5, - num="4.2.1.4.14", + num="4.2.1.5.15", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.fromUnixTimestamp64Micro", level=5, - num="4.2.1.4.15", + num="4.2.1.5.16", ), Heading( name="RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.fromUnixTimestamp64Nano", level=5, - num="4.2.1.4.16", + num="4.2.1.5.17", ), Heading(name="References", level=1, num="5"), ), @@ -2253,6 +2317,8 @@ RQ_SRS_010_DateTime64_ExtendedRange_NonExistentTime, RQ_SRS_010_DateTime64_ExtendedRange_Comparison, RQ_SRS_010_DateTime64_ExtendedRange_SpecificTimestamps, + RQ_SRS_010_DateTime64_ExtendedRange_Transform, + RQ_SRS_010_DateTime64_ExtendedRange_ArrowFormat, RQ_SRS_010_DateTime64_ExtendedRange_Start, RQ_SRS_010_DateTime64_ExtendedRange_End, RQ_SRS_010_DateTime64_ExtendedRange_NonExistentTime_InvalidDate, @@ -2329,6 +2395,7 @@ RQ_SRS_010_DateTime64_ExtendedRange_TypeConversionFunctions_toUInt_8_16_32_64_256_, RQ_SRS_010_DateTime64_ExtendedRange_TypeConversionFunctions_toFloat_32_64_, RQ_SRS_010_DateTime64_ExtendedRange_TypeConversionFunctions_toDate, + RQ_SRS_010_DateTime64_ExtendedRange_TypeConversionFunctions_toDate32, RQ_SRS_010_DateTime64_ExtendedRange_TypeConversionFunctions_toDateTime, RQ_SRS_010_DateTime64_ExtendedRange_TypeConversionFunctions_toDateTime64, RQ_SRS_010_DateTime64_ExtendedRange_TypeConversionFunctions_toDateTime64_FromString_MissingTime, @@ -2343,9 +2410,27 @@ RQ_SRS_010_DateTime64_ExtendedRange_TypeConversionFunctions_fromUnixTimestamp64Nano, ), content=""" -# SRS-010 ClickHouse DateTime64 Extended Range +# QA-SRS010 ClickHouse DateTime64 Extended Range # Software Requirements Specification +(c) 2020 Altinity LTD. All Rights Reserved. + +**Document status:** Confidential + +**Author:** vzakaznikov, zvonand + +**Date:** August 10, 2020 + +## Approval + +**Status:** - + +**Version:** - + +**Approved by:** - + +**Date:** - + ## Table of Contents * 1 [Revision History](#revision-history) @@ -2367,98 +2452,101 @@ * 4.1.0.9 [RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime](#rqsrs-010datetime64extendedrangenonexistenttime) * 4.1.0.10 [RQ.SRS-010.DateTime64.ExtendedRange.Comparison](#rqsrs-010datetime64extendedrangecomparison) * 4.1.0.11 [RQ.SRS-010.DateTime64.ExtendedRange.SpecificTimestamps](#rqsrs-010datetime64extendedrangespecifictimestamps) + * 4.1.0.12 [RQ.SRS-010.DateTime64.ExtendedRange.Transform](#rqsrs-010datetime64extendedrangetransform) * 4.2 [Specific](#specific) - * 4.2.0.1 [RQ.SRS-010.DateTime64.ExtendedRange.Start](#rqsrs-010datetime64extendedrangestart) - * 4.2.0.2 [RQ.SRS-010.DateTime64.ExtendedRange.End](#rqsrs-010datetime64extendedrangeend) - * 4.2.0.3 [Non-Existent Time](#non-existent-time) - * 4.2.0.3.1 [RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.InvalidDate](#rqsrs-010datetime64extendedrangenonexistenttimeinvaliddate) - * 4.2.0.3.2 [RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.InvalidTime](#rqsrs-010datetime64extendedrangenonexistenttimeinvalidtime) - * 4.2.0.3.3 [RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.TimeZoneSwitch](#rqsrs-010datetime64extendedrangenonexistenttimetimezoneswitch) - * 4.2.0.3.4 [RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.DaylightSavingTime](#rqsrs-010datetime64extendedrangenonexistenttimedaylightsavingtime) - * 4.2.0.3.5 [RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.DaylightSavingTime.Disappeared](#rqsrs-010datetime64extendedrangenonexistenttimedaylightsavingtimedisappeared) - * 4.2.0.3.6 [RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.LeapSeconds](#rqsrs-010datetime64extendedrangenonexistenttimeleapseconds) - * 4.2.0.4 [Dates And Times Functions](#dates-and-times-functions) - * 4.2.0.4.1 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toTimeZone](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstotimezone) - * 4.2.0.4.2 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toYear](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoyear) - * 4.2.0.4.3 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toQuarter](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoquarter) - * 4.2.0.4.4 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toMonth](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstomonth) - * 4.2.0.4.5 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toDayOfYear](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstodayofyear) - * 4.2.0.4.6 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toDayOfMonth](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstodayofmonth) - * 4.2.0.4.7 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toDayOfWeek](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstodayofweek) - * 4.2.0.4.8 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toHour](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstohour) - * 4.2.0.4.9 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toMinute](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstominute) - * 4.2.0.4.10 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toSecond](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstosecond) - * 4.2.0.4.11 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toUnixTimestamp](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstounixtimestamp) - * 4.2.0.4.12 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfYear](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofyear) - * 4.2.0.4.13 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfISOYear](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofisoyear) - * 4.2.0.4.14 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfQuarter](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofquarter) - * 4.2.0.4.15 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfMonth](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofmonth) - * 4.2.0.4.16 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toMonday](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstomonday) - * 4.2.0.4.17 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfWeek](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofweek) - * 4.2.0.4.18 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfDay](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofday) - * 4.2.0.4.19 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfHour](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofhour) - * 4.2.0.4.20 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfMinute](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofminute) - * 4.2.0.4.21 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfSecond](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofsecond) - * 4.2.0.4.22 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfFiveMinute](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartoffiveminute) - * 4.2.0.4.23 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfTenMinutes](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartoftenminutes) - * 4.2.0.4.24 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfFifteenMinutes](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartoffifteenminutes) - * 4.2.0.4.25 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfInterval](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofinterval) - * 4.2.0.4.26 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toTime](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstotime) - * 4.2.0.4.27 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeYearNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativeyearnum) - * 4.2.0.4.28 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeQuarterNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativequarternum) - * 4.2.0.4.29 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeMonthNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativemonthnum) - * 4.2.0.4.30 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeWeekNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativeweeknum) - * 4.2.0.4.31 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeDayNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativedaynum) - * 4.2.0.4.32 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeHourNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativehournum) - * 4.2.0.4.33 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeMinuteNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativeminutenum) - * 4.2.0.4.34 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeSecondNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativesecondnum) - * 4.2.0.4.35 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toISOYear](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoisoyear) - * 4.2.0.4.36 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toISOWeek](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoisoweek) - * 4.2.0.4.37 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toWeek](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoweek) - * 4.2.0.4.38 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toYearWeek](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoyearweek) - * 4.2.0.4.39 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.now](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsnow) - * 4.2.0.4.40 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.today](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoday) - * 4.2.0.4.41 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.yesterday](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsyesterday) - * 4.2.0.4.42 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.timeSlot](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstimeslot) - * 4.2.0.4.43 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toYYYYMM](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoyyyymm) - * 4.2.0.4.44 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toYYYYMMDD](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoyyyymmdd) - * 4.2.0.4.45 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toYYYYMMDDhhmmss](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoyyyymmddhhmmss) - * 4.2.0.4.46 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addYears](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsaddyears) - * 4.2.0.4.47 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addMonths](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsaddmonths) - * 4.2.0.4.48 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addWeeks](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsaddweeks) - * 4.2.0.4.49 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addDays](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsadddays) - * 4.2.0.4.50 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addHours](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsaddhours) - * 4.2.0.4.51 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addMinutes](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsaddminutes) - * 4.2.0.4.52 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addSeconds](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsaddseconds) - * 4.2.0.4.53 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addQuarters](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsaddquarters) - * 4.2.0.4.54 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractYears](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtractyears) - * 4.2.0.4.55 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractMonths](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtractmonths) - * 4.2.0.4.56 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractWeeks](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtractweeks) - * 4.2.0.4.57 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractDays](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtractdays) - * 4.2.0.4.58 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractHours](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtracthours) - * 4.2.0.4.59 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractMinutes](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtractminutes) - * 4.2.0.4.60 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractSeconds](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtractseconds) - * 4.2.0.4.61 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractQuarters](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtractquarters) - * 4.2.0.4.62 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.dateDiff](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsdatediff) - * 4.2.0.4.63 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.timeSlots](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstimeslots) - * 4.2.0.4.64 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.formatDateTime](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsformatdatetime) + * 4.2.0.1 [RQ.SRS-010.DateTime64.ExtendedRange.ArrowFormat](#rqsrs-010datetime64extendedrangearrowformat) + * 4.2.0.2 [RQ.SRS-010.DateTime64.ExtendedRange.Start](#rqsrs-010datetime64extendedrangestart) + * 4.2.0.3 [RQ.SRS-010.DateTime64.ExtendedRange.End](#rqsrs-010datetime64extendedrangeend) + * 4.2.0.4 [Non-Existent Time](#non-existent-time) + * 4.2.0.4.1 [RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.InvalidDate](#rqsrs-010datetime64extendedrangenonexistenttimeinvaliddate) + * 4.2.0.4.2 [RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.InvalidTime](#rqsrs-010datetime64extendedrangenonexistenttimeinvalidtime) + * 4.2.0.4.3 [RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.TimeZoneSwitch](#rqsrs-010datetime64extendedrangenonexistenttimetimezoneswitch) + * 4.2.0.4.4 [RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.DaylightSavingTime](#rqsrs-010datetime64extendedrangenonexistenttimedaylightsavingtime) + * 4.2.0.4.5 [RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.DaylightSavingTime.Disappeared](#rqsrs-010datetime64extendedrangenonexistenttimedaylightsavingtimedisappeared) + * 4.2.0.4.6 [RQ.SRS-010.DateTime64.ExtendedRange.NonExistentTime.LeapSeconds](#rqsrs-010datetime64extendedrangenonexistenttimeleapseconds) + * 4.2.0.5 [Dates And Times Functions](#dates-and-times-functions) + * 4.2.0.5.1 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toTimeZone](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstotimezone) + * 4.2.0.5.2 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toYear](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoyear) + * 4.2.0.5.3 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toQuarter](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoquarter) + * 4.2.0.5.4 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toMonth](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstomonth) + * 4.2.0.5.5 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toDayOfYear](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstodayofyear) + * 4.2.0.5.6 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toDayOfMonth](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstodayofmonth) + * 4.2.0.5.7 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toDayOfWeek](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstodayofweek) + * 4.2.0.5.8 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toHour](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstohour) + * 4.2.0.5.9 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toMinute](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstominute) + * 4.2.0.5.10 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toSecond](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstosecond) + * 4.2.0.5.11 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toUnixTimestamp](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstounixtimestamp) + * 4.2.0.5.12 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfYear](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofyear) + * 4.2.0.5.13 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfISOYear](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofisoyear) + * 4.2.0.5.14 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfQuarter](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofquarter) + * 4.2.0.5.15 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfMonth](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofmonth) + * 4.2.0.5.16 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toMonday](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstomonday) + * 4.2.0.5.17 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfWeek](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofweek) + * 4.2.0.5.18 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfDay](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofday) + * 4.2.0.5.19 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfHour](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofhour) + * 4.2.0.5.20 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfMinute](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofminute) + * 4.2.0.5.21 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfSecond](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofsecond) + * 4.2.0.5.22 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfFiveMinute](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartoffiveminute) + * 4.2.0.5.23 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfTenMinutes](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartoftenminutes) + * 4.2.0.5.24 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfFifteenMinutes](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartoffifteenminutes) + * 4.2.0.5.25 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toStartOfInterval](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstostartofinterval) + * 4.2.0.5.26 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toTime](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstotime) + * 4.2.0.5.27 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeYearNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativeyearnum) + * 4.2.0.5.28 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeQuarterNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativequarternum) + * 4.2.0.5.29 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeMonthNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativemonthnum) + * 4.2.0.5.30 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeWeekNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativeweeknum) + * 4.2.0.5.31 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeDayNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativedaynum) + * 4.2.0.5.32 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeHourNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativehournum) + * 4.2.0.5.33 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeMinuteNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativeminutenum) + * 4.2.0.5.34 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toRelativeSecondNum](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstorelativesecondnum) + * 4.2.0.5.35 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toISOYear](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoisoyear) + * 4.2.0.5.36 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toISOWeek](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoisoweek) + * 4.2.0.5.37 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toWeek](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoweek) + * 4.2.0.5.38 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toYearWeek](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoyearweek) + * 4.2.0.5.39 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.now](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsnow) + * 4.2.0.5.40 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.today](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoday) + * 4.2.0.5.41 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.yesterday](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsyesterday) + * 4.2.0.5.42 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.timeSlot](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstimeslot) + * 4.2.0.5.43 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toYYYYMM](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoyyyymm) + * 4.2.0.5.44 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toYYYYMMDD](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoyyyymmdd) + * 4.2.0.5.45 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.toYYYYMMDDhhmmss](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstoyyyymmddhhmmss) + * 4.2.0.5.46 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addYears](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsaddyears) + * 4.2.0.5.47 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addMonths](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsaddmonths) + * 4.2.0.5.48 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addWeeks](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsaddweeks) + * 4.2.0.5.49 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addDays](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsadddays) + * 4.2.0.5.50 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addHours](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsaddhours) + * 4.2.0.5.51 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addMinutes](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsaddminutes) + * 4.2.0.5.52 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addSeconds](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsaddseconds) + * 4.2.0.5.53 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.addQuarters](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsaddquarters) + * 4.2.0.5.54 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractYears](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtractyears) + * 4.2.0.5.55 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractMonths](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtractmonths) + * 4.2.0.5.56 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractWeeks](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtractweeks) + * 4.2.0.5.57 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractDays](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtractdays) + * 4.2.0.5.58 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractHours](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtracthours) + * 4.2.0.5.59 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractMinutes](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtractminutes) + * 4.2.0.5.60 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractSeconds](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtractseconds) + * 4.2.0.5.61 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.subtractQuarters](#rqsrs-010datetime64extendedrangedatesandtimesfunctionssubtractquarters) + * 4.2.0.5.62 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.dateDiff](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsdatediff) + * 4.2.0.5.63 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.timeSlots](#rqsrs-010datetime64extendedrangedatesandtimesfunctionstimeslots) + * 4.2.0.5.64 [RQ.SRS-010.DateTime64.ExtendedRange.DatesAndTimesFunctions.formatDateTime](#rqsrs-010datetime64extendedrangedatesandtimesfunctionsformatdatetime) * 4.2.1 [Type Conversion Functions](#type-conversion-functions) - * 4.2.1.4.1 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toInt(8|16|32|64|128|256)](#rqsrs-010datetime64extendedrangetypeconversionfunctionstoint8163264128256) - * 4.2.1.4.2 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toUInt(8|16|32|64|256)](#rqsrs-010datetime64extendedrangetypeconversionfunctionstouint8163264256) - * 4.2.1.4.3 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toFloat(32|64)](#rqsrs-010datetime64extendedrangetypeconversionfunctionstofloat3264) - * 4.2.1.4.4 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDate](#rqsrs-010datetime64extendedrangetypeconversionfunctionstodate) - * 4.2.1.4.5 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDateTime](#rqsrs-010datetime64extendedrangetypeconversionfunctionstodatetime) - * 4.2.1.4.6 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDateTime64](#rqsrs-010datetime64extendedrangetypeconversionfunctionstodatetime64) - * 4.2.1.4.7 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDateTime64.FromString.MissingTime](#rqsrs-010datetime64extendedrangetypeconversionfunctionstodatetime64fromstringmissingtime) - * 4.2.1.4.8 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDecimal(32|64|128|256)](#rqsrs-010datetime64extendedrangetypeconversionfunctionstodecimal3264128256) - * 4.2.1.4.9 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toString](#rqsrs-010datetime64extendedrangetypeconversionfunctionstostring) - * 4.2.1.4.10 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.CAST(x,T)](#rqsrs-010datetime64extendedrangetypeconversionfunctionscastxt) - * 4.2.1.4.11 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toUnixTimestamp64Milli](#rqsrs-010datetime64extendedrangetypeconversionfunctionstounixtimestamp64milli) - * 4.2.1.4.12 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toUnixTimestamp64Micro](#rqsrs-010datetime64extendedrangetypeconversionfunctionstounixtimestamp64micro) - * 4.2.1.4.13 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toUnixTimestamp64Nano](#rqsrs-010datetime64extendedrangetypeconversionfunctionstounixtimestamp64nano) - * 4.2.1.4.14 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.fromUnixTimestamp64Milli](#rqsrs-010datetime64extendedrangetypeconversionfunctionsfromunixtimestamp64milli) - * 4.2.1.4.15 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.fromUnixTimestamp64Micro](#rqsrs-010datetime64extendedrangetypeconversionfunctionsfromunixtimestamp64micro) - * 4.2.1.4.16 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.fromUnixTimestamp64Nano](#rqsrs-010datetime64extendedrangetypeconversionfunctionsfromunixtimestamp64nano) + * 4.2.1.5.1 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toInt(8|16|32|64|128|256)](#rqsrs-010datetime64extendedrangetypeconversionfunctionstoint8163264128256) + * 4.2.1.5.2 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toUInt(8|16|32|64|256)](#rqsrs-010datetime64extendedrangetypeconversionfunctionstouint8163264256) + * 4.2.1.5.3 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toFloat(32|64)](#rqsrs-010datetime64extendedrangetypeconversionfunctionstofloat3264) + * 4.2.1.5.4 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDate](#rqsrs-010datetime64extendedrangetypeconversionfunctionstodate) + * 4.2.1.5.5 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDate32](#rqsrs-010datetime64extendedrangetypeconversionfunctionstodate32) + * 4.2.1.5.6 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDateTime](#rqsrs-010datetime64extendedrangetypeconversionfunctionstodatetime) + * 4.2.1.5.7 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDateTime64](#rqsrs-010datetime64extendedrangetypeconversionfunctionstodatetime64) + * 4.2.1.5.8 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDateTime64.FromString.MissingTime](#rqsrs-010datetime64extendedrangetypeconversionfunctionstodatetime64fromstringmissingtime) + * 4.2.1.5.9 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDecimal(32|64|128|256)](#rqsrs-010datetime64extendedrangetypeconversionfunctionstodecimal3264128256) + * 4.2.1.5.10 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toString](#rqsrs-010datetime64extendedrangetypeconversionfunctionstostring) + * 4.2.1.5.11 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.CAST(x,T)](#rqsrs-010datetime64extendedrangetypeconversionfunctionscastxt) + * 4.2.1.5.12 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toUnixTimestamp64Milli](#rqsrs-010datetime64extendedrangetypeconversionfunctionstounixtimestamp64milli) + * 4.2.1.5.13 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toUnixTimestamp64Micro](#rqsrs-010datetime64extendedrangetypeconversionfunctionstounixtimestamp64micro) + * 4.2.1.5.14 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toUnixTimestamp64Nano](#rqsrs-010datetime64extendedrangetypeconversionfunctionstounixtimestamp64nano) + * 4.2.1.5.15 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.fromUnixTimestamp64Milli](#rqsrs-010datetime64extendedrangetypeconversionfunctionsfromunixtimestamp64milli) + * 4.2.1.5.16 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.fromUnixTimestamp64Micro](#rqsrs-010datetime64extendedrangetypeconversionfunctionsfromunixtimestamp64micro) + * 4.2.1.5.17 [RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.fromUnixTimestamp64Nano](#rqsrs-010datetime64extendedrangetypeconversionfunctionsfromunixtimestamp64nano) * 5 [References](#references) ## Revision History @@ -2555,9 +2643,20 @@ [9961200,73476000,325666800,354675600,370400400,386125200,388566010,401850000,417574811,496803600,528253200,624423614,636516015,671011200,717555600,752047218,859683600,922582800,1018173600,1035705600,1143334800,1162105223,1174784400,1194156000,1206838823,1224982823,1236495624,1319936400,1319936424,1425798025,1459040400,1509872400,2090451627,2140668000] ``` +##### RQ.SRS-010.DateTime64.ExtendedRange.Transform +version: 1.0 + +[ClickHouse] SHALL support `DateTime64` in [`transform`](https://clickhouse.com/docs/ru/sql-reference/functions/other-functions/#transform). ### Specific +##### RQ.SRS-010.DateTime64.ExtendedRange.ArrowFormat +version: 1.0 + +[ClickHouse] SHALL support exporting of [DateTime64] data type to +[Arrow](https://clickhouse.com/docs/en/interfaces/formats/#data-format-arrow) format and importing +[DateTime64] from corresponding Arrow data types. + ##### RQ.SRS-010.DateTime64.ExtendedRange.Start version: 1.0 @@ -3050,6 +3149,11 @@ to the [Date](https://clickhouse.com/docs/en/sql-reference/data-types/date/) type using the [toDate](https://clickhouse.com/docs/en/sql-reference/functions/type-conversion-functions/#todate) function. This function is ONLY supposed to work in NORMAL RANGE. +###### RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDate32 +version: 1.0 + +[ClickHouse] SHALL support correct conversion of the [DateTime64] data type to the [Date32](https://clickhouse.com/docs/en/sql-reference/data-types/date32/) type using the [toDate32](https://clickhouse.com/docs/en/sql-reference/functions/type-conversion-functions/#todate32) function. + ###### RQ.SRS-010.DateTime64.ExtendedRange.TypeConversionFunctions.toDateTime version: 1.0 @@ -3133,8 +3237,8 @@ * **DateTime64**: https://clickhouse.com/docs/en/sql-reference/data-types/datetime64/ * **ISO 8601 format**: https://en.wikipedia.org/wiki/ISO_8601 * **ClickHouse:** https://clickhouse.com -* **GitHub Repository:** https://github.com/ClickHouse/ClickHouse/blob/master/tests/testflows/datetime64_extended_range/requirements/requirements.md -* **Revision History:** https://github.com/ClickHouse/ClickHouse/commits/master/tests/testflows/datetime64_extended_range/requirements/requirements.md +* **GitLab Repository:** https://gitlab.com/altinity-qa/documents/qa-srs010-clickhouse-datetime64-extended-range/-/blob/master/QA_SRS010_ClickHouse_DateTime64_Extended_Range.md +* **Revision History:** https://gitlab.com/altinity-qa/documents/qa-srs010-clickhouse-datetime64-extended-range/-/commits/master/QA_SRS010_ClickHouse_DateTime64_Extended_Range.md * **Git:** https://git-scm.com/ [SRS]: #srs @@ -3144,9 +3248,9 @@ [DateTime64]: https://clickhouse.com/docs/en/sql-reference/data-types/datetime64/ [ISO 8601 format]: https://en.wikipedia.org/wiki/ISO_8601 [ClickHouse]: https://clickhouse.com -[GitHub Repository]: https://github.com/ClickHouse/ClickHouse/blob/master/tests/testflows/datetime64_extended_range/requirements/requirements.md -[Revision History]: https://github.com/ClickHouse/ClickHouse/commits/master/tests/testflows/datetime64_extended_range/requirements/requirements.md +[GitLab Repository]: https://gitlab.com/altinity-qa/documents/qa-srs010-clickhouse-datetime64-extended-range/-/blob/master/QA_SRS010_ClickHouse_DateTime64_Extended_Range.md +[Revision History]: https://gitlab.com/altinity-qa/documents/qa-srs010-clickhouse-datetime64-extended-range/-/commits/master/QA_SRS010_ClickHouse_DateTime64_Extended_Range.md [Git]: https://git-scm.com/ -[GitHub]: https://github.com +[GitLab]: https://gitlab.com """, ) diff --git a/tests/testflows/datetime64_extended_range/tests/common.py b/tests/testflows/datetime64_extended_range/tests/common.py index 1154cf21b92f..93106fbc6180 100644 --- a/tests/testflows/datetime64_extended_range/tests/common.py +++ b/tests/testflows/datetime64_extended_range/tests/common.py @@ -16,7 +16,7 @@ def in_normal_range(dt: datetime.datetime): def years_range(stress=False, padding=(0, 0)): """Returns a set of year values used for testing.""" - return range(1925 + padding[0], 2283 - padding[1]) if stress else (1927, 2000, 2281) + return range(1925 + padding[0], 2238 - padding[1]) if stress else (1927, 2000, 2236) def timezones_range(stress=False): @@ -124,7 +124,7 @@ def exec_query(self, request, expected=None, exitcode=None): assert r.exitcode == exitcode, error() -@TestStep +@TestOutline def walk_datetime_in_incrementing_steps( self, date, hrs_range=(0, 24), step=1, timezone="UTC", precision=0 ): @@ -137,39 +137,27 @@ def walk_datetime_in_incrementing_steps( """ stress = self.context.stress + secs = f"00{'.' * (precision > 0)}{'0' * precision}" - with Pool(2) as pool: - try: - with When( - f"I loop through datetime range {hrs_range} starting from {date} in {step}min increments" - ): - for hrs in ( - range(*hrs_range) if stress else (hrs_range[0], hrs_range[1] - 1) - ): - for mins in range(0, 60, step) if stress else (0, 59): - datetime = ( - f"{date} {str(hrs).zfill(2)}:{str(mins).zfill(2)}:{secs}" - ) - expected = datetime - - with When(f"time is {datetime}"): - Test( - name=f"{hrs}:{mins}:{secs}", - test=select_check_datetime, - parallel=True, - executor=pool, - )( - datetime=datetime, - precision=precision, - timezone=timezone, - expected=expected, - ) - finally: - join() - - -@TestStep + with When( + f"I loop through datetime range {hrs_range} starting from {date} in {step} min increments" + ): + for hrs in range(*hrs_range) if stress else (hrs_range[0], hrs_range[1] - 1): + for mins in range(0, 60, step) if stress else (0, 59): + datetime = f"{date} {str(hrs).zfill(2)}:{str(mins).zfill(2)}:{secs}" + expected = datetime + + with When(f"time is {datetime}"): + select_check_datetime( + datetime=datetime, + precision=precision, + timezone=timezone, + expected=expected, + ) + + +@TestOutline def walk_datetime_in_decrementing_steps( self, date, hrs_range=(23, 0), step=1, timezone="UTC", precision=0 ): @@ -182,34 +170,23 @@ def walk_datetime_in_decrementing_steps( :param step: step in minutes :param timezone: String """ + stress = self.context.stress + secs = f"00{'.' * (precision > 0)}{'0' * precision}" - with Pool(2) as pool: - try: - with When( - f"I loop through datetime range {hrs_range} starting from {date} in {step}min decrements" - ): - for hrs in ( - range(*hrs_range, -1) if stress else (hrs_range[1], hrs_range[0]) - ): - for mins in range(59, 0, -step) if stress else (59, 0): - datetime = ( - f"{date} {str(hrs).zfill(2)}:{str(mins).zfill(2)}:{secs}" - ) - expected = datetime - - with When(f"time is {datetime}"): - Test( - name=f"{hrs}:{mins}:{secs}", - test=select_check_datetime, - parallel=True, - executor=pool, - )( - datetime=datetime, - precision=precision, - timezone=timezone, - expected=expected, - ) - finally: - join() + with When( + f"I loop through datetime range {hrs_range} starting from {date} in {step}min decrements" + ): + for hrs in range(*hrs_range, -1) if stress else (hrs_range[1], hrs_range[0]): + for mins in range(59, 0, -step) if stress else (59, 0): + datetime = f"{date} {str(hrs).zfill(2)}:{str(mins).zfill(2)}:{secs}" + expected = datetime + + with When(f"time is {datetime}"): + select_check_datetime( + datetime=datetime, + precision=precision, + timezone=timezone, + expected=expected, + ) diff --git a/tests/testflows/datetime64_extended_range/tests/date_time_functions.py b/tests/testflows/datetime64_extended_range/tests/date_time_functions.py index 53add63e8f2b..9d743e998857 100644 --- a/tests/testflows/datetime64_extended_range/tests/date_time_functions.py +++ b/tests/testflows/datetime64_extended_range/tests/date_time_functions.py @@ -2,8 +2,9 @@ import pytz import itertools from testflows.core import * +from dateutil.tz import tzlocal import dateutil.relativedelta as rd -from datetime import datetime +from datetime import datetime, timedelta from datetime64_extended_range.requirements.requirements import * from datetime64_extended_range.common import * @@ -20,7 +21,7 @@ def to_time_zone(self): timezones = timezones_range(stress) for year in years_range(stress): - with Given(f"I select datetimes in {year}"): + with Given("I select datetimes in a year"): datetimes = select_dates_in_year(year=year, stress=stress) for dt in datetimes: @@ -47,7 +48,7 @@ def to_date_part(self, py_func, ch_func): datetimes = select_dates_in_year(year=year, stress=stress) timezones = timezones_range(stress) - with When(f"I check each of the datetimes in {year}"): + with When("I check each of the datetimes"): for dt in datetimes: for tz1, tz2 in itertools.product(timezones, timezones): with Step(f"{dt} {tz1}, {tz2}"): @@ -113,7 +114,7 @@ def to_day_of(self, py_func, ch_func): datetimes = select_dates_in_year(year=year, stress=stress) timezones = timezones_range(stress) - with When(f"I check each of the datetimes in {year}"): + with When("I check each of the datetimes"): for dt in datetimes: for tz1, tz2 in itertools.product(timezones, timezones): with When(f"{dt} {tz1} -> {tz2}"): @@ -168,7 +169,7 @@ def to_time_part(self, py_func, ch_func): datetimes = select_dates_in_year(year=year, stress=stress) timezones = timezones_range(stress) - with When(f"I check each of the datetimes in {year}"): + with When("I check each of the datetimes"): for dt in datetimes: for tz1, tz2 in itertools.product(timezones, timezones): with Step(f"{dt} {tz1} -> {tz2}"): @@ -222,7 +223,7 @@ def to_unix_timestamp(self): datetimes = select_dates_in_year(year=year, stress=stress) timezones = timezones_range(stress) - with When(f"I check each of the datetimes in {year}"): + with When("I check each of the datetimes"): for dt in datetimes: for tz in timezones: with Step(f"{dt} {tz}"): @@ -248,7 +249,7 @@ def to_start_of_year(self): with Given(f"I choose datetimes in {year}"): datetimes = select_dates_in_year(year=year, stress=stress) - with When(f"I check each of the datetimes in {year}"): + with When("I check each of the datetimes"): for dt in datetimes: for tz1, tz2 in itertools.product(timezones, timezones): with Step(f"{dt} {tz1} -> {tz2}"): @@ -284,7 +285,7 @@ def to_start_of_iso_year(self): with Given(f"I choose datetimes in {year}"): datetimes = select_dates_in_year(year=year, stress=stress) - with When(f"I check each of the datetimes in {year}"): + with When("I check each of the datetimes"): for dt in datetimes: for tz in timezones: with Step(f"{dt} {tz}"): @@ -314,8 +315,7 @@ def to_start_of_quarter(self): for year in years_range(stress): with Given(f"I choose datetimes in {year}"): datetimes = select_dates_in_year(year=year, stress=stress) - - with When(f"I check each of the datetimes in {year}"): + with When("I check each of the datetimes"): for dt in datetimes: for tz1, tz2 in itertools.product(timezones, timezones): with Step(f"{dt} {tz1} -> {tz2}"): @@ -346,8 +346,7 @@ def to_start_of_month(self): for year in years_range(stress=stress): with Given(f"I choose datetimes in {year}"): datetimes = select_dates_in_year(year=year, stress=stress) - - with When(f"I check each of the datetimes in {year}"): + with When("I check each of the datetimes"): for dt in datetimes: for tz1, tz2 in itertools.product(timezones, timezones): with Step(f"{dt} {tz1} -> {tz2}"): @@ -376,8 +375,7 @@ def to_monday(self): for year in years_range(stress): with Given(f"I choose datetimes in {year}"): datetimes = select_dates_in_year(year=year, stress=stress) - - with When(f"I check each of the datetimes in {year}"): + with When("I check each of the datetimes"): for dt in datetimes: for tz1, tz2 in itertools.product(timezones, timezones): with Step(f"{dt} {tz1} -> {tz2}"): @@ -411,8 +409,7 @@ def to_start_of_week(self): for year in years_range(stress): with Given(f"I choose datetimes in {year}"): datetimes = select_dates_in_year(year=year, stress=stress) - - with When(f"I check each of the datetimes in {year}"): + with When("I check each of the datetimes"): for dt in datetimes: for tz1, tz2 in itertools.product(timezones, timezones): for mode in ( @@ -450,8 +447,7 @@ def to_start_of_day(self): for year in years_range(stress): with Given(f"I choose datetimes in {year}"): datetimes = select_dates_in_year(year=year, stress=stress) - - with When(f"I check each of the datetimes in {year}"): + with When("I check each of the datetimes"): for dt in datetimes: for tz in timezones: with Step(f"{dt} {tz}"): @@ -477,7 +473,7 @@ def to_start_of_hour(self): with Given(f"I choose datetimes in {year}"): datetimes = select_dates_in_year(year=year, stress=stress) - with When(f"I check each of the datetimes in {year}"): + with When("I check each of the datetimes"): for dt in datetimes: for tz in timezones: with Step(f"{dt} {tz}"): @@ -503,7 +499,7 @@ def to_start_of_minute(self): with Given(f"I choose datetimes in {year}"): datetimes = select_dates_in_year(year=year, stress=stress) - with When(f"I check each of the datetimes in {year}"): + with When("I check each of the datetimes"): for dt in datetimes: for tz in timezones: with Step(f"{dt} {tz}"): @@ -531,7 +527,7 @@ def to_start_of_second(self): year=year, stress=stress, microseconds=True ) - with When(f"I check each of the datetimes in {year}"): + with When("I check each of the datetimes"): for dt in datetimes: for tz in timezones: with Step(f"{dt} {tz}"): @@ -556,7 +552,7 @@ def to_start_of_minutes_interval(self, interval, func): year=year, stress=stress, microseconds=True ) - with When(f"I check each of the datetimes in {year}"): + with When("I check each of the datetimes"): for dt in datetimes: for tz in timezones: with Step(f"{dt} {tz}"): @@ -689,7 +685,7 @@ def to_start_of_interval(self): with Given(f"I choose datetimes in {year}"): datetimes = select_dates_in_year(year=year, stress=stress) - with When(f"I check each of the datetimes in {year}"): + with When("I check each of the datetimes"): for dt in datetimes: for tz in timezones: for interval in intervals_testing_ranges.keys(): @@ -720,7 +716,7 @@ def to_iso(self, func, isocalendar_pos): year=year, stress=stress, microseconds=True ) - with When(f"I check each of the datetimes in {year}"): + with When("I check each of the datetimes"): for dt in datetimes: for tz in timezones: with Step(f"{dt} {tz}"): @@ -766,7 +762,7 @@ def to_time(self): year=year, stress=stress, microseconds=True ) - with When(f"I check each of the datetimes in {year}"): + with When("I check each of the datetimes"): for dt in datetimes: for tz in timezones: with When(f"{dt} {tz}"): @@ -798,7 +794,7 @@ def to_relative_quarter_num(self): year=year, stress=stress, microseconds=True ) - with When(f"I check each of the datetimes in {year}"): + with When("I check each of the datetimes"): for dt in datetimes: for tz in timezones: with When(f"{dt} {tz}"): @@ -825,8 +821,7 @@ def to_relative_week_num(self): datetimes = select_dates_in_year( year=year, stress=stress, microseconds=True ) - - with When(f"I check each of the datetimes in {year}"): + with When("I check each of the datetimes"): for dt in datetimes: for tz in timezones: with When(f"{dt} {tz}"): @@ -862,7 +857,7 @@ def to_relative_month_num(self): year=year, stress=stress, microseconds=True ) - with When(f"I check each of the datetimes in {year}"): + with When("I check each of the datetimes"): for dt in datetimes: for tz in timezones: with When(f"{dt} {tz}"): @@ -891,7 +886,7 @@ def to_relative_day_num(self): year=year, stress=stress, microseconds=True ) - with When(f"I check each of the datetimes in {year}"): + with When("I check each of the datetimes"): for dt in datetimes: for tz in timezones: with When(f"{dt} {tz}"): @@ -919,7 +914,7 @@ def to_relative_time(self, divisor, func): year=year, stress=stress, microseconds=True ) - with When(f"I check each of the datetimes in {year}"): + with When("I check each of the datetimes"): for dt in datetimes: for tz in timezones: with When(f"{dt} {tz}"): @@ -1113,7 +1108,7 @@ def to_week_year_week(self, clh_func, ret_year): with Given(f"I choose datetimes in {year}"): datetimes = select_dates_in_year(year=year, stress=stress) - with When(f"I check each of the datetimes in {year}"): + with When("I check each of the datetimes"): for dt in datetimes: for tz in timezones: for mode in range(0, 10): @@ -1160,7 +1155,7 @@ def to_yyyymm(self): with Given(f"I choose datetimes in {year}"): datetimes = select_dates_in_year(year=year, stress=stress) - with When(f"I check each of the datetimes in {year}"): + with When("I check each of the datetimes"): for dt in datetimes: for tz in timezones: with Step(f"{dt} {tz}"): @@ -1190,7 +1185,7 @@ def to_yyyymmdd(self): with Given(f"I choose datetimes in {year}"): datetimes = select_dates_in_year(year=year, stress=stress) - with When(f"I check each of the datetimes in {year}"): + with When("I check each of the datetimes"): for dt in datetimes: for tz in timezones: with Step(f"{dt} {tz}"): @@ -1222,7 +1217,7 @@ def to_yyyymmddhhmmss(self): with Given(f"I choose datetimes in {year}"): datetimes = select_dates_in_year(year=year, stress=stress) - with When(f"I check each of the datetimes in {year}"): + with When("I check each of the datetimes"): for dt in datetimes: for tz in timezones: with Step(f"{dt} {tz}"): @@ -1246,11 +1241,11 @@ def now(self): timezones = timezones_range(stress) for tz in timezones: - with Given(f"I record current time and localize it for {tz}"): - dt = datetime.datetime.now() + with Given("I record current time and localize it"): + dt = datetime.datetime.now(tzlocal()) dt = dt.astimezone(pytz.timezone(tz)) - with Step(f"{dt} in {tz}"): + with Step(f"{dt} {tz}"): with When("I execute query and format its result to string"): r = self.context.node.query(f"SELECT toDateTime64(now(), 0, '{tz}')") query_result = r.output @@ -1279,8 +1274,8 @@ def today(self): timezones = timezones_range(stress) for tz in timezones: - with Given(f"I record current time and localize it for {tz}"): - dt = datetime.datetime.now() + with Given("I record current time and localize it"): + dt = datetime.datetime.now(tzlocal()) dt = dt.astimezone(pytz.timezone(tz)) with Step(f"{dt} {tz}"): @@ -1300,8 +1295,8 @@ def today(self): else: diff = (dt - received_dt).total_seconds() - with Finally(f"I expect {diff} < 24 hours"): - assert diff < 86400, error() + with Finally(f"I expect {diff} < 48 hours (due to timezone mismatch)"): + assert diff < 172800, error() @TestScenario @@ -1315,8 +1310,8 @@ def yesterday(self): timezones = timezones_range(stress) for tz in timezones: - with Given(f"I record current time and localize it for {tz}"): - dt = datetime.datetime.now() + with Given("I record current time and localize it"): + dt = datetime.datetime.now(tzlocal()) dt = dt.astimezone(pytz.timezone(tz)) with Step(f"{dt} {tz}"): @@ -1364,7 +1359,7 @@ def add_subtract_functions( with Given(f"I choose datetimes in {year}"): datetimes = select_dates_in_year(year=year, stress=stress) - with When(f"I check each of the datetimes in {year}"): + with When("I check each of the datetimes"): for dt in datetimes: for tz in timezones: for incr in test_range: @@ -1686,7 +1681,7 @@ def format_date_time(self): with Given(f"I choose datetimes in {year}"): datetimes = select_dates_in_year(year=year, stress=stress) - with When(f"I format the datetimes in {year} in every possible way"): + with And("I format the datetimes in every possible way"): for dt in datetimes: for tz in timezones: for mode in modes: @@ -1741,8 +1736,7 @@ def time_slots(self): for year in years_range(stress=stress): with Given(f"I choose datetimes in {year}"): datetimes = select_dates_in_year(year=year, stress=stress) - - with When(f"I check each of the datetimes in {year}"): + with When("I check each of the datetimes"): for dt in datetimes: for duration in range(1, 100, 9): for size in range(1, 50, 3): @@ -1770,9 +1764,5 @@ def date_time_funcs(self, node="clickhouse1"): """Check the basic operations with DateTime64""" self.context.node = self.context.cluster.node(node) - with Pool(4) as pool: - try: - for scenario in loads(current_module(), Scenario): - Scenario(run=scenario, parallel=True, executor=pool) - finally: - join() + for scenario in loads(current_module(), Scenario): + Scenario(run=scenario, flags=TE) diff --git a/tests/testflows/datetime64_extended_range/tests/format_conversion.py b/tests/testflows/datetime64_extended_range/tests/format_conversion.py new file mode 100644 index 000000000000..6363b92b3a0f --- /dev/null +++ b/tests/testflows/datetime64_extended_range/tests/format_conversion.py @@ -0,0 +1,120 @@ +import os +import time +import pytz +import decimal +import pyarrow +from dateutil.tz import tzlocal +from datetime import datetime, timedelta +import dateutil.relativedelta as rd +from testflows.core import * + +from datetime64_extended_range.requirements.requirements import * +from datetime64_extended_range.common import * +from datetime64_extended_range.tests.common import * + + +@TestScenario +@Requirements(RQ_SRS_010_DateTime64_ExtendedRange_ArrowFormat("1.0")) +def arrow_format(self): + """Check that DateTime64 is properly represented in Arrow format.""" + node = self.context.node + stress = self.context.stress + timezones = timezones_range(stress=stress) + + try: + for year in years_range(stress): + with Given("I select datetimes in a year"): + datetimes = select_dates_in_year(year=year, stress=stress) + + for dt in datetimes: + for tz in timezones: + for precision in (0, 3, 6): + with Step(f"{dt} {tz}"): + with Given("I drop remaining tables"): + exec_query(request="DROP TABLE IF EXISTS t_src SYNC") + exec_query(request="DROP TABLE IF EXISTS t_dst SYNC") + + dt_str = f'{dt.strftime("%Y-%m-%d %H:%M:%S")}.123456789'[ + : (precision - 9) + ] + if precision == 0: + dt_str = dt.strftime("%Y-%m-%d %H:%M:%S") + + with Check("reading and writing one-line arrow file"): + with When("I create .arrow file"): + query = f"SELECT toDateTime64('{dt_str}', {precision}, '{tz}') FORMAT Arrow" + node.cmd( + cmd=f'clickhouse client --query="{query}" > /var/lib/ch-files/a.arrow' + ) + + with When("I check recorded values"): + data_dict = ( + pyarrow.ipc.open_file( + os.path.join( + os.path.dirname( + os.path.dirname(__file__) + ), + "_instances/clickhouse1/files/a.arrow", + ) + ) + .read_all() + .to_pydict() + ) + + assert dt_str in list(data_dict.values())[0][ + 0 + ].strftime("%Y-%m-%d %H:%M:%S.%f"), error() + + with Check("DT64 -> DT conversion on insert works"): + with Given( + "I create tables to generate and paste values" + ): + exec_query( + request=f"CREATE TABLE t_src " + f"(id Int8, d DateTime64({precision}, '{tz}')) " + f"ENGINE=MergeTree() ORDER BY id" + ) + exec_query( + request=f"CREATE TABLE t_dst " + f"(id Int8, d DateTime('{tz}')) " + f"ENGINE=MergeTree() ORDER BY id" + ) + + with When( + "I insert value into source table and dump it" + ): + exec_query( + request=f"INSERT INTO t_src VALUES (1, toDateTime64('{dt_str}', {precision}, '{tz}'))" + ) + node.cmd( + cmd=f'clickhouse client --query="SELECT * FROM t_src FORMAT Arrow" > /var/lib/ch-files/a.arrow' + ) + + with Then( + "I read file into table with implicit DT conversion" + ): + node.cmd( + cmd=f"cat /var/lib/ch-files/a.arrow | " + f'clickhouse client --query="INSERT INTO t_dst FORMAT Arrow"', + exitcode=0, + ) + + if in_normal_range(dt): + with And("I check pasted DateTime is correct"): + assert ( + dt.strftime("%Y-%m-%d %H:%M:%S") + in node.query(f"SELECT * FROM t_dst").output + ), error() + + finally: + exec_query(request="DROP TABLE IF EXISTS t SYNC") + node.cmd("rm /var/lib/ch-files/a.arrow") + + +@TestFeature +def format_conversion(self, node="clickhouse1"): + """Check that DateTime64 is properly represented in different formats.""" + self.context.node = self.context.cluster.node(node) + + for scenario in loads(current_module(), Scenario): + Scenario(run=scenario, flags=TE) diff --git a/tests/testflows/datetime64_extended_range/tests/generic.py b/tests/testflows/datetime64_extended_range/tests/generic.py index 9ac2975e5a23..b63f6d9c0b09 100644 --- a/tests/testflows/datetime64_extended_range/tests/generic.py +++ b/tests/testflows/datetime64_extended_range/tests/generic.py @@ -5,6 +5,7 @@ from datetime64_extended_range.tests.common import * import pytz +import datetime import itertools @@ -62,7 +63,7 @@ def extended_range_end(self, precision=3): ) -@TestScenario +@TestOutline(Scenario) @Requirements( RQ_SRS_010_DateTime64_ExtendedRange_NormalRange_Start_BeforeEpochForTimeZone("1.0") ) @@ -79,7 +80,7 @@ def timezone_local_below_normal_range(self): ) -@TestScenario +@TestOutline(Scenario) @Requirements( RQ_SRS_010_DateTime64_ExtendedRange_NormalRange_End_AfterEpochForTimeZone("1.0") ) @@ -146,10 +147,20 @@ def timezones_support(self): exec_query(request=query, expected=f"{dt_str}") +@TestScenario +@Requirements(RQ_SRS_010_DateTime64_ExtendedRange_Transform("1.0")) +def transform(self): + """Simple check that transform can accept DateTime64 as argument.""" + with When("forming a toTimeZone ClickHouse query"): + query = "SELECT transform('a', ['a', 'b'], [toDateTime64(0, 3, 'UTC'), toDateTime(1, 3, 'UTC')], toDateTime64(0,3))" + with Then(f"I execute query", flags=TE): + exec_query(request=query, expected="1970-01-01 00:00:00.000") + + @TestFeature def generic(self, node="clickhouse1"): """Check the basic operations with DateTime64""" self.context.node = self.context.cluster.node(node) for scenario in loads(current_module(), Scenario, Suite): - Scenario(run=scenario) + Scenario(run=scenario, flags=TE) diff --git a/tests/testflows/datetime64_extended_range/tests/non_existent_time.py b/tests/testflows/datetime64_extended_range/tests/non_existent_time.py index 0e3e180fe23a..8f8cf678afe7 100644 --- a/tests/testflows/datetime64_extended_range/tests/non_existent_time.py +++ b/tests/testflows/datetime64_extended_range/tests/non_existent_time.py @@ -198,4 +198,4 @@ def feature(self, node="clickhouse1"): self.context.node = self.context.cluster.node(node) for scenario in loads(current_module(), Scenario, Suite): - Scenario(run=scenario) + Scenario(run=scenario, flags=TE) diff --git a/tests/testflows/datetime64_extended_range/tests/reference_times.py b/tests/testflows/datetime64_extended_range/tests/reference_times.py index 9cd9fadc35cd..aa456d950796 100644 --- a/tests/testflows/datetime64_extended_range/tests/reference_times.py +++ b/tests/testflows/datetime64_extended_range/tests/reference_times.py @@ -3,6 +3,7 @@ from testflows.core import * from datetime64_extended_range.common import * +from datetime64_extended_range.tests.common import select_check_datetime from datetime64_extended_range.requirements.requirements import * from datetime64_extended_range.tests.common import * diff --git a/tests/testflows/datetime64_extended_range/tests/type_conversion.py b/tests/testflows/datetime64_extended_range/tests/type_conversion.py index c52ecdce5821..a1b32cb10bf5 100644 --- a/tests/testflows/datetime64_extended_range/tests/type_conversion.py +++ b/tests/testflows/datetime64_extended_range/tests/type_conversion.py @@ -41,7 +41,7 @@ def to_int_8_16_32_64_128_256(self, cast): timezones = timezones_range(stress) for year in years_range(stress): - with Given(f"I select datetimes in {year}"): + with Given("I select datetimes in a year"): datetimes = select_dates_in_year(year=year, stress=stress) for d in datetimes: @@ -100,7 +100,7 @@ def to_uint_8_16_32_64_256(self, cast): timezones = timezones_range(stress) for year in years_range(stress): - with Given(f"I select datetimes in {year}"): + with Given("I select datetimes in a year"): datetimes = select_dates_in_year(year=year, stress=stress) for d in datetimes: @@ -159,7 +159,7 @@ def to_float_32_64(self, cast): timezones = timezones_range(stress) for year in years_range(stress): - with Given(f"I select datetimes in {year}"): + with Given("I select datetimes in a year"): datetimes = select_dates_in_year(year=year, stress=stress) for d in datetimes: @@ -195,7 +195,7 @@ def to_datetime64_from_string_missing_time(self): timezones = timezones_range(stress) for year in years_range(stress): - with Given(f"I select datetimes in {year}"): + with Given("I select datetimes in a year"): datetimes = select_dates_in_year(year=year, stress=stress) for dt in datetimes: @@ -221,7 +221,7 @@ def to_datetime64(self): timezones = timezones_range(stress) for year in years_range(stress): - with Given(f"I select datetimes in {year}"): + with Given("I select datetimes in a year"): datetimes = select_dates_in_year(year=year, stress=stress) for dt in datetimes: @@ -264,7 +264,7 @@ def to_date(self, cast): timezones = timezones_range(stress) for year in years_range(stress): - with Given(f"I select datetimes in {year}"): + with Given("I select datetimes in a year"): datetimes = select_dates_in_year(year=year, stress=stress) for dt in datetimes: @@ -290,6 +290,60 @@ def to_date(self, cast): exec_query(request=query, expected=expected, exitcode=0) +@TestOutline(Scenario) +@Examples( + "cast", + [ + ( + False, + Requirements( + RQ_SRS_010_DateTime64_ExtendedRange_TypeConversionFunctions_toDate32( + "1.0" + ) + ), + ), + ( + True, + Requirements( + RQ_SRS_010_DateTime64_ExtendedRange_TypeConversionFunctions_CAST_x_T_( + "1.0" + ) + ), + ), + ], +) +def to_date32(self, cast): + """Check the toDate32() conversion with DateTime64.""" + stress = self.context.stress + timezones = timezones_range(stress) + + for year in years_range(stress): + with Given("I select datetimes in a year"): + datetimes = select_dates_in_year(year=year, stress=stress) + + for dt in datetimes: + for tz in timezones: + with Step(f"{dt} {tz}"): + expected = None # by default - not checked, checking the exitcode + with By("converting datetime to string"): + dt_str = dt.strftime("%Y-%m-%d %H:%M:%S") + + if in_normal_range(dt): + with And("DateTime64 fits normal range, change its value"): + expected = f"{dt.strftime('%Y-%m-%d')}" + + with Given(f"I make a query string for ClickHouse"): + if cast: + query = f"SELECT CAST(toDateTime64('{dt_str}', 0, '{tz}'), 'Date32')" + else: + query = ( + f"SELECT toDate32(toDateTime64('{dt_str}', 0, '{tz}'))" + ) + + with Then(f"I execute toDate32() query and check return/exitcode"): + exec_query(request=query, expected=expected, exitcode=0) + + @TestOutline(Scenario) @Examples( "cast", @@ -318,7 +372,7 @@ def to_datetime(self, cast): timezones = timezones_range(stress) for year in years_range(stress): - with Given(f"I select datetimes in {year}"): + with Given("I select datetimes in a year"): datetimes = select_dates_in_year(year=year, stress=stress) for dt in datetimes: @@ -327,15 +381,13 @@ def to_datetime(self, cast): dt_str = dt.strftime("%Y-%m-%d %H:%M:%S") with And(f"making a query string for ClickHouse"): if cast: - query = f"SELECT CAST(toDateTime64('{dt_str}', 0, '{tz}'), 'DateTime')" + query = f"SELECT CAST(toDateTime('{dt_str}', 0, '{tz}'), 'DateTime')" with When("figure out expected result in python"): dt_local = pytz.timezone(tz).localize(dt) dt_transformed = dt_local.astimezone(tzlocal()) expected = f"{dt_transformed.strftime('%Y-%m-%d %H:%M:%S')}" else: - query = ( - f"SELECT toDateTime(toDateTime64('{dt_str}', 0, '{tz}'))" - ) + query = f"SELECT toDateTime(toDateTime('{dt_str}', 0, '{tz}'))" with When("figure out expected result in python"): expected = f"{dt.strftime('%Y-%m-%d %H:%M:%S')}" @@ -375,7 +427,7 @@ def to_string(self, cast): timezones = timezones_range(stress) for year in years_range(stress): - with Given(f"I select datetimes in {year}"): + with Given("I select datetimes in a year"): datetimes = select_dates_in_year(year=year, stress=stress) for dt in datetimes: @@ -438,7 +490,7 @@ def to_decimal_32_64_128_256(self, cast): scales = {32: 9, 64: 18, 128: 38, 256: 76} for year in years_range(stress): - with Given(f"I select datetimes in {year}"): + with Given("I select datetimes in a year"): datetimes = select_dates_in_year(year=year, stress=stress) for d in datetimes: @@ -474,7 +526,7 @@ def to_unix_timestamp64_milli_micro_nano(self, scale): func = {3: "Milli", 6: "Micro", 9: "Nano"} for year in years_range(stress): - with Given(f"I select datetimes in {year}"): + with Given("I select datetimes in a year"): datetimes = select_dates_in_year( year=year, stress=stress, microseconds=True ) @@ -542,7 +594,7 @@ def from_unix_timestamp64_milli_micro_nano(self, scale): func = {3: "Milli", 6: "Micro", 9: "Nano"} for year in years_range(stress): - with Given(f"I select datetimes in {year}"): + with Given("I select datetimes in a year"): datetimes = select_dates_in_year( year=year, stress=stress, microseconds=True ) @@ -568,6 +620,82 @@ def from_unix_timestamp64_milli_micro_nano(self, scale): exec_query(request=query, expected=f"{d_str}") +@TestScenario +def force_index_by_date(self): + """Check that when force_index_by_date is enabled, ClickHouse throws an exception if there is a partition key in a table, and it is not used.""" + node = self.context.node + table_name = f"table_{getuid()}" + + try: + with Given("I have a table"): + node.query( + f""" + create table {table_name}(date_time DateTime64(3), x String) + Engine=MergeTree partition by toDate(date_time) order by tuple(); + """ + ) + + with And("I insert some data"): + node.query( + f"insert into {table_name} select toDateTime64('2020-01-01 00:00:00.000',3)+number , '' from numbers(1);" + ) + + with When("I select from the table"): + output = node.query( + f"SELECT count() FROM {table_name} WHERE date_time >= toDateTime64('2020-01-01 00:00:00.000',3) SETTINGS force_index_by_date=1;" + ).output + assert output == "1", error() + + with Then("I check SELECT throws an exception."): + node.query( + f"SELECT count() FROM {table_name} WHERE toDateTime64(date_time,3) >= toDateTime64('2020-01-10 00:00:00.000',3) SETTINGS force_index_by_date=1;", + exitcode=21, + message="DB::Exception: Neither MinMax index by columns (date_time) nor partition expr is used and setting 'force_index_by_date' is set. (INDEX_NOT_USED)", + ) + + finally: + with Finally(f"I drop the table {table_name}"): + node.query(f"DROP TABLE IF EXISTS {table_name}") + + +@TestScenario +def force_primary_key(self): + """Check that when force_primary_key is enabled, ClickHOuse throws an exception if there is primary key in a table, and it is not used.""" + node = self.context.node + table_name = f"table_{getuid()}" + + try: + with Given("I have a table"): + node.query( + f""" + create table {table_name}(date_time DateTime64(3), x String) + Engine=MergeTree partition by toDate(date_time) primary key toDate(date_time); + """ + ) + + with And("I insert some data"): + node.query( + f"insert into {table_name} select toDateTime64('2020-01-01 00:00:00.000',3)+number , '' from numbers(1);" + ) + + with When("I select from the table"): + output = node.query( + f"SELECT count() FROM {table_name} WHERE date_time >= toDateTime64('2020-01-01 00:00:00.000',3) SETTINGS force_primary_key=1;" + ).output + assert output == "1", error() + + with Then("I check SELECT throws an exception."): + node.query( + f"SELECT count() FROM {table_name} WHERE toDateTime64(date_time,3) >= toDateTime64('2020-01-10 00:00:00.000',3) SETTINGS force_primary_key=1;", + exitcode=21, + message="Exception: Primary key (toDate(date_time)) is not used and setting 'force_primary_key' is set. (INDEX_NOT_USED)", + ) + + finally: + with Finally(f"I drop the table {table_name}"): + node.query(f"DROP TABLE IF EXISTS {table_name}") + + @TestScenario @Requirements( RQ_SRS_010_DateTime64_ExtendedRange_TypeConversionFunctions_fromUnixTimestamp64Milli( @@ -604,11 +732,10 @@ def from_unix_timestamp64_nano(self): @TestFeature @Requirements(RQ_SRS_010_DateTime64_ExtendedRange_TypeConversionFunctions("1.0")) def type_conversion(self, node="clickhouse1"): - """Check the type conversion operations with DateTime64. - Cast can be set as Requirement thereby as the module + """Check the type conversion operations with DateTime64. Cast can be set as Requirement thereby as the module tests exactly what CAST does. """ self.context.node = self.context.cluster.node(node) for scenario in loads(current_module(), Scenario): - Scenario(run=scenario) + Scenario(run=scenario, flags=TE) diff --git a/tests/testflows/example/configs/clickhouse/common.xml b/tests/testflows/example/configs/clickhouse/common.xml new file mode 100644 index 000000000000..31fa972199f5 --- /dev/null +++ b/tests/testflows/example/configs/clickhouse/common.xml @@ -0,0 +1,6 @@ + + Europe/Moscow + 0.0.0.0 + /var/lib/clickhouse/ + /var/lib/clickhouse/tmp/ + diff --git a/tests/testflows/example/configs/clickhouse/config.xml b/tests/testflows/example/configs/clickhouse/config.xml new file mode 100644 index 000000000000..9854f9f990e1 --- /dev/null +++ b/tests/testflows/example/configs/clickhouse/config.xml @@ -0,0 +1,436 @@ + + + + + + trace + /var/log/clickhouse-server/clickhouse-server.log + /var/log/clickhouse-server/clickhouse-server.err.log + 1000M + 10 + + + + 8123 + 9000 + + + + + + + + + /etc/clickhouse-server/server.crt + /etc/clickhouse-server/server.key + + /etc/clickhouse-server/dhparam.pem + none + true + true + sslv2,sslv3 + true + + + + true + true + sslv2,sslv3 + true + + + + RejectCertificateHandler + + + + + + + + + 9009 + + + + + + + + + + + + + + + + + + + + 4096 + 3 + + + 100 + + + + + + 8589934592 + + + 5368709120 + + + + /var/lib/clickhouse/ + + + /var/lib/clickhouse/tmp/ + + + /var/lib/clickhouse/user_files/ + + + /var/lib/clickhouse/access/ + + + users.xml + + + default + + + + + + default + + + + + + + + + false + + + + + + + + localhost + 9000 + + + + + + + localhost + 9000 + + + + + localhost + 9000 + + + + + + + localhost + 9440 + 1 + + + + + + + localhost + 9000 + + + + + localhost + 1 + + + + + + + + + + + + + + + + + 3600 + + + + 3600 + + + 60 + + + + + + + + + + system + query_log
+ + toYYYYMM(event_date) + + 7500 +
+ + + + system + trace_log
+ + toYYYYMM(event_date) + 7500 +
+ + + + system + query_thread_log
+ toYYYYMM(event_date) + 7500 +
+ + + + system + part_log
+ toYYYYMM(event_date) + 7500 +
+ + + + + + + + + + + + + + *_dictionary.xml + + + + + + + + + + /clickhouse/task_queue/ddl + + + + + + + + + + + + + + + + click_cost + any + + 0 + 3600 + + + 86400 + 7200 + + + + max + + 0 + 60 + + + 3600 + 300 + + + 86400 + 3600 + + + + + + /var/lib/clickhouse/format_schemas/ + + + +
diff --git a/tests/testflows/example/configs/clickhouse/users.xml b/tests/testflows/example/configs/clickhouse/users.xml new file mode 100644 index 000000000000..c7d0ecae6931 --- /dev/null +++ b/tests/testflows/example/configs/clickhouse/users.xml @@ -0,0 +1,133 @@ + + + + + + + + 10000000000 + + + 0 + + + random + + + + + 1 + + + + + + + + + + + + + ::/0 + + + + default + + + default + + + 1 + + + + + + + + + + + + + + + + + 3600 + + + 0 + 0 + 0 + 0 + 0 + + + + diff --git a/tests/testflows/example/example_env/clickhouse-service.yml b/tests/testflows/example/example_env/clickhouse-service.yml index 74a56b63aabc..7a3be6281f74 100644 --- a/tests/testflows/example/example_env/clickhouse-service.yml +++ b/tests/testflows/example/example_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: altinityinfra/integration-test + image: ${IMAGE_DEPENDENCY_PROXY}clickhouse/clickhouse-integration-test:28741 expose: - "9000" - "9009" diff --git a/tests/testflows/example/example_env/docker-compose.yml b/tests/testflows/example/example_env/docker-compose.yml index 4edb415824fb..0d2aa7a1ac7f 100644 --- a/tests/testflows/example/example_env/docker-compose.yml +++ b/tests/testflows/example/example_env/docker-compose.yml @@ -23,7 +23,7 @@ services: # dummy service which does nothing, but allows to postpone # 'docker-compose up -d' till all dependecies will go healthy all_services_ready: - image: hello-world + image: ${IMAGE_DEPENDENCY_PROXY}hello-world depends_on: clickhouse1: condition: service_healthy diff --git a/tests/testflows/example/example_env/zookeeper-service.yml b/tests/testflows/example/example_env/zookeeper-service.yml index ca732a48dbd7..33cc799ab4cb 100644 --- a/tests/testflows/example/example_env/zookeeper-service.yml +++ b/tests/testflows/example/example_env/zookeeper-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: zookeeper: - image: zookeeper:3.6.2 + image: ${IMAGE_DEPENDENCY_PROXY}zookeeper:3.6.2 expose: - "2181" environment: diff --git a/tests/testflows/example/example_env_arm64/clickhouse-service.yml b/tests/testflows/example/example_env_arm64/clickhouse-service.yml index a73d31421c87..cd7284359aa3 100644 --- a/tests/testflows/example/example_env_arm64/clickhouse-service.yml +++ b/tests/testflows/example/example_env_arm64/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: registry.gitlab.com/altinity-public/container-images/test/clickhouse-integration-test:21.12 + image: registry.gitlab.com/altinity-public/container-images/test/clickhouse-integration-test:8.9.2022 privileged: true expose: - "9000" diff --git a/tests/testflows/example/example_env_arm64/docker-compose.yml b/tests/testflows/example/example_env_arm64/docker-compose.yml index 4edb415824fb..0d2aa7a1ac7f 100644 --- a/tests/testflows/example/example_env_arm64/docker-compose.yml +++ b/tests/testflows/example/example_env_arm64/docker-compose.yml @@ -23,7 +23,7 @@ services: # dummy service which does nothing, but allows to postpone # 'docker-compose up -d' till all dependecies will go healthy all_services_ready: - image: hello-world + image: ${IMAGE_DEPENDENCY_PROXY}hello-world depends_on: clickhouse1: condition: service_healthy diff --git a/tests/testflows/example/example_env_arm64/zookeeper-service.yml b/tests/testflows/example/example_env_arm64/zookeeper-service.yml index ca732a48dbd7..9934af01b828 100644 --- a/tests/testflows/example/example_env_arm64/zookeeper-service.yml +++ b/tests/testflows/example/example_env_arm64/zookeeper-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: zookeeper: - image: zookeeper:3.6.2 + image: ${IMAGE_DEPENDENCY_PROXY}arm64v8/zookeeper:3.6.2 expose: - "2181" environment: diff --git a/tests/testflows/extended_precision_data_types/common.py b/tests/testflows/extended_precision_data_types/common.py index 959ff96a536f..db85aba44f84 100644 --- a/tests/testflows/extended_precision_data_types/common.py +++ b/tests/testflows/extended_precision_data_types/common.py @@ -80,9 +80,17 @@ def execute_query( else: with Then("I check output against snapshot"): with values() as that: + snapshot_name = ( + "tests.pre22.3" + if check_clickhouse_version("<22.3")(current()) + else "tests.post22.3" + ) assert that( snapshot( - "\n" + r.output.strip() + "\n", "tests", name=name, encoder=str + "\n" + r.output.strip() + "\n", + snapshot_name, + name=name, + encoder=str, ) ), error() diff --git a/tests/testflows/extended_precision_data_types/configs/clickhouse/common.xml b/tests/testflows/extended_precision_data_types/configs/clickhouse/common.xml new file mode 100644 index 000000000000..0ba01589b908 --- /dev/null +++ b/tests/testflows/extended_precision_data_types/configs/clickhouse/common.xml @@ -0,0 +1,6 @@ + + Europe/Moscow + :: + /var/lib/clickhouse/ + /var/lib/clickhouse/tmp/ + diff --git a/tests/testflows/extended_precision_data_types/configs/clickhouse/config.xml b/tests/testflows/extended_precision_data_types/configs/clickhouse/config.xml new file mode 100644 index 000000000000..842a0573d49b --- /dev/null +++ b/tests/testflows/extended_precision_data_types/configs/clickhouse/config.xml @@ -0,0 +1,448 @@ + + + + + + trace + /var/log/clickhouse-server/clickhouse-server.log + /var/log/clickhouse-server/clickhouse-server.err.log + 1000M + 10 + + + + 8123 + 9000 + + + + + + + + + /etc/clickhouse-server/server.crt + /etc/clickhouse-server/server.key + + /etc/clickhouse-server/dhparam.pem + none + true + true + sslv2,sslv3 + true + + + + true + true + sslv2,sslv3 + true + + + + RejectCertificateHandler + + + + + + + + + 9009 + + + + + + + + 0.0.0.0 + + + + + + + + + + + + 4096 + 3 + + + 100 + + + + + + 8589934592 + + + 5368709120 + + + + /var/lib/clickhouse/ + + + /var/lib/clickhouse/tmp/ + + + /var/lib/clickhouse/user_files/ + + + /var/lib/clickhouse/access/ + + + + + + users.xml + + + + /var/lib/clickhouse/access/ + + + + + users.xml + + + default + + + + + + default + + + + + + + + + false + + + + + + + + localhost + 9000 + + + + + + + localhost + 9000 + + + + + localhost + 9000 + + + + + + + localhost + 9440 + 1 + + + + + + + localhost + 9000 + + + + + localhost + 1 + + + + + + + + + + + + + + + + + 3600 + + + + 3600 + + + 60 + + + + + + + + + + system + query_log
+ + toYYYYMM(event_date) + + 7500 +
+ + + + system + trace_log
+ + toYYYYMM(event_date) + 7500 +
+ + + + system + query_thread_log
+ toYYYYMM(event_date) + 7500 +
+ + + + system + part_log
+ toYYYYMM(event_date) + 7500 +
+ + + + + + + + + + + + + + *_dictionary.xml + + + + + + + + + + /clickhouse/task_queue/ddl + + + + + + + + + + + + + + + + click_cost + any + + 0 + 3600 + + + 86400 + 60 + + + + max + + 0 + 60 + + + 3600 + 300 + + + 86400 + 3600 + + + + + + /var/lib/clickhouse/format_schemas/ + + + +
diff --git a/tests/testflows/extended_precision_data_types/configs/clickhouse/users.xml b/tests/testflows/extended_precision_data_types/configs/clickhouse/users.xml new file mode 100644 index 000000000000..c7d0ecae6931 --- /dev/null +++ b/tests/testflows/extended_precision_data_types/configs/clickhouse/users.xml @@ -0,0 +1,133 @@ + + + + + + + + 10000000000 + + + 0 + + + random + + + + + 1 + + + + + + + + + + + + + ::/0 + + + + default + + + default + + + 1 + + + + + + + + + + + + + + + + + 3600 + + + 0 + 0 + 0 + 0 + 0 + + + + diff --git a/tests/testflows/extended_precision_data_types/extended_precision_data_types_env/clickhouse-service.yml b/tests/testflows/extended_precision_data_types/extended_precision_data_types_env/clickhouse-service.yml new file mode 100644 index 000000000000..41647b89aeb7 --- /dev/null +++ b/tests/testflows/extended_precision_data_types/extended_precision_data_types_env/clickhouse-service.yml @@ -0,0 +1,28 @@ +version: '2.3' + +services: + clickhouse: + image: ${IMAGE_DEPENDENCY_PROXY}clickhouse/clickhouse-integration-test:28741 + init: true + expose: + - "9000" + - "9009" + - "8123" + volumes: + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/logs.xml:/etc/clickhouse-server/config.d/logs.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/storage.xml:/etc/clickhouse-server/config.d/storage.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.xml:/etc/clickhouse-server/config.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/users.xml:/etc/clickhouse-server/users.xml" + - "${CLICKHOUSE_TESTS_SERVER_BIN_PATH:-/usr/bin/clickhouse}:/usr/bin/clickhouse" + - "${CLICKHOUSE_TESTS_ODBC_BRIDGE_BIN_PATH:-/usr/bin/clickhouse-odbc-bridge}:/usr/bin/clickhouse-odbc-bridge" + entrypoint: bash -c "tail -f /dev/null" + healthcheck: + test: echo 1 + interval: 10s + timeout: 10s + retries: 3 + start_period: 300s + cap_add: + - SYS_PTRACE + security_opt: + - label:disable diff --git a/tests/testflows/extended_precision_data_types/extended_precision_data_types_env/docker-compose.yml b/tests/testflows/extended_precision_data_types/extended_precision_data_types_env/docker-compose.yml new file mode 100644 index 000000000000..d0071768317b --- /dev/null +++ b/tests/testflows/extended_precision_data_types/extended_precision_data_types_env/docker-compose.yml @@ -0,0 +1,30 @@ +version: '2.3' + +services: + + mysql1: + extends: + file: mysql-service.yml + service: mysql + hostname: mysql1 + volumes: + - "${CLICKHOUSE_TESTS_DIR}/_instances/mysql1/database:/var/lib/mysql" + + clickhouse1: + extends: + file: clickhouse-service.yml + service: clickhouse + hostname: clickhouse1 + volumes: + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse1/database/:/var/lib/clickhouse/" + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse1/logs/:/var/log/clickhouse-server/" + + # dummy service which does nothing, but allows to postpone + # 'docker-compose up -d' till all dependecies will go healthy + all_services_ready: + image: ${IMAGE_DEPENDENCY_PROXY}hello-world + depends_on: + clickhouse1: + condition: service_healthy + mysql1: + condition: service_healthy diff --git a/tests/testflows/extended_precision_data_types/extended_precision_data_types_env/mysql-service.yml b/tests/testflows/extended_precision_data_types/extended_precision_data_types_env/mysql-service.yml new file mode 100644 index 000000000000..5423b0e0dab4 --- /dev/null +++ b/tests/testflows/extended_precision_data_types/extended_precision_data_types_env/mysql-service.yml @@ -0,0 +1,19 @@ +version: '2.3' + +services: + mysql: + image: ${IMAGE_DEPENDENCY_PROXY}mysql:5.7.30 + restart: always + environment: + MYSQL_DATABASE: 'db' + MYSQL_USER: 'user' + MYSQL_PASSWORD: 'password' + MYSQL_ROOT_PASSWORD: 'password' + expose: + - '3306' + healthcheck: + test: mysql -D db -u user --password=password -e "select 1;" + interval: 3s + timeout: 2s + retries: 40 + start_period: 2s diff --git a/tests/testflows/extended_precision_data_types/extended_precision_data_types_env_arm64/clickhouse-service.yml b/tests/testflows/extended_precision_data_types/extended_precision_data_types_env_arm64/clickhouse-service.yml new file mode 100644 index 000000000000..1a5c52c83d11 --- /dev/null +++ b/tests/testflows/extended_precision_data_types/extended_precision_data_types_env_arm64/clickhouse-service.yml @@ -0,0 +1,28 @@ +version: '2.3' + +services: + clickhouse: + image: registry.gitlab.com/altinity-public/container-images/test/clickhouse-integration-test:21.12 + privileged: true + expose: + - "9000" + - "9009" + - "8123" + volumes: + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/logs.xml:/etc/clickhouse-server/config.d/logs.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/storage.xml:/etc/clickhouse-server/config.d/storage.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.xml:/etc/clickhouse-server/config.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/users.xml:/etc/clickhouse-server/users.xml" + - "${CLICKHOUSE_TESTS_SERVER_BIN_PATH:-/usr/bin/clickhouse}:/usr/bin/clickhouse" + - "${CLICKHOUSE_TESTS_ODBC_BRIDGE_BIN_PATH:-/usr/bin/clickhouse-odbc-bridge}:/usr/bin/clickhouse-odbc-bridge" + entrypoint: bash -c "clickhouse server --config-file=/etc/clickhouse-server/config.xml --log-file=/var/log/clickhouse-server/clickhouse-server.log --errorlog-file=/var/log/clickhouse-server/clickhouse-server.err.log" + healthcheck: + test: clickhouse client --query='select 1' + interval: 10s + timeout: 10s + retries: 3 + start_period: 300s + cap_add: + - SYS_PTRACE + security_opt: + - label:disable diff --git a/tests/testflows/extended_precision_data_types/extended_precision_data_types_env_arm64/docker-compose.yml b/tests/testflows/extended_precision_data_types/extended_precision_data_types_env_arm64/docker-compose.yml new file mode 100644 index 000000000000..d0071768317b --- /dev/null +++ b/tests/testflows/extended_precision_data_types/extended_precision_data_types_env_arm64/docker-compose.yml @@ -0,0 +1,30 @@ +version: '2.3' + +services: + + mysql1: + extends: + file: mysql-service.yml + service: mysql + hostname: mysql1 + volumes: + - "${CLICKHOUSE_TESTS_DIR}/_instances/mysql1/database:/var/lib/mysql" + + clickhouse1: + extends: + file: clickhouse-service.yml + service: clickhouse + hostname: clickhouse1 + volumes: + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse1/database/:/var/lib/clickhouse/" + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse1/logs/:/var/log/clickhouse-server/" + + # dummy service which does nothing, but allows to postpone + # 'docker-compose up -d' till all dependecies will go healthy + all_services_ready: + image: ${IMAGE_DEPENDENCY_PROXY}hello-world + depends_on: + clickhouse1: + condition: service_healthy + mysql1: + condition: service_healthy diff --git a/tests/testflows/extended_precision_data_types/extended_precision_data_types_env_arm64/mysql-service.yml b/tests/testflows/extended_precision_data_types/extended_precision_data_types_env_arm64/mysql-service.yml new file mode 100644 index 000000000000..fe957e7be0f4 --- /dev/null +++ b/tests/testflows/extended_precision_data_types/extended_precision_data_types_env_arm64/mysql-service.yml @@ -0,0 +1,19 @@ +version: '2.3' + +services: + mysql: + image: ${IMAGE_DEPENDENCY_PROXY}mariadb:10.3 + restart: always + environment: + MYSQL_DATABASE: 'db' + MYSQL_USER: 'user' + MYSQL_PASSWORD: 'password' + MYSQL_ROOT_PASSWORD: 'password' + expose: + - '3306' + healthcheck: + test: mysql -D db -u user --password=password -e "select 1;" + interval: 3s + timeout: 2s + retries: 40 + start_period: 2s diff --git a/tests/testflows/extended_precision_data_types/regression.py b/tests/testflows/extended_precision_data_types/regression.py index f185a5e4ecb0..7e289ab20c9d 100755 --- a/tests/testflows/extended_precision_data_types/regression.py +++ b/tests/testflows/extended_precision_data_types/regression.py @@ -24,25 +24,29 @@ @Requirements( RQ_SRS_020_ClickHouse_Extended_Precision("1.0"), ) -def regression( - self, local, clickhouse_binary_path, clickhouse_version=None, stress=None -): +def regression(self, local, clickhouse_binary_path, clickhouse_version, stress=None): """Extended precision data type regression.""" nodes = {"clickhouse": ("clickhouse1",)} - if stress is not None: - self.context.stress = stress + self.context.clickhouse_version = clickhouse_version + from platform import processor as current_cpu + + folder_name = os.path.basename(current_dir()) + if current_cpu() == "aarch64": + env = f"{folder_name}_env_arm64" + else: + env = f"{folder_name}_env" + with Cluster( local, clickhouse_binary_path, nodes=nodes, - docker_compose_project_dir=os.path.join( - current_dir(), "extended-precision-data-type_env" - ), + docker_compose_project_dir=os.path.join(current_dir(), env), ) as cluster: self.context.cluster = cluster + self.context.stress = stress Feature(run=load("extended_precision_data_types.tests.feature", "feature")) diff --git a/tests/testflows/extended_precision_data_types/snapshots/common.py.tests.post22.3.snapshot b/tests/testflows/extended_precision_data_types/snapshots/common.py.tests.post22.3.snapshot new file mode 100644 index 000000000000..95542b61e19c --- /dev/null +++ b/tests/testflows/extended_precision_data_types/snapshots/common.py.tests.post22.3.snapshot @@ -0,0 +1,6334 @@ +I_check_plus_with_Int128_max_and_min_value = r""" +round(plus(toInt128(\'170141183460469231731687303715884105727\'), toInt128(1)), 7) round(plus(toInt128(\'-170141183460469231731687303715884105728\'), toInt128(1)), 7) +-170141183460469231731687303715884105728 -170141183460469231731687303715884105727 +""" + +I_check_plus_with_Int256_max_and_min_value = r""" +round(plus(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), toInt256(1)), 7) round(plus(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\'), toInt256(1)), 7) +-57896044618658097711785492504343953926634992332820282019728792003956564819968 -57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_plus_with_UInt128_max_and_min_value = r""" +round(plus(toUInt128(\'340282366920938463463374607431768211455\'), toUInt128(1)), 7) round(plus(toUInt128(\'0\'), toUInt128(1)), 7) +0 1 +""" + +I_check_plus_with_UInt256_max_and_min_value = r""" +round(plus(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), toUInt256(1)), 7) round(plus(toUInt256(\'0\'), toUInt256(1)), 7) +0 1 +""" + +I_check_minus_with_Int128_max_and_min_value = r""" +round(minus(toInt128(\'170141183460469231731687303715884105727\'), toInt128(1)), 7) round(minus(toInt128(\'-170141183460469231731687303715884105728\'), toInt128(1)), 7) +170141183460469231731687303715884105726 170141183460469231731687303715884105727 +""" + +I_check_minus_with_Int256_max_and_min_value = r""" +round(minus(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), toInt256(1)), 7) round(minus(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\'), toInt256(1)), 7) +57896044618658097711785492504343953926634992332820282019728792003956564819966 57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_minus_with_UInt128_max_and_min_value = r""" +round(minus(toUInt128(\'340282366920938463463374607431768211455\'), toUInt128(1)), 7) round(minus(toUInt128(\'0\'), toUInt128(1)), 7) +-2 -1 +""" + +I_check_minus_with_UInt256_max_and_min_value = r""" +round(minus(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), toUInt256(1)), 7) round(minus(toUInt256(\'0\'), toUInt256(1)), 7) +-2 -1 +""" + +I_check_multiply_with_Int128_max_and_min_value = r""" +round(multiply(toInt128(\'170141183460469231731687303715884105727\'), toInt128(1)), 7) round(multiply(toInt128(\'-170141183460469231731687303715884105728\'), toInt128(1)), 7) +170141183460469231731687303715884105727 -170141183460469231731687303715884105728 +""" + +I_check_multiply_with_Int256_max_and_min_value = r""" +round(multiply(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), toInt256(1)), 7) round(multiply(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\'), toInt256(1)), 7) +57896044618658097711785492504343953926634992332820282019728792003956564819967 -57896044618658097711785492504343953926634992332820282019728792003956564819968 +""" + +I_check_multiply_with_UInt128_max_and_min_value = r""" +round(multiply(toUInt128(\'340282366920938463463374607431768211455\'), toUInt128(1)), 7) round(multiply(toUInt128(\'0\'), toUInt128(1)), 7) +340282366920938463463374607431768211455 0 +""" + +I_check_multiply_with_UInt256_max_and_min_value = r""" +round(multiply(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), toUInt256(1)), 7) round(multiply(toUInt256(\'0\'), toUInt256(1)), 7) +115792089237316195423570985008687907853269984665640564039457584007913129639935 0 +""" + +I_check_divide_with_Int128_max_and_min_value = r""" +round(divide(toInt128(\'170141183460469231731687303715884105727\'), toInt128(1)), 7) round(divide(toInt128(\'-170141183460469231731687303715884105728\'), toInt128(1)), 7) +1.7014118346046923e38 -1.7014118346046923e38 +""" + +I_check_divide_with_Int256_max_and_min_value = r""" +round(divide(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), toInt256(1)), 7) round(divide(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\'), toInt256(1)), 7) +5.78960446186581e76 -5.78960446186581e76 +""" + +I_check_divide_with_UInt128_max_and_min_value = r""" +round(divide(toUInt128(\'340282366920938463463374607431768211455\'), toUInt128(1)), 7) round(divide(toUInt128(\'0\'), toUInt128(1)), 7) +3.402823669209385e38 0 +""" + +I_check_divide_with_UInt256_max_and_min_value = r""" +round(divide(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), toUInt256(1)), 7) round(divide(toUInt256(\'0\'), toUInt256(1)), 7) +1.157920892373162e77 0 +""" + +I_check_intDiv_with_Int128_max_and_min_value = r""" +round(intDiv(toInt128(\'170141183460469231731687303715884105727\'), toInt128(1)), 7) round(intDiv(toInt128(\'-170141183460469231731687303715884105728\'), toInt128(1)), 7) +170141183460469231731687303715884105727 -170141183460469231731687303715884105728 +""" + +I_check_intDiv_with_Int256_max_and_min_value = r""" +round(intDiv(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), toInt256(1)), 7) round(intDiv(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\'), toInt256(1)), 7) +57896044618658097711785492504343953926634992332820282019728792003956564819967 -57896044618658097711785492504343953926634992332820282019728792003956564819968 +""" + +I_check_intDiv_with_UInt128_max_and_min_value = r""" +round(intDiv(toUInt128(\'340282366920938463463374607431768211455\'), toUInt128(1)), 7) round(intDiv(toUInt128(\'0\'), toUInt128(1)), 7) +340282366920938463463374607431768211455 0 +""" + +I_check_intDiv_with_UInt256_max_and_min_value = r""" +round(intDiv(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), toUInt256(1)), 7) round(intDiv(toUInt256(\'0\'), toUInt256(1)), 7) +115792089237316195423570985008687907853269984665640564039457584007913129639935 0 +""" + +I_check_intDivOrZero_with_Int128_max_and_min_value = r""" +round(intDivOrZero(toInt128(\'170141183460469231731687303715884105727\'), toInt128(1)), 7) round(intDivOrZero(toInt128(\'-170141183460469231731687303715884105728\'), toInt128(1)), 7) +170141183460469231731687303715884105727 -170141183460469231731687303715884105728 +""" + +I_check_intDivOrZero_with_Int256_max_and_min_value = r""" +round(intDivOrZero(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), toInt256(1)), 7) round(intDivOrZero(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\'), toInt256(1)), 7) +57896044618658097711785492504343953926634992332820282019728792003956564819967 -57896044618658097711785492504343953926634992332820282019728792003956564819968 +""" + +I_check_intDivOrZero_with_UInt128_max_and_min_value = r""" +round(intDivOrZero(toUInt128(\'340282366920938463463374607431768211455\'), toUInt128(1)), 7) round(intDivOrZero(toUInt128(\'0\'), toUInt128(1)), 7) +340282366920938463463374607431768211455 0 +""" + +I_check_intDivOrZero_with_UInt256_max_and_min_value = r""" +round(intDivOrZero(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), toUInt256(1)), 7) round(intDivOrZero(toUInt256(\'0\'), toUInt256(1)), 7) +115792089237316195423570985008687907853269984665640564039457584007913129639935 0 +""" + +I_check_modulo_with_Int128_max_and_min_value = r""" +round(modulo(toInt128(\'170141183460469231731687303715884105727\'), toInt128(1)), 7) round(modulo(toInt128(\'-170141183460469231731687303715884105728\'), toInt128(1)), 7) +0 0 +""" + +I_check_modulo_with_Int256_max_and_min_value = r""" +round(modulo(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), toInt256(1)), 7) round(modulo(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\'), toInt256(1)), 7) +0 0 +""" + +I_check_modulo_with_UInt128_max_and_min_value = r""" +round(modulo(toUInt128(\'340282366920938463463374607431768211455\'), toUInt128(1)), 7) round(modulo(toUInt128(\'0\'), toUInt128(1)), 7) +0 0 +""" + +I_check_modulo_with_UInt256_max_and_min_value = r""" +round(modulo(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), toUInt256(1)), 7) round(modulo(toUInt256(\'0\'), toUInt256(1)), 7) +0 0 +""" + +I_check_moduloOrZero_with_Int128_max_and_min_value = r""" +round(moduloOrZero(toInt128(\'170141183460469231731687303715884105727\'), toInt128(1)), 7) round(moduloOrZero(toInt128(\'-170141183460469231731687303715884105728\'), toInt128(1)), 7) +0 0 +""" + +I_check_moduloOrZero_with_Int256_max_and_min_value = r""" +round(moduloOrZero(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), toInt256(1)), 7) round(moduloOrZero(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\'), toInt256(1)), 7) +0 0 +""" + +I_check_moduloOrZero_with_UInt128_max_and_min_value = r""" +round(moduloOrZero(toUInt128(\'340282366920938463463374607431768211455\'), toUInt128(1)), 7) round(moduloOrZero(toUInt128(\'0\'), toUInt128(1)), 7) +0 0 +""" + +I_check_moduloOrZero_with_UInt256_max_and_min_value = r""" +round(moduloOrZero(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), toUInt256(1)), 7) round(moduloOrZero(toUInt256(\'0\'), toUInt256(1)), 7) +0 0 +""" + +I_check_negate_with_Int128_max_and_min_value = r""" +negate(toInt128(\'170141183460469231731687303715884105727\')) negate(toInt128(\'-170141183460469231731687303715884105728\')) +-170141183460469231731687303715884105727 -170141183460469231731687303715884105728 +""" + +I_check_negate_with_Int256_max_and_min_value = r""" +negate(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) negate(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) +-57896044618658097711785492504343953926634992332820282019728792003956564819967 -57896044618658097711785492504343953926634992332820282019728792003956564819968 +""" + +I_check_negate_with_UInt128_max_and_min_value = r""" +negate(toUInt128(\'340282366920938463463374607431768211455\')) negate(toUInt128(\'0\')) +1 0 +""" + +I_check_negate_with_UInt256_max_and_min_value = r""" +negate(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) negate(toUInt256(\'0\')) +1 0 +""" + +I_check_abs_with_Int128_max_and_min_value = r""" +abs(toInt128(\'170141183460469231731687303715884105727\')) abs(toInt128(\'-170141183460469231731687303715884105728\')) +170141183460469231731687303715884105727 170141183460469231731687303715884105728 +""" + +I_check_abs_with_Int256_max_and_min_value = r""" +abs(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) abs(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) +57896044618658097711785492504343953926634992332820282019728792003956564819967 57896044618658097711785492504343953926634992332820282019728792003956564819968 +""" + +I_check_abs_with_UInt128_max_and_min_value = r""" +abs(toUInt128(\'340282366920938463463374607431768211455\')) abs(toUInt128(\'0\')) +340282366920938463463374607431768211455 0 +""" + +I_check_abs_with_UInt256_max_and_min_value = r""" +abs(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) abs(toUInt256(\'0\')) +115792089237316195423570985008687907853269984665640564039457584007913129639935 0 +""" + +I_check_the_table_output_of_plus_with_Int128 = r""" +a +-170141183460469231731687303715884105728 +-170141183460469231731687303715884105727 +2 +""" + +I_check_the_table_output_of_plus_with_Int256 = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +-57896044618658097711785492504343953926634992332820282019728792003956564819967 +2 +""" + +I_check_the_table_output_of_plus_with_UInt128 = r""" +a +0 +1 +2 +""" + +I_check_the_table_output_of_plus_with_UInt256 = r""" +a +0 +1 +2 +""" + +I_check_the_table_output_of_minus_with_Int128 = r""" +a +0 +170141183460469231731687303715884105726 +170141183460469231731687303715884105727 +""" + +I_check_the_table_output_of_minus_with_Int256 = r""" +a +0 +57896044618658097711785492504343953926634992332820282019728792003956564819966 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_the_table_output_of_minus_with_UInt128 = r""" +a +0 +340282366920938463463374607431768211454 +340282366920938463463374607431768211455 +""" + +I_check_the_table_output_of_minus_with_UInt256 = r""" +a +0 +115792089237316195423570985008687907853269984665640564039457584007913129639934 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_the_table_output_of_multiply_with_Int128 = r""" +a +-170141183460469231731687303715884105728 +1 +170141183460469231731687303715884105727 +""" + +I_check_the_table_output_of_multiply_with_Int256 = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +1 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_the_table_output_of_multiply_with_UInt128 = r""" +a +0 +1 +340282366920938463463374607431768211455 +""" + +I_check_the_table_output_of_multiply_with_UInt256 = r""" +a +0 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_the_table_output_of_divide_with_Int128 = r""" +a +-170141183460469231722463931679029329921 +1 +170141183460469231722463931679029329921 +""" + +I_check_the_table_output_of_divide_with_Int256 = r""" +a +-57896044618658097702369839901263932781391731748390190090761097376371310592000 +1 +57896044618658097702369839901263932781391731748390190090761097376371310592000 +""" + +I_check_the_table_output_of_divide_with_UInt128 = r""" +a +0 +1 +340282366920938463426481119284349108225 +""" + +I_check_the_table_output_of_divide_with_UInt256 = r""" +a +0 +1 +57896044618658097702369839901263932781391731748390190090761097376371310592000 +""" + +I_check_the_table_output_of_intDiv_with_Int128 = r""" +a +-170141183460469231731687303715884105728 +1 +170141183460469231731687303715884105727 +""" + +I_check_the_table_output_of_intDiv_with_Int256 = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +1 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_the_table_output_of_intDiv_with_UInt128 = r""" +a +0 +1 +340282366920938463463374607431768211455 +""" + +I_check_the_table_output_of_intDiv_with_UInt256 = r""" +a +0 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_the_table_output_of_intDivOrZero_with_Int128 = r""" +a +-170141183460469231731687303715884105728 +1 +170141183460469231731687303715884105727 +""" + +I_check_the_table_output_of_intDivOrZero_with_Int256 = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +1 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_the_table_output_of_intDivOrZero_with_UInt128 = r""" +a +0 +1 +340282366920938463463374607431768211455 +""" + +I_check_the_table_output_of_intDivOrZero_with_UInt256 = r""" +a +0 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_the_table_output_of_modulo_with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_table_output_of_modulo_with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_table_output_of_modulo_with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_table_output_of_modulo_with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_table_output_of_moduloOrZero_with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_table_output_of_moduloOrZero_with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_table_output_of_moduloOrZero_with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_table_output_of_moduloOrZero_with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_table_output_of_negate_with_Int128 = r""" +a +-170141183460469231731687303715884105728 +-170141183460469231731687303715884105727 +-1 +""" + +I_check_the_table_output_of_negate_with_Int256 = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +-57896044618658097711785492504343953926634992332820282019728792003956564819967 +-1 +""" + +I_check_the_table_output_of_negate_with_UInt128 = r""" +a +0 +1 +340282366920938463463374607431768211455 +""" + +I_check_the_table_output_of_negate_with_UInt256 = r""" +a +0 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_the_table_output_of_abs_with_Int128 = r""" +a +-170141183460469231731687303715884105728 +1 +170141183460469231731687303715884105727 +""" + +I_check_the_table_output_of_abs_with_Int256 = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +1 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_the_table_output_of_abs_with_UInt128 = r""" +a +0 +1 +340282366920938463463374607431768211455 +""" + +I_check_the_table_output_of_abs_with_UInt256 = r""" +a +0 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_the_table_output_of_gcd_with_Int128 = r""" +a +1 +""" + +I_check_the_table_output_of_gcd_with_Int256 = r""" +a +1 +""" + +I_check_the_table_output_of_gcd_with_UInt128 = r""" +a +1 +1 +""" + +I_check_the_table_output_of_gcd_with_UInt256 = r""" +a +1 +1 +""" + +I_check_the_table_output_of_lcm_with_Int128 = r""" +a +1 +""" + +I_check_the_table_output_of_lcm_with_Int256 = r""" +a +1 +""" + +I_check_the_table_output_of_lcm_with_UInt128 = r""" +a +1 +340282366920938463463374607431768211455 +""" + +I_check_the_table_output_of_lcm_with_UInt256 = r""" +a +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_the_table_for_output_of_negate_with_Decimal256 = r""" +a +-1 +""" + +I_check_the_table_for_output_of_abs_with_Decimal256 = r""" +a +1 +""" + +Inline___Int128___arrayPopBack__ = r""" +arrayPopBack(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[3,2] +""" + +Table___Int128___arrayPopBack__ = r""" +a +[3,2] +""" + +Inline___Int128___arrayPopFront__ = r""" +arrayPopFront(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[2,1] +""" + +Table___Int128___arrayPopFront__ = r""" +a +[2,1] +""" + +Inline___Int128___arraySort__ = r""" +arraySort(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[1,2,3] +""" + +Table___Int128___arraySort__ = r""" +a +[1,2,3] +""" + +Inline___Int128___arrayReverseSort__ = r""" +arrayReverseSort(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[3,2,1] +""" + +Table___Int128___arrayReverseSort__ = r""" +a +[3,2,1] +""" + +Inline___Int128___arrayDistinct__ = r""" +arrayDistinct(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[3,2,1] +""" + +Table___Int128___arrayDistinct__ = r""" +a +[3,2,1] +""" + +Inline___Int128___arrayEnumerate__ = r""" +arrayEnumerate(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[1,2,3] +""" + +Table___Int128___arrayEnumerate__ = r""" +a +[1,2,3] +""" + +Inline___Int128___arrayEnumerateDense__ = r""" +arrayEnumerateDense(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[1,2,3] +""" + +Table___Int128___arrayEnumerateDense__ = r""" +a +[1,2,3] +""" + +Inline___Int128___arrayEnumerateUniq__ = r""" +arrayEnumerateUniq(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[1,1,1] +""" + +Table___Int128___arrayEnumerateUniq__ = r""" +a +[1,1,1] +""" + +Inline___Int128___arrayReverse__ = r""" +arrayReverse(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[1,2,3] +""" + +Table___Int128___arrayReverse__ = r""" +a +[1,2,3] +""" + +Inline___Int128___reverse__ = r""" +reverse(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[1,2,3] +""" + +Table___Int128___reverse__ = r""" +a +[1,2,3] +""" + +Inline___Int128___arrayFlatten__ = r""" +arrayFlatten(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[3,2,1] +""" + +Table___Int128___arrayFlatten__ = r""" +a +[3,2,1] +""" + +Inline___Int128___arrayCompact__ = r""" +arrayCompact(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[3,2,1] +""" + +Table___Int128___arrayCompact__ = r""" +a +[3,2,1] +""" + +Inline___Int128___arrayReduceInRanges__sum_____1__5____ = r""" +arrayReduceInRanges(\'sum\', [(1, 5)], array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[6] +""" + +Table___Int128___arrayReduceInRanges__sum_____1__5____ = r""" +a +[6] +""" + +Inline___Int128___arrayMap_x_____x___2___ = r""" +arrayMap(lambda(tuple(x), plus(x, 2)), array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[5,4,3] +""" + +Table___Int128___arrayMap_x_____x___2___ = r""" +a +[5,4,3] +""" + +Inline___Int128___arrayFill_x____x_3__ = r""" +arrayFill(lambda(tuple(x), equals(x, 3)), array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[3,3,3] +""" + +Table___Int128___arrayFill_x____x_3__ = r""" +a +[3,3,3] +""" + +Inline___Int128___arrayReverseFill_x____x_3__ = r""" +arrayReverseFill(lambda(tuple(x), equals(x, 3)), array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[3,1,1] +""" + +Table___Int128___arrayReverseFill_x____x_3__ = r""" +a +[3,1,1] +""" + +Inline___Int128___arrayConcat__toInt128__3____toInt128__2____toInt128__1_____ = r""" +arrayConcat(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\')), array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[3,2,1,3,2,1] +""" + +Table___Int128___arrayConcat__toInt128__3____toInt128__2____toInt128__1_____ = r""" +a +[3,2,1,3,2,1] +""" + +Inline___Int128___arrayFilter_x____x____1___ = r""" +arrayFilter(lambda(tuple(x), equals(x, 1)), array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[1] +""" + +Table___Int128___arrayFilter_x____x____1___ = r""" +a +[1] +""" + +Inline___Int128___arraySplit__x__y_____x_y___0__0__0___ = r""" +arraySplit(lambda(tuple(x, y), equals(x, y)), [0, 0, 0], array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[[0,0,0]] +""" + +Table___Int128___arraySplit__x__y_____x_y___0__0__0___ = r""" +a +[[0,0,0]] +""" + +Inline___Int128___arrayZip__toInt128__1_____ = r""" +arrayZip(array(toInt128(\'1\')), array(toInt128(\'3\'))) +[(1,3)] +""" + +Table___Int128___arrayZip__toInt128__1_____ = r""" +a +[(1,1)] +""" + +Inline___Int128___empty__ = r""" +empty(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +0 +""" + +Table___Int128___empty__ = r""" +a +0 +""" + +Inline___Int128___notEmpty__ = r""" +notEmpty(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +1 +""" + +Table___Int128___notEmpty__ = r""" +a +1 +""" + +Inline___Int128___length__ = r""" +length(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +3 +""" + +Table___Int128___length__ = r""" +a +3 +""" + +Inline___Int128___arrayCount_x____x____1___ = r""" +arrayCount(lambda(tuple(x), equals(x, 1)), array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +1 +""" + +Table___Int128___arrayCount_x____x____1___ = r""" +a +1 +""" + +Inline___Int128___arrayUniq__ = r""" +arrayUniq(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +3 +""" + +Table___Int128___arrayUniq__ = r""" +a +3 +""" + +Inline___Int128___arrayJoin__ = r""" +arrayJoin(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +3 +2 +1 +""" + +Table___Int128___arrayJoin__ = r""" +a +1 +2 +3 +""" + +Inline___Int128___arrayExists_x____x__1__ = r""" +arrayExists(lambda(tuple(x), equals(x, 1)), array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +1 +""" + +Table___Int128___arrayExists_x____x__1__ = r""" +a +1 +""" + +Inline___Int128___arrayAll_x____x__1__ = r""" +arrayAll(lambda(tuple(x), equals(x, 1)), array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +0 +""" + +Table___Int128___arrayAll_x____x__1__ = r""" +a +0 +""" + +Inline___Int128___arrayMin__ = r""" +arrayMin(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +1 +""" + +Table___Int128___arrayMin__ = r""" +a +1 +""" + +Inline___Int128___arrayMax__ = r""" +arrayMax(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +3 +""" + +Table___Int128___arrayMax__ = r""" +a +3 +""" + +Inline___Int128___arraySum__ = r""" +arraySum(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +6 +""" + +Table___Int128___arraySum__ = r""" +a +6 +""" + +Inline___Int128___arrayAvg__ = r""" +arrayAvg(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +2 +""" + +Table___Int128___arrayAvg__ = r""" +a +2 +""" + +Inline___Int128___arrayReduce__max____ = r""" +arrayReduce(\'max\', array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +3 +""" + +Table___Int128___arrayReduce__max____ = r""" +a +3 +""" + +Inline___Int128___arrayFirst_x____x__3__ = r""" +arrayFirst(lambda(tuple(x), equals(x, 3)), array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +3 +""" + +Table___Int128___arrayFirst_x____x__3__ = r""" +a +3 +""" + +Inline___Int128___arrayFirstIndex_x____x__3__ = r""" +arrayFirstIndex(lambda(tuple(x), equals(x, 3)), array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +1 +""" + +Table___Int128___arrayFirstIndex_x____x__3__ = r""" +a +1 +""" + +Inline___Int128___hasAll__toInt128__3____toInt128__2____toInt128__1______ = r""" +hasAll(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\')), array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +1 +""" + +Table___Int128___hasAll__toInt128__3____toInt128__2____toInt128__1______ = r""" +a +1 +""" + +Inline___Int128___hasAny__toInt128__2____toInt128__1______ = r""" +hasAny(array(toInt128(\'2\'), toInt128(\'1\')), array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +1 +""" + +Table___Int128___hasAny__toInt128__2____toInt128__1______ = r""" +a +1 +""" + +Inline___Int128___hasSubstr__toInt128__2____toInt128__1______ = r""" +hasSubstr(array(toInt128(\'2\'), toInt128(\'1\')), array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +0 +""" + +Table___Int128___hasSubstr__toInt128__2____toInt128__1______ = r""" +a +0 +""" + +Table___Int128___arrayDifference__ = r""" +a +""" + +Table___Int128___arrayCumSum__ = r""" +a +""" + +Table___Int128___arrayCumSumNonNegative__ = r""" +a +""" + +Inline___Int128___arrayElement = r""" +arrayElement(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\')), 1) +3 +""" + +Table___Int128___arrayElement = r""" +a +3 +""" + +Inline___Int128___arrayPushBack = r""" +arrayPushBack(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\')), toInt128(\'1\')) +[3,2,1,1] +""" + +Table___Int128___arrayPushBack = r""" +a +[3,2,1,1] +""" + +Inline___Int128___arrayPushFront = r""" +arrayPushFront(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\')), toInt128(\'1\')) +[1,3,2,1] +""" + +Table___Int128___arrayPushFront = r""" +a +[1,3,2,1] +""" + +Inline___Int128___arrayResize = r""" +arrayResize(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\')), 1) +[3] +""" + +Table___Int128___arrayResize = r""" +a +[3] +""" + +Inline___Int128___arraySlice = r""" +arraySlice(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\')), 1) +[3,2,1] +""" + +Table___Int128___arraySlice = r""" +a +[3,2,1] +""" + +Inline___Int128___has = r""" +has(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\')), NULL) +0 +""" + +Table___Int128___has = r""" +a +0 +""" + +Inline___Int128___indexOf = r""" +indexOf(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\')), NULL) +0 +""" + +Table___Int128___indexOf = r""" +a +0 +""" + +Inline___Int128___countEqual = r""" +countEqual(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\')), NULL) +0 +""" + +Table___Int128___countEqual = r""" +a +0 +""" + +Creating_a_tuple_with_Int128_on_a_table = r""" +a +(1,1,1) +""" + +tupleElement_with_Int128_on_a_table = r""" +a +1 +""" + +untuple_with_Int128_on_a_table = r""" +a +1 +""" + +tupleHammingDistance_with_Int128_on_a_table = r""" +a +2 +""" + +Creating_a_map_with_Int128_on_a_table = r""" +a +{'key1':1,'key2':2} +""" + +mapAdd_with_Int128 = r""" +mapAdd(tuple(array(toInt128(\'1\'), toInt128(\'2\')), array(toInt128(\'1\'), toInt128(\'2\'))), tuple(array(toInt128(\'1\'), toInt128(\'2\')), array(toInt128(\'1\'), toInt128(\'2\')))) +([1,2],[2,4]) +""" + +mapAdd_with_Int128_on_a_table = r""" +a +([1,2],[2,4]) +""" + +mapSubtract_with_Int128 = r""" +mapSubtract(tuple(array(toInt128(\'1\'), toInt128(\'2\')), array(toInt128(\'1\'), toInt128(\'2\'))), tuple(array(toInt128(\'1\'), toInt128(\'2\')), array(toInt128(\'1\'), toInt128(\'2\')))) +([1,2],[0,0]) +""" + +mapSubtract_with_Int128_on_a_table = r""" +a +([1,2],[0,0]) +""" + +mapPopulateSeries_with_Int128_on_a_table = r""" +a +([1,2,3,4,5],[1,2,3,0,0]) +""" + +mapContains_with_Int128_on_a_table = r""" +a +1 +""" + +mapKeys_with_Int128_on_a_table = r""" +a +['key1','key2'] +""" + +mapValues_with_Int128_on_a_table = r""" +a +[1,2] +""" + +Inline___Int256___arrayPopBack__ = r""" +arrayPopBack(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[3,2] +""" + +Table___Int256___arrayPopBack__ = r""" +a +[3,2] +""" + +Inline___Int256___arrayPopFront__ = r""" +arrayPopFront(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[2,1] +""" + +Table___Int256___arrayPopFront__ = r""" +a +[2,1] +""" + +Inline___Int256___arraySort__ = r""" +arraySort(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[1,2,3] +""" + +Table___Int256___arraySort__ = r""" +a +[1,2,3] +""" + +Inline___Int256___arrayReverseSort__ = r""" +arrayReverseSort(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[3,2,1] +""" + +Table___Int256___arrayReverseSort__ = r""" +a +[3,2,1] +""" + +Inline___Int256___arrayDistinct__ = r""" +arrayDistinct(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[3,2,1] +""" + +Table___Int256___arrayDistinct__ = r""" +a +[3,2,1] +""" + +Inline___Int256___arrayEnumerate__ = r""" +arrayEnumerate(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[1,2,3] +""" + +Table___Int256___arrayEnumerate__ = r""" +a +[1,2,3] +""" + +Inline___Int256___arrayEnumerateDense__ = r""" +arrayEnumerateDense(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[1,2,3] +""" + +Table___Int256___arrayEnumerateDense__ = r""" +a +[1,2,3] +""" + +Inline___Int256___arrayEnumerateUniq__ = r""" +arrayEnumerateUniq(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[1,1,1] +""" + +Table___Int256___arrayEnumerateUniq__ = r""" +a +[1,1,1] +""" + +Inline___Int256___arrayReverse__ = r""" +arrayReverse(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[1,2,3] +""" + +Table___Int256___arrayReverse__ = r""" +a +[1,2,3] +""" + +Inline___Int256___reverse__ = r""" +reverse(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[1,2,3] +""" + +Table___Int256___reverse__ = r""" +a +[1,2,3] +""" + +Inline___Int256___arrayFlatten__ = r""" +arrayFlatten(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[3,2,1] +""" + +Table___Int256___arrayFlatten__ = r""" +a +[3,2,1] +""" + +Inline___Int256___arrayCompact__ = r""" +arrayCompact(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[3,2,1] +""" + +Table___Int256___arrayCompact__ = r""" +a +[3,2,1] +""" + +Inline___Int256___arrayReduceInRanges__sum_____1__5____ = r""" +arrayReduceInRanges(\'sum\', [(1, 5)], array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[6] +""" + +Table___Int256___arrayReduceInRanges__sum_____1__5____ = r""" +a +[6] +""" + +Inline___Int256___arrayMap_x_____x___2___ = r""" +arrayMap(lambda(tuple(x), plus(x, 2)), array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[5,4,3] +""" + +Table___Int256___arrayMap_x_____x___2___ = r""" +a +[5,4,3] +""" + +Inline___Int256___arrayFill_x____x_3__ = r""" +arrayFill(lambda(tuple(x), equals(x, 3)), array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[3,3,3] +""" + +Table___Int256___arrayFill_x____x_3__ = r""" +a +[3,3,3] +""" + +Inline___Int256___arrayReverseFill_x____x_3__ = r""" +arrayReverseFill(lambda(tuple(x), equals(x, 3)), array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[3,1,1] +""" + +Table___Int256___arrayReverseFill_x____x_3__ = r""" +a +[3,1,1] +""" + +Inline___Int256___arrayConcat__toInt256__3____toInt256__2____toInt256__1_____ = r""" +arrayConcat(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\')), array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[3,2,1,3,2,1] +""" + +Table___Int256___arrayConcat__toInt256__3____toInt256__2____toInt256__1_____ = r""" +a +[3,2,1,3,2,1] +""" + +Inline___Int256___arrayFilter_x____x____1___ = r""" +arrayFilter(lambda(tuple(x), equals(x, 1)), array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[1] +""" + +Table___Int256___arrayFilter_x____x____1___ = r""" +a +[1] +""" + +Inline___Int256___arraySplit__x__y_____x_y___0__0__0___ = r""" +arraySplit(lambda(tuple(x, y), equals(x, y)), [0, 0, 0], array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[[0,0,0]] +""" + +Table___Int256___arraySplit__x__y_____x_y___0__0__0___ = r""" +a +[[0,0,0]] +""" + +Inline___Int256___arrayZip__toInt256__1_____ = r""" +arrayZip(array(toInt256(\'1\')), array(toInt256(\'3\'))) +[(1,3)] +""" + +Table___Int256___arrayZip__toInt256__1_____ = r""" +a +[(1,1)] +""" + +Inline___Int256___empty__ = r""" +empty(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +0 +""" + +Table___Int256___empty__ = r""" +a +0 +""" + +Inline___Int256___notEmpty__ = r""" +notEmpty(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +1 +""" + +Table___Int256___notEmpty__ = r""" +a +1 +""" + +Inline___Int256___length__ = r""" +length(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +3 +""" + +Table___Int256___length__ = r""" +a +3 +""" + +Inline___Int256___arrayCount_x____x____1___ = r""" +arrayCount(lambda(tuple(x), equals(x, 1)), array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +1 +""" + +Table___Int256___arrayCount_x____x____1___ = r""" +a +1 +""" + +Inline___Int256___arrayUniq__ = r""" +arrayUniq(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +3 +""" + +Table___Int256___arrayUniq__ = r""" +a +3 +""" + +Inline___Int256___arrayJoin__ = r""" +arrayJoin(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +3 +2 +1 +""" + +Table___Int256___arrayJoin__ = r""" +a +1 +2 +3 +""" + +Inline___Int256___arrayExists_x____x__1__ = r""" +arrayExists(lambda(tuple(x), equals(x, 1)), array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +1 +""" + +Table___Int256___arrayExists_x____x__1__ = r""" +a +1 +""" + +Inline___Int256___arrayAll_x____x__1__ = r""" +arrayAll(lambda(tuple(x), equals(x, 1)), array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +0 +""" + +Table___Int256___arrayAll_x____x__1__ = r""" +a +0 +""" + +Inline___Int256___arrayMin__ = r""" +arrayMin(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +1 +""" + +Table___Int256___arrayMin__ = r""" +a +1 +""" + +Inline___Int256___arrayMax__ = r""" +arrayMax(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +3 +""" + +Table___Int256___arrayMax__ = r""" +a +3 +""" + +Inline___Int256___arraySum__ = r""" +arraySum(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +6 +""" + +Table___Int256___arraySum__ = r""" +a +6 +""" + +Inline___Int256___arrayAvg__ = r""" +arrayAvg(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +2 +""" + +Table___Int256___arrayAvg__ = r""" +a +2 +""" + +Inline___Int256___arrayReduce__max____ = r""" +arrayReduce(\'max\', array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +3 +""" + +Table___Int256___arrayReduce__max____ = r""" +a +3 +""" + +Inline___Int256___arrayFirst_x____x__3__ = r""" +arrayFirst(lambda(tuple(x), equals(x, 3)), array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +3 +""" + +Table___Int256___arrayFirst_x____x__3__ = r""" +a +3 +""" + +Inline___Int256___arrayFirstIndex_x____x__3__ = r""" +arrayFirstIndex(lambda(tuple(x), equals(x, 3)), array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +1 +""" + +Table___Int256___arrayFirstIndex_x____x__3__ = r""" +a +1 +""" + +Inline___Int256___hasAll__toInt256__3____toInt256__2____toInt256__1______ = r""" +hasAll(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\')), array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +1 +""" + +Table___Int256___hasAll__toInt256__3____toInt256__2____toInt256__1______ = r""" +a +1 +""" + +Inline___Int256___hasAny__toInt256__2____toInt256__1______ = r""" +hasAny(array(toInt256(\'2\'), toInt256(\'1\')), array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +1 +""" + +Table___Int256___hasAny__toInt256__2____toInt256__1______ = r""" +a +1 +""" + +Inline___Int256___hasSubstr__toInt256__2____toInt256__1______ = r""" +hasSubstr(array(toInt256(\'2\'), toInt256(\'1\')), array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +0 +""" + +Table___Int256___hasSubstr__toInt256__2____toInt256__1______ = r""" +a +0 +""" + +Table___Int256___arrayDifference__ = r""" +a +""" + +Table___Int256___arrayCumSum__ = r""" +a +""" + +Table___Int256___arrayCumSumNonNegative__ = r""" +a +""" + +Inline___Int256___arrayElement = r""" +arrayElement(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\')), 1) +3 +""" + +Table___Int256___arrayElement = r""" +a +3 +""" + +Inline___Int256___arrayPushBack = r""" +arrayPushBack(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\')), toInt256(\'1\')) +[3,2,1,1] +""" + +Table___Int256___arrayPushBack = r""" +a +[3,2,1,1] +""" + +Inline___Int256___arrayPushFront = r""" +arrayPushFront(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\')), toInt256(\'1\')) +[1,3,2,1] +""" + +Table___Int256___arrayPushFront = r""" +a +[1,3,2,1] +""" + +Inline___Int256___arrayResize = r""" +arrayResize(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\')), 1) +[3] +""" + +Table___Int256___arrayResize = r""" +a +[3] +""" + +Inline___Int256___arraySlice = r""" +arraySlice(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\')), 1) +[3,2,1] +""" + +Table___Int256___arraySlice = r""" +a +[3,2,1] +""" + +Inline___Int256___has = r""" +has(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\')), NULL) +0 +""" + +Table___Int256___has = r""" +a +0 +""" + +Inline___Int256___indexOf = r""" +indexOf(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\')), NULL) +0 +""" + +Table___Int256___indexOf = r""" +a +0 +""" + +Inline___Int256___countEqual = r""" +countEqual(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\')), NULL) +0 +""" + +Table___Int256___countEqual = r""" +a +0 +""" + +Creating_a_tuple_with_Int256_on_a_table = r""" +a +(1,1,1) +""" + +tupleElement_with_Int256_on_a_table = r""" +a +1 +""" + +untuple_with_Int256_on_a_table = r""" +a +1 +""" + +tupleHammingDistance_with_Int256_on_a_table = r""" +a +2 +""" + +Creating_a_map_with_Int256_on_a_table = r""" +a +{'key1':1,'key2':2} +""" + +mapAdd_with_Int256 = r""" +mapAdd(tuple(array(toInt256(\'1\'), toInt256(\'2\')), array(toInt256(\'1\'), toInt256(\'2\'))), tuple(array(toInt256(\'1\'), toInt256(\'2\')), array(toInt256(\'1\'), toInt256(\'2\')))) +([1,2],[2,4]) +""" + +mapAdd_with_Int256_on_a_table = r""" +a +([1,2],[2,4]) +""" + +mapSubtract_with_Int256 = r""" +mapSubtract(tuple(array(toInt256(\'1\'), toInt256(\'2\')), array(toInt256(\'1\'), toInt256(\'2\'))), tuple(array(toInt256(\'1\'), toInt256(\'2\')), array(toInt256(\'1\'), toInt256(\'2\')))) +([1,2],[0,0]) +""" + +mapSubtract_with_Int256_on_a_table = r""" +a +([1,2],[0,0]) +""" + +mapPopulateSeries_with_Int256_on_a_table = r""" +a +([1,2,3,4,5],[1,2,3,0,0]) +""" + +mapContains_with_Int256_on_a_table = r""" +a +1 +""" + +mapKeys_with_Int256_on_a_table = r""" +a +['key1','key2'] +""" + +mapValues_with_Int256_on_a_table = r""" +a +[1,2] +""" + +Inline___UInt128___arrayPopBack__ = r""" +arrayPopBack(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[3,2] +""" + +Table___UInt128___arrayPopBack__ = r""" +a +[3,2] +""" + +Inline___UInt128___arrayPopFront__ = r""" +arrayPopFront(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[2,1] +""" + +Table___UInt128___arrayPopFront__ = r""" +a +[2,1] +""" + +Inline___UInt128___arraySort__ = r""" +arraySort(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[1,2,3] +""" + +Table___UInt128___arraySort__ = r""" +a +[1,2,3] +""" + +Inline___UInt128___arrayReverseSort__ = r""" +arrayReverseSort(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[3,2,1] +""" + +Table___UInt128___arrayReverseSort__ = r""" +a +[3,2,1] +""" + +Inline___UInt128___arrayDistinct__ = r""" +arrayDistinct(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[3,2,1] +""" + +Table___UInt128___arrayDistinct__ = r""" +a +[3,2,1] +""" + +Inline___UInt128___arrayEnumerate__ = r""" +arrayEnumerate(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[1,2,3] +""" + +Table___UInt128___arrayEnumerate__ = r""" +a +[1,2,3] +""" + +Inline___UInt128___arrayEnumerateDense__ = r""" +arrayEnumerateDense(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[1,2,3] +""" + +Table___UInt128___arrayEnumerateDense__ = r""" +a +[1,2,3] +""" + +Inline___UInt128___arrayEnumerateUniq__ = r""" +arrayEnumerateUniq(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[1,1,1] +""" + +Table___UInt128___arrayEnumerateUniq__ = r""" +a +[1,1,1] +""" + +Inline___UInt128___arrayReverse__ = r""" +arrayReverse(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[1,2,3] +""" + +Table___UInt128___arrayReverse__ = r""" +a +[1,2,3] +""" + +Inline___UInt128___reverse__ = r""" +reverse(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[1,2,3] +""" + +Table___UInt128___reverse__ = r""" +a +[1,2,3] +""" + +Inline___UInt128___arrayFlatten__ = r""" +arrayFlatten(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[3,2,1] +""" + +Table___UInt128___arrayFlatten__ = r""" +a +[3,2,1] +""" + +Inline___UInt128___arrayCompact__ = r""" +arrayCompact(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[3,2,1] +""" + +Table___UInt128___arrayCompact__ = r""" +a +[3,2,1] +""" + +Inline___UInt128___arrayReduceInRanges__sum_____1__5____ = r""" +arrayReduceInRanges(\'sum\', [(1, 5)], array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[6] +""" + +Table___UInt128___arrayReduceInRanges__sum_____1__5____ = r""" +a +[6] +""" + +Inline___UInt128___arrayMap_x_____x___2___ = r""" +arrayMap(lambda(tuple(x), plus(x, 2)), array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[5,4,3] +""" + +Table___UInt128___arrayMap_x_____x___2___ = r""" +a +[5,4,3] +""" + +Inline___UInt128___arrayFill_x____x_3__ = r""" +arrayFill(lambda(tuple(x), equals(x, 3)), array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[3,3,3] +""" + +Table___UInt128___arrayFill_x____x_3__ = r""" +a +[3,3,3] +""" + +Inline___UInt128___arrayReverseFill_x____x_3__ = r""" +arrayReverseFill(lambda(tuple(x), equals(x, 3)), array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[3,1,1] +""" + +Table___UInt128___arrayReverseFill_x____x_3__ = r""" +a +[3,1,1] +""" + +Inline___UInt128___arrayConcat__toUInt128__3____toUInt128__2____toUInt128__1_____ = r""" +arrayConcat(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\')), array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[3,2,1,3,2,1] +""" + +Table___UInt128___arrayConcat__toUInt128__3____toUInt128__2____toUInt128__1_____ = r""" +a +[3,2,1,3,2,1] +""" + +Inline___UInt128___arrayFilter_x____x____1___ = r""" +arrayFilter(lambda(tuple(x), equals(x, 1)), array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[1] +""" + +Table___UInt128___arrayFilter_x____x____1___ = r""" +a +[1] +""" + +Inline___UInt128___arraySplit__x__y_____x_y___0__0__0___ = r""" +arraySplit(lambda(tuple(x, y), equals(x, y)), [0, 0, 0], array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[[0,0,0]] +""" + +Table___UInt128___arraySplit__x__y_____x_y___0__0__0___ = r""" +a +[[0,0,0]] +""" + +Inline___UInt128___arrayZip__toUInt128__1_____ = r""" +arrayZip(array(toUInt128(\'1\')), array(toUInt128(\'3\'))) +[(1,3)] +""" + +Table___UInt128___arrayZip__toUInt128__1_____ = r""" +a +[(1,1)] +""" + +Inline___UInt128___empty__ = r""" +empty(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +0 +""" + +Table___UInt128___empty__ = r""" +a +0 +""" + +Inline___UInt128___notEmpty__ = r""" +notEmpty(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +1 +""" + +Table___UInt128___notEmpty__ = r""" +a +1 +""" + +Inline___UInt128___length__ = r""" +length(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +3 +""" + +Table___UInt128___length__ = r""" +a +3 +""" + +Inline___UInt128___arrayCount_x____x____1___ = r""" +arrayCount(lambda(tuple(x), equals(x, 1)), array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +1 +""" + +Table___UInt128___arrayCount_x____x____1___ = r""" +a +1 +""" + +Inline___UInt128___arrayUniq__ = r""" +arrayUniq(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +3 +""" + +Table___UInt128___arrayUniq__ = r""" +a +3 +""" + +Inline___UInt128___arrayJoin__ = r""" +arrayJoin(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +3 +2 +1 +""" + +Table___UInt128___arrayJoin__ = r""" +a +1 +2 +3 +""" + +Inline___UInt128___arrayExists_x____x__1__ = r""" +arrayExists(lambda(tuple(x), equals(x, 1)), array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +1 +""" + +Table___UInt128___arrayExists_x____x__1__ = r""" +a +1 +""" + +Inline___UInt128___arrayAll_x____x__1__ = r""" +arrayAll(lambda(tuple(x), equals(x, 1)), array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +0 +""" + +Table___UInt128___arrayAll_x____x__1__ = r""" +a +0 +""" + +Inline___UInt128___arrayMin__ = r""" +arrayMin(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +1 +""" + +Table___UInt128___arrayMin__ = r""" +a +1 +""" + +Inline___UInt128___arrayMax__ = r""" +arrayMax(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +3 +""" + +Table___UInt128___arrayMax__ = r""" +a +3 +""" + +Inline___UInt128___arraySum__ = r""" +arraySum(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +6 +""" + +Table___UInt128___arraySum__ = r""" +a +6 +""" + +Inline___UInt128___arrayAvg__ = r""" +arrayAvg(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +2 +""" + +Table___UInt128___arrayAvg__ = r""" +a +2 +""" + +Inline___UInt128___arrayReduce__max____ = r""" +arrayReduce(\'max\', array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +3 +""" + +Table___UInt128___arrayReduce__max____ = r""" +a +3 +""" + +Inline___UInt128___arrayFirst_x____x__3__ = r""" +arrayFirst(lambda(tuple(x), equals(x, 3)), array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +3 +""" + +Table___UInt128___arrayFirst_x____x__3__ = r""" +a +3 +""" + +Inline___UInt128___arrayFirstIndex_x____x__3__ = r""" +arrayFirstIndex(lambda(tuple(x), equals(x, 3)), array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +1 +""" + +Table___UInt128___arrayFirstIndex_x____x__3__ = r""" +a +1 +""" + +Inline___UInt128___hasAll__toUInt128__3____toUInt128__2____toUInt128__1______ = r""" +hasAll(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\')), array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +1 +""" + +Table___UInt128___hasAll__toUInt128__3____toUInt128__2____toUInt128__1______ = r""" +a +1 +""" + +Inline___UInt128___hasAny__toUInt128__2____toUInt128__1______ = r""" +hasAny(array(toUInt128(\'2\'), toUInt128(\'1\')), array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +1 +""" + +Table___UInt128___hasAny__toUInt128__2____toUInt128__1______ = r""" +a +1 +""" + +Inline___UInt128___hasSubstr__toUInt128__2____toUInt128__1______ = r""" +hasSubstr(array(toUInt128(\'2\'), toUInt128(\'1\')), array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +0 +""" + +Table___UInt128___hasSubstr__toUInt128__2____toUInt128__1______ = r""" +a +0 +""" + +Table___UInt128___arrayDifference__ = r""" +a +""" + +Table___UInt128___arrayCumSum__ = r""" +a +""" + +Table___UInt128___arrayCumSumNonNegative__ = r""" +a +""" + +Inline___UInt128___arrayElement = r""" +arrayElement(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\')), 1) +3 +""" + +Table___UInt128___arrayElement = r""" +a +3 +""" + +Inline___UInt128___arrayPushBack = r""" +arrayPushBack(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\')), toUInt128(\'1\')) +[3,2,1,1] +""" + +Table___UInt128___arrayPushBack = r""" +a +[3,2,1,1] +""" + +Inline___UInt128___arrayPushFront = r""" +arrayPushFront(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\')), toUInt128(\'1\')) +[1,3,2,1] +""" + +Table___UInt128___arrayPushFront = r""" +a +[1,3,2,1] +""" + +Inline___UInt128___arrayResize = r""" +arrayResize(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\')), 1) +[3] +""" + +Table___UInt128___arrayResize = r""" +a +[3] +""" + +Inline___UInt128___arraySlice = r""" +arraySlice(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\')), 1) +[3,2,1] +""" + +Table___UInt128___arraySlice = r""" +a +[3,2,1] +""" + +Inline___UInt128___has = r""" +has(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\')), NULL) +0 +""" + +Table___UInt128___has = r""" +a +0 +""" + +Inline___UInt128___indexOf = r""" +indexOf(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\')), NULL) +0 +""" + +Table___UInt128___indexOf = r""" +a +0 +""" + +Inline___UInt128___countEqual = r""" +countEqual(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\')), NULL) +0 +""" + +Table___UInt128___countEqual = r""" +a +0 +""" + +Creating_a_tuple_with_UInt128_on_a_table = r""" +a +(1,1,1) +""" + +tupleElement_with_UInt128_on_a_table = r""" +a +1 +""" + +untuple_with_UInt128_on_a_table = r""" +a +1 +""" + +tupleHammingDistance_with_UInt128_on_a_table = r""" +a +2 +""" + +Creating_a_map_with_UInt128_on_a_table = r""" +a +{'key1':1,'key2':2} +""" + +mapAdd_with_UInt128 = r""" +mapAdd(tuple(array(toUInt128(\'1\'), toUInt128(\'2\')), array(toUInt128(\'1\'), toUInt128(\'2\'))), tuple(array(toUInt128(\'1\'), toUInt128(\'2\')), array(toUInt128(\'1\'), toUInt128(\'2\')))) +([1,2],[2,4]) +""" + +mapAdd_with_UInt128_on_a_table = r""" +a +([1,2],[2,4]) +""" + +mapSubtract_with_UInt128 = r""" +mapSubtract(tuple(array(toUInt128(\'1\'), toUInt128(\'2\')), array(toUInt128(\'1\'), toUInt128(\'2\'))), tuple(array(toUInt128(\'1\'), toUInt128(\'2\')), array(toUInt128(\'1\'), toUInt128(\'2\')))) +([1,2],[0,0]) +""" + +mapSubtract_with_UInt128_on_a_table = r""" +a +([1,2],[0,0]) +""" + +mapPopulateSeries_with_UInt128_on_a_table = r""" +a +([1,2,3,4,5],[1,2,3,0,0]) +""" + +mapContains_with_UInt128_on_a_table = r""" +a +1 +""" + +mapKeys_with_UInt128_on_a_table = r""" +a +['key1','key2'] +""" + +mapValues_with_UInt128_on_a_table = r""" +a +[1,2] +""" + +Inline___UInt256___arrayPopBack__ = r""" +arrayPopBack(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[3,2] +""" + +Table___UInt256___arrayPopBack__ = r""" +a +[3,2] +""" + +Inline___UInt256___arrayPopFront__ = r""" +arrayPopFront(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[2,1] +""" + +Table___UInt256___arrayPopFront__ = r""" +a +[2,1] +""" + +Inline___UInt256___arraySort__ = r""" +arraySort(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[1,2,3] +""" + +Table___UInt256___arraySort__ = r""" +a +[1,2,3] +""" + +Inline___UInt256___arrayReverseSort__ = r""" +arrayReverseSort(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[3,2,1] +""" + +Table___UInt256___arrayReverseSort__ = r""" +a +[3,2,1] +""" + +Inline___UInt256___arrayDistinct__ = r""" +arrayDistinct(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[3,2,1] +""" + +Table___UInt256___arrayDistinct__ = r""" +a +[3,2,1] +""" + +Inline___UInt256___arrayEnumerate__ = r""" +arrayEnumerate(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[1,2,3] +""" + +Table___UInt256___arrayEnumerate__ = r""" +a +[1,2,3] +""" + +Inline___UInt256___arrayEnumerateDense__ = r""" +arrayEnumerateDense(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[1,2,3] +""" + +Table___UInt256___arrayEnumerateDense__ = r""" +a +[1,2,3] +""" + +Inline___UInt256___arrayEnumerateUniq__ = r""" +arrayEnumerateUniq(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[1,1,1] +""" + +Table___UInt256___arrayEnumerateUniq__ = r""" +a +[1,1,1] +""" + +Inline___UInt256___arrayReverse__ = r""" +arrayReverse(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[1,2,3] +""" + +Table___UInt256___arrayReverse__ = r""" +a +[1,2,3] +""" + +Inline___UInt256___reverse__ = r""" +reverse(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[1,2,3] +""" + +Table___UInt256___reverse__ = r""" +a +[1,2,3] +""" + +Inline___UInt256___arrayFlatten__ = r""" +arrayFlatten(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[3,2,1] +""" + +Table___UInt256___arrayFlatten__ = r""" +a +[3,2,1] +""" + +Inline___UInt256___arrayCompact__ = r""" +arrayCompact(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[3,2,1] +""" + +Table___UInt256___arrayCompact__ = r""" +a +[3,2,1] +""" + +Inline___UInt256___arrayReduceInRanges__sum_____1__5____ = r""" +arrayReduceInRanges(\'sum\', [(1, 5)], array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[6] +""" + +Table___UInt256___arrayReduceInRanges__sum_____1__5____ = r""" +a +[6] +""" + +Inline___UInt256___arrayMap_x_____x___2___ = r""" +arrayMap(lambda(tuple(x), plus(x, 2)), array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[5,4,3] +""" + +Table___UInt256___arrayMap_x_____x___2___ = r""" +a +[5,4,3] +""" + +Inline___UInt256___arrayFill_x____x_3__ = r""" +arrayFill(lambda(tuple(x), equals(x, 3)), array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[3,3,3] +""" + +Table___UInt256___arrayFill_x____x_3__ = r""" +a +[3,3,3] +""" + +Inline___UInt256___arrayReverseFill_x____x_3__ = r""" +arrayReverseFill(lambda(tuple(x), equals(x, 3)), array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[3,1,1] +""" + +Table___UInt256___arrayReverseFill_x____x_3__ = r""" +a +[3,1,1] +""" + +Inline___UInt256___arrayConcat__toUInt256__3____toUInt256__2____toUInt256__1_____ = r""" +arrayConcat(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\')), array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[3,2,1,3,2,1] +""" + +Table___UInt256___arrayConcat__toUInt256__3____toUInt256__2____toUInt256__1_____ = r""" +a +[3,2,1,3,2,1] +""" + +Inline___UInt256___arrayFilter_x____x____1___ = r""" +arrayFilter(lambda(tuple(x), equals(x, 1)), array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[1] +""" + +Table___UInt256___arrayFilter_x____x____1___ = r""" +a +[1] +""" + +Inline___UInt256___arraySplit__x__y_____x_y___0__0__0___ = r""" +arraySplit(lambda(tuple(x, y), equals(x, y)), [0, 0, 0], array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[[0,0,0]] +""" + +Table___UInt256___arraySplit__x__y_____x_y___0__0__0___ = r""" +a +[[0,0,0]] +""" + +Inline___UInt256___arrayZip__toUInt256__1_____ = r""" +arrayZip(array(toUInt256(\'1\')), array(toUInt256(\'3\'))) +[(1,3)] +""" + +Table___UInt256___arrayZip__toUInt256__1_____ = r""" +a +[(1,1)] +""" + +Inline___UInt256___empty__ = r""" +empty(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +0 +""" + +Table___UInt256___empty__ = r""" +a +0 +""" + +Inline___UInt256___notEmpty__ = r""" +notEmpty(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +1 +""" + +Table___UInt256___notEmpty__ = r""" +a +1 +""" + +Inline___UInt256___length__ = r""" +length(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +3 +""" + +Table___UInt256___length__ = r""" +a +3 +""" + +Inline___UInt256___arrayCount_x____x____1___ = r""" +arrayCount(lambda(tuple(x), equals(x, 1)), array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +1 +""" + +Table___UInt256___arrayCount_x____x____1___ = r""" +a +1 +""" + +Inline___UInt256___arrayUniq__ = r""" +arrayUniq(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +3 +""" + +Table___UInt256___arrayUniq__ = r""" +a +3 +""" + +Inline___UInt256___arrayJoin__ = r""" +arrayJoin(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +3 +2 +1 +""" + +Table___UInt256___arrayJoin__ = r""" +a +1 +2 +3 +""" + +Inline___UInt256___arrayExists_x____x__1__ = r""" +arrayExists(lambda(tuple(x), equals(x, 1)), array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +1 +""" + +Table___UInt256___arrayExists_x____x__1__ = r""" +a +1 +""" + +Inline___UInt256___arrayAll_x____x__1__ = r""" +arrayAll(lambda(tuple(x), equals(x, 1)), array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +0 +""" + +Table___UInt256___arrayAll_x____x__1__ = r""" +a +0 +""" + +Inline___UInt256___arrayMin__ = r""" +arrayMin(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +1 +""" + +Table___UInt256___arrayMin__ = r""" +a +1 +""" + +Inline___UInt256___arrayMax__ = r""" +arrayMax(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +3 +""" + +Table___UInt256___arrayMax__ = r""" +a +3 +""" + +Inline___UInt256___arraySum__ = r""" +arraySum(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +6 +""" + +Table___UInt256___arraySum__ = r""" +a +6 +""" + +Inline___UInt256___arrayAvg__ = r""" +arrayAvg(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +2 +""" + +Table___UInt256___arrayAvg__ = r""" +a +2 +""" + +Inline___UInt256___arrayReduce__max____ = r""" +arrayReduce(\'max\', array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +3 +""" + +Table___UInt256___arrayReduce__max____ = r""" +a +3 +""" + +Inline___UInt256___arrayFirst_x____x__3__ = r""" +arrayFirst(lambda(tuple(x), equals(x, 3)), array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +3 +""" + +Table___UInt256___arrayFirst_x____x__3__ = r""" +a +3 +""" + +Inline___UInt256___arrayFirstIndex_x____x__3__ = r""" +arrayFirstIndex(lambda(tuple(x), equals(x, 3)), array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +1 +""" + +Table___UInt256___arrayFirstIndex_x____x__3__ = r""" +a +1 +""" + +Inline___UInt256___hasAll__toUInt256__3____toUInt256__2____toUInt256__1______ = r""" +hasAll(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\')), array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +1 +""" + +Table___UInt256___hasAll__toUInt256__3____toUInt256__2____toUInt256__1______ = r""" +a +1 +""" + +Inline___UInt256___hasAny__toUInt256__2____toUInt256__1______ = r""" +hasAny(array(toUInt256(\'2\'), toUInt256(\'1\')), array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +1 +""" + +Table___UInt256___hasAny__toUInt256__2____toUInt256__1______ = r""" +a +1 +""" + +Inline___UInt256___hasSubstr__toUInt256__2____toUInt256__1______ = r""" +hasSubstr(array(toUInt256(\'2\'), toUInt256(\'1\')), array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +0 +""" + +Table___UInt256___hasSubstr__toUInt256__2____toUInt256__1______ = r""" +a +0 +""" + +Table___UInt256___arrayDifference__ = r""" +a +""" + +Table___UInt256___arrayCumSum__ = r""" +a +""" + +Table___UInt256___arrayCumSumNonNegative__ = r""" +a +""" + +Inline___UInt256___arrayElement = r""" +arrayElement(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\')), 1) +3 +""" + +Table___UInt256___arrayElement = r""" +a +3 +""" + +Inline___UInt256___arrayPushBack = r""" +arrayPushBack(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\')), toUInt256(\'1\')) +[3,2,1,1] +""" + +Table___UInt256___arrayPushBack = r""" +a +[3,2,1,1] +""" + +Inline___UInt256___arrayPushFront = r""" +arrayPushFront(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\')), toUInt256(\'1\')) +[1,3,2,1] +""" + +Table___UInt256___arrayPushFront = r""" +a +[1,3,2,1] +""" + +Inline___UInt256___arrayResize = r""" +arrayResize(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\')), 1) +[3] +""" + +Table___UInt256___arrayResize = r""" +a +[3] +""" + +Inline___UInt256___arraySlice = r""" +arraySlice(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\')), 1) +[3,2,1] +""" + +Table___UInt256___arraySlice = r""" +a +[3,2,1] +""" + +Inline___UInt256___has = r""" +has(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\')), NULL) +0 +""" + +Table___UInt256___has = r""" +a +0 +""" + +Inline___UInt256___indexOf = r""" +indexOf(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\')), NULL) +0 +""" + +Table___UInt256___indexOf = r""" +a +0 +""" + +Inline___UInt256___countEqual = r""" +countEqual(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\')), NULL) +0 +""" + +Table___UInt256___countEqual = r""" +a +0 +""" + +Creating_a_tuple_with_UInt256_on_a_table = r""" +a +(1,1,1) +""" + +tupleElement_with_UInt256_on_a_table = r""" +a +1 +""" + +untuple_with_UInt256_on_a_table = r""" +a +1 +""" + +tupleHammingDistance_with_UInt256_on_a_table = r""" +a +2 +""" + +Creating_a_map_with_UInt256_on_a_table = r""" +a +{'key1':1,'key2':2} +""" + +mapAdd_with_UInt256 = r""" +mapAdd(tuple(array(toUInt256(\'1\'), toUInt256(\'2\')), array(toUInt256(\'1\'), toUInt256(\'2\'))), tuple(array(toUInt256(\'1\'), toUInt256(\'2\')), array(toUInt256(\'1\'), toUInt256(\'2\')))) +([1,2],[2,4]) +""" + +mapAdd_with_UInt256_on_a_table = r""" +a +([1,2],[2,4]) +""" + +mapSubtract_with_UInt256 = r""" +mapSubtract(tuple(array(toUInt256(\'1\'), toUInt256(\'2\')), array(toUInt256(\'1\'), toUInt256(\'2\'))), tuple(array(toUInt256(\'1\'), toUInt256(\'2\')), array(toUInt256(\'1\'), toUInt256(\'2\')))) +([1,2],[0,0]) +""" + +mapSubtract_with_UInt256_on_a_table = r""" +a +([1,2],[0,0]) +""" + +mapPopulateSeries_with_UInt256_on_a_table = r""" +a +([1,2,3,4,5],[1,2,3,0,0]) +""" + +mapContains_with_UInt256_on_a_table = r""" +a +1 +""" + +mapKeys_with_UInt256_on_a_table = r""" +a +['key1','key2'] +""" + +mapValues_with_UInt256_on_a_table = r""" +a +[1,2] +""" + +Inline___Decimal256_0____arrayPopBack__ = r""" +arrayPopBack(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[3,2] +""" + +Table___Decimal256_0____arrayPopBack__ = r""" +a +[3,2] +""" + +Inline___Decimal256_0____arrayPopFront__ = r""" +arrayPopFront(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[2,1] +""" + +Table___Decimal256_0____arrayPopFront__ = r""" +a +[2,1] +""" + +Inline___Decimal256_0____arraySort__ = r""" +arraySort(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[1,2,3] +""" + +Table___Decimal256_0____arraySort__ = r""" +a +[1,2,3] +""" + +Inline___Decimal256_0____arrayReverseSort__ = r""" +arrayReverseSort(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[3,2,1] +""" + +Table___Decimal256_0____arrayReverseSort__ = r""" +a +[3,2,1] +""" + +Inline___Decimal256_0____arrayDistinct__ = r""" +arrayDistinct(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[3,2,1] +""" + +Table___Decimal256_0____arrayDistinct__ = r""" +a +[3,2,1] +""" + +Inline___Decimal256_0____arrayEnumerate__ = r""" +arrayEnumerate(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[1,2,3] +""" + +Table___Decimal256_0____arrayEnumerate__ = r""" +a +[1,2,3] +""" + +Inline___Decimal256_0____arrayEnumerateDense__ = r""" +arrayEnumerateDense(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[1,2,3] +""" + +Table___Decimal256_0____arrayEnumerateDense__ = r""" +a +[1,2,3] +""" + +Inline___Decimal256_0____arrayEnumerateUniq__ = r""" +arrayEnumerateUniq(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[1,1,1] +""" + +Table___Decimal256_0____arrayEnumerateUniq__ = r""" +a +[1,1,1] +""" + +Inline___Decimal256_0____arrayReverse__ = r""" +arrayReverse(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[1,2,3] +""" + +Table___Decimal256_0____arrayReverse__ = r""" +a +[1,2,3] +""" + +Inline___Decimal256_0____reverse__ = r""" +reverse(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[1,2,3] +""" + +Table___Decimal256_0____reverse__ = r""" +a +[1,2,3] +""" + +Inline___Decimal256_0____arrayFlatten__ = r""" +arrayFlatten(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[3,2,1] +""" + +Table___Decimal256_0____arrayFlatten__ = r""" +a +[3,2,1] +""" + +Inline___Decimal256_0____arrayCompact__ = r""" +arrayCompact(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[3,2,1] +""" + +Table___Decimal256_0____arrayCompact__ = r""" +a +[3,2,1] +""" + +Inline___Decimal256_0____arrayReduceInRanges__sum_____1__5____ = r""" +arrayReduceInRanges(\'sum\', [(1, 5)], array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[6] +""" + +Table___Decimal256_0____arrayReduceInRanges__sum_____1__5____ = r""" +a +[6] +""" + +Inline___Decimal256_0____arrayMap_x_____x___2___ = r""" +arrayMap(lambda(tuple(x), plus(x, 2)), array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[5,4,3] +""" + +Table___Decimal256_0____arrayMap_x_____x___2___ = r""" +a +[5,4,3] +""" + +Inline___Decimal256_0____arrayFill_x____x_3__ = r""" +arrayFill(lambda(tuple(x), equals(x, 3)), array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[3,3,3] +""" + +Table___Decimal256_0____arrayFill_x____x_3__ = r""" +a +[3,3,3] +""" + +Inline___Decimal256_0____arrayReverseFill_x____x_3__ = r""" +arrayReverseFill(lambda(tuple(x), equals(x, 3)), array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[3,1,1] +""" + +Table___Decimal256_0____arrayReverseFill_x____x_3__ = r""" +a +[3,1,1] +""" + +Inline___Decimal256_0____arrayConcat__toDecimal256__3__0___toDecimal256__2__0___toDecimal256__1__0____ = r""" +arrayConcat(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0)), array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[3,2,1,3,2,1] +""" + +Table___Decimal256_0____arrayConcat__toDecimal256__3__0___toDecimal256__2__0___toDecimal256__1__0____ = r""" +a +[3,2,1,3,2,1] +""" + +Inline___Decimal256_0____arrayFilter_x____x____1___ = r""" +arrayFilter(lambda(tuple(x), equals(x, 1)), array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[1] +""" + +Table___Decimal256_0____arrayFilter_x____x____1___ = r""" +a +[1] +""" + +Inline___Decimal256_0____arraySplit__x__y_____x_y___0__0__0___ = r""" +arraySplit(lambda(tuple(x, y), equals(x, y)), [0, 0, 0], array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[[0,0,0]] +""" + +Table___Decimal256_0____arraySplit__x__y_____x_y___0__0__0___ = r""" +a +[[0,0,0]] +""" + +Inline___Decimal256_0____arrayZip__toDecimal256__1__0____ = r""" +arrayZip(array(toDecimal256(\'1\', 0)), array(toDecimal256(\'3\', 0))) +[(1,3)] +""" + +Table___Decimal256_0____arrayZip__toDecimal256__1__0____ = r""" +a +[(1,1)] +""" + +Inline___Decimal256_0____empty__ = r""" +empty(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +0 +""" + +Table___Decimal256_0____empty__ = r""" +a +0 +""" + +Inline___Decimal256_0____notEmpty__ = r""" +notEmpty(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +1 +""" + +Table___Decimal256_0____notEmpty__ = r""" +a +1 +""" + +Inline___Decimal256_0____length__ = r""" +length(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +3 +""" + +Table___Decimal256_0____length__ = r""" +a +3 +""" + +Inline___Decimal256_0____arrayCount_x____x____1___ = r""" +arrayCount(lambda(tuple(x), equals(x, 1)), array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +1 +""" + +Table___Decimal256_0____arrayCount_x____x____1___ = r""" +a +1 +""" + +Inline___Decimal256_0____arrayUniq__ = r""" +arrayUniq(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +3 +""" + +Table___Decimal256_0____arrayUniq__ = r""" +a +3 +""" + +Inline___Decimal256_0____arrayJoin__ = r""" +arrayJoin(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +3 +2 +1 +""" + +Table___Decimal256_0____arrayJoin__ = r""" +a +1 +2 +3 +""" + +Inline___Decimal256_0____arrayExists_x____x__1__ = r""" +arrayExists(lambda(tuple(x), equals(x, 1)), array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +1 +""" + +Table___Decimal256_0____arrayExists_x____x__1__ = r""" +a +1 +""" + +Inline___Decimal256_0____arrayAll_x____x__1__ = r""" +arrayAll(lambda(tuple(x), equals(x, 1)), array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +0 +""" + +Table___Decimal256_0____arrayAll_x____x__1__ = r""" +a +0 +""" + +Table___Decimal256_0____arrayMin__ = r""" +a +""" + +Table___Decimal256_0____arrayMax__ = r""" +a +""" + +Table___Decimal256_0____arraySum__ = r""" +a +""" + +Table___Decimal256_0____arrayAvg__ = r""" +a +""" + +Inline___Decimal256_0____arrayReduce__max____ = r""" +arrayReduce(\'max\', array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +3 +""" + +Table___Decimal256_0____arrayReduce__max____ = r""" +a +3 +""" + +Inline___Decimal256_0____arrayFirst_x____x__3__ = r""" +arrayFirst(lambda(tuple(x), equals(x, 3)), array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +3 +""" + +Table___Decimal256_0____arrayFirst_x____x__3__ = r""" +a +3 +""" + +Inline___Decimal256_0____arrayFirstIndex_x____x__3__ = r""" +arrayFirstIndex(lambda(tuple(x), equals(x, 3)), array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +1 +""" + +Table___Decimal256_0____arrayFirstIndex_x____x__3__ = r""" +a +1 +""" + +Inline___Decimal256_0____hasAll__toDecimal256__3__0___toDecimal256__2__0___toDecimal256__1__0_____ = r""" +hasAll(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0)), array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +1 +""" + +Table___Decimal256_0____hasAll__toDecimal256__3__0___toDecimal256__2__0___toDecimal256__1__0_____ = r""" +a +1 +""" + +Inline___Decimal256_0____hasAny__toDecimal256__2__0___toDecimal256__1__0_____ = r""" +hasAny(array(toDecimal256(\'2\', 0), toDecimal256(\'1\', 0)), array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +1 +""" + +Table___Decimal256_0____hasAny__toDecimal256__2__0___toDecimal256__1__0_____ = r""" +a +1 +""" + +Inline___Decimal256_0____hasSubstr__toDecimal256__2__0___toDecimal256__1__0_____ = r""" +hasSubstr(array(toDecimal256(\'2\', 0), toDecimal256(\'1\', 0)), array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +0 +""" + +Table___Decimal256_0____hasSubstr__toDecimal256__2__0___toDecimal256__1__0_____ = r""" +a +0 +""" + +Table___Decimal256_0____arrayDifference__ = r""" +a +""" + +Table___Decimal256_0____arrayCumSum__ = r""" +a +""" + +Table___Decimal256_0____arrayCumSumNonNegative__ = r""" +a +""" + +Inline___Decimal256_0____arrayElement = r""" +arrayElement(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0)), 1) +3 +""" + +Table___Decimal256_0____arrayElement = r""" +a +3 +""" + +Inline___Decimal256_0____arrayPushBack = r""" +arrayPushBack(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0)), toDecimal256(\'1\', 0)) +[3,2,1,1] +""" + +Table___Decimal256_0____arrayPushBack = r""" +a +[3,2,1,1] +""" + +Inline___Decimal256_0____arrayPushFront = r""" +arrayPushFront(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0)), toDecimal256(\'1\', 0)) +[1,3,2,1] +""" + +Table___Decimal256_0____arrayPushFront = r""" +a +[1,3,2,1] +""" + +Inline___Decimal256_0____arrayResize = r""" +arrayResize(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0)), 1) +[3] +""" + +Table___Decimal256_0____arrayResize = r""" +a +[3] +""" + +Inline___Decimal256_0____arraySlice = r""" +arraySlice(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0)), 1) +[3,2,1] +""" + +Table___Decimal256_0____arraySlice = r""" +a +[3,2,1] +""" + +Inline___Decimal256_0____has = r""" +has(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0)), NULL) +0 +""" + +Table___Decimal256_0____has = r""" +a +0 +""" + +Inline___Decimal256_0____indexOf = r""" +indexOf(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0)), NULL) +0 +""" + +Table___Decimal256_0____indexOf = r""" +a +0 +""" + +Inline___Decimal256_0____countEqual = r""" +countEqual(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0)), NULL) +0 +""" + +Table___Decimal256_0____countEqual = r""" +a +0 +""" + +Creating_a_tuple_with_Decimal256_0__on_a_table = r""" +a +(1,1,1) +""" + +tupleElement_with_Decimal256_0__on_a_table = r""" +a +1 +""" + +untuple_with_Decimal256_0__on_a_table = r""" +a +1 +""" + +tupleHammingDistance_with_Decimal256_0__on_a_table = r""" +a +2 +""" + +Creating_a_map_with_Decimal256_0__on_a_table = r""" +a +{'key1':1,'key2':2} +""" + +mapAdd_with_Decimal256_0__on_a_table = r""" +a +""" + +mapSubtract_with_Decimal256_0__on_a_table = r""" +a +""" + +mapPopulateSeries_with_Decimal256_0__on_a_table = r""" +a +""" + +mapContains_with_Decimal256_0__on_a_table = r""" +a +1 +""" + +mapKeys_with_Decimal256_0__on_a_table = r""" +a +['key1','key2'] +""" + +mapValues_with_Decimal256_0__on_a_table = r""" +a +[1,2] +""" + +I_check_equals_with_Int128 = r""" +equals(toInt128(1), toInt128(1)) equals(toInt128(\'170141183460469231731687303715884105727\'), toInt128(\'-170141183460469231731687303715884105728\')) +1 0 +""" + +I_check_equals_with_Int256 = r""" +equals(toInt256(1), toInt256(1)) equals(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) +1 0 +""" + +I_check_equals_with_UInt128 = r""" +equals(toUInt128(1), toUInt128(1)) equals(toUInt128(\'340282366920938463463374607431768211455\'), toUInt128(\'0\')) +1 0 +""" + +I_check_equals_with_UInt256 = r""" +equals(toUInt256(1), toUInt256(1)) equals(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), toUInt256(\'0\')) +1 0 +""" + +I_check_notEquals_with_Int128 = r""" +notEquals(toInt128(1), toInt128(1)) notEquals(toInt128(\'170141183460469231731687303715884105727\'), toInt128(\'-170141183460469231731687303715884105728\')) +0 1 +""" + +I_check_notEquals_with_Int256 = r""" +notEquals(toInt256(1), toInt256(1)) notEquals(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) +0 1 +""" + +I_check_notEquals_with_UInt128 = r""" +notEquals(toUInt128(1), toUInt128(1)) notEquals(toUInt128(\'340282366920938463463374607431768211455\'), toUInt128(\'0\')) +0 1 +""" + +I_check_notEquals_with_UInt256 = r""" +notEquals(toUInt256(1), toUInt256(1)) notEquals(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), toUInt256(\'0\')) +0 1 +""" + +I_check_less_with_Int128 = r""" +less(toInt128(1), toInt128(1)) less(toInt128(\'170141183460469231731687303715884105727\'), toInt128(\'-170141183460469231731687303715884105728\')) +0 0 +""" + +I_check_less_with_Int256 = r""" +less(toInt256(1), toInt256(1)) less(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) +0 0 +""" + +I_check_less_with_UInt128 = r""" +less(toUInt128(1), toUInt128(1)) less(toUInt128(\'340282366920938463463374607431768211455\'), toUInt128(\'0\')) +0 0 +""" + +I_check_less_with_UInt256 = r""" +less(toUInt256(1), toUInt256(1)) less(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), toUInt256(\'0\')) +0 0 +""" + +I_check_greater_with_Int128 = r""" +greater(toInt128(1), toInt128(1)) greater(toInt128(\'170141183460469231731687303715884105727\'), toInt128(\'-170141183460469231731687303715884105728\')) +0 1 +""" + +I_check_greater_with_Int256 = r""" +greater(toInt256(1), toInt256(1)) greater(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) +0 1 +""" + +I_check_greater_with_UInt128 = r""" +greater(toUInt128(1), toUInt128(1)) greater(toUInt128(\'340282366920938463463374607431768211455\'), toUInt128(\'0\')) +0 1 +""" + +I_check_greater_with_UInt256 = r""" +greater(toUInt256(1), toUInt256(1)) greater(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), toUInt256(\'0\')) +0 1 +""" + +I_check_lessOrEquals_with_Int128 = r""" +lessOrEquals(toInt128(1), toInt128(1)) lessOrEquals(toInt128(\'170141183460469231731687303715884105727\'), toInt128(\'-170141183460469231731687303715884105728\')) +1 0 +""" + +I_check_lessOrEquals_with_Int256 = r""" +lessOrEquals(toInt256(1), toInt256(1)) lessOrEquals(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) +1 0 +""" + +I_check_lessOrEquals_with_UInt128 = r""" +lessOrEquals(toUInt128(1), toUInt128(1)) lessOrEquals(toUInt128(\'340282366920938463463374607431768211455\'), toUInt128(\'0\')) +1 0 +""" + +I_check_lessOrEquals_with_UInt256 = r""" +lessOrEquals(toUInt256(1), toUInt256(1)) lessOrEquals(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), toUInt256(\'0\')) +1 0 +""" + +I_check_greaterOrEquals_with_Int128 = r""" +greaterOrEquals(toInt128(1), toInt128(1)) greaterOrEquals(toInt128(\'170141183460469231731687303715884105727\'), toInt128(\'-170141183460469231731687303715884105728\')) +1 1 +""" + +I_check_greaterOrEquals_with_Int256 = r""" +greaterOrEquals(toInt256(1), toInt256(1)) greaterOrEquals(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) +1 1 +""" + +I_check_greaterOrEquals_with_UInt128 = r""" +greaterOrEquals(toUInt128(1), toUInt128(1)) greaterOrEquals(toUInt128(\'340282366920938463463374607431768211455\'), toUInt128(\'0\')) +1 1 +""" + +I_check_greaterOrEquals_with_UInt256 = r""" +greaterOrEquals(toUInt256(1), toUInt256(1)) greaterOrEquals(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), toUInt256(\'0\')) +1 1 +""" + +I_check_the_table_for_the_output_of_equals_with_Int128 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_equals_with_Int256 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_equals_with_UInt128 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_equals_with_UInt256 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_notEquals_with_Int128 = r""" +a +0 +1 +1 +""" + +I_check_the_table_for_the_output_of_notEquals_with_Int256 = r""" +a +0 +1 +1 +""" + +I_check_the_table_for_the_output_of_notEquals_with_UInt128 = r""" +a +0 +1 +1 +""" + +I_check_the_table_for_the_output_of_notEquals_with_UInt256 = r""" +a +0 +1 +1 +""" + +I_check_the_table_for_the_output_of_less_with_Int128 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_less_with_Int256 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_less_with_UInt128 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_less_with_UInt256 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_greater_with_Int128 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_greater_with_Int256 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_greater_with_UInt128 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_greater_with_UInt256 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_lessOrEquals_with_Int128 = r""" +a +0 +1 +1 +""" + +I_check_the_table_for_the_output_of_lessOrEquals_with_Int256 = r""" +a +0 +1 +1 +""" + +I_check_the_table_for_the_output_of_lessOrEquals_with_UInt128 = r""" +a +0 +1 +1 +""" + +I_check_the_table_for_the_output_of_lessOrEquals_with_UInt256 = r""" +a +0 +1 +1 +""" + +I_check_the_table_for_the_output_of_greaterOrEquals_with_Int128 = r""" +a +0 +1 +1 +""" + +I_check_the_table_for_the_output_of_greaterOrEquals_with_Int256 = r""" +a +0 +1 +1 +""" + +I_check_the_table_for_the_output_of_greaterOrEquals_with_UInt128 = r""" +a +0 +1 +1 +""" + +I_check_the_table_for_the_output_of_greaterOrEquals_with_UInt256 = r""" +a +0 +1 +1 +""" + +I_check_equals_with_Decimal256 = r""" +equals(toDecimal256(1, 0), toDecimal256(1, 0)) equals(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0), toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +1 0 +""" + +I_check_notEquals_with_Decimal256 = r""" +notEquals(toDecimal256(1, 0), toDecimal256(1, 0)) notEquals(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0), toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +0 1 +""" + +I_check_less_with_Decimal256 = r""" +less(toDecimal256(1, 0), toDecimal256(1, 0)) less(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0), toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +0 0 +""" + +I_check_greater_with_Decimal256 = r""" +greater(toDecimal256(1, 0), toDecimal256(1, 0)) greater(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0), toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +0 1 +""" + +I_check_lessOrEquals_with_Decimal256 = r""" +lessOrEquals(toDecimal256(1, 0), toDecimal256(1, 0)) lessOrEquals(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0), toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +1 0 +""" + +I_check_greaterOrEquals_with_Decimal256 = r""" +greaterOrEquals(toDecimal256(1, 0), toDecimal256(1, 0)) greaterOrEquals(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0), toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +1 1 +""" + +I_check_the_table_for_the_output_of_equals_with_Decimal256 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_notEquals_with_Decimal256 = r""" +a +0 +1 +1 +""" + +I_check_the_table_for_the_output_of_less_with_Decimal256 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_greater_with_Decimal256 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_lessOrEquals_with_Decimal256 = r""" +a +0 +1 +1 +""" + +I_check_the_table_for_the_output_of_greaterOrEquals_with_Decimal256 = r""" +a +0 +1 +1 +""" + +I_check_exp__with_Int128_using_max_and_min = r""" +round(exp(toInt128(\'170141183460469231731687303715884105727\')), 7) round(exp(toInt128(\'-170141183460469231731687303715884105728\')), 7) +inf 0 +""" + +I_check_exp__with_Int256_using_max_and_min = r""" +round(exp(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(exp(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +inf 0 +""" + +I_check_exp__with_UInt128_using_max_and_min = r""" +round(exp(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(exp(toUInt128(\'0\')), 7) +inf 1 +""" + +I_check_exp__with_UInt256_using_max_and_min = r""" +round(exp(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(exp(toUInt256(\'0\')), 7) +inf 1 +""" + +I_check_log__with_Int128_using_max_and_min = r""" +round(log(toInt128(\'170141183460469231731687303715884105727\')), 7) round(log(toInt128(\'-170141183460469231731687303715884105728\')), 7) +88.0296919 nan +""" + +I_check_log__with_Int256_using_max_and_min = r""" +round(log(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(log(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +176.752531 nan +""" + +I_check_log__with_UInt128_using_max_and_min = r""" +round(log(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(log(toUInt128(\'0\')), 7) +88.7228391 -inf +""" + +I_check_log__with_UInt256_using_max_and_min = r""" +round(log(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(log(toUInt256(\'0\')), 7) +177.4456782 -inf +""" + +I_check_ln__with_Int128_using_max_and_min = r""" +round(log(toInt128(\'170141183460469231731687303715884105727\')), 7) round(log(toInt128(\'-170141183460469231731687303715884105728\')), 7) +88.0296919 nan +""" + +I_check_ln__with_Int256_using_max_and_min = r""" +round(log(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(log(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +176.752531 nan +""" + +I_check_ln__with_UInt128_using_max_and_min = r""" +round(log(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(log(toUInt128(\'0\')), 7) +88.7228391 -inf +""" + +I_check_ln__with_UInt256_using_max_and_min = r""" +round(log(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(log(toUInt256(\'0\')), 7) +177.4456782 -inf +""" + +I_check_exp2__with_Int128_using_max_and_min = r""" +round(exp2(toInt128(\'170141183460469231731687303715884105727\')), 7) round(exp2(toInt128(\'-170141183460469231731687303715884105728\')), 7) +inf 0 +""" + +I_check_exp2__with_Int256_using_max_and_min = r""" +round(exp2(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(exp2(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +inf 0 +""" + +I_check_exp2__with_UInt128_using_max_and_min = r""" +round(exp2(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(exp2(toUInt128(\'0\')), 7) +inf 1 +""" + +I_check_exp2__with_UInt256_using_max_and_min = r""" +round(exp2(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(exp2(toUInt256(\'0\')), 7) +inf 1 +""" + +I_check_log2__with_Int128_using_max_and_min = r""" +round(log2(toInt128(\'170141183460469231731687303715884105727\')), 7) round(log2(toInt128(\'-170141183460469231731687303715884105728\')), 7) +127 nan +""" + +I_check_log2__with_Int256_using_max_and_min = r""" +round(log2(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(log2(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +255 nan +""" + +I_check_log2__with_UInt128_using_max_and_min = r""" +round(log2(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(log2(toUInt128(\'0\')), 7) +128 -inf +""" + +I_check_log2__with_UInt256_using_max_and_min = r""" +round(log2(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(log2(toUInt256(\'0\')), 7) +256 -inf +""" + +I_check_exp10__with_Int128_using_max_and_min = r""" +round(exp10(toInt128(\'170141183460469231731687303715884105727\')), 7) round(exp10(toInt128(\'-170141183460469231731687303715884105728\')), 7) +inf 0 +""" + +I_check_exp10__with_Int256_using_max_and_min = r""" +round(exp10(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(exp10(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +inf 0 +""" + +I_check_exp10__with_UInt128_using_max_and_min = r""" +round(exp10(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(exp10(toUInt128(\'0\')), 7) +inf 1 +""" + +I_check_exp10__with_UInt256_using_max_and_min = r""" +round(exp10(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(exp10(toUInt256(\'0\')), 7) +inf 1 +""" + +I_check_log10__with_Int128_using_max_and_min = r""" +round(log10(toInt128(\'170141183460469231731687303715884105727\')), 7) round(log10(toInt128(\'-170141183460469231731687303715884105728\')), 7) +38.2308094 nan +""" + +I_check_log10__with_Int256_using_max_and_min = r""" +round(log10(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(log10(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +76.7626489 nan +""" + +I_check_log10__with_UInt128_using_max_and_min = r""" +round(log10(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(log10(toUInt128(\'0\')), 7) +38.5318394 -inf +""" + +I_check_log10__with_UInt256_using_max_and_min = r""" +round(log10(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(log10(toUInt256(\'0\')), 7) +77.0636789 -inf +""" + +I_check_sqrt__with_Int128_using_max_and_min = r""" +round(sqrt(toInt128(\'170141183460469231731687303715884105727\')), 7) round(sqrt(toInt128(\'-170141183460469231731687303715884105728\')), 7) +13043817825332783000 nan +""" + +I_check_sqrt__with_Int256_using_max_and_min = r""" +round(sqrt(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(sqrt(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +2.4061596916800453e38 nan +""" + +I_check_sqrt__with_UInt128_using_max_and_min = r""" +round(sqrt(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(sqrt(toUInt128(\'0\')), 7) +18446744073709552000 0 +""" + +I_check_sqrt__with_UInt256_using_max_and_min = r""" +round(sqrt(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(sqrt(toUInt256(\'0\')), 7) +3.402823669209385e38 0 +""" + +I_check_cbrt__with_Int128_using_max_and_min = r""" +round(cbrt(toInt128(\'170141183460469231731687303715884105727\')), 7) round(cbrt(toInt128(\'-170141183460469231731687303715884105728\')), 7) +5541191377756.637 -5541191377756.637 +""" + +I_check_cbrt__with_Int256_using_max_and_min = r""" +round(cbrt(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(cbrt(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +3.8685626227668134e25 -3.8685626227668134e25 +""" + +I_check_cbrt__with_UInt128_using_max_and_min = r""" +round(cbrt(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(cbrt(toUInt128(\'0\')), 7) +6981463658331.56 0 +""" + +I_check_cbrt__with_UInt256_using_max_and_min = r""" +round(cbrt(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(cbrt(toUInt256(\'0\')), 7) +4.874083481260429e25 0 +""" + +I_check_erf__with_Int128_using_max_and_min = r""" +round(erf(toInt128(\'170141183460469231731687303715884105727\')), 7) round(erf(toInt128(\'-170141183460469231731687303715884105728\')), 7) +1 -1 +""" + +I_check_erf__with_Int256_using_max_and_min = r""" +round(erf(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(erf(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +1 -1 +""" + +I_check_erf__with_UInt128_using_max_and_min = r""" +round(erf(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(erf(toUInt128(\'0\')), 7) +1 0 +""" + +I_check_erf__with_UInt256_using_max_and_min = r""" +round(erf(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(erf(toUInt256(\'0\')), 7) +1 0 +""" + +I_check_erfc__with_Int128_using_max_and_min = r""" +round(erfc(toInt128(\'170141183460469231731687303715884105727\')), 7) round(erfc(toInt128(\'-170141183460469231731687303715884105728\')), 7) +0 2 +""" + +I_check_erfc__with_Int256_using_max_and_min = r""" +round(erfc(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(erfc(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +0 2 +""" + +I_check_erfc__with_UInt128_using_max_and_min = r""" +round(erfc(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(erfc(toUInt128(\'0\')), 7) +0 1 +""" + +I_check_erfc__with_UInt256_using_max_and_min = r""" +round(erfc(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(erfc(toUInt256(\'0\')), 7) +0 1 +""" + +I_check_lgamma__with_Int128_using_max_and_min = r""" +round(lgamma(toInt128(\'170141183460469231731687303715884105727\')), 7) round(lgamma(toInt128(\'-170141183460469231731687303715884105728\')), 7) +1.4807334781359624e40 -1.4807334781359624e40 +""" + +I_check_lgamma__with_Int256_using_max_and_min = r""" +round(lgamma(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(lgamma(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +1.0175376379095233e79 -1.0175376379095233e79 +""" + +I_check_lgamma__with_UInt128_using_max_and_min = r""" +round(lgamma(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(lgamma(toUInt128(\'0\')), 7) +2.985053532594476e40 inf +""" + +I_check_lgamma__with_UInt256_using_max_and_min = r""" +round(lgamma(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(lgamma(toUInt256(\'0\')), 7) +2.0431013718376458e79 inf +""" + +I_check_tgamma__with_Int128_using_max_and_min = r""" +round(tgamma(toInt128(\'170141183460469231731687303715884105727\')), 7) round(tgamma(toInt128(\'-170141183460469231731687303715884105728\')), 7) +inf nan +""" + +I_check_tgamma__with_Int256_using_max_and_min = r""" +round(tgamma(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(tgamma(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +inf nan +""" + +I_check_tgamma__with_UInt128_using_max_and_min = r""" +round(tgamma(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(tgamma(toUInt128(\'0\')), 7) +inf inf +""" + +I_check_tgamma__with_UInt256_using_max_and_min = r""" +round(tgamma(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(tgamma(toUInt256(\'0\')), 7) +inf inf +""" + +I_check_sin__with_Int128_using_max_and_min = r""" +round(sin(toInt128(\'170141183460469231731687303715884105727\')), 7) round(sin(toInt128(\'-170141183460469231731687303715884105728\')), 7) +0.6233855 -0.6233855 +""" + +I_check_sin__with_Int256_using_max_and_min = r""" +round(sin(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(sin(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +0.9751222 -0.9751222 +""" + +I_check_sin__with_UInt128_using_max_and_min = r""" +round(sin(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(sin(toUInt128(\'0\')), 7) +0.9748685 0 +""" + +I_check_sin__with_UInt256_using_max_and_min = r""" +round(sin(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(sin(toUInt256(\'0\')), 7) +0.4323066 0 +""" + +I_check_cos__with_Int128_using_max_and_min = r""" +round(cos(toInt128(\'170141183460469231731687303715884105727\')), 7) round(cos(toInt128(\'-170141183460469231731687303715884105728\')), 7) +0.7819146 0.7819146 +""" + +I_check_cos__with_Int256_using_max_and_min = r""" +round(cos(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(cos(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +0.2216679 0.2216679 +""" + +I_check_cos__with_UInt128_using_max_and_min = r""" +round(cos(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(cos(toUInt128(\'0\')), 7) +0.222781 1 +""" + +I_check_cos__with_UInt256_using_max_and_min = r""" +round(cos(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(cos(toUInt256(\'0\')), 7) +-0.9017267 1 +""" + +I_check_tan__with_Int128_using_max_and_min = r""" +round(tan(toInt128(\'170141183460469231731687303715884105727\')), 7) round(tan(toInt128(\'-170141183460469231731687303715884105728\')), 7) +0.7972552 -0.7972552 +""" + +I_check_tan__with_Int256_using_max_and_min = r""" +round(tan(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(tan(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +4.3990229 -4.3990229 +""" + +I_check_tan__with_UInt128_using_max_and_min = r""" +round(tan(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(tan(toUInt128(\'0\')), 7) +4.375905 0 +""" + +I_check_tan__with_UInt256_using_max_and_min = r""" +round(tan(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(tan(toUInt256(\'0\')), 7) +-0.4794209 0 +""" + +I_check_asin__with_Int128_using_max_and_min = r""" +round(asin(toInt128(\'170141183460469231731687303715884105727\')), 7) round(asin(toInt128(\'-170141183460469231731687303715884105728\')), 7) +nan nan +""" + +I_check_asin__with_Int256_using_max_and_min = r""" +round(asin(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(asin(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +nan nan +""" + +I_check_asin__with_UInt128_using_max_and_min = r""" +round(asin(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(asin(toUInt128(\'0\')), 7) +nan 0 +""" + +I_check_asin__with_UInt256_using_max_and_min = r""" +round(asin(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(asin(toUInt256(\'0\')), 7) +nan 0 +""" + +I_check_acos__with_Int128_using_max_and_min = r""" +round(acos(toInt128(\'170141183460469231731687303715884105727\')), 7) round(acos(toInt128(\'-170141183460469231731687303715884105728\')), 7) +nan nan +""" + +I_check_acos__with_Int256_using_max_and_min = r""" +round(acos(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(acos(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +nan nan +""" + +I_check_acos__with_UInt128_using_max_and_min = r""" +round(acos(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(acos(toUInt128(\'0\')), 7) +nan 1.5707963 +""" + +I_check_acos__with_UInt256_using_max_and_min = r""" +round(acos(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(acos(toUInt256(\'0\')), 7) +nan 1.5707963 +""" + +I_check_atan__with_Int128_using_max_and_min = r""" +round(atan(toInt128(\'170141183460469231731687303715884105727\')), 7) round(atan(toInt128(\'-170141183460469231731687303715884105728\')), 7) +1.5707963 -1.5707963 +""" + +I_check_atan__with_Int256_using_max_and_min = r""" +round(atan(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(atan(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +1.5707963 -1.5707963 +""" + +I_check_atan__with_UInt128_using_max_and_min = r""" +round(atan(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(atan(toUInt128(\'0\')), 7) +1.5707963 0 +""" + +I_check_atan__with_UInt256_using_max_and_min = r""" +round(atan(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(atan(toUInt256(\'0\')), 7) +1.5707963 0 +""" + +I_check_cosh__with_Int128_using_max_and_min = r""" +round(cosh(toInt128(\'170141183460469231731687303715884105727\')), 7) round(cosh(toInt128(\'-170141183460469231731687303715884105728\')), 7) +inf inf +""" + +I_check_cosh__with_Int256_using_max_and_min = r""" +round(cosh(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(cosh(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +inf inf +""" + +I_check_cosh__with_UInt128_using_max_and_min = r""" +round(cosh(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(cosh(toUInt128(\'0\')), 7) +inf 1 +""" + +I_check_cosh__with_UInt256_using_max_and_min = r""" +round(cosh(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(cosh(toUInt256(\'0\')), 7) +inf 1 +""" + +I_check_acosh__with_Int128_using_max_and_min = r""" +round(acosh(toInt128(\'170141183460469231731687303715884105727\')), 7) round(acosh(toInt128(\'-170141183460469231731687303715884105728\')), 7) +88.7228391 nan +""" + +I_check_acosh__with_Int256_using_max_and_min = r""" +round(acosh(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(acosh(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +177.4456782 nan +""" + +I_check_acosh__with_UInt128_using_max_and_min = r""" +round(acosh(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(acosh(toUInt128(\'0\')), 7) +89.4159863 nan +""" + +I_check_acosh__with_UInt256_using_max_and_min = r""" +round(acosh(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(acosh(toUInt256(\'0\')), 7) +178.1388254 nan +""" + +I_check_sinh__with_Int128_using_max_and_min = r""" +round(sinh(toInt128(\'170141183460469231731687303715884105727\')), 7) round(sinh(toInt128(\'-170141183460469231731687303715884105728\')), 7) +inf -inf +""" + +I_check_sinh__with_Int256_using_max_and_min = r""" +round(sinh(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(sinh(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +inf -inf +""" + +I_check_sinh__with_UInt128_using_max_and_min = r""" +round(sinh(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(sinh(toUInt128(\'0\')), 7) +inf 0 +""" + +I_check_sinh__with_UInt256_using_max_and_min = r""" +round(sinh(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(sinh(toUInt256(\'0\')), 7) +inf 0 +""" + +I_check_asinh__with_Int128_using_max_and_min = r""" +round(asinh(toInt128(\'170141183460469231731687303715884105727\')), 7) round(asinh(toInt128(\'-170141183460469231731687303715884105728\')), 7) +88.7228391 -88.7228391 +""" + +I_check_asinh__with_Int256_using_max_and_min = r""" +round(asinh(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(asinh(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +177.4456782 -177.4456782 +""" + +I_check_asinh__with_UInt128_using_max_and_min = r""" +round(asinh(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(asinh(toUInt128(\'0\')), 7) +89.4159863 0 +""" + +I_check_asinh__with_UInt256_using_max_and_min = r""" +round(asinh(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(asinh(toUInt256(\'0\')), 7) +178.1388254 0 +""" + +I_check_tanh__with_Int128_using_max_and_min = r""" +round(tanh(toInt128(\'170141183460469231731687303715884105727\')), 7) round(tanh(toInt128(\'-170141183460469231731687303715884105728\')), 7) +1 -1 +""" + +I_check_tanh__with_Int256_using_max_and_min = r""" +round(tanh(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(tanh(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +1 -1 +""" + +I_check_tanh__with_UInt128_using_max_and_min = r""" +round(tanh(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(tanh(toUInt128(\'0\')), 7) +1 0 +""" + +I_check_tanh__with_UInt256_using_max_and_min = r""" +round(tanh(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(tanh(toUInt256(\'0\')), 7) +1 0 +""" + +I_check_atanh__with_Int128_using_max_and_min = r""" +round(atanh(toInt128(\'170141183460469231731687303715884105727\')), 7) round(atanh(toInt128(\'-170141183460469231731687303715884105728\')), 7) +nan nan +""" + +I_check_atanh__with_Int256_using_max_and_min = r""" +round(atanh(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(atanh(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +nan nan +""" + +I_check_atanh__with_UInt128_using_max_and_min = r""" +round(atanh(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(atanh(toUInt128(\'0\')), 7) +nan 0 +""" + +I_check_atanh__with_UInt256_using_max_and_min = r""" +round(atanh(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(atanh(toUInt256(\'0\')), 7) +nan 0 +""" + +I_check_log1p__with_Int128_using_max_and_min = r""" +round(log1p(toInt128(\'170141183460469231731687303715884105727\')), 7) round(log1p(toInt128(\'-170141183460469231731687303715884105728\')), 7) +88.0296919 nan +""" + +I_check_log1p__with_Int256_using_max_and_min = r""" +round(log1p(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(log1p(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +176.752531 nan +""" + +I_check_log1p__with_UInt128_using_max_and_min = r""" +round(log1p(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(log1p(toUInt128(\'0\')), 7) +88.7228391 0 +""" + +I_check_log1p__with_UInt256_using_max_and_min = r""" +round(log1p(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(log1p(toUInt256(\'0\')), 7) +177.4456782 0 +""" + +I_check_sign__with_Int128_using_max_and_min = r""" +round(sign(toInt128(\'170141183460469231731687303715884105727\')), 7) round(sign(toInt128(\'-170141183460469231731687303715884105728\')), 7) +1 -1 +""" + +I_check_sign__with_Int256_using_max_and_min = r""" +round(sign(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(sign(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +1 -1 +""" + +I_check_sign__with_UInt128_using_max_and_min = r""" +round(sign(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(sign(toUInt128(\'0\')), 7) +1 0 +""" + +I_check_sign__with_UInt256_using_max_and_min = r""" +round(sign(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(sign(toUInt256(\'0\')), 7) +1 0 +""" + +I_check_the_outputs_of_exp__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_exp__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_exp__with_UInt128 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_exp__with_UInt256 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_log__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_log__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_log__with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_log__with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_ln__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_ln__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_ln__with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_ln__with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_exp2__with_Int128 = r""" +a +0 +0 +2 +""" + +I_check_the_outputs_of_exp2__with_Int256 = r""" +a +0 +0 +2 +""" + +I_check_the_outputs_of_exp2__with_UInt128 = r""" +a +0 +1 +2 +""" + +I_check_the_outputs_of_exp2__with_UInt256 = r""" +a +0 +1 +2 +""" + +I_check_the_outputs_of_log2__with_Int128 = r""" +a +0 +0 +127 +""" + +I_check_the_outputs_of_log2__with_Int256 = r""" +a +0 +0 +255 +""" + +I_check_the_outputs_of_log2__with_UInt128 = r""" +a +0 +0 +128 +""" + +I_check_the_outputs_of_log2__with_UInt256 = r""" +a +0 +0 +256 +""" + +I_check_the_outputs_of_exp10__with_Int128 = r""" +a +0 +0 +10 +""" + +I_check_the_outputs_of_exp10__with_Int256 = r""" +a +0 +0 +10 +""" + +I_check_the_outputs_of_exp10__with_UInt128 = r""" +a +0 +1 +10 +""" + +I_check_the_outputs_of_exp10__with_UInt256 = r""" +a +0 +1 +10 +""" + +I_check_the_outputs_of_log10__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_log10__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_log10__with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_log10__with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_sqrt__with_Int128 = r""" +a +0 +1 +13043817825332783000 +""" + +I_check_the_outputs_of_sqrt__with_Int256 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_sqrt__with_UInt128 = r""" +a +0 +1 +18446744073709552000 +""" + +I_check_the_outputs_of_sqrt__with_UInt256 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_cbrt__with_Int128 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_cbrt__with_Int256 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_cbrt__with_UInt128 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_cbrt__with_UInt256 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_erf__with_Int128 = r""" +a +-1 +0 +1 +""" + +I_check_the_outputs_of_erf__with_Int256 = r""" +a +-1 +0 +1 +""" + +I_check_the_outputs_of_erf__with_UInt128 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_erf__with_UInt256 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_erfc__with_Int128 = r""" +a +0 +0 +2 +""" + +I_check_the_outputs_of_erfc__with_Int256 = r""" +a +0 +0 +2 +""" + +I_check_the_outputs_of_erfc__with_UInt128 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_erfc__with_UInt256 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_lgamma__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_lgamma__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_lgamma__with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_lgamma__with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_tgamma__with_Int128 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_tgamma__with_Int256 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_tgamma__with_UInt128 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_tgamma__with_UInt256 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_sin__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_sin__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_sin__with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_sin__with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_cos__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_cos__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_cos__with_UInt128 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_cos__with_UInt256 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_tan__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_tan__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_tan__with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_tan__with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_asin__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_asin__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_asin__with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_asin__with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_acos__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_acos__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_acos__with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_acos__with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_atan__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_atan__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_atan__with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_atan__with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_cosh__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_cosh__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_cosh__with_UInt128 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_cosh__with_UInt256 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_acosh__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_acosh__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_acosh__with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_acosh__with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_sinh__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_sinh__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_sinh__with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_sinh__with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_asinh__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_asinh__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_asinh__with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_asinh__with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_tanh__with_Int128 = r""" +a +-1 +0 +1 +""" + +I_check_the_outputs_of_tanh__with_Int256 = r""" +a +-1 +0 +1 +""" + +I_check_the_outputs_of_tanh__with_UInt128 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_tanh__with_UInt256 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_atanh__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_atanh__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_atanh__with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_atanh__with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_log1p__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_log1p__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_log1p__with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_log1p__with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_sign__with_Int128 = r""" +a +-1 +1 +1 +""" + +I_check_the_outputs_of_sign__with_Int256 = r""" +a +-1 +1 +1 +""" + +I_check_the_outputs_of_sign__with_UInt128 = r""" +a +0 +1 +1 +""" + +I_check_the_outputs_of_sign__with_UInt256 = r""" +a +0 +1 +1 +""" + +I_check_exp__with_Decimal256_using_max_and_min = r""" +round(exp(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(exp(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +inf 0 +""" + +I_check_log__with_Decimal256_using_max_and_min = r""" +round(log(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(log(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +172.693882 nan +""" + +I_check_ln__with_Decimal256_using_max_and_min = r""" +round(log(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(log(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +172.693882 nan +""" + +I_check_exp2__with_Decimal256_using_max_and_min = r""" +round(exp2(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(exp2(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +inf 0 +""" + +I_check_log2__with_Decimal256_using_max_and_min = r""" +round(log2(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(log2(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +249.1446071 nan +""" + +I_check_exp10__with_Decimal256_using_max_and_min = r""" +round(exp10(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(exp10(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +inf 0 +""" + +I_check_log10__with_Decimal256_using_max_and_min = r""" +round(log10(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(log10(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +75 nan +""" + +I_check_sqrt__with_Decimal256_using_max_and_min = r""" +round(sqrt(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(sqrt(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +3.1622776601683794e37 nan +""" + +I_check_cbrt__with_Decimal256_using_max_and_min = r""" +round(cbrt(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(cbrt(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +1e25 -1e25 +""" + +I_check_erf__with_Decimal256_using_max_and_min = r""" +round(erf(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(erf(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +1 -1 +""" + +I_check_erfc__with_Decimal256_using_max_and_min = r""" +round(erfc(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(erfc(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +0 2 +""" + +I_check_lgamma__with_Decimal256_using_max_and_min = r""" +round(lgamma(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(lgamma(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +1.7169388197455342e77 -1.7169388197455342e77 +""" + +I_check_tgamma__with_Decimal256_using_max_and_min = r""" +round(tgamma(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(tgamma(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +inf nan +""" + +I_check_sin__with_Decimal256_using_max_and_min = r""" +round(sin(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(sin(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +0.6633998 -0.6633998 +""" + +I_check_cos__with_Decimal256_using_max_and_min = r""" +round(cos(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(cos(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +-0.7482652 -0.7482652 +""" + +I_check_tan__with_Decimal256_using_max_and_min = r""" +round(tan(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(tan(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +-0.8865838 0.8865838 +""" + +I_check_asin__with_Decimal256_using_max_and_min = r""" +round(asin(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(asin(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +nan nan +""" + +I_check_acos__with_Decimal256_using_max_and_min = r""" +round(acos(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(acos(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +nan nan +""" + +I_check_atan__with_Decimal256_using_max_and_min = r""" +round(atan(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(atan(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +1.5707963 -1.5707963 +""" + +I_check_cosh__with_Decimal256_using_max_and_min = r""" +round(cosh(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(cosh(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +inf inf +""" + +I_check_acosh__with_Decimal256_using_max_and_min = r""" +round(acosh(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(acosh(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +173.3870292 nan +""" + +I_check_sinh__with_Decimal256_using_max_and_min = r""" +round(sinh(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(sinh(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +inf -inf +""" + +I_check_asinh__with_Decimal256_using_max_and_min = r""" +round(asinh(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(asinh(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +173.3870292 -173.3870292 +""" + +I_check_tanh__with_Decimal256_using_max_and_min = r""" +round(tanh(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(tanh(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +1 -1 +""" + +I_check_atanh__with_Decimal256_using_max_and_min = r""" +round(atanh(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(atanh(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +nan nan +""" + +I_check_log1p__with_Decimal256_using_max_and_min = r""" +round(log1p(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(log1p(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +172.693882 nan +""" + +I_check_sign__with_Decimal256_using_max_and_min = r""" +round(sign(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(sign(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +1 -1 +""" + +I_check_the_outputs_of_exp__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_log__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_ln__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_exp2__with_Decimal256 = r""" +a +0 +0 +2 +""" + +I_check_the_outputs_of_log2__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_exp10__with_Decimal256 = r""" +a +0 +0 +10 +""" + +I_check_the_outputs_of_log10__with_Decimal256 = r""" +a +0 +0 +75 +""" + +I_check_the_outputs_of_sqrt__with_Decimal256 = r""" +a +0 +1 +31622776601683794000000000000000000000 +""" + +I_check_the_outputs_of_cbrt__with_Decimal256 = r""" +a +-10000000000000000000000000 +1 +10000000000000000000000000 +""" + +I_check_the_outputs_of_erf__with_Decimal256 = r""" +a +-1 +0 +1 +""" + +I_check_the_outputs_of_erfc__with_Decimal256 = r""" +a +0 +0 +2 +""" + +I_check_the_outputs_of_lgamma__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_tgamma__with_Decimal256 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_sin__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_cos__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_tan__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_asin__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_acos__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_atan__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_cosh__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_acosh__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_sinh__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_asinh__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_tanh__with_Decimal256 = r""" +a +-1 +0 +1 +""" + +I_check_the_outputs_of_atanh__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_log1p__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_sign__with_Decimal256 = r""" +a +-1 +1 +1 +""" + +I_check_ceil_with_Int128_using_min_and_max_values = r""" +ceil(toInt128(\'-170141183460469231731687303715884105728\')) ceil(toInt128(\'170141183460469231731687303715884105727\')) +-170141183460469231731687303715884105728 170141183460469231731687303715884105727 +""" + +I_check_ceil_with_Int256_using_min_and_max_values = r""" +ceil(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) ceil(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) +-57896044618658097711785492504343953926634992332820282019728792003956564819968 57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_ceil_with_UInt128_using_min_and_max_values = r""" +ceil(toUInt128(\'0\')) ceil(toUInt128(\'340282366920938463463374607431768211455\')) +0 340282366920938463463374607431768211455 +""" + +I_check_ceil_with_UInt256_using_min_and_max_values = r""" +ceil(toUInt256(\'0\')) ceil(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) +0 115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_floor_with_Int128_using_min_and_max_values = r""" +floor(toInt128(\'-170141183460469231731687303715884105728\')) floor(toInt128(\'170141183460469231731687303715884105727\')) +-170141183460469231731687303715884105728 170141183460469231731687303715884105727 +""" + +I_check_floor_with_Int256_using_min_and_max_values = r""" +floor(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) floor(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) +-57896044618658097711785492504343953926634992332820282019728792003956564819968 57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_floor_with_UInt128_using_min_and_max_values = r""" +floor(toUInt128(\'0\')) floor(toUInt128(\'340282366920938463463374607431768211455\')) +0 340282366920938463463374607431768211455 +""" + +I_check_floor_with_UInt256_using_min_and_max_values = r""" +floor(toUInt256(\'0\')) floor(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) +0 115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_trunc_with_Int128_using_min_and_max_values = r""" +trunc(toInt128(\'-170141183460469231731687303715884105728\')) trunc(toInt128(\'170141183460469231731687303715884105727\')) +-170141183460469231731687303715884105728 170141183460469231731687303715884105727 +""" + +I_check_trunc_with_Int256_using_min_and_max_values = r""" +trunc(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) trunc(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) +-57896044618658097711785492504343953926634992332820282019728792003956564819968 57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_trunc_with_UInt128_using_min_and_max_values = r""" +trunc(toUInt128(\'0\')) trunc(toUInt128(\'340282366920938463463374607431768211455\')) +0 340282366920938463463374607431768211455 +""" + +I_check_trunc_with_UInt256_using_min_and_max_values = r""" +trunc(toUInt256(\'0\')) trunc(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) +0 115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_round_with_Int128_using_min_and_max_values = r""" +round(toInt128(\'-170141183460469231731687303715884105728\')) round(toInt128(\'170141183460469231731687303715884105727\')) +-170141183460469231731687303715884105728 170141183460469231731687303715884105727 +""" + +I_check_round_with_Int256_using_min_and_max_values = r""" +round(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) round(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) +-57896044618658097711785492504343953926634992332820282019728792003956564819968 57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_round_with_UInt128_using_min_and_max_values = r""" +round(toUInt128(\'0\')) round(toUInt128(\'340282366920938463463374607431768211455\')) +0 340282366920938463463374607431768211455 +""" + +I_check_round_with_UInt256_using_min_and_max_values = r""" +round(toUInt256(\'0\')) round(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) +0 115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_roundBankers_with_Int128_using_min_and_max_values = r""" +roundBankers(toInt128(\'-170141183460469231731687303715884105728\')) roundBankers(toInt128(\'170141183460469231731687303715884105727\')) +-170141183460469231731687303715884105728 170141183460469231731687303715884105727 +""" + +I_check_roundBankers_with_Int256_using_min_and_max_values = r""" +roundBankers(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) roundBankers(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) +-57896044618658097711785492504343953926634992332820282019728792003956564819968 57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_roundBankers_with_UInt128_using_min_and_max_values = r""" +roundBankers(toUInt128(\'0\')) roundBankers(toUInt128(\'340282366920938463463374607431768211455\')) +0 340282366920938463463374607431768211455 +""" + +I_check_roundBankers_with_UInt256_using_min_and_max_values = r""" +roundBankers(toUInt256(\'0\')) roundBankers(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) +0 115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_roundDuration_with_Int128_using_min_and_max_values = r""" +roundDuration(toInt128(\'-170141183460469231731687303715884105728\')) roundDuration(toInt128(\'170141183460469231731687303715884105727\')) +0 36000 +""" + +I_check_roundDuration_with_Int256_using_min_and_max_values = r""" +roundDuration(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) roundDuration(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) +0 36000 +""" + +I_check_roundDuration_with_UInt128_using_min_and_max_values = r""" +roundDuration(toUInt128(\'0\')) roundDuration(toUInt128(\'340282366920938463463374607431768211455\')) +0 36000 +""" + +I_check_roundDuration_with_UInt256_using_min_and_max_values = r""" +roundDuration(toUInt256(\'0\')) roundDuration(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) +0 36000 +""" + +I_check_roundAge_with_Int128_using_min_and_max_values = r""" +roundAge(toInt128(\'-170141183460469231731687303715884105728\')) roundAge(toInt128(\'170141183460469231731687303715884105727\')) +0 55 +""" + +I_check_roundAge_with_Int256_using_min_and_max_values = r""" +roundAge(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) roundAge(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) +0 55 +""" + +I_check_roundAge_with_UInt128_using_min_and_max_values = r""" +roundAge(toUInt128(\'0\')) roundAge(toUInt128(\'340282366920938463463374607431768211455\')) +0 55 +""" + +I_check_roundAge_with_UInt256_using_min_and_max_values = r""" +roundAge(toUInt256(\'0\')) roundAge(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) +0 55 +""" + +I_select_the_output_of_ceil_with_Int128_from_the_table = r""" +a +-170141183460469231731687303715884105728 +1 +170141183460469231731687303715884105727 +""" + +I_select_the_output_of_ceil_with_Int256_from_the_table = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +1 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_select_the_output_of_ceil_with_UInt128_from_the_table = r""" +a +0 +1 +340282366920938463463374607431768211455 +""" + +I_select_the_output_of_ceil_with_UInt256_from_the_table = r""" +a +0 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_select_the_output_of_floor_with_Int128_from_the_table = r""" +a +-170141183460469231731687303715884105728 +1 +170141183460469231731687303715884105727 +""" + +I_select_the_output_of_floor_with_Int256_from_the_table = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +1 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_select_the_output_of_floor_with_UInt128_from_the_table = r""" +a +0 +1 +340282366920938463463374607431768211455 +""" + +I_select_the_output_of_floor_with_UInt256_from_the_table = r""" +a +0 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_select_the_output_of_trunc_with_Int128_from_the_table = r""" +a +-170141183460469231731687303715884105728 +1 +170141183460469231731687303715884105727 +""" + +I_select_the_output_of_trunc_with_Int256_from_the_table = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +1 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_select_the_output_of_trunc_with_UInt128_from_the_table = r""" +a +0 +1 +340282366920938463463374607431768211455 +""" + +I_select_the_output_of_trunc_with_UInt256_from_the_table = r""" +a +0 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_select_the_output_of_round_with_Int128_from_the_table = r""" +a +-170141183460469231731687303715884105728 +1 +170141183460469231731687303715884105727 +""" + +I_select_the_output_of_round_with_Int256_from_the_table = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +1 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_select_the_output_of_round_with_UInt128_from_the_table = r""" +a +0 +1 +340282366920938463463374607431768211455 +""" + +I_select_the_output_of_round_with_UInt256_from_the_table = r""" +a +0 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_select_the_output_of_roundBankers_with_Int128_from_the_table = r""" +a +-170141183460469231731687303715884105728 +1 +170141183460469231731687303715884105727 +""" + +I_select_the_output_of_roundBankers_with_Int256_from_the_table = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +1 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_select_the_output_of_roundBankers_with_UInt128_from_the_table = r""" +a +0 +1 +340282366920938463463374607431768211455 +""" + +I_select_the_output_of_roundBankers_with_UInt256_from_the_table = r""" +a +0 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_select_the_output_of_roundDuration_with_Int128_from_the_table = r""" +a +0 +1 +36000 +""" + +I_select_the_output_of_roundDuration_with_Int256_from_the_table = r""" +a +0 +1 +36000 +""" + +I_select_the_output_of_roundDuration_with_UInt128_from_the_table = r""" +a +0 +1 +36000 +""" + +I_select_the_output_of_roundDuration_with_UInt256_from_the_table = r""" +a +0 +1 +36000 +""" + +I_select_the_output_of_roundAge_with_Int128_from_the_table = r""" +a +0 +17 +55 +""" + +I_select_the_output_of_roundAge_with_Int256_from_the_table = r""" +a +0 +17 +55 +""" + +I_select_the_output_of_roundAge_with_UInt128_from_the_table = r""" +a +0 +17 +55 +""" + +I_select_the_output_of_roundAge_with_UInt256_from_the_table = r""" +a +0 +17 +55 +""" + +I_check_ceil_with_Decimal256_using_min_and_max_values = r""" +ceil(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) ceil(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +-1000000000000000000000000000000000000000000000000000000000000000000000000000 1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_check_floor_with_Decimal256_using_min_and_max_values = r""" +floor(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) floor(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +-1000000000000000000000000000000000000000000000000000000000000000000000000000 1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_check_trunc_with_Decimal256_using_min_and_max_values = r""" +trunc(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) trunc(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +-1000000000000000000000000000000000000000000000000000000000000000000000000000 1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_check_round_with_Decimal256_using_min_and_max_values = r""" +round(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) round(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +-1000000000000000000000000000000000000000000000000000000000000000000000000000 1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_check_roundBankers_with_Decimal256_using_min_and_max_values = r""" +roundBankers(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) roundBankers(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +-1000000000000000000000000000000000000000000000000000000000000000000000000000 1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_select_the_output_of_ceil_with_Decimal256_from_the_table = r""" +a +-1000000000000000000000000000000000000000000000000000000000000000000000000000 +1 +1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_select_the_output_of_floor_with_Decimal256_from_the_table = r""" +a +-1000000000000000000000000000000000000000000000000000000000000000000000000000 +1 +1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_select_the_output_of_trunc_with_Decimal256_from_the_table = r""" +a +-1000000000000000000000000000000000000000000000000000000000000000000000000000 +1 +1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_select_the_output_of_round_with_Decimal256_from_the_table = r""" +a +-1000000000000000000000000000000000000000000000000000000000000000000000000000 +1 +1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_select_the_output_of_roundBankers_with_Decimal256_from_the_table = r""" +a +-1000000000000000000000000000000000000000000000000000000000000000000000000000 +1 +1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_check_bitAnd_with_Int128 = r""" +bitAnd(toInt128(1), 1) bitAnd(toInt128(\'170141183460469231731687303715884105727\'), 1) bitAnd(toInt128(\'-170141183460469231731687303715884105728\'), 1) +1 1 0 +""" + +I_check_bitAnd_with_Int256 = r""" +bitAnd(toInt256(1), 1) bitAnd(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), 1) bitAnd(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\'), 1) +1 1 0 +""" + +I_check_bitAnd_with_UInt128 = r""" +bitAnd(toUInt128(1), 1) bitAnd(toUInt128(\'340282366920938463463374607431768211455\'), 1) bitAnd(toUInt128(\'0\'), 1) +1 1 0 +""" + +I_check_bitAnd_with_UInt256 = r""" +bitAnd(toUInt256(1), 1) bitAnd(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), 1) bitAnd(toUInt256(\'0\'), 1) +1 1 0 +""" + +I_check_bitOr_with_Int128 = r""" +bitOr(toInt128(1), 1) bitOr(toInt128(\'170141183460469231731687303715884105727\'), 1) bitOr(toInt128(\'-170141183460469231731687303715884105728\'), 1) +1 170141183460469231731687303715884105727 -170141183460469231731687303715884105727 +""" + +I_check_bitOr_with_Int256 = r""" +bitOr(toInt256(1), 1) bitOr(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), 1) bitOr(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\'), 1) +1 57896044618658097711785492504343953926634992332820282019728792003956564819967 -57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_bitOr_with_UInt128 = r""" +bitOr(toUInt128(1), 1) bitOr(toUInt128(\'340282366920938463463374607431768211455\'), 1) bitOr(toUInt128(\'0\'), 1) +1 340282366920938463463374607431768211455 1 +""" + +I_check_bitOr_with_UInt256 = r""" +bitOr(toUInt256(1), 1) bitOr(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), 1) bitOr(toUInt256(\'0\'), 1) +1 115792089237316195423570985008687907853269984665640564039457584007913129639935 1 +""" + +I_check_bitXor_with_Int128 = r""" +bitXor(toInt128(1), 1) bitXor(toInt128(\'170141183460469231731687303715884105727\'), 1) bitXor(toInt128(\'-170141183460469231731687303715884105728\'), 1) +0 170141183460469231731687303715884105726 -170141183460469231731687303715884105727 +""" + +I_check_bitXor_with_Int256 = r""" +bitXor(toInt256(1), 1) bitXor(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), 1) bitXor(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\'), 1) +0 57896044618658097711785492504343953926634992332820282019728792003956564819966 -57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_bitXor_with_UInt128 = r""" +bitXor(toUInt128(1), 1) bitXor(toUInt128(\'340282366920938463463374607431768211455\'), 1) bitXor(toUInt128(\'0\'), 1) +0 340282366920938463463374607431768211454 1 +""" + +I_check_bitXor_with_UInt256 = r""" +bitXor(toUInt256(1), 1) bitXor(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), 1) bitXor(toUInt256(\'0\'), 1) +0 115792089237316195423570985008687907853269984665640564039457584007913129639934 1 +""" + +I_check_bitShiftLeft_with_Int128 = r""" +bitShiftLeft(toInt128(1), 1) bitShiftLeft(toInt128(\'170141183460469231731687303715884105727\'), 1) bitShiftLeft(toInt128(\'-170141183460469231731687303715884105728\'), 1) +2 -2 0 +""" + +I_check_bitShiftLeft_with_Int256 = r""" +bitShiftLeft(toInt256(1), 1) bitShiftLeft(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), 1) bitShiftLeft(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\'), 1) +2 -2 0 +""" + +I_check_bitShiftLeft_with_UInt128 = r""" +bitShiftLeft(toUInt128(1), 1) bitShiftLeft(toUInt128(\'340282366920938463463374607431768211455\'), 1) bitShiftLeft(toUInt128(\'0\'), 1) +2 340282366920938463463374607431768211454 0 +""" + +I_check_bitShiftLeft_with_UInt256 = r""" +bitShiftLeft(toUInt256(1), 1) bitShiftLeft(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), 1) bitShiftLeft(toUInt256(\'0\'), 1) +2 115792089237316195423570985008687907853269984665640564039457584007913129639934 0 +""" + +I_check_bitShiftRight_with_Int128 = r""" +bitShiftRight(toInt128(1), 1) bitShiftRight(toInt128(\'170141183460469231731687303715884105727\'), 1) bitShiftRight(toInt128(\'-170141183460469231731687303715884105728\'), 1) +0 85070591730234615865843651857942052863 -85070591730234615865843651857942052864 +""" + +I_check_bitShiftRight_with_Int256 = r""" +bitShiftRight(toInt256(1), 1) bitShiftRight(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), 1) bitShiftRight(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\'), 1) +0 28948022309329048855892746252171976963317496166410141009864396001978282409983 -28948022309329048855892746252171976963317496166410141009864396001978282409984 +""" + +I_check_bitShiftRight_with_UInt128 = r""" +bitShiftRight(toUInt128(1), 1) bitShiftRight(toUInt128(\'340282366920938463463374607431768211455\'), 1) bitShiftRight(toUInt128(\'0\'), 1) +0 170141183460469231731687303715884105727 0 +""" + +I_check_bitShiftRight_with_UInt256 = r""" +bitShiftRight(toUInt256(1), 1) bitShiftRight(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), 1) bitShiftRight(toUInt256(\'0\'), 1) +0 57896044618658097711785492504343953926634992332820282019728792003956564819967 0 +""" + +Check_bitNot_with_Int128 = r""" +bitNot(toInt128(1)) bitNot(toInt128(\'170141183460469231731687303715884105727\')) bitNot(toInt128(\'-170141183460469231731687303715884105728\')) +-2 -170141183460469231731687303715884105728 170141183460469231731687303715884105727 +""" + +Check_bitNot_with_Int256 = r""" +bitNot(toInt256(1)) bitNot(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) bitNot(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) +-2 -57896044618658097711785492504343953926634992332820282019728792003956564819968 57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +Check_bitNot_with_UInt128 = r""" +bitNot(toUInt128(1)) bitNot(toUInt128(\'340282366920938463463374607431768211455\')) bitNot(toUInt128(\'0\')) +340282366920938463463374607431768211454 0 340282366920938463463374607431768211455 +""" + +Check_bitNot_with_UInt256 = r""" +bitNot(toUInt256(1)) bitNot(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) bitNot(toUInt256(\'0\')) +115792089237316195423570985008687907853269984665640564039457584007913129639934 0 115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +Check_bitCount_with_Int128 = r""" +bitCount(toInt128(1)) bitCount(toInt128(\'170141183460469231731687303715884105727\')) bitCount(toInt128(\'-170141183460469231731687303715884105728\')) +1 64 0 +""" + +Check_bitCount_with_Int256 = r""" +bitCount(toInt256(1)) bitCount(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) bitCount(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) +1 64 0 +""" + +Check_bitCount_with_UInt128 = r""" +bitCount(toUInt128(1)) bitCount(toUInt128(\'340282366920938463463374607431768211455\')) bitCount(toUInt128(\'0\')) +1 64 0 +""" + +Check_bitCount_with_UInt256 = r""" +bitCount(toUInt256(1)) bitCount(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) bitCount(toUInt256(\'0\')) +1 64 0 +""" + +I_check_the_table_with_values_of_bitAnd_and_Int128 = r""" +a +0 +1 +1 +""" + +I_check_the_table_with_values_of_bitAnd_and_Int256 = r""" +a +0 +1 +1 +""" + +I_check_the_table_with_values_of_bitAnd_and_UInt128 = r""" +a +0 +1 +1 +""" + +I_check_the_table_with_values_of_bitAnd_and_UInt256 = r""" +a +0 +1 +1 +""" + +I_check_the_table_with_values_of_bitOr_and_Int128 = r""" +a +-170141183460469231731687303715884105727 +1 +170141183460469231731687303715884105727 +""" + +I_check_the_table_with_values_of_bitOr_and_Int256 = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819967 +1 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_the_table_with_values_of_bitOr_and_UInt128 = r""" +a +1 +1 +340282366920938463463374607431768211455 +""" + +I_check_the_table_with_values_of_bitOr_and_UInt256 = r""" +a +1 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_the_table_with_values_of_bitXor_and_Int128 = r""" +a +-170141183460469231731687303715884105727 +0 +170141183460469231731687303715884105726 +""" + +I_check_the_table_with_values_of_bitXor_and_Int256 = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819967 +0 +57896044618658097711785492504343953926634992332820282019728792003956564819966 +""" + +I_check_the_table_with_values_of_bitXor_and_UInt128 = r""" +a +0 +1 +340282366920938463463374607431768211454 +""" + +I_check_the_table_with_values_of_bitXor_and_UInt256 = r""" +a +0 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639934 +""" + +I_check_the_table_with_values_of_bitShiftLeft_and_Int128 = r""" +a +-2 +0 +2 +""" + +I_check_the_table_with_values_of_bitShiftLeft_and_Int256 = r""" +a +-2 +0 +2 +""" + +I_check_the_table_with_values_of_bitShiftLeft_and_UInt128 = r""" +a +0 +2 +340282366920938463463374607431768211454 +""" + +I_check_the_table_with_values_of_bitShiftLeft_and_UInt256 = r""" +a +0 +2 +115792089237316195423570985008687907853269984665640564039457584007913129639934 +""" + +I_check_the_table_with_values_of_bitShiftRight_and_Int128 = r""" +a +-85070591730234615865843651857942052864 +0 +85070591730234615865843651857942052863 +""" + +I_check_the_table_with_values_of_bitShiftRight_and_Int256 = r""" +a +-28948022309329048855892746252171976963317496166410141009864396001978282409984 +0 +28948022309329048855892746252171976963317496166410141009864396001978282409983 +""" + +I_check_the_table_with_values_of_bitShiftRight_and_UInt128 = r""" +a +0 +0 +170141183460469231731687303715884105727 +""" + +I_check_the_table_with_values_of_bitShiftRight_and_UInt256 = r""" +a +0 +0 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_the_table_with_values_of_bitNot_and_Int128 = r""" +a +-170141183460469231731687303715884105728 +-2 +170141183460469231731687303715884105727 +""" + +I_check_the_table_with_values_of_bitNot_and_Int256 = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +-2 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_the_table_with_values_of_bitNot_and_UInt128 = r""" +a +0 +340282366920938463463374607431768211454 +340282366920938463463374607431768211455 +""" + +I_check_the_table_with_values_of_bitNot_and_UInt256 = r""" +a +0 +115792089237316195423570985008687907853269984665640564039457584007913129639934 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_the_table_with_values_of_bitCount_and_Int128 = r""" +a +0 +1 +64 +""" + +I_check_the_table_with_values_of_bitCount_and_Int256 = r""" +a +0 +1 +64 +""" + +I_check_the_table_with_values_of_bitCount_and_UInt128 = r""" +a +0 +1 +64 +""" + +I_check_the_table_with_values_of_bitCount_and_UInt256 = r""" +a +0 +1 +64 +""" + +I_check_isNull__with_Int128_using_min_and_max = r""" +isNull(toInt128(\'-170141183460469231731687303715884105728\')) isNull(toInt128(\'170141183460469231731687303715884105727\')) +0 0 +""" + +I_check_isNull__with_Int256_using_min_and_max = r""" +isNull(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) isNull(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) +0 0 +""" + +I_check_isNull__with_UInt128_using_min_and_max = r""" +isNull(toUInt128(\'0\')) isNull(toUInt128(\'340282366920938463463374607431768211455\')) +0 0 +""" + +I_check_isNull__with_UInt256_using_min_and_max = r""" +isNull(toUInt256(\'0\')) isNull(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) +0 0 +""" + +I_check_isNotNull__with_Int128_using_min_and_max = r""" +isNotNull(toInt128(\'-170141183460469231731687303715884105728\')) isNotNull(toInt128(\'170141183460469231731687303715884105727\')) +1 1 +""" + +I_check_isNotNull__with_Int256_using_min_and_max = r""" +isNotNull(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) isNotNull(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) +1 1 +""" + +I_check_isNotNull__with_UInt128_using_min_and_max = r""" +isNotNull(toUInt128(\'0\')) isNotNull(toUInt128(\'340282366920938463463374607431768211455\')) +1 1 +""" + +I_check_isNotNull__with_UInt256_using_min_and_max = r""" +isNotNull(toUInt256(\'0\')) isNotNull(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) +1 1 +""" + +I_check_coalesce__with_Int128_using_min_and_max = r""" +coalesce(toInt128(\'-170141183460469231731687303715884105728\')) coalesce(toInt128(\'170141183460469231731687303715884105727\')) +-170141183460469231731687303715884105728 170141183460469231731687303715884105727 +""" + +I_check_coalesce__with_Int256_using_min_and_max = r""" +coalesce(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) coalesce(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) +-57896044618658097711785492504343953926634992332820282019728792003956564819968 57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_coalesce__with_UInt128_using_min_and_max = r""" +coalesce(toUInt128(\'0\')) coalesce(toUInt128(\'340282366920938463463374607431768211455\')) +0 340282366920938463463374607431768211455 +""" + +I_check_coalesce__with_UInt256_using_min_and_max = r""" +coalesce(toUInt256(\'0\')) coalesce(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) +0 115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_assumeNotNull__with_Int128_using_min_and_max = r""" +assumeNotNull(toInt128(\'-170141183460469231731687303715884105728\')) assumeNotNull(toInt128(\'170141183460469231731687303715884105727\')) +-170141183460469231731687303715884105728 170141183460469231731687303715884105727 +""" + +I_check_assumeNotNull__with_Int256_using_min_and_max = r""" +assumeNotNull(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) assumeNotNull(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) +-57896044618658097711785492504343953926634992332820282019728792003956564819968 57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_assumeNotNull__with_UInt128_using_min_and_max = r""" +assumeNotNull(toUInt128(\'0\')) assumeNotNull(toUInt128(\'340282366920938463463374607431768211455\')) +0 340282366920938463463374607431768211455 +""" + +I_check_assumeNotNull__with_UInt256_using_min_and_max = r""" +assumeNotNull(toUInt256(\'0\')) assumeNotNull(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) +0 115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_toNullable__with_Int128_using_min_and_max = r""" +toNullable(toInt128(\'-170141183460469231731687303715884105728\')) toNullable(toInt128(\'170141183460469231731687303715884105727\')) +-170141183460469231731687303715884105728 170141183460469231731687303715884105727 +""" + +I_check_toNullable__with_Int256_using_min_and_max = r""" +toNullable(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) toNullable(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) +-57896044618658097711785492504343953926634992332820282019728792003956564819968 57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_toNullable__with_UInt128_using_min_and_max = r""" +toNullable(toUInt128(\'0\')) toNullable(toUInt128(\'340282366920938463463374607431768211455\')) +0 340282366920938463463374607431768211455 +""" + +I_check_toNullable__with_UInt256_using_min_and_max = r""" +toNullable(toUInt256(\'0\')) toNullable(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) +0 115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_ifNull_1__with_Int128_using_min_and_max = r""" +ifNull(1, toInt128(\'-170141183460469231731687303715884105728\')) ifNull(1, toInt128(\'170141183460469231731687303715884105727\')) +1 1 +""" + +I_check_ifNull_1__with_Int256_using_min_and_max = r""" +ifNull(1, toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) ifNull(1, toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) +1 1 +""" + +I_check_ifNull_1__with_UInt128_using_min_and_max = r""" +ifNull(1, toUInt128(\'0\')) ifNull(1, toUInt128(\'340282366920938463463374607431768211455\')) +1 1 +""" + +I_check_ifNull_1__with_UInt256_using_min_and_max = r""" +ifNull(1, toUInt256(\'0\')) ifNull(1, toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) +1 1 +""" + +I_check_nullIf_1__with_Int128_using_min_and_max = r""" +nullIf(1, toInt128(\'-170141183460469231731687303715884105728\')) nullIf(1, toInt128(\'170141183460469231731687303715884105727\')) +1 1 +""" + +I_check_nullIf_1__with_Int256_using_min_and_max = r""" +nullIf(1, toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) nullIf(1, toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) +1 1 +""" + +I_check_nullIf_1__with_UInt128_using_min_and_max = r""" +nullIf(1, toUInt128(\'0\')) nullIf(1, toUInt128(\'340282366920938463463374607431768211455\')) +1 1 +""" + +I_check_nullIf_1__with_UInt256_using_min_and_max = r""" +nullIf(1, toUInt256(\'0\')) nullIf(1, toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) +1 1 +""" + +I_check_isNull__with_Int128_on_the_table = r""" +a +0 +0 +0 +""" + +I_check_isNull__with_Int256_on_the_table = r""" +a +0 +0 +0 +""" + +I_check_isNull__with_UInt128_on_the_table = r""" +a +0 +0 +0 +""" + +I_check_isNull__with_UInt256_on_the_table = r""" +a +0 +0 +0 +""" + +I_check_isNotNull__with_Int128_on_the_table = r""" +a +1 +1 +1 +""" + +I_check_isNotNull__with_Int256_on_the_table = r""" +a +1 +1 +1 +""" + +I_check_isNotNull__with_UInt128_on_the_table = r""" +a +1 +1 +1 +""" + +I_check_isNotNull__with_UInt256_on_the_table = r""" +a +1 +1 +1 +""" + +I_check_coalesce__with_Int128_on_the_table = r""" +a +-170141183460469231731687303715884105728 +1 +170141183460469231731687303715884105727 +""" + +I_check_coalesce__with_Int256_on_the_table = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +1 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_coalesce__with_UInt128_on_the_table = r""" +a +0 +1 +340282366920938463463374607431768211455 +""" + +I_check_coalesce__with_UInt256_on_the_table = r""" +a +0 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_assumeNotNull__with_Int128_on_the_table = r""" +a +-170141183460469231731687303715884105728 +1 +170141183460469231731687303715884105727 +""" + +I_check_assumeNotNull__with_Int256_on_the_table = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +1 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_assumeNotNull__with_UInt128_on_the_table = r""" +a +0 +1 +340282366920938463463374607431768211455 +""" + +I_check_assumeNotNull__with_UInt256_on_the_table = r""" +a +0 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_toNullable__with_Int128_on_the_table = r""" +a +-170141183460469231731687303715884105728 +1 +170141183460469231731687303715884105727 +""" + +I_check_toNullable__with_Int256_on_the_table = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +1 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_toNullable__with_UInt128_on_the_table = r""" +a +0 +1 +340282366920938463463374607431768211455 +""" + +I_check_toNullable__with_UInt256_on_the_table = r""" +a +0 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_ifNull_1__with_Int128_on_the_table = r""" +a +1 +1 +1 +""" + +I_check_ifNull_1__with_Int256_on_the_table = r""" +a +1 +1 +1 +""" + +I_check_ifNull_1__with_UInt128_on_the_table = r""" +a +1 +1 +1 +""" + +I_check_ifNull_1__with_UInt256_on_the_table = r""" +a +1 +1 +1 +""" + +I_check_nullIf_1__with_Int128_on_the_table = r""" +a +1 +1 +\N +""" + +I_check_nullIf_1__with_Int256_on_the_table = r""" +a +1 +1 +\N +""" + +I_check_nullIf_1__with_UInt128_on_the_table = r""" +a +1 +1 +\N +""" + +I_check_nullIf_1__with_UInt256_on_the_table = r""" +a +1 +1 +\N +""" + +I_check_isNull__with_Decimal256_using_min_and_max = r""" +isNull(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) isNull(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +0 0 +""" + +I_check_isNotNull__with_Decimal256_using_min_and_max = r""" +isNotNull(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) isNotNull(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +1 1 +""" + +I_check_coalesce__with_Decimal256_using_min_and_max = r""" +coalesce(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) coalesce(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +-1000000000000000000000000000000000000000000000000000000000000000000000000000 1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_check_assumeNotNull__with_Decimal256_using_min_and_max = r""" +assumeNotNull(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) assumeNotNull(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +-1000000000000000000000000000000000000000000000000000000000000000000000000000 1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_check_toNullable__with_Decimal256_using_min_and_max = r""" +toNullable(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) toNullable(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +-1000000000000000000000000000000000000000000000000000000000000000000000000000 1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_check_ifNull_1__with_Decimal256_using_min_and_max = r""" +ifNull(1, toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) ifNull(1, toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +1 1 +""" + +I_check_nullIf_1__with_Decimal256_using_min_and_max = r""" +nullIf(1, toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) nullIf(1, toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +1 1 +""" + +I_check_isNull__with_Decimal256_on_the_table = r""" +a +0 +0 +0 +""" + +I_check_isNotNull__with_Decimal256_on_the_table = r""" +a +1 +1 +1 +""" + +I_check_coalesce__with_Decimal256_on_the_table = r""" +a +-1000000000000000000000000000000000000000000000000000000000000000000000000000 +1 +1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_check_assumeNotNull__with_Decimal256_on_the_table = r""" +a +-1000000000000000000000000000000000000000000000000000000000000000000000000000 +1 +1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_check_toNullable__with_Decimal256_on_the_table = r""" +a +-1000000000000000000000000000000000000000000000000000000000000000000000000000 +1 +1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_check_ifNull_1__with_Decimal256_on_the_table = r""" +a +1 +1 +1 +""" + +I_check_nullIf_1__with_Decimal256_on_the_table = r""" +a +1 +1 +\N +""" + diff --git a/tests/testflows/extended_precision_data_types/snapshots/common.py.tests.pre22.3.snapshot b/tests/testflows/extended_precision_data_types/snapshots/common.py.tests.pre22.3.snapshot new file mode 100644 index 000000000000..17f7fc2d2026 --- /dev/null +++ b/tests/testflows/extended_precision_data_types/snapshots/common.py.tests.pre22.3.snapshot @@ -0,0 +1,6286 @@ +I_check_plus_with_Int128_max_and_min_value = r""" +round(plus(toInt128(\'170141183460469231731687303715884105727\'), toInt128(1)), 7) round(plus(toInt128(\'-170141183460469231731687303715884105728\'), toInt128(1)), 7) +-170141183460469231731687303715884105728 -170141183460469231731687303715884105727 +""" + +I_check_plus_with_Int256_max_and_min_value = r""" +round(plus(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), toInt256(1)), 7) round(plus(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\'), toInt256(1)), 7) +-57896044618658097711785492504343953926634992332820282019728792003956564819968 -57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_plus_with_UInt128_max_and_min_value = r""" +round(plus(toUInt128(\'340282366920938463463374607431768211455\'), toUInt128(1)), 7) round(plus(toUInt128(\'0\'), toUInt128(1)), 7) +0 1 +""" + +I_check_plus_with_UInt256_max_and_min_value = r""" +round(plus(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), toUInt256(1)), 7) round(plus(toUInt256(\'0\'), toUInt256(1)), 7) +0 1 +""" + +I_check_minus_with_Int128_max_and_min_value = r""" +round(minus(toInt128(\'170141183460469231731687303715884105727\'), toInt128(1)), 7) round(minus(toInt128(\'-170141183460469231731687303715884105728\'), toInt128(1)), 7) +170141183460469231731687303715884105726 170141183460469231731687303715884105727 +""" + +I_check_minus_with_Int256_max_and_min_value = r""" +round(minus(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), toInt256(1)), 7) round(minus(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\'), toInt256(1)), 7) +57896044618658097711785492504343953926634992332820282019728792003956564819966 57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_minus_with_UInt128_max_and_min_value = r""" +round(minus(toUInt128(\'340282366920938463463374607431768211455\'), toUInt128(1)), 7) round(minus(toUInt128(\'0\'), toUInt128(1)), 7) +-2 -1 +""" + +I_check_minus_with_UInt256_max_and_min_value = r""" +round(minus(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), toUInt256(1)), 7) round(minus(toUInt256(\'0\'), toUInt256(1)), 7) +-2 -1 +""" + +I_check_multiply_with_Int128_max_and_min_value = r""" +round(multiply(toInt128(\'170141183460469231731687303715884105727\'), toInt128(1)), 7) round(multiply(toInt128(\'-170141183460469231731687303715884105728\'), toInt128(1)), 7) +170141183460469231731687303715884105727 -170141183460469231731687303715884105728 +""" + +I_check_multiply_with_Int256_max_and_min_value = r""" +round(multiply(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), toInt256(1)), 7) round(multiply(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\'), toInt256(1)), 7) +57896044618658097711785492504343953926634992332820282019728792003956564819967 -57896044618658097711785492504343953926634992332820282019728792003956564819968 +""" + +I_check_multiply_with_UInt128_max_and_min_value = r""" +round(multiply(toUInt128(\'340282366920938463463374607431768211455\'), toUInt128(1)), 7) round(multiply(toUInt128(\'0\'), toUInt128(1)), 7) +340282366920938463463374607431768211455 0 +""" + +I_check_multiply_with_UInt256_max_and_min_value = r""" +round(multiply(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), toUInt256(1)), 7) round(multiply(toUInt256(\'0\'), toUInt256(1)), 7) +115792089237316195423570985008687907853269984665640564039457584007913129639935 0 +""" + +I_check_intDiv_with_Int128_max_and_min_value = r""" +round(intDiv(toInt128(\'170141183460469231731687303715884105727\'), toInt128(1)), 7) round(intDiv(toInt128(\'-170141183460469231731687303715884105728\'), toInt128(1)), 7) +170141183460469231731687303715884105727 -170141183460469231731687303715884105728 +""" + +I_check_intDiv_with_Int256_max_and_min_value = r""" +round(intDiv(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), toInt256(1)), 7) round(intDiv(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\'), toInt256(1)), 7) +57896044618658097711785492504343953926634992332820282019728792003956564819967 -57896044618658097711785492504343953926634992332820282019728792003956564819968 +""" + +I_check_intDiv_with_UInt128_max_and_min_value = r""" +round(intDiv(toUInt128(\'340282366920938463463374607431768211455\'), toUInt128(1)), 7) round(intDiv(toUInt128(\'0\'), toUInt128(1)), 7) +340282366920938463463374607431768211455 0 +""" + +I_check_intDiv_with_UInt256_max_and_min_value = r""" +round(intDiv(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), toUInt256(1)), 7) round(intDiv(toUInt256(\'0\'), toUInt256(1)), 7) +115792089237316195423570985008687907853269984665640564039457584007913129639935 0 +""" + +I_check_intDivOrZero_with_Int128_max_and_min_value = r""" +round(intDivOrZero(toInt128(\'170141183460469231731687303715884105727\'), toInt128(1)), 7) round(intDivOrZero(toInt128(\'-170141183460469231731687303715884105728\'), toInt128(1)), 7) +170141183460469231731687303715884105727 -170141183460469231731687303715884105728 +""" + +I_check_intDivOrZero_with_Int256_max_and_min_value = r""" +round(intDivOrZero(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), toInt256(1)), 7) round(intDivOrZero(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\'), toInt256(1)), 7) +57896044618658097711785492504343953926634992332820282019728792003956564819967 -57896044618658097711785492504343953926634992332820282019728792003956564819968 +""" + +I_check_intDivOrZero_with_UInt128_max_and_min_value = r""" +round(intDivOrZero(toUInt128(\'340282366920938463463374607431768211455\'), toUInt128(1)), 7) round(intDivOrZero(toUInt128(\'0\'), toUInt128(1)), 7) +340282366920938463463374607431768211455 0 +""" + +I_check_intDivOrZero_with_UInt256_max_and_min_value = r""" +round(intDivOrZero(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), toUInt256(1)), 7) round(intDivOrZero(toUInt256(\'0\'), toUInt256(1)), 7) +115792089237316195423570985008687907853269984665640564039457584007913129639935 0 +""" + +I_check_modulo_with_Int128_max_and_min_value = r""" +round(modulo(toInt128(\'170141183460469231731687303715884105727\'), toInt128(1)), 7) round(modulo(toInt128(\'-170141183460469231731687303715884105728\'), toInt128(1)), 7) +0 0 +""" + +I_check_modulo_with_Int256_max_and_min_value = r""" +round(modulo(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), toInt256(1)), 7) round(modulo(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\'), toInt256(1)), 7) +0 0 +""" + +I_check_modulo_with_UInt128_max_and_min_value = r""" +round(modulo(toUInt128(\'340282366920938463463374607431768211455\'), toUInt128(1)), 7) round(modulo(toUInt128(\'0\'), toUInt128(1)), 7) +0 0 +""" + +I_check_modulo_with_UInt256_max_and_min_value = r""" +round(modulo(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), toUInt256(1)), 7) round(modulo(toUInt256(\'0\'), toUInt256(1)), 7) +0 0 +""" + +I_check_moduloOrZero_with_Int128_max_and_min_value = r""" +round(moduloOrZero(toInt128(\'170141183460469231731687303715884105727\'), toInt128(1)), 7) round(moduloOrZero(toInt128(\'-170141183460469231731687303715884105728\'), toInt128(1)), 7) +0 0 +""" + +I_check_moduloOrZero_with_Int256_max_and_min_value = r""" +round(moduloOrZero(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), toInt256(1)), 7) round(moduloOrZero(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\'), toInt256(1)), 7) +0 0 +""" + +I_check_moduloOrZero_with_UInt128_max_and_min_value = r""" +round(moduloOrZero(toUInt128(\'340282366920938463463374607431768211455\'), toUInt128(1)), 7) round(moduloOrZero(toUInt128(\'0\'), toUInt128(1)), 7) +0 0 +""" + +I_check_moduloOrZero_with_UInt256_max_and_min_value = r""" +round(moduloOrZero(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), toUInt256(1)), 7) round(moduloOrZero(toUInt256(\'0\'), toUInt256(1)), 7) +0 0 +""" + +I_check_negate_with_Int128_max_and_min_value = r""" +negate(toInt128(\'170141183460469231731687303715884105727\')) negate(toInt128(\'-170141183460469231731687303715884105728\')) +-170141183460469231731687303715884105727 -170141183460469231731687303715884105728 +""" + +I_check_negate_with_Int256_max_and_min_value = r""" +negate(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) negate(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) +-57896044618658097711785492504343953926634992332820282019728792003956564819967 -57896044618658097711785492504343953926634992332820282019728792003956564819968 +""" + +I_check_negate_with_UInt128_max_and_min_value = r""" +negate(toUInt128(\'340282366920938463463374607431768211455\')) negate(toUInt128(\'0\')) +1 0 +""" + +I_check_negate_with_UInt256_max_and_min_value = r""" +negate(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) negate(toUInt256(\'0\')) +1 0 +""" + +I_check_abs_with_Int128_max_and_min_value = r""" +abs(toInt128(\'170141183460469231731687303715884105727\')) abs(toInt128(\'-170141183460469231731687303715884105728\')) +170141183460469231731687303715884105727 170141183460469231731687303715884105728 +""" + +I_check_abs_with_Int256_max_and_min_value = r""" +abs(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) abs(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) +57896044618658097711785492504343953926634992332820282019728792003956564819967 57896044618658097711785492504343953926634992332820282019728792003956564819968 +""" + +I_check_abs_with_UInt128_max_and_min_value = r""" +abs(toUInt128(\'340282366920938463463374607431768211455\')) abs(toUInt128(\'0\')) +340282366920938463463374607431768211455 0 +""" + +I_check_abs_with_UInt256_max_and_min_value = r""" +abs(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) abs(toUInt256(\'0\')) +115792089237316195423570985008687907853269984665640564039457584007913129639935 0 +""" + +I_check_the_table_output_of_plus_with_Int128 = r""" +a +-170141183460469231731687303715884105728 +-170141183460469231731687303715884105727 +2 +""" + +I_check_the_table_output_of_plus_with_Int256 = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +-57896044618658097711785492504343953926634992332820282019728792003956564819967 +2 +""" + +I_check_the_table_output_of_plus_with_UInt128 = r""" +a +0 +1 +2 +""" + +I_check_the_table_output_of_plus_with_UInt256 = r""" +a +0 +1 +2 +""" + +I_check_the_table_output_of_minus_with_Int128 = r""" +a +0 +170141183460469231731687303715884105726 +170141183460469231731687303715884105727 +""" + +I_check_the_table_output_of_minus_with_Int256 = r""" +a +0 +57896044618658097711785492504343953926634992332820282019728792003956564819966 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_the_table_output_of_minus_with_UInt128 = r""" +a +0 +340282366920938463463374607431768211454 +340282366920938463463374607431768211455 +""" + +I_check_the_table_output_of_minus_with_UInt256 = r""" +a +0 +115792089237316195423570985008687907853269984665640564039457584007913129639934 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_the_table_output_of_multiply_with_Int128 = r""" +a +-170141183460469231731687303715884105728 +1 +170141183460469231731687303715884105727 +""" + +I_check_the_table_output_of_multiply_with_Int256 = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +1 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_the_table_output_of_multiply_with_UInt128 = r""" +a +0 +1 +340282366920938463463374607431768211455 +""" + +I_check_the_table_output_of_multiply_with_UInt256 = r""" +a +0 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_the_table_output_of_intDiv_with_Int128 = r""" +a +-170141183460469231731687303715884105728 +1 +170141183460469231731687303715884105727 +""" + +I_check_the_table_output_of_intDiv_with_Int256 = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +1 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_the_table_output_of_intDiv_with_UInt128 = r""" +a +0 +1 +340282366920938463463374607431768211455 +""" + +I_check_the_table_output_of_intDiv_with_UInt256 = r""" +a +0 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_the_table_output_of_intDivOrZero_with_Int128 = r""" +a +-170141183460469231731687303715884105728 +1 +170141183460469231731687303715884105727 +""" + +I_check_the_table_output_of_intDivOrZero_with_Int256 = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +1 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_the_table_output_of_intDivOrZero_with_UInt128 = r""" +a +0 +1 +340282366920938463463374607431768211455 +""" + +I_check_the_table_output_of_intDivOrZero_with_UInt256 = r""" +a +0 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_the_table_output_of_modulo_with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_table_output_of_modulo_with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_table_output_of_modulo_with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_table_output_of_modulo_with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_table_output_of_moduloOrZero_with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_table_output_of_moduloOrZero_with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_table_output_of_moduloOrZero_with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_table_output_of_moduloOrZero_with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_table_output_of_negate_with_Int128 = r""" +a +-170141183460469231731687303715884105728 +-170141183460469231731687303715884105727 +-1 +""" + +I_check_the_table_output_of_negate_with_Int256 = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +-57896044618658097711785492504343953926634992332820282019728792003956564819967 +-1 +""" + +I_check_the_table_output_of_negate_with_UInt128 = r""" +a +0 +1 +340282366920938463463374607431768211455 +""" + +I_check_the_table_output_of_negate_with_UInt256 = r""" +a +0 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_the_table_output_of_abs_with_Int128 = r""" +a +-170141183460469231731687303715884105728 +1 +170141183460469231731687303715884105727 +""" + +I_check_the_table_output_of_abs_with_Int256 = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +1 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_the_table_output_of_abs_with_UInt128 = r""" +a +0 +1 +340282366920938463463374607431768211455 +""" + +I_check_the_table_output_of_abs_with_UInt256 = r""" +a +0 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_the_table_output_of_gcd_with_Int128 = r""" +a +1 +""" + +I_check_the_table_output_of_gcd_with_Int256 = r""" +a +1 +""" + +I_check_the_table_output_of_gcd_with_UInt128 = r""" +a +1 +1 +""" + +I_check_the_table_output_of_gcd_with_UInt256 = r""" +a +1 +1 +""" + +I_check_the_table_output_of_lcm_with_Int128 = r""" +a +1 +""" + +I_check_the_table_output_of_lcm_with_Int256 = r""" +a +1 +""" + +I_check_the_table_output_of_lcm_with_UInt128 = r""" +a +1 +340282366920938463463374607431768211455 +""" + +I_check_the_table_output_of_lcm_with_UInt256 = r""" +a +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_the_table_for_output_of_negate_with_Decimal256 = r""" +a +-1 +""" + +I_check_the_table_for_output_of_abs_with_Decimal256 = r""" +a +1 +""" + +Inline___Int128___arrayPopBack__ = r""" +arrayPopBack(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[3,2] +""" + +Table___Int128___arrayPopBack__ = r""" +a +[3,2] +""" + +Inline___Int128___arrayPopFront__ = r""" +arrayPopFront(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[2,1] +""" + +Table___Int128___arrayPopFront__ = r""" +a +[2,1] +""" + +Inline___Int128___arraySort__ = r""" +arraySort(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[1,2,3] +""" + +Table___Int128___arraySort__ = r""" +a +[1,2,3] +""" + +Inline___Int128___arrayReverseSort__ = r""" +arrayReverseSort(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[3,2,1] +""" + +Table___Int128___arrayReverseSort__ = r""" +a +[3,2,1] +""" + +Inline___Int128___arrayDistinct__ = r""" +arrayDistinct(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[3,2,1] +""" + +Table___Int128___arrayDistinct__ = r""" +a +[3,2,1] +""" + +Inline___Int128___arrayEnumerate__ = r""" +arrayEnumerate(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[1,2,3] +""" + +Table___Int128___arrayEnumerate__ = r""" +a +[1,2,3] +""" + +Inline___Int128___arrayEnumerateDense__ = r""" +arrayEnumerateDense(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[1,2,3] +""" + +Table___Int128___arrayEnumerateDense__ = r""" +a +[1,2,3] +""" + +Inline___Int128___arrayEnumerateUniq__ = r""" +arrayEnumerateUniq(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[1,1,1] +""" + +Table___Int128___arrayEnumerateUniq__ = r""" +a +[1,1,1] +""" + +Inline___Int128___arrayReverse__ = r""" +arrayReverse(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[1,2,3] +""" + +Table___Int128___arrayReverse__ = r""" +a +[1,2,3] +""" + +Inline___Int128___reverse__ = r""" +reverse(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[1,2,3] +""" + +Table___Int128___reverse__ = r""" +a +[1,2,3] +""" + +Inline___Int128___arrayFlatten__ = r""" +arrayFlatten(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[3,2,1] +""" + +Table___Int128___arrayFlatten__ = r""" +a +[3,2,1] +""" + +Inline___Int128___arrayCompact__ = r""" +arrayCompact(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[3,2,1] +""" + +Table___Int128___arrayCompact__ = r""" +a +[3,2,1] +""" + +Inline___Int128___arrayReduceInRanges__sum_____1__5____ = r""" +arrayReduceInRanges(\'sum\', array((1, 5)), array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[6] +""" + +Table___Int128___arrayReduceInRanges__sum_____1__5____ = r""" +a +[6] +""" + +Inline___Int128___arrayMap_x_____x___2___ = r""" +arrayMap(lambda(tuple(x), plus(x, 2)), array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[5,4,3] +""" + +Table___Int128___arrayMap_x_____x___2___ = r""" +a +[5,4,3] +""" + +Inline___Int128___arrayFill_x____x_3__ = r""" +arrayFill(lambda(tuple(x), equals(x, 3)), array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[3,3,3] +""" + +Table___Int128___arrayFill_x____x_3__ = r""" +a +[3,3,3] +""" + +Inline___Int128___arrayReverseFill_x____x_3__ = r""" +arrayReverseFill(lambda(tuple(x), equals(x, 3)), array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[3,1,1] +""" + +Table___Int128___arrayReverseFill_x____x_3__ = r""" +a +[3,1,1] +""" + +Inline___Int128___arrayConcat__toInt128__3____toInt128__2____toInt128__1_____ = r""" +arrayConcat(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\')), array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[3,2,1,3,2,1] +""" + +Table___Int128___arrayConcat__toInt128__3____toInt128__2____toInt128__1_____ = r""" +a +[3,2,1,3,2,1] +""" + +Inline___Int128___arrayFilter_x____x____1___ = r""" +arrayFilter(lambda(tuple(x), equals(x, 1)), array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[1] +""" + +Table___Int128___arrayFilter_x____x____1___ = r""" +a +[1] +""" + +Inline___Int128___arraySplit__x__y_____x_y___0__0__0___ = r""" +arraySplit(lambda(tuple(x, y), equals(x, y)), [0, 0, 0], array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +[[0,0,0]] +""" + +Table___Int128___arraySplit__x__y_____x_y___0__0__0___ = r""" +a +[[0,0,0]] +""" + +Inline___Int128___arrayZip__toInt128__1_____ = r""" +arrayZip(array(toInt128(\'1\')), array(toInt128(\'3\'))) +[(1,3)] +""" + +Table___Int128___arrayZip__toInt128__1_____ = r""" +a +[(1,1)] +""" + +Inline___Int128___empty__ = r""" +empty(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +0 +""" + +Table___Int128___empty__ = r""" +a +0 +""" + +Inline___Int128___notEmpty__ = r""" +notEmpty(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +1 +""" + +Table___Int128___notEmpty__ = r""" +a +1 +""" + +Inline___Int128___length__ = r""" +length(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +3 +""" + +Table___Int128___length__ = r""" +a +3 +""" + +Inline___Int128___arrayCount_x____x____1___ = r""" +arrayCount(lambda(tuple(x), equals(x, 1)), array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +1 +""" + +Table___Int128___arrayCount_x____x____1___ = r""" +a +1 +""" + +Inline___Int128___arrayUniq__ = r""" +arrayUniq(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +3 +""" + +Table___Int128___arrayUniq__ = r""" +a +3 +""" + +Inline___Int128___arrayJoin__ = r""" +arrayJoin(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +3 +2 +1 +""" + +Table___Int128___arrayJoin__ = r""" +a +1 +2 +3 +""" + +Inline___Int128___arrayExists_x____x__1__ = r""" +arrayExists(lambda(tuple(x), equals(x, 1)), array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +1 +""" + +Table___Int128___arrayExists_x____x__1__ = r""" +a +1 +""" + +Inline___Int128___arrayAll_x____x__1__ = r""" +arrayAll(lambda(tuple(x), equals(x, 1)), array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +0 +""" + +Table___Int128___arrayAll_x____x__1__ = r""" +a +0 +""" + +Inline___Int128___arrayMin__ = r""" +arrayMin(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +1 +""" + +Table___Int128___arrayMin__ = r""" +a +1 +""" + +Inline___Int128___arrayMax__ = r""" +arrayMax(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +3 +""" + +Table___Int128___arrayMax__ = r""" +a +3 +""" + +Inline___Int128___arraySum__ = r""" +arraySum(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +6 +""" + +Table___Int128___arraySum__ = r""" +a +6 +""" + +Inline___Int128___arrayAvg__ = r""" +arrayAvg(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +2 +""" + +Table___Int128___arrayAvg__ = r""" +a +2 +""" + +Inline___Int128___arrayReduce__max____ = r""" +arrayReduce(\'max\', array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +3 +""" + +Table___Int128___arrayReduce__max____ = r""" +a +3 +""" + +Inline___Int128___arrayFirst_x____x__3__ = r""" +arrayFirst(lambda(tuple(x), equals(x, 3)), array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +3 +""" + +Table___Int128___arrayFirst_x____x__3__ = r""" +a +3 +""" + +Inline___Int128___arrayFirstIndex_x____x__3__ = r""" +arrayFirstIndex(lambda(tuple(x), equals(x, 3)), array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +1 +""" + +Table___Int128___arrayFirstIndex_x____x__3__ = r""" +a +1 +""" + +Inline___Int128___hasAll__toInt128__3____toInt128__2____toInt128__1______ = r""" +hasAll(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\')), array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +1 +""" + +Table___Int128___hasAll__toInt128__3____toInt128__2____toInt128__1______ = r""" +a +1 +""" + +Inline___Int128___hasAny__toInt128__2____toInt128__1______ = r""" +hasAny(array(toInt128(\'2\'), toInt128(\'1\')), array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +1 +""" + +Table___Int128___hasAny__toInt128__2____toInt128__1______ = r""" +a +1 +""" + +Inline___Int128___hasSubstr__toInt128__2____toInt128__1______ = r""" +hasSubstr(array(toInt128(\'2\'), toInt128(\'1\')), array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\'))) +0 +""" + +Table___Int128___hasSubstr__toInt128__2____toInt128__1______ = r""" +a +0 +""" + +Table___Int128___arrayDifference__ = r""" +a +""" + +Table___Int128___arrayCumSum__ = r""" +a +""" + +Table___Int128___arrayCumSumNonNegative__ = r""" +a +""" + +Inline___Int128___arrayElement = r""" +arrayElement(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\')), 1) +3 +""" + +Table___Int128___arrayElement = r""" +a +3 +""" + +Inline___Int128___arrayPushBack = r""" +arrayPushBack(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\')), toInt128(\'1\')) +[3,2,1,1] +""" + +Table___Int128___arrayPushBack = r""" +a +[3,2,1,1] +""" + +Inline___Int128___arrayPushFront = r""" +arrayPushFront(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\')), toInt128(\'1\')) +[1,3,2,1] +""" + +Table___Int128___arrayPushFront = r""" +a +[1,3,2,1] +""" + +Inline___Int128___arrayResize = r""" +arrayResize(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\')), 1) +[3] +""" + +Table___Int128___arrayResize = r""" +a +[3] +""" + +Inline___Int128___arraySlice = r""" +arraySlice(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\')), 1) +[3,2,1] +""" + +Table___Int128___arraySlice = r""" +a +[3,2,1] +""" + +Inline___Int128___has = r""" +has(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\')), NULL) +0 +""" + +Table___Int128___has = r""" +a +0 +""" + +Inline___Int128___indexOf = r""" +indexOf(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\')), NULL) +0 +""" + +Table___Int128___indexOf = r""" +a +0 +""" + +Inline___Int128___countEqual = r""" +countEqual(array(toInt128(\'3\'), toInt128(\'2\'), toInt128(\'1\')), NULL) +0 +""" + +Table___Int128___countEqual = r""" +a +0 +""" + +Creating_a_tuple_with_Int128_on_a_table = r""" +a +(1,1,1) +""" + +tupleElement_with_Int128_on_a_table = r""" +a +1 +""" + +untuple_with_Int128_on_a_table = r""" +a +1 +""" + +tupleHammingDistance_with_Int128_on_a_table = r""" +a +2 +""" + +Creating_a_map_with_Int128_on_a_table = r""" +a +{'key1':1,'key2':2} +""" + +mapAdd_with_Int128 = r""" +mapAdd(tuple(array(toInt128(\'1\'), toInt128(\'2\')), array(toInt128(\'1\'), toInt128(\'2\'))), tuple(array(toInt128(\'1\'), toInt128(\'2\')), array(toInt128(\'1\'), toInt128(\'2\')))) +([1,2],[2,4]) +""" + +mapAdd_with_Int128_on_a_table = r""" +a +([1,2],[2,4]) +""" + +mapSubtract_with_Int128 = r""" +mapSubtract(tuple(array(toInt128(\'1\'), toInt128(\'2\')), array(toInt128(\'1\'), toInt128(\'2\'))), tuple(array(toInt128(\'1\'), toInt128(\'2\')), array(toInt128(\'1\'), toInt128(\'2\')))) +([1,2],[0,0]) +""" + +mapSubtract_with_Int128_on_a_table = r""" +a +([1,2],[0,0]) +""" + +mapPopulateSeries_with_Int128_on_a_table = r""" +a +([1,2,3,4,5],[1,2,3,0,0]) +""" + +mapContains_with_Int128_on_a_table = r""" +a +1 +""" + +mapKeys_with_Int128_on_a_table = r""" +a +['key1','key2'] +""" + +mapValues_with_Int128_on_a_table = r""" +a +[1,2] +""" + +Inline___Int256___arrayPopBack__ = r""" +arrayPopBack(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[3,2] +""" + +Table___Int256___arrayPopBack__ = r""" +a +[3,2] +""" + +Inline___Int256___arrayPopFront__ = r""" +arrayPopFront(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[2,1] +""" + +Table___Int256___arrayPopFront__ = r""" +a +[2,1] +""" + +Inline___Int256___arraySort__ = r""" +arraySort(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[1,2,3] +""" + +Table___Int256___arraySort__ = r""" +a +[1,2,3] +""" + +Inline___Int256___arrayReverseSort__ = r""" +arrayReverseSort(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[3,2,1] +""" + +Table___Int256___arrayReverseSort__ = r""" +a +[3,2,1] +""" + +Inline___Int256___arrayDistinct__ = r""" +arrayDistinct(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[3,2,1] +""" + +Table___Int256___arrayDistinct__ = r""" +a +[3,2,1] +""" + +Inline___Int256___arrayEnumerate__ = r""" +arrayEnumerate(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[1,2,3] +""" + +Table___Int256___arrayEnumerate__ = r""" +a +[1,2,3] +""" + +Inline___Int256___arrayEnumerateDense__ = r""" +arrayEnumerateDense(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[1,2,3] +""" + +Table___Int256___arrayEnumerateDense__ = r""" +a +[1,2,3] +""" + +Inline___Int256___arrayEnumerateUniq__ = r""" +arrayEnumerateUniq(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[1,1,1] +""" + +Table___Int256___arrayEnumerateUniq__ = r""" +a +[1,1,1] +""" + +Inline___Int256___arrayReverse__ = r""" +arrayReverse(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[1,2,3] +""" + +Table___Int256___arrayReverse__ = r""" +a +[1,2,3] +""" + +Inline___Int256___reverse__ = r""" +reverse(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[1,2,3] +""" + +Table___Int256___reverse__ = r""" +a +[1,2,3] +""" + +Inline___Int256___arrayFlatten__ = r""" +arrayFlatten(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[3,2,1] +""" + +Table___Int256___arrayFlatten__ = r""" +a +[3,2,1] +""" + +Inline___Int256___arrayCompact__ = r""" +arrayCompact(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[3,2,1] +""" + +Table___Int256___arrayCompact__ = r""" +a +[3,2,1] +""" + +Inline___Int256___arrayReduceInRanges__sum_____1__5____ = r""" +arrayReduceInRanges(\'sum\', array((1, 5)), array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[6] +""" + +Table___Int256___arrayReduceInRanges__sum_____1__5____ = r""" +a +[6] +""" + +Inline___Int256___arrayMap_x_____x___2___ = r""" +arrayMap(lambda(tuple(x), plus(x, 2)), array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[5,4,3] +""" + +Table___Int256___arrayMap_x_____x___2___ = r""" +a +[5,4,3] +""" + +Inline___Int256___arrayFill_x____x_3__ = r""" +arrayFill(lambda(tuple(x), equals(x, 3)), array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[3,3,3] +""" + +Table___Int256___arrayFill_x____x_3__ = r""" +a +[3,3,3] +""" + +Inline___Int256___arrayReverseFill_x____x_3__ = r""" +arrayReverseFill(lambda(tuple(x), equals(x, 3)), array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[3,1,1] +""" + +Table___Int256___arrayReverseFill_x____x_3__ = r""" +a +[3,1,1] +""" + +Inline___Int256___arrayConcat__toInt256__3____toInt256__2____toInt256__1_____ = r""" +arrayConcat(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\')), array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[3,2,1,3,2,1] +""" + +Table___Int256___arrayConcat__toInt256__3____toInt256__2____toInt256__1_____ = r""" +a +[3,2,1,3,2,1] +""" + +Inline___Int256___arrayFilter_x____x____1___ = r""" +arrayFilter(lambda(tuple(x), equals(x, 1)), array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[1] +""" + +Table___Int256___arrayFilter_x____x____1___ = r""" +a +[1] +""" + +Inline___Int256___arraySplit__x__y_____x_y___0__0__0___ = r""" +arraySplit(lambda(tuple(x, y), equals(x, y)), [0, 0, 0], array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +[[0,0,0]] +""" + +Table___Int256___arraySplit__x__y_____x_y___0__0__0___ = r""" +a +[[0,0,0]] +""" + +Inline___Int256___arrayZip__toInt256__1_____ = r""" +arrayZip(array(toInt256(\'1\')), array(toInt256(\'3\'))) +[(1,3)] +""" + +Table___Int256___arrayZip__toInt256__1_____ = r""" +a +[(1,1)] +""" + +Inline___Int256___empty__ = r""" +empty(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +0 +""" + +Table___Int256___empty__ = r""" +a +0 +""" + +Inline___Int256___notEmpty__ = r""" +notEmpty(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +1 +""" + +Table___Int256___notEmpty__ = r""" +a +1 +""" + +Inline___Int256___length__ = r""" +length(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +3 +""" + +Table___Int256___length__ = r""" +a +3 +""" + +Inline___Int256___arrayCount_x____x____1___ = r""" +arrayCount(lambda(tuple(x), equals(x, 1)), array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +1 +""" + +Table___Int256___arrayCount_x____x____1___ = r""" +a +1 +""" + +Inline___Int256___arrayUniq__ = r""" +arrayUniq(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +3 +""" + +Table___Int256___arrayUniq__ = r""" +a +3 +""" + +Inline___Int256___arrayJoin__ = r""" +arrayJoin(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +3 +2 +1 +""" + +Table___Int256___arrayJoin__ = r""" +a +1 +2 +3 +""" + +Inline___Int256___arrayExists_x____x__1__ = r""" +arrayExists(lambda(tuple(x), equals(x, 1)), array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +1 +""" + +Table___Int256___arrayExists_x____x__1__ = r""" +a +1 +""" + +Inline___Int256___arrayAll_x____x__1__ = r""" +arrayAll(lambda(tuple(x), equals(x, 1)), array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +0 +""" + +Table___Int256___arrayAll_x____x__1__ = r""" +a +0 +""" + +Inline___Int256___arrayMin__ = r""" +arrayMin(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +1 +""" + +Table___Int256___arrayMin__ = r""" +a +1 +""" + +Inline___Int256___arrayMax__ = r""" +arrayMax(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +3 +""" + +Table___Int256___arrayMax__ = r""" +a +3 +""" + +Inline___Int256___arraySum__ = r""" +arraySum(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +6 +""" + +Table___Int256___arraySum__ = r""" +a +6 +""" + +Inline___Int256___arrayAvg__ = r""" +arrayAvg(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +2 +""" + +Table___Int256___arrayAvg__ = r""" +a +2 +""" + +Inline___Int256___arrayReduce__max____ = r""" +arrayReduce(\'max\', array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +3 +""" + +Table___Int256___arrayReduce__max____ = r""" +a +3 +""" + +Inline___Int256___arrayFirst_x____x__3__ = r""" +arrayFirst(lambda(tuple(x), equals(x, 3)), array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +3 +""" + +Table___Int256___arrayFirst_x____x__3__ = r""" +a +3 +""" + +Inline___Int256___arrayFirstIndex_x____x__3__ = r""" +arrayFirstIndex(lambda(tuple(x), equals(x, 3)), array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +1 +""" + +Table___Int256___arrayFirstIndex_x____x__3__ = r""" +a +1 +""" + +Inline___Int256___hasAll__toInt256__3____toInt256__2____toInt256__1______ = r""" +hasAll(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\')), array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +1 +""" + +Table___Int256___hasAll__toInt256__3____toInt256__2____toInt256__1______ = r""" +a +1 +""" + +Inline___Int256___hasAny__toInt256__2____toInt256__1______ = r""" +hasAny(array(toInt256(\'2\'), toInt256(\'1\')), array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +1 +""" + +Table___Int256___hasAny__toInt256__2____toInt256__1______ = r""" +a +1 +""" + +Inline___Int256___hasSubstr__toInt256__2____toInt256__1______ = r""" +hasSubstr(array(toInt256(\'2\'), toInt256(\'1\')), array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\'))) +0 +""" + +Table___Int256___hasSubstr__toInt256__2____toInt256__1______ = r""" +a +0 +""" + +Table___Int256___arrayDifference__ = r""" +a +""" + +Table___Int256___arrayCumSum__ = r""" +a +""" + +Table___Int256___arrayCumSumNonNegative__ = r""" +a +""" + +Inline___Int256___arrayElement = r""" +arrayElement(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\')), 1) +3 +""" + +Table___Int256___arrayElement = r""" +a +3 +""" + +Inline___Int256___arrayPushBack = r""" +arrayPushBack(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\')), toInt256(\'1\')) +[3,2,1,1] +""" + +Table___Int256___arrayPushBack = r""" +a +[3,2,1,1] +""" + +Inline___Int256___arrayPushFront = r""" +arrayPushFront(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\')), toInt256(\'1\')) +[1,3,2,1] +""" + +Table___Int256___arrayPushFront = r""" +a +[1,3,2,1] +""" + +Inline___Int256___arrayResize = r""" +arrayResize(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\')), 1) +[3] +""" + +Table___Int256___arrayResize = r""" +a +[3] +""" + +Inline___Int256___arraySlice = r""" +arraySlice(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\')), 1) +[3,2,1] +""" + +Table___Int256___arraySlice = r""" +a +[3,2,1] +""" + +Inline___Int256___has = r""" +has(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\')), NULL) +0 +""" + +Table___Int256___has = r""" +a +0 +""" + +Inline___Int256___indexOf = r""" +indexOf(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\')), NULL) +0 +""" + +Table___Int256___indexOf = r""" +a +0 +""" + +Inline___Int256___countEqual = r""" +countEqual(array(toInt256(\'3\'), toInt256(\'2\'), toInt256(\'1\')), NULL) +0 +""" + +Table___Int256___countEqual = r""" +a +0 +""" + +Creating_a_tuple_with_Int256_on_a_table = r""" +a +(1,1,1) +""" + +tupleElement_with_Int256_on_a_table = r""" +a +1 +""" + +untuple_with_Int256_on_a_table = r""" +a +1 +""" + +tupleHammingDistance_with_Int256_on_a_table = r""" +a +2 +""" + +Creating_a_map_with_Int256_on_a_table = r""" +a +{'key1':1,'key2':2} +""" + +mapAdd_with_Int256 = r""" +mapAdd(tuple(array(toInt256(\'1\'), toInt256(\'2\')), array(toInt256(\'1\'), toInt256(\'2\'))), tuple(array(toInt256(\'1\'), toInt256(\'2\')), array(toInt256(\'1\'), toInt256(\'2\')))) +([1,2],[2,4]) +""" + +mapAdd_with_Int256_on_a_table = r""" +a +([1,2],[2,4]) +""" + +mapSubtract_with_Int256 = r""" +mapSubtract(tuple(array(toInt256(\'1\'), toInt256(\'2\')), array(toInt256(\'1\'), toInt256(\'2\'))), tuple(array(toInt256(\'1\'), toInt256(\'2\')), array(toInt256(\'1\'), toInt256(\'2\')))) +([1,2],[0,0]) +""" + +mapSubtract_with_Int256_on_a_table = r""" +a +([1,2],[0,0]) +""" + +mapPopulateSeries_with_Int256_on_a_table = r""" +a +([1,2,3,4,5],[1,2,3,0,0]) +""" + +mapContains_with_Int256_on_a_table = r""" +a +1 +""" + +mapKeys_with_Int256_on_a_table = r""" +a +['key1','key2'] +""" + +mapValues_with_Int256_on_a_table = r""" +a +[1,2] +""" + +Inline___UInt128___arrayPopBack__ = r""" +arrayPopBack(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[3,2] +""" + +Table___UInt128___arrayPopBack__ = r""" +a +[3,2] +""" + +Inline___UInt128___arrayPopFront__ = r""" +arrayPopFront(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[2,1] +""" + +Table___UInt128___arrayPopFront__ = r""" +a +[2,1] +""" + +Inline___UInt128___arraySort__ = r""" +arraySort(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[1,2,3] +""" + +Table___UInt128___arraySort__ = r""" +a +[1,2,3] +""" + +Inline___UInt128___arrayReverseSort__ = r""" +arrayReverseSort(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[3,2,1] +""" + +Table___UInt128___arrayReverseSort__ = r""" +a +[3,2,1] +""" + +Inline___UInt128___arrayDistinct__ = r""" +arrayDistinct(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[3,2,1] +""" + +Table___UInt128___arrayDistinct__ = r""" +a +[3,2,1] +""" + +Inline___UInt128___arrayEnumerate__ = r""" +arrayEnumerate(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[1,2,3] +""" + +Table___UInt128___arrayEnumerate__ = r""" +a +[1,2,3] +""" + +Inline___UInt128___arrayEnumerateDense__ = r""" +arrayEnumerateDense(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[1,2,3] +""" + +Table___UInt128___arrayEnumerateDense__ = r""" +a +[1,2,3] +""" + +Inline___UInt128___arrayEnumerateUniq__ = r""" +arrayEnumerateUniq(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[1,1,1] +""" + +Table___UInt128___arrayEnumerateUniq__ = r""" +a +[1,1,1] +""" + +Inline___UInt128___arrayReverse__ = r""" +arrayReverse(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[1,2,3] +""" + +Table___UInt128___arrayReverse__ = r""" +a +[1,2,3] +""" + +Inline___UInt128___reverse__ = r""" +reverse(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[1,2,3] +""" + +Table___UInt128___reverse__ = r""" +a +[1,2,3] +""" + +Inline___UInt128___arrayFlatten__ = r""" +arrayFlatten(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[3,2,1] +""" + +Table___UInt128___arrayFlatten__ = r""" +a +[3,2,1] +""" + +Inline___UInt128___arrayCompact__ = r""" +arrayCompact(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[3,2,1] +""" + +Table___UInt128___arrayCompact__ = r""" +a +[3,2,1] +""" + +Inline___UInt128___arrayReduceInRanges__sum_____1__5____ = r""" +arrayReduceInRanges(\'sum\', array((1, 5)), array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[6] +""" + +Table___UInt128___arrayReduceInRanges__sum_____1__5____ = r""" +a +[6] +""" + +Inline___UInt128___arrayMap_x_____x___2___ = r""" +arrayMap(lambda(tuple(x), plus(x, 2)), array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[5,4,3] +""" + +Table___UInt128___arrayMap_x_____x___2___ = r""" +a +[5,4,3] +""" + +Inline___UInt128___arrayFill_x____x_3__ = r""" +arrayFill(lambda(tuple(x), equals(x, 3)), array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[3,3,3] +""" + +Table___UInt128___arrayFill_x____x_3__ = r""" +a +[3,3,3] +""" + +Inline___UInt128___arrayReverseFill_x____x_3__ = r""" +arrayReverseFill(lambda(tuple(x), equals(x, 3)), array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[3,1,1] +""" + +Table___UInt128___arrayReverseFill_x____x_3__ = r""" +a +[3,1,1] +""" + +Inline___UInt128___arrayConcat__toUInt128__3____toUInt128__2____toUInt128__1_____ = r""" +arrayConcat(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\')), array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[3,2,1,3,2,1] +""" + +Table___UInt128___arrayConcat__toUInt128__3____toUInt128__2____toUInt128__1_____ = r""" +a +[3,2,1,3,2,1] +""" + +Inline___UInt128___arrayFilter_x____x____1___ = r""" +arrayFilter(lambda(tuple(x), equals(x, 1)), array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[1] +""" + +Table___UInt128___arrayFilter_x____x____1___ = r""" +a +[1] +""" + +Inline___UInt128___arraySplit__x__y_____x_y___0__0__0___ = r""" +arraySplit(lambda(tuple(x, y), equals(x, y)), [0, 0, 0], array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +[[0,0,0]] +""" + +Table___UInt128___arraySplit__x__y_____x_y___0__0__0___ = r""" +a +[[0,0,0]] +""" + +Inline___UInt128___arrayZip__toUInt128__1_____ = r""" +arrayZip(array(toUInt128(\'1\')), array(toUInt128(\'3\'))) +[(1,3)] +""" + +Table___UInt128___arrayZip__toUInt128__1_____ = r""" +a +[(1,1)] +""" + +Inline___UInt128___empty__ = r""" +empty(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +0 +""" + +Table___UInt128___empty__ = r""" +a +0 +""" + +Inline___UInt128___notEmpty__ = r""" +notEmpty(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +1 +""" + +Table___UInt128___notEmpty__ = r""" +a +1 +""" + +Inline___UInt128___length__ = r""" +length(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +3 +""" + +Table___UInt128___length__ = r""" +a +3 +""" + +Inline___UInt128___arrayCount_x____x____1___ = r""" +arrayCount(lambda(tuple(x), equals(x, 1)), array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +1 +""" + +Table___UInt128___arrayCount_x____x____1___ = r""" +a +1 +""" + +Inline___UInt128___arrayUniq__ = r""" +arrayUniq(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +3 +""" + +Table___UInt128___arrayUniq__ = r""" +a +3 +""" + +Inline___UInt128___arrayJoin__ = r""" +arrayJoin(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +3 +2 +1 +""" + +Table___UInt128___arrayJoin__ = r""" +a +1 +2 +3 +""" + +Inline___UInt128___arrayExists_x____x__1__ = r""" +arrayExists(lambda(tuple(x), equals(x, 1)), array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +1 +""" + +Table___UInt128___arrayExists_x____x__1__ = r""" +a +1 +""" + +Inline___UInt128___arrayAll_x____x__1__ = r""" +arrayAll(lambda(tuple(x), equals(x, 1)), array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +0 +""" + +Table___UInt128___arrayAll_x____x__1__ = r""" +a +0 +""" + +Inline___UInt128___arrayMin__ = r""" +arrayMin(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +1 +""" + +Table___UInt128___arrayMin__ = r""" +a +1 +""" + +Inline___UInt128___arrayMax__ = r""" +arrayMax(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +3 +""" + +Table___UInt128___arrayMax__ = r""" +a +3 +""" + +Inline___UInt128___arraySum__ = r""" +arraySum(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +6 +""" + +Table___UInt128___arraySum__ = r""" +a +6 +""" + +Inline___UInt128___arrayAvg__ = r""" +arrayAvg(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +2 +""" + +Table___UInt128___arrayAvg__ = r""" +a +2 +""" + +Inline___UInt128___arrayReduce__max____ = r""" +arrayReduce(\'max\', array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +3 +""" + +Table___UInt128___arrayReduce__max____ = r""" +a +3 +""" + +Inline___UInt128___arrayFirst_x____x__3__ = r""" +arrayFirst(lambda(tuple(x), equals(x, 3)), array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +3 +""" + +Table___UInt128___arrayFirst_x____x__3__ = r""" +a +3 +""" + +Inline___UInt128___arrayFirstIndex_x____x__3__ = r""" +arrayFirstIndex(lambda(tuple(x), equals(x, 3)), array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +1 +""" + +Table___UInt128___arrayFirstIndex_x____x__3__ = r""" +a +1 +""" + +Inline___UInt128___hasAll__toUInt128__3____toUInt128__2____toUInt128__1______ = r""" +hasAll(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\')), array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +1 +""" + +Table___UInt128___hasAll__toUInt128__3____toUInt128__2____toUInt128__1______ = r""" +a +1 +""" + +Inline___UInt128___hasAny__toUInt128__2____toUInt128__1______ = r""" +hasAny(array(toUInt128(\'2\'), toUInt128(\'1\')), array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +1 +""" + +Table___UInt128___hasAny__toUInt128__2____toUInt128__1______ = r""" +a +1 +""" + +Inline___UInt128___hasSubstr__toUInt128__2____toUInt128__1______ = r""" +hasSubstr(array(toUInt128(\'2\'), toUInt128(\'1\')), array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\'))) +0 +""" + +Table___UInt128___hasSubstr__toUInt128__2____toUInt128__1______ = r""" +a +0 +""" + +Table___UInt128___arrayDifference__ = r""" +a +""" + +Table___UInt128___arrayCumSum__ = r""" +a +""" + +Table___UInt128___arrayCumSumNonNegative__ = r""" +a +""" + +Inline___UInt128___arrayElement = r""" +arrayElement(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\')), 1) +3 +""" + +Table___UInt128___arrayElement = r""" +a +3 +""" + +Inline___UInt128___arrayPushBack = r""" +arrayPushBack(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\')), toUInt128(\'1\')) +[3,2,1,1] +""" + +Table___UInt128___arrayPushBack = r""" +a +[3,2,1,1] +""" + +Inline___UInt128___arrayPushFront = r""" +arrayPushFront(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\')), toUInt128(\'1\')) +[1,3,2,1] +""" + +Table___UInt128___arrayPushFront = r""" +a +[1,3,2,1] +""" + +Inline___UInt128___arrayResize = r""" +arrayResize(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\')), 1) +[3] +""" + +Table___UInt128___arrayResize = r""" +a +[3] +""" + +Inline___UInt128___arraySlice = r""" +arraySlice(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\')), 1) +[3,2,1] +""" + +Table___UInt128___arraySlice = r""" +a +[3,2,1] +""" + +Inline___UInt128___has = r""" +has(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\')), NULL) +0 +""" + +Table___UInt128___has = r""" +a +0 +""" + +Inline___UInt128___indexOf = r""" +indexOf(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\')), NULL) +0 +""" + +Table___UInt128___indexOf = r""" +a +0 +""" + +Inline___UInt128___countEqual = r""" +countEqual(array(toUInt128(\'3\'), toUInt128(\'2\'), toUInt128(\'1\')), NULL) +0 +""" + +Table___UInt128___countEqual = r""" +a +0 +""" + +Creating_a_tuple_with_UInt128_on_a_table = r""" +a +(1,1,1) +""" + +tupleElement_with_UInt128_on_a_table = r""" +a +1 +""" + +untuple_with_UInt128_on_a_table = r""" +a +1 +""" + +tupleHammingDistance_with_UInt128_on_a_table = r""" +a +2 +""" + +Creating_a_map_with_UInt128_on_a_table = r""" +a +{'key1':1,'key2':2} +""" + +mapAdd_with_UInt128 = r""" +mapAdd(tuple(array(toUInt128(\'1\'), toUInt128(\'2\')), array(toUInt128(\'1\'), toUInt128(\'2\'))), tuple(array(toUInt128(\'1\'), toUInt128(\'2\')), array(toUInt128(\'1\'), toUInt128(\'2\')))) +([1,2],[2,4]) +""" + +mapAdd_with_UInt128_on_a_table = r""" +a +([1,2],[2,4]) +""" + +mapSubtract_with_UInt128 = r""" +mapSubtract(tuple(array(toUInt128(\'1\'), toUInt128(\'2\')), array(toUInt128(\'1\'), toUInt128(\'2\'))), tuple(array(toUInt128(\'1\'), toUInt128(\'2\')), array(toUInt128(\'1\'), toUInt128(\'2\')))) +([1,2],[0,0]) +""" + +mapSubtract_with_UInt128_on_a_table = r""" +a +([1,2],[0,0]) +""" + +mapPopulateSeries_with_UInt128_on_a_table = r""" +a +([1,2,3,4,5],[1,2,3,0,0]) +""" + +mapContains_with_UInt128_on_a_table = r""" +a +1 +""" + +mapKeys_with_UInt128_on_a_table = r""" +a +['key1','key2'] +""" + +mapValues_with_UInt128_on_a_table = r""" +a +[1,2] +""" + +Inline___UInt256___arrayPopBack__ = r""" +arrayPopBack(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[3,2] +""" + +Table___UInt256___arrayPopBack__ = r""" +a +[3,2] +""" + +Inline___UInt256___arrayPopFront__ = r""" +arrayPopFront(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[2,1] +""" + +Table___UInt256___arrayPopFront__ = r""" +a +[2,1] +""" + +Inline___UInt256___arraySort__ = r""" +arraySort(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[1,2,3] +""" + +Table___UInt256___arraySort__ = r""" +a +[1,2,3] +""" + +Inline___UInt256___arrayReverseSort__ = r""" +arrayReverseSort(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[3,2,1] +""" + +Table___UInt256___arrayReverseSort__ = r""" +a +[3,2,1] +""" + +Inline___UInt256___arrayDistinct__ = r""" +arrayDistinct(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[3,2,1] +""" + +Table___UInt256___arrayDistinct__ = r""" +a +[3,2,1] +""" + +Inline___UInt256___arrayEnumerate__ = r""" +arrayEnumerate(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[1,2,3] +""" + +Table___UInt256___arrayEnumerate__ = r""" +a +[1,2,3] +""" + +Inline___UInt256___arrayEnumerateDense__ = r""" +arrayEnumerateDense(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[1,2,3] +""" + +Table___UInt256___arrayEnumerateDense__ = r""" +a +[1,2,3] +""" + +Inline___UInt256___arrayEnumerateUniq__ = r""" +arrayEnumerateUniq(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[1,1,1] +""" + +Table___UInt256___arrayEnumerateUniq__ = r""" +a +[1,1,1] +""" + +Inline___UInt256___arrayReverse__ = r""" +arrayReverse(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[1,2,3] +""" + +Table___UInt256___arrayReverse__ = r""" +a +[1,2,3] +""" + +Inline___UInt256___reverse__ = r""" +reverse(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[1,2,3] +""" + +Table___UInt256___reverse__ = r""" +a +[1,2,3] +""" + +Inline___UInt256___arrayFlatten__ = r""" +arrayFlatten(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[3,2,1] +""" + +Table___UInt256___arrayFlatten__ = r""" +a +[3,2,1] +""" + +Inline___UInt256___arrayCompact__ = r""" +arrayCompact(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[3,2,1] +""" + +Table___UInt256___arrayCompact__ = r""" +a +[3,2,1] +""" + +Inline___UInt256___arrayReduceInRanges__sum_____1__5____ = r""" +arrayReduceInRanges(\'sum\', array((1, 5)), array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[6] +""" + +Table___UInt256___arrayReduceInRanges__sum_____1__5____ = r""" +a +[6] +""" + +Inline___UInt256___arrayMap_x_____x___2___ = r""" +arrayMap(lambda(tuple(x), plus(x, 2)), array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[5,4,3] +""" + +Table___UInt256___arrayMap_x_____x___2___ = r""" +a +[5,4,3] +""" + +Inline___UInt256___arrayFill_x____x_3__ = r""" +arrayFill(lambda(tuple(x), equals(x, 3)), array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[3,3,3] +""" + +Table___UInt256___arrayFill_x____x_3__ = r""" +a +[3,3,3] +""" + +Inline___UInt256___arrayReverseFill_x____x_3__ = r""" +arrayReverseFill(lambda(tuple(x), equals(x, 3)), array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[3,1,1] +""" + +Table___UInt256___arrayReverseFill_x____x_3__ = r""" +a +[3,1,1] +""" + +Inline___UInt256___arrayConcat__toUInt256__3____toUInt256__2____toUInt256__1_____ = r""" +arrayConcat(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\')), array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[3,2,1,3,2,1] +""" + +Table___UInt256___arrayConcat__toUInt256__3____toUInt256__2____toUInt256__1_____ = r""" +a +[3,2,1,3,2,1] +""" + +Inline___UInt256___arrayFilter_x____x____1___ = r""" +arrayFilter(lambda(tuple(x), equals(x, 1)), array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[1] +""" + +Table___UInt256___arrayFilter_x____x____1___ = r""" +a +[1] +""" + +Inline___UInt256___arraySplit__x__y_____x_y___0__0__0___ = r""" +arraySplit(lambda(tuple(x, y), equals(x, y)), [0, 0, 0], array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +[[0,0,0]] +""" + +Table___UInt256___arraySplit__x__y_____x_y___0__0__0___ = r""" +a +[[0,0,0]] +""" + +Inline___UInt256___arrayZip__toUInt256__1_____ = r""" +arrayZip(array(toUInt256(\'1\')), array(toUInt256(\'3\'))) +[(1,3)] +""" + +Table___UInt256___arrayZip__toUInt256__1_____ = r""" +a +[(1,1)] +""" + +Inline___UInt256___empty__ = r""" +empty(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +0 +""" + +Table___UInt256___empty__ = r""" +a +0 +""" + +Inline___UInt256___notEmpty__ = r""" +notEmpty(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +1 +""" + +Table___UInt256___notEmpty__ = r""" +a +1 +""" + +Inline___UInt256___length__ = r""" +length(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +3 +""" + +Table___UInt256___length__ = r""" +a +3 +""" + +Inline___UInt256___arrayCount_x____x____1___ = r""" +arrayCount(lambda(tuple(x), equals(x, 1)), array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +1 +""" + +Table___UInt256___arrayCount_x____x____1___ = r""" +a +1 +""" + +Inline___UInt256___arrayUniq__ = r""" +arrayUniq(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +3 +""" + +Table___UInt256___arrayUniq__ = r""" +a +3 +""" + +Inline___UInt256___arrayJoin__ = r""" +arrayJoin(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +3 +2 +1 +""" + +Table___UInt256___arrayJoin__ = r""" +a +1 +2 +3 +""" + +Inline___UInt256___arrayExists_x____x__1__ = r""" +arrayExists(lambda(tuple(x), equals(x, 1)), array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +1 +""" + +Table___UInt256___arrayExists_x____x__1__ = r""" +a +1 +""" + +Inline___UInt256___arrayAll_x____x__1__ = r""" +arrayAll(lambda(tuple(x), equals(x, 1)), array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +0 +""" + +Table___UInt256___arrayAll_x____x__1__ = r""" +a +0 +""" + +Inline___UInt256___arrayMin__ = r""" +arrayMin(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +1 +""" + +Table___UInt256___arrayMin__ = r""" +a +1 +""" + +Inline___UInt256___arrayMax__ = r""" +arrayMax(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +3 +""" + +Table___UInt256___arrayMax__ = r""" +a +3 +""" + +Inline___UInt256___arraySum__ = r""" +arraySum(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +6 +""" + +Table___UInt256___arraySum__ = r""" +a +6 +""" + +Inline___UInt256___arrayAvg__ = r""" +arrayAvg(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +2 +""" + +Table___UInt256___arrayAvg__ = r""" +a +2 +""" + +Inline___UInt256___arrayReduce__max____ = r""" +arrayReduce(\'max\', array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +3 +""" + +Table___UInt256___arrayReduce__max____ = r""" +a +3 +""" + +Inline___UInt256___arrayFirst_x____x__3__ = r""" +arrayFirst(lambda(tuple(x), equals(x, 3)), array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +3 +""" + +Table___UInt256___arrayFirst_x____x__3__ = r""" +a +3 +""" + +Inline___UInt256___arrayFirstIndex_x____x__3__ = r""" +arrayFirstIndex(lambda(tuple(x), equals(x, 3)), array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +1 +""" + +Table___UInt256___arrayFirstIndex_x____x__3__ = r""" +a +1 +""" + +Inline___UInt256___hasAll__toUInt256__3____toUInt256__2____toUInt256__1______ = r""" +hasAll(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\')), array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +1 +""" + +Table___UInt256___hasAll__toUInt256__3____toUInt256__2____toUInt256__1______ = r""" +a +1 +""" + +Inline___UInt256___hasAny__toUInt256__2____toUInt256__1______ = r""" +hasAny(array(toUInt256(\'2\'), toUInt256(\'1\')), array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +1 +""" + +Table___UInt256___hasAny__toUInt256__2____toUInt256__1______ = r""" +a +1 +""" + +Inline___UInt256___hasSubstr__toUInt256__2____toUInt256__1______ = r""" +hasSubstr(array(toUInt256(\'2\'), toUInt256(\'1\')), array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\'))) +0 +""" + +Table___UInt256___hasSubstr__toUInt256__2____toUInt256__1______ = r""" +a +0 +""" + +Table___UInt256___arrayDifference__ = r""" +a +""" + +Table___UInt256___arrayCumSum__ = r""" +a +""" + +Table___UInt256___arrayCumSumNonNegative__ = r""" +a +""" + +Inline___UInt256___arrayElement = r""" +arrayElement(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\')), 1) +3 +""" + +Table___UInt256___arrayElement = r""" +a +3 +""" + +Inline___UInt256___arrayPushBack = r""" +arrayPushBack(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\')), toUInt256(\'1\')) +[3,2,1,1] +""" + +Table___UInt256___arrayPushBack = r""" +a +[3,2,1,1] +""" + +Inline___UInt256___arrayPushFront = r""" +arrayPushFront(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\')), toUInt256(\'1\')) +[1,3,2,1] +""" + +Table___UInt256___arrayPushFront = r""" +a +[1,3,2,1] +""" + +Inline___UInt256___arrayResize = r""" +arrayResize(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\')), 1) +[3] +""" + +Table___UInt256___arrayResize = r""" +a +[3] +""" + +Inline___UInt256___arraySlice = r""" +arraySlice(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\')), 1) +[3,2,1] +""" + +Table___UInt256___arraySlice = r""" +a +[3,2,1] +""" + +Inline___UInt256___has = r""" +has(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\')), NULL) +0 +""" + +Table___UInt256___has = r""" +a +0 +""" + +Inline___UInt256___indexOf = r""" +indexOf(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\')), NULL) +0 +""" + +Table___UInt256___indexOf = r""" +a +0 +""" + +Inline___UInt256___countEqual = r""" +countEqual(array(toUInt256(\'3\'), toUInt256(\'2\'), toUInt256(\'1\')), NULL) +0 +""" + +Table___UInt256___countEqual = r""" +a +0 +""" + +Creating_a_tuple_with_UInt256_on_a_table = r""" +a +(1,1,1) +""" + +tupleElement_with_UInt256_on_a_table = r""" +a +1 +""" + +untuple_with_UInt256_on_a_table = r""" +a +1 +""" + +tupleHammingDistance_with_UInt256_on_a_table = r""" +a +2 +""" + +Creating_a_map_with_UInt256_on_a_table = r""" +a +{'key1':1,'key2':2} +""" + +mapAdd_with_UInt256 = r""" +mapAdd(tuple(array(toUInt256(\'1\'), toUInt256(\'2\')), array(toUInt256(\'1\'), toUInt256(\'2\'))), tuple(array(toUInt256(\'1\'), toUInt256(\'2\')), array(toUInt256(\'1\'), toUInt256(\'2\')))) +([1,2],[2,4]) +""" + +mapAdd_with_UInt256_on_a_table = r""" +a +([1,2],[2,4]) +""" + +mapSubtract_with_UInt256 = r""" +mapSubtract(tuple(array(toUInt256(\'1\'), toUInt256(\'2\')), array(toUInt256(\'1\'), toUInt256(\'2\'))), tuple(array(toUInt256(\'1\'), toUInt256(\'2\')), array(toUInt256(\'1\'), toUInt256(\'2\')))) +([1,2],[0,0]) +""" + +mapSubtract_with_UInt256_on_a_table = r""" +a +([1,2],[0,0]) +""" + +mapPopulateSeries_with_UInt256_on_a_table = r""" +a +([1,2,3,4,5],[1,2,3,0,0]) +""" + +mapContains_with_UInt256_on_a_table = r""" +a +1 +""" + +mapKeys_with_UInt256_on_a_table = r""" +a +['key1','key2'] +""" + +mapValues_with_UInt256_on_a_table = r""" +a +[1,2] +""" + +Inline___Decimal256_0____arrayPopBack__ = r""" +arrayPopBack(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[3,2] +""" + +Table___Decimal256_0____arrayPopBack__ = r""" +a +[3,2] +""" + +Inline___Decimal256_0____arrayPopFront__ = r""" +arrayPopFront(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[2,1] +""" + +Table___Decimal256_0____arrayPopFront__ = r""" +a +[2,1] +""" + +Inline___Decimal256_0____arraySort__ = r""" +arraySort(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[1,2,3] +""" + +Table___Decimal256_0____arraySort__ = r""" +a +[1,2,3] +""" + +Inline___Decimal256_0____arrayReverseSort__ = r""" +arrayReverseSort(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[3,2,1] +""" + +Table___Decimal256_0____arrayReverseSort__ = r""" +a +[3,2,1] +""" + +Inline___Decimal256_0____arrayDistinct__ = r""" +arrayDistinct(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[3,2,1] +""" + +Table___Decimal256_0____arrayDistinct__ = r""" +a +[3,2,1] +""" + +Inline___Decimal256_0____arrayEnumerate__ = r""" +arrayEnumerate(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[1,2,3] +""" + +Table___Decimal256_0____arrayEnumerate__ = r""" +a +[1,2,3] +""" + +Inline___Decimal256_0____arrayEnumerateDense__ = r""" +arrayEnumerateDense(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[1,2,3] +""" + +Table___Decimal256_0____arrayEnumerateDense__ = r""" +a +[1,2,3] +""" + +Inline___Decimal256_0____arrayEnumerateUniq__ = r""" +arrayEnumerateUniq(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[1,1,1] +""" + +Table___Decimal256_0____arrayEnumerateUniq__ = r""" +a +[1,1,1] +""" + +Inline___Decimal256_0____arrayReverse__ = r""" +arrayReverse(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[1,2,3] +""" + +Table___Decimal256_0____arrayReverse__ = r""" +a +[1,2,3] +""" + +Inline___Decimal256_0____reverse__ = r""" +reverse(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[1,2,3] +""" + +Table___Decimal256_0____reverse__ = r""" +a +[1,2,3] +""" + +Inline___Decimal256_0____arrayFlatten__ = r""" +arrayFlatten(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[3,2,1] +""" + +Table___Decimal256_0____arrayFlatten__ = r""" +a +[3,2,1] +""" + +Inline___Decimal256_0____arrayCompact__ = r""" +arrayCompact(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[3,2,1] +""" + +Table___Decimal256_0____arrayCompact__ = r""" +a +[3,2,1] +""" + +Inline___Decimal256_0____arrayReduceInRanges__sum_____1__5____ = r""" +arrayReduceInRanges(\'sum\', array((1, 5)), array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[6] +""" + +Table___Decimal256_0____arrayReduceInRanges__sum_____1__5____ = r""" +a +[6] +""" + +Inline___Decimal256_0____arrayMap_x_____x___2___ = r""" +arrayMap(lambda(tuple(x), plus(x, 2)), array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[5,4,3] +""" + +Table___Decimal256_0____arrayMap_x_____x___2___ = r""" +a +[5,4,3] +""" + +Inline___Decimal256_0____arrayFill_x____x_3__ = r""" +arrayFill(lambda(tuple(x), equals(x, 3)), array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[3,3,3] +""" + +Table___Decimal256_0____arrayFill_x____x_3__ = r""" +a +[3,3,3] +""" + +Inline___Decimal256_0____arrayReverseFill_x____x_3__ = r""" +arrayReverseFill(lambda(tuple(x), equals(x, 3)), array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[3,1,1] +""" + +Table___Decimal256_0____arrayReverseFill_x____x_3__ = r""" +a +[3,1,1] +""" + +Inline___Decimal256_0____arrayConcat__toDecimal256__3__0___toDecimal256__2__0___toDecimal256__1__0____ = r""" +arrayConcat(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0)), array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[3,2,1,3,2,1] +""" + +Table___Decimal256_0____arrayConcat__toDecimal256__3__0___toDecimal256__2__0___toDecimal256__1__0____ = r""" +a +[3,2,1,3,2,1] +""" + +Inline___Decimal256_0____arrayFilter_x____x____1___ = r""" +arrayFilter(lambda(tuple(x), equals(x, 1)), array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[1] +""" + +Table___Decimal256_0____arrayFilter_x____x____1___ = r""" +a +[1] +""" + +Inline___Decimal256_0____arraySplit__x__y_____x_y___0__0__0___ = r""" +arraySplit(lambda(tuple(x, y), equals(x, y)), [0, 0, 0], array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +[[0,0,0]] +""" + +Table___Decimal256_0____arraySplit__x__y_____x_y___0__0__0___ = r""" +a +[[0,0,0]] +""" + +Inline___Decimal256_0____arrayZip__toDecimal256__1__0____ = r""" +arrayZip(array(toDecimal256(\'1\', 0)), array(toDecimal256(\'3\', 0))) +[(1,3)] +""" + +Table___Decimal256_0____arrayZip__toDecimal256__1__0____ = r""" +a +[(1,1)] +""" + +Inline___Decimal256_0____empty__ = r""" +empty(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +0 +""" + +Table___Decimal256_0____empty__ = r""" +a +0 +""" + +Inline___Decimal256_0____notEmpty__ = r""" +notEmpty(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +1 +""" + +Table___Decimal256_0____notEmpty__ = r""" +a +1 +""" + +Inline___Decimal256_0____length__ = r""" +length(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +3 +""" + +Table___Decimal256_0____length__ = r""" +a +3 +""" + +Inline___Decimal256_0____arrayCount_x____x____1___ = r""" +arrayCount(lambda(tuple(x), equals(x, 1)), array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +1 +""" + +Table___Decimal256_0____arrayCount_x____x____1___ = r""" +a +1 +""" + +Inline___Decimal256_0____arrayUniq__ = r""" +arrayUniq(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +3 +""" + +Table___Decimal256_0____arrayUniq__ = r""" +a +3 +""" + +Inline___Decimal256_0____arrayJoin__ = r""" +arrayJoin(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +3 +2 +1 +""" + +Table___Decimal256_0____arrayJoin__ = r""" +a +1 +2 +3 +""" + +Inline___Decimal256_0____arrayExists_x____x__1__ = r""" +arrayExists(lambda(tuple(x), equals(x, 1)), array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +1 +""" + +Table___Decimal256_0____arrayExists_x____x__1__ = r""" +a +1 +""" + +Inline___Decimal256_0____arrayAll_x____x__1__ = r""" +arrayAll(lambda(tuple(x), equals(x, 1)), array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +0 +""" + +Table___Decimal256_0____arrayAll_x____x__1__ = r""" +a +0 +""" + +Table___Decimal256_0____arrayMin__ = r""" +a +""" + +Table___Decimal256_0____arrayMax__ = r""" +a +""" + +Table___Decimal256_0____arraySum__ = r""" +a +""" + +Table___Decimal256_0____arrayAvg__ = r""" +a +""" + +Inline___Decimal256_0____arrayReduce__max____ = r""" +arrayReduce(\'max\', array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +3 +""" + +Table___Decimal256_0____arrayReduce__max____ = r""" +a +3 +""" + +Inline___Decimal256_0____arrayFirst_x____x__3__ = r""" +arrayFirst(lambda(tuple(x), equals(x, 3)), array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +3 +""" + +Table___Decimal256_0____arrayFirst_x____x__3__ = r""" +a +3 +""" + +Inline___Decimal256_0____arrayFirstIndex_x____x__3__ = r""" +arrayFirstIndex(lambda(tuple(x), equals(x, 3)), array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +1 +""" + +Table___Decimal256_0____arrayFirstIndex_x____x__3__ = r""" +a +1 +""" + +Inline___Decimal256_0____hasAll__toDecimal256__3__0___toDecimal256__2__0___toDecimal256__1__0_____ = r""" +hasAll(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0)), array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +1 +""" + +Table___Decimal256_0____hasAll__toDecimal256__3__0___toDecimal256__2__0___toDecimal256__1__0_____ = r""" +a +1 +""" + +Inline___Decimal256_0____hasAny__toDecimal256__2__0___toDecimal256__1__0_____ = r""" +hasAny(array(toDecimal256(\'2\', 0), toDecimal256(\'1\', 0)), array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +1 +""" + +Table___Decimal256_0____hasAny__toDecimal256__2__0___toDecimal256__1__0_____ = r""" +a +1 +""" + +Inline___Decimal256_0____hasSubstr__toDecimal256__2__0___toDecimal256__1__0_____ = r""" +hasSubstr(array(toDecimal256(\'2\', 0), toDecimal256(\'1\', 0)), array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0))) +0 +""" + +Table___Decimal256_0____hasSubstr__toDecimal256__2__0___toDecimal256__1__0_____ = r""" +a +0 +""" + +Table___Decimal256_0____arrayDifference__ = r""" +a +""" + +Table___Decimal256_0____arrayCumSum__ = r""" +a +""" + +Table___Decimal256_0____arrayCumSumNonNegative__ = r""" +a +""" + +Inline___Decimal256_0____arrayElement = r""" +arrayElement(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0)), 1) +3 +""" + +Table___Decimal256_0____arrayElement = r""" +a +3 +""" + +Inline___Decimal256_0____arrayPushBack = r""" +arrayPushBack(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0)), toDecimal256(\'1\', 0)) +[3,2,1,1] +""" + +Table___Decimal256_0____arrayPushBack = r""" +a +[3,2,1,1] +""" + +Inline___Decimal256_0____arrayPushFront = r""" +arrayPushFront(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0)), toDecimal256(\'1\', 0)) +[1,3,2,1] +""" + +Table___Decimal256_0____arrayPushFront = r""" +a +[1,3,2,1] +""" + +Inline___Decimal256_0____arrayResize = r""" +arrayResize(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0)), 1) +[3] +""" + +Table___Decimal256_0____arrayResize = r""" +a +[3] +""" + +Inline___Decimal256_0____arraySlice = r""" +arraySlice(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0)), 1) +[3,2,1] +""" + +Table___Decimal256_0____arraySlice = r""" +a +[3,2,1] +""" + +Inline___Decimal256_0____has = r""" +has(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0)), NULL) +0 +""" + +Table___Decimal256_0____has = r""" +a +0 +""" + +Inline___Decimal256_0____indexOf = r""" +indexOf(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0)), NULL) +0 +""" + +Table___Decimal256_0____indexOf = r""" +a +0 +""" + +Inline___Decimal256_0____countEqual = r""" +countEqual(array(toDecimal256(\'3\', 0), toDecimal256(\'2\', 0), toDecimal256(\'1\', 0)), NULL) +0 +""" + +Table___Decimal256_0____countEqual = r""" +a +0 +""" + +Creating_a_tuple_with_Decimal256_0__on_a_table = r""" +a +(1,1,1) +""" + +tupleElement_with_Decimal256_0__on_a_table = r""" +a +1 +""" + +untuple_with_Decimal256_0__on_a_table = r""" +a +1 +""" + +tupleHammingDistance_with_Decimal256_0__on_a_table = r""" +a +2 +""" + +Creating_a_map_with_Decimal256_0__on_a_table = r""" +a +{'key1':1,'key2':2} +""" + +mapAdd_with_Decimal256_0__on_a_table = r""" +a +""" + +mapSubtract_with_Decimal256_0__on_a_table = r""" +a +""" + +mapPopulateSeries_with_Decimal256_0__on_a_table = r""" +a +""" + +mapContains_with_Decimal256_0__on_a_table = r""" +a +1 +""" + +mapKeys_with_Decimal256_0__on_a_table = r""" +a +['key1','key2'] +""" + +mapValues_with_Decimal256_0__on_a_table = r""" +a +[1,2] +""" + +I_check_equals_with_Int128 = r""" +equals(toInt128(1), toInt128(1)) equals(toInt128(\'170141183460469231731687303715884105727\'), toInt128(\'-170141183460469231731687303715884105728\')) +1 0 +""" + +I_check_equals_with_Int256 = r""" +equals(toInt256(1), toInt256(1)) equals(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) +1 0 +""" + +I_check_equals_with_UInt128 = r""" +equals(toUInt128(1), toUInt128(1)) equals(toUInt128(\'340282366920938463463374607431768211455\'), toUInt128(\'0\')) +1 0 +""" + +I_check_equals_with_UInt256 = r""" +equals(toUInt256(1), toUInt256(1)) equals(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), toUInt256(\'0\')) +1 0 +""" + +I_check_notEquals_with_Int128 = r""" +notEquals(toInt128(1), toInt128(1)) notEquals(toInt128(\'170141183460469231731687303715884105727\'), toInt128(\'-170141183460469231731687303715884105728\')) +0 1 +""" + +I_check_notEquals_with_Int256 = r""" +notEquals(toInt256(1), toInt256(1)) notEquals(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) +0 1 +""" + +I_check_notEquals_with_UInt128 = r""" +notEquals(toUInt128(1), toUInt128(1)) notEquals(toUInt128(\'340282366920938463463374607431768211455\'), toUInt128(\'0\')) +0 1 +""" + +I_check_notEquals_with_UInt256 = r""" +notEquals(toUInt256(1), toUInt256(1)) notEquals(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), toUInt256(\'0\')) +0 1 +""" + +I_check_less_with_Int128 = r""" +less(toInt128(1), toInt128(1)) less(toInt128(\'170141183460469231731687303715884105727\'), toInt128(\'-170141183460469231731687303715884105728\')) +0 0 +""" + +I_check_less_with_Int256 = r""" +less(toInt256(1), toInt256(1)) less(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) +0 0 +""" + +I_check_less_with_UInt128 = r""" +less(toUInt128(1), toUInt128(1)) less(toUInt128(\'340282366920938463463374607431768211455\'), toUInt128(\'0\')) +0 0 +""" + +I_check_less_with_UInt256 = r""" +less(toUInt256(1), toUInt256(1)) less(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), toUInt256(\'0\')) +0 0 +""" + +I_check_greater_with_Int128 = r""" +greater(toInt128(1), toInt128(1)) greater(toInt128(\'170141183460469231731687303715884105727\'), toInt128(\'-170141183460469231731687303715884105728\')) +0 1 +""" + +I_check_greater_with_Int256 = r""" +greater(toInt256(1), toInt256(1)) greater(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) +0 1 +""" + +I_check_greater_with_UInt128 = r""" +greater(toUInt128(1), toUInt128(1)) greater(toUInt128(\'340282366920938463463374607431768211455\'), toUInt128(\'0\')) +0 1 +""" + +I_check_greater_with_UInt256 = r""" +greater(toUInt256(1), toUInt256(1)) greater(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), toUInt256(\'0\')) +0 1 +""" + +I_check_lessOrEquals_with_Int128 = r""" +lessOrEquals(toInt128(1), toInt128(1)) lessOrEquals(toInt128(\'170141183460469231731687303715884105727\'), toInt128(\'-170141183460469231731687303715884105728\')) +1 0 +""" + +I_check_lessOrEquals_with_Int256 = r""" +lessOrEquals(toInt256(1), toInt256(1)) lessOrEquals(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) +1 0 +""" + +I_check_lessOrEquals_with_UInt128 = r""" +lessOrEquals(toUInt128(1), toUInt128(1)) lessOrEquals(toUInt128(\'340282366920938463463374607431768211455\'), toUInt128(\'0\')) +1 0 +""" + +I_check_lessOrEquals_with_UInt256 = r""" +lessOrEquals(toUInt256(1), toUInt256(1)) lessOrEquals(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), toUInt256(\'0\')) +1 0 +""" + +I_check_greaterOrEquals_with_Int128 = r""" +greaterOrEquals(toInt128(1), toInt128(1)) greaterOrEquals(toInt128(\'170141183460469231731687303715884105727\'), toInt128(\'-170141183460469231731687303715884105728\')) +1 1 +""" + +I_check_greaterOrEquals_with_Int256 = r""" +greaterOrEquals(toInt256(1), toInt256(1)) greaterOrEquals(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) +1 1 +""" + +I_check_greaterOrEquals_with_UInt128 = r""" +greaterOrEquals(toUInt128(1), toUInt128(1)) greaterOrEquals(toUInt128(\'340282366920938463463374607431768211455\'), toUInt128(\'0\')) +1 1 +""" + +I_check_greaterOrEquals_with_UInt256 = r""" +greaterOrEquals(toUInt256(1), toUInt256(1)) greaterOrEquals(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), toUInt256(\'0\')) +1 1 +""" + +I_check_the_table_for_the_output_of_equals_with_Int128 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_equals_with_Int256 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_equals_with_UInt128 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_equals_with_UInt256 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_notEquals_with_Int128 = r""" +a +0 +1 +1 +""" + +I_check_the_table_for_the_output_of_notEquals_with_Int256 = r""" +a +0 +1 +1 +""" + +I_check_the_table_for_the_output_of_notEquals_with_UInt128 = r""" +a +0 +1 +1 +""" + +I_check_the_table_for_the_output_of_notEquals_with_UInt256 = r""" +a +0 +1 +1 +""" + +I_check_the_table_for_the_output_of_less_with_Int128 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_less_with_Int256 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_less_with_UInt128 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_less_with_UInt256 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_greater_with_Int128 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_greater_with_Int256 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_greater_with_UInt128 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_greater_with_UInt256 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_lessOrEquals_with_Int128 = r""" +a +0 +1 +1 +""" + +I_check_the_table_for_the_output_of_lessOrEquals_with_Int256 = r""" +a +0 +1 +1 +""" + +I_check_the_table_for_the_output_of_lessOrEquals_with_UInt128 = r""" +a +0 +1 +1 +""" + +I_check_the_table_for_the_output_of_lessOrEquals_with_UInt256 = r""" +a +0 +1 +1 +""" + +I_check_the_table_for_the_output_of_greaterOrEquals_with_Int128 = r""" +a +0 +1 +1 +""" + +I_check_the_table_for_the_output_of_greaterOrEquals_with_Int256 = r""" +a +0 +1 +1 +""" + +I_check_the_table_for_the_output_of_greaterOrEquals_with_UInt128 = r""" +a +0 +1 +1 +""" + +I_check_the_table_for_the_output_of_greaterOrEquals_with_UInt256 = r""" +a +0 +1 +1 +""" + +I_check_equals_with_Decimal256 = r""" +equals(toDecimal256(1, 0), toDecimal256(1, 0)) equals(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0), toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +1 0 +""" + +I_check_notEquals_with_Decimal256 = r""" +notEquals(toDecimal256(1, 0), toDecimal256(1, 0)) notEquals(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0), toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +0 1 +""" + +I_check_less_with_Decimal256 = r""" +less(toDecimal256(1, 0), toDecimal256(1, 0)) less(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0), toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +0 0 +""" + +I_check_greater_with_Decimal256 = r""" +greater(toDecimal256(1, 0), toDecimal256(1, 0)) greater(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0), toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +0 1 +""" + +I_check_lessOrEquals_with_Decimal256 = r""" +lessOrEquals(toDecimal256(1, 0), toDecimal256(1, 0)) lessOrEquals(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0), toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +1 0 +""" + +I_check_greaterOrEquals_with_Decimal256 = r""" +greaterOrEquals(toDecimal256(1, 0), toDecimal256(1, 0)) greaterOrEquals(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0), toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +1 1 +""" + +I_check_the_table_for_the_output_of_equals_with_Decimal256 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_notEquals_with_Decimal256 = r""" +a +0 +1 +1 +""" + +I_check_the_table_for_the_output_of_less_with_Decimal256 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_greater_with_Decimal256 = r""" +a +0 +0 +1 +""" + +I_check_the_table_for_the_output_of_lessOrEquals_with_Decimal256 = r""" +a +0 +1 +1 +""" + +I_check_the_table_for_the_output_of_greaterOrEquals_with_Decimal256 = r""" +a +0 +1 +1 +""" + +I_check_exp__with_Int128_using_max_and_min = r""" +round(exp(toInt128(\'170141183460469231731687303715884105727\')), 7) round(exp(toInt128(\'-170141183460469231731687303715884105728\')), 7) +inf 0 +""" + +I_check_exp__with_Int256_using_max_and_min = r""" +round(exp(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(exp(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +inf 0 +""" + +I_check_exp__with_UInt128_using_max_and_min = r""" +round(exp(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(exp(toUInt128(\'0\')), 7) +inf 1 +""" + +I_check_exp__with_UInt256_using_max_and_min = r""" +round(exp(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(exp(toUInt256(\'0\')), 7) +inf 1 +""" + +I_check_log__with_Int128_using_max_and_min = r""" +round(log(toInt128(\'170141183460469231731687303715884105727\')), 7) round(log(toInt128(\'-170141183460469231731687303715884105728\')), 7) +88.0296919 nan +""" + +I_check_log__with_Int256_using_max_and_min = r""" +round(log(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(log(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +176.752531 nan +""" + +I_check_log__with_UInt128_using_max_and_min = r""" +round(log(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(log(toUInt128(\'0\')), 7) +88.7228391 -inf +""" + +I_check_log__with_UInt256_using_max_and_min = r""" +round(log(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(log(toUInt256(\'0\')), 7) +177.4456782 -inf +""" + +I_check_ln__with_Int128_using_max_and_min = r""" +round(log(toInt128(\'170141183460469231731687303715884105727\')), 7) round(log(toInt128(\'-170141183460469231731687303715884105728\')), 7) +88.0296919 nan +""" + +I_check_ln__with_Int256_using_max_and_min = r""" +round(log(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(log(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +176.752531 nan +""" + +I_check_ln__with_UInt128_using_max_and_min = r""" +round(log(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(log(toUInt128(\'0\')), 7) +88.7228391 -inf +""" + +I_check_ln__with_UInt256_using_max_and_min = r""" +round(log(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(log(toUInt256(\'0\')), 7) +177.4456782 -inf +""" + +I_check_exp2__with_Int128_using_max_and_min = r""" +round(exp2(toInt128(\'170141183460469231731687303715884105727\')), 7) round(exp2(toInt128(\'-170141183460469231731687303715884105728\')), 7) +inf 0 +""" + +I_check_exp2__with_Int256_using_max_and_min = r""" +round(exp2(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(exp2(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +inf 0 +""" + +I_check_exp2__with_UInt128_using_max_and_min = r""" +round(exp2(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(exp2(toUInt128(\'0\')), 7) +inf 1 +""" + +I_check_exp2__with_UInt256_using_max_and_min = r""" +round(exp2(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(exp2(toUInt256(\'0\')), 7) +inf 1 +""" + +I_check_log2__with_Int128_using_max_and_min = r""" +round(log2(toInt128(\'170141183460469231731687303715884105727\')), 7) round(log2(toInt128(\'-170141183460469231731687303715884105728\')), 7) +127 nan +""" + +I_check_log2__with_Int256_using_max_and_min = r""" +round(log2(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(log2(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +255 nan +""" + +I_check_log2__with_UInt128_using_max_and_min = r""" +round(log2(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(log2(toUInt128(\'0\')), 7) +128 -inf +""" + +I_check_log2__with_UInt256_using_max_and_min = r""" +round(log2(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(log2(toUInt256(\'0\')), 7) +256 -inf +""" + +I_check_exp10__with_Int128_using_max_and_min = r""" +round(exp10(toInt128(\'170141183460469231731687303715884105727\')), 7) round(exp10(toInt128(\'-170141183460469231731687303715884105728\')), 7) +inf 0 +""" + +I_check_exp10__with_Int256_using_max_and_min = r""" +round(exp10(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(exp10(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +inf 0 +""" + +I_check_exp10__with_UInt128_using_max_and_min = r""" +round(exp10(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(exp10(toUInt128(\'0\')), 7) +inf 1 +""" + +I_check_exp10__with_UInt256_using_max_and_min = r""" +round(exp10(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(exp10(toUInt256(\'0\')), 7) +inf 1 +""" + +I_check_log10__with_Int128_using_max_and_min = r""" +round(log10(toInt128(\'170141183460469231731687303715884105727\')), 7) round(log10(toInt128(\'-170141183460469231731687303715884105728\')), 7) +38.2308094 nan +""" + +I_check_log10__with_Int256_using_max_and_min = r""" +round(log10(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(log10(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +76.7626489 nan +""" + +I_check_log10__with_UInt128_using_max_and_min = r""" +round(log10(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(log10(toUInt128(\'0\')), 7) +38.5318394 -inf +""" + +I_check_log10__with_UInt256_using_max_and_min = r""" +round(log10(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(log10(toUInt256(\'0\')), 7) +77.0636789 -inf +""" + +I_check_sqrt__with_Int128_using_max_and_min = r""" +round(sqrt(toInt128(\'170141183460469231731687303715884105727\')), 7) round(sqrt(toInt128(\'-170141183460469231731687303715884105728\')), 7) +13043817825332783000 nan +""" + +I_check_sqrt__with_Int256_using_max_and_min = r""" +round(sqrt(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(sqrt(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +2.4061596916800453e38 nan +""" + +I_check_sqrt__with_UInt128_using_max_and_min = r""" +round(sqrt(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(sqrt(toUInt128(\'0\')), 7) +18446744073709552000 0 +""" + +I_check_sqrt__with_UInt256_using_max_and_min = r""" +round(sqrt(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(sqrt(toUInt256(\'0\')), 7) +3.402823669209385e38 0 +""" + +I_check_cbrt__with_Int128_using_max_and_min = r""" +round(cbrt(toInt128(\'170141183460469231731687303715884105727\')), 7) round(cbrt(toInt128(\'-170141183460469231731687303715884105728\')), 7) +5541191377756.637 -5541191377756.637 +""" + +I_check_cbrt__with_Int256_using_max_and_min = r""" +round(cbrt(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(cbrt(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +3.8685626227668134e25 -3.8685626227668134e25 +""" + +I_check_cbrt__with_UInt128_using_max_and_min = r""" +round(cbrt(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(cbrt(toUInt128(\'0\')), 7) +6981463658331.56 0 +""" + +I_check_cbrt__with_UInt256_using_max_and_min = r""" +round(cbrt(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(cbrt(toUInt256(\'0\')), 7) +4.874083481260429e25 0 +""" + +I_check_erf__with_Int128_using_max_and_min = r""" +round(erf(toInt128(\'170141183460469231731687303715884105727\')), 7) round(erf(toInt128(\'-170141183460469231731687303715884105728\')), 7) +1 -1 +""" + +I_check_erf__with_Int256_using_max_and_min = r""" +round(erf(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(erf(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +1 -1 +""" + +I_check_erf__with_UInt128_using_max_and_min = r""" +round(erf(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(erf(toUInt128(\'0\')), 7) +1 0 +""" + +I_check_erf__with_UInt256_using_max_and_min = r""" +round(erf(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(erf(toUInt256(\'0\')), 7) +1 0 +""" + +I_check_erfc__with_Int128_using_max_and_min = r""" +round(erfc(toInt128(\'170141183460469231731687303715884105727\')), 7) round(erfc(toInt128(\'-170141183460469231731687303715884105728\')), 7) +0 2 +""" + +I_check_erfc__with_Int256_using_max_and_min = r""" +round(erfc(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(erfc(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +0 2 +""" + +I_check_erfc__with_UInt128_using_max_and_min = r""" +round(erfc(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(erfc(toUInt128(\'0\')), 7) +0 1 +""" + +I_check_erfc__with_UInt256_using_max_and_min = r""" +round(erfc(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(erfc(toUInt256(\'0\')), 7) +0 1 +""" + +I_check_lgamma__with_Int128_using_max_and_min = r""" +round(lgamma(toInt128(\'170141183460469231731687303715884105727\')), 7) round(lgamma(toInt128(\'-170141183460469231731687303715884105728\')), 7) +1.4807334781359624e40 -1.4807334781359624e40 +""" + +I_check_lgamma__with_Int256_using_max_and_min = r""" +round(lgamma(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(lgamma(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +1.0175376379095233e79 -1.0175376379095233e79 +""" + +I_check_lgamma__with_UInt128_using_max_and_min = r""" +round(lgamma(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(lgamma(toUInt128(\'0\')), 7) +2.985053532594476e40 inf +""" + +I_check_lgamma__with_UInt256_using_max_and_min = r""" +round(lgamma(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(lgamma(toUInt256(\'0\')), 7) +2.0431013718376458e79 inf +""" + +I_check_tgamma__with_Int128_using_max_and_min = r""" +round(tgamma(toInt128(\'170141183460469231731687303715884105727\')), 7) round(tgamma(toInt128(\'-170141183460469231731687303715884105728\')), 7) +inf nan +""" + +I_check_tgamma__with_Int256_using_max_and_min = r""" +round(tgamma(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(tgamma(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +inf nan +""" + +I_check_tgamma__with_UInt128_using_max_and_min = r""" +round(tgamma(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(tgamma(toUInt128(\'0\')), 7) +inf inf +""" + +I_check_tgamma__with_UInt256_using_max_and_min = r""" +round(tgamma(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(tgamma(toUInt256(\'0\')), 7) +inf inf +""" + +I_check_sin__with_Int128_using_max_and_min = r""" +round(sin(toInt128(\'170141183460469231731687303715884105727\')), 7) round(sin(toInt128(\'-170141183460469231731687303715884105728\')), 7) +0.6233855 -0.6233855 +""" + +I_check_sin__with_Int256_using_max_and_min = r""" +round(sin(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(sin(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +0.9751222 -0.9751222 +""" + +I_check_sin__with_UInt128_using_max_and_min = r""" +round(sin(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(sin(toUInt128(\'0\')), 7) +0.9748685 0 +""" + +I_check_sin__with_UInt256_using_max_and_min = r""" +round(sin(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(sin(toUInt256(\'0\')), 7) +0.4323066 0 +""" + +I_check_cos__with_Int128_using_max_and_min = r""" +round(cos(toInt128(\'170141183460469231731687303715884105727\')), 7) round(cos(toInt128(\'-170141183460469231731687303715884105728\')), 7) +0.7819146 0.7819146 +""" + +I_check_cos__with_Int256_using_max_and_min = r""" +round(cos(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(cos(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +0.2216679 0.2216679 +""" + +I_check_cos__with_UInt128_using_max_and_min = r""" +round(cos(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(cos(toUInt128(\'0\')), 7) +0.222781 1 +""" + +I_check_cos__with_UInt256_using_max_and_min = r""" +round(cos(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(cos(toUInt256(\'0\')), 7) +-0.9017267 1 +""" + +I_check_tan__with_Int128_using_max_and_min = r""" +round(tan(toInt128(\'170141183460469231731687303715884105727\')), 7) round(tan(toInt128(\'-170141183460469231731687303715884105728\')), 7) +0.7972552 -0.7972552 +""" + +I_check_tan__with_Int256_using_max_and_min = r""" +round(tan(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(tan(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +4.3990229 -4.3990229 +""" + +I_check_tan__with_UInt128_using_max_and_min = r""" +round(tan(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(tan(toUInt128(\'0\')), 7) +4.375905 0 +""" + +I_check_tan__with_UInt256_using_max_and_min = r""" +round(tan(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(tan(toUInt256(\'0\')), 7) +-0.4794209 0 +""" + +I_check_asin__with_Int128_using_max_and_min = r""" +round(asin(toInt128(\'170141183460469231731687303715884105727\')), 7) round(asin(toInt128(\'-170141183460469231731687303715884105728\')), 7) +nan nan +""" + +I_check_asin__with_Int256_using_max_and_min = r""" +round(asin(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(asin(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +nan nan +""" + +I_check_asin__with_UInt128_using_max_and_min = r""" +round(asin(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(asin(toUInt128(\'0\')), 7) +nan 0 +""" + +I_check_asin__with_UInt256_using_max_and_min = r""" +round(asin(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(asin(toUInt256(\'0\')), 7) +nan 0 +""" + +I_check_acos__with_Int128_using_max_and_min = r""" +round(acos(toInt128(\'170141183460469231731687303715884105727\')), 7) round(acos(toInt128(\'-170141183460469231731687303715884105728\')), 7) +nan nan +""" + +I_check_acos__with_Int256_using_max_and_min = r""" +round(acos(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(acos(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +nan nan +""" + +I_check_acos__with_UInt128_using_max_and_min = r""" +round(acos(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(acos(toUInt128(\'0\')), 7) +nan 1.5707963 +""" + +I_check_acos__with_UInt256_using_max_and_min = r""" +round(acos(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(acos(toUInt256(\'0\')), 7) +nan 1.5707963 +""" + +I_check_atan__with_Int128_using_max_and_min = r""" +round(atan(toInt128(\'170141183460469231731687303715884105727\')), 7) round(atan(toInt128(\'-170141183460469231731687303715884105728\')), 7) +1.5707963 -1.5707963 +""" + +I_check_atan__with_Int256_using_max_and_min = r""" +round(atan(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(atan(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +1.5707963 -1.5707963 +""" + +I_check_atan__with_UInt128_using_max_and_min = r""" +round(atan(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(atan(toUInt128(\'0\')), 7) +1.5707963 0 +""" + +I_check_atan__with_UInt256_using_max_and_min = r""" +round(atan(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(atan(toUInt256(\'0\')), 7) +1.5707963 0 +""" + +I_check_cosh__with_Int128_using_max_and_min = r""" +round(cosh(toInt128(\'170141183460469231731687303715884105727\')), 7) round(cosh(toInt128(\'-170141183460469231731687303715884105728\')), 7) +inf inf +""" + +I_check_cosh__with_Int256_using_max_and_min = r""" +round(cosh(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(cosh(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +inf inf +""" + +I_check_cosh__with_UInt128_using_max_and_min = r""" +round(cosh(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(cosh(toUInt128(\'0\')), 7) +inf 1 +""" + +I_check_cosh__with_UInt256_using_max_and_min = r""" +round(cosh(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(cosh(toUInt256(\'0\')), 7) +inf 1 +""" + +I_check_acosh__with_Int128_using_max_and_min = r""" +round(acosh(toInt128(\'170141183460469231731687303715884105727\')), 7) round(acosh(toInt128(\'-170141183460469231731687303715884105728\')), 7) +88.7228391 nan +""" + +I_check_acosh__with_Int256_using_max_and_min = r""" +round(acosh(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(acosh(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +177.4456782 nan +""" + +I_check_acosh__with_UInt128_using_max_and_min = r""" +round(acosh(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(acosh(toUInt128(\'0\')), 7) +89.4159863 nan +""" + +I_check_acosh__with_UInt256_using_max_and_min = r""" +round(acosh(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(acosh(toUInt256(\'0\')), 7) +178.1388254 nan +""" + +I_check_sinh__with_Int128_using_max_and_min = r""" +round(sinh(toInt128(\'170141183460469231731687303715884105727\')), 7) round(sinh(toInt128(\'-170141183460469231731687303715884105728\')), 7) +inf -inf +""" + +I_check_sinh__with_Int256_using_max_and_min = r""" +round(sinh(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(sinh(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +inf -inf +""" + +I_check_sinh__with_UInt128_using_max_and_min = r""" +round(sinh(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(sinh(toUInt128(\'0\')), 7) +inf 0 +""" + +I_check_sinh__with_UInt256_using_max_and_min = r""" +round(sinh(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(sinh(toUInt256(\'0\')), 7) +inf 0 +""" + +I_check_asinh__with_Int128_using_max_and_min = r""" +round(asinh(toInt128(\'170141183460469231731687303715884105727\')), 7) round(asinh(toInt128(\'-170141183460469231731687303715884105728\')), 7) +88.7228391 -88.7228391 +""" + +I_check_asinh__with_Int256_using_max_and_min = r""" +round(asinh(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(asinh(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +177.4456782 -177.4456782 +""" + +I_check_asinh__with_UInt128_using_max_and_min = r""" +round(asinh(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(asinh(toUInt128(\'0\')), 7) +89.4159863 0 +""" + +I_check_asinh__with_UInt256_using_max_and_min = r""" +round(asinh(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(asinh(toUInt256(\'0\')), 7) +178.1388254 0 +""" + +I_check_tanh__with_Int128_using_max_and_min = r""" +round(tanh(toInt128(\'170141183460469231731687303715884105727\')), 7) round(tanh(toInt128(\'-170141183460469231731687303715884105728\')), 7) +1 -1 +""" + +I_check_tanh__with_Int256_using_max_and_min = r""" +round(tanh(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(tanh(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +1 -1 +""" + +I_check_tanh__with_UInt128_using_max_and_min = r""" +round(tanh(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(tanh(toUInt128(\'0\')), 7) +1 0 +""" + +I_check_tanh__with_UInt256_using_max_and_min = r""" +round(tanh(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(tanh(toUInt256(\'0\')), 7) +1 0 +""" + +I_check_atanh__with_Int128_using_max_and_min = r""" +round(atanh(toInt128(\'170141183460469231731687303715884105727\')), 7) round(atanh(toInt128(\'-170141183460469231731687303715884105728\')), 7) +nan nan +""" + +I_check_atanh__with_Int256_using_max_and_min = r""" +round(atanh(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(atanh(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +nan nan +""" + +I_check_atanh__with_UInt128_using_max_and_min = r""" +round(atanh(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(atanh(toUInt128(\'0\')), 7) +nan 0 +""" + +I_check_atanh__with_UInt256_using_max_and_min = r""" +round(atanh(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(atanh(toUInt256(\'0\')), 7) +nan 0 +""" + +I_check_log1p__with_Int128_using_max_and_min = r""" +round(log1p(toInt128(\'170141183460469231731687303715884105727\')), 7) round(log1p(toInt128(\'-170141183460469231731687303715884105728\')), 7) +88.0296919 nan +""" + +I_check_log1p__with_Int256_using_max_and_min = r""" +round(log1p(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(log1p(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +176.752531 nan +""" + +I_check_log1p__with_UInt128_using_max_and_min = r""" +round(log1p(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(log1p(toUInt128(\'0\')), 7) +88.7228391 0 +""" + +I_check_log1p__with_UInt256_using_max_and_min = r""" +round(log1p(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(log1p(toUInt256(\'0\')), 7) +177.4456782 0 +""" + +I_check_sign__with_Int128_using_max_and_min = r""" +round(sign(toInt128(\'170141183460469231731687303715884105727\')), 7) round(sign(toInt128(\'-170141183460469231731687303715884105728\')), 7) +0 0 +""" + +I_check_sign__with_Int256_using_max_and_min = r""" +round(sign(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')), 7) round(sign(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')), 7) +0 0 +""" + +I_check_sign__with_UInt128_using_max_and_min = r""" +round(sign(toUInt128(\'340282366920938463463374607431768211455\')), 7) round(sign(toUInt128(\'0\')), 7) +0 0 +""" + +I_check_sign__with_UInt256_using_max_and_min = r""" +round(sign(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')), 7) round(sign(toUInt256(\'0\')), 7) +0 0 +""" + +I_check_the_outputs_of_exp__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_exp__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_exp__with_UInt128 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_exp__with_UInt256 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_log__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_log__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_log__with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_log__with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_ln__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_ln__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_ln__with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_ln__with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_exp2__with_Int128 = r""" +a +0 +0 +2 +""" + +I_check_the_outputs_of_exp2__with_Int256 = r""" +a +0 +0 +2 +""" + +I_check_the_outputs_of_exp2__with_UInt128 = r""" +a +0 +1 +2 +""" + +I_check_the_outputs_of_exp2__with_UInt256 = r""" +a +0 +1 +2 +""" + +I_check_the_outputs_of_log2__with_Int128 = r""" +a +0 +0 +127 +""" + +I_check_the_outputs_of_log2__with_Int256 = r""" +a +0 +0 +255 +""" + +I_check_the_outputs_of_log2__with_UInt128 = r""" +a +0 +0 +128 +""" + +I_check_the_outputs_of_log2__with_UInt256 = r""" +a +0 +0 +256 +""" + +I_check_the_outputs_of_exp10__with_Int128 = r""" +a +0 +0 +10 +""" + +I_check_the_outputs_of_exp10__with_Int256 = r""" +a +0 +0 +10 +""" + +I_check_the_outputs_of_exp10__with_UInt128 = r""" +a +0 +1 +10 +""" + +I_check_the_outputs_of_exp10__with_UInt256 = r""" +a +0 +1 +10 +""" + +I_check_the_outputs_of_log10__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_log10__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_log10__with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_log10__with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_sqrt__with_Int128 = r""" +a +0 +1 +13043817825332783000 +""" + +I_check_the_outputs_of_sqrt__with_Int256 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_sqrt__with_UInt128 = r""" +a +0 +1 +18446744073709552000 +""" + +I_check_the_outputs_of_sqrt__with_UInt256 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_cbrt__with_Int128 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_cbrt__with_Int256 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_cbrt__with_UInt128 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_cbrt__with_UInt256 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_erf__with_Int128 = r""" +a +-1 +0 +1 +""" + +I_check_the_outputs_of_erf__with_Int256 = r""" +a +-1 +0 +1 +""" + +I_check_the_outputs_of_erf__with_UInt128 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_erf__with_UInt256 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_erfc__with_Int128 = r""" +a +0 +0 +2 +""" + +I_check_the_outputs_of_erfc__with_Int256 = r""" +a +0 +0 +2 +""" + +I_check_the_outputs_of_erfc__with_UInt128 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_erfc__with_UInt256 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_lgamma__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_lgamma__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_lgamma__with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_lgamma__with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_tgamma__with_Int128 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_tgamma__with_Int256 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_tgamma__with_UInt128 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_tgamma__with_UInt256 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_sin__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_sin__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_sin__with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_sin__with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_cos__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_cos__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_cos__with_UInt128 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_cos__with_UInt256 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_tan__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_tan__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_tan__with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_tan__with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_asin__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_asin__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_asin__with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_asin__with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_acos__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_acos__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_acos__with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_acos__with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_atan__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_atan__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_atan__with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_atan__with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_cosh__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_cosh__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_cosh__with_UInt128 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_cosh__with_UInt256 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_acosh__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_acosh__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_acosh__with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_acosh__with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_sinh__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_sinh__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_sinh__with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_sinh__with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_asinh__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_asinh__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_asinh__with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_asinh__with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_tanh__with_Int128 = r""" +a +-1 +0 +1 +""" + +I_check_the_outputs_of_tanh__with_Int256 = r""" +a +-1 +0 +1 +""" + +I_check_the_outputs_of_tanh__with_UInt128 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_tanh__with_UInt256 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_atanh__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_atanh__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_atanh__with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_atanh__with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_log1p__with_Int128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_log1p__with_Int256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_log1p__with_UInt128 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_log1p__with_UInt256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_sign__with_Int128 = r""" +a +-1 +1 +1 +""" + +I_check_the_outputs_of_sign__with_Int256 = r""" +a +-1 +1 +1 +""" + +I_check_the_outputs_of_sign__with_UInt128 = r""" +a +0 +1 +1 +""" + +I_check_the_outputs_of_sign__with_UInt256 = r""" +a +0 +1 +1 +""" + +I_check_exp__with_Decimal256_using_max_and_min = r""" +round(exp(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(exp(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +inf 0 +""" + +I_check_log__with_Decimal256_using_max_and_min = r""" +round(log(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(log(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +172.693882 nan +""" + +I_check_ln__with_Decimal256_using_max_and_min = r""" +round(log(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(log(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +172.693882 nan +""" + +I_check_exp2__with_Decimal256_using_max_and_min = r""" +round(exp2(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(exp2(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +inf 0 +""" + +I_check_log2__with_Decimal256_using_max_and_min = r""" +round(log2(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(log2(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +249.1446071 nan +""" + +I_check_exp10__with_Decimal256_using_max_and_min = r""" +round(exp10(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(exp10(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +inf 0 +""" + +I_check_log10__with_Decimal256_using_max_and_min = r""" +round(log10(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(log10(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +75 nan +""" + +I_check_sqrt__with_Decimal256_using_max_and_min = r""" +round(sqrt(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(sqrt(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +3.1622776601683794e37 nan +""" + +I_check_cbrt__with_Decimal256_using_max_and_min = r""" +round(cbrt(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(cbrt(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +1e25 -1e25 +""" + +I_check_erf__with_Decimal256_using_max_and_min = r""" +round(erf(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(erf(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +1 -1 +""" + +I_check_erfc__with_Decimal256_using_max_and_min = r""" +round(erfc(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(erfc(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +0 2 +""" + +I_check_lgamma__with_Decimal256_using_max_and_min = r""" +round(lgamma(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(lgamma(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +1.7169388197455342e77 -1.7169388197455342e77 +""" + +I_check_tgamma__with_Decimal256_using_max_and_min = r""" +round(tgamma(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(tgamma(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +inf nan +""" + +I_check_sin__with_Decimal256_using_max_and_min = r""" +round(sin(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(sin(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +0.6633998 -0.6633998 +""" + +I_check_cos__with_Decimal256_using_max_and_min = r""" +round(cos(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(cos(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +-0.7482652 -0.7482652 +""" + +I_check_tan__with_Decimal256_using_max_and_min = r""" +round(tan(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(tan(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +-0.8865838 0.8865838 +""" + +I_check_asin__with_Decimal256_using_max_and_min = r""" +round(asin(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(asin(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +nan nan +""" + +I_check_acos__with_Decimal256_using_max_and_min = r""" +round(acos(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(acos(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +nan nan +""" + +I_check_atan__with_Decimal256_using_max_and_min = r""" +round(atan(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(atan(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +1.5707963 -1.5707963 +""" + +I_check_cosh__with_Decimal256_using_max_and_min = r""" +round(cosh(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(cosh(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +inf inf +""" + +I_check_acosh__with_Decimal256_using_max_and_min = r""" +round(acosh(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(acosh(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +173.3870292 nan +""" + +I_check_sinh__with_Decimal256_using_max_and_min = r""" +round(sinh(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(sinh(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +inf -inf +""" + +I_check_asinh__with_Decimal256_using_max_and_min = r""" +round(asinh(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(asinh(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +173.3870292 -173.3870292 +""" + +I_check_tanh__with_Decimal256_using_max_and_min = r""" +round(tanh(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(tanh(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +1 -1 +""" + +I_check_atanh__with_Decimal256_using_max_and_min = r""" +round(atanh(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(atanh(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +nan nan +""" + +I_check_log1p__with_Decimal256_using_max_and_min = r""" +round(log1p(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(log1p(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +172.693882 nan +""" + +I_check_sign__with_Decimal256_using_max_and_min = r""" +round(sign(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) round(sign(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)), 7) +0 0 +""" + +I_check_the_outputs_of_exp__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_log__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_ln__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_exp2__with_Decimal256 = r""" +a +0 +0 +2 +""" + +I_check_the_outputs_of_log2__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_exp10__with_Decimal256 = r""" +a +0 +0 +10 +""" + +I_check_the_outputs_of_log10__with_Decimal256 = r""" +a +0 +0 +75 +""" + +I_check_the_outputs_of_sqrt__with_Decimal256 = r""" +a +0 +1 +31622776601683794000000000000000000000 +""" + +I_check_the_outputs_of_cbrt__with_Decimal256 = r""" +a +-10000000000000000000000000 +1 +10000000000000000000000000 +""" + +I_check_the_outputs_of_erf__with_Decimal256 = r""" +a +-1 +0 +1 +""" + +I_check_the_outputs_of_erfc__with_Decimal256 = r""" +a +0 +0 +2 +""" + +I_check_the_outputs_of_lgamma__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_tgamma__with_Decimal256 = r""" +a +0 +0 +1 +""" + +I_check_the_outputs_of_sin__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_cos__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_tan__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_asin__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_acos__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_atan__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_cosh__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_acosh__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_sinh__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_asinh__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_tanh__with_Decimal256 = r""" +a +-1 +0 +1 +""" + +I_check_the_outputs_of_atanh__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_log1p__with_Decimal256 = r""" +a +0 +0 +0 +""" + +I_check_the_outputs_of_sign__with_Decimal256 = r""" +a +-1 +1 +1 +""" + +I_check_ceil_with_Int128_using_min_and_max_values = r""" +ceil(toInt128(\'-170141183460469231731687303715884105728\')) ceil(toInt128(\'170141183460469231731687303715884105727\')) +-170141183460469231731687303715884105728 170141183460469231731687303715884105727 +""" + +I_check_ceil_with_Int256_using_min_and_max_values = r""" +ceil(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) ceil(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) +-57896044618658097711785492504343953926634992332820282019728792003956564819968 57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_ceil_with_UInt128_using_min_and_max_values = r""" +ceil(toUInt128(\'0\')) ceil(toUInt128(\'340282366920938463463374607431768211455\')) +0 340282366920938463463374607431768211455 +""" + +I_check_ceil_with_UInt256_using_min_and_max_values = r""" +ceil(toUInt256(\'0\')) ceil(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) +0 115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_floor_with_Int128_using_min_and_max_values = r""" +floor(toInt128(\'-170141183460469231731687303715884105728\')) floor(toInt128(\'170141183460469231731687303715884105727\')) +-170141183460469231731687303715884105728 170141183460469231731687303715884105727 +""" + +I_check_floor_with_Int256_using_min_and_max_values = r""" +floor(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) floor(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) +-57896044618658097711785492504343953926634992332820282019728792003956564819968 57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_floor_with_UInt128_using_min_and_max_values = r""" +floor(toUInt128(\'0\')) floor(toUInt128(\'340282366920938463463374607431768211455\')) +0 340282366920938463463374607431768211455 +""" + +I_check_floor_with_UInt256_using_min_and_max_values = r""" +floor(toUInt256(\'0\')) floor(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) +0 115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_trunc_with_Int128_using_min_and_max_values = r""" +trunc(toInt128(\'-170141183460469231731687303715884105728\')) trunc(toInt128(\'170141183460469231731687303715884105727\')) +-170141183460469231731687303715884105728 170141183460469231731687303715884105727 +""" + +I_check_trunc_with_Int256_using_min_and_max_values = r""" +trunc(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) trunc(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) +-57896044618658097711785492504343953926634992332820282019728792003956564819968 57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_trunc_with_UInt128_using_min_and_max_values = r""" +trunc(toUInt128(\'0\')) trunc(toUInt128(\'340282366920938463463374607431768211455\')) +0 340282366920938463463374607431768211455 +""" + +I_check_trunc_with_UInt256_using_min_and_max_values = r""" +trunc(toUInt256(\'0\')) trunc(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) +0 115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_round_with_Int128_using_min_and_max_values = r""" +round(toInt128(\'-170141183460469231731687303715884105728\')) round(toInt128(\'170141183460469231731687303715884105727\')) +-170141183460469231731687303715884105728 170141183460469231731687303715884105727 +""" + +I_check_round_with_Int256_using_min_and_max_values = r""" +round(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) round(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) +-57896044618658097711785492504343953926634992332820282019728792003956564819968 57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_round_with_UInt128_using_min_and_max_values = r""" +round(toUInt128(\'0\')) round(toUInt128(\'340282366920938463463374607431768211455\')) +0 340282366920938463463374607431768211455 +""" + +I_check_round_with_UInt256_using_min_and_max_values = r""" +round(toUInt256(\'0\')) round(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) +0 115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_roundBankers_with_Int128_using_min_and_max_values = r""" +roundBankers(toInt128(\'-170141183460469231731687303715884105728\')) roundBankers(toInt128(\'170141183460469231731687303715884105727\')) +-170141183460469231731687303715884105728 170141183460469231731687303715884105727 +""" + +I_check_roundBankers_with_Int256_using_min_and_max_values = r""" +roundBankers(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) roundBankers(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) +-57896044618658097711785492504343953926634992332820282019728792003956564819968 57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_roundBankers_with_UInt128_using_min_and_max_values = r""" +roundBankers(toUInt128(\'0\')) roundBankers(toUInt128(\'340282366920938463463374607431768211455\')) +0 340282366920938463463374607431768211455 +""" + +I_check_roundBankers_with_UInt256_using_min_and_max_values = r""" +roundBankers(toUInt256(\'0\')) roundBankers(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) +0 115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_roundDuration_with_Int128_using_min_and_max_values = r""" +roundDuration(toInt128(\'-170141183460469231731687303715884105728\')) roundDuration(toInt128(\'170141183460469231731687303715884105727\')) +0 36000 +""" + +I_check_roundDuration_with_Int256_using_min_and_max_values = r""" +roundDuration(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) roundDuration(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) +0 36000 +""" + +I_check_roundDuration_with_UInt128_using_min_and_max_values = r""" +roundDuration(toUInt128(\'0\')) roundDuration(toUInt128(\'340282366920938463463374607431768211455\')) +0 36000 +""" + +I_check_roundDuration_with_UInt256_using_min_and_max_values = r""" +roundDuration(toUInt256(\'0\')) roundDuration(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) +0 36000 +""" + +I_check_roundAge_with_Int128_using_min_and_max_values = r""" +roundAge(toInt128(\'-170141183460469231731687303715884105728\')) roundAge(toInt128(\'170141183460469231731687303715884105727\')) +0 55 +""" + +I_check_roundAge_with_Int256_using_min_and_max_values = r""" +roundAge(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) roundAge(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) +0 55 +""" + +I_check_roundAge_with_UInt128_using_min_and_max_values = r""" +roundAge(toUInt128(\'0\')) roundAge(toUInt128(\'340282366920938463463374607431768211455\')) +0 55 +""" + +I_check_roundAge_with_UInt256_using_min_and_max_values = r""" +roundAge(toUInt256(\'0\')) roundAge(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) +0 55 +""" + +I_select_the_output_of_ceil_with_Int128_from_the_table = r""" +a +-170141183460469231731687303715884105728 +1 +170141183460469231731687303715884105727 +""" + +I_select_the_output_of_ceil_with_Int256_from_the_table = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +1 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_select_the_output_of_ceil_with_UInt128_from_the_table = r""" +a +0 +1 +340282366920938463463374607431768211455 +""" + +I_select_the_output_of_ceil_with_UInt256_from_the_table = r""" +a +0 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_select_the_output_of_floor_with_Int128_from_the_table = r""" +a +-170141183460469231731687303715884105728 +1 +170141183460469231731687303715884105727 +""" + +I_select_the_output_of_floor_with_Int256_from_the_table = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +1 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_select_the_output_of_floor_with_UInt128_from_the_table = r""" +a +0 +1 +340282366920938463463374607431768211455 +""" + +I_select_the_output_of_floor_with_UInt256_from_the_table = r""" +a +0 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_select_the_output_of_trunc_with_Int128_from_the_table = r""" +a +-170141183460469231731687303715884105728 +1 +170141183460469231731687303715884105727 +""" + +I_select_the_output_of_trunc_with_Int256_from_the_table = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +1 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_select_the_output_of_trunc_with_UInt128_from_the_table = r""" +a +0 +1 +340282366920938463463374607431768211455 +""" + +I_select_the_output_of_trunc_with_UInt256_from_the_table = r""" +a +0 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_select_the_output_of_round_with_Int128_from_the_table = r""" +a +-170141183460469231731687303715884105728 +1 +170141183460469231731687303715884105727 +""" + +I_select_the_output_of_round_with_Int256_from_the_table = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +1 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_select_the_output_of_round_with_UInt128_from_the_table = r""" +a +0 +1 +340282366920938463463374607431768211455 +""" + +I_select_the_output_of_round_with_UInt256_from_the_table = r""" +a +0 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_select_the_output_of_roundBankers_with_Int128_from_the_table = r""" +a +-170141183460469231731687303715884105728 +1 +170141183460469231731687303715884105727 +""" + +I_select_the_output_of_roundBankers_with_Int256_from_the_table = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +1 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_select_the_output_of_roundBankers_with_UInt128_from_the_table = r""" +a +0 +1 +340282366920938463463374607431768211455 +""" + +I_select_the_output_of_roundBankers_with_UInt256_from_the_table = r""" +a +0 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_select_the_output_of_roundDuration_with_Int128_from_the_table = r""" +a +0 +1 +36000 +""" + +I_select_the_output_of_roundDuration_with_Int256_from_the_table = r""" +a +0 +1 +36000 +""" + +I_select_the_output_of_roundDuration_with_UInt128_from_the_table = r""" +a +0 +1 +36000 +""" + +I_select_the_output_of_roundDuration_with_UInt256_from_the_table = r""" +a +0 +1 +36000 +""" + +I_select_the_output_of_roundAge_with_Int128_from_the_table = r""" +a +0 +17 +55 +""" + +I_select_the_output_of_roundAge_with_Int256_from_the_table = r""" +a +0 +17 +55 +""" + +I_select_the_output_of_roundAge_with_UInt128_from_the_table = r""" +a +0 +17 +55 +""" + +I_select_the_output_of_roundAge_with_UInt256_from_the_table = r""" +a +0 +17 +55 +""" + +I_check_ceil_with_Decimal256_using_min_and_max_values = r""" +ceil(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) ceil(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +-1000000000000000000000000000000000000000000000000000000000000000000000000000 1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_check_floor_with_Decimal256_using_min_and_max_values = r""" +floor(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) floor(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +-1000000000000000000000000000000000000000000000000000000000000000000000000000 1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_check_trunc_with_Decimal256_using_min_and_max_values = r""" +trunc(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) trunc(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +-1000000000000000000000000000000000000000000000000000000000000000000000000000 1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_check_round_with_Decimal256_using_min_and_max_values = r""" +round(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) round(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +-1000000000000000000000000000000000000000000000000000000000000000000000000000 1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_check_roundBankers_with_Decimal256_using_min_and_max_values = r""" +roundBankers(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) roundBankers(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +-1000000000000000000000000000000000000000000000000000000000000000000000000000 1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_select_the_output_of_ceil_with_Decimal256_from_the_table = r""" +a +-1000000000000000000000000000000000000000000000000000000000000000000000000000 +1 +1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_select_the_output_of_floor_with_Decimal256_from_the_table = r""" +a +-1000000000000000000000000000000000000000000000000000000000000000000000000000 +1 +1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_select_the_output_of_trunc_with_Decimal256_from_the_table = r""" +a +-1000000000000000000000000000000000000000000000000000000000000000000000000000 +1 +1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_select_the_output_of_round_with_Decimal256_from_the_table = r""" +a +-1000000000000000000000000000000000000000000000000000000000000000000000000000 +1 +1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_select_the_output_of_roundBankers_with_Decimal256_from_the_table = r""" +a +-1000000000000000000000000000000000000000000000000000000000000000000000000000 +1 +1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_check_bitAnd_with_Int128 = r""" +bitAnd(toInt128(1), 1) bitAnd(toInt128(\'170141183460469231731687303715884105727\'), 1) bitAnd(toInt128(\'-170141183460469231731687303715884105728\'), 1) +1 1 0 +""" + +I_check_bitAnd_with_Int256 = r""" +bitAnd(toInt256(1), 1) bitAnd(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), 1) bitAnd(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\'), 1) +1 1 0 +""" + +I_check_bitAnd_with_UInt128 = r""" +bitAnd(toUInt128(1), 1) bitAnd(toUInt128(\'340282366920938463463374607431768211455\'), 1) bitAnd(toUInt128(\'0\'), 1) +1 1 0 +""" + +I_check_bitAnd_with_UInt256 = r""" +bitAnd(toUInt256(1), 1) bitAnd(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), 1) bitAnd(toUInt256(\'0\'), 1) +1 1 0 +""" + +I_check_bitOr_with_Int128 = r""" +bitOr(toInt128(1), 1) bitOr(toInt128(\'170141183460469231731687303715884105727\'), 1) bitOr(toInt128(\'-170141183460469231731687303715884105728\'), 1) +1 170141183460469231731687303715884105727 -170141183460469231731687303715884105727 +""" + +I_check_bitOr_with_Int256 = r""" +bitOr(toInt256(1), 1) bitOr(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), 1) bitOr(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\'), 1) +1 57896044618658097711785492504343953926634992332820282019728792003956564819967 -57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_bitOr_with_UInt128 = r""" +bitOr(toUInt128(1), 1) bitOr(toUInt128(\'340282366920938463463374607431768211455\'), 1) bitOr(toUInt128(\'0\'), 1) +1 340282366920938463463374607431768211455 1 +""" + +I_check_bitOr_with_UInt256 = r""" +bitOr(toUInt256(1), 1) bitOr(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), 1) bitOr(toUInt256(\'0\'), 1) +1 115792089237316195423570985008687907853269984665640564039457584007913129639935 1 +""" + +I_check_bitXor_with_Int128 = r""" +bitXor(toInt128(1), 1) bitXor(toInt128(\'170141183460469231731687303715884105727\'), 1) bitXor(toInt128(\'-170141183460469231731687303715884105728\'), 1) +0 170141183460469231731687303715884105726 -170141183460469231731687303715884105727 +""" + +I_check_bitXor_with_Int256 = r""" +bitXor(toInt256(1), 1) bitXor(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), 1) bitXor(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\'), 1) +0 57896044618658097711785492504343953926634992332820282019728792003956564819966 -57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_bitXor_with_UInt128 = r""" +bitXor(toUInt128(1), 1) bitXor(toUInt128(\'340282366920938463463374607431768211455\'), 1) bitXor(toUInt128(\'0\'), 1) +0 340282366920938463463374607431768211454 1 +""" + +I_check_bitXor_with_UInt256 = r""" +bitXor(toUInt256(1), 1) bitXor(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), 1) bitXor(toUInt256(\'0\'), 1) +0 115792089237316195423570985008687907853269984665640564039457584007913129639934 1 +""" + +I_check_bitShiftLeft_with_Int128 = r""" +bitShiftLeft(toInt128(1), 1) bitShiftLeft(toInt128(\'170141183460469231731687303715884105727\'), 1) bitShiftLeft(toInt128(\'-170141183460469231731687303715884105728\'), 1) +2 -2 0 +""" + +I_check_bitShiftLeft_with_Int256 = r""" +bitShiftLeft(toInt256(1), 1) bitShiftLeft(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), 1) bitShiftLeft(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\'), 1) +2 -2 0 +""" + +I_check_bitShiftLeft_with_UInt128 = r""" +bitShiftLeft(toUInt128(1), 1) bitShiftLeft(toUInt128(\'340282366920938463463374607431768211455\'), 1) bitShiftLeft(toUInt128(\'0\'), 1) +2 340282366920938463463374607431768211454 0 +""" + +I_check_bitShiftLeft_with_UInt256 = r""" +bitShiftLeft(toUInt256(1), 1) bitShiftLeft(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), 1) bitShiftLeft(toUInt256(\'0\'), 1) +2 115792089237316195423570985008687907853269984665640564039457584007913129639934 0 +""" + +I_check_bitShiftRight_with_Int128 = r""" +bitShiftRight(toInt128(1), 1) bitShiftRight(toInt128(\'170141183460469231731687303715884105727\'), 1) bitShiftRight(toInt128(\'-170141183460469231731687303715884105728\'), 1) +0 85070591730234615865843651857942052863 -85070591730234615865843651857942052864 +""" + +I_check_bitShiftRight_with_Int256 = r""" +bitShiftRight(toInt256(1), 1) bitShiftRight(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\'), 1) bitShiftRight(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\'), 1) +0 28948022309329048855892746252171976963317496166410141009864396001978282409983 -28948022309329048855892746252171976963317496166410141009864396001978282409984 +""" + +I_check_bitShiftRight_with_UInt128 = r""" +bitShiftRight(toUInt128(1), 1) bitShiftRight(toUInt128(\'340282366920938463463374607431768211455\'), 1) bitShiftRight(toUInt128(\'0\'), 1) +0 170141183460469231731687303715884105727 0 +""" + +I_check_bitShiftRight_with_UInt256 = r""" +bitShiftRight(toUInt256(1), 1) bitShiftRight(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\'), 1) bitShiftRight(toUInt256(\'0\'), 1) +0 57896044618658097711785492504343953926634992332820282019728792003956564819967 0 +""" + +Check_bitNot_with_Int128 = r""" +bitNot(toInt128(1)) bitNot(toInt128(\'170141183460469231731687303715884105727\')) bitNot(toInt128(\'-170141183460469231731687303715884105728\')) +-2 -170141183460469231731687303715884105728 170141183460469231731687303715884105727 +""" + +Check_bitNot_with_Int256 = r""" +bitNot(toInt256(1)) bitNot(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) bitNot(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) +-2 -57896044618658097711785492504343953926634992332820282019728792003956564819968 57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +Check_bitNot_with_UInt128 = r""" +bitNot(toUInt128(1)) bitNot(toUInt128(\'340282366920938463463374607431768211455\')) bitNot(toUInt128(\'0\')) +340282366920938463463374607431768211454 0 340282366920938463463374607431768211455 +""" + +Check_bitNot_with_UInt256 = r""" +bitNot(toUInt256(1)) bitNot(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) bitNot(toUInt256(\'0\')) +115792089237316195423570985008687907853269984665640564039457584007913129639934 0 115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +Check_bitCount_with_Int128 = r""" +bitCount(toInt128(1)) bitCount(toInt128(\'170141183460469231731687303715884105727\')) bitCount(toInt128(\'-170141183460469231731687303715884105728\')) +1 64 0 +""" + +Check_bitCount_with_Int256 = r""" +bitCount(toInt256(1)) bitCount(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) bitCount(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) +1 64 0 +""" + +Check_bitCount_with_UInt128 = r""" +bitCount(toUInt128(1)) bitCount(toUInt128(\'340282366920938463463374607431768211455\')) bitCount(toUInt128(\'0\')) +1 64 0 +""" + +Check_bitCount_with_UInt256 = r""" +bitCount(toUInt256(1)) bitCount(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) bitCount(toUInt256(\'0\')) +1 64 0 +""" + +I_check_the_table_with_values_of_bitAnd_and_Int128 = r""" +a +0 +1 +1 +""" + +I_check_the_table_with_values_of_bitAnd_and_Int256 = r""" +a +0 +1 +1 +""" + +I_check_the_table_with_values_of_bitAnd_and_UInt128 = r""" +a +0 +1 +1 +""" + +I_check_the_table_with_values_of_bitAnd_and_UInt256 = r""" +a +0 +1 +1 +""" + +I_check_the_table_with_values_of_bitOr_and_Int128 = r""" +a +-170141183460469231731687303715884105727 +1 +170141183460469231731687303715884105727 +""" + +I_check_the_table_with_values_of_bitOr_and_Int256 = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819967 +1 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_the_table_with_values_of_bitOr_and_UInt128 = r""" +a +1 +1 +340282366920938463463374607431768211455 +""" + +I_check_the_table_with_values_of_bitOr_and_UInt256 = r""" +a +1 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_the_table_with_values_of_bitXor_and_Int128 = r""" +a +-170141183460469231731687303715884105727 +0 +170141183460469231731687303715884105726 +""" + +I_check_the_table_with_values_of_bitXor_and_Int256 = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819967 +0 +57896044618658097711785492504343953926634992332820282019728792003956564819966 +""" + +I_check_the_table_with_values_of_bitXor_and_UInt128 = r""" +a +0 +1 +340282366920938463463374607431768211454 +""" + +I_check_the_table_with_values_of_bitXor_and_UInt256 = r""" +a +0 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639934 +""" + +I_check_the_table_with_values_of_bitShiftLeft_and_Int128 = r""" +a +-2 +0 +2 +""" + +I_check_the_table_with_values_of_bitShiftLeft_and_Int256 = r""" +a +-2 +0 +2 +""" + +I_check_the_table_with_values_of_bitShiftLeft_and_UInt128 = r""" +a +0 +2 +340282366920938463463374607431768211454 +""" + +I_check_the_table_with_values_of_bitShiftLeft_and_UInt256 = r""" +a +0 +2 +115792089237316195423570985008687907853269984665640564039457584007913129639934 +""" + +I_check_the_table_with_values_of_bitShiftRight_and_Int128 = r""" +a +-85070591730234615865843651857942052864 +0 +85070591730234615865843651857942052863 +""" + +I_check_the_table_with_values_of_bitShiftRight_and_Int256 = r""" +a +-28948022309329048855892746252171976963317496166410141009864396001978282409984 +0 +28948022309329048855892746252171976963317496166410141009864396001978282409983 +""" + +I_check_the_table_with_values_of_bitShiftRight_and_UInt128 = r""" +a +0 +0 +170141183460469231731687303715884105727 +""" + +I_check_the_table_with_values_of_bitShiftRight_and_UInt256 = r""" +a +0 +0 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_the_table_with_values_of_bitNot_and_Int128 = r""" +a +-170141183460469231731687303715884105728 +-2 +170141183460469231731687303715884105727 +""" + +I_check_the_table_with_values_of_bitNot_and_Int256 = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +-2 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_the_table_with_values_of_bitNot_and_UInt128 = r""" +a +0 +340282366920938463463374607431768211454 +340282366920938463463374607431768211455 +""" + +I_check_the_table_with_values_of_bitNot_and_UInt256 = r""" +a +0 +115792089237316195423570985008687907853269984665640564039457584007913129639934 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_the_table_with_values_of_bitCount_and_Int128 = r""" +a +0 +1 +64 +""" + +I_check_the_table_with_values_of_bitCount_and_Int256 = r""" +a +0 +1 +64 +""" + +I_check_the_table_with_values_of_bitCount_and_UInt128 = r""" +a +0 +1 +64 +""" + +I_check_the_table_with_values_of_bitCount_and_UInt256 = r""" +a +0 +1 +64 +""" + +I_check_isNull__with_Int128_using_min_and_max = r""" +isNull(toInt128(\'-170141183460469231731687303715884105728\')) isNull(toInt128(\'170141183460469231731687303715884105727\')) +0 0 +""" + +I_check_isNull__with_Int256_using_min_and_max = r""" +isNull(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) isNull(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) +0 0 +""" + +I_check_isNull__with_UInt128_using_min_and_max = r""" +isNull(toUInt128(\'0\')) isNull(toUInt128(\'340282366920938463463374607431768211455\')) +0 0 +""" + +I_check_isNull__with_UInt256_using_min_and_max = r""" +isNull(toUInt256(\'0\')) isNull(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) +0 0 +""" + +I_check_isNotNull__with_Int128_using_min_and_max = r""" +isNotNull(toInt128(\'-170141183460469231731687303715884105728\')) isNotNull(toInt128(\'170141183460469231731687303715884105727\')) +1 1 +""" + +I_check_isNotNull__with_Int256_using_min_and_max = r""" +isNotNull(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) isNotNull(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) +1 1 +""" + +I_check_isNotNull__with_UInt128_using_min_and_max = r""" +isNotNull(toUInt128(\'0\')) isNotNull(toUInt128(\'340282366920938463463374607431768211455\')) +1 1 +""" + +I_check_isNotNull__with_UInt256_using_min_and_max = r""" +isNotNull(toUInt256(\'0\')) isNotNull(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) +1 1 +""" + +I_check_coalesce__with_Int128_using_min_and_max = r""" +coalesce(toInt128(\'-170141183460469231731687303715884105728\')) coalesce(toInt128(\'170141183460469231731687303715884105727\')) +-170141183460469231731687303715884105728 170141183460469231731687303715884105727 +""" + +I_check_coalesce__with_Int256_using_min_and_max = r""" +coalesce(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) coalesce(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) +-57896044618658097711785492504343953926634992332820282019728792003956564819968 57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_coalesce__with_UInt128_using_min_and_max = r""" +coalesce(toUInt128(\'0\')) coalesce(toUInt128(\'340282366920938463463374607431768211455\')) +0 340282366920938463463374607431768211455 +""" + +I_check_coalesce__with_UInt256_using_min_and_max = r""" +coalesce(toUInt256(\'0\')) coalesce(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) +0 115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_assumeNotNull__with_Int128_using_min_and_max = r""" +assumeNotNull(toInt128(\'-170141183460469231731687303715884105728\')) assumeNotNull(toInt128(\'170141183460469231731687303715884105727\')) +-170141183460469231731687303715884105728 170141183460469231731687303715884105727 +""" + +I_check_assumeNotNull__with_Int256_using_min_and_max = r""" +assumeNotNull(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) assumeNotNull(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) +-57896044618658097711785492504343953926634992332820282019728792003956564819968 57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_assumeNotNull__with_UInt128_using_min_and_max = r""" +assumeNotNull(toUInt128(\'0\')) assumeNotNull(toUInt128(\'340282366920938463463374607431768211455\')) +0 340282366920938463463374607431768211455 +""" + +I_check_assumeNotNull__with_UInt256_using_min_and_max = r""" +assumeNotNull(toUInt256(\'0\')) assumeNotNull(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) +0 115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_toNullable__with_Int128_using_min_and_max = r""" +toNullable(toInt128(\'-170141183460469231731687303715884105728\')) toNullable(toInt128(\'170141183460469231731687303715884105727\')) +-170141183460469231731687303715884105728 170141183460469231731687303715884105727 +""" + +I_check_toNullable__with_Int256_using_min_and_max = r""" +toNullable(toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) toNullable(toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) +-57896044618658097711785492504343953926634992332820282019728792003956564819968 57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_toNullable__with_UInt128_using_min_and_max = r""" +toNullable(toUInt128(\'0\')) toNullable(toUInt128(\'340282366920938463463374607431768211455\')) +0 340282366920938463463374607431768211455 +""" + +I_check_toNullable__with_UInt256_using_min_and_max = r""" +toNullable(toUInt256(\'0\')) toNullable(toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) +0 115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_ifNull_1__with_Int128_using_min_and_max = r""" +ifNull(1, toInt128(\'-170141183460469231731687303715884105728\')) ifNull(1, toInt128(\'170141183460469231731687303715884105727\')) +1 1 +""" + +I_check_ifNull_1__with_Int256_using_min_and_max = r""" +ifNull(1, toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) ifNull(1, toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) +1 1 +""" + +I_check_ifNull_1__with_UInt128_using_min_and_max = r""" +ifNull(1, toUInt128(\'0\')) ifNull(1, toUInt128(\'340282366920938463463374607431768211455\')) +1 1 +""" + +I_check_ifNull_1__with_UInt256_using_min_and_max = r""" +ifNull(1, toUInt256(\'0\')) ifNull(1, toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) +1 1 +""" + +I_check_nullIf_1__with_Int128_using_min_and_max = r""" +nullIf(1, toInt128(\'-170141183460469231731687303715884105728\')) nullIf(1, toInt128(\'170141183460469231731687303715884105727\')) +1 1 +""" + +I_check_nullIf_1__with_Int256_using_min_and_max = r""" +nullIf(1, toInt256(\'-57896044618658097711785492504343953926634992332820282019728792003956564819968\')) nullIf(1, toInt256(\'57896044618658097711785492504343953926634992332820282019728792003956564819967\')) +1 1 +""" + +I_check_nullIf_1__with_UInt128_using_min_and_max = r""" +nullIf(1, toUInt128(\'0\')) nullIf(1, toUInt128(\'340282366920938463463374607431768211455\')) +1 1 +""" + +I_check_nullIf_1__with_UInt256_using_min_and_max = r""" +nullIf(1, toUInt256(\'0\')) nullIf(1, toUInt256(\'115792089237316195423570985008687907853269984665640564039457584007913129639935\')) +1 1 +""" + +I_check_isNull__with_Int128_on_the_table = r""" +a +0 +0 +0 +""" + +I_check_isNull__with_Int256_on_the_table = r""" +a +0 +0 +0 +""" + +I_check_isNull__with_UInt128_on_the_table = r""" +a +0 +0 +0 +""" + +I_check_isNull__with_UInt256_on_the_table = r""" +a +0 +0 +0 +""" + +I_check_isNotNull__with_Int128_on_the_table = r""" +a +1 +1 +1 +""" + +I_check_isNotNull__with_Int256_on_the_table = r""" +a +1 +1 +1 +""" + +I_check_isNotNull__with_UInt128_on_the_table = r""" +a +1 +1 +1 +""" + +I_check_isNotNull__with_UInt256_on_the_table = r""" +a +1 +1 +1 +""" + +I_check_coalesce__with_Int128_on_the_table = r""" +a +-170141183460469231731687303715884105728 +1 +170141183460469231731687303715884105727 +""" + +I_check_coalesce__with_Int256_on_the_table = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +1 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_coalesce__with_UInt128_on_the_table = r""" +a +0 +1 +340282366920938463463374607431768211455 +""" + +I_check_coalesce__with_UInt256_on_the_table = r""" +a +0 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_assumeNotNull__with_Int128_on_the_table = r""" +a +-170141183460469231731687303715884105728 +1 +170141183460469231731687303715884105727 +""" + +I_check_assumeNotNull__with_Int256_on_the_table = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +1 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_assumeNotNull__with_UInt128_on_the_table = r""" +a +0 +1 +340282366920938463463374607431768211455 +""" + +I_check_assumeNotNull__with_UInt256_on_the_table = r""" +a +0 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_toNullable__with_Int128_on_the_table = r""" +a +-170141183460469231731687303715884105728 +1 +170141183460469231731687303715884105727 +""" + +I_check_toNullable__with_Int256_on_the_table = r""" +a +-57896044618658097711785492504343953926634992332820282019728792003956564819968 +1 +57896044618658097711785492504343953926634992332820282019728792003956564819967 +""" + +I_check_toNullable__with_UInt128_on_the_table = r""" +a +0 +1 +340282366920938463463374607431768211455 +""" + +I_check_toNullable__with_UInt256_on_the_table = r""" +a +0 +1 +115792089237316195423570985008687907853269984665640564039457584007913129639935 +""" + +I_check_ifNull_1__with_Int128_on_the_table = r""" +a +1 +1 +1 +""" + +I_check_ifNull_1__with_Int256_on_the_table = r""" +a +1 +1 +1 +""" + +I_check_ifNull_1__with_UInt128_on_the_table = r""" +a +1 +1 +1 +""" + +I_check_ifNull_1__with_UInt256_on_the_table = r""" +a +1 +1 +1 +""" + +I_check_nullIf_1__with_Int128_on_the_table = r""" +a +1 +1 +\N +""" + +I_check_nullIf_1__with_Int256_on_the_table = r""" +a +1 +1 +\N +""" + +I_check_nullIf_1__with_UInt128_on_the_table = r""" +a +1 +1 +\N +""" + +I_check_nullIf_1__with_UInt256_on_the_table = r""" +a +1 +1 +\N +""" + +I_check_isNull__with_Decimal256_using_min_and_max = r""" +isNull(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) isNull(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +0 0 +""" + +I_check_isNotNull__with_Decimal256_using_min_and_max = r""" +isNotNull(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) isNotNull(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +1 1 +""" + +I_check_coalesce__with_Decimal256_using_min_and_max = r""" +coalesce(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) coalesce(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +-1000000000000000000000000000000000000000000000000000000000000000000000000000 1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_check_assumeNotNull__with_Decimal256_using_min_and_max = r""" +assumeNotNull(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) assumeNotNull(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +-1000000000000000000000000000000000000000000000000000000000000000000000000000 1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_check_toNullable__with_Decimal256_using_min_and_max = r""" +toNullable(toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) toNullable(toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +-1000000000000000000000000000000000000000000000000000000000000000000000000000 1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_check_ifNull_1__with_Decimal256_using_min_and_max = r""" +ifNull(1, toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) ifNull(1, toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +1 1 +""" + +I_check_nullIf_1__with_Decimal256_using_min_and_max = r""" +nullIf(1, toDecimal256(\'-1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) nullIf(1, toDecimal256(\'1000000000000000000000000000000000000000000000000000000000000000000000000000\', 0)) +1 1 +""" + +I_check_isNull__with_Decimal256_on_the_table = r""" +a +0 +0 +0 +""" + +I_check_isNotNull__with_Decimal256_on_the_table = r""" +a +1 +1 +1 +""" + +I_check_coalesce__with_Decimal256_on_the_table = r""" +a +-1000000000000000000000000000000000000000000000000000000000000000000000000000 +1 +1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_check_assumeNotNull__with_Decimal256_on_the_table = r""" +a +-1000000000000000000000000000000000000000000000000000000000000000000000000000 +1 +1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_check_toNullable__with_Decimal256_on_the_table = r""" +a +-1000000000000000000000000000000000000000000000000000000000000000000000000000 +1 +1000000000000000000000000000000000000000000000000000000000000000000000000000 +""" + +I_check_ifNull_1__with_Decimal256_on_the_table = r""" +a +1 +1 +1 +""" + +I_check_nullIf_1__with_Decimal256_on_the_table = r""" +a +1 +1 +\N +""" + diff --git a/tests/testflows/extended_precision_data_types/tests/arithmetic.py b/tests/testflows/extended_precision_data_types/tests/arithmetic.py index e949ef65f53c..5a0508db9e96 100644 --- a/tests/testflows/extended_precision_data_types/tests/arithmetic.py +++ b/tests/testflows/extended_precision_data_types/tests/arithmetic.py @@ -37,6 +37,9 @@ def inline_check(self, arithmetic_func, expected_result, int_type, min, max, nod if node is None: node = self.context.node + if arithmetic_func == "divide": + skip("divide is not supported for int types") + if arithmetic_func in ["negate", "abs"]: with When(f"I check {arithmetic_func} with {int_type}"): @@ -92,6 +95,9 @@ def table_check(self, arithmetic_func, expected_result, int_type, min, max, node table_name = f"table_{getuid()}" + if arithmetic_func == "divide": + skip("divide is not supported for int types") + with Given(f"I have a table"): table(name=table_name, data_type=int_type) diff --git a/tests/testflows/extended_precision_data_types/tests/array_tuple_map.py b/tests/testflows/extended_precision_data_types/tests/array_tuple_map.py index 106458d58bce..457c9caba3cc 100644 --- a/tests/testflows/extended_precision_data_types/tests/array_tuple_map.py +++ b/tests/testflows/extended_precision_data_types/tests/array_tuple_map.py @@ -2,6 +2,7 @@ from extended_precision_data_types.requirements import * from extended_precision_data_types.common import * +from helpers.common import check_clickhouse_version def get_table_name(): @@ -477,11 +478,16 @@ def map_func(self, data_type, node=None): ) exitcode, message = 0, None - if data_type.startswith("Decimal"): - exitcode, message = 44, "Exception:" + if data_type.startswith("Decimal") or check_clickhouse_version("<21.9")(self): + (exitcode, message) = ( + (44, "Exception:") + if check_clickhouse_version("<22.3")(self) + else (43, "Exception:") + ) node.query(sql, exitcode=exitcode, message=message) with Scenario(f"mapPopulateSeries with {data_type} on a table"): + table_name = get_table_name() table( @@ -496,10 +502,15 @@ def map_func(self, data_type, node=None): exitcode, message = 0, None if data_type.startswith("Decimal"): - exitcode, message = 44, "Exception:" + (exitcode, message) = ( + (44, "Exception:") + if check_clickhouse_version("<22.3")(self) + else (43, "Exception:") + ) node.query(sql, exitcode=exitcode, message=message) - execute_query(f"SELECT * FROM {table_name} ORDER BY a ASC") + if check_clickhouse_version(">=21.9")(self): + execute_query(f"SELECT * FROM {table_name} ORDER BY a ASC") with Scenario(f"mapContains with {data_type}"): node.query( diff --git a/tests/testflows/helpers/argparser.py b/tests/testflows/helpers/argparser.py index ec26b8f654bb..763d455a7a0f 100644 --- a/tests/testflows/helpers/argparser.py +++ b/tests/testflows/helpers/argparser.py @@ -7,7 +7,7 @@ def argparser(parser): "--local", action="store_true", help="run regression in local mode", - default=False, + default=True, ) parser.add_argument( diff --git a/tests/testflows/helpers/cluster.py b/tests/testflows/helpers/cluster.py index ae9f9d6623e4..138c0af0a385 100755 --- a/tests/testflows/helpers/cluster.py +++ b/tests/testflows/helpers/cluster.py @@ -14,6 +14,7 @@ from testflows.connect import Shell as ShellBase from testflows.uexpect import ExpectTimeoutError from testflows._core.testtype import TestSubType +from helpers.common import check_clickhouse_version, current_cpu MESSAGES_TO_RETRY = [ "DB::Exception: ZooKeeper session has been expired", @@ -72,7 +73,7 @@ def close_bashes(self): """Close all active bashes to the node.""" with self.cluster.lock: for key in list(self.cluster._bash.keys()): - if key.endswith(f"-{self.name}"): + if key[1].endswith(f"{self.name}"): shell = self.cluster._bash.pop(key) shell.__exit__(None, None, None) @@ -169,8 +170,8 @@ def cmd( class ClickHouseNode(Node): """Node with ClickHouse server.""" - def thread_fuzzer(self): - with Given("exporting THREAD_FUZZER"): + def enable_thread_fuzzer(self): + with Given("enabling THREAD_FUZZER"): self.command("export THREAD_FUZZER_CPU_TIME_PERIOD_US=1000") self.command("export THREAD_FUZZER_SLEEP_PROBABILITY=0.1") self.command("export THREAD_FUZZER_SLEEP_TIME_US=100000") @@ -230,7 +231,7 @@ def wait_clickhouse_healthy(self, timeout=300): if current().context.clickhouse_version is None: current().context.clickhouse_version = node_version else: - assert current().context.clickhouse_version == node_version, error() + assert check_clickhouse_version(f"={node_version}")(current()), error() def clickhouse_pid(self): """Return ClickHouse server pid if present @@ -283,7 +284,7 @@ def start_clickhouse( raise RuntimeError(f"ClickHouse server already running with pid {pid}") if thread_fuzzer: - self.thread_fuzzer() + self.enable_thread_fuzzer() if user is None: with By("starting ClickHouse server process"): @@ -521,6 +522,7 @@ def query( messages_to_retry=None, retry_delay=5, secure=False, + max_query_output_in_bytes="-0", *args, **kwargs, ): @@ -537,12 +539,16 @@ def query( which retry should be triggered, default: MESSAGES_TO_RETRY :param retry_delay: number of seconds to sleep before retry, default: 5 :param secure: use secure connection, default: False + :param max_query_output_in_bytes: truncate query output the specified number of bytes using 'head' command utility, default: -0 (do not truncate any output) """ retry_count = max(0, int(retry_count)) retry_delay = max(0, float(retry_delay)) settings = list(settings or []) query_settings = list(settings) + if raise_on_exception: + steps = False + if messages_to_retry is None: messages_to_retry = MESSAGES_TO_RETRY @@ -557,10 +563,14 @@ def query( with tempfile.NamedTemporaryFile("w", encoding="utf-8") as query: query.write(sql) query.flush() - command = f'cat "{query.name}" | {self.cluster.docker_compose} exec -T {self.name} {client}' + + client_options = "" for setting in query_settings: name, value = setting - command += f' --{name} "{value}"' + client_options += f' --{name} "{value}"' + + command = f'cat "{query.name}" | {self.cluster.docker_compose} exec -T {self.name} bash -c "(set -o pipefail && {client}{client_options} 2>&1 | head -c {max_query_output_in_bytes})"' + description = f""" echo -e \"{sql[:100]}...\" > {query.name} {command} @@ -576,10 +586,13 @@ def query( self.cluster.close_bash(None) raise else: - command = f'echo -e "{sql}" | {client}' + client_options = "" for setting in query_settings: name, value = setting - command += f' --{name} "{value}"' + client_options += f' --{name} "{value}"' + + command = f'(set -o pipefail && echo -e "{sql}" | {client}{client_options} 2>&1 | head -c {max_query_output_in_bytes})' + with step( "executing command", description=command, format_description=False ) if steps else NullStep(): @@ -644,6 +657,7 @@ def __init__( docker_compose_project_dir=None, docker_compose_file="docker-compose.yml", environ=None, + thread_fuzzer=False, ): self._bash = {} @@ -655,6 +669,8 @@ def __init__( self.local = local self.nodes = nodes or {} self.docker_compose = docker_compose + self.thread_fuzzer = thread_fuzzer + self.running = False frame = inspect.currentframe().f_back caller_dir = os.path.dirname(os.path.abspath(frame.f_globals["__file__"])) @@ -668,11 +684,20 @@ def __init__( if not os.path.exists(self.configs_dir): raise TypeError(f"configs directory '{self.configs_dir}' does not exist") - if docker_compose_project_dir is None: + if not docker_compose_project_dir: raise TypeError("docker compose directory must be specified.") + if current_cpu() == "aarch64": + if not docker_compose_project_dir.endswith("_arm64"): + docker_compose_project_dir += f"_arm64" + + if not os.path.exists(docker_compose_project_dir): + raise TypeError( + f"docker compose project directory '{docker_compose_project_dir}' does not exist" + ) + docker_compose_file_path = os.path.join( - docker_compose_project_dir or "", docker_compose_file + docker_compose_project_dir, docker_compose_file ) if not os.path.exists(docker_compose_file_path): @@ -680,25 +705,28 @@ def __init__( f"docker compose file '{docker_compose_file_path}' does not exist" ) - if self.clickhouse_binary_path and self.clickhouse_binary_path.startswith( - "docker://" - ): - if current().context.clickhouse_version is None: - try: - current().context.clickhouse_version = ( - self.clickhouse_binary_path.split(":")[2] - ) - debug( - f"auto setting clickhouse version to {current().context.clickhouse_version}" - ) - except IndexError: - current().context.clickhouse_version = None - ( - self.clickhouse_binary_path, - self.clickhouse_odbc_bridge_binary_path, - ) = self.get_clickhouse_binary_from_docker_container( - self.clickhouse_binary_path - ) + if self.clickhouse_binary_path: + if self.clickhouse_binary_path.startswith("docker://"): + if current().context.clickhouse_version is None: + parsed_version = "" + for c in self.clickhouse_binary_path.rsplit(":", 1)[-1]: + if c in ".0123456789": + parsed_version += c + else: + break + if parsed_version: + if not ( + parsed_version.startswith(".") + or parsed_version.endswith(".") + ): + current().context.clickhouse_version = parsed_version + + ( + self.clickhouse_binary_path, + self.clickhouse_odbc_bridge_binary_path, + ) = self.get_clickhouse_binary_from_docker_container( + self.clickhouse_binary_path + ) self.docker_compose += f' --ansi never --project-directory "{docker_compose_project_dir}" --file "{docker_compose_file_path}"' self.lock = threading.Lock() @@ -754,18 +782,21 @@ def control_shell(self, timeout=300): return self._control_shell time_start = time.time() + i = -1 while True: - try: - shell = Shell() - shell.timeout = 30 - shell("echo 1") - break - except IOError: - raise - except Exception as exc: - shell.__exit__(None, None, None) - if time.time() - time_start > timeout: - raise RuntimeError(f"failed to open control shell") + i += 1 + with By(f"attempt #{i}"): + try: + shell = Shell() + shell.timeout = 30 + shell("echo 1") + break + except IOError: + raise + except Exception as exc: + shell.__exit__(None, None, None) + if time.time() - time_start > timeout: + raise RuntimeError(f"failed to open control shell") self._control_shell = shell return self._control_shell @@ -781,23 +812,26 @@ def node_container_id(self, node, timeout=300): """Must be called with self.lock acquired.""" container_id = None time_start = time.time() + i = -1 while True: - try: - c = self.control_shell( - f"{self.docker_compose} ps -q {node}", timeout=timeout - ) - container_id = c.output.strip() - if c.exitcode == 0 and len(container_id) > 1: - break - except IOError: - raise - except ExpectTimeoutError: - self.close_control_shell() - timeout = timeout - (time.time() - time_start) - if timeout <= 0: - raise RuntimeError( - f"failed to get docker container id for the {node} service" + i += 1 + with By(f"attempt #{i}"): + try: + c = self.control_shell( + f"{self.docker_compose} ps -q {node}", timeout=timeout ) + container_id = c.output.strip() + if c.exitcode == 0 and len(container_id) > 1: + break + except IOError: + raise + except ExpectTimeoutError: + self.close_control_shell() + timeout = timeout - (time.time() - time_start) + if timeout <= 0: + raise RuntimeError( + f"failed to get docker container id for the {node} service" + ) return container_id def shell(self, node, timeout=300): @@ -809,29 +843,32 @@ def shell(self, node, timeout=300): container_id = self.node_container_id(node=node, timeout=timeout) time_start = time.time() + i = -1 while True: - try: - if node is None: - shell = Shell() - else: - shell = Shell( - command=[ - "/bin/bash", - "--noediting", - "-c", - f"docker exec -it {container_id} bash --noediting", - ], - name=node, - ) - shell.timeout = 30 - shell("echo 1") - break - except IOError: - raise - except Exception as exc: - shell.__exit__(None, None, None) - if time.time() - time_start > timeout: - raise RuntimeError(f"failed to open bash to node {node}") + i += 1 + with By(f"attempt #{i}"): + try: + if node is None: + shell = Shell() + else: + shell = Shell( + command=[ + "/bin/bash", + "--noediting", + "-c", + f"docker exec -it {container_id} bash --noediting", + ], + name=node, + ) + shell.timeout = 30 + shell("echo 1") + break + except IOError: + raise + except Exception as exc: + shell.__exit__(None, None, None) + if time.time() - time_start > timeout: + raise RuntimeError(f"failed to open bash to node {node}") shell.timeout = timeout return shell @@ -844,7 +881,7 @@ def bash(self, node, timeout=300, command="bash --noediting"): test = current() current_thread = threading.current_thread() - id = f"{current_thread.name}-{node}" + id = (current_thread.name, f"{node}") with self.lock: if self._bash.get(id) is None: @@ -852,29 +889,34 @@ def bash(self, node, timeout=300, command="bash --noediting"): container_id = self.node_container_id(node=node, timeout=timeout) time_start = time.time() + i = -1 while True: - try: - if node is None: - self._bash[id] = Shell() - else: - self._bash[id] = Shell( - command=[ - "/bin/bash", - "--noediting", - "-c", - f"docker exec -it {container_id} {command}", - ], - name=node, - ).__enter__() - self._bash[id].timeout = 30 - self._bash[id]("echo 1") - break - except IOError: - raise - except Exception as exc: - self._bash[id].__exit__(None, None, None) - if time.time() - time_start > timeout: - raise RuntimeError(f"failed to open bash to node {node}") + i += 1 + with By(f"attempt #{i}"): + try: + if node is None: + self._bash[id] = Shell() + else: + self._bash[id] = Shell( + command=[ + "/bin/bash", + "--noediting", + "-c", + f"docker exec -it {container_id} {command}", + ], + name=node, + ).__enter__() + self._bash[id].timeout = 30 + self._bash[id]("echo 1") + break + except IOError: + raise + except Exception as exc: + self._bash[id].__exit__(None, None, None) + if time.time() - time_start > timeout: + raise RuntimeError( + f"failed to open bash to node {node}" + ) if node is None: for name, value in self.environ.items(): @@ -886,7 +928,7 @@ def bash(self, node, timeout=300, command="bash --noediting"): active_thread_names = {thread.name for thread in threading.enumerate()} for bash_id in list(self._bash.keys()): - thread_name, node_name = bash_id.rsplit("-", 1) + thread_name, node_name = bash_id if thread_name not in active_thread_names: self._bash[bash_id].__exit__(None, None, None) del self._bash[bash_id] @@ -895,7 +937,7 @@ def bash(self, node, timeout=300, command="bash --noediting"): def close_bash(self, node): current_thread = threading.current_thread() - id = f"{current_thread.name}-{node}" + id = (current_thread.name, f"{node}") with self.lock: if self._bash.get(id) is None: @@ -929,7 +971,7 @@ def down(self, timeout=300): """Bring cluster down by executing docker-compose down.""" # add message to each clickhouse-server.log - if settings.debug: + if settings.debug and self.running: for node in self.nodes["clickhouse"]: self.command( node=node, @@ -970,6 +1012,7 @@ def temp_file(self, name): return f"{os.path.join(self.temp_path(), name)}" def up(self, timeout=30 * 60): + """Bring cluster up.""" if self.local: with Given("I am running in local mode"): with Then("check --clickhouse-binary-path is specified"): @@ -980,6 +1023,9 @@ def up(self, timeout=30 * 60): assert os.path.exists(self.clickhouse_binary_path) with And("I set all the necessary environment variables"): + self.environ["IMAGE_DEPENDENCY_PROXY"] = os.getenv( + "IMAGE_DEPENDENCY_PROXY", "" + ) self.environ["COMPOSE_HTTP_TIMEOUT"] = "300" self.environ[ "CLICKHOUSE_TESTS_SERVER_BIN_PATH" @@ -1064,7 +1110,9 @@ def up(self, timeout=30 * 60): for name in self.nodes["clickhouse"]: self.node(name).wait_healthy() if name.startswith("clickhouse"): - self.node(name).start_clickhouse() + self.node(name).start_clickhouse(thread_fuzzer=self.thread_fuzzer) + + self.running = True def command( self, diff --git a/tests/testflows/helpers/common.py b/tests/testflows/helpers/common.py index 2ba6aef11eec..358d71081b35 100644 --- a/tests/testflows/helpers/common.py +++ b/tests/testflows/helpers/common.py @@ -1,8 +1,8 @@ import os import uuid import time +import platform import xml.etree.ElementTree as xmltree -import packaging.version as pkg_version from collections import namedtuple import testflows.settings as settings @@ -12,6 +12,11 @@ from testflows._core.testtype import TestSubType +def current_cpu(): + """Return current cpu architecture.""" + return platform.processor() + + def check_clickhouse_version(version): """Compare ClickHouse version.""" @@ -19,34 +24,39 @@ def check(test): if getattr(test.context, "clickhouse_version", None) is None: return False - clickhouse_version = pkg_version.parse(str(test.context.clickhouse_version)) + version_list = version.translate({ord(i): None for i in "<>="}).split(".") + clickhouse_version_list = test.context.clickhouse_version.split(".") + + index = None + + for i in clickhouse_version_list: + if i.isnumeric(): + continue + index = clickhouse_version_list.index(i) + + index = ( + min(len(version_list), len(clickhouse_version_list)) + if index is None + else min(len(version_list), index) + ) + + version_list = [int(i) for i in version_list[0:index]] + clickhouse_version_list = [int(i) for i in clickhouse_version_list[0:index]] if version.startswith("=="): - return clickhouse_version == pkg_version.parse( - str(version.split("==", 1)[-1]) - ) + return clickhouse_version_list == version_list elif version.startswith(">="): - return clickhouse_version >= pkg_version.parse( - str(version.split(">=", 1)[-1]) - ) + return clickhouse_version_list >= version_list elif version.startswith("<="): - return clickhouse_version <= pkg_version.parse( - str(version.split("<=", 1)[-1]) - ) + return clickhouse_version_list <= version_list elif version.startswith("="): - return clickhouse_version == pkg_version.parse( - str(version.split("=", 1)[-1]) - ) + return clickhouse_version_list == version_list elif version.startswith(">"): - return clickhouse_version > pkg_version.parse( - str(version.split(">", 1)[-1]) - ) + return clickhouse_version_list > version_list elif version.startswith("<"): - return clickhouse_version < pkg_version.parse( - str(version.split("<", 1)[-1]) - ) + return clickhouse_version_list < version_list else: - return clickhouse_version == pkg_version.parse(str(version)) + return clickhouse_version_list == version_list return check @@ -186,7 +196,7 @@ def create_xml_config_content( uid = getuid() path = os.path.join(config_d_dir, config_file) name = config_file - root = xmltree.Element("clickhouse") + root = xmltree.Element("yandex") root.append(xmltree.Comment(text=f"config uid: {uid}")) def create_xml_tree(entries, root): @@ -389,15 +399,24 @@ def wait_for_config_to_be_loaded(user=None): with Then("I wait for config reload message in the log file"): if restart: - bash.expect( - f"ConfigReloader: Loaded config '/etc/clickhouse-server/config.xml', performed update on configuration", + choice = bash.expect( + ( + f"(ConfigReloader: Loaded config '/etc/clickhouse-server/config.xml', performed update on configuration)|" + f"(ConfigReloader: Error updating configuration from '/etc/clickhouse-server/config.xml')" + ), timeout=timeout, ) else: - bash.expect( - f"ConfigReloader: Loaded config '/etc/clickhouse-server/{config.preprocessed_name}', performed update on configuration", + choice = bash.expect( + ( + f"(ConfigReloader: Loaded config '/etc/clickhouse-server/{config.preprocessed_name}', performed update on configuration)|" + f"(ConfigReloader: Error updating configuration from '/etc/clickhouse-server/{config.preprocessed_name}')" + ), timeout=timeout, ) + if choice.group(2): + bash.expect(".+\n", timeout=5, expect_timeout=True) + fail("ConfigReloader: Error updating configuration") try: with Given(f"{config.name}"): @@ -456,7 +475,7 @@ def wait_for_config_to_be_loaded(user=None): wait_for_config_to_be_loaded() -@TestStep(When) +@TestStep(Given) def copy( self, dest_node, @@ -477,8 +496,12 @@ def copy( assert cmd.exitcode == 0, error() contents = cmd.output - - dest_node.command(f"cat << {eof} > {dest_path}\n{contents}\n{eof}") + try: + dest_node.command(f"cat << {eof} > {dest_path}\n{contents}\n{eof}") + yield dest_path + finally: + with Finally(f"I delete {dest_path}"): + dest_node.command(f"rm -rf {dest_path}") @TestStep(Given) diff --git a/tests/testflows/kerberos/configs/clickhouse/common.xml b/tests/testflows/kerberos/configs/clickhouse/common.xml new file mode 100644 index 000000000000..df952b28c82d --- /dev/null +++ b/tests/testflows/kerberos/configs/clickhouse/common.xml @@ -0,0 +1,6 @@ + + Europe/Moscow + 0.0.0.0 + /var/lib/clickhouse/ + /var/lib/clickhouse/tmp/ + diff --git a/tests/testflows/kerberos/configs/clickhouse/config.d/logs.xml b/tests/testflows/kerberos/configs/clickhouse/config.d/logs.xml index 2ee8bb55f38c..bdf1bbc11c11 100644 --- a/tests/testflows/kerberos/configs/clickhouse/config.d/logs.xml +++ b/tests/testflows/kerberos/configs/clickhouse/config.d/logs.xml @@ -1,4 +1,4 @@ - + 3 trace @@ -14,4 +14,4 @@ part_log
500 -
+ diff --git a/tests/testflows/kerberos/configs/clickhouse/config.d/ports.xml b/tests/testflows/kerberos/configs/clickhouse/config.d/ports.xml index 1e061e2252e1..fbc6cea74c0c 100644 --- a/tests/testflows/kerberos/configs/clickhouse/config.d/ports.xml +++ b/tests/testflows/kerberos/configs/clickhouse/config.d/ports.xml @@ -1,5 +1,5 @@ - + 8443 9440 - \ No newline at end of file + \ No newline at end of file diff --git a/tests/testflows/kerberos/configs/clickhouse/config.d/remote.xml b/tests/testflows/kerberos/configs/clickhouse/config.d/remote.xml index 04066290061b..51be2a6e8e3b 100644 --- a/tests/testflows/kerberos/configs/clickhouse/config.d/remote.xml +++ b/tests/testflows/kerberos/configs/clickhouse/config.d/remote.xml @@ -1,5 +1,5 @@ - + @@ -104,4 +104,4 @@ - + diff --git a/tests/testflows/kerberos/configs/clickhouse/config.d/ssl.xml b/tests/testflows/kerberos/configs/clickhouse/config.d/ssl.xml index 77e03e9cf0f4..ca65ffd5e043 100644 --- a/tests/testflows/kerberos/configs/clickhouse/config.d/ssl.xml +++ b/tests/testflows/kerberos/configs/clickhouse/config.d/ssl.xml @@ -1,4 +1,4 @@ - + /etc/clickhouse-server/ssl/server.crt @@ -14,4 +14,4 @@ - + diff --git a/tests/testflows/kerberos/configs/clickhouse/config.d/storage.xml b/tests/testflows/kerberos/configs/clickhouse/config.d/storage.xml index 0c53fd705937..618fd6b6d24a 100644 --- a/tests/testflows/kerberos/configs/clickhouse/config.d/storage.xml +++ b/tests/testflows/kerberos/configs/clickhouse/config.d/storage.xml @@ -1,4 +1,4 @@ - + @@ -17,4 +17,4 @@ - + diff --git a/tests/testflows/kerberos/configs/clickhouse/config.d/zookeeper.xml b/tests/testflows/kerberos/configs/clickhouse/config.d/zookeeper.xml index 1d5c0b6cb8b5..96270e7b645a 100644 --- a/tests/testflows/kerberos/configs/clickhouse/config.d/zookeeper.xml +++ b/tests/testflows/kerberos/configs/clickhouse/config.d/zookeeper.xml @@ -1,5 +1,5 @@ - + zookeeper @@ -7,4 +7,4 @@ 15000 - + diff --git a/tests/testflows/kerberos/configs/clickhouse/config.xml b/tests/testflows/kerberos/configs/clickhouse/config.xml new file mode 100644 index 000000000000..854e9deaaa5d --- /dev/null +++ b/tests/testflows/kerberos/configs/clickhouse/config.xml @@ -0,0 +1,440 @@ + + + + + + trace + /var/log/clickhouse-server/clickhouse-server.log + /var/log/clickhouse-server/clickhouse-server.err.log + 1000M + 10 + + + + 8123 + 9000 + + + + + + + + + /etc/clickhouse-server/server.crt + /etc/clickhouse-server/server.key + + /etc/clickhouse-server/dhparam.pem + none + true + true + sslv2,sslv3 + true + + + + true + true + sslv2,sslv3 + true + + + + RejectCertificateHandler + + + + + + + + + 9009 + + + + + + + + 0.0.0.0 + + + + + + + + + + + + 4096 + 3 + + + 100 + + + + + + 8589934592 + + + 5368709120 + + + + /var/lib/clickhouse/ + + + /var/lib/clickhouse/tmp/ + + + /var/lib/clickhouse/user_files/ + + + + + + users.xml + + + + /var/lib/clickhouse/access/ + + + + + default + + + + + + default + + + + + + + + + false + + + + + + + + localhost + 9000 + + + + + + + localhost + 9000 + + + + + localhost + 9000 + + + + + + + localhost + 9440 + 1 + + + + + + + localhost + 9000 + + + + + localhost + 1 + + + + + + + + + + + + + + + + + 3600 + + + + 3600 + + + 60 + + + + + + + + + + system + query_log
+ + toYYYYMM(event_date) + + 7500 +
+ + + + system + trace_log
+ + toYYYYMM(event_date) + 7500 +
+ + + + system + query_thread_log
+ toYYYYMM(event_date) + 7500 +
+ + + + system + part_log
+ toYYYYMM(event_date) + 7500 +
+ + + + + + + + + + + + + + *_dictionary.xml + + + + + + + + + + /clickhouse/task_queue/ddl + + + + + + + + + + + + + + + + click_cost + any + + 0 + 3600 + + + 86400 + 60 + + + + max + + 0 + 60 + + + 3600 + 300 + + + 86400 + 3600 + + + + + + /var/lib/clickhouse/format_schemas/ + + + +
diff --git a/tests/testflows/kerberos/configs/clickhouse/users.xml b/tests/testflows/kerberos/configs/clickhouse/users.xml new file mode 100644 index 000000000000..86b2cd9e1e3d --- /dev/null +++ b/tests/testflows/kerberos/configs/clickhouse/users.xml @@ -0,0 +1,133 @@ + + + + + + + + 10000000000 + + + 0 + + + random + + + + + 1 + + + + + + + + + + + + + ::/0 + + + + default + + + default + + + 1 + + + + + + + + + + + + + + + + + 3600 + + + 0 + 0 + 0 + 0 + 0 + + + + diff --git a/tests/testflows/kerberos/configs/clickhouse1/config.d/kerberos.xml b/tests/testflows/kerberos/configs/clickhouse1/config.d/kerberos.xml index 93ae1a315345..e45c4519c737 100644 --- a/tests/testflows/kerberos/configs/clickhouse1/config.d/kerberos.xml +++ b/tests/testflows/kerberos/configs/clickhouse1/config.d/kerberos.xml @@ -1,6 +1,6 @@ - + EXAMPLE.COM - + diff --git a/tests/testflows/kerberos/configs/clickhouse1/config.d/macros.xml b/tests/testflows/kerberos/configs/clickhouse1/config.d/macros.xml index c59a85cad8f4..6cdcc1b440c3 100644 --- a/tests/testflows/kerberos/configs/clickhouse1/config.d/macros.xml +++ b/tests/testflows/kerberos/configs/clickhouse1/config.d/macros.xml @@ -1,8 +1,8 @@ - + clickhouse1 01 01 - + diff --git a/tests/testflows/kerberos/configs/clickhouse1/users.d/kerberos-users.xml b/tests/testflows/kerberos/configs/clickhouse1/users.d/kerberos-users.xml index 60d2bba71bb8..7029f20217f4 100644 --- a/tests/testflows/kerberos/configs/clickhouse1/users.d/kerberos-users.xml +++ b/tests/testflows/kerberos/configs/clickhouse1/users.d/kerberos-users.xml @@ -1,4 +1,4 @@ - + @@ -7,4 +7,4 @@ 1 - \ No newline at end of file + \ No newline at end of file diff --git a/tests/testflows/kerberos/configs/clickhouse2/config.d/kerberos.xml b/tests/testflows/kerberos/configs/clickhouse2/config.d/kerberos.xml index 703aa6c8a8da..ceaa497c561a 100644 --- a/tests/testflows/kerberos/configs/clickhouse2/config.d/kerberos.xml +++ b/tests/testflows/kerberos/configs/clickhouse2/config.d/kerberos.xml @@ -1,5 +1,5 @@ - + EXAMPLE.COM - \ No newline at end of file + \ No newline at end of file diff --git a/tests/testflows/kerberos/configs/clickhouse2/config.d/macros.xml b/tests/testflows/kerberos/configs/clickhouse2/config.d/macros.xml index 1f880da0f653..a114a9ce4ab1 100644 --- a/tests/testflows/kerberos/configs/clickhouse2/config.d/macros.xml +++ b/tests/testflows/kerberos/configs/clickhouse2/config.d/macros.xml @@ -1,8 +1,8 @@ - + clickhouse2 01 02 - + diff --git a/tests/testflows/kerberos/configs/clickhouse3/config.d/kerberos.xml b/tests/testflows/kerberos/configs/clickhouse3/config.d/kerberos.xml index 93ae1a315345..e45c4519c737 100644 --- a/tests/testflows/kerberos/configs/clickhouse3/config.d/kerberos.xml +++ b/tests/testflows/kerberos/configs/clickhouse3/config.d/kerberos.xml @@ -1,6 +1,6 @@ - + EXAMPLE.COM - + diff --git a/tests/testflows/kerberos/configs/clickhouse3/config.d/macros.xml b/tests/testflows/kerberos/configs/clickhouse3/config.d/macros.xml index d2f1dbafa046..904a27b01723 100644 --- a/tests/testflows/kerberos/configs/clickhouse3/config.d/macros.xml +++ b/tests/testflows/kerberos/configs/clickhouse3/config.d/macros.xml @@ -1,8 +1,8 @@ - + clickhouse3 01 03 - + diff --git a/tests/testflows/kerberos/kerberos_env/clickhouse-service.yml b/tests/testflows/kerberos/kerberos_env/clickhouse-service.yml index 7671684f6ee0..3e27ee9c161c 100644 --- a/tests/testflows/kerberos/kerberos_env/clickhouse-service.yml +++ b/tests/testflows/kerberos/kerberos_env/clickhouse-service.yml @@ -2,7 +2,8 @@ version: '2.3' services: clickhouse: - image: altinityinfra/integration-test + image: ${IMAGE_DEPENDENCY_PROXY}clickhouse/clickhouse-integration-test:28741 + init: true expose: - "9000" - "9009" @@ -14,9 +15,9 @@ services: - "${CLICKHOUSE_TESTS_SERVER_BIN_PATH:-/usr/bin/clickhouse}:/usr/bin/clickhouse" - "${CLICKHOUSE_TESTS_ODBC_BRIDGE_BIN_PATH:-/usr/bin/clickhouse-odbc-bridge}:/usr/bin/clickhouse-odbc-bridge" - "${CLICKHOUSE_TESTS_DIR}/configs/kerberos/etc/krb5.conf:/etc/krb5.conf" - entrypoint: bash -c "clickhouse server --config-file=/etc/clickhouse-server/config.xml --log-file=/var/log/clickhouse-server/clickhouse-server.log --errorlog-file=/var/log/clickhouse-server/clickhouse-server.err.log" + entrypoint: bash -c "tail -f /dev/null" healthcheck: - test: clickhouse client --query='select 1' + test: echo 1 interval: 10s timeout: 10s retries: 3 diff --git a/tests/testflows/kerberos/kerberos_env/docker-compose.yml b/tests/testflows/kerberos/kerberos_env/docker-compose.yml index e89d18a52998..e92d12275ce4 100644 --- a/tests/testflows/kerberos/kerberos_env/docker-compose.yml +++ b/tests/testflows/kerberos/kerberos_env/docker-compose.yml @@ -61,7 +61,7 @@ services: # dummy service which does nothing, but allows to postpone # 'docker-compose up -d' till all dependecies will go healthy all_services_ready: - image: hello-world + image: ${IMAGE_DEPENDENCY_PROXY}hello-world depends_on: clickhouse1: condition: service_healthy diff --git a/tests/testflows/kerberos/kerberos_env/kerberos-service.yml b/tests/testflows/kerberos/kerberos_env/kerberos-service.yml index b34751258da8..71dda2303728 100644 --- a/tests/testflows/kerberos/kerberos_env/kerberos-service.yml +++ b/tests/testflows/kerberos/kerberos_env/kerberos-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: kerberos: - image: zvonand/docker-krb5-server:1.0.0 + image: registry.gitlab.com/altinity-public/container-images/docker-krb5-server:1.0 expose: - "88" - "464" diff --git a/tests/testflows/kerberos/kerberos_env/zookeeper-service.yml b/tests/testflows/kerberos/kerberos_env/zookeeper-service.yml index 6691a2df31c1..33cc799ab4cb 100644 --- a/tests/testflows/kerberos/kerberos_env/zookeeper-service.yml +++ b/tests/testflows/kerberos/kerberos_env/zookeeper-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: zookeeper: - image: zookeeper:3.4.12 + image: ${IMAGE_DEPENDENCY_PROXY}zookeeper:3.6.2 expose: - "2181" environment: diff --git a/tests/testflows/kerberos/kerberos_env_arm64/clickhouse-service.yml b/tests/testflows/kerberos/kerberos_env_arm64/clickhouse-service.yml new file mode 100644 index 000000000000..f8688ad4ef7f --- /dev/null +++ b/tests/testflows/kerberos/kerberos_env_arm64/clickhouse-service.yml @@ -0,0 +1,32 @@ +version: '2.3' + +services: + clickhouse: + image: registry.gitlab.com/altinity-public/container-images/test/clickhouse-integration-test:21.12 + expose: + - "9000" + - "9009" + - "8123" + volumes: + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/ssl:/etc/clickhouse-server/ssl" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.xml:/etc/clickhouse-server/config.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/users.xml:/etc/clickhouse-server/users.xml" + - "${CLICKHOUSE_TESTS_SERVER_BIN_PATH:-/usr/bin/clickhouse}:/usr/bin/clickhouse" + - "${CLICKHOUSE_TESTS_ODBC_BRIDGE_BIN_PATH:-/usr/bin/clickhouse-odbc-bridge}:/usr/bin/clickhouse-odbc-bridge" + - "${CLICKHOUSE_TESTS_DIR}/configs/kerberos/etc/krb5.conf:/etc/krb5.conf" + entrypoint: bash -c "clickhouse server --config-file=/etc/clickhouse-server/config.xml --log-file=/var/log/clickhouse-server/clickhouse-server.log --errorlog-file=/var/log/clickhouse-server/clickhouse-server.err.log" + healthcheck: + test: clickhouse client --query='select 1' + interval: 10s + timeout: 10s + retries: 3 + start_period: 300s + + environment: + KRB5_CLIENT_KTNAME: /etc/krb5.keytab + KRB5_KTNAME: /etc/krb5.keytab + + cap_add: + - SYS_PTRACE + security_opt: + - label:disable diff --git a/tests/testflows/kerberos/kerberos_env_arm64/docker-compose.yml b/tests/testflows/kerberos/kerberos_env_arm64/docker-compose.yml new file mode 100644 index 000000000000..e92d12275ce4 --- /dev/null +++ b/tests/testflows/kerberos/kerberos_env_arm64/docker-compose.yml @@ -0,0 +1,80 @@ +version: '2.3' + +services: + + zookeeper: + extends: + file: zookeeper-service.yml + service: zookeeper + + kerberos: + extends: + file: kerberos-service.yml + service: kerberos + hostname: kerberos + depends_on: + zookeeper: + condition: service_healthy + + clickhouse1: + extends: + file: clickhouse-service.yml + service: clickhouse + hostname: clickhouse1 + volumes: + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse1/database/:/var/lib/clickhouse/" + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse1/logs/:/var/log/clickhouse-server/" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse1/config.d:/etc/clickhouse-server/config.d" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse1/users.d:/etc/clickhouse-server/users.d" + depends_on: + zookeeper: + condition: service_healthy + + clickhouse2: + extends: + file: clickhouse-service.yml + service: clickhouse + hostname: clickhouse2 + volumes: + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse2/database/:/var/lib/clickhouse/" + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse2/logs/:/var/log/clickhouse-server/" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse2/config.d:/etc/clickhouse-server/config.d" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse2/users.d:/etc/clickhouse-server/users.d" + depends_on: + zookeeper: + condition: service_healthy + + clickhouse3: + extends: + file: clickhouse-service.yml + service: clickhouse + hostname: clickhouse3 + volumes: + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse3/database/:/var/lib/clickhouse/" + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse3/logs/:/var/log/clickhouse-server/" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse3/config.d:/etc/clickhouse-server/config.d" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse3/users.d:/etc/clickhouse-server/users.d" + depends_on: + zookeeper: + condition: service_healthy + + # dummy service which does nothing, but allows to postpone + # 'docker-compose up -d' till all dependecies will go healthy + all_services_ready: + image: ${IMAGE_DEPENDENCY_PROXY}hello-world + depends_on: + clickhouse1: + condition: service_healthy + clickhouse2: + condition: service_healthy + clickhouse3: + condition: service_healthy + zookeeper: + condition: service_healthy + kerberos: + condition: service_healthy + +networks: + default: + name: krbnet + driver: bridge diff --git a/tests/testflows/kerberos/kerberos_env_arm64/kerberos-service.yml b/tests/testflows/kerberos/kerberos_env_arm64/kerberos-service.yml new file mode 100644 index 000000000000..4e5faab92f2b --- /dev/null +++ b/tests/testflows/kerberos/kerberos_env_arm64/kerberos-service.yml @@ -0,0 +1,26 @@ +version: '2.3' + +services: + kerberos: + image: registry.gitlab.com/altinity-qa/clickhouse/cicd/release/kerberos-arm + expose: + - "88" + - "464" + - "749" + healthcheck: + test: echo 1 + interval: 10s + timeout: 10s + retries: 3 + start_period: 300s + environment: + KRB5_PASS: pwd + KRB5_REALM: EXAMPLE.COM + KRB5_KDC: 0.0.0.0 + volumes: + - "${CLICKHOUSE_TESTS_DIR}/configs/kerberos/etc/krb5kdc/kdc.conf:/etc/krb5kdc/kdc.conf" + - "${CLICKHOUSE_TESTS_DIR}/_instances/kerberos/krb5kdc/log/kdc.log:/usr/local/var/krb5kdc/kdc.log" + - "${CLICKHOUSE_TESTS_DIR}/_instances/kerberos/krb5kdc/log/kadmin.log:/usr/local/var/krb5kdc/kadmin.log" + - "${CLICKHOUSE_TESTS_DIR}/_instances/kerberos/var/log:/var/log" + security_opt: + - label:disable diff --git a/tests/testflows/kerberos/kerberos_env_arm64/zookeeper-service.yml b/tests/testflows/kerberos/kerberos_env_arm64/zookeeper-service.yml new file mode 100644 index 000000000000..33cc799ab4cb --- /dev/null +++ b/tests/testflows/kerberos/kerberos_env_arm64/zookeeper-service.yml @@ -0,0 +1,18 @@ +version: '2.3' + +services: + zookeeper: + image: ${IMAGE_DEPENDENCY_PROXY}zookeeper:3.6.2 + expose: + - "2181" + environment: + ZOO_TICK_TIME: 500 + ZOO_MY_ID: 1 + healthcheck: + test: echo stat | nc localhost 2181 + interval: 10s + timeout: 10s + retries: 3 + start_period: 300s + security_opt: + - label:disable diff --git a/tests/testflows/kerberos/regression.py b/tests/testflows/kerberos/regression.py index 0206cd3bf000..da610f89e698 100755 --- a/tests/testflows/kerberos/regression.py +++ b/tests/testflows/kerberos/regression.py @@ -2,6 +2,7 @@ import os import sys from testflows.core import * +from platform import processor as current_cpu append_path(sys.path, "..") @@ -21,24 +22,29 @@ @ArgumentParser(argparser) @Requirements(RQ_SRS_016_Kerberos("1.0")) @XFails(xfails) -def regression( - self, local, clickhouse_binary_path, clickhouse_version=None, stress=None -): +def regression(self, local, clickhouse_binary_path, clickhouse_version, stress=None): """ClickHouse Kerberos authentication test regression module.""" nodes = { "clickhouse": ("clickhouse1", "clickhouse2", "clickhouse3"), "kerberos": ("kerberos",), } + self.context.clickhouse_version = clickhouse_version + if stress is not None: self.context.stress = stress - self.context.clickhouse_version = clickhouse_version + + folder_name = os.path.basename(current_dir()) + if current_cpu() == "aarch64": + env = f"{folder_name}_env_arm64" + else: + env = f"{folder_name}_env" with Cluster( local, clickhouse_binary_path, nodes=nodes, - docker_compose_project_dir=os.path.join(current_dir(), "kerberos_env"), + docker_compose_project_dir=os.path.join(current_dir(), env), ) as cluster: self.context.cluster = cluster diff --git a/tests/testflows/kerberos/tests/common.py b/tests/testflows/kerberos/tests/common.py index 0e0f7f2ebc23..b4c6a2493f83 100644 --- a/tests/testflows/kerberos/tests/common.py +++ b/tests/testflows/kerberos/tests/common.py @@ -32,13 +32,11 @@ def create_default_config(filename): contents = "" if "kerberos_users.xml" in filename: contents = ( - "EXAMPLE.COM" - "" + "EXAMPLE.COM" + "" ) elif "kerberos.xml" in filename: - contents = ( - "EXAMPLE.COM" - ) + contents = "EXAMPLE.COM" with open(filename, "w") as f: f.write(contents) @@ -107,7 +105,7 @@ def temp_erase(self, node, filename=None): try: with Then("I overwrite file to be dummy"): with open(filename, "w") as f: - f.write("\n") + f.write("\n") node.restart() yield finally: diff --git a/tests/testflows/kerberos/tests/config.py b/tests/testflows/kerberos/tests/config.py index e682858d557e..4b9609321fd1 100644 --- a/tests/testflows/kerberos/tests/config.py +++ b/tests/testflows/kerberos/tests/config.py @@ -1,11 +1,12 @@ -from testflows.core import * -from kerberos.tests.common import * -from kerberos.requirements.requirements import * - +import os import time import datetime import itertools +from testflows.core import * +from kerberos.tests.common import * +from kerberos.requirements.requirements import * + @TestScenario @Requirements(RQ_SRS_016_Kerberos_Configuration_KerberosNotEnabled("1.0")) @@ -14,10 +15,12 @@ def kerberos_not_enabled(self): but Kerberos itself is not enabled in config.xml. """ ch_nodes = self.context.ch_nodes - config_path = f"kerberos/configs/{ch_nodes[0].name}/config.d/kerberos.xml" + config_path = os.path.join( + current_dir(), "..", f"configs/{ch_nodes[0].name}/config.d/kerberos.xml" + ) def modify_file(root): - return xmltree.fromstring("") + return xmltree.fromstring("") check_wrong_config( node=ch_nodes[0], @@ -33,7 +36,9 @@ def modify_file(root): def multiple_kerberos(self): """ClickHouse SHALL disable Kerberos authentication if more than one kerberos sections specified in config.xml.""" ch_nodes = self.context.ch_nodes - config_path = f"kerberos/configs/{ch_nodes[0].name}/config.d/kerberos.xml" + config_path = os.path.join( + current_dir(), "..", f"configs/{ch_nodes[0].name}/config.d/kerberos.xml" + ) def modify_file(root): second_section = "EXAM.COM" @@ -58,7 +63,9 @@ def wrong_user_realm(self): """ ch_nodes = self.context.ch_nodes - config_path = f"kerberos/configs/{ch_nodes[0].name}/users.d/kerberos-users.xml" + config_path = os.path.join( + current_dir(), "..", f"configs/{ch_nodes[0].name}/users.d/kerberos-users.xml" + ) def modify_file(root): krb = root.find("users").find("kerberos_user") @@ -81,7 +88,9 @@ def multiple_auth_methods(self): auth method is specified for user alongside with Kerberos. """ ch_nodes = self.context.ch_nodes - config_path = f"kerberos/configs/{ch_nodes[0].name}/users.d/kerberos-users.xml" + config_path = os.path.join( + current_dir(), "..", f"configs/{ch_nodes[0].name}/users.d/kerberos-users.xml" + ) def modify_file(root): krb = root.find("users").find("kerberos_user") @@ -103,7 +112,9 @@ def modify_file(root): def principal_and_realm_specified(self): """ClickHouse SHALL drop an exception if both realm and principal fields are specified in config.xml.""" ch_nodes = self.context.ch_nodes - config_path = f"kerberos/configs/{ch_nodes[0].name}/config.d/kerberos.xml" + config_path = os.path.join( + current_dir(), "..", f"configs/{ch_nodes[0].name}/config.d/kerberos.xml" + ) def modify_file(root): krb = root.find("kerberos") @@ -125,7 +136,9 @@ def modify_file(root): def multiple_realm(self): """ClickHouse SHALL throw an exception and disable Kerberos if more than one realm is specified in config.xml.""" ch_nodes = self.context.ch_nodes - config_path = f"kerberos/configs/{ch_nodes[0].name}/config.d/kerberos.xml" + config_path = os.path.join( + current_dir(), "..", f"configs/{ch_nodes[0].name}/config.d/kerberos.xml" + ) def modify_file(root): krb = root.find("kerberos") @@ -146,7 +159,9 @@ def modify_file(root): def multiple_principal(self): """ClickHouse SHALL throw an exception and disable Kerberos if more than one principal is specified in config.xml.""" ch_nodes = self.context.ch_nodes - config_path = f"kerberos/configs/{ch_nodes[0].name}/config.d/kerberos.xml" + config_path = os.path.join( + current_dir(), "..", f"configs/{ch_nodes[0].name}/config.d/kerberos.xml" + ) def modify_file(root): krb = root.find("kerberos") diff --git a/tests/testflows/ldap/authentication/authentication_env/clickhouse-service.yml b/tests/testflows/ldap/authentication/authentication_env/clickhouse-service.yml index f8cc0a62c67c..fe495187f5ec 100644 --- a/tests/testflows/ldap/authentication/authentication_env/clickhouse-service.yml +++ b/tests/testflows/ldap/authentication/authentication_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: altinityinfra/integration-test + image: ${IMAGE_DEPENDENCY_PROXY}clickhouse/clickhouse-integration-test:28741 init: true expose: - "9000" diff --git a/tests/testflows/ldap/authentication/authentication_env/docker-compose.yml b/tests/testflows/ldap/authentication/authentication_env/docker-compose.yml index 36e25ef766e5..cb4c6e696729 100644 --- a/tests/testflows/ldap/authentication/authentication_env/docker-compose.yml +++ b/tests/testflows/ldap/authentication/authentication_env/docker-compose.yml @@ -138,7 +138,7 @@ services: # dummy service which does nothing, but allows to postpone # 'docker-compose up -d' till all dependecies will go healthy all_services_ready: - image: hello-world + image: ${IMAGE_DEPENDENCY_PROXY}hello-world depends_on: clickhouse1: condition: service_healthy diff --git a/tests/testflows/ldap/authentication/authentication_env/openldap-service.yml b/tests/testflows/ldap/authentication/authentication_env/openldap-service.yml index 606ea3f723fc..fe33082cd53b 100644 --- a/tests/testflows/ldap/authentication/authentication_env/openldap-service.yml +++ b/tests/testflows/ldap/authentication/authentication_env/openldap-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: openldap: - image: osixia/openldap:1.4.0 + image: ${IMAGE_DEPENDENCY_PROXY}osixia/openldap:1.4.0 command: "--copy-service --loglevel debug" environment: LDAP_ORGANIZATION: "company" @@ -22,7 +22,7 @@ services: - label:disable phpldapadmin: - image: osixia/phpldapadmin:0.9.0 + image: ${IMAGE_DEPENDENCY_PROXY}osixia/phpldapadmin:0.9.0 environment: PHPLDAPADMIN_HTTPS=false: healthcheck: diff --git a/tests/testflows/ldap/authentication/authentication_env/zookeeper-service.yml b/tests/testflows/ldap/authentication/authentication_env/zookeeper-service.yml index 6691a2df31c1..33cc799ab4cb 100644 --- a/tests/testflows/ldap/authentication/authentication_env/zookeeper-service.yml +++ b/tests/testflows/ldap/authentication/authentication_env/zookeeper-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: zookeeper: - image: zookeeper:3.4.12 + image: ${IMAGE_DEPENDENCY_PROXY}zookeeper:3.6.2 expose: - "2181" environment: diff --git a/tests/testflows/ldap/authentication/authentication_env_arm64/docker-compose.yml b/tests/testflows/ldap/authentication/authentication_env_arm64/docker-compose.yml index 36e25ef766e5..cb4c6e696729 100644 --- a/tests/testflows/ldap/authentication/authentication_env_arm64/docker-compose.yml +++ b/tests/testflows/ldap/authentication/authentication_env_arm64/docker-compose.yml @@ -138,7 +138,7 @@ services: # dummy service which does nothing, but allows to postpone # 'docker-compose up -d' till all dependecies will go healthy all_services_ready: - image: hello-world + image: ${IMAGE_DEPENDENCY_PROXY}hello-world depends_on: clickhouse1: condition: service_healthy diff --git a/tests/testflows/ldap/authentication/authentication_env_arm64/openldap-service.yml b/tests/testflows/ldap/authentication/authentication_env_arm64/openldap-service.yml index 606ea3f723fc..fe33082cd53b 100644 --- a/tests/testflows/ldap/authentication/authentication_env_arm64/openldap-service.yml +++ b/tests/testflows/ldap/authentication/authentication_env_arm64/openldap-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: openldap: - image: osixia/openldap:1.4.0 + image: ${IMAGE_DEPENDENCY_PROXY}osixia/openldap:1.4.0 command: "--copy-service --loglevel debug" environment: LDAP_ORGANIZATION: "company" @@ -22,7 +22,7 @@ services: - label:disable phpldapadmin: - image: osixia/phpldapadmin:0.9.0 + image: ${IMAGE_DEPENDENCY_PROXY}osixia/phpldapadmin:0.9.0 environment: PHPLDAPADMIN_HTTPS=false: healthcheck: diff --git a/tests/testflows/ldap/authentication/authentication_env_arm64/zookeeper-service.yml b/tests/testflows/ldap/authentication/authentication_env_arm64/zookeeper-service.yml index 6691a2df31c1..33cc799ab4cb 100644 --- a/tests/testflows/ldap/authentication/authentication_env_arm64/zookeeper-service.yml +++ b/tests/testflows/ldap/authentication/authentication_env_arm64/zookeeper-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: zookeeper: - image: zookeeper:3.4.12 + image: ${IMAGE_DEPENDENCY_PROXY}zookeeper:3.6.2 expose: - "2181" environment: diff --git a/tests/testflows/ldap/authentication/configs/clickhouse/common.xml b/tests/testflows/ldap/authentication/configs/clickhouse/common.xml new file mode 100644 index 000000000000..df952b28c82d --- /dev/null +++ b/tests/testflows/ldap/authentication/configs/clickhouse/common.xml @@ -0,0 +1,6 @@ + + Europe/Moscow + 0.0.0.0 + /var/lib/clickhouse/ + /var/lib/clickhouse/tmp/ + diff --git a/tests/testflows/ldap/authentication/configs/clickhouse/config.d/logs.xml b/tests/testflows/ldap/authentication/configs/clickhouse/config.d/logs.xml index 2ee8bb55f38c..bdf1bbc11c11 100644 --- a/tests/testflows/ldap/authentication/configs/clickhouse/config.d/logs.xml +++ b/tests/testflows/ldap/authentication/configs/clickhouse/config.d/logs.xml @@ -1,4 +1,4 @@ - + 3 trace @@ -14,4 +14,4 @@ part_log
500 -
+ diff --git a/tests/testflows/ldap/authentication/configs/clickhouse/config.d/ports.xml b/tests/testflows/ldap/authentication/configs/clickhouse/config.d/ports.xml index 1e061e2252e1..fbc6cea74c0c 100644 --- a/tests/testflows/ldap/authentication/configs/clickhouse/config.d/ports.xml +++ b/tests/testflows/ldap/authentication/configs/clickhouse/config.d/ports.xml @@ -1,5 +1,5 @@ - + 8443 9440 - \ No newline at end of file + \ No newline at end of file diff --git a/tests/testflows/ldap/authentication/configs/clickhouse/config.d/remote.xml b/tests/testflows/ldap/authentication/configs/clickhouse/config.d/remote.xml index 04066290061b..51be2a6e8e3b 100644 --- a/tests/testflows/ldap/authentication/configs/clickhouse/config.d/remote.xml +++ b/tests/testflows/ldap/authentication/configs/clickhouse/config.d/remote.xml @@ -1,5 +1,5 @@ - + @@ -104,4 +104,4 @@ - + diff --git a/tests/testflows/ldap/authentication/configs/clickhouse/config.d/ssl.xml b/tests/testflows/ldap/authentication/configs/clickhouse/config.d/ssl.xml index 77e03e9cf0f4..ca65ffd5e043 100644 --- a/tests/testflows/ldap/authentication/configs/clickhouse/config.d/ssl.xml +++ b/tests/testflows/ldap/authentication/configs/clickhouse/config.d/ssl.xml @@ -1,4 +1,4 @@ - + /etc/clickhouse-server/ssl/server.crt @@ -14,4 +14,4 @@ - + diff --git a/tests/testflows/ldap/authentication/configs/clickhouse/config.d/storage.xml b/tests/testflows/ldap/authentication/configs/clickhouse/config.d/storage.xml index 0c53fd705937..618fd6b6d24a 100644 --- a/tests/testflows/ldap/authentication/configs/clickhouse/config.d/storage.xml +++ b/tests/testflows/ldap/authentication/configs/clickhouse/config.d/storage.xml @@ -1,4 +1,4 @@ - + @@ -17,4 +17,4 @@ - + diff --git a/tests/testflows/ldap/authentication/configs/clickhouse/config.d/zookeeper.xml b/tests/testflows/ldap/authentication/configs/clickhouse/config.d/zookeeper.xml index 1d5c0b6cb8b5..96270e7b645a 100644 --- a/tests/testflows/ldap/authentication/configs/clickhouse/config.d/zookeeper.xml +++ b/tests/testflows/ldap/authentication/configs/clickhouse/config.d/zookeeper.xml @@ -1,5 +1,5 @@ - + zookeeper @@ -7,4 +7,4 @@ 15000 - + diff --git a/tests/testflows/ldap/authentication/configs/clickhouse/config.xml b/tests/testflows/ldap/authentication/configs/clickhouse/config.xml new file mode 100644 index 000000000000..26ac36232cc5 --- /dev/null +++ b/tests/testflows/ldap/authentication/configs/clickhouse/config.xml @@ -0,0 +1,442 @@ + + + + + + trace + /var/log/clickhouse-server/clickhouse-server.log + /var/log/clickhouse-server/clickhouse-server.err.log + 1000M + 10 + + + + 8123 + 9000 + + + + + + + + + /etc/clickhouse-server/server.crt + /etc/clickhouse-server/server.key + + /etc/clickhouse-server/dhparam.pem + none + true + true + sslv2,sslv3 + true + + + + true + true + sslv2,sslv3 + true + + + + RejectCertificateHandler + + + + + + + + + 9009 + + + + + + + + + + + + + + + + + + + + 4096 + 3 + + + 100 + + + + + + 8589934592 + + + 5368709120 + + + + /var/lib/clickhouse/ + + + /var/lib/clickhouse/tmp/ + + + /var/lib/clickhouse/user_files/ + + + + + + users.xml + + + + /var/lib/clickhouse/access/ + + + + + default + + + + + + default + + + + + + + + + false + + + + + + + + localhost + 9000 + + + + + + + localhost + 9000 + + + + + localhost + 9000 + + + + + + + localhost + 9440 + 1 + + + + + + + localhost + 9000 + + + + + localhost + 1 + + + + + + + + + + + + + + + + + 3600 + + + + 3600 + + + 60 + + + + + + + + + + system + query_log
+ + toYYYYMM(event_date) + + 7500 +
+ + + + system + trace_log
+ + toYYYYMM(event_date) + 7500 +
+ + + + system + query_thread_log
+ toYYYYMM(event_date) + 7500 +
+ + + + system + part_log
+ toYYYYMM(event_date) + 7500 +
+ + + + + + + + + + + + + + *_dictionary.xml + + + + + + + + + + /clickhouse/task_queue/ddl + + + + + + + + + + + + + + + + click_cost + any + + 0 + 3600 + + + 86400 + 7200 + + + + max + + 0 + 60 + + + 3600 + 300 + + + 86400 + 3600 + + + + + + /var/lib/clickhouse/format_schemas/ + + + +
diff --git a/tests/testflows/ldap/authentication/configs/clickhouse/users.xml b/tests/testflows/ldap/authentication/configs/clickhouse/users.xml new file mode 100644 index 000000000000..86b2cd9e1e3d --- /dev/null +++ b/tests/testflows/ldap/authentication/configs/clickhouse/users.xml @@ -0,0 +1,133 @@ + + + + + + + + 10000000000 + + + 0 + + + random + + + + + 1 + + + + + + + + + + + + + ::/0 + + + + default + + + default + + + 1 + + + + + + + + + + + + + + + + + 3600 + + + 0 + 0 + 0 + 0 + 0 + + + + diff --git a/tests/testflows/ldap/authentication/configs/clickhouse1/config.d/macros.xml b/tests/testflows/ldap/authentication/configs/clickhouse1/config.d/macros.xml index c59a85cad8f4..6cdcc1b440c3 100644 --- a/tests/testflows/ldap/authentication/configs/clickhouse1/config.d/macros.xml +++ b/tests/testflows/ldap/authentication/configs/clickhouse1/config.d/macros.xml @@ -1,8 +1,8 @@ - + clickhouse1 01 01 - + diff --git a/tests/testflows/ldap/authentication/configs/clickhouse2/config.d/macros.xml b/tests/testflows/ldap/authentication/configs/clickhouse2/config.d/macros.xml index 1f880da0f653..a114a9ce4ab1 100644 --- a/tests/testflows/ldap/authentication/configs/clickhouse2/config.d/macros.xml +++ b/tests/testflows/ldap/authentication/configs/clickhouse2/config.d/macros.xml @@ -1,8 +1,8 @@ - + clickhouse2 01 02 - + diff --git a/tests/testflows/ldap/authentication/configs/clickhouse3/config.d/macros.xml b/tests/testflows/ldap/authentication/configs/clickhouse3/config.d/macros.xml index d2f1dbafa046..904a27b01723 100644 --- a/tests/testflows/ldap/authentication/configs/clickhouse3/config.d/macros.xml +++ b/tests/testflows/ldap/authentication/configs/clickhouse3/config.d/macros.xml @@ -1,8 +1,8 @@ - + clickhouse3 01 03 - + diff --git a/tests/testflows/ldap/authentication/regression.py b/tests/testflows/ldap/authentication/regression.py index d2e541598eaa..1f6343ac3342 100755 --- a/tests/testflows/ldap/authentication/regression.py +++ b/tests/testflows/ldap/authentication/regression.py @@ -42,9 +42,7 @@ @Specifications(SRS_007_ClickHouse_Authentication_of_Users_via_LDAP) @Requirements(RQ_SRS_007_LDAP_Authentication("1.0")) @XFails(xfails) -def regression( - self, local, clickhouse_binary_path, clickhouse_version=None, stress=None -): +def regression(self, local, clickhouse_binary_path, clickhouse_version, stress=None): """ClickHouse integration with LDAP regression module.""" nodes = { "clickhouse": ("clickhouse1", "clickhouse2", "clickhouse3"), diff --git a/tests/testflows/ldap/authentication/requirements/requirements.md b/tests/testflows/ldap/authentication/requirements/requirements.md index a42e2eaa132e..372c44e20c59 100644 --- a/tests/testflows/ldap/authentication/requirements/requirements.md +++ b/tests/testflows/ldap/authentication/requirements/requirements.md @@ -442,7 +442,7 @@ version: 2.0 configuration file or of any configuration file inside the `config.d` directory. ```xml - + localhost 636 @@ -458,7 +458,7 @@ configuration file or of any configuration file inside the `config.d` directory. /path/to/tls_ca_cert_dir ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:AES256-GCM-SHA384 - + ``` #### RQ.SRS-007.LDAP.Configuration.User.RBAC @@ -478,7 +478,7 @@ version: 1.0 an [LDAP] server inside the `users.xml` file or any configuration file inside the `users.d` directory. ```xml - + @@ -486,7 +486,7 @@ an [LDAP] server inside the `users.xml` file or any configuration file inside th - + ``` #### RQ.SRS-007.LDAP.Configuration.User.Name.Empty diff --git a/tests/testflows/ldap/authentication/requirements/requirements.py b/tests/testflows/ldap/authentication/requirements/requirements.py index 6ee904bd40e4..fe8b2d5d60c7 100644 --- a/tests/testflows/ldap/authentication/requirements/requirements.py +++ b/tests/testflows/ldap/authentication/requirements/requirements.py @@ -917,7 +917,7 @@ "configuration file or of any configuration file inside the `config.d` directory.\n" "\n" "```xml\n" - "\n" + "\n" " \n" " localhost\n" " 636\n" @@ -933,7 +933,7 @@ " /path/to/tls_ca_cert_dir\n" " ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:AES256-GCM-SHA384\n" " \n" - "\n" + "\n" "```\n" "\n" ), @@ -975,7 +975,7 @@ "an [LDAP] server inside the `users.xml` file or any configuration file inside the `users.d` directory.\n" "\n" "```xml\n" - "\n" + "\n" " \n" " \n" " \n" @@ -983,7 +983,7 @@ " \n" " \n" " \n" - "\n" + "\n" "```\n" "\n" ), @@ -2092,7 +2092,7 @@ configuration file or of any configuration file inside the `config.d` directory. ```xml - + localhost 636 @@ -2108,7 +2108,7 @@ /path/to/tls_ca_cert_dir ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:AES256-GCM-SHA384 - + ``` #### RQ.SRS-007.LDAP.Configuration.User.RBAC @@ -2128,7 +2128,7 @@ an [LDAP] server inside the `users.xml` file or any configuration file inside the `users.d` directory. ```xml - + @@ -2136,7 +2136,7 @@ - + ``` #### RQ.SRS-007.LDAP.Configuration.User.Name.Empty diff --git a/tests/testflows/ldap/authentication/tests/common.py b/tests/testflows/ldap/authentication/tests/common.py index 17b4fcd3e629..d35d2c7a85f1 100644 --- a/tests/testflows/ldap/authentication/tests/common.py +++ b/tests/testflows/ldap/authentication/tests/common.py @@ -80,7 +80,7 @@ def create_ldap_servers_config_content( path = os.path.join(config_d_dir, config_file) name = config_file - root = xmltree.fromstring("") + root = xmltree.fromstring("") xml_servers = root.find("ldap_servers") xml_servers.append(xmltree.Comment(text=f"LDAP servers {uid}")) @@ -128,7 +128,7 @@ def create_ldap_users_config_content( path = os.path.join(config_d_dir, config_file) name = config_file - root = xmltree.fromstring("") + root = xmltree.fromstring("") xml_users = root.find("users") xml_users.append(xmltree.Comment(text=f"LDAP users {uid}")) diff --git a/tests/testflows/ldap/authentication/tests/server_config.py b/tests/testflows/ldap/authentication/tests/server_config.py index af15a1495dfb..af817ee147a6 100644 --- a/tests/testflows/ldap/authentication/tests/server_config.py +++ b/tests/testflows/ldap/authentication/tests/server_config.py @@ -358,7 +358,7 @@ def invalid_verification_cooldown_value(self, invalid_value, timeout=300): def syntax(self): """Check that server configuration with valid syntax can be loaded. ```xml - + localhost 636 @@ -374,7 +374,7 @@ def syntax(self): /path/to/tls_ca_cert_dir ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:AES256-GCM-SHA384 - + ``` """ servers = { diff --git a/tests/testflows/ldap/external_user_directory/configs/clickhouse/common.xml b/tests/testflows/ldap/external_user_directory/configs/clickhouse/common.xml new file mode 100644 index 000000000000..df952b28c82d --- /dev/null +++ b/tests/testflows/ldap/external_user_directory/configs/clickhouse/common.xml @@ -0,0 +1,6 @@ + + Europe/Moscow + 0.0.0.0 + /var/lib/clickhouse/ + /var/lib/clickhouse/tmp/ + diff --git a/tests/testflows/ldap/external_user_directory/configs/clickhouse/config.d/logs.xml b/tests/testflows/ldap/external_user_directory/configs/clickhouse/config.d/logs.xml index 2ee8bb55f38c..bdf1bbc11c11 100644 --- a/tests/testflows/ldap/external_user_directory/configs/clickhouse/config.d/logs.xml +++ b/tests/testflows/ldap/external_user_directory/configs/clickhouse/config.d/logs.xml @@ -1,4 +1,4 @@ - + 3 trace @@ -14,4 +14,4 @@ part_log
500 -
+ diff --git a/tests/testflows/ldap/external_user_directory/configs/clickhouse/config.d/ports.xml b/tests/testflows/ldap/external_user_directory/configs/clickhouse/config.d/ports.xml index 1e061e2252e1..fbc6cea74c0c 100644 --- a/tests/testflows/ldap/external_user_directory/configs/clickhouse/config.d/ports.xml +++ b/tests/testflows/ldap/external_user_directory/configs/clickhouse/config.d/ports.xml @@ -1,5 +1,5 @@ - + 8443 9440 - \ No newline at end of file + \ No newline at end of file diff --git a/tests/testflows/ldap/external_user_directory/configs/clickhouse/config.d/remote.xml b/tests/testflows/ldap/external_user_directory/configs/clickhouse/config.d/remote.xml index 04066290061b..51be2a6e8e3b 100644 --- a/tests/testflows/ldap/external_user_directory/configs/clickhouse/config.d/remote.xml +++ b/tests/testflows/ldap/external_user_directory/configs/clickhouse/config.d/remote.xml @@ -1,5 +1,5 @@ - + @@ -104,4 +104,4 @@ - + diff --git a/tests/testflows/ldap/external_user_directory/configs/clickhouse/config.d/ssl.xml b/tests/testflows/ldap/external_user_directory/configs/clickhouse/config.d/ssl.xml index 77e03e9cf0f4..ca65ffd5e043 100644 --- a/tests/testflows/ldap/external_user_directory/configs/clickhouse/config.d/ssl.xml +++ b/tests/testflows/ldap/external_user_directory/configs/clickhouse/config.d/ssl.xml @@ -1,4 +1,4 @@ - + /etc/clickhouse-server/ssl/server.crt @@ -14,4 +14,4 @@ - + diff --git a/tests/testflows/ldap/external_user_directory/configs/clickhouse/config.d/storage.xml b/tests/testflows/ldap/external_user_directory/configs/clickhouse/config.d/storage.xml index 0c53fd705937..618fd6b6d24a 100644 --- a/tests/testflows/ldap/external_user_directory/configs/clickhouse/config.d/storage.xml +++ b/tests/testflows/ldap/external_user_directory/configs/clickhouse/config.d/storage.xml @@ -1,4 +1,4 @@ - + @@ -17,4 +17,4 @@ - + diff --git a/tests/testflows/ldap/external_user_directory/configs/clickhouse/config.d/zookeeper.xml b/tests/testflows/ldap/external_user_directory/configs/clickhouse/config.d/zookeeper.xml index 1d5c0b6cb8b5..96270e7b645a 100644 --- a/tests/testflows/ldap/external_user_directory/configs/clickhouse/config.d/zookeeper.xml +++ b/tests/testflows/ldap/external_user_directory/configs/clickhouse/config.d/zookeeper.xml @@ -1,5 +1,5 @@ - + zookeeper @@ -7,4 +7,4 @@ 15000 - + diff --git a/tests/testflows/ldap/external_user_directory/configs/clickhouse/config.xml b/tests/testflows/ldap/external_user_directory/configs/clickhouse/config.xml new file mode 100644 index 000000000000..26ac36232cc5 --- /dev/null +++ b/tests/testflows/ldap/external_user_directory/configs/clickhouse/config.xml @@ -0,0 +1,442 @@ + + + + + + trace + /var/log/clickhouse-server/clickhouse-server.log + /var/log/clickhouse-server/clickhouse-server.err.log + 1000M + 10 + + + + 8123 + 9000 + + + + + + + + + /etc/clickhouse-server/server.crt + /etc/clickhouse-server/server.key + + /etc/clickhouse-server/dhparam.pem + none + true + true + sslv2,sslv3 + true + + + + true + true + sslv2,sslv3 + true + + + + RejectCertificateHandler + + + + + + + + + 9009 + + + + + + + + + + + + + + + + + + + + 4096 + 3 + + + 100 + + + + + + 8589934592 + + + 5368709120 + + + + /var/lib/clickhouse/ + + + /var/lib/clickhouse/tmp/ + + + /var/lib/clickhouse/user_files/ + + + + + + users.xml + + + + /var/lib/clickhouse/access/ + + + + + default + + + + + + default + + + + + + + + + false + + + + + + + + localhost + 9000 + + + + + + + localhost + 9000 + + + + + localhost + 9000 + + + + + + + localhost + 9440 + 1 + + + + + + + localhost + 9000 + + + + + localhost + 1 + + + + + + + + + + + + + + + + + 3600 + + + + 3600 + + + 60 + + + + + + + + + + system + query_log
+ + toYYYYMM(event_date) + + 7500 +
+ + + + system + trace_log
+ + toYYYYMM(event_date) + 7500 +
+ + + + system + query_thread_log
+ toYYYYMM(event_date) + 7500 +
+ + + + system + part_log
+ toYYYYMM(event_date) + 7500 +
+ + + + + + + + + + + + + + *_dictionary.xml + + + + + + + + + + /clickhouse/task_queue/ddl + + + + + + + + + + + + + + + + click_cost + any + + 0 + 3600 + + + 86400 + 7200 + + + + max + + 0 + 60 + + + 3600 + 300 + + + 86400 + 3600 + + + + + + /var/lib/clickhouse/format_schemas/ + + + +
diff --git a/tests/testflows/ldap/external_user_directory/configs/clickhouse/users.xml b/tests/testflows/ldap/external_user_directory/configs/clickhouse/users.xml new file mode 100644 index 000000000000..86b2cd9e1e3d --- /dev/null +++ b/tests/testflows/ldap/external_user_directory/configs/clickhouse/users.xml @@ -0,0 +1,133 @@ + + + + + + + + 10000000000 + + + 0 + + + random + + + + + 1 + + + + + + + + + + + + + ::/0 + + + + default + + + default + + + 1 + + + + + + + + + + + + + + + + + 3600 + + + 0 + 0 + 0 + 0 + 0 + + + + diff --git a/tests/testflows/ldap/external_user_directory/configs/clickhouse1/config.d/macros.xml b/tests/testflows/ldap/external_user_directory/configs/clickhouse1/config.d/macros.xml index c59a85cad8f4..6cdcc1b440c3 100644 --- a/tests/testflows/ldap/external_user_directory/configs/clickhouse1/config.d/macros.xml +++ b/tests/testflows/ldap/external_user_directory/configs/clickhouse1/config.d/macros.xml @@ -1,8 +1,8 @@ - + clickhouse1 01 01 - + diff --git a/tests/testflows/ldap/external_user_directory/configs/clickhouse2/config.d/macros.xml b/tests/testflows/ldap/external_user_directory/configs/clickhouse2/config.d/macros.xml index 1f880da0f653..a114a9ce4ab1 100644 --- a/tests/testflows/ldap/external_user_directory/configs/clickhouse2/config.d/macros.xml +++ b/tests/testflows/ldap/external_user_directory/configs/clickhouse2/config.d/macros.xml @@ -1,8 +1,8 @@ - + clickhouse2 01 02 - + diff --git a/tests/testflows/ldap/external_user_directory/configs/clickhouse3/config.d/macros.xml b/tests/testflows/ldap/external_user_directory/configs/clickhouse3/config.d/macros.xml index d2f1dbafa046..904a27b01723 100644 --- a/tests/testflows/ldap/external_user_directory/configs/clickhouse3/config.d/macros.xml +++ b/tests/testflows/ldap/external_user_directory/configs/clickhouse3/config.d/macros.xml @@ -1,8 +1,8 @@ - + clickhouse3 01 03 - + diff --git a/tests/testflows/ldap/external_user_directory/external_user_directory_env/clickhouse-service.yml b/tests/testflows/ldap/external_user_directory/external_user_directory_env/clickhouse-service.yml index f8cc0a62c67c..fe495187f5ec 100644 --- a/tests/testflows/ldap/external_user_directory/external_user_directory_env/clickhouse-service.yml +++ b/tests/testflows/ldap/external_user_directory/external_user_directory_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: altinityinfra/integration-test + image: ${IMAGE_DEPENDENCY_PROXY}clickhouse/clickhouse-integration-test:28741 init: true expose: - "9000" diff --git a/tests/testflows/ldap/external_user_directory/external_user_directory_env/docker-compose.yml b/tests/testflows/ldap/external_user_directory/external_user_directory_env/docker-compose.yml index 36e25ef766e5..cb4c6e696729 100644 --- a/tests/testflows/ldap/external_user_directory/external_user_directory_env/docker-compose.yml +++ b/tests/testflows/ldap/external_user_directory/external_user_directory_env/docker-compose.yml @@ -138,7 +138,7 @@ services: # dummy service which does nothing, but allows to postpone # 'docker-compose up -d' till all dependecies will go healthy all_services_ready: - image: hello-world + image: ${IMAGE_DEPENDENCY_PROXY}hello-world depends_on: clickhouse1: condition: service_healthy diff --git a/tests/testflows/ldap/external_user_directory/external_user_directory_env/openldap-service.yml b/tests/testflows/ldap/external_user_directory/external_user_directory_env/openldap-service.yml index 606ea3f723fc..fe33082cd53b 100644 --- a/tests/testflows/ldap/external_user_directory/external_user_directory_env/openldap-service.yml +++ b/tests/testflows/ldap/external_user_directory/external_user_directory_env/openldap-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: openldap: - image: osixia/openldap:1.4.0 + image: ${IMAGE_DEPENDENCY_PROXY}osixia/openldap:1.4.0 command: "--copy-service --loglevel debug" environment: LDAP_ORGANIZATION: "company" @@ -22,7 +22,7 @@ services: - label:disable phpldapadmin: - image: osixia/phpldapadmin:0.9.0 + image: ${IMAGE_DEPENDENCY_PROXY}osixia/phpldapadmin:0.9.0 environment: PHPLDAPADMIN_HTTPS=false: healthcheck: diff --git a/tests/testflows/ldap/external_user_directory/external_user_directory_env/zookeeper-service.yml b/tests/testflows/ldap/external_user_directory/external_user_directory_env/zookeeper-service.yml index 6691a2df31c1..33cc799ab4cb 100644 --- a/tests/testflows/ldap/external_user_directory/external_user_directory_env/zookeeper-service.yml +++ b/tests/testflows/ldap/external_user_directory/external_user_directory_env/zookeeper-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: zookeeper: - image: zookeeper:3.4.12 + image: ${IMAGE_DEPENDENCY_PROXY}zookeeper:3.6.2 expose: - "2181" environment: diff --git a/tests/testflows/ldap/external_user_directory/external_user_directory_env_arm64/docker-compose.yml b/tests/testflows/ldap/external_user_directory/external_user_directory_env_arm64/docker-compose.yml index 36e25ef766e5..cb4c6e696729 100644 --- a/tests/testflows/ldap/external_user_directory/external_user_directory_env_arm64/docker-compose.yml +++ b/tests/testflows/ldap/external_user_directory/external_user_directory_env_arm64/docker-compose.yml @@ -138,7 +138,7 @@ services: # dummy service which does nothing, but allows to postpone # 'docker-compose up -d' till all dependecies will go healthy all_services_ready: - image: hello-world + image: ${IMAGE_DEPENDENCY_PROXY}hello-world depends_on: clickhouse1: condition: service_healthy diff --git a/tests/testflows/ldap/external_user_directory/external_user_directory_env_arm64/openldap-service.yml b/tests/testflows/ldap/external_user_directory/external_user_directory_env_arm64/openldap-service.yml index 606ea3f723fc..fe33082cd53b 100644 --- a/tests/testflows/ldap/external_user_directory/external_user_directory_env_arm64/openldap-service.yml +++ b/tests/testflows/ldap/external_user_directory/external_user_directory_env_arm64/openldap-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: openldap: - image: osixia/openldap:1.4.0 + image: ${IMAGE_DEPENDENCY_PROXY}osixia/openldap:1.4.0 command: "--copy-service --loglevel debug" environment: LDAP_ORGANIZATION: "company" @@ -22,7 +22,7 @@ services: - label:disable phpldapadmin: - image: osixia/phpldapadmin:0.9.0 + image: ${IMAGE_DEPENDENCY_PROXY}osixia/phpldapadmin:0.9.0 environment: PHPLDAPADMIN_HTTPS=false: healthcheck: diff --git a/tests/testflows/ldap/external_user_directory/external_user_directory_env_arm64/zookeeper-service.yml b/tests/testflows/ldap/external_user_directory/external_user_directory_env_arm64/zookeeper-service.yml index 6691a2df31c1..33cc799ab4cb 100644 --- a/tests/testflows/ldap/external_user_directory/external_user_directory_env_arm64/zookeeper-service.yml +++ b/tests/testflows/ldap/external_user_directory/external_user_directory_env_arm64/zookeeper-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: zookeeper: - image: zookeeper:3.4.12 + image: ${IMAGE_DEPENDENCY_PROXY}zookeeper:3.6.2 expose: - "2181" environment: diff --git a/tests/testflows/ldap/external_user_directory/regression.py b/tests/testflows/ldap/external_user_directory/regression.py index de53bf9128e1..da2be345759e 100755 --- a/tests/testflows/ldap/external_user_directory/regression.py +++ b/tests/testflows/ldap/external_user_directory/regression.py @@ -55,9 +55,7 @@ @Requirements(RQ_SRS_009_LDAP_ExternalUserDirectory_Authentication("1.0")) @XFails(xfails) @FFails(ffails) -def regression( - self, local, clickhouse_binary_path, clickhouse_version=None, stress=None -): +def regression(self, local, clickhouse_binary_path, clickhouse_version, stress=None): """ClickHouse LDAP external user directory regression module.""" nodes = { "clickhouse": ("clickhouse1", "clickhouse2", "clickhouse3"), diff --git a/tests/testflows/ldap/external_user_directory/requirements/requirements.md b/tests/testflows/ldap/external_user_directory/requirements/requirements.md index 44c9bdf4c673..8159df710bb7 100644 --- a/tests/testflows/ldap/external_user_directory/requirements/requirements.md +++ b/tests/testflows/ldap/external_user_directory/requirements/requirements.md @@ -611,7 +611,7 @@ version: 2.0 configuration file or of any configuration file inside the `config.d` directory. ```xml - + localhost 636 @@ -627,7 +627,7 @@ configuration file or of any configuration file inside the `config.d` directory. /path/to/tls_ca_cert_dir ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:AES256-GCM-SHA384 - + ``` ##### RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.LDAPUserDirectory @@ -649,7 +649,7 @@ version: 1.0 [ClickHouse] SHALL support `` section with the following syntax ```xml - + my_ldap_server @@ -659,7 +659,7 @@ version: 1.0 - + ``` ##### RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server diff --git a/tests/testflows/ldap/external_user_directory/requirements/requirements.py b/tests/testflows/ldap/external_user_directory/requirements/requirements.py index e15cc7a034ef..8fdee3be16b0 100644 --- a/tests/testflows/ldap/external_user_directory/requirements/requirements.py +++ b/tests/testflows/ldap/external_user_directory/requirements/requirements.py @@ -1234,7 +1234,7 @@ "configuration file or of any configuration file inside the `config.d` directory.\n" "\n" "```xml\n" - "\n" + "\n" " \n" " localhost\n" " 636\n" @@ -1250,7 +1250,7 @@ " /path/to/tls_ca_cert_dir\n" " ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:AES256-GCM-SHA384\n" " \n" - "\n" + "\n" "```\n" "\n" ), @@ -1305,7 +1305,7 @@ "[ClickHouse] SHALL support `` section with the following syntax\n" "\n" "```xml\n" - "\n" + "\n" " \n" " \n" " my_ldap_server\n" @@ -1315,7 +1315,7 @@ " \n" " \n" " \n" - "\n" + "\n" "```\n" "\n" ), @@ -2862,7 +2862,7 @@ configuration file or of any configuration file inside the `config.d` directory. ```xml - + localhost 636 @@ -2878,7 +2878,7 @@ /path/to/tls_ca_cert_dir ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:AES256-GCM-SHA384 - + ``` ##### RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.LDAPUserDirectory @@ -2900,7 +2900,7 @@ [ClickHouse] SHALL support `` section with the following syntax ```xml - + my_ldap_server @@ -2910,7 +2910,7 @@ - + ``` ##### RQ.SRS-009.LDAP.ExternalUserDirectory.Configuration.Users.Parameters.Server diff --git a/tests/testflows/ldap/external_user_directory/tests/common.py b/tests/testflows/ldap/external_user_directory/tests/common.py index 871be815a359..348abed002fa 100644 --- a/tests/testflows/ldap/external_user_directory/tests/common.py +++ b/tests/testflows/ldap/external_user_directory/tests/common.py @@ -129,9 +129,7 @@ def create_entries_ldap_external_user_directory_config_content( path = os.path.join(config_d_dir, config_file) name = config_file - root = xmltree.fromstring( - "" - ) + root = xmltree.fromstring("") xml_user_directories = root.find("user_directories") xml_user_directories.append( xmltree.Comment(text=f"LDAP external user directories {uid}") diff --git a/tests/testflows/ldap/external_user_directory/tests/server_config.py b/tests/testflows/ldap/external_user_directory/tests/server_config.py index a26713e28cfb..0e6b454fd1c8 100644 --- a/tests/testflows/ldap/external_user_directory/tests/server_config.py +++ b/tests/testflows/ldap/external_user_directory/tests/server_config.py @@ -392,7 +392,7 @@ def invalid_verification_cooldown_value(self, invalid_value, timeout=300): def syntax(self): """Check that server configuration with valid syntax can be loaded. ```xml - + localhost 636 @@ -408,7 +408,7 @@ def syntax(self): /path/to/tls_ca_cert_dir ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:AES256-GCM-SHA384 - + ``` """ servers = { diff --git a/tests/testflows/ldap/regression.py b/tests/testflows/ldap/regression.py index 5b3ea30ef73f..661410dc44a9 100755 --- a/tests/testflows/ldap/regression.py +++ b/tests/testflows/ldap/regression.py @@ -10,9 +10,7 @@ @TestModule @Name("ldap") @ArgumentParser(argparser) -def regression( - self, local, clickhouse_binary_path, clickhouse_version=None, stress=None -): +def regression(self, local, clickhouse_binary_path, clickhouse_version, stress=None): """ClickHouse LDAP integration regression module.""" args = { "local": local, diff --git a/tests/testflows/ldap/role_mapping/configs/clickhouse/common.xml b/tests/testflows/ldap/role_mapping/configs/clickhouse/common.xml new file mode 100644 index 000000000000..df952b28c82d --- /dev/null +++ b/tests/testflows/ldap/role_mapping/configs/clickhouse/common.xml @@ -0,0 +1,6 @@ + + Europe/Moscow + 0.0.0.0 + /var/lib/clickhouse/ + /var/lib/clickhouse/tmp/ + diff --git a/tests/testflows/ldap/role_mapping/configs/clickhouse/config.d/logs.xml b/tests/testflows/ldap/role_mapping/configs/clickhouse/config.d/logs.xml index 2ee8bb55f38c..bdf1bbc11c11 100644 --- a/tests/testflows/ldap/role_mapping/configs/clickhouse/config.d/logs.xml +++ b/tests/testflows/ldap/role_mapping/configs/clickhouse/config.d/logs.xml @@ -1,4 +1,4 @@ - + 3 trace @@ -14,4 +14,4 @@ part_log
500 -
+ diff --git a/tests/testflows/ldap/role_mapping/configs/clickhouse/config.d/ports.xml b/tests/testflows/ldap/role_mapping/configs/clickhouse/config.d/ports.xml index 1e061e2252e1..fbc6cea74c0c 100644 --- a/tests/testflows/ldap/role_mapping/configs/clickhouse/config.d/ports.xml +++ b/tests/testflows/ldap/role_mapping/configs/clickhouse/config.d/ports.xml @@ -1,5 +1,5 @@ - + 8443 9440 - \ No newline at end of file + \ No newline at end of file diff --git a/tests/testflows/ldap/role_mapping/configs/clickhouse/config.d/remote.xml b/tests/testflows/ldap/role_mapping/configs/clickhouse/config.d/remote.xml index 04066290061b..51be2a6e8e3b 100644 --- a/tests/testflows/ldap/role_mapping/configs/clickhouse/config.d/remote.xml +++ b/tests/testflows/ldap/role_mapping/configs/clickhouse/config.d/remote.xml @@ -1,5 +1,5 @@ - + @@ -104,4 +104,4 @@ - + diff --git a/tests/testflows/ldap/role_mapping/configs/clickhouse/config.d/ssl.xml b/tests/testflows/ldap/role_mapping/configs/clickhouse/config.d/ssl.xml index c90b60f98b6d..16d82440b89e 100644 --- a/tests/testflows/ldap/role_mapping/configs/clickhouse/config.d/ssl.xml +++ b/tests/testflows/ldap/role_mapping/configs/clickhouse/config.d/ssl.xml @@ -1,4 +1,4 @@ - + /etc/clickhouse-server/ssl/server.crt @@ -15,4 +15,4 @@ - + diff --git a/tests/testflows/ldap/role_mapping/configs/clickhouse/config.d/storage.xml b/tests/testflows/ldap/role_mapping/configs/clickhouse/config.d/storage.xml index 0c53fd705937..618fd6b6d24a 100644 --- a/tests/testflows/ldap/role_mapping/configs/clickhouse/config.d/storage.xml +++ b/tests/testflows/ldap/role_mapping/configs/clickhouse/config.d/storage.xml @@ -1,4 +1,4 @@ - + @@ -17,4 +17,4 @@ - + diff --git a/tests/testflows/ldap/role_mapping/configs/clickhouse/config.d/zookeeper.xml b/tests/testflows/ldap/role_mapping/configs/clickhouse/config.d/zookeeper.xml index 1d5c0b6cb8b5..96270e7b645a 100644 --- a/tests/testflows/ldap/role_mapping/configs/clickhouse/config.d/zookeeper.xml +++ b/tests/testflows/ldap/role_mapping/configs/clickhouse/config.d/zookeeper.xml @@ -1,5 +1,5 @@ - + zookeeper @@ -7,4 +7,4 @@ 15000 - + diff --git a/tests/testflows/ldap/role_mapping/configs/clickhouse/config.xml b/tests/testflows/ldap/role_mapping/configs/clickhouse/config.xml new file mode 100644 index 000000000000..26ac36232cc5 --- /dev/null +++ b/tests/testflows/ldap/role_mapping/configs/clickhouse/config.xml @@ -0,0 +1,442 @@ + + + + + + trace + /var/log/clickhouse-server/clickhouse-server.log + /var/log/clickhouse-server/clickhouse-server.err.log + 1000M + 10 + + + + 8123 + 9000 + + + + + + + + + /etc/clickhouse-server/server.crt + /etc/clickhouse-server/server.key + + /etc/clickhouse-server/dhparam.pem + none + true + true + sslv2,sslv3 + true + + + + true + true + sslv2,sslv3 + true + + + + RejectCertificateHandler + + + + + + + + + 9009 + + + + + + + + + + + + + + + + + + + + 4096 + 3 + + + 100 + + + + + + 8589934592 + + + 5368709120 + + + + /var/lib/clickhouse/ + + + /var/lib/clickhouse/tmp/ + + + /var/lib/clickhouse/user_files/ + + + + + + users.xml + + + + /var/lib/clickhouse/access/ + + + + + default + + + + + + default + + + + + + + + + false + + + + + + + + localhost + 9000 + + + + + + + localhost + 9000 + + + + + localhost + 9000 + + + + + + + localhost + 9440 + 1 + + + + + + + localhost + 9000 + + + + + localhost + 1 + + + + + + + + + + + + + + + + + 3600 + + + + 3600 + + + 60 + + + + + + + + + + system + query_log
+ + toYYYYMM(event_date) + + 7500 +
+ + + + system + trace_log
+ + toYYYYMM(event_date) + 7500 +
+ + + + system + query_thread_log
+ toYYYYMM(event_date) + 7500 +
+ + + + system + part_log
+ toYYYYMM(event_date) + 7500 +
+ + + + + + + + + + + + + + *_dictionary.xml + + + + + + + + + + /clickhouse/task_queue/ddl + + + + + + + + + + + + + + + + click_cost + any + + 0 + 3600 + + + 86400 + 7200 + + + + max + + 0 + 60 + + + 3600 + 300 + + + 86400 + 3600 + + + + + + /var/lib/clickhouse/format_schemas/ + + + +
diff --git a/tests/testflows/ldap/role_mapping/configs/clickhouse/users.d/common.xml b/tests/testflows/ldap/role_mapping/configs/clickhouse/users.d/common.xml index f6e0553ca33e..1b1a3a4a5ac8 100644 --- a/tests/testflows/ldap/role_mapping/configs/clickhouse/users.d/common.xml +++ b/tests/testflows/ldap/role_mapping/configs/clickhouse/users.d/common.xml @@ -1,4 +1,4 @@ - + - \ No newline at end of file + \ No newline at end of file diff --git a/tests/testflows/ldap/role_mapping/configs/clickhouse/users.xml b/tests/testflows/ldap/role_mapping/configs/clickhouse/users.xml new file mode 100644 index 000000000000..86b2cd9e1e3d --- /dev/null +++ b/tests/testflows/ldap/role_mapping/configs/clickhouse/users.xml @@ -0,0 +1,133 @@ + + + + + + + + 10000000000 + + + 0 + + + random + + + + + 1 + + + + + + + + + + + + + ::/0 + + + + default + + + default + + + 1 + + + + + + + + + + + + + + + + + 3600 + + + 0 + 0 + 0 + 0 + 0 + + + + diff --git a/tests/testflows/ldap/role_mapping/configs/clickhouse1/config.d/macros.xml b/tests/testflows/ldap/role_mapping/configs/clickhouse1/config.d/macros.xml index c59a85cad8f4..6cdcc1b440c3 100644 --- a/tests/testflows/ldap/role_mapping/configs/clickhouse1/config.d/macros.xml +++ b/tests/testflows/ldap/role_mapping/configs/clickhouse1/config.d/macros.xml @@ -1,8 +1,8 @@ - + clickhouse1 01 01 - + diff --git a/tests/testflows/ldap/role_mapping/configs/clickhouse2/config.d/macros.xml b/tests/testflows/ldap/role_mapping/configs/clickhouse2/config.d/macros.xml index 1f880da0f653..a114a9ce4ab1 100644 --- a/tests/testflows/ldap/role_mapping/configs/clickhouse2/config.d/macros.xml +++ b/tests/testflows/ldap/role_mapping/configs/clickhouse2/config.d/macros.xml @@ -1,8 +1,8 @@ - + clickhouse2 01 02 - + diff --git a/tests/testflows/ldap/role_mapping/configs/clickhouse3/config.d/macros.xml b/tests/testflows/ldap/role_mapping/configs/clickhouse3/config.d/macros.xml index d2f1dbafa046..904a27b01723 100644 --- a/tests/testflows/ldap/role_mapping/configs/clickhouse3/config.d/macros.xml +++ b/tests/testflows/ldap/role_mapping/configs/clickhouse3/config.d/macros.xml @@ -1,8 +1,8 @@ - + clickhouse3 01 03 - + diff --git a/tests/testflows/ldap/role_mapping/regression.py b/tests/testflows/ldap/role_mapping/regression.py index fc2b85dba6f2..cdca1e61f436 100755 --- a/tests/testflows/ldap/role_mapping/regression.py +++ b/tests/testflows/ldap/role_mapping/regression.py @@ -38,9 +38,7 @@ @Requirements(RQ_SRS_014_LDAP_RoleMapping("1.0")) @XFails(xfails) @FFails(ffails) -def regression( - self, local, clickhouse_binary_path, clickhouse_version=None, stress=None -): +def regression(self, local, clickhouse_binary_path, clickhouse_version, stress=None): """ClickHouse LDAP role mapping regression module.""" nodes = { "clickhouse": ("clickhouse1", "clickhouse2", "clickhouse3"), diff --git a/tests/testflows/ldap/role_mapping/requirements/requirements.md b/tests/testflows/ldap/role_mapping/requirements/requirements.md index a9414749be37..75fa4feffff2 100644 --- a/tests/testflows/ldap/role_mapping/requirements/requirements.md +++ b/tests/testflows/ldap/role_mapping/requirements/requirements.md @@ -308,7 +308,7 @@ with the actual user name during each authentication attempt. For example, ```xml - + @@ -316,7 +316,7 @@ For example, - + ``` ##### RQ.SRS-014.LDAP.RoleMapping.Configuration.Server.BindDN.ConflictWith.AuthDN @@ -399,7 +399,7 @@ of the `config.xml`. For example, ```xml - + @@ -412,7 +412,7 @@ For example, - + ``` #### Special Characters Escaping @@ -563,7 +563,7 @@ cluster is configured with and without ``. For example, ```xml - + qwerty123 @@ -583,13 +583,13 @@ For example, - + ``` or ```xml - + @@ -608,7 +608,7 @@ or - + ``` ## References diff --git a/tests/testflows/ldap/role_mapping/requirements/requirements.py b/tests/testflows/ldap/role_mapping/requirements/requirements.py index e63e8593e995..dc1790f09b32 100644 --- a/tests/testflows/ldap/role_mapping/requirements/requirements.py +++ b/tests/testflows/ldap/role_mapping/requirements/requirements.py @@ -482,7 +482,7 @@ "For example, \n" "\n" "```xml\n" - "\n" + "\n" " \n" " \n" " \n" @@ -490,7 +490,7 @@ " \n" " \n" " \n" - "\n" + "\n" "```\n" "\n" ), @@ -633,7 +633,7 @@ "For example,\n" "\n" "```xml\n" - "\n" + "\n" " \n" " \n" " \n" @@ -646,7 +646,7 @@ " \n" " \n" " \n" - "\n" + "\n" "```\n" "\n" ), @@ -979,7 +979,7 @@ "For example,\n" "\n" "```xml\n" - "\n" + "\n" " \n" " \n" " qwerty123\n" @@ -999,13 +999,13 @@ " \n" " \n" " \n" - "\n" + "\n" "```\n" "\n" "or \n" "\n" "```xml\n" - "\n" + "\n" " \n" " \n" " \n" @@ -1024,7 +1024,7 @@ " \n" " \n" " \n" - "\n" + "\n" "```\n" "\n" ), @@ -1669,7 +1669,7 @@ For example, ```xml - + @@ -1677,7 +1677,7 @@ - + ``` ##### RQ.SRS-014.LDAP.RoleMapping.Configuration.Server.BindDN.ConflictWith.AuthDN @@ -1760,7 +1760,7 @@ For example, ```xml - + @@ -1773,7 +1773,7 @@ - + ``` #### Special Characters Escaping @@ -1924,7 +1924,7 @@ For example, ```xml - + qwerty123 @@ -1944,13 +1944,13 @@ - + ``` or ```xml - + @@ -1969,7 +1969,7 @@ - + ``` ## References diff --git a/tests/testflows/ldap/role_mapping/role_mapping_env/clickhouse-service.yml b/tests/testflows/ldap/role_mapping/role_mapping_env/clickhouse-service.yml index 3fe80bfce343..1875e0659f4e 100644 --- a/tests/testflows/ldap/role_mapping/role_mapping_env/clickhouse-service.yml +++ b/tests/testflows/ldap/role_mapping/role_mapping_env/clickhouse-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: clickhouse: - image: altinityinfra/integration-test + image: ${IMAGE_DEPENDENCY_PROXY}clickhouse/clickhouse-integration-test:28741 init: true expose: - "9000" diff --git a/tests/testflows/ldap/role_mapping/role_mapping_env/docker-compose.yml b/tests/testflows/ldap/role_mapping/role_mapping_env/docker-compose.yml index 624a5a184986..284273ca47dd 100644 --- a/tests/testflows/ldap/role_mapping/role_mapping_env/docker-compose.yml +++ b/tests/testflows/ldap/role_mapping/role_mapping_env/docker-compose.yml @@ -135,7 +135,7 @@ services: # dummy service which does nothing, but allows to postpone # 'docker-compose up -d' till all dependecies will go healthy all_services_ready: - image: hello-world + image: ${IMAGE_DEPENDENCY_PROXY}hello-world depends_on: clickhouse1: condition: service_healthy diff --git a/tests/testflows/ldap/role_mapping/role_mapping_env/openldap-service.yml b/tests/testflows/ldap/role_mapping/role_mapping_env/openldap-service.yml index 606ea3f723fc..fe33082cd53b 100644 --- a/tests/testflows/ldap/role_mapping/role_mapping_env/openldap-service.yml +++ b/tests/testflows/ldap/role_mapping/role_mapping_env/openldap-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: openldap: - image: osixia/openldap:1.4.0 + image: ${IMAGE_DEPENDENCY_PROXY}osixia/openldap:1.4.0 command: "--copy-service --loglevel debug" environment: LDAP_ORGANIZATION: "company" @@ -22,7 +22,7 @@ services: - label:disable phpldapadmin: - image: osixia/phpldapadmin:0.9.0 + image: ${IMAGE_DEPENDENCY_PROXY}osixia/phpldapadmin:0.9.0 environment: PHPLDAPADMIN_HTTPS=false: healthcheck: diff --git a/tests/testflows/ldap/role_mapping/role_mapping_env/zookeeper-service.yml b/tests/testflows/ldap/role_mapping/role_mapping_env/zookeeper-service.yml index 6691a2df31c1..33cc799ab4cb 100644 --- a/tests/testflows/ldap/role_mapping/role_mapping_env/zookeeper-service.yml +++ b/tests/testflows/ldap/role_mapping/role_mapping_env/zookeeper-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: zookeeper: - image: zookeeper:3.4.12 + image: ${IMAGE_DEPENDENCY_PROXY}zookeeper:3.6.2 expose: - "2181" environment: diff --git a/tests/testflows/ldap/role_mapping/role_mapping_env_arm64/docker-compose.yml b/tests/testflows/ldap/role_mapping/role_mapping_env_arm64/docker-compose.yml index 624a5a184986..284273ca47dd 100644 --- a/tests/testflows/ldap/role_mapping/role_mapping_env_arm64/docker-compose.yml +++ b/tests/testflows/ldap/role_mapping/role_mapping_env_arm64/docker-compose.yml @@ -135,7 +135,7 @@ services: # dummy service which does nothing, but allows to postpone # 'docker-compose up -d' till all dependecies will go healthy all_services_ready: - image: hello-world + image: ${IMAGE_DEPENDENCY_PROXY}hello-world depends_on: clickhouse1: condition: service_healthy diff --git a/tests/testflows/ldap/role_mapping/role_mapping_env_arm64/openldap-service.yml b/tests/testflows/ldap/role_mapping/role_mapping_env_arm64/openldap-service.yml index 606ea3f723fc..fe33082cd53b 100644 --- a/tests/testflows/ldap/role_mapping/role_mapping_env_arm64/openldap-service.yml +++ b/tests/testflows/ldap/role_mapping/role_mapping_env_arm64/openldap-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: openldap: - image: osixia/openldap:1.4.0 + image: ${IMAGE_DEPENDENCY_PROXY}osixia/openldap:1.4.0 command: "--copy-service --loglevel debug" environment: LDAP_ORGANIZATION: "company" @@ -22,7 +22,7 @@ services: - label:disable phpldapadmin: - image: osixia/phpldapadmin:0.9.0 + image: ${IMAGE_DEPENDENCY_PROXY}osixia/phpldapadmin:0.9.0 environment: PHPLDAPADMIN_HTTPS=false: healthcheck: diff --git a/tests/testflows/ldap/role_mapping/role_mapping_env_arm64/zookeeper-service.yml b/tests/testflows/ldap/role_mapping/role_mapping_env_arm64/zookeeper-service.yml index 6691a2df31c1..33cc799ab4cb 100644 --- a/tests/testflows/ldap/role_mapping/role_mapping_env_arm64/zookeeper-service.yml +++ b/tests/testflows/ldap/role_mapping/role_mapping_env_arm64/zookeeper-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: zookeeper: - image: zookeeper:3.4.12 + image: ${IMAGE_DEPENDENCY_PROXY}zookeeper:3.6.2 expose: - "2181" environment: diff --git a/tests/testflows/map_type/configs/clickhouse/config.d/macros.xml b/tests/testflows/map_type/configs/clickhouse/config.d/macros.xml new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/testflows/map_type/configs/clickhouse/config.xml b/tests/testflows/map_type/configs/clickhouse/config.xml new file mode 100644 index 000000000000..842a0573d49b --- /dev/null +++ b/tests/testflows/map_type/configs/clickhouse/config.xml @@ -0,0 +1,448 @@ + + + + + + trace + /var/log/clickhouse-server/clickhouse-server.log + /var/log/clickhouse-server/clickhouse-server.err.log + 1000M + 10 + + + + 8123 + 9000 + + + + + + + + + /etc/clickhouse-server/server.crt + /etc/clickhouse-server/server.key + + /etc/clickhouse-server/dhparam.pem + none + true + true + sslv2,sslv3 + true + + + + true + true + sslv2,sslv3 + true + + + + RejectCertificateHandler + + + + + + + + + 9009 + + + + + + + + 0.0.0.0 + + + + + + + + + + + + 4096 + 3 + + + 100 + + + + + + 8589934592 + + + 5368709120 + + + + /var/lib/clickhouse/ + + + /var/lib/clickhouse/tmp/ + + + /var/lib/clickhouse/user_files/ + + + /var/lib/clickhouse/access/ + + + + + + users.xml + + + + /var/lib/clickhouse/access/ + + + + + users.xml + + + default + + + + + + default + + + + + + + + + false + + + + + + + + localhost + 9000 + + + + + + + localhost + 9000 + + + + + localhost + 9000 + + + + + + + localhost + 9440 + 1 + + + + + + + localhost + 9000 + + + + + localhost + 1 + + + + + + + + + + + + + + + + + 3600 + + + + 3600 + + + 60 + + + + + + + + + + system + query_log
+ + toYYYYMM(event_date) + + 7500 +
+ + + + system + trace_log
+ + toYYYYMM(event_date) + 7500 +
+ + + + system + query_thread_log
+ toYYYYMM(event_date) + 7500 +
+ + + + system + part_log
+ toYYYYMM(event_date) + 7500 +
+ + + + + + + + + + + + + + *_dictionary.xml + + + + + + + + + + /clickhouse/task_queue/ddl + + + + + + + + + + + + + + + + click_cost + any + + 0 + 3600 + + + 86400 + 60 + + + + max + + 0 + 60 + + + 3600 + 300 + + + 86400 + 3600 + + + + + + /var/lib/clickhouse/format_schemas/ + + + +
diff --git a/tests/testflows/map_type/configs/clickhouse/users.xml b/tests/testflows/map_type/configs/clickhouse/users.xml new file mode 100644 index 000000000000..c7d0ecae6931 --- /dev/null +++ b/tests/testflows/map_type/configs/clickhouse/users.xml @@ -0,0 +1,133 @@ + + + + + + + + 10000000000 + + + 0 + + + random + + + + + 1 + + + + + + + + + + + + + ::/0 + + + + default + + + default + + + 1 + + + + + + + + + + + + + + + + + 3600 + + + 0 + 0 + 0 + 0 + 0 + + + + diff --git a/tests/testflows/map_type/map_type_env/clickhouse-service.yml b/tests/testflows/map_type/map_type_env/clickhouse-service.yml index 9162d06bf27d..4d5f827ee30c 100755 --- a/tests/testflows/map_type/map_type_env/clickhouse-service.yml +++ b/tests/testflows/map_type/map_type_env/clickhouse-service.yml @@ -2,21 +2,24 @@ version: '2.3' services: clickhouse: - image: altinityinfra/integration-test + image: ${IMAGE_DEPENDENCY_PROXY}clickhouse/clickhouse-integration-test:28741 + init: true expose: - "9000" - "9009" - "8123" volumes: - - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d:/etc/clickhouse-server/config.d" - - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/users.d:/etc/clickhouse-server/users.d" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/logs.xml:/etc/clickhouse-server/config.d/logs.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/remote.xml:/etc/clickhouse-server/config.d/remote.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/macros.xml:/etc/clickhouse-server/config.d/macros.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/zookeeper.xml:/etc/clickhouse-server/config.d/zookeeper.xml" - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.xml:/etc/clickhouse-server/config.xml" - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/users.xml:/etc/clickhouse-server/users.xml" - "${CLICKHOUSE_TESTS_SERVER_BIN_PATH:-/usr/bin/clickhouse}:/usr/bin/clickhouse" - "${CLICKHOUSE_TESTS_ODBC_BRIDGE_BIN_PATH:-/usr/bin/clickhouse-odbc-bridge}:/usr/bin/clickhouse-odbc-bridge" - entrypoint: bash -c "clickhouse server --config-file=/etc/clickhouse-server/config.xml --log-file=/var/log/clickhouse-server/clickhouse-server.log --errorlog-file=/var/log/clickhouse-server/clickhouse-server.err.log" + entrypoint: bash -c "tail -f /dev/null" healthcheck: - test: clickhouse client --query='select 1' + test: echo 1 interval: 10s timeout: 10s retries: 3 diff --git a/tests/testflows/map_type/map_type_env/docker-compose.yml b/tests/testflows/map_type/map_type_env/docker-compose.yml index 29f2ef524706..c9ae33d78848 100755 --- a/tests/testflows/map_type/map_type_env/docker-compose.yml +++ b/tests/testflows/map_type/map_type_env/docker-compose.yml @@ -48,7 +48,7 @@ services: # dummy service which does nothing, but allows to postpone # 'docker-compose up -d' till all dependecies will go healthy all_services_ready: - image: hello-world + image: ${IMAGE_DEPENDENCY_PROXY}hello-world depends_on: clickhouse1: condition: service_healthy diff --git a/tests/testflows/map_type/map_type_env/zookeeper-service.yml b/tests/testflows/map_type/map_type_env/zookeeper-service.yml index f3df33358be7..60c0e4e7de37 100755 --- a/tests/testflows/map_type/map_type_env/zookeeper-service.yml +++ b/tests/testflows/map_type/map_type_env/zookeeper-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: zookeeper: - image: zookeeper:3.4.12 + image: ${IMAGE_DEPENDENCY_PROXY}zookeeper:3.6.2 expose: - "2181" environment: diff --git a/tests/testflows/map_type/map_type_env_arm64/clickhouse-service.yml b/tests/testflows/map_type/map_type_env_arm64/clickhouse-service.yml new file mode 100755 index 000000000000..6de5d62e6c0d --- /dev/null +++ b/tests/testflows/map_type/map_type_env_arm64/clickhouse-service.yml @@ -0,0 +1,30 @@ +version: '2.3' + +services: + clickhouse: + image: registry.gitlab.com/altinity-public/container-images/test/clickhouse-integration-test:21.12 + privileged: true + expose: + - "9000" + - "9009" + - "8123" + volumes: + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/logs.xml:/etc/clickhouse-server/config.d/logs.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/macros.xml:/etc/clickhouse-server/config.d/macros.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/remote.xml:/etc/clickhouse-server/config.d/remote.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/zookeeper.xml:/etc/clickhouse-server/config.d/zookeeper.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.xml:/etc/clickhouse-server/config.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/users.xml:/etc/clickhouse-server/users.xml" + - "${CLICKHOUSE_TESTS_SERVER_BIN_PATH:-/usr/bin/clickhouse}:/usr/bin/clickhouse" + - "${CLICKHOUSE_TESTS_ODBC_BRIDGE_BIN_PATH:-/usr/bin/clickhouse-odbc-bridge}:/usr/bin/clickhouse-odbc-bridge" + entrypoint: bash -c "clickhouse server --config-file=/etc/clickhouse-server/config.xml --log-file=/var/log/clickhouse-server/clickhouse-server.log --errorlog-file=/var/log/clickhouse-server/clickhouse-server.err.log" + healthcheck: + test: clickhouse client --query='select 1' + interval: 10s + timeout: 10s + retries: 3 + start_period: 300s + cap_add: + - SYS_PTRACE + security_opt: + - label:disable diff --git a/tests/testflows/map_type/map_type_env_arm64/docker-compose.yml b/tests/testflows/map_type/map_type_env_arm64/docker-compose.yml new file mode 100755 index 000000000000..c9ae33d78848 --- /dev/null +++ b/tests/testflows/map_type/map_type_env_arm64/docker-compose.yml @@ -0,0 +1,60 @@ +version: '2.3' + +services: + zookeeper: + extends: + file: zookeeper-service.yml + service: zookeeper + + clickhouse1: + extends: + file: clickhouse-service.yml + service: clickhouse + hostname: clickhouse1 + volumes: + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse1/database/:/var/lib/clickhouse/" + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse1/logs/:/var/log/clickhouse-server/" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse1/config.d/macros.xml:/etc/clickhouse-server/config.d/macros.xml" + depends_on: + zookeeper: + condition: service_healthy + + clickhouse2: + extends: + file: clickhouse-service.yml + service: clickhouse + hostname: clickhouse2 + volumes: + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse2/database/:/var/lib/clickhouse/" + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse2/logs/:/var/log/clickhouse-server/" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse2/config.d/macros.xml:/etc/clickhouse-server/config.d/macros.xml" + depends_on: + zookeeper: + condition: service_healthy + + clickhouse3: + extends: + file: clickhouse-service.yml + service: clickhouse + hostname: clickhouse3 + volumes: + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse3/database/:/var/lib/clickhouse/" + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse3/logs/:/var/log/clickhouse-server/" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse3/config.d/macros.xml:/etc/clickhouse-server/config.d/macros.xml" + depends_on: + zookeeper: + condition: service_healthy + + # dummy service which does nothing, but allows to postpone + # 'docker-compose up -d' till all dependecies will go healthy + all_services_ready: + image: ${IMAGE_DEPENDENCY_PROXY}hello-world + depends_on: + clickhouse1: + condition: service_healthy + clickhouse2: + condition: service_healthy + clickhouse3: + condition: service_healthy + zookeeper: + condition: service_healthy diff --git a/tests/testflows/map_type/map_type_env_arm64/zookeeper-service.yml b/tests/testflows/map_type/map_type_env_arm64/zookeeper-service.yml new file mode 100755 index 000000000000..60c0e4e7de37 --- /dev/null +++ b/tests/testflows/map_type/map_type_env_arm64/zookeeper-service.yml @@ -0,0 +1,18 @@ +version: '2.3' + +services: + zookeeper: + image: ${IMAGE_DEPENDENCY_PROXY}zookeeper:3.6.2 + expose: + - "2181" + environment: + ZOO_TICK_TIME: 500 + ZOO_MY_ID: 1 + healthcheck: + test: echo stat | nc localhost 2181 + interval: 3s + timeout: 2s + retries: 5 + start_period: 2s + security_opt: + - label:disable diff --git a/tests/testflows/map_type/regression.py b/tests/testflows/map_type/regression.py index 321a6944b2bb..3821da7d2f78 100755 --- a/tests/testflows/map_type/regression.py +++ b/tests/testflows/map_type/regression.py @@ -8,6 +8,7 @@ from helpers.cluster import Cluster from helpers.argparser import argparser +from helpers.common import check_clickhouse_version from map_type.requirements import SRS018_ClickHouse_Map_Data_Type xfails = { @@ -136,32 +137,59 @@ "tests/table map with value integer/UInt64": [ (Fail, "new bug due to JSON changes") ], + "tests/:/:": [(Fail, "debug")], } xflags = {} +ffails = { + "/clickhouse/map type/tests/table map unsupported types/nullable map": ( + XFail, + "type supported in 21.12", + (lambda test: check_clickhouse_version(">=21.12")(test)), + ), + "/clickhouse/map type/tests/table map unsupported types/map with nothing type for key and value": ( + XFail, + "type supported in 21.12", + (lambda test: check_clickhouse_version(">=21.12")(test)), + ), + "/clickhouse/map type/tests/table map invalid key/integer when key is string": ( + XFail, + "type supported in 21.12", + (lambda test: check_clickhouse_version(">=21.12")(test)), + ), +} + @TestModule @ArgumentParser(argparser) @XFails(xfails) @XFlags(xflags) +@FFails(ffails) @Name("map type") @Specifications(SRS018_ClickHouse_Map_Data_Type) -def regression( - self, local, clickhouse_binary_path, clickhouser_version=None, stress=None -): +def regression(self, local, clickhouse_binary_path, clickhouse_version, stress=None): """Map type regression.""" nodes = {"clickhouse": ("clickhouse1", "clickhouse2", "clickhouse3")} + self.context.clickhouse_version = clickhouse_version + if stress is not None: self.context.stress = stress - self.context.clickhouse_version = clickhouse_version + + from platform import processor as current_cpu + + folder_name = os.path.basename(current_dir()) + if current_cpu() == "aarch64": + env = f"{folder_name}_env_arm64" + else: + env = f"{folder_name}_env" with Cluster( local, clickhouse_binary_path, nodes=nodes, - docker_compose_project_dir=os.path.join(current_dir(), "map_type_env"), + docker_compose_project_dir=os.path.join(current_dir(), env), ) as cluster: self.context.cluster = cluster diff --git a/tests/testflows/map_type/tests/feature.py b/tests/testflows/map_type/tests/feature.py index 0aee235c1ed9..e3b4f253c98f 100755 --- a/tests/testflows/map_type/tests/feature.py +++ b/tests/testflows/map_type/tests/feature.py @@ -6,6 +6,7 @@ from map_type.requirements import * from map_type.tests.common import * +from helpers.common import check_clickhouse_version @TestOutline @@ -1241,19 +1242,18 @@ def invalid_key(self): """Check when key is not valid.""" node = self.context.node + exitcode = 43 if check_clickhouse_version("<21.12")(self) else 0 + message = ( + "DB::Exception: Illegal types of arguments" + if check_clickhouse_version("<21.12")(self) + else "" + ) + with When("I try to use an integer key that is too large"): - node.query( - "SELECT map(1,2) AS m, m[256]", - exitcode=43, - message="DB::Exception: Illegal types of arguments", - ) + node.query("SELECT map(1,2) AS m, m[256]", exitcode=exitcode, message=message) with When("I try to use an integer key that is negative when key is unsigned"): - node.query( - "SELECT map(1,2) AS m, m[-1]", - exitcode=43, - message="DB::Exception: Illegal types of arguments", - ) + node.query("SELECT map(1,2) AS m, m[-1]", exitcode=exitcode, message=message) with When("I try to use a string key when key is an integer"): node.query( @@ -1278,18 +1278,14 @@ def invalid_key(self): with When("I try to use wrong type conversion in key"): r = node.query( - "SELECT map(1,2) AS m, m[toInt8('1')]", - exitcode=43, - message="DB::Exception: Illegal types of arguments", + "SELECT map(1,2) AS m, m[toInt8('1')]", exitcode=exitcode, message=message ) with When( "in array of maps I try to use an integer key that is negative when key is unsigned" ): node.query( - "SELECT [map(1,2)] AS m, m[1][-1]", - exitcode=43, - message="DB::Exception: Illegal types of arguments", + "SELECT [map(1,2)] AS m, m[1][-1]", exitcode=exitcode, message=message ) with When("I try to use a NULL key when key is not nullable"): diff --git a/tests/testflows/rbac/configs/clickhouse/common.xml b/tests/testflows/rbac/configs/clickhouse/common.xml new file mode 100644 index 000000000000..0ba01589b908 --- /dev/null +++ b/tests/testflows/rbac/configs/clickhouse/common.xml @@ -0,0 +1,6 @@ + + Europe/Moscow + :: + /var/lib/clickhouse/ + /var/lib/clickhouse/tmp/ + diff --git a/tests/testflows/rbac/configs/clickhouse/config.d/ports.xml b/tests/testflows/rbac/configs/clickhouse/config.d/ports.xml index 1e061e2252e1..01ce4adf52d8 100644 --- a/tests/testflows/rbac/configs/clickhouse/config.d/ports.xml +++ b/tests/testflows/rbac/configs/clickhouse/config.d/ports.xml @@ -2,4 +2,6 @@ 8443 9440 + 9005 + 9004 \ No newline at end of file diff --git a/tests/testflows/rbac/configs/clickhouse/config.xml b/tests/testflows/rbac/configs/clickhouse/config.xml new file mode 100644 index 000000000000..f71f14f47331 --- /dev/null +++ b/tests/testflows/rbac/configs/clickhouse/config.xml @@ -0,0 +1,456 @@ + + + + + + trace + /var/log/clickhouse-server/clickhouse-server.log + /var/log/clickhouse-server/clickhouse-server.err.log + 1000M + 10 + + + + 8123 + 9000 + + + + + + + + + /etc/clickhouse-server/server.crt + /etc/clickhouse-server/server.key + + /etc/clickhouse-server/dhparam.pem + none + true + true + sslv2,sslv3 + true + + + + true + true + sslv2,sslv3 + true + + + + RejectCertificateHandler + + + + + + + + + 9009 + + + + + + + + 0.0.0.0 + + + + + + + + + + + + 4096 + 3 + + + 100 + + + + + + 8589934592 + + + 5368709120 + + + + /var/lib/clickhouse/ + + + /var/lib/clickhouse/tmp/ + + + /var/lib/clickhouse/user_files/ + + + /var/lib/clickhouse/access/ + + + + + + users.xml + + + + /var/lib/clickhouse/access/ + + + + + users.xml + + + default + + + + + + default + + + + + + + + + false + + + + + + + + localhost + 9000 + + + + + + + localhost + 9000 + + + + + localhost + 9000 + + + + + + + localhost + 9440 + 1 + + + + + + + localhost + 9000 + + + + + localhost + 1 + + + + + + + + + + + + + + + + + 3600 + + + + 3600 + + + 60 + + + + + + + + + + system + query_log
+ + toYYYYMM(event_date) + + 7500 +
+ + + + system + trace_log
+ + toYYYYMM(event_date) + 7500 +
+ + + + system + query_thread_log
+ toYYYYMM(event_date) + 7500 +
+ + + + system + query_views_log
+ toYYYYMM(event_date) + 7500 +
+ + + + system + part_log
+ toYYYYMM(event_date) + 7500 +
+ + + + + + + + + + + + + + *_dictionary.xml + + + + + + + + + + /clickhouse/task_queue/ddl + + + + + + + + + + + + + + + + click_cost + any + + 0 + 3600 + + + 86400 + 7200 + + + + max + + 0 + 60 + + + 3600 + 300 + + + 86400 + 3600 + + + + + + /var/lib/clickhouse/format_schemas/ + + + +
diff --git a/tests/testflows/rbac/configs/clickhouse/users.xml b/tests/testflows/rbac/configs/clickhouse/users.xml new file mode 100644 index 000000000000..c7d0ecae6931 --- /dev/null +++ b/tests/testflows/rbac/configs/clickhouse/users.xml @@ -0,0 +1,133 @@ + + + + + + + + 10000000000 + + + 0 + + + random + + + + + 1 + + + + + + + + + + + + + ::/0 + + + + default + + + default + + + 1 + + + + + + + + + + + + + + + + + 3600 + + + 0 + 0 + 0 + 0 + 0 + + + + diff --git a/tests/testflows/rbac/rbac_env/Dockerfile b/tests/testflows/rbac/rbac_env/Dockerfile new file mode 100644 index 000000000000..1daacd9b49d1 --- /dev/null +++ b/tests/testflows/rbac/rbac_env/Dockerfile @@ -0,0 +1,7 @@ +FROM ${IMAGE_DEPENDENCY_PROXY}clickhouse/clickhouse-integration-test:28741 + +RUN sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' +RUN wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - + +RUN apt-get update +RUN apt-get -y install postgresql diff --git a/tests/testflows/rbac/rbac_env/clickhouse-service.yml b/tests/testflows/rbac/rbac_env/clickhouse-service.yml index 4634f3b8721f..6f5f88f9aa32 100755 --- a/tests/testflows/rbac/rbac_env/clickhouse-service.yml +++ b/tests/testflows/rbac/rbac_env/clickhouse-service.yml @@ -2,15 +2,21 @@ version: '2.3' services: clickhouse: - image: altinityinfra/integration-test + build: + context: . + dockerfile: Dockerfile init: true expose: - "9000" - "9009" - "8123" volumes: - - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d:/etc/clickhouse-server/config.d" - - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/users.d:/etc/clickhouse-server/users.d" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/logs.xml:/etc/clickhouse-server/config.d/logs.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/ports.xml:/etc/clickhouse-server/config.d/ports.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/remote.xml:/etc/clickhouse-server/config.d/remote.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/ssl.xml:/etc/clickhouse-server/config.d/ssl.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/storage.xml:/etc/clickhouse-server/config.d/storage.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/zookeeper.xml:/etc/clickhouse-server/config.d/zookeeper.xml" - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/ssl:/etc/clickhouse-server/ssl" - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.xml:/etc/clickhouse-server/config.xml" - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/users.xml:/etc/clickhouse-server/users.xml" diff --git a/tests/testflows/rbac/rbac_env/docker-compose.yml b/tests/testflows/rbac/rbac_env/docker-compose.yml index 29f2ef524706..7e8faf531173 100755 --- a/tests/testflows/rbac/rbac_env/docker-compose.yml +++ b/tests/testflows/rbac/rbac_env/docker-compose.yml @@ -6,6 +6,14 @@ services: file: zookeeper-service.yml service: zookeeper + mysql1: + extends: + file: mysql-service.yml + service: mysql + hostname: mysql1 + volumes: + - "${CLICKHOUSE_TESTS_DIR}/_instances/mysql1/database:/var/lib/mysql" + clickhouse1: extends: file: clickhouse-service.yml @@ -48,7 +56,7 @@ services: # dummy service which does nothing, but allows to postpone # 'docker-compose up -d' till all dependecies will go healthy all_services_ready: - image: hello-world + image: ${IMAGE_DEPENDENCY_PROXY}hello-world depends_on: clickhouse1: condition: service_healthy diff --git a/tests/testflows/rbac/rbac_env/mysql-service.yml b/tests/testflows/rbac/rbac_env/mysql-service.yml new file mode 100644 index 000000000000..d23b28b396ff --- /dev/null +++ b/tests/testflows/rbac/rbac_env/mysql-service.yml @@ -0,0 +1,19 @@ +version: '2.3' + +services: + mysql: + image: ${IMAGE_DEPENDENCY_PROXY}mysql:5.7.30 + restart: always + environment: + MYSQL_DATABASE: 'default' + MYSQL_USER: 'user' + MYSQL_PASSWORD: 'password' + MYSQL_ROOT_PASSWORD: 'password' + expose: + - '3306' + healthcheck: + test: mysql -D default -u user --password=password -e "select 1;" + interval: 3s + timeout: 2s + retries: 40 + start_period: 2s diff --git a/tests/testflows/rbac/rbac_env/zookeeper-service.yml b/tests/testflows/rbac/rbac_env/zookeeper-service.yml index f3df33358be7..60c0e4e7de37 100755 --- a/tests/testflows/rbac/rbac_env/zookeeper-service.yml +++ b/tests/testflows/rbac/rbac_env/zookeeper-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: zookeeper: - image: zookeeper:3.4.12 + image: ${IMAGE_DEPENDENCY_PROXY}zookeeper:3.6.2 expose: - "2181" environment: diff --git a/tests/testflows/rbac/rbac_env_arm64/clickhouse-service.yml b/tests/testflows/rbac/rbac_env_arm64/clickhouse-service.yml index a7d6c7053d29..b9b20ece3a94 100755 --- a/tests/testflows/rbac/rbac_env_arm64/clickhouse-service.yml +++ b/tests/testflows/rbac/rbac_env_arm64/clickhouse-service.yml @@ -9,8 +9,12 @@ services: - "9009" - "8123" volumes: - - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d:/etc/clickhouse-server/config.d" - - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/users.d:/etc/clickhouse-server/users.d" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/logs.xml:/etc/clickhouse-server/config.d/logs.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/ports.xml:/etc/clickhouse-server/config.d/ports.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/remote.xml:/etc/clickhouse-server/config.d/remote.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/ssl.xml:/etc/clickhouse-server/config.d/ssl.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/storage.xml:/etc/clickhouse-server/config.d/storage.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/zookeeper.xml:/etc/clickhouse-server/config.d/zookeeper.xml" - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/ssl:/etc/clickhouse-server/ssl" - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.xml:/etc/clickhouse-server/config.xml" - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/users.xml:/etc/clickhouse-server/users.xml" diff --git a/tests/testflows/rbac/rbac_env_arm64/docker-compose.yml b/tests/testflows/rbac/rbac_env_arm64/docker-compose.yml index 29f2ef524706..c9ae33d78848 100755 --- a/tests/testflows/rbac/rbac_env_arm64/docker-compose.yml +++ b/tests/testflows/rbac/rbac_env_arm64/docker-compose.yml @@ -48,7 +48,7 @@ services: # dummy service which does nothing, but allows to postpone # 'docker-compose up -d' till all dependecies will go healthy all_services_ready: - image: hello-world + image: ${IMAGE_DEPENDENCY_PROXY}hello-world depends_on: clickhouse1: condition: service_healthy diff --git a/tests/testflows/rbac/rbac_env_arm64/zookeeper-service.yml b/tests/testflows/rbac/rbac_env_arm64/zookeeper-service.yml index f3df33358be7..60c0e4e7de37 100755 --- a/tests/testflows/rbac/rbac_env_arm64/zookeeper-service.yml +++ b/tests/testflows/rbac/rbac_env_arm64/zookeeper-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: zookeeper: - image: zookeeper:3.4.12 + image: ${IMAGE_DEPENDENCY_PROXY}zookeeper:3.6.2 expose: - "2181" environment: diff --git a/tests/testflows/rbac/regression.py b/tests/testflows/rbac/regression.py index eb1d6c9acf71..cf242b0a92bb 100755 --- a/tests/testflows/rbac/regression.py +++ b/tests/testflows/rbac/regression.py @@ -32,6 +32,9 @@ issue_21084 = "https://github.com/ClickHouse/ClickHouse/issues/21084" issue_25413 = "https://github.com/ClickHouse/ClickHouse/issues/25413" issue_26746 = "https://github.com/ClickHouse/ClickHouse/issues/26746" +issue_37389 = "https://github.com/ClickHouse/ClickHouse/issues/37389" +issue_37580 = "https://github.com/ClickHouse/ClickHouse/issues/37580" +issue_38716 = "https://github.com/ClickHouse/ClickHouse/issues/38716" xfails = { "syntax/show create quota/I show create quota current": [ @@ -184,6 +187,12 @@ "views/live view/create with join subquery privilege granted directly or via role/create with join subquery, privilege granted through a role": [ (Fail, issue_26746) ], + "privileges/table functions/cluster": [(Fail, issue_37389)], + "privileges/table functions/remote": [(Fail, issue_37389)], + "privileges/create row policy/remote": [(Fail, issue_37580)], + "privileges/system drop replica/:/drop replica/check privilege:/:": [ + (Fail, issue_38716) + ], } xflags = { @@ -210,6 +219,36 @@ and check_clickhouse_version("<21.10")(test) ), ), + "/clickhouse/rbac/privileges/system merges": ( + XFail, + "Does not work on clickhouse 21.8", + (lambda test: check_clickhouse_version("<21.9")(test)), + ), + "/clickhouse/rbac/privileges/system ttl merges": ( + XFail, + "Does not work on clickhouse 21.8", + (lambda test: check_clickhouse_version("<21.9")(test)), + ), + "/clickhouse/rbac/privileges/system moves": ( + XFail, + "Does not work on clickhouse 21.8", + (lambda test: check_clickhouse_version("<21.9")(test)), + ), + "/clickhouse/rbac/privileges/system sends": ( + XFail, + "Does not work on clickhouse 21.8", + (lambda test: check_clickhouse_version("<21.9")(test)), + ), + "/clickhouse/rbac/privileges/system fetches": ( + XFail, + "Does not work on clickhouse 21.8", + (lambda test: check_clickhouse_version("<21.9")(test)), + ), + "/clickhouse/rbac/privileges/system replication queues": ( + XFail, + "Does not work on clickhouse 21.8", + (lambda test: check_clickhouse_version("<21.9")(test)), + ), } diff --git a/tests/testflows/rbac/requirements/requirements.md b/tests/testflows/rbac/requirements/requirements.md index 1c59f51fce48..8d6bb9a11bbe 100644 --- a/tests/testflows/rbac/requirements/requirements.md +++ b/tests/testflows/rbac/requirements/requirements.md @@ -2,6 +2,7 @@ # Software Requirements Specification ## Table of Contents + * 1 [Revision History](#revision-history) * 2 [Introduction](#introduction) * 3 [Terminology](#terminology) @@ -397,6 +398,9 @@ * 5.19.2 [RQ.SRS-006.RBAC.Select.Column](#rqsrs-006rbacselectcolumn) * 5.19.3 [RQ.SRS-006.RBAC.Select.Cluster](#rqsrs-006rbacselectcluster) * 5.19.4 [RQ.SRS-006.RBAC.Select.TableEngines](#rqsrs-006rbacselecttableengines) + * 5.19.5 [Table Functions](#table-functions) + * 5.19.5.1 [RQ.SRS-006.RBAC.Select.TableFunctions.Remote](#rqsrs-006rbacselecttablefunctionsremote) + * 5.19.5.2 [RQ.SRS-006.RBAC.Select.TableFunctions.Cluster](#rqsrs-006rbacselecttablefunctionscluster) * 5.20 [Insert](#insert) * 5.20.1 [RQ.SRS-006.RBAC.Insert](#rqsrs-006rbacinsert) * 5.20.2 [RQ.SRS-006.RBAC.Insert.Column](#rqsrs-006rbacinsertcolumn) @@ -459,20 +463,33 @@ * 5.21.10.2 [RQ.SRS-006.RBAC.Privileges.AlterMove.Grant](#rqsrs-006rbacprivilegesaltermovegrant) * 5.21.10.3 [RQ.SRS-006.RBAC.Privileges.AlterMove.Revoke](#rqsrs-006rbacprivilegesaltermoverevoke) * 5.21.10.4 [RQ.SRS-006.RBAC.Privileges.AlterMove.TableEngines](#rqsrs-006rbacprivilegesaltermovetableengines) + * 5.21.11 [Alter Projection](#alter-projection) + * 5.21.11.1 [RQ.SRS-006.RBAC.Privileges.AlterProjection](#rqsrs-006rbacprivilegesalterprojection) + * 5.21.11.2 [RQ.SRS-006.RBAC.Privileges.AlterProjection.Add](#rqsrs-006rbacprivilegesalterprojectionadd) + * 5.21.11.3 [RQ.SRS-006.RBAC.Privileges.AlterProjection.Drop](#rqsrs-006rbacprivilegesalterprojectiondrop) + * 5.21.11.4 [RQ.SRS-006.RBAC.Privileges.AlterProjection.Materialize](#rqsrs-006rbacprivilegesalterprojectionmaterialize) + * 5.21.11.5 [RQ.SRS-006.RBAC.Privileges.AlterProjection.Clear](#rqsrs-006rbacprivilegesalterprojectionclear) + * 5.21.12 [Alter Database](#alter-database) + * 5.21.12.1 [RQ.SRS-006.RBAC.Privileges.AlterDatabase](#rqsrs-006rbacprivilegesalterdatabase) + * 5.21.12.2 [RQ.SRS-006.RBAC.Privileges.AlterDatabase.Settings](#rqsrs-006rbacprivilegesalterdatabasesettings) * 5.22 [Create](#create) - * 5.22.1 [RQ.SRS-006.RBAC.Privileges.CreateTable](#rqsrs-006rbacprivilegescreatetable) - * 5.22.2 [RQ.SRS-006.RBAC.Privileges.CreateDatabase](#rqsrs-006rbacprivilegescreatedatabase) - * 5.22.3 [RQ.SRS-006.RBAC.Privileges.CreateDictionary](#rqsrs-006rbacprivilegescreatedictionary) - * 5.22.4 [RQ.SRS-006.RBAC.Privileges.CreateTemporaryTable](#rqsrs-006rbacprivilegescreatetemporarytable) + * 5.22.1 [RQ.SRS-006.RBAC.Privileges.Create](#rqsrs-006rbacprivilegescreate) + * 5.22.2 [RQ.SRS-006.RBAC.Privileges.CreateTable](#rqsrs-006rbacprivilegescreatetable) + * 5.22.3 [RQ.SRS-006.RBAC.Privileges.CreateDatabase](#rqsrs-006rbacprivilegescreatedatabase) + * 5.22.4 [RQ.SRS-006.RBAC.Privileges.CreateDictionary](#rqsrs-006rbacprivilegescreatedictionary) + * 5.22.5 [RQ.SRS-006.RBAC.Privileges.CreateTemporaryTable](#rqsrs-006rbacprivilegescreatetemporarytable) + * 5.22.6 [RQ.SRS-006.RBAC.Privileges.CreateFunction](#rqsrs-006rbacprivilegescreatefunction) * 5.23 [Attach](#attach) * 5.23.1 [RQ.SRS-006.RBAC.Privileges.AttachDatabase](#rqsrs-006rbacprivilegesattachdatabase) * 5.23.2 [RQ.SRS-006.RBAC.Privileges.AttachDictionary](#rqsrs-006rbacprivilegesattachdictionary) * 5.23.3 [RQ.SRS-006.RBAC.Privileges.AttachTemporaryTable](#rqsrs-006rbacprivilegesattachtemporarytable) * 5.23.4 [RQ.SRS-006.RBAC.Privileges.AttachTable](#rqsrs-006rbacprivilegesattachtable) * 5.24 [Drop](#drop) - * 5.24.1 [RQ.SRS-006.RBAC.Privileges.DropTable](#rqsrs-006rbacprivilegesdroptable) - * 5.24.2 [RQ.SRS-006.RBAC.Privileges.DropDatabase](#rqsrs-006rbacprivilegesdropdatabase) - * 5.24.3 [RQ.SRS-006.RBAC.Privileges.DropDictionary](#rqsrs-006rbacprivilegesdropdictionary) + * 5.24.1 [RQ.SRS-006.RBAC.Privileges.Drop](#rqsrs-006rbacprivilegesdrop) + * 5.24.2 [RQ.SRS-006.RBAC.Privileges.DropTable](#rqsrs-006rbacprivilegesdroptable) + * 5.24.3 [RQ.SRS-006.RBAC.Privileges.DropDatabase](#rqsrs-006rbacprivilegesdropdatabase) + * 5.24.4 [RQ.SRS-006.RBAC.Privileges.DropDictionary](#rqsrs-006rbacprivilegesdropdictionary) + * 5.24.5 [RQ.SRS-006.RBAC.Privileges.DropFunction](#rqsrs-006rbacprivilegesdropfunction) * 5.25 [Detach](#detach) * 5.25.1 [RQ.SRS-006.RBAC.Privileges.DetachTable](#rqsrs-006rbacprivilegesdetachtable) * 5.25.2 [RQ.SRS-006.RBAC.Privileges.DetachView](#rqsrs-006rbacprivilegesdetachview) @@ -550,32 +567,41 @@ * 5.33 [Introspection](#introspection) * 5.33.1 [RQ.SRS-006.RBAC.Privileges.Introspection](#rqsrs-006rbacprivilegesintrospection) * 5.33.2 [RQ.SRS-006.RBAC.Privileges.Introspection.addressToLine](#rqsrs-006rbacprivilegesintrospectionaddresstoline) - * 5.33.3 [RQ.SRS-006.RBAC.Privileges.Introspection.addressToSymbol](#rqsrs-006rbacprivilegesintrospectionaddresstosymbol) - * 5.33.4 [RQ.SRS-006.RBAC.Privileges.Introspection.demangle](#rqsrs-006rbacprivilegesintrospectiondemangle) + * 5.33.3 [RQ.SRS-006.RBAC.Privileges.Introspection.addressToLineWithInlines](#rqsrs-006rbacprivilegesintrospectionaddresstolinewithinlines) + * 5.33.4 [RQ.SRS-006.RBAC.Privileges.Introspection.addressToSymbol](#rqsrs-006rbacprivilegesintrospectionaddresstosymbol) + * 5.33.5 [RQ.SRS-006.RBAC.Privileges.Introspection.demangle](#rqsrs-006rbacprivilegesintrospectiondemangle) * 5.34 [System](#system) * 5.34.1 [RQ.SRS-006.RBAC.Privileges.System.Shutdown](#rqsrs-006rbacprivilegessystemshutdown) * 5.34.2 [RQ.SRS-006.RBAC.Privileges.System.DropCache](#rqsrs-006rbacprivilegessystemdropcache) * 5.34.3 [RQ.SRS-006.RBAC.Privileges.System.DropCache.DNS](#rqsrs-006rbacprivilegessystemdropcachedns) * 5.34.4 [RQ.SRS-006.RBAC.Privileges.System.DropCache.Mark](#rqsrs-006rbacprivilegessystemdropcachemark) * 5.34.5 [RQ.SRS-006.RBAC.Privileges.System.DropCache.Uncompressed](#rqsrs-006rbacprivilegessystemdropcacheuncompressed) - * 5.34.6 [RQ.SRS-006.RBAC.Privileges.System.Reload](#rqsrs-006rbacprivilegessystemreload) - * 5.34.7 [RQ.SRS-006.RBAC.Privileges.System.Reload.Config](#rqsrs-006rbacprivilegessystemreloadconfig) - * 5.34.8 [RQ.SRS-006.RBAC.Privileges.System.Reload.Dictionary](#rqsrs-006rbacprivilegessystemreloaddictionary) - * 5.34.9 [RQ.SRS-006.RBAC.Privileges.System.Reload.Dictionaries](#rqsrs-006rbacprivilegessystemreloaddictionaries) - * 5.34.10 [RQ.SRS-006.RBAC.Privileges.System.Reload.EmbeddedDictionaries](#rqsrs-006rbacprivilegessystemreloadembeddeddictionaries) - * 5.34.11 [RQ.SRS-006.RBAC.Privileges.System.Merges](#rqsrs-006rbacprivilegessystemmerges) - * 5.34.12 [RQ.SRS-006.RBAC.Privileges.System.TTLMerges](#rqsrs-006rbacprivilegessystemttlmerges) - * 5.34.13 [RQ.SRS-006.RBAC.Privileges.System.Fetches](#rqsrs-006rbacprivilegessystemfetches) - * 5.34.14 [RQ.SRS-006.RBAC.Privileges.System.Moves](#rqsrs-006rbacprivilegessystemmoves) - * 5.34.15 [RQ.SRS-006.RBAC.Privileges.System.Sends](#rqsrs-006rbacprivilegessystemsends) - * 5.34.16 [RQ.SRS-006.RBAC.Privileges.System.Sends.Distributed](#rqsrs-006rbacprivilegessystemsendsdistributed) - * 5.34.17 [RQ.SRS-006.RBAC.Privileges.System.Sends.Replicated](#rqsrs-006rbacprivilegessystemsendsreplicated) - * 5.34.18 [RQ.SRS-006.RBAC.Privileges.System.ReplicationQueues](#rqsrs-006rbacprivilegessystemreplicationqueues) - * 5.34.19 [RQ.SRS-006.RBAC.Privileges.System.SyncReplica](#rqsrs-006rbacprivilegessystemsyncreplica) - * 5.34.20 [RQ.SRS-006.RBAC.Privileges.System.RestartReplica](#rqsrs-006rbacprivilegessystemrestartreplica) - * 5.34.21 [RQ.SRS-006.RBAC.Privileges.System.Flush](#rqsrs-006rbacprivilegessystemflush) - * 5.34.22 [RQ.SRS-006.RBAC.Privileges.System.Flush.Distributed](#rqsrs-006rbacprivilegessystemflushdistributed) - * 5.34.23 [RQ.SRS-006.RBAC.Privileges.System.Flush.Logs](#rqsrs-006rbacprivilegessystemflushlogs) + * 5.34.6 [RQ.SRS-006.RBAC.Privileges.System.DropCache.Mmap](#rqsrs-006rbacprivilegessystemdropcachemmap) + * 5.34.7 [RQ.SRS-006.RBAC.Privileges.System.DropCache.CompiledExpression](#rqsrs-006rbacprivilegessystemdropcachecompiledexpression) + * 5.34.8 [RQ.SRS-006.RBAC.Privileges.System.Reload](#rqsrs-006rbacprivilegessystemreload) + * 5.34.9 [RQ.SRS-006.RBAC.Privileges.System.Reload.Config](#rqsrs-006rbacprivilegessystemreloadconfig) + * 5.34.10 [RQ.SRS-006.RBAC.Privileges.System.Reload.Dictionary](#rqsrs-006rbacprivilegessystemreloaddictionary) + * 5.34.11 [RQ.SRS-006.RBAC.Privileges.System.Reload.Dictionaries](#rqsrs-006rbacprivilegessystemreloaddictionaries) + * 5.34.12 [RQ.SRS-006.RBAC.Privileges.System.Reload.EmbeddedDictionaries](#rqsrs-006rbacprivilegessystemreloadembeddeddictionaries) + * 5.34.13 [RQ.SRS-006.RBAC.Privileges.System.Reload.Symbols](#rqsrs-006rbacprivilegessystemreloadsymbols) + * 5.34.14 [RQ.SRS-006.RBAC.Privileges.System.Reload.Function](#rqsrs-006rbacprivilegessystemreloadfunction) + * 5.34.15 [RQ.SRS-006.RBAC.Privileges.System.RestartDisk](#rqsrs-006rbacprivilegessystemrestartdisk) + * 5.34.16 [RQ.SRS-006.RBAC.Privileges.System.ThreadFuzzer](#rqsrs-006rbacprivilegessystemthreadfuzzer) + * 5.34.17 [RQ.SRS-006.RBAC.Privileges.System.Merges](#rqsrs-006rbacprivilegessystemmerges) + * 5.34.18 [RQ.SRS-006.RBAC.Privileges.System.TTLMerges](#rqsrs-006rbacprivilegessystemttlmerges) + * 5.34.19 [RQ.SRS-006.RBAC.Privileges.System.Fetches](#rqsrs-006rbacprivilegessystemfetches) + * 5.34.20 [RQ.SRS-006.RBAC.Privileges.System.Moves](#rqsrs-006rbacprivilegessystemmoves) + * 5.34.21 [RQ.SRS-006.RBAC.Privileges.System.Sends](#rqsrs-006rbacprivilegessystemsends) + * 5.34.22 [RQ.SRS-006.RBAC.Privileges.System.Sends.Distributed](#rqsrs-006rbacprivilegessystemsendsdistributed) + * 5.34.23 [RQ.SRS-006.RBAC.Privileges.System.Sends.Replicated](#rqsrs-006rbacprivilegessystemsendsreplicated) + * 5.34.24 [RQ.SRS-006.RBAC.Privileges.System.ReplicationQueues](#rqsrs-006rbacprivilegessystemreplicationqueues) + * 5.34.25 [RQ.SRS-006.RBAC.Privileges.System.SyncReplica](#rqsrs-006rbacprivilegessystemsyncreplica) + * 5.34.26 [RQ.SRS-006.RBAC.Privileges.System.RestartReplica](#rqsrs-006rbacprivilegessystemrestartreplica) + * 5.34.27 [RQ.SRS-006.RBAC.Privileges.System.DropReplica](#rqsrs-006rbacprivilegessystemdropreplica) + * 5.34.28 [RQ.SRS-006.RBAC.Privileges.System.RestoreReplica](#rqsrs-006rbacprivilegessystemrestorereplica) + * 5.34.29 [RQ.SRS-006.RBAC.Privileges.System.Flush](#rqsrs-006rbacprivilegessystemflush) + * 5.34.30 [RQ.SRS-006.RBAC.Privileges.System.Flush.Distributed](#rqsrs-006rbacprivilegessystemflushdistributed) + * 5.34.31 [RQ.SRS-006.RBAC.Privileges.System.Flush.Logs](#rqsrs-006rbacprivilegessystemflushlogs) * 5.35 [Sources](#sources) * 5.35.1 [RQ.SRS-006.RBAC.Privileges.Sources](#rqsrs-006rbacprivilegessources) * 5.35.2 [RQ.SRS-006.RBAC.Privileges.Sources.File](#rqsrs-006rbacprivilegessourcesfile) @@ -586,6 +612,9 @@ * 5.35.7 [RQ.SRS-006.RBAC.Privileges.Sources.JDBC](#rqsrs-006rbacprivilegessourcesjdbc) * 5.35.8 [RQ.SRS-006.RBAC.Privileges.Sources.HDFS](#rqsrs-006rbacprivilegessourceshdfs) * 5.35.9 [RQ.SRS-006.RBAC.Privileges.Sources.S3](#rqsrs-006rbacprivilegessourcess3) + * 5.35.10 [RQ.SRS-006.RBAC.Privileges.Sources.Mongo](#rqsrs-006rbacprivilegessourcesmongo) + * 5.35.11 [RQ.SRS-006.RBAC.Privileges.Sources.Postgres](#rqsrs-006rbacprivilegessourcespostgres) + * 5.35.12 [RQ.SRS-006.RBAC.Privileges.Sources.Sqlite](#rqsrs-006rbacprivilegessourcessqlite) * 5.36 [RQ.SRS-006.RBAC.Privileges.GrantOption](#rqsrs-006rbacprivilegesgrantoption) * 5.37 [RQ.SRS-006.RBAC.Privileges.All](#rqsrs-006rbacprivilegesall) * 5.38 [RQ.SRS-006.RBAC.Privileges.RoleAll](#rqsrs-006rbacprivilegesroleall) @@ -3222,6 +3251,21 @@ on tables created using the following engines * ReplicatedVersionedCollapsingMergeTree * ReplicatedGraphiteMergeTree +#### Table Functions + +##### RQ.SRS-006.RBAC.Select.TableFunctions.Remote +version: 1.0 + +[ClickHouse] SHALL only execute `SELECT FROM remote()` query if +the user has **REMOTE**, **CREATE TEMPORARY TABLE** and **SELECT** privileges. + + +##### RQ.SRS-006.RBAC.Select.TableFunctions.Cluster +version: 1.0 + +[ClickHouse] SHALL only execute `SELECT FROM cluster()` query if +the user has **REMOTE**, **CREATE TEMPORARY TABLE** and **SELECT** privileges. + ### Insert #### RQ.SRS-006.RBAC.Insert @@ -3278,7 +3322,7 @@ version: 1.0 [ClickHouse] SHALL support controlling access to the **alter column** privilege for a database or a specific table to one or more **users** or **roles**. -Any `ALTER TABLE ... ADD|DROP|CLEAR|COMMENT|MODIFY COLUMN` statements SHALL +Any `ALTER TABLE ... ADD|DROP|CLEAR|COMMENT|MODIFY|MATERIALIZE COLUMN` statements SHALL return an error, unless the user has the **alter column** privilege for the destination table either because of the explicit grant or through one of the roles assigned to the user. @@ -3300,7 +3344,7 @@ version: 1.0 [ClickHouse] SHALL support granting or revoking **alter column** privilege for one or more specified columns in a table to one or more **users** or **roles**. -Any `ALTER TABLE ... ADD|DROP|CLEAR|COMMENT|MODIFY COLUMN` statements SHALL return an error, +Any `ALTER TABLE ... ADD|DROP|CLEAR|COMMENT|MODIFY|MATERIALIZE COLUMN` statements SHALL return an error, unless the user has the **alter column** privilege for the destination column either because of the explicit grant or through one of the roles assigned to the user. @@ -3309,7 +3353,7 @@ version: 1.0 [ClickHouse] SHALL support granting or revoking **alter column** privilege on a specified cluster to one or more **users** or **roles**. -Any `ALTER TABLE ... ADD|DROP|CLEAR|COMMENT|MODIFY COLUMN` +Any `ALTER TABLE ... ADD|DROP|CLEAR|COMMENT|MODIFY|MATERIALIZE COLUMN` statements SHALL succeed only on nodes where the table exists and privilege was granted. ##### RQ.SRS-006.RBAC.Privileges.AlterColumn.TableEngines @@ -3735,8 +3779,61 @@ on tables created using the following engines * ReplicatedVersionedCollapsingMergeTree * ReplicatedGraphiteMergeTree +#### Alter Projection + +##### RQ.SRS-006.RBAC.Privileges.AlterProjection +version: 1.0 + +[ClickHouse] SHALL support granting and revoking `ALTER PROJECTION` privilege, +which permits users to **add**, **drop**, **materialize**, and **clear** projections. + +##### RQ.SRS-006.RBAC.Privileges.AlterProjection.Add +version: 1.0 + +[ClickHouse] SHALL support granting and revoking `ALTER ADD PROJECTION` privilege, +which permits users to **add** projections. + +##### RQ.SRS-006.RBAC.Privileges.AlterProjection.Drop +version: 1.0 + +[ClickHouse] SHALL support granting and revoking `ALTER DROP PROJECTION` privilege, +which permits users to **drop** projections. + +##### RQ.SRS-006.RBAC.Privileges.AlterProjection.Materialize +version: 1.0 + +[ClickHouse] SHALL support granting and revoking `ALTER MATERIALIZE PROJECTION` privilege, +which permits users to **materialize** projections. + +##### RQ.SRS-006.RBAC.Privileges.AlterProjection.Clear +version: 1.0 + +[ClickHouse] SHALL support granting and revoking `ALTER CLEAR PROJECTION` privilege, +which permits users to **clear** projections. + +#### Alter Database + +##### RQ.SRS-006.RBAC.Privileges.AlterDatabase +version: 1.0 + +[ClickHouse] SHALL support granting and revoking `ALTER DATABASE` privilege, +which permits users to **alter** a database. + +##### RQ.SRS-006.RBAC.Privileges.AlterDatabase.Settings +version: 1.0 + +[ClickHouse] SHALL support granting and revoking `ALTER DATABASE SETTINGS` privilege, +which permits users to **alter** the settings of a database. + ### Create +#### RQ.SRS-006.RBAC.Privileges.Create +version: 1.0 + +[ClickHouse] SHALL support granting and revoking `CREATE` privilege, +which permits users to **create** tables, databases, dictionaries, and temporary tables. +This privilege does NOT grant the permission to create users, roles, or quotas, row policies, or settings profiles. + #### RQ.SRS-006.RBAC.Privileges.CreateTable version: 1.0 @@ -3773,6 +3870,12 @@ version: 1.0 [ClickHouse] SHALL successfully execute `CREATE TEMPORARY TABLE` statement if and only if the user has **create temporary table** privilege on the table, either directly or through a role. +#### RQ.SRS-006.RBAC.Privileges.CreateFunction +version: 1.0 + +[ClickHouse] SHALL successfully execute `CREATE FUNCTION` statement if and only if the user has **create function** privilege, +either directly or through a role. + ### Attach #### RQ.SRS-006.RBAC.Privileges.AttachDatabase @@ -3801,6 +3904,13 @@ either directly or through a role. ### Drop +#### RQ.SRS-006.RBAC.Privileges.Drop +version: 1.0 + +[ClickHouse] SHALL support granting and revoking `DROP` privilege, +which permits users to **drop** tables, databases, dictionaries, and temporary tables. +This privilege does NOT grant the permission to create users, roles, or quotas, row policies, or settings profiles. + #### RQ.SRS-006.RBAC.Privileges.DropTable version: 1.0 @@ -3819,6 +3929,12 @@ version: 1.0 [ClickHouse] SHALL successfully execute `DROP DICTIONARY` statement if and only if the user has **drop dictionary** privilege on the dictionary, either directly or through a role. +#### RQ.SRS-006.RBAC.Privileges.DropFunction +version: 1.0 + +[ClickHouse] SHALL successfully execute `DROP FUNCTION` statement if and only if the user has **drop function** privilege, +either directly or through a role. + ### Detach #### RQ.SRS-006.RBAC.Privileges.DetachTable @@ -4258,6 +4374,12 @@ version: 1.0 [ClickHouse] SHALL successfully execute `addressToLine` statement if and only if the user has **introspection** privilege, either directly or through a role. +#### RQ.SRS-006.RBAC.Privileges.Introspection.addressToLineWithInlines +version: 1.0 + +[ClickHouse] SHALL successfully execute `addressToLineWithInlines` statement if and only if +the user has **introspection** privilege, either directly or through a role. + #### RQ.SRS-006.RBAC.Privileges.Introspection.addressToSymbol version: 1.0 @@ -4305,6 +4427,20 @@ version: 1.0 the user is granted `SYSTEM`, `SYSTEM DROP CACHE`, `DROP CACHE`, `SYSTEM DROP UNCOMPRESSED CACHE`, `SYSTEM DROP UNCOMPRESSED`, `DROP UNCOMPRESSED CACHE`, or `DROP UNCOMPRESSED`. +#### RQ.SRS-006.RBAC.Privileges.System.DropCache.Mmap +version: 1.0 + +[ClickHouse] SHALL successfully grant `SYSTEM DROP MMAP CACHE` privilege when +the user is granted `SYSTEM`, `SYSTEM DROP CACHE`, `DROP CACHE`, `SYSTEM DROP MMAP CACHE`, +`SYSTEM DROP MMAP`, `DROP MMAP CACHE`, or `DROP MMAP`. + +#### RQ.SRS-006.RBAC.Privileges.System.DropCache.CompiledExpression +version: 1.0 + +[ClickHouse] SHALL successfully grant `SYSTEM DROP COMPILED EXPRESSION CACHE` privilege when +the user is granted `SYSTEM`, `SYSTEM DROP CACHE`, `DROP CACHE`, `SYSTEM DROP COMPILED EXPRESSION CACHE`, +`SYSTEM DROP COMPILED EXPRESSION`, or `DROP COMPILED EXPRESSION CACHE` + #### RQ.SRS-006.RBAC.Privileges.System.Reload version: 1.0 @@ -4335,6 +4471,30 @@ version: 1.0 [ClickHouse] SHALL successfully grant `SYSTEM RELOAD EMBEDDED DICTIONARIES` privilege when the user is granted `SYSTEM`, `SYSTEM RELOAD`, `SYSTEM RELOAD DICTIONARY ON *.*`, or `SYSTEM RELOAD EMBEDDED DICTIONARIES`. +#### RQ.SRS-006.RBAC.Privileges.System.Reload.Symbols +version: 1.0 + +[ClickHouse] SHALL successfully grant `SYSTEM RELOAD SYMBOLS` privilege when +the user is granted `SYSTEM`, `SYSTEM RELOAD`, or `SYSTEM RELOAD SYMBOLS`. + +#### RQ.SRS-006.RBAC.Privileges.System.Reload.Function +version: 1.0 + +[ClickHouse] SHALL successfully grant `SYSTEM RELOAD FUNCTION` privilege when +the user is granted `SYSTEM`, `SYSTEM RELOAD`, or `SYSTEM RELOAD FUNCTION`. + +#### RQ.SRS-006.RBAC.Privileges.System.RestartDisk +version: 1.0 + +[ClickHouse] SHALL successfully grant `SYSTEM RESTART DISK` privilege when +the user is granted `SYSTEM RESTART DISK`. + +#### RQ.SRS-006.RBAC.Privileges.System.ThreadFuzzer +version: 1.0 + +[ClickHouse] SHALL successfully grant `SYSTEM THREAD FUZZER` privilege when +the user is granted `SYSTEM THREAD FUZZER`. + #### RQ.SRS-006.RBAC.Privileges.System.Merges version: 1.0 @@ -4398,6 +4558,18 @@ version: 1.0 [ClickHouse] SHALL successfully grant `SYSTEM RESTART REPLICA` privilege when the user is granted `SYSTEM`, `SYSTEM RESTART REPLICA`, or `RESTART REPLICA`. +#### RQ.SRS-006.RBAC.Privileges.System.DropReplica +version: 1.0 + +[ClickHouse] SHALL successfully grant `SYSTEM DROP REPLICA` privilege when +the user is granted `SYSTEM`, `SYSTEM DROP REPLICA`, or `DROP REPLICA`. + +#### RQ.SRS-006.RBAC.Privileges.System.RestoreReplica +version: 1.0 + +[ClickHouse] SHALL successfully grant `SYSTEM RESTORE REPLICA` privilege when +the user is granted `SYSTEM`, `SYSTEM RESTORE REPLICA`, or `RESTORE REPLICA`. + #### RQ.SRS-006.RBAC.Privileges.System.Flush version: 1.0 @@ -4472,6 +4644,24 @@ version: 1.0 [ClickHouse] SHALL support the use of `S3` source by a user if and only if the user has `S3` or `SOURCES` privileges granted to them directly or through a role. +#### RQ.SRS-006.RBAC.Privileges.Sources.Mongo +version: 1.0 + +[ClickHouse] SHALL support the use of `MONGO` source by a user if and only if +the user has `MONGO` or `SOURCES` privileges granted to them directly or through a role. + +#### RQ.SRS-006.RBAC.Privileges.Sources.Postgres +version: 1.0 + +[ClickHouse] SHALL support the use of `POSTGRES` source by a user if and only if +the user has `POSTGRES` or `SOURCES` privileges granted to them directly or through a role. + +#### RQ.SRS-006.RBAC.Privileges.Sources.Sqlite +version: 1.0 + +[ClickHouse] SHALL support the use of `SQLITE` source by a user if and only if +the user has `SQLITE` or `SOURCES` privileges granted to them directly or through a role. + ### RQ.SRS-006.RBAC.Privileges.GrantOption version: 1.0 diff --git a/tests/testflows/rbac/requirements/requirements.py b/tests/testflows/rbac/requirements/requirements.py index 552588e49b93..0dbd38eab41f 100755 --- a/tests/testflows/rbac/requirements/requirements.py +++ b/tests/testflows/rbac/requirements/requirements.py @@ -1,6 +1,6 @@ # These requirements were auto generated # from software requirements specification (SRS) -# document by TestFlows v1.6.201216.1172002. +# document by TestFlows v1.9.220620.1143643. # Do not edit by hand but re-generate instead # using 'tfs requirements generate' command. from testflows.core import Specification @@ -6300,6 +6300,41 @@ num="5.19.4", ) +RQ_SRS_006_RBAC_Select_TableFunctions_Remote = Requirement( + name="RQ.SRS-006.RBAC.Select.TableFunctions.Remote", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL only execute `SELECT FROM remote()` query if\n" + "the user has **REMOTE**, **CREATE TEMPORARY TABLE** and **SELECT** privileges.\n" + "\n" + "\n" + ), + link=None, + level=4, + num="5.19.5.1", +) + +RQ_SRS_006_RBAC_Select_TableFunctions_Cluster = Requirement( + name="RQ.SRS-006.RBAC.Select.TableFunctions.Cluster", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL only execute `SELECT FROM cluster()` query if\n" + "the user has **REMOTE**, **CREATE TEMPORARY TABLE** and **SELECT** privileges.\n" + "\n" + ), + link=None, + level=4, + num="5.19.5.2", +) + RQ_SRS_006_RBAC_Insert = Requirement( name="RQ.SRS-006.RBAC.Insert", version="1.0", @@ -6399,7 +6434,7 @@ description=( "[ClickHouse] SHALL support controlling access to the **alter column** privilege\n" "for a database or a specific table to one or more **users** or **roles**.\n" - "Any `ALTER TABLE ... ADD|DROP|CLEAR|COMMENT|MODIFY COLUMN` statements SHALL\n" + "Any `ALTER TABLE ... ADD|DROP|CLEAR|COMMENT|MODIFY|MATERIALIZE COLUMN` statements SHALL\n" "return an error, unless the user has the **alter column** privilege for\n" "the destination table either because of the explicit grant or through one of\n" "the roles assigned to the user.\n" @@ -6454,7 +6489,7 @@ description=( "[ClickHouse] SHALL support granting or revoking **alter column** privilege\n" "for one or more specified columns in a table to one or more **users** or **roles**.\n" - "Any `ALTER TABLE ... ADD|DROP|CLEAR|COMMENT|MODIFY COLUMN` statements SHALL return an error,\n" + "Any `ALTER TABLE ... ADD|DROP|CLEAR|COMMENT|MODIFY|MATERIALIZE COLUMN` statements SHALL return an error,\n" "unless the user has the **alter column** privilege for the destination column\n" "either because of the explicit grant or through one of the roles assigned to the user.\n" "\n" @@ -6474,7 +6509,7 @@ description=( "[ClickHouse] SHALL support granting or revoking **alter column** privilege\n" "on a specified cluster to one or more **users** or **roles**.\n" - "Any `ALTER TABLE ... ADD|DROP|CLEAR|COMMENT|MODIFY COLUMN`\n" + "Any `ALTER TABLE ... ADD|DROP|CLEAR|COMMENT|MODIFY|MATERIALIZE COLUMN`\n" "statements SHALL succeed only on nodes where the table exists and privilege was granted.\n" "\n" ), @@ -7339,6 +7374,143 @@ num="5.21.10.4", ) +RQ_SRS_006_RBAC_Privileges_AlterProjection = Requirement( + name="RQ.SRS-006.RBAC.Privileges.AlterProjection", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL support granting and revoking `ALTER PROJECTION` privilege,\n" + "which permits users to **add**, **drop**, **materialize**, and **clear** projections.\n" + "\n" + ), + link=None, + level=4, + num="5.21.11.1", +) + +RQ_SRS_006_RBAC_Privileges_AlterProjection_Add = Requirement( + name="RQ.SRS-006.RBAC.Privileges.AlterProjection.Add", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL support granting and revoking `ALTER ADD PROJECTION` privilege,\n" + "which permits users to **add** projections.\n" + "\n" + ), + link=None, + level=4, + num="5.21.11.2", +) + +RQ_SRS_006_RBAC_Privileges_AlterProjection_Drop = Requirement( + name="RQ.SRS-006.RBAC.Privileges.AlterProjection.Drop", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL support granting and revoking `ALTER DROP PROJECTION` privilege,\n" + "which permits users to **drop** projections.\n" + "\n" + ), + link=None, + level=4, + num="5.21.11.3", +) + +RQ_SRS_006_RBAC_Privileges_AlterProjection_Materialize = Requirement( + name="RQ.SRS-006.RBAC.Privileges.AlterProjection.Materialize", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL support granting and revoking `ALTER MATERIALIZE PROJECTION` privilege,\n" + "which permits users to **materialize** projections.\n" + "\n" + ), + link=None, + level=4, + num="5.21.11.4", +) + +RQ_SRS_006_RBAC_Privileges_AlterProjection_Clear = Requirement( + name="RQ.SRS-006.RBAC.Privileges.AlterProjection.Clear", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL support granting and revoking `ALTER CLEAR PROJECTION` privilege,\n" + "which permits users to **clear** projections.\n" + "\n" + ), + link=None, + level=4, + num="5.21.11.5", +) + +RQ_SRS_006_RBAC_Privileges_AlterDatabase = Requirement( + name="RQ.SRS-006.RBAC.Privileges.AlterDatabase", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL support granting and revoking `ALTER DATABASE` privilege,\n" + "which permits users to **alter** a database.\n" + "\n" + ), + link=None, + level=4, + num="5.21.12.1", +) + +RQ_SRS_006_RBAC_Privileges_AlterDatabase_Settings = Requirement( + name="RQ.SRS-006.RBAC.Privileges.AlterDatabase.Settings", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL support granting and revoking `ALTER DATABASE SETTINGS` privilege,\n" + "which permits users to **alter** the settings of a database.\n" + "\n" + ), + link=None, + level=4, + num="5.21.12.2", +) + +RQ_SRS_006_RBAC_Privileges_Create = Requirement( + name="RQ.SRS-006.RBAC.Privileges.Create", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL support granting and revoking `CREATE` privilege,\n" + "which permits users to **create** tables, databases, dictionaries, and temporary tables.\n" + "This privilege does NOT grant the permission to create users, roles, or quotas, row policies, or settings profiles.\n" + "\n" + ), + link=None, + level=3, + num="5.22.1", +) + RQ_SRS_006_RBAC_Privileges_CreateTable = Requirement( name="RQ.SRS-006.RBAC.Privileges.CreateTable", version="1.0", @@ -7365,7 +7537,7 @@ ), link=None, level=3, - num="5.22.1", + num="5.22.2", ) RQ_SRS_006_RBAC_Privileges_CreateDatabase = Requirement( @@ -7382,7 +7554,7 @@ ), link=None, level=3, - num="5.22.2", + num="5.22.3", ) RQ_SRS_006_RBAC_Privileges_CreateDictionary = Requirement( @@ -7399,7 +7571,7 @@ ), link=None, level=3, - num="5.22.3", + num="5.22.4", ) RQ_SRS_006_RBAC_Privileges_CreateTemporaryTable = Requirement( @@ -7416,7 +7588,24 @@ ), link=None, level=3, - num="5.22.4", + num="5.22.5", +) + +RQ_SRS_006_RBAC_Privileges_CreateFunction = Requirement( + name="RQ.SRS-006.RBAC.Privileges.CreateFunction", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL successfully execute `CREATE FUNCTION` statement if and only if the user has **create function** privilege,\n" + "either directly or through a role.\n" + "\n" + ), + link=None, + level=3, + num="5.22.6", ) RQ_SRS_006_RBAC_Privileges_AttachDatabase = Requirement( @@ -7487,6 +7676,24 @@ num="5.23.4", ) +RQ_SRS_006_RBAC_Privileges_Drop = Requirement( + name="RQ.SRS-006.RBAC.Privileges.Drop", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL support granting and revoking `DROP` privilege,\n" + "which permits users to **drop** tables, databases, dictionaries, and temporary tables.\n" + "This privilege does NOT grant the permission to create users, roles, or quotas, row policies, or settings profiles.\n" + "\n" + ), + link=None, + level=3, + num="5.24.1", +) + RQ_SRS_006_RBAC_Privileges_DropTable = Requirement( name="RQ.SRS-006.RBAC.Privileges.DropTable", version="1.0", @@ -7501,7 +7708,7 @@ ), link=None, level=3, - num="5.24.1", + num="5.24.2", ) RQ_SRS_006_RBAC_Privileges_DropDatabase = Requirement( @@ -7518,7 +7725,7 @@ ), link=None, level=3, - num="5.24.2", + num="5.24.3", ) RQ_SRS_006_RBAC_Privileges_DropDictionary = Requirement( @@ -7535,7 +7742,24 @@ ), link=None, level=3, - num="5.24.3", + num="5.24.4", +) + +RQ_SRS_006_RBAC_Privileges_DropFunction = Requirement( + name="RQ.SRS-006.RBAC.Privileges.DropFunction", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL successfully execute `DROP FUNCTION` statement if and only if the user has **drop function** privilege,\n" + "either directly or through a role.\n" + "\n" + ), + link=None, + level=3, + num="5.24.5", ) RQ_SRS_006_RBAC_Privileges_DetachTable = Requirement( @@ -8694,6 +8918,23 @@ num="5.33.2", ) +RQ_SRS_006_RBAC_Privileges_Introspection_addressToLineWithInlines = Requirement( + name="RQ.SRS-006.RBAC.Privileges.Introspection.addressToLineWithInlines", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL successfully execute `addressToLineWithInlines` statement if and only if\n" + "the user has **introspection** privilege, either directly or through a role.\n" + "\n" + ), + link=None, + level=3, + num="5.33.3", +) + RQ_SRS_006_RBAC_Privileges_Introspection_addressToSymbol = Requirement( name="RQ.SRS-006.RBAC.Privileges.Introspection.addressToSymbol", version="1.0", @@ -8708,7 +8949,7 @@ ), link=None, level=3, - num="5.33.3", + num="5.33.4", ) RQ_SRS_006_RBAC_Privileges_Introspection_demangle = Requirement( @@ -8725,7 +8966,7 @@ ), link=None, level=3, - num="5.33.4", + num="5.33.5", ) RQ_SRS_006_RBAC_Privileges_System_Shutdown = Requirement( @@ -8816,6 +9057,42 @@ num="5.34.5", ) +RQ_SRS_006_RBAC_Privileges_System_DropCache_Mmap = Requirement( + name="RQ.SRS-006.RBAC.Privileges.System.DropCache.Mmap", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL successfully grant `SYSTEM DROP MMAP CACHE` privilege when\n" + "the user is granted `SYSTEM`, `SYSTEM DROP CACHE`, `DROP CACHE`, `SYSTEM DROP MMAP CACHE`,\n" + "`SYSTEM DROP MMAP`, `DROP MMAP CACHE`, or `DROP MMAP`.\n" + "\n" + ), + link=None, + level=3, + num="5.34.6", +) + +RQ_SRS_006_RBAC_Privileges_System_DropCache_CompiledExpression = Requirement( + name="RQ.SRS-006.RBAC.Privileges.System.DropCache.CompiledExpression", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL successfully grant `SYSTEM DROP COMPILED EXPRESSION CACHE` privilege when\n" + "the user is granted `SYSTEM`, `SYSTEM DROP CACHE`, `DROP CACHE`, `SYSTEM DROP COMPILED EXPRESSION CACHE`,\n" + "`SYSTEM DROP COMPILED EXPRESSION`, or `DROP COMPILED EXPRESSION CACHE`\n" + "\n" + ), + link=None, + level=3, + num="5.34.7", +) + RQ_SRS_006_RBAC_Privileges_System_Reload = Requirement( name="RQ.SRS-006.RBAC.Privileges.System.Reload", version="1.0", @@ -8830,7 +9107,7 @@ ), link=None, level=3, - num="5.34.6", + num="5.34.8", ) RQ_SRS_006_RBAC_Privileges_System_Reload_Config = Requirement( @@ -8847,7 +9124,7 @@ ), link=None, level=3, - num="5.34.7", + num="5.34.9", ) RQ_SRS_006_RBAC_Privileges_System_Reload_Dictionary = Requirement( @@ -8864,7 +9141,7 @@ ), link=None, level=3, - num="5.34.8", + num="5.34.10", ) RQ_SRS_006_RBAC_Privileges_System_Reload_Dictionaries = Requirement( @@ -8881,7 +9158,7 @@ ), link=None, level=3, - num="5.34.9", + num="5.34.11", ) RQ_SRS_006_RBAC_Privileges_System_Reload_EmbeddedDictionaries = Requirement( @@ -8898,7 +9175,75 @@ ), link=None, level=3, - num="5.34.10", + num="5.34.12", +) + +RQ_SRS_006_RBAC_Privileges_System_Reload_Symbols = Requirement( + name="RQ.SRS-006.RBAC.Privileges.System.Reload.Symbols", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL successfully grant `SYSTEM RELOAD SYMBOLS` privilege when\n" + "the user is granted `SYSTEM`, `SYSTEM RELOAD`, or `SYSTEM RELOAD SYMBOLS`.\n" + "\n" + ), + link=None, + level=3, + num="5.34.13", +) + +RQ_SRS_006_RBAC_Privileges_System_Reload_Function = Requirement( + name="RQ.SRS-006.RBAC.Privileges.System.Reload.Function", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL successfully grant `SYSTEM RELOAD FUNCTION` privilege when\n" + "the user is granted `SYSTEM`, `SYSTEM RELOAD`, or `SYSTEM RELOAD FUNCTION`.\n" + "\n" + ), + link=None, + level=3, + num="5.34.14", +) + +RQ_SRS_006_RBAC_Privileges_System_RestartDisk = Requirement( + name="RQ.SRS-006.RBAC.Privileges.System.RestartDisk", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL successfully grant `SYSTEM RESTART DISK` privilege when\n" + "the user is granted `SYSTEM RESTART DISK`.\n" + "\n" + ), + link=None, + level=3, + num="5.34.15", +) + +RQ_SRS_006_RBAC_Privileges_System_ThreadFuzzer = Requirement( + name="RQ.SRS-006.RBAC.Privileges.System.ThreadFuzzer", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL successfully grant `SYSTEM THREAD FUZZER` privilege when\n" + "the user is granted `SYSTEM THREAD FUZZER`.\n" + "\n" + ), + link=None, + level=3, + num="5.34.16", ) RQ_SRS_006_RBAC_Privileges_System_Merges = Requirement( @@ -8915,7 +9260,7 @@ ), link=None, level=3, - num="5.34.11", + num="5.34.17", ) RQ_SRS_006_RBAC_Privileges_System_TTLMerges = Requirement( @@ -8932,7 +9277,7 @@ ), link=None, level=3, - num="5.34.12", + num="5.34.18", ) RQ_SRS_006_RBAC_Privileges_System_Fetches = Requirement( @@ -8949,7 +9294,7 @@ ), link=None, level=3, - num="5.34.13", + num="5.34.19", ) RQ_SRS_006_RBAC_Privileges_System_Moves = Requirement( @@ -8966,7 +9311,7 @@ ), link=None, level=3, - num="5.34.14", + num="5.34.20", ) RQ_SRS_006_RBAC_Privileges_System_Sends = Requirement( @@ -8983,7 +9328,7 @@ ), link=None, level=3, - num="5.34.15", + num="5.34.21", ) RQ_SRS_006_RBAC_Privileges_System_Sends_Distributed = Requirement( @@ -9001,7 +9346,7 @@ ), link=None, level=3, - num="5.34.16", + num="5.34.22", ) RQ_SRS_006_RBAC_Privileges_System_Sends_Replicated = Requirement( @@ -9019,7 +9364,7 @@ ), link=None, level=3, - num="5.34.17", + num="5.34.23", ) RQ_SRS_006_RBAC_Privileges_System_ReplicationQueues = Requirement( @@ -9037,7 +9382,7 @@ ), link=None, level=3, - num="5.34.18", + num="5.34.24", ) RQ_SRS_006_RBAC_Privileges_System_SyncReplica = Requirement( @@ -9054,7 +9399,7 @@ ), link=None, level=3, - num="5.34.19", + num="5.34.25", ) RQ_SRS_006_RBAC_Privileges_System_RestartReplica = Requirement( @@ -9071,7 +9416,41 @@ ), link=None, level=3, - num="5.34.20", + num="5.34.26", +) + +RQ_SRS_006_RBAC_Privileges_System_DropReplica = Requirement( + name="RQ.SRS-006.RBAC.Privileges.System.DropReplica", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL successfully grant `SYSTEM DROP REPLICA` privilege when\n" + "the user is granted `SYSTEM`, `SYSTEM DROP REPLICA`, or `DROP REPLICA`.\n" + "\n" + ), + link=None, + level=3, + num="5.34.27", +) + +RQ_SRS_006_RBAC_Privileges_System_RestoreReplica = Requirement( + name="RQ.SRS-006.RBAC.Privileges.System.RestoreReplica", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL successfully grant `SYSTEM RESTORE REPLICA` privilege when\n" + "the user is granted `SYSTEM`, `SYSTEM RESTORE REPLICA`, or `RESTORE REPLICA`.\n" + "\n" + ), + link=None, + level=3, + num="5.34.28", ) RQ_SRS_006_RBAC_Privileges_System_Flush = Requirement( @@ -9088,7 +9467,7 @@ ), link=None, level=3, - num="5.34.21", + num="5.34.29", ) RQ_SRS_006_RBAC_Privileges_System_Flush_Distributed = Requirement( @@ -9105,7 +9484,7 @@ ), link=None, level=3, - num="5.34.22", + num="5.34.30", ) RQ_SRS_006_RBAC_Privileges_System_Flush_Logs = Requirement( @@ -9122,7 +9501,7 @@ ), link=None, level=3, - num="5.34.23", + num="5.34.31", ) RQ_SRS_006_RBAC_Privileges_Sources = Requirement( @@ -9278,6 +9657,57 @@ num="5.35.9", ) +RQ_SRS_006_RBAC_Privileges_Sources_Mongo = Requirement( + name="RQ.SRS-006.RBAC.Privileges.Sources.Mongo", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL support the use of `MONGO` source by a user if and only if\n" + "the user has `MONGO` or `SOURCES` privileges granted to them directly or through a role.\n" + "\n" + ), + link=None, + level=3, + num="5.35.10", +) + +RQ_SRS_006_RBAC_Privileges_Sources_Postgres = Requirement( + name="RQ.SRS-006.RBAC.Privileges.Sources.Postgres", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL support the use of `POSTGRES` source by a user if and only if\n" + "the user has `POSTGRES` or `SOURCES` privileges granted to them directly or through a role.\n" + "\n" + ), + link=None, + level=3, + num="5.35.11", +) + +RQ_SRS_006_RBAC_Privileges_Sources_Sqlite = Requirement( + name="RQ.SRS-006.RBAC.Privileges.Sources.Sqlite", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL support the use of `SQLITE` source by a user if and only if\n" + "the user has `SQLITE` or `SOURCES` privileges granted to them directly or through a role.\n" + "\n" + ), + link=None, + level=3, + num="5.35.12", +) + RQ_SRS_006_RBAC_Privileges_GrantOption = Requirement( name="RQ.SRS-006.RBAC.Privileges.GrantOption", version="1.0", @@ -10219,6 +10649,15 @@ Heading(name="RQ.SRS-006.RBAC.Select.Column", level=3, num="5.19.2"), Heading(name="RQ.SRS-006.RBAC.Select.Cluster", level=3, num="5.19.3"), Heading(name="RQ.SRS-006.RBAC.Select.TableEngines", level=3, num="5.19.4"), + Heading(name="Table Functions", level=3, num="5.19.5"), + Heading( + name="RQ.SRS-006.RBAC.Select.TableFunctions.Remote", level=4, num="5.19.5.1" + ), + Heading( + name="RQ.SRS-006.RBAC.Select.TableFunctions.Cluster", + level=4, + num="5.19.5.2", + ), Heading(name="Insert", level=2, num="5.20"), Heading(name="RQ.SRS-006.RBAC.Insert", level=3, num="5.20.1"), Heading(name="RQ.SRS-006.RBAC.Insert.Column", level=3, num="5.20.2"), @@ -10403,18 +10842,55 @@ level=4, num="5.21.10.4", ), + Heading(name="Alter Projection", level=3, num="5.21.11"), + Heading( + name="RQ.SRS-006.RBAC.Privileges.AlterProjection", level=4, num="5.21.11.1" + ), + Heading( + name="RQ.SRS-006.RBAC.Privileges.AlterProjection.Add", + level=4, + num="5.21.11.2", + ), + Heading( + name="RQ.SRS-006.RBAC.Privileges.AlterProjection.Drop", + level=4, + num="5.21.11.3", + ), + Heading( + name="RQ.SRS-006.RBAC.Privileges.AlterProjection.Materialize", + level=4, + num="5.21.11.4", + ), + Heading( + name="RQ.SRS-006.RBAC.Privileges.AlterProjection.Clear", + level=4, + num="5.21.11.5", + ), + Heading(name="Alter Database", level=3, num="5.21.12"), + Heading( + name="RQ.SRS-006.RBAC.Privileges.AlterDatabase", level=4, num="5.21.12.1" + ), + Heading( + name="RQ.SRS-006.RBAC.Privileges.AlterDatabase.Settings", + level=4, + num="5.21.12.2", + ), Heading(name="Create", level=2, num="5.22"), - Heading(name="RQ.SRS-006.RBAC.Privileges.CreateTable", level=3, num="5.22.1"), + Heading(name="RQ.SRS-006.RBAC.Privileges.Create", level=3, num="5.22.1"), + Heading(name="RQ.SRS-006.RBAC.Privileges.CreateTable", level=3, num="5.22.2"), Heading( - name="RQ.SRS-006.RBAC.Privileges.CreateDatabase", level=3, num="5.22.2" + name="RQ.SRS-006.RBAC.Privileges.CreateDatabase", level=3, num="5.22.3" ), Heading( - name="RQ.SRS-006.RBAC.Privileges.CreateDictionary", level=3, num="5.22.3" + name="RQ.SRS-006.RBAC.Privileges.CreateDictionary", level=3, num="5.22.4" ), Heading( name="RQ.SRS-006.RBAC.Privileges.CreateTemporaryTable", level=3, - num="5.22.4", + num="5.22.5", + ), + Heading( + name="RQ.SRS-006.RBAC.Privileges.CreateFunction", level=3, num="5.22.6" ), Heading(name="Attach", level=2, num="5.23"), Heading( @@ -10430,11 +10906,13 @@ ), Heading(name="RQ.SRS-006.RBAC.Privileges.AttachTable", level=3, num="5.23.4"), Heading(name="Drop", level=2, num="5.24"), - Heading(name="RQ.SRS-006.RBAC.Privileges.DropTable", level=3, num="5.24.1"), - Heading(name="RQ.SRS-006.RBAC.Privileges.DropDatabase", level=3, num="5.24.2"), + Heading(name="RQ.SRS-006.RBAC.Privileges.Drop", level=3, num="5.24.1"), + Heading(name="RQ.SRS-006.RBAC.Privileges.DropTable", level=3, num="5.24.2"), + Heading(name="RQ.SRS-006.RBAC.Privileges.DropDatabase", level=3, num="5.24.3"), Heading( - name="RQ.SRS-006.RBAC.Privileges.DropDictionary", level=3, num="5.24.3" + name="RQ.SRS-006.RBAC.Privileges.DropDictionary", level=3, num="5.24.4" ), + Heading(name="RQ.SRS-006.RBAC.Privileges.DropFunction", level=3, num="5.24.5"), Heading(name="Detach", level=2, num="5.25"), Heading(name="RQ.SRS-006.RBAC.Privileges.DetachTable", level=3, num="5.25.1"), Heading(name="RQ.SRS-006.RBAC.Privileges.DetachView", level=3, num="5.25.2"), @@ -10651,15 +11129,20 @@ num="5.33.2", ), Heading( - name="RQ.SRS-006.RBAC.Privileges.Introspection.addressToSymbol", + name="RQ.SRS-006.RBAC.Privileges.Introspection.addressToLineWithInlines", level=3, num="5.33.3", ), Heading( - name="RQ.SRS-006.RBAC.Privileges.Introspection.demangle", + name="RQ.SRS-006.RBAC.Privileges.Introspection.addressToSymbol", level=3, num="5.33.4", ), + Heading( + name="RQ.SRS-006.RBAC.Privileges.Introspection.demangle", + level=3, + num="5.33.5", + ), Heading(name="System", level=2, num="5.34"), Heading( name="RQ.SRS-006.RBAC.Privileges.System.Shutdown", level=3, num="5.34.1" @@ -10682,69 +11165,105 @@ level=3, num="5.34.5", ), - Heading(name="RQ.SRS-006.RBAC.Privileges.System.Reload", level=3, num="5.34.6"), Heading( - name="RQ.SRS-006.RBAC.Privileges.System.Reload.Config", + name="RQ.SRS-006.RBAC.Privileges.System.DropCache.Mmap", + level=3, + num="5.34.6", + ), + Heading( + name="RQ.SRS-006.RBAC.Privileges.System.DropCache.CompiledExpression", level=3, num="5.34.7", ), + Heading(name="RQ.SRS-006.RBAC.Privileges.System.Reload", level=3, num="5.34.8"), + Heading( + name="RQ.SRS-006.RBAC.Privileges.System.Reload.Config", + level=3, + num="5.34.9", + ), Heading( name="RQ.SRS-006.RBAC.Privileges.System.Reload.Dictionary", level=3, - num="5.34.8", + num="5.34.10", ), Heading( name="RQ.SRS-006.RBAC.Privileges.System.Reload.Dictionaries", level=3, - num="5.34.9", + num="5.34.11", ), Heading( name="RQ.SRS-006.RBAC.Privileges.System.Reload.EmbeddedDictionaries", level=3, - num="5.34.10", + num="5.34.12", ), Heading( - name="RQ.SRS-006.RBAC.Privileges.System.Merges", level=3, num="5.34.11" + name="RQ.SRS-006.RBAC.Privileges.System.Reload.Symbols", + level=3, + num="5.34.13", ), Heading( - name="RQ.SRS-006.RBAC.Privileges.System.TTLMerges", level=3, num="5.34.12" + name="RQ.SRS-006.RBAC.Privileges.System.Reload.Function", + level=3, + num="5.34.14", ), Heading( - name="RQ.SRS-006.RBAC.Privileges.System.Fetches", level=3, num="5.34.13" + name="RQ.SRS-006.RBAC.Privileges.System.RestartDisk", level=3, num="5.34.15" ), - Heading(name="RQ.SRS-006.RBAC.Privileges.System.Moves", level=3, num="5.34.14"), - Heading(name="RQ.SRS-006.RBAC.Privileges.System.Sends", level=3, num="5.34.15"), Heading( - name="RQ.SRS-006.RBAC.Privileges.System.Sends.Distributed", + name="RQ.SRS-006.RBAC.Privileges.System.ThreadFuzzer", level=3, num="5.34.16", ), + Heading( + name="RQ.SRS-006.RBAC.Privileges.System.Merges", level=3, num="5.34.17" + ), + Heading( + name="RQ.SRS-006.RBAC.Privileges.System.TTLMerges", level=3, num="5.34.18" + ), + Heading( + name="RQ.SRS-006.RBAC.Privileges.System.Fetches", level=3, num="5.34.19" + ), + Heading(name="RQ.SRS-006.RBAC.Privileges.System.Moves", level=3, num="5.34.20"), + Heading(name="RQ.SRS-006.RBAC.Privileges.System.Sends", level=3, num="5.34.21"), + Heading( + name="RQ.SRS-006.RBAC.Privileges.System.Sends.Distributed", + level=3, + num="5.34.22", + ), Heading( name="RQ.SRS-006.RBAC.Privileges.System.Sends.Replicated", level=3, - num="5.34.17", + num="5.34.23", ), Heading( name="RQ.SRS-006.RBAC.Privileges.System.ReplicationQueues", level=3, - num="5.34.18", + num="5.34.24", ), Heading( - name="RQ.SRS-006.RBAC.Privileges.System.SyncReplica", level=3, num="5.34.19" + name="RQ.SRS-006.RBAC.Privileges.System.SyncReplica", level=3, num="5.34.25" ), Heading( name="RQ.SRS-006.RBAC.Privileges.System.RestartReplica", level=3, - num="5.34.20", + num="5.34.26", + ), + Heading( + name="RQ.SRS-006.RBAC.Privileges.System.DropReplica", level=3, num="5.34.27" + ), + Heading( + name="RQ.SRS-006.RBAC.Privileges.System.RestoreReplica", + level=3, + num="5.34.28", ), - Heading(name="RQ.SRS-006.RBAC.Privileges.System.Flush", level=3, num="5.34.21"), + Heading(name="RQ.SRS-006.RBAC.Privileges.System.Flush", level=3, num="5.34.29"), Heading( name="RQ.SRS-006.RBAC.Privileges.System.Flush.Distributed", level=3, - num="5.34.22", + num="5.34.30", ), Heading( - name="RQ.SRS-006.RBAC.Privileges.System.Flush.Logs", level=3, num="5.34.23" + name="RQ.SRS-006.RBAC.Privileges.System.Flush.Logs", level=3, num="5.34.31" ), Heading(name="Sources", level=2, num="5.35"), Heading(name="RQ.SRS-006.RBAC.Privileges.Sources", level=3, num="5.35.1"), @@ -10758,6 +11277,15 @@ Heading(name="RQ.SRS-006.RBAC.Privileges.Sources.JDBC", level=3, num="5.35.7"), Heading(name="RQ.SRS-006.RBAC.Privileges.Sources.HDFS", level=3, num="5.35.8"), Heading(name="RQ.SRS-006.RBAC.Privileges.Sources.S3", level=3, num="5.35.9"), + Heading( + name="RQ.SRS-006.RBAC.Privileges.Sources.Mongo", level=3, num="5.35.10" + ), + Heading( + name="RQ.SRS-006.RBAC.Privileges.Sources.Postgres", level=3, num="5.35.11" + ), + Heading( + name="RQ.SRS-006.RBAC.Privileges.Sources.Sqlite", level=3, num="5.35.12" + ), Heading(name="RQ.SRS-006.RBAC.Privileges.GrantOption", level=2, num="5.36"), Heading(name="RQ.SRS-006.RBAC.Privileges.All", level=2, num="5.37"), Heading(name="RQ.SRS-006.RBAC.Privileges.RoleAll", level=2, num="5.38"), @@ -11113,6 +11641,8 @@ RQ_SRS_006_RBAC_Select_Column, RQ_SRS_006_RBAC_Select_Cluster, RQ_SRS_006_RBAC_Select_TableEngines, + RQ_SRS_006_RBAC_Select_TableFunctions_Remote, + RQ_SRS_006_RBAC_Select_TableFunctions_Cluster, RQ_SRS_006_RBAC_Insert, RQ_SRS_006_RBAC_Insert_Column, RQ_SRS_006_RBAC_Insert_Cluster, @@ -11163,17 +11693,28 @@ RQ_SRS_006_RBAC_Privileges_AlterMove_Grant, RQ_SRS_006_RBAC_Privileges_AlterMove_Revoke, RQ_SRS_006_RBAC_Privileges_AlterMove_TableEngines, + RQ_SRS_006_RBAC_Privileges_AlterProjection, + RQ_SRS_006_RBAC_Privileges_AlterProjection_Add, + RQ_SRS_006_RBAC_Privileges_AlterProjection_Drop, + RQ_SRS_006_RBAC_Privileges_AlterProjection_Materialize, + RQ_SRS_006_RBAC_Privileges_AlterProjection_Clear, + RQ_SRS_006_RBAC_Privileges_AlterDatabase, + RQ_SRS_006_RBAC_Privileges_AlterDatabase_Settings, + RQ_SRS_006_RBAC_Privileges_Create, RQ_SRS_006_RBAC_Privileges_CreateTable, RQ_SRS_006_RBAC_Privileges_CreateDatabase, RQ_SRS_006_RBAC_Privileges_CreateDictionary, RQ_SRS_006_RBAC_Privileges_CreateTemporaryTable, + RQ_SRS_006_RBAC_Privileges_CreateFunction, RQ_SRS_006_RBAC_Privileges_AttachDatabase, RQ_SRS_006_RBAC_Privileges_AttachDictionary, RQ_SRS_006_RBAC_Privileges_AttachTemporaryTable, RQ_SRS_006_RBAC_Privileges_AttachTable, + RQ_SRS_006_RBAC_Privileges_Drop, RQ_SRS_006_RBAC_Privileges_DropTable, RQ_SRS_006_RBAC_Privileges_DropDatabase, RQ_SRS_006_RBAC_Privileges_DropDictionary, + RQ_SRS_006_RBAC_Privileges_DropFunction, RQ_SRS_006_RBAC_Privileges_DetachTable, RQ_SRS_006_RBAC_Privileges_DetachView, RQ_SRS_006_RBAC_Privileges_DetachDatabase, @@ -11241,6 +11782,7 @@ RQ_SRS_006_RBAC_dictIsIn_RequiredPrivilege, RQ_SRS_006_RBAC_Privileges_Introspection, RQ_SRS_006_RBAC_Privileges_Introspection_addressToLine, + RQ_SRS_006_RBAC_Privileges_Introspection_addressToLineWithInlines, RQ_SRS_006_RBAC_Privileges_Introspection_addressToSymbol, RQ_SRS_006_RBAC_Privileges_Introspection_demangle, RQ_SRS_006_RBAC_Privileges_System_Shutdown, @@ -11248,11 +11790,17 @@ RQ_SRS_006_RBAC_Privileges_System_DropCache_DNS, RQ_SRS_006_RBAC_Privileges_System_DropCache_Mark, RQ_SRS_006_RBAC_Privileges_System_DropCache_Uncompressed, + RQ_SRS_006_RBAC_Privileges_System_DropCache_Mmap, + RQ_SRS_006_RBAC_Privileges_System_DropCache_CompiledExpression, RQ_SRS_006_RBAC_Privileges_System_Reload, RQ_SRS_006_RBAC_Privileges_System_Reload_Config, RQ_SRS_006_RBAC_Privileges_System_Reload_Dictionary, RQ_SRS_006_RBAC_Privileges_System_Reload_Dictionaries, RQ_SRS_006_RBAC_Privileges_System_Reload_EmbeddedDictionaries, + RQ_SRS_006_RBAC_Privileges_System_Reload_Symbols, + RQ_SRS_006_RBAC_Privileges_System_Reload_Function, + RQ_SRS_006_RBAC_Privileges_System_RestartDisk, + RQ_SRS_006_RBAC_Privileges_System_ThreadFuzzer, RQ_SRS_006_RBAC_Privileges_System_Merges, RQ_SRS_006_RBAC_Privileges_System_TTLMerges, RQ_SRS_006_RBAC_Privileges_System_Fetches, @@ -11263,6 +11811,8 @@ RQ_SRS_006_RBAC_Privileges_System_ReplicationQueues, RQ_SRS_006_RBAC_Privileges_System_SyncReplica, RQ_SRS_006_RBAC_Privileges_System_RestartReplica, + RQ_SRS_006_RBAC_Privileges_System_DropReplica, + RQ_SRS_006_RBAC_Privileges_System_RestoreReplica, RQ_SRS_006_RBAC_Privileges_System_Flush, RQ_SRS_006_RBAC_Privileges_System_Flush_Distributed, RQ_SRS_006_RBAC_Privileges_System_Flush_Logs, @@ -11275,6 +11825,9 @@ RQ_SRS_006_RBAC_Privileges_Sources_JDBC, RQ_SRS_006_RBAC_Privileges_Sources_HDFS, RQ_SRS_006_RBAC_Privileges_Sources_S3, + RQ_SRS_006_RBAC_Privileges_Sources_Mongo, + RQ_SRS_006_RBAC_Privileges_Sources_Postgres, + RQ_SRS_006_RBAC_Privileges_Sources_Sqlite, RQ_SRS_006_RBAC_Privileges_GrantOption, RQ_SRS_006_RBAC_Privileges_All, RQ_SRS_006_RBAC_Privileges_RoleAll, @@ -11286,6 +11839,7 @@ # Software Requirements Specification ## Table of Contents + * 1 [Revision History](#revision-history) * 2 [Introduction](#introduction) * 3 [Terminology](#terminology) @@ -11681,6 +12235,9 @@ * 5.19.2 [RQ.SRS-006.RBAC.Select.Column](#rqsrs-006rbacselectcolumn) * 5.19.3 [RQ.SRS-006.RBAC.Select.Cluster](#rqsrs-006rbacselectcluster) * 5.19.4 [RQ.SRS-006.RBAC.Select.TableEngines](#rqsrs-006rbacselecttableengines) + * 5.19.5 [Table Functions](#table-functions) + * 5.19.5.1 [RQ.SRS-006.RBAC.Select.TableFunctions.Remote](#rqsrs-006rbacselecttablefunctionsremote) + * 5.19.5.2 [RQ.SRS-006.RBAC.Select.TableFunctions.Cluster](#rqsrs-006rbacselecttablefunctionscluster) * 5.20 [Insert](#insert) * 5.20.1 [RQ.SRS-006.RBAC.Insert](#rqsrs-006rbacinsert) * 5.20.2 [RQ.SRS-006.RBAC.Insert.Column](#rqsrs-006rbacinsertcolumn) @@ -11743,20 +12300,33 @@ * 5.21.10.2 [RQ.SRS-006.RBAC.Privileges.AlterMove.Grant](#rqsrs-006rbacprivilegesaltermovegrant) * 5.21.10.3 [RQ.SRS-006.RBAC.Privileges.AlterMove.Revoke](#rqsrs-006rbacprivilegesaltermoverevoke) * 5.21.10.4 [RQ.SRS-006.RBAC.Privileges.AlterMove.TableEngines](#rqsrs-006rbacprivilegesaltermovetableengines) + * 5.21.11 [Alter Projection](#alter-projection) + * 5.21.11.1 [RQ.SRS-006.RBAC.Privileges.AlterProjection](#rqsrs-006rbacprivilegesalterprojection) + * 5.21.11.2 [RQ.SRS-006.RBAC.Privileges.AlterProjection.Add](#rqsrs-006rbacprivilegesalterprojectionadd) + * 5.21.11.3 [RQ.SRS-006.RBAC.Privileges.AlterProjection.Drop](#rqsrs-006rbacprivilegesalterprojectiondrop) + * 5.21.11.4 [RQ.SRS-006.RBAC.Privileges.AlterProjection.Materialize](#rqsrs-006rbacprivilegesalterprojectionmaterialize) + * 5.21.11.5 [RQ.SRS-006.RBAC.Privileges.AlterProjection.Clear](#rqsrs-006rbacprivilegesalterprojectionclear) + * 5.21.12 [Alter Database](#alter-database) + * 5.21.12.1 [RQ.SRS-006.RBAC.Privileges.AlterDatabase](#rqsrs-006rbacprivilegesalterdatabase) + * 5.21.12.2 [RQ.SRS-006.RBAC.Privileges.AlterDatabase.Settings](#rqsrs-006rbacprivilegesalterdatabasesettings) * 5.22 [Create](#create) - * 5.22.1 [RQ.SRS-006.RBAC.Privileges.CreateTable](#rqsrs-006rbacprivilegescreatetable) - * 5.22.2 [RQ.SRS-006.RBAC.Privileges.CreateDatabase](#rqsrs-006rbacprivilegescreatedatabase) - * 5.22.3 [RQ.SRS-006.RBAC.Privileges.CreateDictionary](#rqsrs-006rbacprivilegescreatedictionary) - * 5.22.4 [RQ.SRS-006.RBAC.Privileges.CreateTemporaryTable](#rqsrs-006rbacprivilegescreatetemporarytable) + * 5.22.1 [RQ.SRS-006.RBAC.Privileges.Create](#rqsrs-006rbacprivilegescreate) + * 5.22.2 [RQ.SRS-006.RBAC.Privileges.CreateTable](#rqsrs-006rbacprivilegescreatetable) + * 5.22.3 [RQ.SRS-006.RBAC.Privileges.CreateDatabase](#rqsrs-006rbacprivilegescreatedatabase) + * 5.22.4 [RQ.SRS-006.RBAC.Privileges.CreateDictionary](#rqsrs-006rbacprivilegescreatedictionary) + * 5.22.5 [RQ.SRS-006.RBAC.Privileges.CreateTemporaryTable](#rqsrs-006rbacprivilegescreatetemporarytable) + * 5.22.6 [RQ.SRS-006.RBAC.Privileges.CreateFunction](#rqsrs-006rbacprivilegescreatefunction) * 5.23 [Attach](#attach) * 5.23.1 [RQ.SRS-006.RBAC.Privileges.AttachDatabase](#rqsrs-006rbacprivilegesattachdatabase) * 5.23.2 [RQ.SRS-006.RBAC.Privileges.AttachDictionary](#rqsrs-006rbacprivilegesattachdictionary) * 5.23.3 [RQ.SRS-006.RBAC.Privileges.AttachTemporaryTable](#rqsrs-006rbacprivilegesattachtemporarytable) * 5.23.4 [RQ.SRS-006.RBAC.Privileges.AttachTable](#rqsrs-006rbacprivilegesattachtable) * 5.24 [Drop](#drop) - * 5.24.1 [RQ.SRS-006.RBAC.Privileges.DropTable](#rqsrs-006rbacprivilegesdroptable) - * 5.24.2 [RQ.SRS-006.RBAC.Privileges.DropDatabase](#rqsrs-006rbacprivilegesdropdatabase) - * 5.24.3 [RQ.SRS-006.RBAC.Privileges.DropDictionary](#rqsrs-006rbacprivilegesdropdictionary) + * 5.24.1 [RQ.SRS-006.RBAC.Privileges.Drop](#rqsrs-006rbacprivilegesdrop) + * 5.24.2 [RQ.SRS-006.RBAC.Privileges.DropTable](#rqsrs-006rbacprivilegesdroptable) + * 5.24.3 [RQ.SRS-006.RBAC.Privileges.DropDatabase](#rqsrs-006rbacprivilegesdropdatabase) + * 5.24.4 [RQ.SRS-006.RBAC.Privileges.DropDictionary](#rqsrs-006rbacprivilegesdropdictionary) + * 5.24.5 [RQ.SRS-006.RBAC.Privileges.DropFunction](#rqsrs-006rbacprivilegesdropfunction) * 5.25 [Detach](#detach) * 5.25.1 [RQ.SRS-006.RBAC.Privileges.DetachTable](#rqsrs-006rbacprivilegesdetachtable) * 5.25.2 [RQ.SRS-006.RBAC.Privileges.DetachView](#rqsrs-006rbacprivilegesdetachview) @@ -11834,32 +12404,41 @@ * 5.33 [Introspection](#introspection) * 5.33.1 [RQ.SRS-006.RBAC.Privileges.Introspection](#rqsrs-006rbacprivilegesintrospection) * 5.33.2 [RQ.SRS-006.RBAC.Privileges.Introspection.addressToLine](#rqsrs-006rbacprivilegesintrospectionaddresstoline) - * 5.33.3 [RQ.SRS-006.RBAC.Privileges.Introspection.addressToSymbol](#rqsrs-006rbacprivilegesintrospectionaddresstosymbol) - * 5.33.4 [RQ.SRS-006.RBAC.Privileges.Introspection.demangle](#rqsrs-006rbacprivilegesintrospectiondemangle) + * 5.33.3 [RQ.SRS-006.RBAC.Privileges.Introspection.addressToLineWithInlines](#rqsrs-006rbacprivilegesintrospectionaddresstolinewithinlines) + * 5.33.4 [RQ.SRS-006.RBAC.Privileges.Introspection.addressToSymbol](#rqsrs-006rbacprivilegesintrospectionaddresstosymbol) + * 5.33.5 [RQ.SRS-006.RBAC.Privileges.Introspection.demangle](#rqsrs-006rbacprivilegesintrospectiondemangle) * 5.34 [System](#system) * 5.34.1 [RQ.SRS-006.RBAC.Privileges.System.Shutdown](#rqsrs-006rbacprivilegessystemshutdown) * 5.34.2 [RQ.SRS-006.RBAC.Privileges.System.DropCache](#rqsrs-006rbacprivilegessystemdropcache) * 5.34.3 [RQ.SRS-006.RBAC.Privileges.System.DropCache.DNS](#rqsrs-006rbacprivilegessystemdropcachedns) * 5.34.4 [RQ.SRS-006.RBAC.Privileges.System.DropCache.Mark](#rqsrs-006rbacprivilegessystemdropcachemark) * 5.34.5 [RQ.SRS-006.RBAC.Privileges.System.DropCache.Uncompressed](#rqsrs-006rbacprivilegessystemdropcacheuncompressed) - * 5.34.6 [RQ.SRS-006.RBAC.Privileges.System.Reload](#rqsrs-006rbacprivilegessystemreload) - * 5.34.7 [RQ.SRS-006.RBAC.Privileges.System.Reload.Config](#rqsrs-006rbacprivilegessystemreloadconfig) - * 5.34.8 [RQ.SRS-006.RBAC.Privileges.System.Reload.Dictionary](#rqsrs-006rbacprivilegessystemreloaddictionary) - * 5.34.9 [RQ.SRS-006.RBAC.Privileges.System.Reload.Dictionaries](#rqsrs-006rbacprivilegessystemreloaddictionaries) - * 5.34.10 [RQ.SRS-006.RBAC.Privileges.System.Reload.EmbeddedDictionaries](#rqsrs-006rbacprivilegessystemreloadembeddeddictionaries) - * 5.34.11 [RQ.SRS-006.RBAC.Privileges.System.Merges](#rqsrs-006rbacprivilegessystemmerges) - * 5.34.12 [RQ.SRS-006.RBAC.Privileges.System.TTLMerges](#rqsrs-006rbacprivilegessystemttlmerges) - * 5.34.13 [RQ.SRS-006.RBAC.Privileges.System.Fetches](#rqsrs-006rbacprivilegessystemfetches) - * 5.34.14 [RQ.SRS-006.RBAC.Privileges.System.Moves](#rqsrs-006rbacprivilegessystemmoves) - * 5.34.15 [RQ.SRS-006.RBAC.Privileges.System.Sends](#rqsrs-006rbacprivilegessystemsends) - * 5.34.16 [RQ.SRS-006.RBAC.Privileges.System.Sends.Distributed](#rqsrs-006rbacprivilegessystemsendsdistributed) - * 5.34.17 [RQ.SRS-006.RBAC.Privileges.System.Sends.Replicated](#rqsrs-006rbacprivilegessystemsendsreplicated) - * 5.34.18 [RQ.SRS-006.RBAC.Privileges.System.ReplicationQueues](#rqsrs-006rbacprivilegessystemreplicationqueues) - * 5.34.19 [RQ.SRS-006.RBAC.Privileges.System.SyncReplica](#rqsrs-006rbacprivilegessystemsyncreplica) - * 5.34.20 [RQ.SRS-006.RBAC.Privileges.System.RestartReplica](#rqsrs-006rbacprivilegessystemrestartreplica) - * 5.34.21 [RQ.SRS-006.RBAC.Privileges.System.Flush](#rqsrs-006rbacprivilegessystemflush) - * 5.34.22 [RQ.SRS-006.RBAC.Privileges.System.Flush.Distributed](#rqsrs-006rbacprivilegessystemflushdistributed) - * 5.34.23 [RQ.SRS-006.RBAC.Privileges.System.Flush.Logs](#rqsrs-006rbacprivilegessystemflushlogs) + * 5.34.6 [RQ.SRS-006.RBAC.Privileges.System.DropCache.Mmap](#rqsrs-006rbacprivilegessystemdropcachemmap) + * 5.34.7 [RQ.SRS-006.RBAC.Privileges.System.DropCache.CompiledExpression](#rqsrs-006rbacprivilegessystemdropcachecompiledexpression) + * 5.34.8 [RQ.SRS-006.RBAC.Privileges.System.Reload](#rqsrs-006rbacprivilegessystemreload) + * 5.34.9 [RQ.SRS-006.RBAC.Privileges.System.Reload.Config](#rqsrs-006rbacprivilegessystemreloadconfig) + * 5.34.10 [RQ.SRS-006.RBAC.Privileges.System.Reload.Dictionary](#rqsrs-006rbacprivilegessystemreloaddictionary) + * 5.34.11 [RQ.SRS-006.RBAC.Privileges.System.Reload.Dictionaries](#rqsrs-006rbacprivilegessystemreloaddictionaries) + * 5.34.12 [RQ.SRS-006.RBAC.Privileges.System.Reload.EmbeddedDictionaries](#rqsrs-006rbacprivilegessystemreloadembeddeddictionaries) + * 5.34.13 [RQ.SRS-006.RBAC.Privileges.System.Reload.Symbols](#rqsrs-006rbacprivilegessystemreloadsymbols) + * 5.34.14 [RQ.SRS-006.RBAC.Privileges.System.Reload.Function](#rqsrs-006rbacprivilegessystemreloadfunction) + * 5.34.15 [RQ.SRS-006.RBAC.Privileges.System.RestartDisk](#rqsrs-006rbacprivilegessystemrestartdisk) + * 5.34.16 [RQ.SRS-006.RBAC.Privileges.System.ThreadFuzzer](#rqsrs-006rbacprivilegessystemthreadfuzzer) + * 5.34.17 [RQ.SRS-006.RBAC.Privileges.System.Merges](#rqsrs-006rbacprivilegessystemmerges) + * 5.34.18 [RQ.SRS-006.RBAC.Privileges.System.TTLMerges](#rqsrs-006rbacprivilegessystemttlmerges) + * 5.34.19 [RQ.SRS-006.RBAC.Privileges.System.Fetches](#rqsrs-006rbacprivilegessystemfetches) + * 5.34.20 [RQ.SRS-006.RBAC.Privileges.System.Moves](#rqsrs-006rbacprivilegessystemmoves) + * 5.34.21 [RQ.SRS-006.RBAC.Privileges.System.Sends](#rqsrs-006rbacprivilegessystemsends) + * 5.34.22 [RQ.SRS-006.RBAC.Privileges.System.Sends.Distributed](#rqsrs-006rbacprivilegessystemsendsdistributed) + * 5.34.23 [RQ.SRS-006.RBAC.Privileges.System.Sends.Replicated](#rqsrs-006rbacprivilegessystemsendsreplicated) + * 5.34.24 [RQ.SRS-006.RBAC.Privileges.System.ReplicationQueues](#rqsrs-006rbacprivilegessystemreplicationqueues) + * 5.34.25 [RQ.SRS-006.RBAC.Privileges.System.SyncReplica](#rqsrs-006rbacprivilegessystemsyncreplica) + * 5.34.26 [RQ.SRS-006.RBAC.Privileges.System.RestartReplica](#rqsrs-006rbacprivilegessystemrestartreplica) + * 5.34.27 [RQ.SRS-006.RBAC.Privileges.System.DropReplica](#rqsrs-006rbacprivilegessystemdropreplica) + * 5.34.28 [RQ.SRS-006.RBAC.Privileges.System.RestoreReplica](#rqsrs-006rbacprivilegessystemrestorereplica) + * 5.34.29 [RQ.SRS-006.RBAC.Privileges.System.Flush](#rqsrs-006rbacprivilegessystemflush) + * 5.34.30 [RQ.SRS-006.RBAC.Privileges.System.Flush.Distributed](#rqsrs-006rbacprivilegessystemflushdistributed) + * 5.34.31 [RQ.SRS-006.RBAC.Privileges.System.Flush.Logs](#rqsrs-006rbacprivilegessystemflushlogs) * 5.35 [Sources](#sources) * 5.35.1 [RQ.SRS-006.RBAC.Privileges.Sources](#rqsrs-006rbacprivilegessources) * 5.35.2 [RQ.SRS-006.RBAC.Privileges.Sources.File](#rqsrs-006rbacprivilegessourcesfile) @@ -11870,6 +12449,9 @@ * 5.35.7 [RQ.SRS-006.RBAC.Privileges.Sources.JDBC](#rqsrs-006rbacprivilegessourcesjdbc) * 5.35.8 [RQ.SRS-006.RBAC.Privileges.Sources.HDFS](#rqsrs-006rbacprivilegessourceshdfs) * 5.35.9 [RQ.SRS-006.RBAC.Privileges.Sources.S3](#rqsrs-006rbacprivilegessourcess3) + * 5.35.10 [RQ.SRS-006.RBAC.Privileges.Sources.Mongo](#rqsrs-006rbacprivilegessourcesmongo) + * 5.35.11 [RQ.SRS-006.RBAC.Privileges.Sources.Postgres](#rqsrs-006rbacprivilegessourcespostgres) + * 5.35.12 [RQ.SRS-006.RBAC.Privileges.Sources.Sqlite](#rqsrs-006rbacprivilegessourcessqlite) * 5.36 [RQ.SRS-006.RBAC.Privileges.GrantOption](#rqsrs-006rbacprivilegesgrantoption) * 5.37 [RQ.SRS-006.RBAC.Privileges.All](#rqsrs-006rbacprivilegesall) * 5.38 [RQ.SRS-006.RBAC.Privileges.RoleAll](#rqsrs-006rbacprivilegesroleall) @@ -14506,6 +15088,21 @@ * ReplicatedVersionedCollapsingMergeTree * ReplicatedGraphiteMergeTree +#### Table Functions + +##### RQ.SRS-006.RBAC.Select.TableFunctions.Remote +version: 1.0 + +[ClickHouse] SHALL only execute `SELECT FROM remote()` query if +the user has **REMOTE**, **CREATE TEMPORARY TABLE** and **SELECT** privileges. + + +##### RQ.SRS-006.RBAC.Select.TableFunctions.Cluster +version: 1.0 + +[ClickHouse] SHALL only execute `SELECT FROM cluster()` query if +the user has **REMOTE**, **CREATE TEMPORARY TABLE** and **SELECT** privileges. + ### Insert #### RQ.SRS-006.RBAC.Insert @@ -14562,7 +15159,7 @@ [ClickHouse] SHALL support controlling access to the **alter column** privilege for a database or a specific table to one or more **users** or **roles**. -Any `ALTER TABLE ... ADD|DROP|CLEAR|COMMENT|MODIFY COLUMN` statements SHALL +Any `ALTER TABLE ... ADD|DROP|CLEAR|COMMENT|MODIFY|MATERIALIZE COLUMN` statements SHALL return an error, unless the user has the **alter column** privilege for the destination table either because of the explicit grant or through one of the roles assigned to the user. @@ -14584,7 +15181,7 @@ [ClickHouse] SHALL support granting or revoking **alter column** privilege for one or more specified columns in a table to one or more **users** or **roles**. -Any `ALTER TABLE ... ADD|DROP|CLEAR|COMMENT|MODIFY COLUMN` statements SHALL return an error, +Any `ALTER TABLE ... ADD|DROP|CLEAR|COMMENT|MODIFY|MATERIALIZE COLUMN` statements SHALL return an error, unless the user has the **alter column** privilege for the destination column either because of the explicit grant or through one of the roles assigned to the user. @@ -14593,7 +15190,7 @@ [ClickHouse] SHALL support granting or revoking **alter column** privilege on a specified cluster to one or more **users** or **roles**. -Any `ALTER TABLE ... ADD|DROP|CLEAR|COMMENT|MODIFY COLUMN` +Any `ALTER TABLE ... ADD|DROP|CLEAR|COMMENT|MODIFY|MATERIALIZE COLUMN` statements SHALL succeed only on nodes where the table exists and privilege was granted. ##### RQ.SRS-006.RBAC.Privileges.AlterColumn.TableEngines @@ -15019,8 +15616,61 @@ * ReplicatedVersionedCollapsingMergeTree * ReplicatedGraphiteMergeTree +#### Alter Projection + +##### RQ.SRS-006.RBAC.Privileges.AlterProjection +version: 1.0 + +[ClickHouse] SHALL support granting and revoking `ALTER PROJECTION` privilege, +which permits users to **add**, **drop**, **materialize**, and **clear** projections. + +##### RQ.SRS-006.RBAC.Privileges.AlterProjection.Add +version: 1.0 + +[ClickHouse] SHALL support granting and revoking `ALTER ADD PROJECTION` privilege, +which permits users to **add** projections. + +##### RQ.SRS-006.RBAC.Privileges.AlterProjection.Drop +version: 1.0 + +[ClickHouse] SHALL support granting and revoking `ALTER DROP PROJECTION` privilege, +which permits users to **drop** projections. + +##### RQ.SRS-006.RBAC.Privileges.AlterProjection.Materialize +version: 1.0 + +[ClickHouse] SHALL support granting and revoking `ALTER MATERIALIZE PROJECTION` privilege, +which permits users to **materialize** projections. + +##### RQ.SRS-006.RBAC.Privileges.AlterProjection.Clear +version: 1.0 + +[ClickHouse] SHALL support granting and revoking `ALTER CLEAR PROJECTION` privilege, +which permits users to **clear** projections. + +#### Alter Database + +##### RQ.SRS-006.RBAC.Privileges.AlterDatabase +version: 1.0 + +[ClickHouse] SHALL support granting and revoking `ALTER DATABASE` privilege, +which permits users to **alter** a database. + +##### RQ.SRS-006.RBAC.Privileges.AlterDatabase.Settings +version: 1.0 + +[ClickHouse] SHALL support granting and revoking `ALTER DATABASE SETTINGS` privilege, +which permits users to **alter** the settings of a database. + ### Create +#### RQ.SRS-006.RBAC.Privileges.Create +version: 1.0 + +[ClickHouse] SHALL support granting and revoking `CREATE` privilege, +which permits users to **create** tables, databases, dictionaries, and temporary tables. +This privilege does NOT grant the permission to create users, roles, or quotas, row policies, or settings profiles. + #### RQ.SRS-006.RBAC.Privileges.CreateTable version: 1.0 @@ -15057,6 +15707,12 @@ [ClickHouse] SHALL successfully execute `CREATE TEMPORARY TABLE` statement if and only if the user has **create temporary table** privilege on the table, either directly or through a role. +#### RQ.SRS-006.RBAC.Privileges.CreateFunction +version: 1.0 + +[ClickHouse] SHALL successfully execute `CREATE FUNCTION` statement if and only if the user has **create function** privilege, +either directly or through a role. + ### Attach #### RQ.SRS-006.RBAC.Privileges.AttachDatabase @@ -15085,6 +15741,13 @@ ### Drop +#### RQ.SRS-006.RBAC.Privileges.Drop +version: 1.0 + +[ClickHouse] SHALL support granting and revoking `DROP` privilege, +which permits users to **drop** tables, databases, dictionaries, and temporary tables. +This privilege does NOT grant the permission to create users, roles, or quotas, row policies, or settings profiles. + #### RQ.SRS-006.RBAC.Privileges.DropTable version: 1.0 @@ -15103,6 +15766,12 @@ [ClickHouse] SHALL successfully execute `DROP DICTIONARY` statement if and only if the user has **drop dictionary** privilege on the dictionary, either directly or through a role. +#### RQ.SRS-006.RBAC.Privileges.DropFunction +version: 1.0 + +[ClickHouse] SHALL successfully execute `DROP FUNCTION` statement if and only if the user has **drop function** privilege, +either directly or through a role. + ### Detach #### RQ.SRS-006.RBAC.Privileges.DetachTable @@ -15542,6 +16211,12 @@ [ClickHouse] SHALL successfully execute `addressToLine` statement if and only if the user has **introspection** privilege, either directly or through a role. +#### RQ.SRS-006.RBAC.Privileges.Introspection.addressToLineWithInlines +version: 1.0 + +[ClickHouse] SHALL successfully execute `addressToLineWithInlines` statement if and only if +the user has **introspection** privilege, either directly or through a role. + #### RQ.SRS-006.RBAC.Privileges.Introspection.addressToSymbol version: 1.0 @@ -15589,6 +16264,20 @@ the user is granted `SYSTEM`, `SYSTEM DROP CACHE`, `DROP CACHE`, `SYSTEM DROP UNCOMPRESSED CACHE`, `SYSTEM DROP UNCOMPRESSED`, `DROP UNCOMPRESSED CACHE`, or `DROP UNCOMPRESSED`. +#### RQ.SRS-006.RBAC.Privileges.System.DropCache.Mmap +version: 1.0 + +[ClickHouse] SHALL successfully grant `SYSTEM DROP MMAP CACHE` privilege when +the user is granted `SYSTEM`, `SYSTEM DROP CACHE`, `DROP CACHE`, `SYSTEM DROP MMAP CACHE`, +`SYSTEM DROP MMAP`, `DROP MMAP CACHE`, or `DROP MMAP`. + +#### RQ.SRS-006.RBAC.Privileges.System.DropCache.CompiledExpression +version: 1.0 + +[ClickHouse] SHALL successfully grant `SYSTEM DROP COMPILED EXPRESSION CACHE` privilege when +the user is granted `SYSTEM`, `SYSTEM DROP CACHE`, `DROP CACHE`, `SYSTEM DROP COMPILED EXPRESSION CACHE`, +`SYSTEM DROP COMPILED EXPRESSION`, or `DROP COMPILED EXPRESSION CACHE` + #### RQ.SRS-006.RBAC.Privileges.System.Reload version: 1.0 @@ -15619,6 +16308,30 @@ [ClickHouse] SHALL successfully grant `SYSTEM RELOAD EMBEDDED DICTIONARIES` privilege when the user is granted `SYSTEM`, `SYSTEM RELOAD`, `SYSTEM RELOAD DICTIONARY ON *.*`, or `SYSTEM RELOAD EMBEDDED DICTIONARIES`. +#### RQ.SRS-006.RBAC.Privileges.System.Reload.Symbols +version: 1.0 + +[ClickHouse] SHALL successfully grant `SYSTEM RELOAD SYMBOLS` privilege when +the user is granted `SYSTEM`, `SYSTEM RELOAD`, or `SYSTEM RELOAD SYMBOLS`. + +#### RQ.SRS-006.RBAC.Privileges.System.Reload.Function +version: 1.0 + +[ClickHouse] SHALL successfully grant `SYSTEM RELOAD FUNCTION` privilege when +the user is granted `SYSTEM`, `SYSTEM RELOAD`, or `SYSTEM RELOAD FUNCTION`. + +#### RQ.SRS-006.RBAC.Privileges.System.RestartDisk +version: 1.0 + +[ClickHouse] SHALL successfully grant `SYSTEM RESTART DISK` privilege when +the user is granted `SYSTEM RESTART DISK`. + +#### RQ.SRS-006.RBAC.Privileges.System.ThreadFuzzer +version: 1.0 + +[ClickHouse] SHALL successfully grant `SYSTEM THREAD FUZZER` privilege when +the user is granted `SYSTEM THREAD FUZZER`. + #### RQ.SRS-006.RBAC.Privileges.System.Merges version: 1.0 @@ -15682,6 +16395,18 @@ [ClickHouse] SHALL successfully grant `SYSTEM RESTART REPLICA` privilege when the user is granted `SYSTEM`, `SYSTEM RESTART REPLICA`, or `RESTART REPLICA`. +#### RQ.SRS-006.RBAC.Privileges.System.DropReplica +version: 1.0 + +[ClickHouse] SHALL successfully grant `SYSTEM DROP REPLICA` privilege when +the user is granted `SYSTEM`, `SYSTEM DROP REPLICA`, or `DROP REPLICA`. + +#### RQ.SRS-006.RBAC.Privileges.System.RestoreReplica +version: 1.0 + +[ClickHouse] SHALL successfully grant `SYSTEM RESTORE REPLICA` privilege when +the user is granted `SYSTEM`, `SYSTEM RESTORE REPLICA`, or `RESTORE REPLICA`. + #### RQ.SRS-006.RBAC.Privileges.System.Flush version: 1.0 @@ -15756,6 +16481,24 @@ [ClickHouse] SHALL support the use of `S3` source by a user if and only if the user has `S3` or `SOURCES` privileges granted to them directly or through a role. +#### RQ.SRS-006.RBAC.Privileges.Sources.Mongo +version: 1.0 + +[ClickHouse] SHALL support the use of `MONGO` source by a user if and only if +the user has `MONGO` or `SOURCES` privileges granted to them directly or through a role. + +#### RQ.SRS-006.RBAC.Privileges.Sources.Postgres +version: 1.0 + +[ClickHouse] SHALL support the use of `POSTGRES` source by a user if and only if +the user has `POSTGRES` or `SOURCES` privileges granted to them directly or through a role. + +#### RQ.SRS-006.RBAC.Privileges.Sources.Sqlite +version: 1.0 + +[ClickHouse] SHALL support the use of `SQLITE` source by a user if and only if +the user has `SQLITE` or `SOURCES` privileges granted to them directly or through a role. + ### RQ.SRS-006.RBAC.Privileges.GrantOption version: 1.0 diff --git a/tests/testflows/rbac/tests/privileges/alter/alter_database.py b/tests/testflows/rbac/tests/privileges/alter/alter_database.py new file mode 100644 index 000000000000..208b29b95aa7 --- /dev/null +++ b/tests/testflows/rbac/tests/privileges/alter/alter_database.py @@ -0,0 +1,144 @@ +from rbac.requirements import * +from rbac.helper.common import * +import rbac.helper.errors as errors + + +@TestSuite +def privilege_granted_directly_or_via_role(self, node=None): + """Check that user is only able to execute ALTER DATABASE when they have required privilege, either directly or via role.""" + role_name = f"role_{getuid()}" + user_name = f"user_{getuid()}" + + if node is None: + node = self.context.node + + with Suite("user with direct privilege"): + with user(node, user_name): + + with When( + f"I run checks that {user_name} is only able to execute ALTER DATABASE with required privileges" + ): + privilege_check( + grant_target_name=user_name, user_name=user_name, node=node + ) + + with Suite("user with privilege via role"): + with user(node, user_name), role(node, role_name): + + with When("I grant the role to the user"): + node.query(f"GRANT {role_name} TO {user_name}") + + with And( + f"I run checks that {user_name} with {role_name} is only able to execute ALTER DATABASE with required privileges" + ): + privilege_check( + grant_target_name=role_name, user_name=user_name, node=node + ) + + +def privilege_check(grant_target_name, user_name, node=None): + """Run scenarios to check the user's access with different privileges.""" + exitcode, message = errors.not_enough_privileges(name=f"{user_name}") + + with Scenario("user without privilege"): + db_name = f"db_{getuid()}" + + try: + with Given("I have a database"): + node.query(f"CREATE DATABASE {db_name}") + + with When("I grant the user NONE privilege"): + node.query(f"GRANT NONE TO {grant_target_name}") + + with And("I grant the user USAGE privilege"): + node.query(f"GRANT USAGE ON *.* TO {grant_target_name}") + + with Then("I attempt to alter a database"): + node.query( + f"ALTER DATABASE {db_name} MODIFY SETTING engine='Lazy'", + settings=[("user", user_name)], + exitcode=exitcode, + message=message, + ) + finally: + with Finally("I drop the database"): + node.query(f"DROP DATABASE IF EXISTS {db_name}") + + with Scenario("user with privilege"): + db_name = f"db_{getuid()}" + + try: + with Given("I have a database"): + node.query(f"CREATE DATABASE {db_name}") + + with When(f"I grant {current().context.privilege} privilege"): + node.query( + f"GRANT {current().context.privilege} ON *.* TO {grant_target_name}" + ) + + with Then("I attempt to alter a database"): + node.query( + f"ALTER DATABASE {db_name} MODIFY SETTING engine='Lazy'", + settings=[("user", user_name)], + exitcode=48, + message="DB::Exception: Database engine Atomic either does not support settings", + ) + + finally: + with Finally("I drop the database"): + node.query(f"DROP DATABASE IF EXISTS {db_name}") + + with Scenario("user with revoked privilege"): + db_name = f"db_{getuid()}" + + try: + with Given("I have a database"): + node.query(f"CREATE DATABASE {db_name}") + + with When(f"I grant the {current().context.privilege} privilege"): + node.query( + f"GRANT {current().context.privilege} ON *.* TO {grant_target_name}" + ) + + with And(f"I revoke the {current().context.privilege} privilege"): + node.query( + f"REVOKE {current().context.privilege} ON *.* FROM {grant_target_name}" + ) + + with Then("I attempt to alter a database"): + node.query( + f"ALTER DATABASE {db_name} MODIFY SETTING engine='Lazy'", + settings=[("user", user_name)], + exitcode=exitcode, + message=message, + ) + + finally: + with Finally("I drop the database"): + node.query(f"DROP DATABASE IF EXISTS {db_name}") + + +@TestFeature +@Requirements( + RQ_SRS_006_RBAC_Privileges_AlterDatabase("1.0"), + RQ_SRS_006_RBAC_Privileges_AlterDatabase_Settings("1.0"), + RQ_SRS_006_RBAC_Privileges_All("1.0"), + RQ_SRS_006_RBAC_Privileges_None("1.0"), +) +@Name("alter database") +def feature(self, node="clickhouse1", stress=None, parallel=None): + """Check the RBAC functionality of ALTER DATABASE.""" + self.context.node = self.context.cluster.node(node) + + if parallel is not None: + self.context.parallel = parallel + if stress is not None: + self.context.stress = stress + + for current().context.privilege in ["ALL", "ALTER DATABASE", "ALTER"]: + with Suite( + f" {current().context.privilege} privilege", + test=privilege_granted_directly_or_via_role, + setup=instrument_clickhouse_server_log, + ): + privilege_granted_directly_or_via_role() diff --git a/tests/testflows/rbac/tests/privileges/create/create_database.py b/tests/testflows/rbac/tests/privileges/create/create_database.py index 8a8b71b19a31..dbe983ae4c2f 100644 --- a/tests/testflows/rbac/tests/privileges/create/create_database.py +++ b/tests/testflows/rbac/tests/privileges/create/create_database.py @@ -66,9 +66,9 @@ def privilege_check(grant_target_name, user_name, node=None): db_name = f"db_{getuid()}" try: - with When("I grant create database privilege"): + with When(f"I grant {current().context.privilege} privilege"): node.query( - f"GRANT CREATE DATABASE ON {db_name}.* TO {grant_target_name}" + f"GRANT {current().context.privilege} ON *.* TO {grant_target_name}" ) with Then("I attempt to create a database"): @@ -82,14 +82,14 @@ def privilege_check(grant_target_name, user_name, node=None): db_name = f"db_{getuid()}" try: - with When("I grant the create database privilege"): + with When(f"I grant the {current().context.privilege} privilege"): node.query( - f"GRANT CREATE DATABASE ON {db_name}.* TO {grant_target_name}" + f"GRANT {current().context.privilege} ON *.* TO {grant_target_name}" ) - with And("I revoke the create database privilege"): + with And(f"I revoke the {current().context.privilege} privilege"): node.query( - f"REVOKE CREATE DATABASE ON {db_name}.* FROM {grant_target_name}" + f"REVOKE {current().context.privilege} ON *.* FROM {grant_target_name}" ) with Then("I attempt to create a database"): @@ -104,44 +104,6 @@ def privilege_check(grant_target_name, user_name, node=None): with Finally("I drop the database"): node.query(f"DROP DATABASE IF EXISTS {db_name}") - with Scenario("user with revoked ALL privilege"): - db_name = f"db_{getuid()}" - - try: - with When("I grant the create database privilege"): - node.query( - f"GRANT CREATE DATABASE ON {db_name}.* TO {grant_target_name}" - ) - - with And("I revoke ALL privilege"): - node.query(f"REVOKE ALL ON *.* FROM {grant_target_name}") - - with Then("I attempt to create a database"): - node.query( - f"CREATE DATABASE {db_name}", - settings=[("user", user_name)], - exitcode=exitcode, - message=message, - ) - - finally: - with Finally("I drop the database"): - node.query(f"DROP DATABASE IF EXISTS {db_name}") - - with Scenario("user with ALL privilege"): - db_name = f"db_{getuid()}" - - try: - with When("I grant ALL privilege"): - node.query(f"GRANT ALL ON *.* TO {grant_target_name}") - - with Then("I attempt to create a database"): - node.query(f"CREATE DATABASE {db_name}", settings=[("user", user_name)]) - - finally: - with Finally("I drop the database"): - node.query(f"DROP DATABASE IF EXISTS {db_name}") - @TestFeature @Requirements( @@ -159,8 +121,10 @@ def feature(self, node="clickhouse1", stress=None, parallel=None): if stress is not None: self.context.stress = stress - with Suite( - test=privilege_granted_directly_or_via_role, - setup=instrument_clickhouse_server_log, - ): - privilege_granted_directly_or_via_role() + for current().context.privilege in ["ALL", "CREATE", "CREATE DATABASE"]: + with Suite( + f" {current().context.privilege} privilege", + test=privilege_granted_directly_or_via_role, + setup=instrument_clickhouse_server_log, + ): + privilege_granted_directly_or_via_role() diff --git a/tests/testflows/rbac/tests/privileges/create/create_dictionary.py b/tests/testflows/rbac/tests/privileges/create/create_dictionary.py index 76125aa02398..4ab52ec8177b 100644 --- a/tests/testflows/rbac/tests/privileges/create/create_dictionary.py +++ b/tests/testflows/rbac/tests/privileges/create/create_dictionary.py @@ -66,9 +66,9 @@ def privilege_check(grant_target_name, user_name, node=None): dict_name = f"dict_{getuid()}" try: - with When("I grant create dictionary privilege"): + with When(f"I grant the {current().context.privilege} privilege"): node.query( - f"GRANT CREATE DICTIONARY ON {dict_name} TO {grant_target_name}" + f"GRANT {current().context.privilege} ON *.* TO {grant_target_name}" ) with Then("I attempt to create a dictionary"): @@ -85,14 +85,14 @@ def privilege_check(grant_target_name, user_name, node=None): dict_name = f"dict_{getuid()}" try: - with When("I grant the create dictionary privilege"): + with When(f"I grant the {current().context.privilege} privilege"): node.query( - f"GRANT CREATE DICTIONARY ON {dict_name} TO {grant_target_name}" + f"GRANT {current().context.privilege} ON *.* TO {grant_target_name}" ) - with And("I revoke the create dictionary privilege"): + with And(f"I revoke the {current().context.privilege} privilege"): node.query( - f"REVOKE CREATE DICTIONARY ON {dict_name} FROM {grant_target_name}" + f"REVOKE {current().context.privilege} ON *.* FROM {grant_target_name}" ) with Then("I attempt to create a dictionary"): @@ -107,47 +107,6 @@ def privilege_check(grant_target_name, user_name, node=None): with Finally("I drop the dictionary"): node.query(f"DROP DICTIONARY IF EXISTS {dict_name}") - with Scenario("user with revoked ALL privilege"): - dict_name = f"dict_{getuid()}" - - try: - with When("I grant the create dictionary privilege"): - node.query( - f"GRANT CREATE DICTIONARY ON {dict_name} TO {grant_target_name}" - ) - - with And("I revoke ALL privilege"): - node.query(f"REVOKE ALL ON *.* FROM {grant_target_name}") - - with Then("I attempt to create a dictionary"): - node.query( - f"CREATE DICTIONARY {dict_name}(x Int32, y Int32) PRIMARY KEY x LAYOUT(FLAT()) SOURCE(CLICKHOUSE()) LIFETIME(0)", - settings=[("user", user_name)], - exitcode=exitcode, - message=message, - ) - - finally: - with Finally("I drop the dictionary"): - node.query(f"DROP DICTIONARY IF EXISTS {dict_name}") - - with Scenario("user with ALL privilege"): - dict_name = f"dict_{getuid()}" - - try: - with When("I grant ALL privilege"): - node.query(f"GRANT ALL ON *.* TO {grant_target_name}") - - with Then("I attempt to create a dictionary"): - node.query( - f"CREATE DICTIONARY {dict_name}(x Int32, y Int32) PRIMARY KEY x LAYOUT(FLAT()) SOURCE(CLICKHOUSE()) LIFETIME(0)", - settings=[("user", user_name)], - ) - - finally: - with Finally("I drop the dictionary"): - node.query(f"DROP DICTIONARY IF EXISTS {dict_name}") - @TestFeature @Requirements( @@ -165,8 +124,10 @@ def feature(self, node="clickhouse1", stress=None, parallel=None): if stress is not None: self.context.stress = stress - with Suite( - test=privilege_granted_directly_or_via_role, - setup=instrument_clickhouse_server_log, - ): - privilege_granted_directly_or_via_role() + for current().context.privilege in ["ALL", "CREATE", "CREATE DICTIONARY"]: + with Suite( + f" {current().context.privilege} privilege", + test=privilege_granted_directly_or_via_role, + setup=instrument_clickhouse_server_log, + ): + privilege_granted_directly_or_via_role() diff --git a/tests/testflows/rbac/tests/privileges/create/create_function.py b/tests/testflows/rbac/tests/privileges/create/create_function.py new file mode 100644 index 000000000000..c2f27448203e --- /dev/null +++ b/tests/testflows/rbac/tests/privileges/create/create_function.py @@ -0,0 +1,134 @@ +from rbac.requirements import * +from rbac.helper.common import * +import rbac.helper.errors as errors + + +@TestSuite +def privilege_granted_directly_or_via_role(self, node=None): + """Check that user is only able to execute CREATE FUNCTION when they have required privilege, either directly or via role.""" + role_name = f"role_{getuid()}" + user_name = f"user_{getuid()}" + + if node is None: + node = self.context.node + + with Suite("user with direct privilege"): + with user(node, user_name): + + with When( + f"I run checks that {user_name} is only able to execute CREATE FUNCTION with required privileges" + ): + privilege_check( + grant_target_name=user_name, user_name=user_name, node=node + ) + + with Suite("user with privilege via role"): + with user(node, user_name), role(node, role_name): + + with When("I grant the role to the user"): + node.query(f"GRANT {role_name} TO {user_name}") + + with And( + f"I run checks that {user_name} with {role_name} is only able to execute CREATE DICTIONARY with required privileges" + ): + privilege_check( + grant_target_name=role_name, user_name=user_name, node=node + ) + + +def privilege_check(grant_target_name, user_name, node=None): + """Run scenarios to check the user's access with different privileges.""" + exitcode, message = errors.not_enough_privileges(name=f"{user_name}") + + with Scenario("user without privilege"): + func_name = f"dict_{getuid()}" + + try: + with When("I grant the user NONE privilege"): + node.query(f"GRANT NONE TO {grant_target_name}") + + with And("I grant the user USAGE privilege"): + node.query(f"GRANT USAGE ON *.* TO {grant_target_name}") + + with Then("I attempt to create a FUNCTION without privilege"): + node.query( + f"CREATE FUNCTION {func_name} AS (x) -> 2*x;", + settings=[("user", user_name)], + exitcode=exitcode, + message=message, + ) + + finally: + with Finally("I drop the function"): + node.query(f"DROP FUNCTION IF EXISTS {func_name}") + + with Scenario("user with privilege"): + func_name = f"dict_{getuid()}" + + try: + with When(f"I grant the {current().context.privilege} privilege"): + node.query( + f"GRANT {current().context.privilege} ON *.* TO {grant_target_name}" + ) + + with Then("I attempt to create a function"): + node.query( + f"CREATE FUNCTION {func_name} AS (x) -> 2*x;", + settings=[("user", user_name)], + ) + + finally: + with Finally("I drop the function"): + node.query(f"DROP FUNCTION IF EXISTS {func_name}") + + with Scenario("user with revoked privilege"): + func_name = f"dict_{getuid()}" + + try: + with When(f"I grant the {current().context.privilege} privilege"): + node.query( + f"GRANT {current().context.privilege} ON *.* TO {grant_target_name}" + ) + + with And(f"I revoke the {current().context.privilege} privilege"): + node.query( + f"REVOKE {current().context.privilege} ON *.* FROM {grant_target_name}" + ) + + with Then("I attempt to create a function"): + node.query( + f"CREATE FUNCTION {func_name} AS (x) -> 2*x;", + settings=[("user", user_name)], + exitcode=exitcode, + message=message, + ) + + finally: + with Finally("I drop the function"): + node.query(f"DROP FUNCTION IF EXISTS {func_name}") + + +@TestFeature +@Requirements( + RQ_SRS_006_RBAC_Privileges_CreateFunction("1.0"), + RQ_SRS_006_RBAC_Privileges_All("1.0"), + RQ_SRS_006_RBAC_Privileges_None("1.0"), + RQ_SRS_006_RBAC_Privileges_Create("1.0"), +) +@Name("create function") +def feature(self, node="clickhouse1", stress=None, parallel=None): + """Check the RBAC functionality of CREATE FUNCTION.""" + self.context.node = self.context.cluster.node(node) + + if parallel is not None: + self.context.parallel = parallel + if stress is not None: + self.context.stress = stress + + for current().context.privilege in ["ALL", "CREATE", "CREATE FUNCTION"]: + with Suite( + f" {current().context.privilege} privilege", + test=privilege_granted_directly_or_via_role, + setup=instrument_clickhouse_server_log, + ): + privilege_granted_directly_or_via_role() diff --git a/tests/testflows/rbac/tests/privileges/create/create_row_policy.py b/tests/testflows/rbac/tests/privileges/create/create_row_policy.py index 81b9d093e7ee..c419d7827ee8 100644 --- a/tests/testflows/rbac/tests/privileges/create/create_row_policy.py +++ b/tests/testflows/rbac/tests/privileges/create/create_row_policy.py @@ -1206,6 +1206,227 @@ def dict(self, node=None): node.query(f"DROP TABLE IF EXISTS {table_name}") +@TestScenario +def remote(self, node=None): + """Check that row policies do not only allow access from the source node.""" + user_name = f"user_{getuid()}" + table_name = f"table_{getuid()}" + dist_table_name = f"dist_table_{getuid()}" + pol_name = f"pol_{getuid()}" + exitcode, message = errors.not_enough_privileges(name=f"{user_name}") + + if node is None: + node = self.context.node + + node2 = self.context.cluster.node("clickhouse2") + node3 = self.context.cluster.node("clickhouse3") + + try: + with Given("I have a table on a cluster with two nodes on seperate shards"): + node.query( + f"CREATE TABLE {table_name} ON CLUSTER sharded_cluster12 (x UInt8) ENGINE=Memory" + ) + + with And("I have a distributed table"): + node.query( + f"CREATE TABLE {dist_table_name} ON CLUSTER sharded_cluster (x UInt8) ENGINE=Distributed(sharded_cluster12, default, {table_name})" + ) + + with And(f"I have a user {user_name} on three nodes on seperate shards"): + node.query(f"CREATE USER {user_name} ON CLUSTER sharded_cluster") + + with And("I have some data in the table on clickhouse1"): + node.query(f"INSERT INTO {table_name} VALUES (1),(2)") + + with And("I have a row policy on clickhouse1"): + node.query( + f"CREATE ROW POLICY {pol_name} ON {table_name} AS PERMISSIVE USING x=1 TO {user_name}" + ) + + with And( + f"The user {user_name} has SELECT privilege on {table_name} and {dist_table_name}" + ): + node.query( + f"GRANT SELECT ON default.{table_name} TO {user_name} ON CLUSTER sharded_cluster" + ) + node.query( + f"GRANT SELECT ON default.{dist_table_name} TO {user_name} ON CLUSTER sharded_cluster" + ) + + with When( + f"I select from the table as the user to set a baseline for comparison" + ): + expected = node.query( + f"SELECT * FROM {table_name}", settings=[("user", f"{user_name}")] + ).output + assert expected == "1", error() + + with Then(f"I compare it with SELECT from {dist_table_name}"): + output = node.query( + f"SELECT * FROM {dist_table_name}", settings=[("user", f"{user_name}")] + ).output + assert output == expected, error() + + with And( + "I grant CREATE TEMPORARY TABLE and cluster privileges to the user on all nodes" + ): + node.query( + f"GRANT CREATE TEMPORARY TABLE ON *.* TO {user_name} ON CLUSTER sharded_cluster" + ) + node.query(f"GRANT REMOTE ON *.* TO {user_name} ON CLUSTER sharded_cluster") + + with When(f"I select from the cluster table as {user_name}"): + output = node.query( + f"SELECT * FROM cluster(sharded_cluster12, default.{table_name})", + settings=[("user", user_name)], + ).output + assert output == expected, error() + + with And(f"I select from the remote table as {user_name}"): + output = node.query( + f"SELECT * FROM remote(sharded_cluster12, default.{table_name})", + settings=[("user", user_name)], + ).output + assert output == expected, error() + + with And(f"I select from the cluster table as {user_name} from clickhouse2"): + output = node2.query( + f"SELECT * FROM cluster(sharded_cluster12, default.{table_name})", + settings=[("user", user_name)], + ).output + assert output == expected, error() + + with And(f"I select from the remote table as {user_name} from clickhouse2"): + output = node2.query( + f"SELECT * FROM remote(sharded_cluster12, default.{table_name})", + settings=[("user", user_name)], + ).output + assert output == expected, error() + + with And(f"I SELECT from {dist_table_name} from clickhouse2"): + output = node2.query( + f"SELECT * FROM {dist_table_name}", settings=[("user", f"{user_name}")] + ).output + assert output == expected, error() + + with And(f"I select from the cluster table as {user_name} from clickhouse3"): + output = node3.query( + f"SELECT * FROM cluster(sharded_cluster12, default.{table_name})", + settings=[("user", user_name)], + ).output + assert output == expected, error() + + with And(f"I select from the remote table as {user_name} from clickhouse3"): + output = node3.query( + f"SELECT * FROM remote(sharded_cluster12, default.{table_name})", + settings=[("user", user_name)], + ).output + assert output == expected, error() + + with And(f"I SELECT from {dist_table_name} from clickhouse3"): + output = node3.query( + f"SELECT * FROM {dist_table_name}", settings=[("user", f"{user_name}")] + ).output + assert output == expected, error() + + finally: + with Finally(f"I drop the table {table_name} from the cluster", flags=TE): + node.query( + f"DROP TABLE IF EXISTS {table_name} ON CLUSTER sharded_cluster12" + ) + + with And(f"I drop the table {dist_table_name} from the cluster", flags=TE): + node.query( + f"DROP TABLE IF EXISTS {dist_table_name} ON CLUSTER sharded_cluster" + ) + + with And(f"I drop the user {user_name} from the cluster", flags=TE): + node.query(f"DROP USER IF EXISTS {user_name} ON CLUSTER sharded_cluster") + + +@TestScenario +def postgresql(self): + """Check that row policies apply correctly to postgresql.""" + node = self.context.node + table_name = f"table_{getuid()}" + pol_name = f"pol_{getuid()}" + user_name = f"user_{getuid()}" + + try: + with Given("I have a table"): + node.query( + f"CREATE TABLE {table_name} (x UInt32) ENGINE=MergeTree ORDER BY x;" + ) + + with And("I have a user"): + node.query( + f"CREATE USER {user_name} IDENTIFIED WITH plaintext_password BY 'x'" + ) + + with And("The user has select privilege on the table."): + node.query(f"GRANT SELECT ON default.{table_name} TO {user_name}") + + with When("I insert into the table"): + node.query(f"INSERT INTO {table_name} SELECT * FROM numbers(10)") + + with And("I have a row policy"): + node.query( + f"CREATE ROW POLICY {pol_name} ON default.{table_name} FOR SELECT USING x = 2 TO {user_name}" + ) + + with Then("I select from table using postgress"): + psql_out = node.command( + f"PGPASSWORD=x psql -p 9005 -h 127.0.0.1 -U {user_name} default -c'SELECT * FROM {table_name}'" + ).output + assert psql_out == " x \n---\n 2\n(1 row)", error() + + finally: + with Finally("I drop the user"): + node.query(f"DROP USER IF EXISTS {user_name}") + + with And("I drop the table"): + node.query(f"DROP TABLE IF EXISTS {table_name}") + + +@TestScenario +def mysql(self): + """Check that row policies apply correctly to mysql.""" + node = self.context.node + table_name = f"table_{getuid()}" + pol_name = f"pol_{getuid()}" + user_name = f"user_{getuid()}" + + try: + with Given("I have a table"): + node.query( + f"CREATE TABLE {table_name} (x UInt32) ENGINE=MergeTree ORDER BY x;" + ) + + with When("I insert into the table"): + node.query(f"INSERT INTO {table_name} SELECT * FROM numbers(10)") + + with And("I have a row policy"): + node.query( + f"CREATE ROW POLICY {pol_name} ON default.{table_name} FOR SELECT USING x = 2 TO default" + ) + + with Then("I select from table using mysql"): + msql_out = ( + self.context.cluster.node("mysql1") + .command( + f"mysql -h clickhouse1 -P 9004 -D default -u default -e 'SELECT * FROM {table_name}'" + ) + .output + ) + assert ( + msql_out == "+------+\n| x |\n+------+\n| 2 |\n+------+" + ), error() + + finally: + with Finally("I drop the table"): + node.query(f"DROP TABLE IF EXISTS {table_name}") + + @TestFeature @Name("create row policy") @Requirements( @@ -1249,3 +1470,6 @@ def feature(self, node="clickhouse1"): Scenario(run=no_table, setup=instrument_clickhouse_server_log) Scenario(run=policy_before_table, setup=instrument_clickhouse_server_log) Scenario(run=dict, setup=instrument_clickhouse_server_log) + Scenario(run=remote, setup=instrument_clickhouse_server_log) + Scenario(run=postgresql, setup=instrument_clickhouse_server_log) + Scenario(run=mysql, setup=instrument_clickhouse_server_log) diff --git a/tests/testflows/rbac/tests/privileges/create/create_table.py b/tests/testflows/rbac/tests/privileges/create/create_table.py index 9d10d4fc9f03..bbb96fdf3ddb 100644 --- a/tests/testflows/rbac/tests/privileges/create/create_table.py +++ b/tests/testflows/rbac/tests/privileges/create/create_table.py @@ -148,6 +148,59 @@ def create_with_all_privilege(self, grant_target_name, user_name, node=None): node.query(f"DROP TABLE IF EXISTS {table_name}") +@TestScenario +@Requirements(RQ_SRS_006_RBAC_Privileges_All("1.0")) +def create_with_create_privilege_granted_directly_or_via_role(self, node=None): + """Check that user is able to create a table with CREATE privilege, either granted directly or through a role.""" + user_name = f"user_{getuid()}" + role_name = f"role_{getuid()}" + + if node is None: + node = self.context.node + + with user(node, f"{user_name}"): + + Scenario( + test=create_with_create_privilege, + name="create with CREATE privilege granted directly", + )(grant_target_name=user_name, user_name=user_name) + + with user(node, f"{user_name}"), role(node, f"{role_name}"): + + with When("I grant the role to the user"): + node.query(f"GRANT {role_name} TO {user_name}") + + Scenario( + test=create_with_create_privilege, + name="create with CREATE privilege granted through a role", + )(grant_target_name=role_name, user_name=user_name) + + +@TestOutline +def create_with_create_privilege(self, grant_target_name, user_name, node=None): + """Check that user is able to create a table with the CREATE privilege.""" + table_name = f"table_{getuid()}" + + if node is None: + node = self.context.node + try: + with Given("I don't have a table"): + node.query(f"DROP TABLE IF EXISTS {table_name}") + + with When("I grant CREATE privilege"): + node.query(f"GRANT CREATE ON *.* TO {grant_target_name}") + + with Then("I try to create a table without privilege as the user"): + node.query( + f"CREATE TABLE {table_name} (x Int8) ENGINE = Memory", + settings=[("user", f"{user_name}")], + ) + + finally: + with Then("I drop the table"): + node.query(f"DROP TABLE IF EXISTS {table_name}") + + @TestScenario def create_with_revoked_create_table_privilege_revoked_directly_or_from_role( self, node=None diff --git a/tests/testflows/rbac/tests/privileges/create/create_temp_table.py b/tests/testflows/rbac/tests/privileges/create/create_temp_table.py index 0cc3211bddff..b5d0e881abbe 100644 --- a/tests/testflows/rbac/tests/privileges/create/create_temp_table.py +++ b/tests/testflows/rbac/tests/privileges/create/create_temp_table.py @@ -66,9 +66,9 @@ def privilege_check(grant_target_name, user_name, node=None): temp_table_name = f"temp_table_{getuid()}" try: - with When("I grant create temporary table privilege"): + with When(f"I grant the {current().context.privilege} privilege"): node.query( - f"GRANT CREATE TEMPORARY TABLE ON *.* TO {grant_target_name}" + f"GRANT {current().context.privilege} ON *.* TO {grant_target_name}" ) with Then("I attempt to create aa temporary table"): @@ -85,14 +85,14 @@ def privilege_check(grant_target_name, user_name, node=None): temp_table_name = f"temp_table_{getuid()}" try: - with When("I grant the create temporary table privilege"): + with When(f"I grant the {current().context.privilege} privilege"): node.query( - f"GRANT CREATE TEMPORARY TABLE ON *.* TO {grant_target_name}" + f"GRANT {current().context.privilege} ON *.* TO {grant_target_name}" ) - with And("I revoke the create temporary table privilege"): + with And(f"I revoke the {current().context.privilege} privilege"): node.query( - f"REVOKE CREATE TEMPORARY TABLE ON *.* FROM {grant_target_name}" + f"REVOKE {current().context.privilege} ON *.* FROM {grant_target_name}" ) with Then("I attempt to create a temporary table"): @@ -107,47 +107,6 @@ def privilege_check(grant_target_name, user_name, node=None): with Finally("I drop the temporary table"): node.query(f"DROP TEMPORARY TABLE IF EXISTS {temp_table_name}") - with Scenario("user with revoked ALL privilege"): - temp_table_name = f"temp_table_{getuid()}" - - try: - with When("I grant the create temporary table privilege"): - node.query( - f"GRANT CREATE TEMPORARY TABLE ON *.* TO {grant_target_name}" - ) - - with And("I revoke ALL privilege"): - node.query(f"REVOKE ALL ON *.* FROM {grant_target_name}") - - with Then("I attempt to create a temporary table"): - node.query( - f"CREATE TEMPORARY TABLE {temp_table_name} (x Int8)", - settings=[("user", user_name)], - exitcode=exitcode, - message=message, - ) - - finally: - with Finally("I drop the temporary table"): - node.query(f"DROP TEMPORARY TABLE IF EXISTS {temp_table_name}") - - with Scenario("user with ALL privilege"): - temp_table_name = f"temp_table_{getuid()}" - - try: - with When("I grant ALL privilege"): - node.query(f"GRANT ALL ON *.* TO {grant_target_name}") - - with Then("I attempt to create aa temporary table"): - node.query( - f"CREATE TEMPORARY TABLE {temp_table_name} (x Int8)", - settings=[("user", user_name)], - ) - - finally: - with Finally("I drop the temporary table"): - node.query(f"DROP TEMPORARY TABLE IF EXISTS {temp_table_name}") - @TestFeature @Requirements( @@ -165,8 +124,10 @@ def feature(self, node="clickhouse1", stress=None, parallel=None): if stress is not None: self.context.stress = stress - with Suite( - test=privilege_granted_directly_or_via_role, - setup=instrument_clickhouse_server_log, - ): - privilege_granted_directly_or_via_role() + for current().context.privilege in ["ALL", "CREATE", "CREATE TEMPORARY TABLE"]: + with Suite( + f" {current().context.privilege} privilege", + test=privilege_granted_directly_or_via_role, + setup=instrument_clickhouse_server_log, + ): + privilege_granted_directly_or_via_role() diff --git a/tests/testflows/rbac/tests/privileges/drop/drop_database.py b/tests/testflows/rbac/tests/privileges/drop/drop_database.py index 3001285ef373..4d6a7f89d235 100644 --- a/tests/testflows/rbac/tests/privileges/drop/drop_database.py +++ b/tests/testflows/rbac/tests/privileges/drop/drop_database.py @@ -71,8 +71,10 @@ def privilege_check(grant_target_name, user_name, node=None): with Given("I have a database"): node.query(f"CREATE DATABASE {db_name}") - with When("I grant drop database privilege"): - node.query(f"GRANT DROP DATABASE ON {db_name}.* TO {grant_target_name}") + with When(f"I grant {current().context.privilege} privilege"): + node.query( + f"GRANT {current().context.privilege} ON *.* TO {grant_target_name}" + ) with Then("I attempt to drop a database"): node.query(f"DROP DATABASE {db_name}", settings=[("user", user_name)]) @@ -88,39 +90,16 @@ def privilege_check(grant_target_name, user_name, node=None): with Given("I have a database"): node.query(f"CREATE DATABASE {db_name}") - with When("I grant the drop database privilege"): - node.query(f"GRANT DROP DATABASE ON {db_name}.* TO {grant_target_name}") - - with And("I revoke the drop database privilege"): + with When(f"I grant the {current().context.privilege} privilege"): node.query( - f"REVOKE DROP DATABASE ON {db_name}.* FROM {grant_target_name}" + f"GRANT {current().context.privilege} ON *.* TO {grant_target_name}" ) - with Then("I attempt to drop a database"): + with And(f"I revoke the {current().context.privilege} privilege"): node.query( - f"DROP DATABASE {db_name}", - settings=[("user", user_name)], - exitcode=exitcode, - message=message, + f"REVOKE {current().context.privilege} ON *.* FROM {grant_target_name}" ) - finally: - with Finally("I drop the database"): - node.query(f"DROP DATABASE IF EXISTS {db_name}") - - with Scenario("user with revoked ALL privilege"): - db_name = f"db_{getuid()}" - - try: - with Given("I have a database"): - node.query(f"CREATE DATABASE {db_name}") - - with When("I grant the drop database privilege"): - node.query(f"GRANT DROP DATABASE ON {db_name}.* TO {grant_target_name}") - - with And("I revoke ALL privilege"): - node.query(f"REVOKE ALL ON *.* FROM {grant_target_name}") - with Then("I attempt to drop a database"): node.query( f"DROP DATABASE {db_name}", @@ -133,27 +112,11 @@ def privilege_check(grant_target_name, user_name, node=None): with Finally("I drop the database"): node.query(f"DROP DATABASE IF EXISTS {db_name}") - with Scenario("user with ALL privilege"): - db_name = f"db_{getuid()}" - - try: - with Given("I have a database"): - node.query(f"CREATE DATABASE {db_name}") - - with When("I grant ALL privilege"): - node.query(f"GRANT ALL ON *.* TO {grant_target_name}") - - with Then("I attempt to drop a database"): - node.query(f"DROP DATABASE {db_name}", settings=[("user", user_name)]) - - finally: - with Finally("I drop the database"): - node.query(f"DROP DATABASE IF EXISTS {db_name}") - @TestFeature @Requirements( RQ_SRS_006_RBAC_Privileges_DropDatabase("1.0"), + RQ_SRS_006_RBAC_Privileges_Drop("1.0"), RQ_SRS_006_RBAC_Privileges_All("1.0"), RQ_SRS_006_RBAC_Privileges_None("1.0"), ) @@ -167,8 +130,10 @@ def feature(self, node="clickhouse1", stress=None, parallel=None): if stress is not None: self.context.stress = stress - with Suite( - test=privilege_granted_directly_or_via_role, - setup=instrument_clickhouse_server_log, - ): - privilege_granted_directly_or_via_role() + for current().context.privilege in ["ALL", "DROP", "DROP DATABASE"]: + with Suite( + f" {current().context.privilege} privilege", + test=privilege_granted_directly_or_via_role, + setup=instrument_clickhouse_server_log, + ): + privilege_granted_directly_or_via_role() diff --git a/tests/testflows/rbac/tests/privileges/drop/drop_dictionary.py b/tests/testflows/rbac/tests/privileges/drop/drop_dictionary.py index 7d5958945b13..84e63eeaca05 100644 --- a/tests/testflows/rbac/tests/privileges/drop/drop_dictionary.py +++ b/tests/testflows/rbac/tests/privileges/drop/drop_dictionary.py @@ -73,9 +73,9 @@ def privilege_check(grant_target_name, user_name, node=None): f"CREATE DICTIONARY {dict_name}(x Int32, y Int32) PRIMARY KEY x LAYOUT(FLAT()) SOURCE(CLICKHOUSE()) LIFETIME(0)" ) - with When("I grant drop dictionary privilege"): + with When(f"I grant {current().context.privilege} privilege"): node.query( - f"GRANT DROP DICTIONARY ON {dict_name} TO {grant_target_name}" + f"GRANT {current().context.privilege} ON *.* TO {grant_target_name}" ) with Then("I attempt to drop aa dictionary"): @@ -96,14 +96,14 @@ def privilege_check(grant_target_name, user_name, node=None): f"CREATE DICTIONARY {dict_name}(x Int32, y Int32) PRIMARY KEY x LAYOUT(FLAT()) SOURCE(CLICKHOUSE()) LIFETIME(0)" ) - with When("I grant the drop dictionary privilege"): + with When(f"I grant the {current().context.privilege} privilege"): node.query( - f"GRANT DROP DICTIONARY ON {dict_name} TO {grant_target_name}" + f"GRANT {current().context.privilege} ON *.* TO {grant_target_name}" ) - with And("I revoke the drop dictionary privilege"): + with And(f"I revoke the {current().context.privilege} privilege"): node.query( - f"REVOKE DROP DICTIONARY ON {dict_name} FROM {grant_target_name}" + f"REVOKE {current().context.privilege} ON *.* FROM {grant_target_name}" ) with Then("I attempt to drop a dictionary"): @@ -118,31 +118,11 @@ def privilege_check(grant_target_name, user_name, node=None): with Finally("I drop the dictionary"): node.query(f"DROP DICTIONARY IF EXISTS {dict_name}") - with Scenario("user with ALL privilege"): - dict_name = f"db_{getuid()}" - - try: - with Given("I have a dictionary"): - node.query( - f"CREATE DICTIONARY {dict_name}(x Int32, y Int32) PRIMARY KEY x LAYOUT(FLAT()) SOURCE(CLICKHOUSE()) LIFETIME(0)" - ) - - with When("I grant ALL privilege"): - node.query(f"GRANT ALL ON *.* TO {grant_target_name}") - - with Then("I drop the dictionary"): - node.query( - f"DROP DICTIONARY {dict_name}", settings=[("user", user_name)] - ) - - finally: - with Finally("I drop the dictionary"): - node.query(f"DROP DICTIONARY IF EXISTS {dict_name}") - @TestFeature @Requirements( RQ_SRS_006_RBAC_Privileges_DropDictionary("1.0"), + RQ_SRS_006_RBAC_Privileges_Drop("1.0"), RQ_SRS_006_RBAC_Privileges_All("1.0"), RQ_SRS_006_RBAC_Privileges_None("1.0"), ) @@ -156,8 +136,10 @@ def feature(self, node="clickhouse1", stress=None, parallel=None): if stress is not None: self.context.stress = stress - with Suite( - test=privilege_granted_directly_or_via_role, - setup=instrument_clickhouse_server_log, - ): - privilege_granted_directly_or_via_role() + for current().context.privilege in ["ALL", "DROP", "DROP DICTIONARY"]: + with Suite( + f" {current().context.privilege} privilege", + test=privilege_granted_directly_or_via_role, + setup=instrument_clickhouse_server_log, + ): + privilege_granted_directly_or_via_role() diff --git a/tests/testflows/rbac/tests/privileges/drop/drop_function.py b/tests/testflows/rbac/tests/privileges/drop/drop_function.py new file mode 100644 index 000000000000..b7985e923478 --- /dev/null +++ b/tests/testflows/rbac/tests/privileges/drop/drop_function.py @@ -0,0 +1,139 @@ +from rbac.requirements import * +from rbac.helper.common import * +import rbac.helper.errors as errors + + +@TestSuite +def privilege_granted_directly_or_via_role(self, node=None): + """Check that user is only able to execute DROP FUNCTION when they have required privilege, either directly or via role.""" + role_name = f"role_{getuid()}" + user_name = f"user_{getuid()}" + + if node is None: + node = self.context.node + + with Suite("user with direct privilege"): + with user(node, user_name): + + with When( + f"I run checks that {user_name} is only able to execute DROP FUNCTION with required privileges" + ): + privilege_check( + grant_target_name=user_name, user_name=user_name, node=node + ) + + with Suite("user with privilege via role"): + with user(node, user_name), role(node, role_name): + + with When("I grant the role to the user"): + node.query(f"GRANT {role_name} TO {user_name}") + + with And( + f"I run checks that {user_name} with {role_name} is only able to execute DROP FUNCTION with required privileges" + ): + privilege_check( + grant_target_name=role_name, user_name=user_name, node=node + ) + + +def privilege_check(grant_target_name, user_name, node=None): + """Run scenarios to check the user's access with different privileges.""" + exitcode, message = errors.not_enough_privileges(name=f"{user_name}") + + with Scenario("user without privilege"): + func_name = f"db_{getuid()}" + + try: + with Given("I have a function"): + node.query(f"CREATE FUNCTION {func_name} AS (x) -> 2*x;") + + with When("I grant the user NONE privilege"): + node.query(f"GRANT NONE TO {grant_target_name}") + + with And("I grant the user USAGE privilege"): + node.query(f"GRANT USAGE ON *.* TO {grant_target_name}") + + with Then("I attempt to drop the function"): + node.query( + f"DROP FUNCTION {func_name}", + settings=[("user", user_name)], + exitcode=exitcode, + message=message, + ) + finally: + with Finally("I drop the function"): + node.query(f"DROP FUNCTION IF EXISTS {func_name}") + + with Scenario("user with privilege"): + func_name = f"db_{getuid()}" + + try: + with Given("I have a function"): + node.query(f"CREATE FUNCTION {func_name} AS (x) -> 2*x;") + + with When(f"I grant {current().context.privilege} privilege"): + node.query( + f"GRANT {current().context.privilege} ON *.* TO {grant_target_name}" + ) + + with Then("I attempt to drop a function"): + node.query(f"DROP FUNCTION {func_name}", settings=[("user", user_name)]) + + finally: + with Finally("I drop the function"): + node.query(f"DROP FUNCTION IF EXISTS {func_name}") + + with Scenario("user with revoked privilege"): + func_name = f"db_{getuid()}" + + try: + with Given("I have a function"): + node.query(f"CREATE FUNCTION {func_name} AS (x) -> 2*x;") + + with When(f"I grant the {current().context.privilege} privilege"): + node.query( + f"GRANT {current().context.privilege} ON *.* TO {grant_target_name}" + ) + + with And(f"I revoke the {current().context.privilege} privilege"): + node.query( + f"REVOKE {current().context.privilege} ON *.* FROM {grant_target_name}" + ) + + with Then("I attempt to drop a function"): + node.query( + f"DROP FUNCTION {func_name}", + settings=[("user", user_name)], + exitcode=exitcode, + message=message, + ) + + finally: + with Finally("I drop the function"): + node.query(f"DROP FUNCTION IF EXISTS {func_name}") + + +@TestFeature +@Requirements( + RQ_SRS_006_RBAC_Privileges_Drop("1.0"), + RQ_SRS_006_RBAC_Privileges_DropFunction("1.0"), + RQ_SRS_006_RBAC_Privileges_All("1.0"), + RQ_SRS_006_RBAC_Privileges_None("1.0"), +) +@Name("drop function") +def feature(self, node="clickhouse1", stress=None, parallel=None): + """Check the RBAC functionality of DROP FUNCTION.""" + self.context.node = self.context.cluster.node(node) + + if parallel is not None: + self.context.parallel = parallel + if stress is not None: + self.context.stress = stress + + for current().context.privilege in ["ALL", "DROP", "DROP FUNCTION"]: + with Suite( + f" {current().context.privilege} privilege", + test=privilege_granted_directly_or_via_role, + setup=instrument_clickhouse_server_log, + ): + privilege_granted_directly_or_via_role() diff --git a/tests/testflows/rbac/tests/privileges/drop/drop_replica.py b/tests/testflows/rbac/tests/privileges/drop/drop_replica.py new file mode 100644 index 000000000000..20ac088f5518 --- /dev/null +++ b/tests/testflows/rbac/tests/privileges/drop/drop_replica.py @@ -0,0 +1,146 @@ +from testflows.core import * +from testflows.asserts import error + +from rbac.requirements import * +from rbac.helper.common import * +import rbac.helper.errors as errors + + +@TestSuite +def privileges_granted_directly(self, node=None): + """Check that a user is able to execute `SYSTEM DROP REPLICA` commands if and only if + the privilege has been granted directly. + """ + user_name = f"user_{getuid()}" + + if node is None: + node = self.context.node + + with user(node, f"{user_name}"): + + Suite( + run=drop_replica, + examples=Examples( + "privilege on grant_target_name user_name", + [ + tuple(list(row) + [user_name, user_name]) + for row in drop_replica.examples + ], + args=Args(name="check privilege={privilege}", format_name=True), + ), + ) + + +@TestSuite +def privileges_granted_via_role(self, node=None): + """Check that a user is able to execute `SYSTEM DROP REPLICA` commands if and only if + the privilege has been granted via role. + """ + user_name = f"user_{getuid()}" + role_name = f"role_{getuid()}" + + if node is None: + node = self.context.node + + with user(node, f"{user_name}"), role(node, f"{role_name}"): + + with When("I grant the role to the user"): + node.query(f"GRANT {role_name} TO {user_name}") + + Suite( + run=drop_replica, + examples=Examples( + "privilege on grant_target_name user_name", + [ + tuple(list(row) + [role_name, user_name]) + for row in drop_replica.examples + ], + args=Args(name="check privilege={privilege}", format_name=True), + ), + ) + + +@TestOutline(Suite) +@Examples( + "privilege on", + [ + ("ALL", "*.*"), + ("SYSTEM", "*.*"), + ("SYSTEM DROP REPLICA", "table"), + ("DROP REPLICA", "table"), + ], +) +def drop_replica(self, privilege, on, grant_target_name, user_name, node=None): + """Check that user is only able to execute `SYSTEM DROP REPLICA` when they have privilege.""" + exitcode, message = errors.not_enough_privileges(name=user_name) + table_name = f"table_name_{getuid()}" + + if node is None: + node = self.context.node + + on = on.replace("table", f"{table_name}") + + with table(node, table_name, "ReplicatedMergeTree-sharded_cluster"): + + with When("I get the name of the replica associated with the table"): + replica_name = node.query( + f"SELECT replica_name FROM system.replicas WHERE table = '{table_name}'" + ).output + + with Scenario("SYSTEM DROP REPLICA without privilege"): + + with When("I grant the user NONE privilege"): + node.query(f"GRANT NONE TO {grant_target_name}") + + with And("I grant the user USAGE privilege"): + node.query(f"GRANT USAGE ON *.* TO {grant_target_name}") + + with Then("I check the user can't drop replica"): + node.query( + f"SYSTEM DROP REPLICA '{replica_name}'", + settings=[("user", f"{user_name}")], + exitcode=exitcode, + message=message, + ) + + with Scenario("SYSTEM DROP REPLICA with privilege"): + + with When(f"I grant {privilege} on the table"): + node.query(f"GRANT {privilege} ON {on} TO {grant_target_name}") + + with Then("I check the user can drop replica"): + node.query( + f"SYSTEM DROP REPLICA {replica_name}", + settings=[("user", f"{user_name}")], + ) + + with Scenario("SYSTEM DROP REPLICA with revoked privilege"): + + with When(f"I grant {privilege} on the table"): + node.query(f"GRANT {privilege} ON {on} TO {grant_target_name}") + + with And(f"I revoke {privilege} on the table"): + node.query(f"REVOKE {privilege} ON {on} FROM {grant_target_name}") + + with Then("I check the user can't drop replica"): + node.query( + f"SYSTEM DROP REPLICA {replica_name}", + settings=[("user", f"{user_name}")], + exitcode=exitcode, + message=message, + ) + + +@TestFeature +@Name("system drop replica") +@Requirements( + RQ_SRS_006_RBAC_Privileges_System_DropReplica("1.0"), + RQ_SRS_006_RBAC_Privileges_All("1.0"), + RQ_SRS_006_RBAC_Privileges_None("1.0"), +) +def feature(self, node="clickhouse1"): + """Check the RBAC functionality of SYSTEM DROP REPLICA.""" + self.context.node = self.context.cluster.node(node) + + Suite(run=privileges_granted_directly, setup=instrument_clickhouse_server_log) + Suite(run=privileges_granted_via_role, setup=instrument_clickhouse_server_log) diff --git a/tests/testflows/rbac/tests/privileges/drop/drop_table.py b/tests/testflows/rbac/tests/privileges/drop/drop_table.py index daafa250462b..9b3214649c90 100644 --- a/tests/testflows/rbac/tests/privileges/drop/drop_table.py +++ b/tests/testflows/rbac/tests/privileges/drop/drop_table.py @@ -147,11 +147,29 @@ def privilege_check(grant_target_name, user_name, node=None): with Finally("I drop the table"): node.query(f"DROP TABLE IF EXISTS {table_name}") + with Scenario("user with DROP privilege"): + table_name = f"table_{getuid()}" + + try: + with Given("I have a table"): + node.query(f"CREATE TABLE {table_name} (x Int8) ENGINE=Memory") + + with When("I grant DROP privilege"): + node.query(f"GRANT DROP ON *.* TO {grant_target_name}") + + with Then("I drop the table"): + node.query(f"DROP TABLE {table_name}", settings=[("user", user_name)]) + + finally: + with Finally("I drop the table"): + node.query(f"DROP TABLE IF EXISTS {table_name}") + @TestFeature @Requirements( RQ_SRS_006_RBAC_Privileges_DropTable("1.0"), RQ_SRS_006_RBAC_Privileges_All("1.0"), + RQ_SRS_006_RBAC_Privileges_Drop("1.0"), RQ_SRS_006_RBAC_Privileges_None("1.0"), ) @Name("drop table") diff --git a/tests/testflows/rbac/tests/privileges/feature.py b/tests/testflows/rbac/tests/privileges/feature.py index 58d24d1f1f77..9bcb20cec6be 100755 --- a/tests/testflows/rbac/tests/privileges/feature.py +++ b/tests/testflows/rbac/tests/privileges/feature.py @@ -203,6 +203,11 @@ def feature(self): parallel=True, executor=pool, ) + Feature( + run=load("rbac.tests.privileges.alter.alter_database", "feature"), + parallel=True, + executor=pool, + ) Feature( run=load( "rbac.tests.privileges.alter.alter_settings_profile", "feature" @@ -258,6 +263,11 @@ def feature(self): parallel=True, executor=pool, ) + Feature( + run=load("rbac.tests.privileges.create.create_function", "feature"), + parallel=True, + executor=pool, + ) Feature( run=load("rbac.tests.privileges.attach.attach_database", "feature"), @@ -320,6 +330,16 @@ def feature(self): parallel=True, executor=pool, ) + Feature( + run=load("rbac.tests.privileges.drop.drop_function", "feature"), + parallel=True, + executor=pool, + ) + Feature( + run=load("rbac.tests.privileges.drop.drop_replica", "feature"), + parallel=True, + executor=pool, + ) Feature( run=load("rbac.tests.privileges.detach.detach_database", "feature"), @@ -382,6 +402,11 @@ def feature(self): parallel=True, executor=pool, ) + Feature( + run=load("rbac.tests.privileges.system.restore_replica", "feature"), + parallel=True, + executor=pool, + ) Feature( run=load("rbac.tests.privileges.system.sends", "feature"), parallel=True, @@ -397,6 +422,36 @@ def feature(self): parallel=True, executor=pool, ) + Feature( + run=load("rbac.tests.privileges.system.restart_disk", "feature"), + parallel=True, + executor=pool, + ) + Feature( + run=load("rbac.tests.privileges.system.thread_fuzzer", "feature"), + parallel=True, + executor=pool, + ) + Feature( + run=load("rbac.tests.privileges.table_functions", "feature"), + parallel=True, + executor=pool, + ) + Feature( + run=load("rbac.tests.privileges.projections", "feature"), + parallel=True, + executor=pool, + ) + Feature( + run=load("rbac.tests.privileges.grant_drop_user_race", "feature"), + parallel=True, + executor=pool, + ) + Feature( + run=load("rbac.tests.privileges.race_condition", "feature"), + parallel=True, + executor=pool, + ) finally: join() diff --git a/tests/testflows/rbac/tests/privileges/grant_drop_user_race.py b/tests/testflows/rbac/tests/privileges/grant_drop_user_race.py new file mode 100644 index 000000000000..cfcedca8a76a --- /dev/null +++ b/tests/testflows/rbac/tests/privileges/grant_drop_user_race.py @@ -0,0 +1,83 @@ +import time + +from testflows.core import * +from testflows.asserts import error + +from rbac.requirements import * +from rbac.helper.common import * + + +@TestStep(Then) +def drop_user(self, node, user): + """Drop the provided user.""" + with Then(f"I drop {user}"): + node.query(f"DROP USER IF EXISTS {user}") + + +@TestStep(Then) +def grant_all(self, node, from_user, to_user): + """Grant all from one user to another.""" + try: + with Then(f"{from_user} grants all to {to_user}"): + node.query(f"GRANT ALL ON *.* TO {to_user}", settings=[("user", from_user)]) + self.context.privilege = True + + except: + self.context.privilege = False + + +@TestFeature +@Name("grant drop user race") +def feature(self, stress=None, node="clickhouse1"): + """Check race condition when granting privileges and dropping users.""" + node = self.context.cluster.node(node) + + role_name = f"role_{getuid()}" + user0_name = f"user0_{getuid()}" + user1_name = f"user1_{getuid()}" + table_name = f"table_{getuid()}" + + if stress is not None: + self.context.stress = stress + + with role(node, role_name): + with user(node, user0_name): + with Given("The user has no privileges"): + node.query(f"REVOKE ALL ON *.* FROM {user0_name}") + + with And(f"I have a table {table_name}"): + node.query(f"CREATE TABLE {table_name} (n int) engine=Memory") + + with And("It has some values"): + node.query(f"INSERT INTO {table_name} VALUES (1)") + start = time.time() + + while time.time() - start < 10: + + with When("I create another user"): + node.query(f"CREATE USER IF NOT EXISTS {user1_name} GRANTEES NONE") + + with And(f"I grant all to the user {user1_name} with grant option"): + node.query(f"GRANT ALL ON *.* TO {user1_name} WITH GRANT OPTION") + + with Then(f"I drop the user"): + Step(test=drop_user, parallel=True)(node=node, user=user1_name) + + with And(f"Grant privilege to the original user in parallel"): + Step(test=grant_all, parallel=True)( + node=node, to_user=user0_name, from_user=user1_name + ) + + with Finally(f"I check if {user0_name} succesfully gained privileges."): + if self.context.privilege: + node.query( + f"SELECT * FROM {table_name}", + settings=[("user", user0_name)], + ) + else: + node.query( + f"SELECT * FROM {table_name}", + message="DB::Exception: user0_grant_drop_user_race", + exitcode=241, + settings=[("user", user0_name)], + ) diff --git a/tests/testflows/rbac/tests/privileges/introspection.py b/tests/testflows/rbac/tests/privileges/introspection.py index b36085ced96c..676a1564810f 100644 --- a/tests/testflows/rbac/tests/privileges/introspection.py +++ b/tests/testflows/rbac/tests/privileges/introspection.py @@ -145,6 +145,122 @@ def addressToLine(self, privilege, grant_target_name, user_name, node=None): ) +@TestSuite +def addressToLineWithInlines_privileges_granted_directly(self, node=None): + """Check that a user is able to execute `addressToLineWithInlines` with privileges are granted directly.""" + + user_name = f"user_{getuid()}" + + if node is None: + node = self.context.node + + with user(node, f"{user_name}"): + + Suite( + run=addressToLineWithInlines, + examples=Examples( + "privilege grant_target_name user_name", + [ + tuple(list(row) + [user_name, user_name]) + for row in addressToLineWithInlines.examples + ], + args=Args(name="privilege={privilege}", format_name=True), + ), + ) + + +@TestSuite +def addressToLineWithInlines_privileges_granted_via_role(self, node=None): + """Check that a user is able to execute `addressToLineWithInlines` with privileges are granted through a role.""" + + user_name = f"user_{getuid()}" + role_name = f"role_{getuid()}" + + if node is None: + node = self.context.node + + with user(node, f"{user_name}"), role(node, f"{role_name}"): + + with When("I grant the role to the user"): + node.query(f"GRANT {role_name} TO {user_name}") + + Suite( + run=addressToLineWithInlines, + examples=Examples( + "privilege grant_target_name user_name", + [ + tuple(list(row) + [role_name, user_name]) + for row in addressToLineWithInlines.examples + ], + args=Args(name="privilege={privilege}", format_name=True), + ), + ) + + +@TestOutline(Suite) +@Examples( + "privilege", + [ + ("ALL",), + ("INTROSPECTION",), + ("INTROSPECTION FUNCTIONS",), + ("addressToLineWithInlines",), + ], +) +@Requirements( + RQ_SRS_006_RBAC_Privileges_Introspection_addressToLineWithInlines("1.0"), +) +def addressToLineWithInlines(self, privilege, grant_target_name, user_name, node=None): + """Check that user is only able to execute `addressToLineWithInlines` when they have the necessary privilege.""" + exitcode, message = errors.not_enough_privileges(name=user_name) + + if node is None: + node = self.context.node + + with Scenario("addressToLineWithInlines without privilege"): + + with When("I grant the user NONE privilege"): + node.query(f"GRANT NONE TO {grant_target_name}") + + with And("I grant the user USAGE privilege"): + node.query(f"GRANT USAGE ON *.* TO {grant_target_name}") + + with Then("I check the user can't use addressToLineWithInlines"): + node.query( + f"WITH addressToLineWithInlines(toUInt64(dummy)) AS addr SELECT 1 WHERE addr = ''", + settings=[("user", user_name)], + exitcode=exitcode, + message=message, + ) + + with Scenario("addressToLineWithInlines with privilege"): + + with When(f"I grant {privilege}"): + node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}") + + with Then("I check the user can use addressToLineWithInlines"): + node.query( + f"WITH addressToLineWithInlines(toUInt64(dummy)) AS addr SELECT 1 WHERE addr = '[]'", + settings=[("user", f"{user_name}")], + ) + + with Scenario("addressToLineWithInlines with revoked privilege"): + + with When(f"I grant {privilege}"): + node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}") + + with And(f"I revoke {privilege}"): + node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}") + + with Then("I check the user cannot use addressToLineWithInlines"): + node.query( + f"WITH addressToLineWithInlines(toUInt64(dummy)) AS addr SELECT 1 WHERE addr = ''", + settings=[("user", user_name)], + exitcode=exitcode, + message=message, + ) + + @TestSuite def addressToSymbol_privileges_granted_directly(self, node=None): """Check that a user is able to execute `addressToSymbol` with privileges are granted directly.""" @@ -405,6 +521,14 @@ def feature(self, node="clickhouse1"): run=addressToSymbol_privileges_granted_via_role, setup=instrument_clickhouse_server_log, ) + Suite( + run=addressToLineWithInlines_privileges_granted_directly, + setup=instrument_clickhouse_server_log, + ) + Suite( + run=addressToLineWithInlines_privileges_granted_via_role, + setup=instrument_clickhouse_server_log, + ) Suite( run=demangle_privileges_granted_directly, setup=instrument_clickhouse_server_log, diff --git a/tests/testflows/rbac/tests/privileges/projections.py b/tests/testflows/rbac/tests/privileges/projections.py new file mode 100644 index 000000000000..5cd31df55c71 --- /dev/null +++ b/tests/testflows/rbac/tests/privileges/projections.py @@ -0,0 +1,304 @@ +from rbac.requirements import * +from rbac.helper.common import * +import rbac.helper.errors as errors + + +@TestSuite +def privilege_granted_directly_or_via_role(self, node=None): + """Check that user is only able to manipulate projections when they have required privilege, either directly or via role.""" + role_name = f"role_{getuid()}" + user_name = f"user_{getuid()}" + + if node is None: + node = self.context.node + + with Suite("user with direct privilege"): + with user(node, user_name): + + with When( + f"I run checks that {user_name} is only able to manipulate projections with required privileges" + ): + for scenario in loads(current_module(), Scenario): + Scenario(test=scenario, name=scenario.name)( + grant_target_name=user_name, user_name=user_name + ) + + with Suite("user with privilege via role"): + with user(node, user_name), role(node, role_name): + + with When("I grant the role to the user"): + node.query(f"GRANT {role_name} TO {user_name}") + + with And( + f"I run checks that {user_name} with {role_name} is only able to manipulate projections with required privileges" + ): + for scenario in loads(current_module(), Scenario): + Scenario(test=scenario, name=scenario.name)( + grant_target_name=role_name, user_name=user_name + ) + + +@TestScenario +@Name("ADD PROJECTION") +@Requirements( + RQ_SRS_006_RBAC_Privileges_AlterProjection_Add("1.0"), +) +def add_projection(self, grant_target_name, user_name, node=None): + """I check that user is only able to add projections with the ALTER TABLE ADD PROJECTION.""" + exitcode, message = errors.not_enough_privileges(name=f"{user_name}") + table_name = f"table_{getuid()}" + proj_name = f"proj_{getuid()}" + node = node or self.context.node + privilege = ( + current().context.privilege + if current().context.privilege != "default" + else "ADD PROJECTION" + ) + + with table(node, table_name): + with When("I check that user is unable to add a projection"): + node.query( + f"ALTER TABLE {table_name} ADD PROJECTION {proj_name} (SELECT y)", + settings=[("user", user_name)], + exitcode=exitcode, + message=message, + ) + + with Then(f"I grant the {privilege} to the {grant_target_name}"): + node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}") + + with When("I check the user is able to add a projection"): + node.query( + f"ALTER TABLE {table_name} ADD PROJECTION {proj_name} (SELECT y)", + settings=[("user", user_name)], + ) + + with Then(f"I drop the projection"): + node.query(f"ALTER TABLE {table_name} DROP PROJECTION {proj_name}") + + with And(f"I revoke the {privilege} from {grant_target_name}"): + node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}") + + with When("I check that user is unable to add a projection"): + node.query( + f"ALTER TABLE {table_name} ADD PROJECTION {proj_name} (SELECT y)", + settings=[("user", user_name)], + exitcode=exitcode, + message=message, + ) + + +@TestScenario +@Name("DROP PROJECTION") +@Requirements( + RQ_SRS_006_RBAC_Privileges_AlterProjection_Drop("1.0"), +) +def drop_projection(self, grant_target_name, user_name, node=None): + """I check that user is only able to drop projections with the ALTER TABLE DROP PROJECTION.""" + exitcode, message = errors.not_enough_privileges(name=f"{user_name}") + table_name = f"table_{getuid()}" + proj_name = f"proj_{getuid()}" + node = node or self.context.node + privilege = ( + current().context.privilege + if current().context.privilege != "default" + else "DROP PROJECTION" + ) + + with table(node, table_name): + + with Given("I have a projection"): + node.query( + f"ALTER TABLE {table_name} ADD PROJECTION {proj_name} (SELECT y)" + ) + + with When("I check that user is unable to drop a projection"): + node.query( + f"ALTER TABLE {table_name} DROP PROJECTION {proj_name}", + settings=[("user", user_name)], + exitcode=exitcode, + message=message, + ) + + with Then(f"I grant the {privilege} to the {grant_target_name}"): + node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}") + + with When("I check the user is able to drop a projection"): + node.query( + f"ALTER TABLE {table_name} DROP PROJECTION {proj_name}", + settings=[("user", user_name)], + ) + + with Then(f"I add the projection back"): + node.query( + f"ALTER TABLE {table_name} ADD PROJECTION {proj_name} (SELECT y)" + ) + + with And(f"I revoke the {privilege} from {grant_target_name}"): + node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}") + + with When("I check that user is unable to drop a projection"): + node.query( + f"ALTER TABLE {table_name} DROP PROJECTION {proj_name}", + settings=[("user", user_name)], + exitcode=exitcode, + message=message, + ) + + +@TestScenario +@Name("MATERIALIZE PROJECTION") +@Requirements( + RQ_SRS_006_RBAC_Privileges_AlterProjection_Materialize("1.0"), +) +def materialize_projection(self, grant_target_name, user_name, node=None): + """I check that user is only able to drop projections with the ALTER TABLE MATERIALIZE PROJECTION.""" + exitcode, message = errors.not_enough_privileges(name=f"{user_name}") + table_name = f"table_{getuid()}" + proj_name = f"proj_{getuid()}" + node = node or self.context.node + privilege = ( + current().context.privilege + if current().context.privilege != "default" + else "MATERIALIZE PROJECTION" + ) + + with table(node, table_name, "ReplicatedMergeTree-one_shard_cluster"): + + with Given("I have a projection"): + node.query( + f"ALTER TABLE {table_name} ADD PROJECTION {proj_name} (SELECT y)" + ) + + with And("I have some data on the table"): + node.query( + f"INSERT INTO {table_name} VALUES ('2019-01-01', 'a', 2, 'zzzz', 8)" + ) + + with When("I check that user is unable to materialize a projection"): + node.query( + f"ALTER TABLE {table_name} MATERIALIZE PROJECTION {proj_name} IN PARTITION 8", + settings=[("user", user_name)], + exitcode=exitcode, + message=message, + ) + + with Then(f"I grant the {privilege} to the {grant_target_name}"): + node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}") + + with When("I check the user is able to materialize a projection"): + node.query( + f"ALTER TABLE {table_name} MATERIALIZE PROJECTION {proj_name} IN PARTITION 8", + settings=[("user", user_name)], + ) + + with And(f"I revoke the {privilege} from {grant_target_name}"): + node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}") + + with When("I check that user is unable to materialize a projection"): + node.query( + f"ALTER TABLE {table_name} MATERIALIZE PROJECTION {proj_name} IN PARTITION 8", + settings=[("user", user_name)], + exitcode=exitcode, + message=message, + ) + + +@TestScenario +@Name("CLEAR PROJECTION") +@Requirements( + RQ_SRS_006_RBAC_Privileges_AlterProjection_Clear("1.0"), +) +def clear_projection(self, grant_target_name, user_name, node=None): + """I check that user is only able to drop projections with the ALTER TABLE CLEAR PROJECTION.""" + exitcode, message = errors.not_enough_privileges(name=f"{user_name}") + table_name = f"table_{getuid()}" + proj_name = f"proj_{getuid()}" + node = node or self.context.node + privilege = ( + current().context.privilege + if current().context.privilege != "default" + else "CLEAR PROJECTION" + ) + + with table(node, table_name, "ReplicatedMergeTree-one_shard_cluster"): + + with Given("I have a projection"): + node.query( + f"ALTER TABLE {table_name} ADD PROJECTION {proj_name} (SELECT y)" + ) + + with And("I have some data on the table"): + node.query( + f"INSERT INTO {table_name} VALUES ('2019-01-01', 'a', 2, 'zzzz', 8)" + ) + + with When("I check that user is unable to clear a projection"): + node.query( + f"ALTER TABLE {table_name} CLEAR PROJECTION {proj_name} IN PARTITION 8", + settings=[("user", user_name)], + exitcode=exitcode, + message=message, + ) + + with Then(f"I grant the {privilege} to the {grant_target_name}"): + node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}") + + with When("I check the user is able to clear a projection"): + node.query( + f"ALTER TABLE {table_name} CLEAR PROJECTION {proj_name} IN PARTITION 8", + settings=[("user", user_name)], + ) + + # with Then(f"I add the projection back"): + # node.query(f"ALTER TABLE {table_name} ADD PROJECTION {proj_name} (SELECT y)") + + with And(f"I revoke the {privilege} from {grant_target_name}"): + node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}") + + with When("I check that user is unable to clear a projection"): + node.query( + f"ALTER TABLE {table_name} CLEAR PROJECTION {proj_name} IN PARTITION 8", + settings=[("user", user_name)], + exitcode=exitcode, + message=message, + ) + + +@TestFeature +@Requirements( + RQ_SRS_006_RBAC_Privileges_AlterProjection("1.0"), + RQ_SRS_006_RBAC_Privileges_All("1.0"), +) +@Name("projections") +def feature(self, node="clickhouse1", stress=None, parallel=None): + """Check the RBAC functionality of projections.""" + self.context.node = self.context.cluster.node(node) + + if parallel is not None: + self.context.parallel = parallel + if stress is not None: + self.context.stress = stress + + user_name = f"user_{getuid()}" + role_name = f"role_{getuid()}" + + for current().context.privilege in ["ALL", "ALTER PROJECTION", "default"]: + for scenario in loads(current_module(), Scenario): + with user(self.context.node, user_name): + Scenario( + f" {current().context.privilege} privilege, {scenario.name}, privilege granted to user", + test=scenario, + setup=instrument_clickhouse_server_log, + )(grant_target_name=user_name, user_name=user_name) + + with user(self.context.node, user_name), role(self.context.node, role_name): + + with When("I grant the role to the user"): + self.context.node.query(f"GRANT {role_name} TO {user_name}") + + Scenario( + f" {current().context.privilege} privilege, {scenario.name}, privilege granted to role", + test=scenario, + setup=instrument_clickhouse_server_log, + )(grant_target_name=role_name, user_name=user_name) diff --git a/tests/testflows/rbac/tests/privileges/race_condition.py b/tests/testflows/rbac/tests/privileges/race_condition.py new file mode 100644 index 000000000000..aee5c19e180c --- /dev/null +++ b/tests/testflows/rbac/tests/privileges/race_condition.py @@ -0,0 +1,15 @@ +from testflows.core import * + +from rbac.helper.common import * + + +@TestFeature +@Name("race condition") +def feature(self): + """Check RBAC behaves as expected under heavy load with threadfuzzer enabled.""" + + self.context.cluster.node("clickhouse1").enable_thread_fuzzer() + + Feature( + run=load("rbac.tests.privileges.alter.alter_column", "feature"), parallel=True + ) diff --git a/tests/testflows/rbac/tests/privileges/system/drop_cache.py b/tests/testflows/rbac/tests/privileges/system/drop_cache.py index cda6838b9745..d49bc8eb476b 100644 --- a/tests/testflows/rbac/tests/privileges/system/drop_cache.py +++ b/tests/testflows/rbac/tests/privileges/system/drop_cache.py @@ -369,6 +369,253 @@ def uncompressed_cache(self, privilege, grant_target_name, user_name, node=None) ) +@TestSuite +def mmap_cache_privileges_granted_directly(self, node=None): + """Check that a user is able to execute `SYSTEM DROP MMAP CACHE` if and only if + they have `SYSTEM DROP MMAP CACHE` privilege granted directly. + """ + user_name = f"user_{getuid()}" + + if node is None: + node = self.context.node + + with user(node, f"{user_name}"): + + Suite( + run=mmap_cache, + examples=Examples( + "privilege grant_target_name user_name", + [ + tuple(list(row) + [user_name, user_name]) + for row in mmap_cache.examples + ], + args=Args(name="check privilege={privilege}", format_name=True), + ), + ) + + +@TestSuite +def mmap_cache_privileges_granted_via_role(self, node=None): + """Check that a user is able to execute `SYSTEM DROP MMAP CACHE` if and only if + they have `SYSTEM DROP MMAP CACHE` privilege granted via role. + """ + user_name = f"user_{getuid()}" + role_name = f"role_{getuid()}" + + if node is None: + node = self.context.node + + with user(node, f"{user_name}"), role(node, f"{role_name}"): + + with When("I grant the role to the user"): + node.query(f"GRANT {role_name} TO {user_name}") + + Suite( + run=mmap_cache, + examples=Examples( + "privilege grant_target_name user_name", + [ + tuple(list(row) + [role_name, user_name]) + for row in mmap_cache.examples + ], + args=Args(name="check privilege={privilege}", format_name=True), + ), + ) + + +@TestOutline(Suite) +@Requirements( + RQ_SRS_006_RBAC_Privileges_System_DropCache_Mmap("1.0"), +) +@Examples( + "privilege", + [ + ("ALL",), + ("SYSTEM",), + ("SYSTEM DROP CACHE",), + ("SYSTEM DROP MMAP CACHE",), + ("DROP CACHE",), + ("DROP MMAP CACHE",), + ("SYSTEM DROP MMAP",), + ("DROP MMAP",), + ], +) +def mmap_cache(self, privilege, grant_target_name, user_name, node=None): + """Run checks for `SYSTEM DROP MMAP CACHE` privilege.""" + exitcode, message = errors.not_enough_privileges(name=user_name) + + if node is None: + node = self.context.node + + with Scenario("SYSTEM DROP MMAP CACHE without privilege"): + + with When("I grant the user NONE privilege"): + node.query(f"GRANT NONE TO {grant_target_name}") + + with And("I grant the user USAGE privilege"): + node.query(f"GRANT USAGE ON *.* TO {grant_target_name}") + + with Then("I check the user is unable to execute SYSTEM DROP MMAP CACHE"): + node.query( + "SYSTEM DROP MMAP CACHE", + settings=[("user", f"{user_name}")], + exitcode=exitcode, + message=message, + ) + + with Scenario("SYSTEM DROP MMAP CACHE with privilege"): + + with When(f"I grant {privilege} on the table"): + node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}") + + with Then("I check the user is bale to execute SYSTEM DROP MMAP CACHE"): + node.query("SYSTEM DROP MMAP CACHE", settings=[("user", f"{user_name}")]) + + with Scenario("SYSTEM DROP MMAP CACHE with revoked privilege"): + + with When(f"I grant {privilege} on the table"): + node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}") + + with And(f"I revoke {privilege} on the table"): + node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}") + + with Then("I check the user is unable to execute SYSTEM DROP MMAP CACHE"): + node.query( + "SYSTEM DROP MMAP CACHE", + settings=[("user", f"{user_name}")], + exitcode=exitcode, + message=message, + ) + + +@TestSuite +def compiled_expression_cache_privileges_granted_directly(self, node=None): + """Check that a user is able to execute `SYSTEM DROP COMPILED EXPRESSION CACHE` if and only if + they have `SYSTEM DROP COMPILED EXPRESSION CACHE` privilege granted directly. + """ + user_name = f"user_{getuid()}" + + if node is None: + node = self.context.node + + with user(node, f"{user_name}"): + + Suite( + run=compiled_expression_cache, + examples=Examples( + "privilege grant_target_name user_name", + [ + tuple(list(row) + [user_name, user_name]) + for row in compiled_expression_cache.examples + ], + args=Args(name="check privilege={privilege}", format_name=True), + ), + ) + + +@TestSuite +def compiled_expression_cache_privileges_granted_via_role(self, node=None): + """Check that a user is able to execute `SYSTEM DROP COMPILED EXPRESSION CACHE` if and only if + they have `SYSTEM DROP COMPILED EXPRESSION CACHE` privilege granted via role. + """ + user_name = f"user_{getuid()}" + role_name = f"role_{getuid()}" + + if node is None: + node = self.context.node + + with user(node, f"{user_name}"), role(node, f"{role_name}"): + + with When("I grant the role to the user"): + node.query(f"GRANT {role_name} TO {user_name}") + + Suite( + run=compiled_expression_cache, + examples=Examples( + "privilege grant_target_name user_name", + [ + tuple(list(row) + [role_name, user_name]) + for row in compiled_expression_cache.examples + ], + args=Args(name="check privilege={privilege}", format_name=True), + ), + ) + + +@TestOutline(Suite) +@Requirements( + RQ_SRS_006_RBAC_Privileges_System_DropCache_CompiledExpression("1.0"), +) +@Examples( + "privilege", + [ + ("ALL",), + ("SYSTEM",), + ("SYSTEM DROP CACHE",), + ("SYSTEM DROP COMPILED EXPRESSION CACHE",), + ("DROP CACHE",), + ("DROP COMPILED EXPRESSION CACHE",), + ("SYSTEM DROP COMPILED EXPRESSION",), + # ("DROP COMPILED EXPRESSION",), + ], +) +def compiled_expression_cache(self, privilege, grant_target_name, user_name, node=None): + """Run checks for `SYSTEM DROP COMPILED EXPRESSION CACHE` privilege.""" + exitcode, message = errors.not_enough_privileges(name=user_name) + + if node is None: + node = self.context.node + + with Scenario("SYSTEM DROP COMPILED EXPRESSION CACHE without privilege"): + + with When("I grant the user NONE privilege"): + node.query(f"GRANT NONE TO {grant_target_name}") + + with And("I grant the user USAGE privilege"): + node.query(f"GRANT USAGE ON *.* TO {grant_target_name}") + + with Then( + "I check the user is unable to execute SYSTEM DROP COMPILED EXPRESSION CACHE" + ): + node.query( + "SYSTEM DROP COMPILED EXPRESSION CACHE", + settings=[("user", f"{user_name}")], + exitcode=exitcode, + message=message, + ) + + with Scenario("SYSTEM DROP COMPILED EXPRESSION CACHE with privilege"): + + with When(f"I grant {privilege} on the table"): + node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}") + + with Then( + "I check the user is bale to execute SYSTEM DROP COMPILED EXPRESSION CACHE" + ): + node.query( + "SYSTEM DROP COMPILED EXPRESSION CACHE", + settings=[("user", f"{user_name}")], + ) + + with Scenario("SYSTEM DROP COMPILED EXPRESSION CACHE with revoked privilege"): + + with When(f"I grant {privilege} on the table"): + node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}") + + with And(f"I revoke {privilege} on the table"): + node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}") + + with Then( + "I check the user is unable to execute SYSTEM DROP COMPILED EXPRESSION CACHE" + ): + node.query( + "SYSTEM DROP COMPILED EXPRESSION CACHE", + settings=[("user", f"{user_name}")], + exitcode=exitcode, + message=message, + ) + + @TestFeature @Name("system drop cache") @Requirements( @@ -404,3 +651,19 @@ def feature(self, node="clickhouse1"): run=uncompressed_cache_privileges_granted_via_role, setup=instrument_clickhouse_server_log, ) + Suite( + run=mmap_cache_privileges_granted_directly, + setup=instrument_clickhouse_server_log, + ) + Suite( + run=mmap_cache_privileges_granted_via_role, + setup=instrument_clickhouse_server_log, + ) + Suite( + run=compiled_expression_cache_privileges_granted_directly, + setup=instrument_clickhouse_server_log, + ) + Suite( + run=compiled_expression_cache_privileges_granted_via_role, + setup=instrument_clickhouse_server_log, + ) diff --git a/tests/testflows/rbac/tests/privileges/system/reload.py b/tests/testflows/rbac/tests/privileges/system/reload.py index d0c7a2caea86..3a184009c095 100644 --- a/tests/testflows/rbac/tests/privileges/system/reload.py +++ b/tests/testflows/rbac/tests/privileges/system/reload.py @@ -121,7 +121,7 @@ def config(self, privilege, grant_target_name, user_name, node=None): with When(f"I grant {privilege} on the table"): node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}") - with Then("I check the user is bale to execute SYSTEM RELOAD CONFIG"): + with Then("I check the user is able to execute SYSTEM RELOAD CONFIG"): node.query("SYSTEM RELOAD CONFIG", settings=[("user", f"{user_name}")]) with Scenario("SYSTEM RELOAD CONFIG with revoked privilege"): @@ -248,7 +248,7 @@ def dictionary(self, privilege, grant_target_name, user_name, node=None): with When(f"I grant {privilege} on the table"): node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}") - with Then("I check the user is bale to execute SYSTEM RELOAD DICTIONARY"): + with Then("I check the user is able to execute SYSTEM RELOAD DICTIONARY"): node.query( f"SYSTEM RELOAD DICTIONARY default.{dict_name}", settings=[("user", f"{user_name}")], @@ -373,7 +373,7 @@ def dictionaries(self, privilege, grant_target_name, user_name, node=None): with When(f"I grant {privilege} on the table"): node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}") - with Then("I check the user is bale to execute SYSTEM RELOAD DICTIONARIES"): + with Then("I check the user is able to execute SYSTEM RELOAD DICTIONARIES"): node.query( "SYSTEM RELOAD DICTIONARIES", settings=[("user", f"{user_name}")] ) @@ -494,7 +494,7 @@ def embedded_dictionaries(self, privilege, grant_target_name, user_name, node=No node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}") with Then( - "I check the user is bale to execute SYSTEM RELOAD EMBEDDED DICTIONARIES" + "I check the user is able to execute SYSTEM RELOAD EMBEDDED DICTIONARIES" ): node.query( "SYSTEM RELOAD EMBEDDED DICTIONARIES", @@ -520,6 +520,272 @@ def embedded_dictionaries(self, privilege, grant_target_name, user_name, node=No ) +@TestSuite +def function_privileges_granted_directly(self, node=None): + """Check that a user is able to execute `SYSTEM RELOAD FUNCTION` if and only if + they have `SYSTEM RELOAD FUNCTION` privilege granted directly. + """ + user_name = f"user_{getuid()}" + + if node is None: + node = self.context.node + + with user(node, f"{user_name}"): + + Suite( + run=function, + examples=Examples( + "privilege grant_target_name user_name", + [ + tuple(list(row) + [user_name, user_name]) + for row in function.examples + ], + args=Args(name="check privilege={privilege}", format_name=True), + ), + ) + + +@TestSuite +def function_privileges_granted_via_role(self, node=None): + """Check that a user is able to execute `SYSTEM RELOAD FUNCTION` if and only if + they have `SYSTEM RELOAD FUNCTION` privilege granted via role. + """ + user_name = f"user_{getuid()}" + role_name = f"role_{getuid()}" + + if node is None: + node = self.context.node + + with user(node, f"{user_name}"), role(node, f"{role_name}"): + + with When("I grant the role to the user"): + node.query(f"GRANT {role_name} TO {user_name}") + + Suite( + run=function, + examples=Examples( + "privilege grant_target_name user_name", + [ + tuple(list(row) + [role_name, user_name]) + for row in function.examples + ], + args=Args(name="check privilege={privilege}", format_name=True), + ), + ) + + +@TestOutline(Suite) +@Requirements( + RQ_SRS_006_RBAC_Privileges_System_Reload_Function("1.0"), +) +@Examples( + "privilege", + [ + ("ALL",), + ("SYSTEM",), + ("SYSTEM RELOAD",), + ("SYSTEM RELOAD FUNCTION",), + ], +) +def function(self, privilege, grant_target_name, user_name, node=None): + """Run checks for `SYSTEM RELOAD FUNCTION` privilege.""" + exitcode, message = errors.not_enough_privileges(name=user_name) + func_name = f"func_{getuid()}" + + if node is None: + node = self.context.node + + try: + with Given("I have a function"): + node.query(f"CREATE FUNCTION {func_name} AS (x) -> 2*x;") + + with Scenario("SYSTEM RELOAD FUNCTION without privilege"): + + with When("I grant the user NONE privilege"): + node.query(f"GRANT NONE TO {grant_target_name}") + + with And("I grant the user USAGE privilege"): + node.query(f"GRANT USAGE ON *.* TO {grant_target_name}") + + with Then("I check the user is unable to execute SYSTEM RELOAD FUNCTIONS"): + node.query( + "SYSTEM RELOAD FUNCTIONS", + settings=[("user", f"{user_name}")], + exitcode=exitcode, + message=message, + ) + + # with And( + # "I check the user is unable to execute SYSTEM RELOAD FUNCTION on a specific function" + # ): + # node.query( + # f"SYSTEM RELOAD FUNCTION {func_name}", + # settings=[("user", f"{user_name}")], + # exitcode=exitcode, + # message=message, + # ) + + with Scenario("SYSTEM RELOAD FUNCTION with privilege"): + + with When(f"I grant {privilege} on the table"): + node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}") + + with Then("I check the user is able to execute SYSTEM RELOAD FUNCTIONS"): + node.query( + "SYSTEM RELOAD FUNCTIONS", + settings=[("user", f"{user_name}")], + ) + + # with And( + # "I check the user is able to execute SYSTEM RELOAD FUNCTION on a specific function" + # ): + # node.query( + # f"SYSTEM RELOAD FUNCTION {func_name}", + # settings=[("user", f"{user_name}")], + # ) + + with Scenario("SYSTEM RELOAD FUNCTION with revoked privilege"): + + with When(f"I grant {privilege} on the table"): + node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}") + + with And(f"I revoke {privilege} on the table"): + node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}") + + with Then("I check the user is unable to execute SYSTEM RELOAD FUNCTIONS"): + node.query( + "SYSTEM RELOAD FUNCTIONS", + settings=[("user", f"{user_name}")], + exitcode=exitcode, + message=message, + ) + + # with And( + # "I check the user is unable to execute SYSTEM RELOAD FUNCTION on a specific function" + # ): + # node.query( + # f"SYSTEM RELOAD FUNCTION {func_name}", + # settings=[("user", f"{user_name}")], + # exitcode=exitcode, + # message=message, + # ) + finally: + with Finally("I drop the function"): + node.query(f"DROP FUNCTION IF EXISTS {func_name}") + + +@TestSuite +def symbols_privileges_granted_directly(self, node=None): + """Check that a user is able to execute `SYSTEM RELOAD SYMBOLS` if and only if + they have `SYSTEM RELOAD SYMBOLS` privilege granted directly. + """ + user_name = f"user_{getuid()}" + + if node is None: + node = self.context.node + + with user(node, f"{user_name}"): + + Suite( + run=symbols, + examples=Examples( + "privilege grant_target_name user_name", + [tuple(list(row) + [user_name, user_name]) for row in symbols.examples], + args=Args(name="check privilege={privilege}", format_name=True), + ), + ) + + +@TestSuite +def symbols_privileges_granted_via_role(self, node=None): + """Check that a user is able to execute `SYSTEM RELOAD SYMBOLS` if and only if + they have `SYSTEM RELOAD SYMBOLS` privilege granted via role. + """ + user_name = f"user_{getuid()}" + role_name = f"role_{getuid()}" + + if node is None: + node = self.context.node + + with user(node, f"{user_name}"), role(node, f"{role_name}"): + + with When("I grant the role to the user"): + node.query(f"GRANT {role_name} TO {user_name}") + + Suite( + run=symbols, + examples=Examples( + "privilege grant_target_name user_name", + [tuple(list(row) + [role_name, user_name]) for row in symbols.examples], + args=Args(name="check privilege={privilege}", format_name=True), + ), + ) + + +@TestOutline(Suite) +@Requirements( + RQ_SRS_006_RBAC_Privileges_System_Reload_Symbols("1.0"), +) +@Examples( + "privilege", + [ + ("ALL",), + ("SYSTEM",), + ("SYSTEM RELOAD",), + ("SYSTEM RELOAD SYMBOLS",), + ], +) +def symbols(self, privilege, grant_target_name, user_name, node=None): + """Run checks for `SYSTEM RELOAD SYMBOLS` privilege.""" + exitcode, message = errors.not_enough_privileges(name=user_name) + + if node is None: + node = self.context.node + + with Scenario("SYSTEM RELOAD SYMBOLS without privilege"): + + with When("I grant the user NONE privilege"): + node.query(f"GRANT NONE TO {grant_target_name}") + + with And("I grant the user USAGE privilege"): + node.query(f"GRANT USAGE ON *.* TO {grant_target_name}") + + with Then("I check the user is unable to execute SYSTEM RELOAD SYMBOLS"): + node.query( + "SYSTEM RELOAD SYMBOLS", + settings=[("user", f"{user_name}")], + exitcode=exitcode, + message=message, + ) + + with Scenario("SYSTEM RELOAD SYMBOLS with privilege"): + + with When(f"I grant {privilege} on the table"): + node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}") + + with Then("I check the user is able to execute SYSTEM RELOAD SYMBOLS"): + node.query( + "SYSTEM RELOAD SYMBOLS", + settings=[("user", f"{user_name}")], + ) + + with Scenario("SYSTEM RELOAD SYMBOLS with revoked privilege"): + + with When(f"I grant {privilege} on the table"): + node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}") + + with And(f"I revoke {privilege} on the table"): + node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}") + + with Then("I check the user is unable to execute SYSTEM RELOAD SYMBOLS"): + node.query( + "SYSTEM RELOAD SYMBOLS", + settings=[("user", f"{user_name}")], + exitcode=exitcode, + message=message, + ) + + @TestFeature @Name("system reload") @Requirements( @@ -561,3 +827,19 @@ def feature(self, node="clickhouse1"): run=embedded_dictionaries_privileges_granted_via_role, setup=instrument_clickhouse_server_log, ) + Suite( + run=function_privileges_granted_directly, + setup=instrument_clickhouse_server_log, + ) + Suite( + run=function_privileges_granted_via_role, + setup=instrument_clickhouse_server_log, + ) + Suite( + run=symbols_privileges_granted_directly, + setup=instrument_clickhouse_server_log, + ) + Suite( + run=symbols_privileges_granted_via_role, + setup=instrument_clickhouse_server_log, + ) diff --git a/tests/testflows/rbac/tests/privileges/system/restart_disk.py b/tests/testflows/rbac/tests/privileges/system/restart_disk.py new file mode 100644 index 000000000000..cebc7718f0c7 --- /dev/null +++ b/tests/testflows/rbac/tests/privileges/system/restart_disk.py @@ -0,0 +1,137 @@ +from testflows.core import * +from testflows.asserts import error + +from rbac.requirements import * +from rbac.helper.common import * +import rbac.helper.errors as errors + + +@TestSuite +def privileges_granted_directly(self, node=None): + """Check that a user is able to execute `SYSTEM RESTART DISK` commands if and only if + the privilege has been granted directly. + """ + user_name = f"user_{getuid()}" + + if node is None: + node = self.context.node + + with user(node, f"{user_name}"): + + Suite( + run=restart_disk, + examples=Examples( + "privilege grant_target_name user_name", + [ + tuple(list(row) + [user_name, user_name]) + for row in restart_disk.examples + ], + args=Args(name="check privilege={privilege}", format_name=True), + ), + ) + + +@TestSuite +def privileges_granted_via_role(self, node=None): + """Check that a user is able to execute `SYSTEM RESTART DISK` commands if and only if + the privilege has been granted via role. + """ + user_name = f"user_{getuid()}" + role_name = f"role_{getuid()}" + + if node is None: + node = self.context.node + + with user(node, f"{user_name}"), role(node, f"{role_name}"): + + with When("I grant the role to the user"): + node.query(f"GRANT {role_name} TO {user_name}") + + Suite( + run=restart_disk, + examples=Examples( + "privilege grant_target_name user_name", + [ + tuple(list(row) + [role_name, user_name]) + for row in restart_disk.examples + ], + args=Args(name="check privilege={privilege}", format_name=True), + ), + ) + + +@TestOutline(Suite) +@Examples( + "privilege", + [ + ("ALL",), + ("SYSTEM",), + ("SYSTEM RESTART DISK",), + ], +) +def restart_disk(self, privilege, grant_target_name, user_name, node=None): + """Check that user is only able to execute `SYSTEM RESTART DISK` when they have privilege.""" + exitcode, message = errors.not_enough_privileges(name=user_name) + + if node is None: + node = self.context.node + + with Scenario("SYSTEM RESTART DISK without privilege"): + + with When("I grant the user NONE privilege"): + node.query(f"GRANT NONE TO {grant_target_name}") + + with And("I grant the user USAGE privilege"): + node.query(f"GRANT USAGE ON *.* TO {grant_target_name}") + + with Then("I check the user can't restart disk"): + node.query( + f"SYSTEM RESTART DISK some_disk", + settings=[("user", f"{user_name}")], + exitcode=exitcode, + message=message, + ) + + with Scenario("SYSTEM RESTART DISK with privilege"): + + with When(f"I grant {privilege} on the table"): + node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}") + + with Then("I check the user can restart disk"): + node.query( + f"SYSTEM RESTART DISK some_disk", + settings=[("user", f"{user_name}")], + exitcode=223, + message="DB::Exception: Unknown disk", + ) + + with Scenario("SYSTEM RESTART DISK with revoked privilege"): + + with When(f"I grant {privilege} on the table"): + node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}") + + with And(f"I revoke {privilege} on the table"): + node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}") + + with Then("I check the user can't restart disk"): + node.query( + f"SYSTEM RESTART DISK some_disk", + settings=[("user", f"{user_name}")], + exitcode=exitcode, + message=message, + ) + + +@TestFeature +@Name("system restart disk") +@Requirements( + RQ_SRS_006_RBAC_Privileges_System_RestartDisk("1.0"), + RQ_SRS_006_RBAC_Privileges_All("1.0"), + RQ_SRS_006_RBAC_Privileges_None("1.0"), +) +def feature(self, node="clickhouse1"): + """Check the RBAC functionality of SYSTEM RESTART DISK.""" + self.context.node = self.context.cluster.node(node) + + Suite(run=privileges_granted_directly, setup=instrument_clickhouse_server_log) + Suite(run=privileges_granted_via_role, setup=instrument_clickhouse_server_log) diff --git a/tests/testflows/rbac/tests/privileges/system/restore_replica.py b/tests/testflows/rbac/tests/privileges/system/restore_replica.py new file mode 100644 index 000000000000..29f7316f5438 --- /dev/null +++ b/tests/testflows/rbac/tests/privileges/system/restore_replica.py @@ -0,0 +1,143 @@ +from testflows.core import * +from testflows.asserts import error + +from rbac.requirements import * +from rbac.helper.common import * +import rbac.helper.errors as errors + + +@TestSuite +def privileges_granted_directly(self, node=None): + """Check that a user is able to execute `SYSTEM RESTORE REPLICA` commands if and only if + the privilege has been granted directly. + """ + user_name = f"user_{getuid()}" + + if node is None: + node = self.context.node + + with user(node, f"{user_name}"): + + Suite( + run=restore_replica, + examples=Examples( + "privilege on grant_target_name user_name", + [ + tuple(list(row) + [user_name, user_name]) + for row in restore_replica.examples + ], + args=Args(name="check privilege={privilege}", format_name=True), + ), + ) + + +@TestSuite +def privileges_granted_via_role(self, node=None): + """Check that a user is able to execute `SYSTEM RESTORE REPLICA` commands if and only if + the privilege has been granted via role. + """ + user_name = f"user_{getuid()}" + role_name = f"role_{getuid()}" + + if node is None: + node = self.context.node + + with user(node, f"{user_name}"), role(node, f"{role_name}"): + + with When("I grant the role to the user"): + node.query(f"GRANT {role_name} TO {user_name}") + + Suite( + run=restore_replica, + examples=Examples( + "privilege on grant_target_name user_name", + [ + tuple(list(row) + [role_name, user_name]) + for row in restore_replica.examples + ], + args=Args(name="check privilege={privilege}", format_name=True), + ), + ) + + +@TestOutline(Suite) +@Examples( + "privilege on", + [ + ("ALL", "*.*"), + ("SYSTEM", "*.*"), + ("SYSTEM RESTORE REPLICA", "table"), + ("RESTORE REPLICA", "table"), + ], +) +def restore_replica(self, privilege, on, grant_target_name, user_name, node=None): + """Check that user is only able to execute `SYSTEM RESTORE REPLICA` when they have privilege.""" + exitcode, message = errors.not_enough_privileges(name=user_name) + table_name = f"table_name_{getuid()}" + + if node is None: + node = self.context.node + + on = on.replace("table", f"{table_name}") + + with table(node, table_name, "ReplicatedMergeTree-sharded_cluster"): + + with Scenario("SYSTEM RESTORE REPLICA without privilege"): + + with When("I grant the user NONE privilege"): + node.query(f"GRANT NONE TO {grant_target_name}") + + with And("I grant the user USAGE privilege"): + node.query(f"GRANT USAGE ON *.* TO {grant_target_name}") + + with Then("I check the user can't restore replica"): + node.query( + f"SYSTEM RESTORE REPLICA {table_name}", + settings=[("user", f"{user_name}")], + exitcode=exitcode, + message=message, + ) + + with Scenario("SYSTEM RESTORE REPLICA with privilege"): + + with When(f"I grant {privilege} on the table"): + node.query(f"GRANT {privilege} ON {on} TO {grant_target_name}") + + with Then("I check the user can restore replica"): + node.query( + f"SYSTEM RESTORE REPLICA {table_name}", + settings=[("user", f"{user_name}")], + exitcode=36, + message="DB::Exception: Replica must be readonly", + ) + + with Scenario("SYSTEM RESTORE REPLICA with revoked privilege"): + + with When(f"I grant {privilege} on the table"): + node.query(f"GRANT {privilege} ON {on} TO {grant_target_name}") + + with And(f"I revoke {privilege} on the table"): + node.query(f"REVOKE {privilege} ON {on} FROM {grant_target_name}") + + with Then("I check the user can't restore replica"): + node.query( + f"SYSTEM RESTORE REPLICA {table_name}", + settings=[("user", f"{user_name}")], + exitcode=exitcode, + message=message, + ) + + +@TestFeature +@Name("system restore replica") +@Requirements( + RQ_SRS_006_RBAC_Privileges_System_RestoreReplica("1.0"), + RQ_SRS_006_RBAC_Privileges_All("1.0"), + RQ_SRS_006_RBAC_Privileges_None("1.0"), +) +def feature(self, node="clickhouse1"): + """Check the RBAC functionality of SYSTEM RESTORE REPLICA.""" + self.context.node = self.context.cluster.node(node) + + Suite(run=privileges_granted_directly, setup=instrument_clickhouse_server_log) + Suite(run=privileges_granted_via_role, setup=instrument_clickhouse_server_log) diff --git a/tests/testflows/rbac/tests/privileges/system/thread_fuzzer.py b/tests/testflows/rbac/tests/privileges/system/thread_fuzzer.py new file mode 100644 index 000000000000..a6294d8bf3f8 --- /dev/null +++ b/tests/testflows/rbac/tests/privileges/system/thread_fuzzer.py @@ -0,0 +1,217 @@ +from testflows.core import * +from testflows.asserts import error + +from rbac.requirements import * +from rbac.helper.common import * +import rbac.helper.errors as errors + + +@TestSuite +def privileges_granted_directly(self, node=None): + """Check that a user is able to execute `SYSTEM THREAD FUZZER` commands if and only if + the privilege has been granted directly. + """ + user_name = f"user_{getuid()}" + + if node is None: + node = self.context.node + + with user(node, f"{user_name}"): + + Suite( + run=start_thread_fuzzer, + examples=Examples( + "privilege grant_target_name user_name", + [ + tuple(list(row) + [user_name, user_name]) + for row in start_thread_fuzzer.examples + ], + args=Args(name="check privilege={privilege}", format_name=True), + ), + ) + + Suite( + run=stop_thread_fuzzer, + examples=Examples( + "privilege grant_target_name user_name", + [ + tuple(list(row) + [user_name, user_name]) + for row in stop_thread_fuzzer.examples + ], + args=Args(name="check privilege={privilege}", format_name=True), + ), + ) + + +@TestSuite +def privileges_granted_via_role(self, node=None): + """Check that a user is able to execute `SYSTEM THREAD FUZZER` commands if and only if + the privilege has been granted via role. + """ + user_name = f"user_{getuid()}" + role_name = f"role_{getuid()}" + + if node is None: + node = self.context.node + + with user(node, f"{user_name}"), role(node, f"{role_name}"): + + with When("I grant the role to the user"): + node.query(f"GRANT {role_name} TO {user_name}") + + Suite( + run=start_thread_fuzzer, + examples=Examples( + "privilege grant_target_name user_name", + [ + tuple(list(row) + [role_name, user_name]) + for row in start_thread_fuzzer.examples + ], + args=Args(name="check privilege={privilege}", format_name=True), + ), + ) + + Suite( + run=stop_thread_fuzzer, + examples=Examples( + "privilege grant_target_name user_name", + [ + tuple(list(row) + [role_name, user_name]) + for row in stop_thread_fuzzer.examples + ], + args=Args(name="check privilege={privilege}", format_name=True), + ), + ) + + +@TestOutline(Suite) +@Examples( + "privilege", + [ + ("ALL",), + ("SYSTEM",), + ("SYSTEM THREAD FUZZER",), + ], +) +def start_thread_fuzzer(self, privilege, grant_target_name, user_name, node=None): + """Check that user is only able to execute `SYSTEM START THREAD FUZZER` when they have privilege.""" + exitcode, message = errors.not_enough_privileges(name=user_name) + + if node is None: + node = self.context.node + + with Scenario("SYSTEM START THREAD FUZZER without privilege"): + + with When("I grant the user NONE privilege"): + node.query(f"GRANT NONE TO {grant_target_name}") + + with And("I grant the user USAGE privilege"): + node.query(f"GRANT USAGE ON *.* TO {grant_target_name}") + + with Then("I check the user can't start thread fuzzer"): + node.query( + f"SYSTEM START THREAD FUZZER", + settings=[("user", f"{user_name}")], + exitcode=exitcode, + message=message, + ) + + with Scenario("SYSTEM START THREAD FUZZER with privilege"): + + with When(f"I grant {privilege} on the table"): + node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}") + + with Then("I check the user can start thread fuzzer"): + node.query( + f"SYSTEM START THREAD FUZZER", settings=[("user", f"{user_name}")] + ) + + with Scenario("SYSTEM START THREAD FUZZER with revoked privilege"): + + with When(f"I grant {privilege} on the table"): + node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}") + + with And(f"I revoke {privilege} on the table"): + node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}") + + with Then("I check the user can't start thread fuzzer"): + node.query( + f"SYSTEM START THREAD FUZZER", + settings=[("user", f"{user_name}")], + exitcode=exitcode, + message=message, + ) + + +@TestOutline(Suite) +@Examples( + "privilege", + [ + ("ALL",), + ("SYSTEM",), + ("SYSTEM THREAD FUZZER",), + ], +) +def stop_thread_fuzzer(self, privilege, grant_target_name, user_name, node=None): + """Check that user is only able to execute `SYSTEM STOP THREAD FUZZER` when they have privilege.""" + exitcode, message = errors.not_enough_privileges(name=user_name) + + if node is None: + node = self.context.node + + with Scenario("SYSTEM STOP THREAD FUZZER without privilege"): + + with When("I grant the user NONE privilege"): + node.query(f"GRANT NONE TO {grant_target_name}") + + with And("I grant the user USAGE privilege"): + node.query(f"GRANT USAGE ON *.* TO {grant_target_name}") + + with Then("I check the user can't stop thread fuzzer"): + node.query( + f"SYSTEM STOP THREAD FUZZER", + settings=[("user", f"{user_name}")], + exitcode=exitcode, + message=message, + ) + + with Scenario("SYSTEM STOP THREAD FUZZER with privilege"): + + with When(f"I grant {privilege} on the table"): + node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}") + + with Then("I check the user can stop thread fuzzer"): + node.query( + f"SYSTEM STOP THREAD FUZZER", settings=[("user", f"{user_name}")] + ) + + with Scenario("SYSTEM STOP THREAD FUZZER with revoked privilege"): + + with When(f"I grant {privilege} on the table"): + node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}") + + with And(f"I revoke {privilege} on the table"): + node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}") + + with Then("I check the user can't stop thread fuzzer"): + node.query( + f"SYSTEM STOP THREAD FUZZER", + settings=[("user", f"{user_name}")], + exitcode=exitcode, + message=message, + ) + + +@TestFeature +@Name("system thread fuzzer") +@Requirements( + RQ_SRS_006_RBAC_Privileges_System_ThreadFuzzer("1.0"), + RQ_SRS_006_RBAC_Privileges_All("1.0"), + RQ_SRS_006_RBAC_Privileges_None("1.0"), +) +def feature(self, node="clickhouse1"): + """Check the RBAC functionality of SYSTEM THREAD FUZZER.""" + self.context.node = self.context.cluster.node(node) + + Suite(run=privileges_granted_directly, setup=instrument_clickhouse_server_log) + Suite(run=privileges_granted_via_role, setup=instrument_clickhouse_server_log) diff --git a/tests/testflows/rbac/tests/privileges/table_functions.py b/tests/testflows/rbac/tests/privileges/table_functions.py new file mode 100644 index 000000000000..c089ac2ed0c7 --- /dev/null +++ b/tests/testflows/rbac/tests/privileges/table_functions.py @@ -0,0 +1,320 @@ +from testflows.core import * +from testflows.asserts import error + +from rbac.helper.common import * +import rbac.helper.errors as errors +from rbac.requirements import ( + RQ_SRS_006_RBAC_Select_TableFunctions_Cluster, + RQ_SRS_006_RBAC_Select_TableFunctions_Remote, +) + + +@TestScenario +@Requirements(RQ_SRS_006_RBAC_Select_TableFunctions_Remote("1.0")) +def remote(self, node=None): + """Check that user is able to create a table as remote table function + only if they have REMOTE, SELECT, and CREATE TEMPORARY TABLE priviliges. + """ + user_name = f"user_{getuid()}" + table_name = f"table_{getuid()}" + exitcode, message = errors.not_enough_privileges(name=f"{user_name}") + + if node is None: + node = self.context.node + + node2 = self.context.cluster.node("clickhouse2") + node3 = self.context.cluster.node("clickhouse3") + + try: + with Given("I have a table on a cluster with two nodes on seperate shards"): + node.query( + f"CREATE TABLE {table_name} ON CLUSTER sharded_cluster12 (x UInt8) ENGINE=Memory" + ) + + with And(f"I have a user {user_name} on three nodes on seperate shards"): + node.query(f"CREATE USER {user_name} ON CLUSTER sharded_cluster") + + with And("I have some data in the table on clickhouse1"): + node.query(f"INSERT INTO {table_name} VALUES (1)") + + with When( + f"I try to select from the table using remote table function as {user_name}" + ): + node.query( + f"SELECT * FROM remote(sharded_cluster12, default.{table_name})", + exitcode=exitcode, + message=message, + settings=[("user", f"{user_name}")], + ) + + with Then("I grant CREATE TEMPORARY TABLE and REMOTE privileges to a user"): + node.query(f"GRANT CREATE TEMPORARY TABLE ON *.* TO {user_name}") + node.query(f"GRANT REMOTE ON *.* TO {user_name}") + + with When( + f"I try to select from the table using remote table function as {user_name}" + ): + node.query( + f"SELECT * FROM remote(sharded_cluster12, default.{table_name})", + exitcode=exitcode, + message=message, + settings=[("user", f"{user_name}")], + ) + + with Then("I grant SELECT privilege on the source table"): + node.query(f"GRANT SELECT ON {table_name} TO {user_name}") + + with And(f"I successfully select from the remote table as {user_name}"): + output = node.query( + f"SELECT * FROM remote(sharded_cluster12, default.{table_name})", + settings=[("user", f"{user_name}")], + ).output + default = node.query(f"SELECT * FROM {table_name}").output + assert output == default, error() + + with When( + "I try to select from the table as the same user, but from clickhouse2" + ): + node2.query( + f"SELECT * FROM remote(sharded_cluster12, default.{table_name})", + exitcode=exitcode, + message=message, + settings=[("user", f"{user_name}")], + ) + + with Then( + "I grant CREATE TEMPORARY TABLE and REMOTE to the user on clickhouse2" + ): + node2.query(f"GRANT CREATE TEMPORARY TABLE ON *.* TO {user_name}") + node2.query(f"GRANT REMOTE ON *.* TO {user_name}") + + with When( + "I try to select from the table as the same user, but from clickhouse2" + ): + node2.query( + f"SELECT * FROM remote(sharded_cluster12, default.{table_name})", + exitcode=exitcode, + message=message, + settings=[("user", f"{user_name}")], + ) + + with Then(f"I grant SELECT on {table_name} to the user on clickhouse2"): + node2.query(f"GRANT SELECT ON {table_name} TO {user_name}") + + with Then( + f"I successfully select from the remote table as {user_name} from clickhouse2" + ): + output = node2.query( + f"SELECT * FROM remote(sharded_cluster12, default.{table_name})", + settings=[("user", f"{user_name}")], + ).output + assert output == default, error() + + with When( + "I try to select from the table as the same user, but from clickhouse3" + ): + node3.query( + f"SELECT * FROM remote(sharded_cluster12, default.{table_name})", + exitcode=exitcode, + message=message, + settings=[("user", f"{user_name}")], + ) + + with Then( + "I grant CREATE TEMPORARY TABLE and REMOTE to the user on clickhouse3" + ): + node3.query(f"GRANT CREATE TEMPORARY TABLE ON *.* TO {user_name}") + node3.query(f"GRANT REMOTE ON *.* TO {user_name}") + + with When( + "I try to select from the table as the same user, but from clickhouse3" + ): + node3.query( + f"SELECT * FROM remote(sharded_cluster12, default.{table_name})", + exitcode=exitcode, + message=message, + settings=[("user", f"{user_name}")], + ) + + with Then(f"I grant SELECT on {table_name} to the user on clickhouse3"): + node3.query(f"GRANT SELECT ON {table_name} TO {user_name}") + + with Then( + f"I successfully select from the remote table as {user_name} from clickhouse3" + ): + output = node3.query( + f"SELECT * FROM remote(sharded_cluster12, default.{table_name})", + settings=[("user", f"{user_name}")], + ).output + assert output == default, error() + + finally: + with Finally(f"I drop the table {table_name} from the cluster", flags=TE): + node.query( + f"DROP TABLE IF EXISTS {table_name} ON CLUSTER sharded_cluster12" + ) + + with And(f"I drop the user from the cluster", flags=TE): + node.query(f"DROP USER IF EXISTS {user_name} ON CLUSTER sharded_cluster") + + +@TestScenario +@Requirements(RQ_SRS_006_RBAC_Select_TableFunctions_Cluster("1.0")) +def cluster(self, node=None): + """Check that user is able to create a table as cluster table function + only if they have REMOTE, SELECT, and CREATE TEMPORARY TABLE priviliges. + """ + user_name = f"user_{getuid()}" + table_name = f"table_{getuid()}" + exitcode, message = errors.not_enough_privileges(name=f"{user_name}") + + if node is None: + node = self.context.node + + node2 = self.context.cluster.node("clickhouse2") + node3 = self.context.cluster.node("clickhouse3") + + try: + with Given("I have a table on a cluster with two nodes on seperate shards"): + node.query( + f"CREATE TABLE {table_name} ON CLUSTER sharded_cluster12 (x UInt8) ENGINE=Memory" + ) + + with And(f"I have a user {user_name} on three nodes on seperate shards"): + node.query(f"CREATE USER {user_name} ON CLUSTER sharded_cluster") + + with And("I have some data in the table on clickhouse1"): + node.query(f"INSERT INTO {table_name} VALUES (1)") + + with When( + f"I try to select from the table using cluster table function as {user_name}" + ): + node.query( + f"SELECT * FROM cluster(sharded_cluster12, default.{table_name})", + exitcode=exitcode, + message=message, + settings=[("user", f"{user_name}")], + ) + + with Then("I grant CREATE TEMPORARY TABLE and cluster privileges to a user"): + node.query(f"GRANT CREATE TEMPORARY TABLE ON *.* TO {user_name}") + node.query(f"GRANT REMOTE ON *.* TO {user_name}") + + with When( + f"I try to select from the table using cluster table function as {user_name}" + ): + node.query( + f"SELECT * FROM cluster(sharded_cluster12, default.{table_name})", + exitcode=exitcode, + message=message, + settings=[("user", f"{user_name}")], + ) + + with Then("I grant SELECT privilege on the source table"): + node.query(f"GRANT SELECT ON {table_name} TO {user_name}") + + with And(f"I successfully select from the cluster table as {user_name}"): + output = node.query( + f"SELECT * FROM cluster(sharded_cluster12, default.{table_name})", + settings=[("user", f"{user_name}")], + ).output + default = node.query(f"SELECT * FROM {table_name}").output + assert output == default, error() + + with When( + "I try to select from the table as the same user, but from clickhouse2" + ): + node2.query( + f"SELECT * FROM cluster(sharded_cluster12, default.{table_name})", + exitcode=exitcode, + message=message, + settings=[("user", f"{user_name}")], + ) + + with Then( + "I grant CREATE TEMPORARY TABLE and cluster to the user on clickhouse2" + ): + node2.query(f"GRANT CREATE TEMPORARY TABLE ON *.* TO {user_name}") + node2.query(f"GRANT REMOTE ON *.* TO {user_name}") + + with When( + "I try to select from the table as the same user, but from clickhouse2" + ): + node2.query( + f"SELECT * FROM cluster(sharded_cluster12, default.{table_name})", + exitcode=exitcode, + message=message, + settings=[("user", f"{user_name}")], + ) + + with Then(f"I grant SELECT on {table_name} to the user on clickhouse2"): + node2.query(f"GRANT SELECT ON {table_name} TO {user_name}") + + with Then( + f"I successfully select from the cluster table as {user_name} from clickhouse2" + ): + output = node2.query( + f"SELECT * FROM cluster(sharded_cluster12, default.{table_name})", + settings=[("user", f"{user_name}")], + ).output + assert output == default, error() + + with When( + "I try to select from the table as the same user, but from clickhouse3" + ): + node3.query( + f"SELECT * FROM cluster(sharded_cluster12, default.{table_name})", + exitcode=exitcode, + message=message, + settings=[("user", f"{user_name}")], + ) + + with Then( + "I grant CREATE TEMPORARY TABLE and cluster to the user on clickhouse3" + ): + node3.query(f"GRANT CREATE TEMPORARY TABLE ON *.* TO {user_name}") + node3.query(f"GRANT REMOTE ON *.* TO {user_name}") + + with When( + "I try to select from the table as the same user, but from clickhouse3" + ): + node3.query( + f"SELECT * FROM cluster(sharded_cluster12, default.{table_name})", + exitcode=exitcode, + message=message, + settings=[("user", f"{user_name}")], + ) + + with Then(f"I grant SELECT on {table_name} to the user on clickhouse3"): + node3.query(f"GRANT SELECT ON {table_name} TO {user_name}") + + with Then( + f"I successfully select from the cluster table as {user_name} from clickhouse3" + ): + output = node3.query( + f"SELECT * FROM cluster(sharded_cluster12, default.{table_name})", + settings=[("user", f"{user_name}")], + ).output + assert output == default, error() + + finally: + with Finally(f"I drop the table {table_name} from the cluster", flags=TE): + node.query( + f"DROP TABLE IF EXISTS {table_name} ON CLUSTER sharded_cluster12" + ) + + with And(f"I drop the user from the cluster", flags=TE): + node.query(f"DROP USER IF EXISTS {user_name} ON CLUSTER sharded_cluster") + + +@TestOutline(Feature) +@Name("table functions") +def feature(self, stress=None, node="clickhouse1"): + """Check the RBAC functionality of table functions.""" + self.context.node = self.context.cluster.node(node) + + if stress is not None: + self.context.stress = stress + + Scenario(run=remote) + Scenario(run=cluster) diff --git a/tests/testflows/regression.py b/tests/testflows/regression.py index 80bbb2bc3f2e..faa2982d8da5 100755 --- a/tests/testflows/regression.py +++ b/tests/testflows/regression.py @@ -10,9 +10,7 @@ @TestModule @Name("clickhouse") @ArgumentParser(argparser) -def regression( - self, local, clickhouse_binary_path, clickhouse_version=None, stress=None -): +def regression(self, local, clickhouse_binary_path, clickhouse_version, stress=None): """ClickHouse regression.""" args = { "local": local, @@ -22,53 +20,45 @@ def regression( } self.context.stress = stress - self.context.clickhouse_version = clickhouse_version - with Pool(8) as pool: + with Pool(4) as pool: try: + # Feature( + # test=load("aes_encryption.regression", "regression"), + # parallel=True, + # executor=pool, + # )(**args) + # Feature( + # test=load("datetime64_extended_range.regression", "regression"), + # parallel=True, + # executor=pool, + # )(**args) Feature( test=load("example.regression", "regression"), parallel=True, executor=pool, )(**args) -# Feature( -# test=load("ldap.regression", "regression"), parallel=True, executor=pool -# )(**args) -# Feature( -# test=load("rbac.regression", "regression"), parallel=True, executor=pool -# )(**args) -# Feature( -# test=load("aes_encryption.regression", "regression"), -# parallel=True, -# executor=pool, -# )( -# **args -# ) # TODO: fix it! -# # Feature(test=load("map_type.regression", "regression"), parallel=True, executor=pool)(**args) # TODO: fix it! -# Feature( -# test=load("window_functions.regression", "regression"), -# parallel=True, -# executor=pool, -# )( -# **args -# ) # TODO: fix it! -# Feature( -# test=load("datetime64_extended_range.regression", "regression"), -# parallel=True, -# executor=pool, -# )(**args) -# Feature( -# test=load("kerberos.regression", "regression"), -# parallel=True, -# executor=pool, -# )(**args) -# Feature( -# test=load("extended_precision_data_types.regression", "regression"), -# parallel=True, -# executor=pool, -# )( -# **args -# ) # TODO: fix it! + # Feature( + # test=load("extended_precision_data_types.regression", "regression"), + # parallel=True, + # executor=pool, + # )(**args) + # Feature( + # test=load("ldap.regression", "regression"), parallel=True, executor=pool + # )(**args) + # Feature( + # test=load("map_type.regression", "regression"), + # parallel=True, + # executor=pool, + # )(**args) + # Feature( + # test=load("rbac.regression", "regression"), parallel=True, executor=pool + # )(**args) + # Feature( + # test=load("window_functions.regression", "regression"), + # parallel=True, + # executor=pool, + # )(**args) finally: join() diff --git a/tests/testflows/window_functions/configs/clickhouse/config.d/macros.xml b/tests/testflows/window_functions/configs/clickhouse/config.d/macros.xml new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/testflows/window_functions/configs/clickhouse/config.xml b/tests/testflows/window_functions/configs/clickhouse/config.xml new file mode 100644 index 000000000000..842a0573d49b --- /dev/null +++ b/tests/testflows/window_functions/configs/clickhouse/config.xml @@ -0,0 +1,448 @@ + + + + + + trace + /var/log/clickhouse-server/clickhouse-server.log + /var/log/clickhouse-server/clickhouse-server.err.log + 1000M + 10 + + + + 8123 + 9000 + + + + + + + + + /etc/clickhouse-server/server.crt + /etc/clickhouse-server/server.key + + /etc/clickhouse-server/dhparam.pem + none + true + true + sslv2,sslv3 + true + + + + true + true + sslv2,sslv3 + true + + + + RejectCertificateHandler + + + + + + + + + 9009 + + + + + + + + 0.0.0.0 + + + + + + + + + + + + 4096 + 3 + + + 100 + + + + + + 8589934592 + + + 5368709120 + + + + /var/lib/clickhouse/ + + + /var/lib/clickhouse/tmp/ + + + /var/lib/clickhouse/user_files/ + + + /var/lib/clickhouse/access/ + + + + + + users.xml + + + + /var/lib/clickhouse/access/ + + + + + users.xml + + + default + + + + + + default + + + + + + + + + false + + + + + + + + localhost + 9000 + + + + + + + localhost + 9000 + + + + + localhost + 9000 + + + + + + + localhost + 9440 + 1 + + + + + + + localhost + 9000 + + + + + localhost + 1 + + + + + + + + + + + + + + + + + 3600 + + + + 3600 + + + 60 + + + + + + + + + + system + query_log
+ + toYYYYMM(event_date) + + 7500 +
+ + + + system + trace_log
+ + toYYYYMM(event_date) + 7500 +
+ + + + system + query_thread_log
+ toYYYYMM(event_date) + 7500 +
+ + + + system + part_log
+ toYYYYMM(event_date) + 7500 +
+ + + + + + + + + + + + + + *_dictionary.xml + + + + + + + + + + /clickhouse/task_queue/ddl + + + + + + + + + + + + + + + + click_cost + any + + 0 + 3600 + + + 86400 + 60 + + + + max + + 0 + 60 + + + 3600 + 300 + + + 86400 + 3600 + + + + + + /var/lib/clickhouse/format_schemas/ + + + +
diff --git a/tests/testflows/window_functions/configs/clickhouse/users.xml b/tests/testflows/window_functions/configs/clickhouse/users.xml new file mode 100644 index 000000000000..c7d0ecae6931 --- /dev/null +++ b/tests/testflows/window_functions/configs/clickhouse/users.xml @@ -0,0 +1,133 @@ + + + + + + + + 10000000000 + + + 0 + + + random + + + + + 1 + + + + + + + + + + + + + ::/0 + + + + default + + + default + + + 1 + + + + + + + + + + + + + + + + + 3600 + + + 0 + 0 + 0 + 0 + 0 + + + + diff --git a/tests/testflows/window_functions/regression.py b/tests/testflows/window_functions/regression.py index f7fa116ead85..2a1cef7b1110 100755 --- a/tests/testflows/window_functions/regression.py +++ b/tests/testflows/window_functions/regression.py @@ -12,6 +12,7 @@ SRS019_ClickHouse_Window_Functions, RQ_SRS_019_ClickHouse_WindowFunctions, ) +from helpers.common import check_clickhouse_version xfails = { "tests/:/frame clause/range frame/between expr following and expr following without order by error": [ @@ -116,33 +117,82 @@ "tests/:/funcs/lagInFrame/default offset": [ (Fail, "https://github.com/ClickHouse/ClickHouse/issues/23902") ], + "tests/:/time decayed funcs/exponentialTimeDecayed:/check decay length with INTERVAL": [ + (Fail, "not supported and should return an error") + ], + "tests/:/non-negative derivative func/check intervals/valid/:": [ + (Fail, "bug, LOGICAL_ERROR that needs to be investigated") + ], + "tests/:/non-negative derivative func/valid metric types/:": [ + (Fail, "bug, LOGICAL_ERROR that needs to be investigated") + ], + "tests/:/non-negative derivative func/valid timestamp types/:": [ + (Fail, "bug, LOGICAL_ERROR that needs to be investigated") + ], + "tests/:/non-negative derivative func/check one row per partition": [ + (Fail, "bug, LOGICAL_ERROR that needs to be investigated") + ], + "tests/:/non-negative derivative func/check over windows/:": [ + (Fail, "bug, LOGICAL_ERROR that needs to be investigated") + ], } xflags = {} +ffails = { + "tests/:/datatypes/low cardinality": ( + Skip, + "Server crashes on 21.9 and 21.10", + ( + lambda test: check_clickhouse_version(">=21.9")(test) + and check_clickhouse_version("<21.11")(test) + ), + ), + "tests/:/aggregate funcs/avgWeighted/:": ( + Skip, + "Server crashes on 21.9", + ( + lambda test: check_clickhouse_version(">=21.9")(test) + and check_clickhouse_version("<21.10")(test) + ), + ), + "tests/:/time decayed funcs": ( + Skip, + "Not implemented before 21.11", + check_clickhouse_version("<21.11"), + ), + "tests/:/non-negative derivative func": ( + Skip, + "Not implemented before 22.6", + check_clickhouse_version("<22.6"), + ), +} + @TestModule @ArgumentParser(argparser) @XFails(xfails) @XFlags(xflags) +@FFails(ffails) @Name("window functions") @Specifications(SRS019_ClickHouse_Window_Functions) @Requirements(RQ_SRS_019_ClickHouse_WindowFunctions("1.0")) -def regression( - self, local, clickhouse_binary_path, clickhouse_version=None, stress=None -): +def regression(self, local, clickhouse_binary_path, clickhouse_version, stress=None): """Window functions regression.""" nodes = {"clickhouse": ("clickhouse1", "clickhouse2", "clickhouse3")} + self.context.clickhouse_version = clickhouse_version + if stress is not None: self.context.stress = stress - self.context.clickhouse_version = clickhouse_version with Cluster( local, clickhouse_binary_path, nodes=nodes, - docker_compose_project_dir=os.path.join(current_dir(), "window_functions_env"), + docker_compose_project_dir=os.path.join( + current_dir(), os.path.basename(current_dir()) + "_env" + ), ) as cluster: self.context.cluster = cluster diff --git a/tests/testflows/window_functions/requirements/requirements.md b/tests/testflows/window_functions/requirements/requirements.md index 1079e7f8e910..4e70f0d3c91a 100644 --- a/tests/testflows/window_functions/requirements/requirements.md +++ b/tests/testflows/window_functions/requirements/requirements.md @@ -196,6 +196,15 @@ * 3.8.2.2.1 [RQ.SRS-019.ClickHouse.WindowFunctions.AggregateFunctions.Combinators](#rqsrs-019clickhousewindowfunctionsaggregatefunctionscombinators) * 3.8.2.3 [Parametric](#parametric) * 3.8.2.3.1 [RQ.SRS-019.ClickHouse.WindowFunctions.AggregateFunctions.Parametric](#rqsrs-019clickhousewindowfunctionsaggregatefunctionsparametric) + * 3.8.2.4 [Time Decayed](#time-decayed) + * 3.8.2.4.1 [RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed](#rqsrs-019clickhousewindowfunctionsexponentialtimedecayed) + * 3.8.2.4.2 [RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed.ExponentialTimeDecayedSum](#rqsrs-019clickhousewindowfunctionsexponentialtimedecayedexponentialtimedecayedsum) + * 3.8.2.4.3 [RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed.ExponentialTimeDecayedMax](#rqsrs-019clickhousewindowfunctionsexponentialtimedecayedexponentialtimedecayedmax) + * 3.8.2.4.4 [RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed.ExponentialTimeDecayedCount](#rqsrs-019clickhousewindowfunctionsexponentialtimedecayedexponentialtimedecayedcount) + * 3.8.2.4.5 [RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed.ExponentialTimeDecayedAvg](#rqsrs-019clickhousewindowfunctionsexponentialtimedecayedexponentialtimedecayedavg) + * 3.8.3 [Specific](#specific) + * 3.8.3.1 [Non-negative Derivative](#non-negative-derivative) + * 3.8.3.1.1 [RQ.SRS-019.ClickHouse.WindowFunctions.Specific.NonNegativeDerivative](#rqsrs-019clickhousewindowfunctionsspecificnonnegativederivative) * 4 [References](#references) @@ -2331,6 +2340,124 @@ version: 1.0 * [uniqUpTo(N)(x)](https://clickhouse.com/docs/en/sql-reference/aggregate-functions/parametric-functions/#uniquptonx) * [sumMapFiltered(keys_to_keep)(keys, values)](https://clickhouse.com/docs/en/sql-reference/aggregate-functions/parametric-functions/#summapfilteredkeys-to-keepkeys-values) +##### Time Decayed + +###### RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed +version: 1.0 + +[ClickHouse] SHALL support using exponential time decayed aggregate functions over windows. + +* exponentialTimeDecayedSum +* exponentialTimeDecayedMax +* exponentialTimeDecayedCount +* exponentialTimeDecayedAvg + +###### RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed.ExponentialTimeDecayedSum +version: 1.0 + +[ClickHouse] SHALL support `exponentialTimeDecayedSum` window function +that SHALL take a decay length parameter, a datetime input column and a numeric input column. + +It SHALL update as + +`time_gap = current_time - previous_time` + +and + +`current_output = current_input + previous_output * exp(-time_gap / decay_length)` + +The function SHALL have the following syntax + +```sql +exponentialTimeDecayedSum()(, ) +``` + +###### RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed.ExponentialTimeDecayedMax +version: 1.0 + +[ClickHouse] SHALL support `exponentialTimeDecayedMax` window function +that SHALL take a decay length parameter, a datetime input column and a numeric input column. + +It SHALL update as + +`time_gap = current_time - previous_time` + +and + +`current_output = max(current_input, previous_output * exp(-time_gap / decay_length))` + +The function SHALL have the following syntax + +```sql +exponentialTimeDecayedSum()(, ) +``` + +###### RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed.ExponentialTimeDecayedCount +version: 1.0 + +[ClickHouse] SHALL support `exponentialTimeDecayedCount` window function +that SHALL take a decay length parameter, a datetime input column. + +It SHALL update as + +`time_gap = current_time - previous_time` + +and + +`current_output = 1 + previous_count * exp(-time_gap / decay_length))` + +The function SHALL have the following syntax + +```sql +exponentialTimeDecayedSum()() +``` + +###### RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed.ExponentialTimeDecayedAvg +version: 1.0 + +[ClickHouse] SHALL support `exponentialTimeDecayedAvg` window function +that SHALL take a decay length parameter, a datetime input column and a numeric input column. + +It SHALL update as + +`time_gap = current_time - previous_time` + +and + +`current_count = 1 + previous_count * exp(-time_gap / decay_length))` + +and + +`current_output = (current_input + previous_output * exp(-time_gap / decay_length)) / current_count` + +The function SHALL have the following syntax + +```sql +exponentialTimeDecayedSum()(, ) +``` + +#### Specific + +##### Non-negative Derivative + +###### RQ.SRS-019.ClickHouse.WindowFunctions.Specific.NonNegativeDerivative +version: 1.0 + +[ClickHouse] SHALL support `nonNegativeDerivative` window function that SHALL have +the following syntax: + +```SQL +nonNegativeDerivative(metric_column, timestamp_column[, INTERVAL X UNITS]) +``` + +which SHALL calculate non-negative derivative for given `metric_column` by `timestamp_column`. +The `INTERVAL` argument MAY be omitted, the default SHALL be `INTERVAL 1 SECOND`. + +The computed value SHALL be the following for each row: + +- `0` for $`1_{st}`$ row, +- $`{metric_i - metric_{i-1} \over timestamp_i - timestamp_{i-1}} * interval`$ for $`i_{th}`$ row. + ## References * [ClickHouse] diff --git a/tests/testflows/window_functions/requirements/requirements.py b/tests/testflows/window_functions/requirements/requirements.py index 3e9a8a467196..a686fa113d61 100644 --- a/tests/testflows/window_functions/requirements/requirements.py +++ b/tests/testflows/window_functions/requirements/requirements.py @@ -1,6 +1,6 @@ # These requirements were auto generated # from software requirements specification (SRS) -# document by TestFlows v1.6.210505.1133630. +# document by TestFlows v1.9.220620.1143643. # Do not edit by hand but re-generate instead # using 'tfs requirements generate' command. from testflows.core import Specification @@ -3464,6 +3464,184 @@ num="3.8.2.3.1", ) +RQ_SRS_019_ClickHouse_WindowFunctions_ExponentialTimeDecayed = Requirement( + name="RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL support using exponential time decayed aggregate functions over windows.\n" + "\n" + "* exponentialTimeDecayedSum\n" + "* exponentialTimeDecayedMax\n" + "* exponentialTimeDecayedCount\n" + "* exponentialTimeDecayedAvg\n" + "\n" + ), + link=None, + level=5, + num="3.8.2.4.1", +) + +RQ_SRS_019_ClickHouse_WindowFunctions_ExponentialTimeDecayed_ExponentialTimeDecayedSum = Requirement( + name="RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed.ExponentialTimeDecayedSum", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL support `exponentialTimeDecayedSum` window function\n" + "that SHALL take a decay length parameter, a datetime input column and a numeric input column.\n" + "\n" + "It SHALL update as \n" + "\n" + "`time_gap = current_time - previous_time`\n" + "\n" + "and \n" + "\n" + "`current_output = current_input + previous_output * exp(-time_gap / decay_length)`\n" + "\n" + "The function SHALL have the following syntax\n" + "\n" + "```sql\n" + "exponentialTimeDecayedSum()(, )\n" + "```\n" + "\n" + ), + link=None, + level=5, + num="3.8.2.4.2", +) + +RQ_SRS_019_ClickHouse_WindowFunctions_ExponentialTimeDecayed_ExponentialTimeDecayedMax = Requirement( + name="RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed.ExponentialTimeDecayedMax", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL support `exponentialTimeDecayedMax` window function\n" + "that SHALL take a decay length parameter, a datetime input column and a numeric input column.\n" + "\n" + "It SHALL update as \n" + "\n" + "`time_gap = current_time - previous_time`\n" + "\n" + "and\n" + "\n" + "`current_output = max(current_input, previous_output * exp(-time_gap / decay_length))`\n" + "\n" + "The function SHALL have the following syntax\n" + "\n" + "```sql\n" + "exponentialTimeDecayedSum()(, )\n" + "```\n" + "\n" + ), + link=None, + level=5, + num="3.8.2.4.3", +) + +RQ_SRS_019_ClickHouse_WindowFunctions_ExponentialTimeDecayed_ExponentialTimeDecayedCount = Requirement( + name="RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed.ExponentialTimeDecayedCount", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL support `exponentialTimeDecayedCount` window function\n" + "that SHALL take a decay length parameter, a datetime input column.\n" + "\n" + "It SHALL update as \n" + "\n" + "`time_gap = current_time - previous_time`\n" + "\n" + "and\n" + "\n" + "`current_output = 1 + previous_count * exp(-time_gap / decay_length))`\n" + "\n" + "The function SHALL have the following syntax\n" + "\n" + "```sql\n" + "exponentialTimeDecayedSum()()\n" + "```\n" + "\n" + ), + link=None, + level=5, + num="3.8.2.4.4", +) + +RQ_SRS_019_ClickHouse_WindowFunctions_ExponentialTimeDecayed_ExponentialTimeDecayedAvg = Requirement( + name="RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed.ExponentialTimeDecayedAvg", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL support `exponentialTimeDecayedAvg` window function\n" + "that SHALL take a decay length parameter, a datetime input column and a numeric input column.\n" + "\n" + "It SHALL update as \n" + "\n" + "`time_gap = current_time - previous_time`\n" + "\n" + "and \n" + "\n" + "`current_count = 1 + previous_count * exp(-time_gap / decay_length))`\n" + "\n" + "and\n" + "\n" + "`current_output = (current_input + previous_output * exp(-time_gap / decay_length)) / current_count`\n" + "\n" + "The function SHALL have the following syntax\n" + "\n" + "```sql\n" + "exponentialTimeDecayedSum()(, )\n" + "```\n" + "\n" + ), + link=None, + level=5, + num="3.8.2.4.5", +) + +RQ_SRS_019_ClickHouse_WindowFunctions_Specific_NonNegativeDerivative = Requirement( + name="RQ.SRS-019.ClickHouse.WindowFunctions.Specific.NonNegativeDerivative", + version="1.0", + priority=None, + group=None, + type=None, + uid=None, + description=( + "[ClickHouse] SHALL support `nonNegativeDerivative` window function that SHALL have\n" + "the following syntax:\n" + "\n" + "```SQL\n" + "nonNegativeDerivative(metric_column, timestamp_column[, INTERVAL X UNITS])\n" + "```\n" + "\n" + "which SHALL calculate non-negative derivative for given `metric_column` by `timestamp_column`.\n" + "The `INTERVAL` argument MAY be omitted, the default SHALL be `INTERVAL 1 SECOND`.\n" + "\n" + "The computed value SHALL be the following for each row:\n" + "\n" + "- `0` for 1st row,\n" + "- ${metric_i - metric_{i-1} \\over timestamp_i - timestamp_{i-1}} * interval$ for $i_th$ row.\n" + "\n" + ), + link=None, + level=5, + num="3.8.3.1.1", +) + SRS019_ClickHouse_Window_Functions = Specification( name="SRS019 ClickHouse Window Functions", description=None, @@ -4208,6 +4386,39 @@ level=5, num="3.8.2.3.1", ), + Heading(name="Time Decayed", level=4, num="3.8.2.4"), + Heading( + name="RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed", + level=5, + num="3.8.2.4.1", + ), + Heading( + name="RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed.ExponentialTimeDecayedSum", + level=5, + num="3.8.2.4.2", + ), + Heading( + name="RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed.ExponentialTimeDecayedMax", + level=5, + num="3.8.2.4.3", + ), + Heading( + name="RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed.ExponentialTimeDecayedCount", + level=5, + num="3.8.2.4.4", + ), + Heading( + name="RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed.ExponentialTimeDecayedAvg", + level=5, + num="3.8.2.4.5", + ), + Heading(name="Specific", level=3, num="3.8.3"), + Heading(name="Non-negative Derivative", level=4, num="3.8.3.1"), + Heading( + name="RQ.SRS-019.ClickHouse.WindowFunctions.Specific.NonNegativeDerivative", + level=5, + num="3.8.3.1.1", + ), Heading(name="References", level=1, num="4"), ), requirements=( @@ -4344,6 +4555,12 @@ RQ_SRS_019_ClickHouse_WindowFunctions_AggregateFunctions, RQ_SRS_019_ClickHouse_WindowFunctions_AggregateFunctions_Combinators, RQ_SRS_019_ClickHouse_WindowFunctions_AggregateFunctions_Parametric, + RQ_SRS_019_ClickHouse_WindowFunctions_ExponentialTimeDecayed, + RQ_SRS_019_ClickHouse_WindowFunctions_ExponentialTimeDecayed_ExponentialTimeDecayedSum, + RQ_SRS_019_ClickHouse_WindowFunctions_ExponentialTimeDecayed_ExponentialTimeDecayedMax, + RQ_SRS_019_ClickHouse_WindowFunctions_ExponentialTimeDecayed_ExponentialTimeDecayedCount, + RQ_SRS_019_ClickHouse_WindowFunctions_ExponentialTimeDecayed_ExponentialTimeDecayedAvg, + RQ_SRS_019_ClickHouse_WindowFunctions_Specific_NonNegativeDerivative, ), content=""" # SRS019 ClickHouse Window Functions @@ -4544,6 +4761,15 @@ * 3.8.2.2.1 [RQ.SRS-019.ClickHouse.WindowFunctions.AggregateFunctions.Combinators](#rqsrs-019clickhousewindowfunctionsaggregatefunctionscombinators) * 3.8.2.3 [Parametric](#parametric) * 3.8.2.3.1 [RQ.SRS-019.ClickHouse.WindowFunctions.AggregateFunctions.Parametric](#rqsrs-019clickhousewindowfunctionsaggregatefunctionsparametric) + * 3.8.2.4 [Time Decayed](#time-decayed) + * 3.8.2.4.1 [RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed](#rqsrs-019clickhousewindowfunctionsexponentialtimedecayed) + * 3.8.2.4.2 [RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed.ExponentialTimeDecayedSum](#rqsrs-019clickhousewindowfunctionsexponentialtimedecayedexponentialtimedecayedsum) + * 3.8.2.4.3 [RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed.ExponentialTimeDecayedMax](#rqsrs-019clickhousewindowfunctionsexponentialtimedecayedexponentialtimedecayedmax) + * 3.8.2.4.4 [RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed.ExponentialTimeDecayedCount](#rqsrs-019clickhousewindowfunctionsexponentialtimedecayedexponentialtimedecayedcount) + * 3.8.2.4.5 [RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed.ExponentialTimeDecayedAvg](#rqsrs-019clickhousewindowfunctionsexponentialtimedecayedexponentialtimedecayedavg) + * 3.8.3 [Specific](#specific) + * 3.8.3.1 [Non-negative Derivative](#non-negative-derivative) + * 3.8.3.1.1 [RQ.SRS-019.ClickHouse.WindowFunctions.Specific.NonNegativeDerivative](#rqsrs-019clickhousewindowfunctionsspecificnonnegativederivative) * 4 [References](#references) @@ -6679,6 +6905,124 @@ * [uniqUpTo(N)(x)](https://clickhouse.com/docs/en/sql-reference/aggregate-functions/parametric-functions/#uniquptonx) * [sumMapFiltered(keys_to_keep)(keys, values)](https://clickhouse.com/docs/en/sql-reference/aggregate-functions/parametric-functions/#summapfilteredkeys-to-keepkeys-values) +##### Time Decayed + +###### RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed +version: 1.0 + +[ClickHouse] SHALL support using exponential time decayed aggregate functions over windows. + +* exponentialTimeDecayedSum +* exponentialTimeDecayedMax +* exponentialTimeDecayedCount +* exponentialTimeDecayedAvg + +###### RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed.ExponentialTimeDecayedSum +version: 1.0 + +[ClickHouse] SHALL support `exponentialTimeDecayedSum` window function +that SHALL take a decay length parameter, a datetime input column and a numeric input column. + +It SHALL update as + +`time_gap = current_time - previous_time` + +and + +`current_output = current_input + previous_output * exp(-time_gap / decay_length)` + +The function SHALL have the following syntax + +```sql +exponentialTimeDecayedSum()(, ) +``` + +###### RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed.ExponentialTimeDecayedMax +version: 1.0 + +[ClickHouse] SHALL support `exponentialTimeDecayedMax` window function +that SHALL take a decay length parameter, a datetime input column and a numeric input column. + +It SHALL update as + +`time_gap = current_time - previous_time` + +and + +`current_output = max(current_input, previous_output * exp(-time_gap / decay_length))` + +The function SHALL have the following syntax + +```sql +exponentialTimeDecayedSum()(, ) +``` + +###### RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed.ExponentialTimeDecayedCount +version: 1.0 + +[ClickHouse] SHALL support `exponentialTimeDecayedCount` window function +that SHALL take a decay length parameter, a datetime input column. + +It SHALL update as + +`time_gap = current_time - previous_time` + +and + +`current_output = 1 + previous_count * exp(-time_gap / decay_length))` + +The function SHALL have the following syntax + +```sql +exponentialTimeDecayedSum()() +``` + +###### RQ.SRS-019.ClickHouse.WindowFunctions.ExponentialTimeDecayed.ExponentialTimeDecayedAvg +version: 1.0 + +[ClickHouse] SHALL support `exponentialTimeDecayedAvg` window function +that SHALL take a decay length parameter, a datetime input column and a numeric input column. + +It SHALL update as + +`time_gap = current_time - previous_time` + +and + +`current_count = 1 + previous_count * exp(-time_gap / decay_length))` + +and + +`current_output = (current_input + previous_output * exp(-time_gap / decay_length)) / current_count` + +The function SHALL have the following syntax + +```sql +exponentialTimeDecayedSum()(, ) +``` + +#### Specific + +##### Non-negative Derivative + +###### RQ.SRS-019.ClickHouse.WindowFunctions.Specific.NonNegativeDerivative +version: 1.0 + +[ClickHouse] SHALL support `nonNegativeDerivative` window function that SHALL have +the following syntax: + +```SQL +nonNegativeDerivative(metric_column, timestamp_column[, INTERVAL X UNITS]) +``` + +which SHALL calculate non-negative derivative for given `metric_column` by `timestamp_column`. +The `INTERVAL` argument MAY be omitted, the default SHALL be `INTERVAL 1 SECOND`. + +The computed value SHALL be the following for each row: + +- `0` for 1st row, +- ${metric_i - metric_{i-1} \over timestamp_i - timestamp_{i-1}} * interval$ for $i_th$ row. + ## References * [ClickHouse] diff --git a/tests/testflows/window_functions/tests/aggregate_funcs.py b/tests/testflows/window_functions/tests/aggregate_funcs.py index faac8a84c495..13dc90369ca6 100644 --- a/tests/testflows/window_functions/tests/aggregate_funcs.py +++ b/tests/testflows/window_functions/tests/aggregate_funcs.py @@ -1,3 +1,4 @@ +from tracemalloc import Snapshot from testflows.core import * from testflows.asserts import values, error, snapshot @@ -79,11 +80,25 @@ ) def aggregate_funcs_over_rows_frame(self, func): """Checking aggregate funcs over rows frame.""" + snapshot_name = ( + "/window functions" + + current() + .name.replace(f"{sep}non distributed{sep}", ":") + .replace(f"{sep}distributed{sep}", ":") + .split("/window functions", 1)[-1] + ) + + if ( + func.startswith("studentTTest") or func.startswith("welchTTest") + ) and check_clickhouse_version(">=22")(self): + snapshot_name += "/version>=22" + execute_query( f""" SELECT {func} OVER (ORDER BY salary, empno ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS func FROM empsalary - """ + """, + snapshot_name=snapshot_name, ) @@ -353,6 +368,81 @@ def ungrouped_aggregate_over_empty_row_set(self): ) +@TestScenario +def avgWeighted(self): + """Check special case of using `avgWeighted` function with Decimal and mixed types + when used over a window function. + """ + with Example("decimal weight"): + expected = convert_output( + """ + avg + ------- + nan + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + """ + ) + + execute_query( + "SELECT avgWeighted(a, toDecimal64(c, 9)) OVER (PARTITION BY c) AS avg FROM (SELECT number AS a, number AS c FROM numbers(10))", + expected=expected, + ) + + with Example("decimal value and weight"): + expected = convert_output( + """ + avg + ------- + nan + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + """ + ) + + execute_query( + "SELECT avgWeighted(toDecimal64(a, 4), toDecimal64(c, 9)) OVER (PARTITION BY c) AS avg FROM (SELECT number AS a, number AS c FROM numbers(10))", + expected=expected, + ) + + with Example("float value and decimal weight from table"): + expected = convert_output( + """ + avg + ------- + 5000 + 3900 + 4800 + 4800 + 3500 + 4200 + 6000 + 4500 + 5200 + 5200 + """ + ) + + execute_query( + "SELECT avgWeighted(toFloat64(salary) + 0.02, toDecimal64(empno, 9)) OVER (PARTITION BY empno) AS avg FROM (SELECT * FROM empsalary ORDER BY empno)", + expected=expected, + ) + + @TestFeature @Name("aggregate funcs") @Requirements(RQ_SRS_019_ClickHouse_WindowFunctions_AggregateFunctions("1.0")) diff --git a/tests/testflows/window_functions/tests/common.py b/tests/testflows/window_functions/tests/common.py index b0bca328e4d3..ab2a6143a095 100644 --- a/tests/testflows/window_functions/tests/common.py +++ b/tests/testflows/window_functions/tests/common.py @@ -5,9 +5,57 @@ from testflows.core import * from testflows.core.name import basename, parentname +from testflows._core.name import sep from testflows._core.testtype import TestSubType from testflows.asserts import values, error, snapshot +from helpers.common import check_clickhouse_version, getuid + +interval_periods = [ + "SECOND", + "MINUTE", + "HOUR", + "DAY", + "WEEK", + "MONTH", + "QUARTER", + "YEAR", +] + + +def windows(order_by): + return [ + # frame with all rows + "" + # rows + "ROWS BETWEEN CURRENT ROW AND CURRENT ROW", + "ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING", + "ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING", + "ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW", + "ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING", + "ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING", + "ROWS BETWEEN 1 PRECEDING AND CURRENT ROW", + "ROWS BETWEEN 1 PRECEDING AND UNBOUNDED FOLLOWING", + "ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING", + "ROWS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING", + # range + "RANGE BETWEEN CURRENT ROW AND CURRENT ROW", + "RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING", + "RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW", + "RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING", + # range with order by + f"ORDER BY {order_by} RANGE BETWEEN CURRENT ROW AND CURRENT ROW", + f"ORDER BY {order_by} RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING", + f"ORDER BY {order_by} RANGE BETWEEN CURRENT ROW AND 1 FOLLOWING", + f"ORDER BY {order_by} RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW", + f"ORDER BY {order_by} RANGE BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING", + f"ORDER BY {order_by} RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING", + f"ORDER BY {order_by} RANGE BETWEEN 1 PRECEDING AND CURRENT ROW", + f"ORDER BY {order_by} RANGE BETWEEN 1 PRECEDING AND UNBOUNDED FOLLOWING", + f"ORDER BY {order_by} RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING", + f"ORDER BY {order_by} RANGE BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING", + ] + def window_frame_error(): return (36, "Exception: Window frame") @@ -44,8 +92,12 @@ def syntax_error(): return (62, "Exception: Syntax error") -def groups_frame_error(): - return (48, "Exception: Window frame 'Groups' is not implemented") +def groups_frame_error(test): + return ( + (48, "Exception: Window frame 'GROUPS' is not implemented") + if check_clickhouse_version("<21.11")(test) + else (48, "Exception: Window frame 'Groups' is not implemented") + ) def getuid(): @@ -70,15 +122,33 @@ def convert_output(s): def execute_query( - sql, expected=None, exitcode=None, message=None, format="TabSeparatedWithNames" + sql, + expected=None, + exitcode=None, + message=None, + no_checks=False, + snapshot_name=None, + format="TabSeparatedWithNames", ): """Execute SQL query and compare the output to the snapshot.""" - name = basename(current().name) + if snapshot_name is None: + snapshot_name = ( + "/window functions" + + current() + .name.replace(f"{sep}non distributed{sep}", ":") + .replace(f"{sep}distributed{sep}", ":") + .split("/window functions", 1)[-1] + ) with When("I execute query", description=sql): r = current().context.node.query( - sql + " FORMAT " + format, exitcode=exitcode, message=message + sql + " FORMAT " + format, + exitcode=exitcode, + message=message, + no_checks=no_checks, ) + if no_checks: + return r if message is None: if expected is not None: @@ -91,7 +161,7 @@ def execute_query( snapshot( "\n" + r.output.strip() + "\n", "tests", - name=name, + name=snapshot_name, encoder=str, ) ), error() @@ -210,6 +280,76 @@ def datetimes_table(self, name="datetimes", distributed=False): return table +@TestStep(Given) +def datetimes_table_from_data_query( + self, data_query, name="datetimes2", distributed=False +): + """Create datetimes table that is populated + automatically based on the passed SELECT data generating query. + + For example, + + ```data_query=("SELECT number AS id, " + "toDate('2020-01-01') + number AS f_date, " + "toDate32('2020-01-01') + number AS f_date32, " + "toDateTime('2020-01-01', 'CET') + number AS f_timestamptz, " + "toDateTime('2020-01-01') + number AS f_timestamp, " + "toDateTime64('2020-01-01', 9, 'CET') + number AS f_timestamp64tz, " + "toDateTime64('2020-01-01', 9) + number AS f_timestamp64 " + "FROM numbers(10)") + ``` + """ + table = None + + if not distributed: + with By("creating table"): + sql = """ + CREATE TABLE {name} ( + id UInt32, + f_date Date, + --f_date32 Date32, + f_timestamptz DateTime('CET'), + f_timestamp DateTime, + f_timestamp64tz DateTime64(9, 'CET'), + f_timestamp64 DateTime64 + ) ENGINE = MergeTree() ORDER BY tuple() + """ + table = create_table(name=name, statement=sql) + + with And("populating table with data"): + sql = f"INSERT INTO {name} {data_query}" + self.context.node.query(sql) + + else: + with By("creating table"): + sql = """ + CREATE TABLE {name} ON CLUSTER sharded_cluster ( + id UInt32, + f_timestamptz DateTime('CET'), + f_timestamp DateTime + ) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{{shard}}/{name}', '{{replica}}') ORDER BY tuple() + """ + create_table( + name=name + "_source", statement=sql, on_cluster="sharded_cluster" + ) + + with And("a distributed table"): + sql = ( + "CREATE TABLE {name} AS " + + name + + "_source" + + " ENGINE = Distributed(sharded_cluster, default, " + + f"{name + '_source'}, id % toUInt8(getMacro('shard')))" + ) + table = create_table(name=name, statement=sql) + + with And("populating table with data"): + sql = f"INSERT INTO {name} {data_query}" + self.context.node.query(sql) + + return table + + @TestStep(Given) def numerics_table(self, name="numerics", distributed=False): """Create numerics tables.""" @@ -465,3 +605,53 @@ def create_table(self, name, statement, on_cluster=False): node.query(f"DROP TABLE IF EXISTS {name} ON CLUSTER {on_cluster}") else: node.query(f"DROP TABLE IF EXISTS {name}") + + +@TestStep(Given) +def allow_experimental_window_functions(self): + """Set allow_experimental_window_functions = 1""" + setting = ("allow_experimental_window_functions", 1) + default_query_settings = None + + try: + with By( + "adding allow_experimental_window_functions to the default query settings" + ): + default_query_settings = getsattr( + current().context, "default_query_settings", [] + ) + default_query_settings.append(setting) + yield + finally: + with Finally( + "I remove allow_experimental_window_functions from the default query settings" + ): + if default_query_settings: + try: + default_query_settings.pop(default_query_settings.index(setting)) + except ValueError: + pass + + +@TestStep(Given) +def allow_distributed_product_mode(self): + """Set distributed_product_mode = 'allow'""" + setting = ("distributed_product_mode", "allow") + default_query_settings = None + + try: + with By("adding distributed_product_mode to the default query settings"): + default_query_settings = getsattr( + current().context, "default_query_settings", [] + ) + default_query_settings.append(setting) + yield + finally: + with Finally( + "I remove distributed_product_mode from the default query settings" + ): + if default_query_settings: + try: + default_query_settings.pop(default_query_settings.index(setting)) + except ValueError: + pass diff --git a/tests/testflows/window_functions/tests/datatypes.py b/tests/testflows/window_functions/tests/datatypes.py new file mode 100644 index 000000000000..e6afcc9dede9 --- /dev/null +++ b/tests/testflows/window_functions/tests/datatypes.py @@ -0,0 +1,339 @@ +from testflows.core import * +from testflows.asserts import values, error, snapshot + +from window_functions.requirements import * +from window_functions.tests.common import * + + +@TestScenario +def low_cardinality(self): + """Check using LowCardinality data type.""" + expected = convert_output( + """ + ma + ----- + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + """ + ) + + # note((lambda test: check_clickhouse_version("=21.10.5.3")(test))(self)) + execute_query( + "SELECT max(a) OVER () AS ma FROM (SELECT toLowCardinality(toString(number % 2 + 1)) AS a FROM numbers(10))", + expected=expected, + ) + + +@TestScenario +def datetime64(self): + """Check using DateTime64 data type.""" + expected = convert_output( + """ + ma + --------------------------- + 1972-11-07 19:00:00.00000 + 1972-11-07 19:00:00.00000 + 1972-11-07 19:00:00.00000 + 1972-11-07 19:00:00.00000 + 1972-11-07 19:00:00.00000 + 1972-11-07 19:00:00.00000 + 1972-11-07 19:00:00.00000 + 1972-11-07 19:00:00.00000 + 1972-11-07 19:00:00.00000 + 1972-11-07 19:00:00.00000 + """ + ) + + execute_query( + "SELECT max(a) OVER () AS ma FROM ( SELECT toDateTime64(number*10000000,5) AS a FROM numbers(10))", + expected=expected, + ) + + +@TestScenario +def datetime(self): + """Check using DateTime data type.""" + expected = convert_output( + """ + ma + --------------------- + 1972-11-07 19:00:00 + 1972-11-07 19:00:00 + 1972-11-07 19:00:00 + 1972-11-07 19:00:00 + 1972-11-07 19:00:00 + 1972-11-07 19:00:00 + 1972-11-07 19:00:00 + 1972-11-07 19:00:00 + 1972-11-07 19:00:00 + 1972-11-07 19:00:00 + """ + ) + + execute_query( + "SELECT max(a) OVER () AS ma FROM ( SELECT toDateTime(number*10000000) AS a FROM numbers(10))", + expected=expected, + ) + + +@TestScenario +def date(self): + """Check using Date data type.""" + expected = convert_output( + """ + ma + ------------ + 1972-11-07 + 1972-11-07 + 1972-11-07 + 1972-11-07 + 1972-11-07 + 1972-11-07 + 1972-11-07 + 1972-11-07 + 1972-11-07 + 1972-11-07 + """ + ) + + execute_query( + "SELECT max(a) OVER () AS ma FROM ( SELECT toDate(number*10000000) AS a FROM numbers(10))", + expected=expected, + ) + + +@TestScenario +def enum(self): + """Check using Enum data type.""" + expected = convert_output( + """ + ma + ---- + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + """ + ) + + execute_query( + "SELECT max(a) OVER () AS ma FROM ( SELECT CAST(a, 'Enum(\\'1\\' = 1, \\'2\\' = 2)') AS a FROM (SELECT toString(number % 2 + 1) AS a FROM numbers(10)))", + expected=expected, + ) + + +@TestScenario +def decimal32(self): + """Check using Decimal32 data type.""" + + if check_clickhouse_version("<21.9")(self): + expected = convert_output( + """ + ma + ------ + 1.80 + 1.80 + 1.80 + 1.80 + 1.80 + 1.80 + 1.80 + 1.80 + 1.80 + 1.80 + """ + ) + else: + expected = convert_output( + """ + ma + ------ + 1.8 + 1.8 + 1.8 + 1.8 + 1.8 + 1.8 + 1.8 + 1.8 + 1.8 + 1.8 + """ + ) + + execute_query( + "SELECT max(a) OVER () AS ma FROM ( SELECT toDecimal32(number*0.2,2) AS a FROM numbers(10))", + expected=expected, + ) + + +@TestScenario +def string(self): + """Check using String data type.""" + expected = convert_output( + """ + ma + ------ + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + """ + ) + + execute_query( + "SELECT max(a) OVER () AS ma FROM ( SELECT toString(number % 2 + 1) AS a FROM numbers(10))", + expected=expected, + ) + + +@TestScenario +def fixed_string(self): + """Check using FixedString data type.""" + expected = convert_output( + """ + ma + ------ + 2\\0\\0 + 2\\0\\0 + 2\\0\\0 + 2\\0\\0 + 2\\0\\0 + 2\\0\\0 + 2\\0\\0 + 2\\0\\0 + 2\\0\\0 + 2\\0\\0 + """ + ) + + execute_query( + "SELECT max(a) OVER () AS ma FROM ( SELECT toFixedString(toString(number % 2 + 1), 3) AS a FROM numbers(10))", + expected=expected, + ) + + +@TestScenario +def uuid(self): + """Check using UUID data type.""" + expected = convert_output( + """ + ma + ------ + 61f0c404-5cb3-11e7-907b-a6006ad3dba0 + 61f0c404-5cb3-11e7-907b-a6006ad3dba0 + 61f0c404-5cb3-11e7-907b-a6006ad3dba0 + 61f0c404-5cb3-11e7-907b-a6006ad3dba0 + 61f0c404-5cb3-11e7-907b-a6006ad3dba0 + 61f0c404-5cb3-11e7-907b-a6006ad3dba0 + 61f0c404-5cb3-11e7-907b-a6006ad3dba0 + 61f0c404-5cb3-11e7-907b-a6006ad3dba0 + 61f0c404-5cb3-11e7-907b-a6006ad3dba0 + 61f0c404-5cb3-11e7-907b-a6006ad3dba0 + """ + ) + + execute_query( + "SELECT max(a) OVER () AS ma FROM ( SELECT toUUID('61f0c404-5cb3-11e7-907b-a6006ad3dba0') AS a FROM numbers(10))", + expected=expected, + ) + + +@TestScenario +def ipv4(self): + """Check using IPv4 data type.""" + expected = convert_output( + """ + ma + ------ + 171.225.130.45 + 171.225.130.45 + 171.225.130.45 + 171.225.130.45 + 171.225.130.45 + 171.225.130.45 + 171.225.130.45 + 171.225.130.45 + 171.225.130.45 + 171.225.130.45 + """ + ) + + execute_query( + "SELECT max(a) OVER () AS ma FROM ( SELECT toIPv4('171.225.130.45') AS a FROM numbers(10))", + expected=expected, + ) + + +@TestScenario +def ipv6(self): + """Check using IPv6 data type.""" + expected = convert_output( + """ + ma + ------ + 2001:438:ffff::407d:1bc1 + 2001:438:ffff::407d:1bc1 + 2001:438:ffff::407d:1bc1 + 2001:438:ffff::407d:1bc1 + 2001:438:ffff::407d:1bc1 + 2001:438:ffff::407d:1bc1 + 2001:438:ffff::407d:1bc1 + 2001:438:ffff::407d:1bc1 + 2001:438:ffff::407d:1bc1 + 2001:438:ffff::407d:1bc1 + """ + ) + + execute_query( + "SELECT max(a) OVER () AS ma FROM ( SELECT toIPv6('2001:438:ffff::407d:1bc1') AS a FROM numbers(10))", + expected=expected, + ) + + +@TestScenario +def map(self): + """Check using Map data type.""" + expected = convert_output( + """ + ma + ------ + {'hello2':2} + {'hello2':2} + """ + ) + + execute_query( + "SELECT max(a) OVER () AS ma FROM (SELECT m AS a FROM values('m Map(String, UInt64)', map('hello',1), map('hello2',2)))", + expected=expected, + ) + + +@TestFeature +@Name("datatypes") +@Requirements() +def feature(self): + """Check using aggregate function over windows with different data types.""" + + for scenario in loads(current_module(), Scenario): + Scenario(run=scenario, flags=TE) diff --git a/tests/testflows/window_functions/tests/errors.py b/tests/testflows/window_functions/tests/errors.py index ee9452eecbaa..0837d2a50504 100644 --- a/tests/testflows/window_functions/tests/errors.py +++ b/tests/testflows/window_functions/tests/errors.py @@ -1,4 +1,5 @@ from testflows.core import * +from helpers.common import check_clickhouse_version from window_functions.requirements import * from window_functions.tests.common import * @@ -45,12 +46,21 @@ def error_window_function_in_where(self): @TestScenario def error_window_function_in_join(self): """Check that trying to use window function in `JOIN` returns an error.""" - exitcode = 147 - message = "DB::Exception: Cannot get JOIN keys from JOIN ON section: row_number() OVER (ORDER BY salary ASC) < 10" + note(self.context.clickhouse_version) + + if self.context.distributed: + allow_distributed_product_mode() + + exitcode = 48 if check_clickhouse_version("<21.9")(self) else 147 + message = ( + "DB::Exception: JOIN ON inequalities are not supported" + if check_clickhouse_version("<21.9")(self) + else "DB::Exception: Cannot get JOIN keys from JOIN ON section" + ) sql = "SELECT * FROM empsalary INNER JOIN tenk1 ON row_number() OVER (ORDER BY salary) < 10" - with When("I execute query", description=sql): + with When(f"I execute query", description=sql): r = current().context.node.query(sql, exitcode=exitcode, message=message) @@ -58,7 +68,7 @@ def error_window_function_in_join(self): def error_window_function_in_group_by(self): """Check that trying to use window function in `GROUP BY` returns an error.""" exitcode = 47 - message = "DB::Exception: Unknown identifier: row_number() OVER (ORDER BY salary ASC); there are columns" + message = "DB::Exception: Unknown identifier" sql = "SELECT rank() OVER (ORDER BY 1), count(*) FROM empsalary GROUP BY row_number() OVER (ORDER BY salary) < 10" diff --git a/tests/testflows/window_functions/tests/feature.py b/tests/testflows/window_functions/tests/feature.py index c1454a419e9e..a9b7c8448c6d 100755 --- a/tests/testflows/window_functions/tests/feature.py +++ b/tests/testflows/window_functions/tests/feature.py @@ -32,6 +32,10 @@ def feature(self, distributed, node="clickhouse1"): self.context.distributed = distributed self.context.node = self.context.cluster.node(node) + if check_clickhouse_version("<21.9")(self): + with Given("I allow experimental window functions"): + allow_experimental_window_functions() + with Given("employee salary table"): empsalary_table(distributed=distributed) @@ -44,6 +48,21 @@ def feature(self, distributed, node="clickhouse1"): with And("datetimes table"): datetimes_table(distributed=distributed) + with And("datetimes2 table populated using data query"): + datetimes_table_from_data_query( + data_query=( + "SELECT number AS id, " + "toDate('2020-01-01') + number AS f_date, " + # "toDate32('2020-01-01') + number AS f_date32, " + "toDateTime('2020-01-01', 'CET') + number AS f_timestamptz, " + "toDateTime('2020-01-01') + number AS f_timestamp, " + "toDateTime64('2020-01-01', 9, 'CET') + number AS f_timestamp64tz, " + "toDateTime64('2020-01-01', 9) + number AS f_timestamp64 " + "FROM numbers(10000)" + ), + name="datetimes2", + ) + with And("t1 table"): t1_table(distributed=distributed) @@ -55,5 +74,10 @@ def feature(self, distributed, node="clickhouse1"): Feature(run=load("window_functions.tests.over_clause", "feature"), flags=TE) Feature(run=load("window_functions.tests.funcs", "feature"), flags=TE) Feature(run=load("window_functions.tests.aggregate_funcs", "feature"), flags=TE) + Feature(run=load("window_functions.tests.time_decayed_funcs", "feature"), flags=TE) Feature(run=load("window_functions.tests.errors", "feature"), flags=TE) Feature(run=load("window_functions.tests.misc", "feature"), flags=TE) + Feature(run=load("window_functions.tests.datatypes", "feature"), flags=TE) + Feature( + run=load("window_functions.tests.non_negative_derivative", "feature"), flags=TE + ) diff --git a/tests/testflows/window_functions/tests/misc.py b/tests/testflows/window_functions/tests/misc.py index 5cb579c39542..73a9ce66a54d 100644 --- a/tests/testflows/window_functions/tests/misc.py +++ b/tests/testflows/window_functions/tests/misc.py @@ -222,7 +222,7 @@ def from_subquery(self): @TestScenario def groups_frame(self): """Check using `GROUPS` frame.""" - exitcode, message = groups_frame_error() + exitcode, message = groups_frame_error(self) expected = convert_output( """ diff --git a/tests/testflows/window_functions/tests/non_negative_derivative.py b/tests/testflows/window_functions/tests/non_negative_derivative.py new file mode 100644 index 000000000000..3db5d570dcea --- /dev/null +++ b/tests/testflows/window_functions/tests/non_negative_derivative.py @@ -0,0 +1,357 @@ +import textwrap + +from testflows.core import * + +from helpers.common import check_clickhouse_version +from window_functions.requirements import * +from window_functions.tests.common import * + + +@TestScenario +def null_values(self): + """Check NULL values in either metric or timestamp column.""" + with Example(f"metric column"): + execute_query( + f"SELECT id, metric, ts, nonNegativeDerivative(metric, ts) OVER () AS nnd FROM values('id Int8, metric Nullable(Float64), ts DateTime', (1,1,'2022-12-12 00:00:00'),(1,null,'2022-12-12 00:00:01'))", + exitcode=36, + message="Exception: Argument 0 must be a number", + ) + + with Example("timestamp column"): + execute_query( + f"SELECT id, metric, ts, nonNegativeDerivative(metric, ts) OVER () AS nnd FROM values('id Int8, metric Float64, ts Nullable(DateTime)', (1,1,'2022-12-12 00:00:00'),(1,1,null))", + exitcode=36, + message="Exception: Argument 1 must be DateTime or DateTime64", + ) + + +@TestScenario +def division_by_zero(self): + """Check division by zero when two timestamps are the same.""" + execute_query( + f"SELECT id, metric, ts, nonNegativeDerivative(metric, ts) OVER (ORDER BY id) AS nnd FROM values('id Int8, metric Float64, ts DateTime', (1,1,'2022-12-12 00:00:00'),(2,1,'2022-12-12 00:00:00'))", + ) + + +@TestScenario +def division_by_tiny(self): + """Check division by tiny difference between consecutive timestamps.""" + with Example("numerator is non-zero"): + execute_query( + f"SELECT id, metric, ts, nonNegativeDerivative(metric, ts) OVER (ORDER BY id) AS nnd FROM values('id Int8, metric Float64, ts DateTime64(9)', (1,1,'2022-12-12 00:00:00'), (2,2,'2022-12-12 00:00:00.000000001'),(3,3,'2022-12-12 00:00:00.000000002'))", + ) + + with Example("numerator is zero"): + execute_query( + f"SELECT id, metric, ts, nonNegativeDerivative(metric, ts) OVER (ORDER BY id) AS nnd FROM values('id Int8, metric Float64, ts DateTime64(9)', (1,1,'2022-12-12 00:00:00'), (2,1,'2022-12-12 00:00:00.000000001'),(3,1,'2022-12-12 00:00:00.000000002'))", + ) + + +@TestScenario +def check_negative_result(self): + """Check that negative result is converted to 0.""" + expected = convert_output( + """ + id | metric | ts | nnd + ---+--------+-------------------------------+----------------------- + 1 | 1 | 2022-12-12 00:00:02.000000000 | 0 + 2 | 2 | 2022-12-12 00:00:01.000000000 | 0 + 3 | 3 | 2022-12-12 00:00:00.000000000 | 0 + """ + ) + + execute_query( + f"SELECT id, metric, ts, nonNegativeDerivative(metric, ts) OVER (ORDER BY id ASC) AS nnd FROM values('id Int8, metric Float64, ts DateTime64(9)', (1,1,'2022-12-12 00:00:02'), (2,2,'2022-12-12 00:00:01'),(3,3,'2022-12-12 00:00:00'))", + expected=expected, + ) + + +@TestScenario +def wrong_number_of_arguments_or_types(self): + """Check passing wrong number of arguments or types.""" + exitcode = 36 + message = "Exception: Function nonNegativeDerivative takes 2 or 3 arguments" + table = "values('id Int8, metric Float64, ts DateTime64(9)', (1,1,'2022-12-12 00:00:00'), (2,2,'2022-12-12 00:00:01'),(3,3,'2022-12-12 00:00:02'))" + + with Example("missing timestamp"): + execute_query( + f"SELECT id, metric, ts, nonNegativeDerivative(metric) OVER (ORDER BY id ASC) AS nnd FROM {table}", + exitcode=exitcode, + message=message, + ) + + with Example("missing metric"): + execute_query( + f"SELECT id, metric, ts, nonNegativeDerivative(ts) OVER (ORDER BY id ASC) AS nnd FROM {table}", + exitcode=exitcode, + message=message, + ) + + with Example("missing metric with interval"): + execute_query( + f"SELECT id, metric, ts, nonNegativeDerivative(INTERVAL 3 SECOND) OVER (ORDER BY id ASC) AS nnd FROM {table}", + exitcode=exitcode, + message=message, + ) + + with Example("missing timestamp with interval"): + execute_query( + f"SELECT id, metric, ts, nonNegativeDerivative(metric, INTERVAL 3 SECOND) OVER (ORDER BY id ASC) AS nnd FROM {table}", + exitcode=exitcode, + message="Exception: Argument 1 must be DateTime or DateTime64", + ) + + with Example("empty"): + execute_query( + f"SELECT id, metric, ts, nonNegativeDerivative() OVER (ORDER BY id ASC) AS nnd FROM {table}", + exitcode=exitcode, + message=message, + ) + + with Example("invalid interval type"): + execute_query( + f"SELECT id, metric, ts, nonNegativeDerivative(metric, ts, 2) OVER (ORDER BY id ASC) AS nnd FROM {table}", + exitcode=exitcode, + message="Exception: Argument 2 must be an INTERVAL", + ) + + with Example("invalid metric type"): + execute_query( + f"SELECT id, metric, ts, nonNegativeDerivative(ts, ts) OVER (ORDER BY id ASC) AS nnd FROM {table}", + exitcode=exitcode, + message="Exception: Argument 0 must be a number", + ) + + with Example("invalid timestamp type"): + execute_query( + f"SELECT id, metric, ts, nonNegativeDerivative(metric, metric) OVER (ORDER BY id ASC) AS nnd FROM {table}", + exitcode=exitcode, + message="Exception: Argument 1 must be DateTime or DateTime64", + ) + + +@TestScenario +def zero_interval(self): + """Check using zero second interval.""" + expected = convert_output( + """ + id | metric | ts | nnd + ---+--------+---------------------+----- + 1 | 1 | 2022-12-12 00:00:00 | 0 + 2 | 2 | 2022-12-12 00:00:01 | 0 + 3 | 3 | 2022-12-12 00:00:02 | 0 + """ + ) + + execute_query( + "SELECT id, metric, ts, nonNegativeDerivative(metric, ts, INTERVAL 0 SECOND) OVER (ORDER BY id ASC) AS nnd FROM values('id Int8, metric Float64, ts DateTime64(0)', (1,1,'2022-12-12 00:00:00'), (2,2,'2022-12-12 00:00:01'),(3,3,'2022-12-12 00:00:02'))", + expected=expected, + ) + + +@TestScenario +def empty_table(self): + """Check against empty table.""" + node = self.context.node + table = f"table_{getuid()}" + + with Given("I create empty table"): + node.query( + f"CREATE TABLE {table} (id Int8, metric Float64, ts DateTime64(9)) ENGINE=MergeTree() ORDER BY tuple()" + ) + + try: + execute_query( + f"SELECT id, metric, ts, nonNegativeDerivative(metric, ts) OVER (ORDER BY id ASC) AS nnd FROM {table}" + ) + finally: + with Finally("drop table"): + node.query(f"DROP TABLE IF EXISTS {table}") + + +@TestScenario +def check_intervals(self): + with Check("valid"): + valid = ("SECOND", "MINUTE", "HOUR", "DAY", "WEEK") + + for interval in valid: + with Example(f"{interval}", flags=TE): + execute_query( + f"SELECT id, f_timestamp, nonNegativeDerivative(id, f_timestamp, INTERVAL 1 {interval}) OVER (ROWS BETWEEN CURRENT ROW AND CURRENT ROW) AS nnd FROM (SELECT * FROM datetimes2 ORDER BY f_timestamp LIMIT 10)" + ) + + with Check("invalid"): + invalid = ( + "MONTH", + "QUARTER", + "YEAR", + ) + + for interval in invalid: + with Example(f"{interval}", flags=TE): + execute_query( + f"SELECT id, f_timestamp, nonNegativeDerivative(id, f_timestamp, INTERVAL 1 {interval}) OVER (ROWS BETWEEN CURRENT ROW AND CURRENT ROW) AS nnd FROM (SELECT * FROM datetimes2 ORDER BY f_timestamp LIMIT 10)", + exitcode=43, + message="Exception: The INTERVAL must be a week or shorter", + ) + + +@TestScenario +def check_one_row_per_partition(self): + """Check when there is only 1 row per partition.""" + execute_query( + f"SELECT id, f_timestamp, nonNegativeDerivative(id, f_timestamp) OVER (PARTITION BY id) AS nnd FROM datetimes2 ORDER BY id LIMIT 10" + ) + + +@TestScenario +def check_over_windows(self): + """Check function over different window frames.""" + for window in windows(order_by="id"): + with Check(f"{window}", flags=TE): + execute_query( + f"SELECT id, f_timestamp, nonNegativeDerivative(id, f_timestamp, INTERVAL 1 SECOND) OVER (PARTITION BY toStartOfHour(f_timestamp) {window}) AS nnd FROM datetimes2 ORDER BY id LIMIT 10" + ) + + +@TestScenario +def valid_metric_types(self): + """Check valid numerical metric types.""" + datatypes = ( + "Float32", + "Float64", + "UInt8", + "UInt16", + "UInt32", + "UInt64", + "Int8", + "Int16", + "Int32", + "Int64", + "Decimal32(4)", + "Decimal64(4)", + "Decimal128(4)", + ) + + for datatype in datatypes: + with Example(f"{datatype}"): + execute_query( + f"SELECT id, metric, ts, nonNegativeDerivative(metric, ts) OVER (ORDER BY id ASC) AS nnd FROM values('id Int8, metric {datatype}, ts DateTime64(0)', (1,1,'2022-12-12 00:00:00'), (2,2,'2022-12-12 00:00:01'),(3,3,'2022-12-12 00:00:02'))" + ) + + +@TestScenario +def valid_timestamp_types(self): + """Check valid timestamp types.""" + datatypes = ("DateTime", "DateTime64(0)", "DateTime64(3, EST)") + + for datatype in datatypes: + with Example(f"{datatype}"): + execute_query( + f"SELECT id, metric, ts, nonNegativeDerivative(metric, ts) OVER (ORDER BY id ASC) AS nnd FROM values('id Int8, metric UInt8, ts {datatype}', (1,1,'2022-12-12 00:00:00'), (2,2,'2022-12-12 00:00:01'),(3,3,'2022-12-12 00:00:02'))" + ) + + +@TestScenario +def acceptance(self): + """Check function returns the expected result as defined by the original feature request.""" + data = """ + SELECT * FROM values('timestamp DateTime(3), metric_name String, metric_value UInt64', + ('2020-01-01 00:00:01.000','cpu',10), + ('2020-01-01 00:00:01.001','cpu',20), + ('2020-01-01 00:00:01.003','cpu',25), + ('2020-01-01 00:00:01.004','cpu',25), + ('2020-01-01 00:00:01.005','cpu',20), + ('2020-01-01 00:00:01.005','cpu',21), + ('2020-01-01 00:00:01.015','cpu',30), + ('2020-01-01 00:00:02.015','cpu',40), + ('2020-01-01 00:00:05.015','cpu',40), + ('2020-01-01 00:00:05.016','cpu',40), + ('2020-01-01 00:05:05.016','cpu',45) + )""".strip() + + with Check("workaround query"): + expected = convert_output( + """ + timestamp | metric_name | metric_value | rateNonNegative + -------------------------+-------------+--------------+---------------------- + 2020-01-01 00:00:01.000 | cpu | 10 | 6.337834459352404e-9 + 2020-01-01 00:00:01.001 | cpu | 20 | 10000 + 2020-01-01 00:00:01.003 | cpu | 25 | 2500 + 2020-01-01 00:00:01.004 | cpu | 25 | 0 + 2020-01-01 00:00:01.005 | cpu | 20 | 0 + 2020-01-01 00:00:01.005 | cpu | 21 | inf + 2020-01-01 00:00:01.015 | cpu | 30 | 900 + 2020-01-01 00:00:02.015 | cpu | 40 | 10 + 2020-01-01 00:00:05.015 | cpu | 40 | 0 + 2020-01-01 00:00:05.016 | cpu | 40 | 0 + 2020-01-01 00:05:05.016 | cpu | 45 | 0.016666666666666666 + """ + ) + + query = f""" + WITH + metric_value - prev_metric_value as delta_metric, + (toUnixTimestamp64Milli(timestamp) - toUnixTimestamp64Milli(prev_timestamp))/1000 as delta_ts + SELECT + timestamp, + metric_name, + metric_value, + if( delta_metric / delta_ts > 0, delta_metric / delta_ts, 0) as rateNonNegative + FROM + ( + SELECT + timestamp, + metric_name, + metric_value, + lagInFrame(metric_value) OVER (PARTITION BY metric_name ORDER BY timestamp ASC ROWS + BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS prev_metric_value, + lagInFrame(timestamp) OVER (PARTITION BY metric_name ORDER BY timestamp ASC ROWS + BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS prev_timestamp + FROM ( + {textwrap.indent(data, " "*8)} + ) t + )""" + + execute_query( + query, + expected=expected, + ) + + with Check("using nonNegativeDerivative"): + expected = convert_output( + """ + timestamp | metric_name | metric_value | rateNonNegative + -------------------------+-------------+--------------+---------------------- + 2020-01-01 00:00:01.000 | cpu | 10 | 0 + 2020-01-01 00:00:01.001 | cpu | 20 | 10000 + 2020-01-01 00:00:01.003 | cpu | 25 | 2500 + 2020-01-01 00:00:01.004 | cpu | 25 | 0 + 2020-01-01 00:00:01.005 | cpu | 20 | 0 + 2020-01-01 00:00:01.005 | cpu | 21 | 0 + 2020-01-01 00:00:01.015 | cpu | 30 | 900 + 2020-01-01 00:00:02.015 | cpu | 40 | 10 + 2020-01-01 00:00:05.015 | cpu | 40 | 0 + 2020-01-01 00:00:05.016 | cpu | 40 | 0 + 2020-01-01 00:05:05.016 | cpu | 45 | 0.016666666666666666 + """ + ) + + execute_query( + "SELECT timestamp, metric_name, metric_value, nonNegativeDerivative(" + "metric_value, timestamp, INTERVAL 1 SECOND) OVER " + "(PARTITION BY metric_name ORDER BY timestamp ASC ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS rateNonNegative " + f"FROM ({data})", + expected=expected, + ) + + +@TestFeature +@Name("non-negative derivative func") +@Requirements( + RQ_SRS_019_ClickHouse_WindowFunctions_Specific_NonNegativeDerivative("1.0") +) +def feature(self): + """Check non-negative derivative window function.""" + for scenario in loads(current_module(), Scenario): + Scenario(run=scenario, flags=TE) diff --git a/tests/testflows/window_functions/tests/snapshots/common.py.tests.snapshot b/tests/testflows/window_functions/tests/snapshots/common.py.tests.snapshot index 68607f86d924..d9ddfe20a791 100644 --- a/tests/testflows/window_functions/tests/snapshots/common.py.tests.snapshot +++ b/tests/testflows/window_functions/tests/snapshots/common.py.tests.snapshot @@ -1,4 +1,4 @@ -func__count_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__count_salary__ = r""" func 10 9 @@ -12,7 +12,7 @@ func 1 """ -func__min_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__min_salary__ = r""" func 3500 3900 @@ -26,7 +26,7 @@ func 6000 """ -func__max_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__max_salary__ = r""" func 6000 6000 @@ -40,7 +40,7 @@ func 6000 """ -func__sum_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__sum_salary__ = r""" func 47100 43600 @@ -54,7 +54,7 @@ func 6000 """ -func__avg_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__avg_salary__ = r""" func 4710 4844.444444444444 @@ -68,7 +68,7 @@ func 6000 """ -func__any_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__any_salary__ = r""" func 3500 3900 @@ -82,7 +82,7 @@ func 6000 """ -func__stddevPop_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__stddevPop_salary__ = r""" func 683.3008122342604 581.3989089756045 @@ -96,7 +96,7 @@ func 0 """ -func__stddevSamp_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__stddevSamp_salary__ = r""" func 720.2622979011034 616.6666666666654 @@ -110,7 +110,7 @@ func nan """ -func__varPop_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__varPop_salary__ = r""" func 466900 338024.6913580232 @@ -124,7 +124,7 @@ func 0 """ -func__varSamp_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__varSamp_salary__ = r""" func 518777.77777777775 380277.7777777761 @@ -138,7 +138,7 @@ func nan """ -func__covarPop_salary__2000__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__covarPop_salary__2000__ = r""" func 0 0 @@ -152,7 +152,7 @@ func 0 """ -func__covarSamp_salary__2000__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__covarSamp_salary__2000__ = r""" func 0 0 @@ -166,7 +166,7 @@ func nan """ -func__anyHeavy_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__anyHeavy_salary__ = r""" func 5200 5200 @@ -180,7 +180,7 @@ func 6000 """ -func__anyLast_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__anyLast_salary__ = r""" func 6000 6000 @@ -194,7 +194,7 @@ func 6000 """ -func__argMin_salary__5000__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__argMin_salary__5000__ = r""" func 3500 3900 @@ -208,7 +208,7 @@ func 6000 """ -func__argMax_salary__5000__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__argMax_salary__5000__ = r""" func 3500 3900 @@ -222,7 +222,7 @@ func 6000 """ -func__avgWeighted_salary__1__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__avgWeighted_salary__1__ = r""" func 4710 4844.444444444444 @@ -236,7 +236,7 @@ func 6000 """ -func__corr_salary__0_5__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__corr_salary__0_5__ = r""" func nan nan @@ -250,7 +250,7 @@ nan nan """ -func__topK_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__topK_salary__ = r""" func [4800,5200,3500,3900,4200,4500,5000,6000] [4800,5200,3900,4200,4500,5000,6000] @@ -264,7 +264,7 @@ func [6000] """ -func__topKWeighted_salary__1__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__topKWeighted_salary__1__ = r""" func [4800,5200,3500,3900,4200,4500,5000,6000] [4800,5200,3900,4200,4500,5000,6000] @@ -278,7 +278,7 @@ func [6000] """ -func__groupArray_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__groupArray_salary__ = r""" func [3500,3900,4200,4500,4800,4800,5000,5200,5200,6000] [3900,4200,4500,4800,4800,5000,5200,5200,6000] @@ -292,7 +292,7 @@ func [6000] """ -func__groupUniqArray_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__groupUniqArray_salary__ = r""" func [3500,5000,6000,3900,4800,5200,4200,4500] [5000,6000,3900,4800,5200,4200,4500] @@ -306,7 +306,7 @@ func [6000] """ -func__groupArrayInsertAt_salary__0__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__groupArrayInsertAt_salary__0__ = r""" func [3500] [3900] @@ -320,7 +320,7 @@ func [6000] """ -func__groupArrayMovingSum_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__groupArrayMovingSum_salary__ = r""" func [3500,7400,11600,16100,20900,25700,30700,35900,41100,47100] [3900,8100,12600,17400,22200,27200,32400,37600,43600] @@ -334,7 +334,7 @@ func [6000] """ -func__groupArrayMovingAvg_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__groupArrayMovingAvg_salary__ = r""" func [350,740,1160,1610,2090,2570,3070,3590,4110,4710] [433.3333333333333,900,1400,1933.3333333333333,2466.6666666666665,3022.222222222222,3600,4177.777777777777,4844.444444444444] @@ -348,7 +348,7 @@ func [6000] """ -func__groupArraySample_3__1234__salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__groupArraySample_3__1234__salary__ = r""" func [6000,4800,4200] [4800,5000,4500] @@ -362,7 +362,7 @@ func [6000] """ -func__groupBitAnd_toUInt8_salary___ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__groupBitAnd_toUInt8_salary___ = r""" func 0 0 @@ -376,7 +376,7 @@ func 112 """ -func__groupBitOr_toUInt8_salary___ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__groupBitOr_toUInt8_salary___ = r""" func 252 252 @@ -390,7 +390,7 @@ func 112 """ -func__groupBitXor_toUInt8_salary___ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__groupBitXor_toUInt8_salary___ = r""" func 148 56 @@ -404,7 +404,7 @@ func 112 """ -func__groupBitmap_toUInt8_salary___ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__groupBitmap_toUInt8_salary___ = r""" func 8 7 @@ -418,7 +418,7 @@ func 1 """ -func__sumWithOverflow_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__sumWithOverflow_salary__ = r""" func 47100 43600 @@ -432,7 +432,7 @@ func 6000 """ -func__deltaSum_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__deltaSum_salary__ = r""" func 2500 2100 @@ -446,7 +446,7 @@ func 0 """ -func__sumMap__5000____salary___ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__sumMap__5000____salary___ = r""" func ([5000],[47100]) ([5000],[43600]) @@ -460,7 +460,7 @@ func ([5000],[6000]) """ -func__minMap__5000____salary___ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__minMap__5000____salary___ = r""" func ([5000],[3500]) ([5000],[3900]) @@ -474,7 +474,7 @@ func ([5000],[6000]) """ -func__maxMap__5000____salary___ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__maxMap__5000____salary___ = r""" func ([5000],[6000]) ([5000],[6000]) @@ -488,7 +488,7 @@ func ([5000],[6000]) """ -func__skewPop_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__skewPop_salary__ = r""" func -0.01162261667454972 0.2745338273905704 @@ -502,7 +502,7 @@ func nan """ -func__skewSamp_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__skewSamp_salary__ = r""" func -0.009923564086909852 0.2300737552746305 @@ -516,7 +516,7 @@ func nan """ -func__kurtPop_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__kurtPop_salary__ = r""" func 2.539217051205756 2.7206630060048402 @@ -530,7 +530,7 @@ func nan """ -func__kurtSamp_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__kurtSamp_salary__ = r""" func 2.0567658114766627 2.1496596590655526 @@ -544,7 +544,7 @@ func nan """ -func__uniq_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__uniq_salary__ = r""" func 8 7 @@ -558,7 +558,7 @@ func 1 """ -func__uniqExact_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__uniqExact_salary__ = r""" func 8 7 @@ -572,7 +572,7 @@ func 1 """ -func__uniqCombined_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__uniqCombined_salary__ = r""" func 8 7 @@ -586,7 +586,7 @@ func 1 """ -func__uniqCombined64_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__uniqCombined64_salary__ = r""" func 8 7 @@ -600,7 +600,7 @@ func 1 """ -func__uniqHLL12_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__uniqHLL12_salary__ = r""" func 8 7 @@ -614,7 +614,7 @@ func 1 """ -func__quantile_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__quantile_salary__ = r""" func 4800 4800 @@ -628,7 +628,7 @@ func 6000 """ -func__quantiles_0_5__salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__quantiles_0_5__salary__ = r""" func [4800] [4800] @@ -642,7 +642,7 @@ func [6000] """ -func__quantileExact_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__quantileExact_salary__ = r""" func 4800 4800 @@ -656,7 +656,7 @@ func 6000 """ -func__quantileExactWeighted_salary__1__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__quantileExactWeighted_salary__1__ = r""" func 4800 4800 @@ -670,7 +670,7 @@ func 6000 """ -func__quantileTiming_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__quantileTiming_salary__ = r""" func 4800 4800 @@ -684,7 +684,7 @@ func 6000 """ -func__quantileTimingWeighted_salary__1__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__quantileTimingWeighted_salary__1__ = r""" func 4800 4800 @@ -698,7 +698,7 @@ func 6000 """ -func__quantileDeterministic_salary__1234__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__quantileDeterministic_salary__1234__ = r""" func 4800 4800 @@ -712,7 +712,7 @@ func 6000 """ -func__quantileTDigest_salary__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__quantileTDigest_salary__ = r""" func 4800 4800 @@ -726,7 +726,7 @@ func 6000 """ -func__quantileTDigestWeighted_salary__1__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__quantileTDigestWeighted_salary__1__ = r""" func 4800 4800 @@ -740,7 +740,7 @@ func 6000 """ -func__simpleLinearRegression_salary__empno__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__simpleLinearRegression_salary__empno__ = r""" func (0.0017991004497751124,-2.473763118440779) (0.0023192111029948868,-5.12417823228634) @@ -754,7 +754,7 @@ func (nan,nan) """ -func__stochasticLinearRegression_salary__1__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__stochasticLinearRegression_salary__1__ = r""" func [0,0] [0,0] @@ -768,7 +768,7 @@ func [0,0] """ -func__stochasticLogisticRegression_salary__1__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__stochasticLogisticRegression_salary__1__ = r""" func [0,0] [0,0] @@ -782,7 +782,5446 @@ func [0,0] """ -func__studentTTest_salary__1__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__studentTTest_salary__1___version__22 = r""" +func +(nan,nan) +(nan,nan) +(nan,nan) +(nan,nan) +(nan,nan) +(nan,nan) +(nan,nan) +(nan,nan) +(nan,nan) +(nan,nan) +""" + +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__welchTTest_salary__1___version__22 = r""" +func +(nan,nan) +(nan,nan) +(nan,nan) +(nan,nan) +(nan,nan) +(nan,nan) +(nan,nan) +(nan,nan) +(nan,nan) +(nan,nan) +""" + +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__median_salary__ = r""" +func +4800 +4800 +4900 +5000 +5100 +5200 +5200 +5200 +5600 +6000 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_DateTime_time_column = r""" +id time w +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 0.7310585786300049 +2 2020-01-01 00:00:02 1.5752103826044412 +3 2020-01-01 00:00:03 2.4926527345857696 +4 2020-01-01 00:00:04 3.4519415676621947 +5 2020-01-01 00:00:05 4.432932763071741 +6 2020-01-01 00:00:06 5.424412292903226 +7 2020-01-01 00:00:07 6.420707894737403 +8 2020-01-01 00:00:08 7.419134118454189 +9 2020-01-01 00:00:09 8.41847731304077 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_DateTime64_time_column = r""" +id time w +0 2020-01-01 00:00:00.000 0 +1 2020-01-01 00:00:01.000 0.7310585786300049 +2 2020-01-01 00:00:02.000 1.5752103826044412 +3 2020-01-01 00:00:03.000 2.4926527345857696 +4 2020-01-01 00:00:04.000 3.4519415676621947 +5 2020-01-01 00:00:05.000 4.432932763071741 +6 2020-01-01 00:00:06.000 5.424412292903226 +7 2020-01-01 00:00:07.000 6.420707894737403 +8 2020-01-01 00:00:08.000 7.419134118454189 +9 2020-01-01 00:00:09.000 8.41847731304077 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_DateTimeTZ_time_column = r""" +id time w +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 0.7310585786300049 +2 2020-01-01 00:00:02 1.5752103826044412 +3 2020-01-01 00:00:03 2.4926527345857696 +4 2020-01-01 00:00:04 3.4519415676621947 +5 2020-01-01 00:00:05 4.432932763071741 +6 2020-01-01 00:00:06 5.424412292903226 +7 2020-01-01 00:00:07 6.420707894737403 +8 2020-01-01 00:00:08 7.419134118454189 +9 2020-01-01 00:00:09 8.41847731304077 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_DateTime64TZ_time_column = r""" +id time w +0 2020-01-01 00:00:00.000000000 0 +1 2020-01-01 00:00:01.000000000 0.7310585786300049 +2 2020-01-01 00:00:02.000000000 1.5752103826044412 +3 2020-01-01 00:00:03.000000000 2.4926527345857696 +4 2020-01-01 00:00:04.000000000 3.4519415676621947 +5 2020-01-01 00:00:05.000000000 4.432932763071741 +6 2020-01-01 00:00:06.000000000 5.424412292903226 +7 2020-01-01 00:00:07.000000000 6.420707894737403 +8 2020-01-01 00:00:08.000000000 7.419134118454189 +9 2020-01-01 00:00:09.000000000 8.41847731304077 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_decay_length_with_zero = r""" +id time w +0 2020-01-01 00:00:00 nan +1 2020-01-01 00:00:01 nan +2 2020-01-01 00:00:02 nan +3 2020-01-01 00:00:03 nan +4 2020-01-01 00:00:04 nan +5 2020-01-01 00:00:05 nan +6 2020-01-01 00:00:06 nan +7 2020-01-01 00:00:07 nan +8 2020-01-01 00:00:08 nan +9 2020-01-01 00:00:09 nan +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_decay_length_with_negative = r""" +id time w +0 2020-01-01 00:00:00 999.0460634232365 +1 2020-01-01 00:00:01 999.0460634232365 +2 2020-01-01 00:00:02 999.0460634232365 +3 2020-01-01 00:00:03 999.0460634232365 +4 2020-01-01 00:00:04 999.0460634232365 +5 2020-01-01 00:00:05 999.0460634232365 +6 2020-01-01 00:00:06 999.0460634232365 +7 2020-01-01 00:00:07 999.0460634232365 +8 2020-01-01 00:00:08 999.0460634232365 +9 2020-01-01 00:00:09 999.0460634232365 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_input_value_with_0 = r""" +id time w +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 0 +2 2020-01-01 00:00:02 0 +3 2020-01-01 00:00:03 0 +4 2020-01-01 00:00:04 0 +5 2020-01-01 00:00:05 0 +6 2020-01-01 00:00:06 0 +7 2020-01-01 00:00:07 0 +8 2020-01-01 00:00:08 0 +9 2020-01-01 00:00:09 0 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_input_time_with_0 = r""" +id time w +0 2020-01-01 00:00:00 4999.5 +1 2020-01-01 00:00:01 4999.5 +2 2020-01-01 00:00:02 4999.5 +3 2020-01-01 00:00:03 4999.5 +4 2020-01-01 00:00:04 4999.5 +5 2020-01-01 00:00:05 4999.5 +6 2020-01-01 00:00:06 4999.5 +7 2020-01-01 00:00:07 4999.5 +8 2020-01-01 00:00:08 4999.5 +9 2020-01-01 00:00:09 4999.5 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_decay_length_overflow = r""" +id time w +0 2020-01-01 00:00:00 4999.5 +1 2020-01-01 00:00:01 4999.5 +2 2020-01-01 00:00:02 4999.5 +3 2020-01-01 00:00:03 4999.5 +4 2020-01-01 00:00:04 4999.5 +5 2020-01-01 00:00:05 4999.5 +6 2020-01-01 00:00:06 4999.5 +7 2020-01-01 00:00:07 4999.5 +8 2020-01-01 00:00:08 4999.5 +9 2020-01-01 00:00:09 4999.5 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_input_value_with_Int8_type = r""" +id time exponentialTimeDecayedAvg(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 2.4428577575728454 +1 1970-01-01 03:00:02 2.4428577575728454 +2 1970-01-01 03:00:03 2.4428577575728454 +3 1970-01-01 03:00:03 2.4428577575728454 +3 1970-01-01 03:00:05 2.4428577575728454 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_input_value_with_Int16_type = r""" +id time exponentialTimeDecayedAvg(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 2.4428577575728454 +1 1970-01-01 03:00:02 2.4428577575728454 +2 1970-01-01 03:00:03 2.4428577575728454 +3 1970-01-01 03:00:03 2.4428577575728454 +3 1970-01-01 03:00:05 2.4428577575728454 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_input_value_with_Int32_type = r""" +id time exponentialTimeDecayedAvg(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 2.4428577575728454 +1 1970-01-01 03:00:02 2.4428577575728454 +2 1970-01-01 03:00:03 2.4428577575728454 +3 1970-01-01 03:00:03 2.4428577575728454 +3 1970-01-01 03:00:05 2.4428577575728454 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_input_value_with_Int64_type = r""" +id time exponentialTimeDecayedAvg(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 2.4428577575728454 +1 1970-01-01 03:00:02 2.4428577575728454 +2 1970-01-01 03:00:03 2.4428577575728454 +3 1970-01-01 03:00:03 2.4428577575728454 +3 1970-01-01 03:00:05 2.4428577575728454 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_input_value_with_UInt8_type = r""" +id time exponentialTimeDecayedAvg(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 2.4428577575728454 +1 1970-01-01 03:00:02 2.4428577575728454 +2 1970-01-01 03:00:03 2.4428577575728454 +3 1970-01-01 03:00:03 2.4428577575728454 +3 1970-01-01 03:00:05 2.4428577575728454 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_input_value_with_UInt16_type = r""" +id time exponentialTimeDecayedAvg(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 2.4428577575728454 +1 1970-01-01 03:00:02 2.4428577575728454 +2 1970-01-01 03:00:03 2.4428577575728454 +3 1970-01-01 03:00:03 2.4428577575728454 +3 1970-01-01 03:00:05 2.4428577575728454 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_input_value_with_UInt32_type = r""" +id time exponentialTimeDecayedAvg(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 2.4428577575728454 +1 1970-01-01 03:00:02 2.4428577575728454 +2 1970-01-01 03:00:03 2.4428577575728454 +3 1970-01-01 03:00:03 2.4428577575728454 +3 1970-01-01 03:00:05 2.4428577575728454 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_input_value_with_UInt64_type = r""" +id time exponentialTimeDecayedAvg(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 2.4428577575728454 +1 1970-01-01 03:00:02 2.4428577575728454 +2 1970-01-01 03:00:03 2.4428577575728454 +3 1970-01-01 03:00:03 2.4428577575728454 +3 1970-01-01 03:00:05 2.4428577575728454 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_input_value_with_Float32_type = r""" +id time exponentialTimeDecayedAvg(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 2.4428577575728454 +1 1970-01-01 03:00:02 2.4428577575728454 +2 1970-01-01 03:00:03 2.4428577575728454 +3 1970-01-01 03:00:03 2.4428577575728454 +3 1970-01-01 03:00:05 2.4428577575728454 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_input_value_with_Float64_type = r""" +id time exponentialTimeDecayedAvg(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 2.4428577575728454 +1 1970-01-01 03:00:02 2.4428577575728454 +2 1970-01-01 03:00:03 2.4428577575728454 +3 1970-01-01 03:00:03 2.4428577575728454 +3 1970-01-01 03:00:05 2.4428577575728454 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_input_value_with_Decimal32_4__type = r""" +id time exponentialTimeDecayedAvg(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 2.4428577575728454 +1 1970-01-01 03:00:02 2.4428577575728454 +2 1970-01-01 03:00:03 2.4428577575728454 +3 1970-01-01 03:00:03 2.4428577575728454 +3 1970-01-01 03:00:05 2.4428577575728454 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_input_value_with_Decimal64_4__type = r""" +id time exponentialTimeDecayedAvg(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 2.4428577575728454 +1 1970-01-01 03:00:02 2.4428577575728454 +2 1970-01-01 03:00:03 2.4428577575728454 +3 1970-01-01 03:00:03 2.4428577575728454 +3 1970-01-01 03:00:05 2.4428577575728454 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_input_value_with_Decimal128_4__type = r""" +id time exponentialTimeDecayedAvg(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 2.4428577575728454 +1 1970-01-01 03:00:02 2.4428577575728454 +2 1970-01-01 03:00:03 2.4428577575728454 +3 1970-01-01 03:00:03 2.4428577575728454 +3 1970-01-01 03:00:05 2.4428577575728454 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_toUInt32_decay_length = r""" +id time w +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 0.52497918747894 +2 2020-01-01 00:00:02 1.0665557957551983 +3 2020-01-01 00:00:03 1.6246471821038961 +4 2020-01-01 00:00:04 2.199138467908942 +5 2020-01-01 00:00:05 2.7898833461901753 +6 2020-01-01 00:00:06 3.3967051006661944 +7 2020-01-01 00:00:07 4.019397822553705 +8 2020-01-01 00:00:08 4.657727808870022 +9 2020-01-01 00:00:09 5.311435123918215 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_toUInt64_decay_length = r""" +id time w +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 0.52497918747894 +2 2020-01-01 00:00:02 1.0665557957551983 +3 2020-01-01 00:00:03 1.6246471821038961 +4 2020-01-01 00:00:04 2.199138467908942 +5 2020-01-01 00:00:05 2.7898833461901753 +6 2020-01-01 00:00:06 3.3967051006661944 +7 2020-01-01 00:00:07 4.019397822553705 +8 2020-01-01 00:00:08 4.657727808870022 +9 2020-01-01 00:00:09 5.311435123918215 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_toFloat32_decay_length = r""" +id time w +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 0.5237915435013812 +2 2020-01-01 00:00:02 1.0633962697787114 +3 2020-01-01 00:00:03 1.6187427337534772 +4 2020-01-01 00:00:04 2.1897315566228315 +5 2020-01-01 00:00:05 2.77623606042911 +6 2020-01-01 00:00:06 3.378103070246944 +7 2020-01-01 00:00:07 3.995153874104678 +8 2020-01-01 00:00:08 4.627185328971674 +9 2020-01-01 00:00:09 5.273971099585486 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_toFloat64_decay_length = r""" +id time w +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 0.5237915435013812 +2 2020-01-01 00:00:02 1.0633962697787114 +3 2020-01-01 00:00:03 1.6187427337534772 +4 2020-01-01 00:00:04 2.1897315566228315 +5 2020-01-01 00:00:05 2.77623606042911 +6 2020-01-01 00:00:06 3.378103070246944 +7 2020-01-01 00:00:07 3.995153874104678 +8 2020-01-01 00:00:08 4.627185328971674 +9 2020-01-01 00:00:09 5.273971099585486 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_one_row_partitions = r""" +id time w +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2 +3 2020-01-01 00:00:03 3 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 5 +6 2020-01-01 00:00:06 6 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 8 +9 2020-01-01 00:00:09 9 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check__1_time_gap = r""" +id time exponentialTimeDecayedAvg(1)(id, time) OVER () +1 2022-01-01 00:00:00 3.255762093989612 +2 2022-01-01 00:00:00 3.255762093989612 +3 2022-01-01 00:00:00 3.255762093989612 +4 2022-01-01 00:00:01 3.255762093989612 +5 2022-01-01 00:00:00 3.255762093989612 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_0_time_gap = r""" +id time exponentialTimeDecayedAvg(1)(id, time) OVER () +1 2022-01-01 00:00:00 -1.8 +2 2022-01-01 00:00:00 -1.8 +-3 2022-01-01 00:00:00 -1.8 +-4 2022-01-01 00:00:00 -1.8 +-5 2022-01-01 00:00:00 -1.8 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 5 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 8 +10 2020-01-01 00:00:10 10 +11 2020-01-01 00:00:11 11 +13 2020-01-01 00:00:13 13 +14 2020-01-01 00:00:14 14 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2 +3 2020-01-01 00:00:03 3 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 5 +6 2020-01-01 00:00:06 6 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 8 +9 2020-01-01 00:00:09 9 +10 2020-01-01 00:00:10 10 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 6912631249882696577 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 16831297400999588778 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 9988.95009144695 +2 2020-01-01 00:00:02 9988.95009144695 +4 2020-01-01 00:00:04 9988.95009144695 +5 2020-01-01 00:00:05 9988.95009144695 +7 2020-01-01 00:00:07 9988.95009144695 +8 2020-01-01 00:00:08 9988.95009144695 +10 2020-01-01 00:00:10 9988.95009144695 +11 2020-01-01 00:00:11 9988.95009144695 +13 2020-01-01 00:00:13 9988.95009144695 +14 2020-01-01 00:00:14 9988.95009144695 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 48.66779947798152 +2 2020-01-01 00:00:02 48.68302837130835 +3 2020-01-01 00:00:03 48.699517020495165 +4 2020-01-01 00:00:04 48.71736284737082 +5 2020-01-01 00:00:05 48.736669997228184 +6 2020-01-01 00:00:06 48.75754970551674 +7 2020-01-01 00:00:07 48.78012067255031 +8 2020-01-01 00:00:08 48.80450944473328 +9 2020-01-01 00:00:09 48.830850800540155 +10 2020-01-01 00:00:10 48.85928813919405 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 13495758327850693138 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 799083818724233891 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1.5249791874789398 +2 2020-01-01 00:00:02 3.099667994624956 +4 2020-01-01 00:00:04 4.52497918747894 +5 2020-01-01 00:00:05 6.099667994624955 +7 2020-01-01 00:00:07 7.52497918747894 +8 2020-01-01 00:00:08 9.099667994624957 +10 2020-01-01 00:00:10 10.524979187478941 +11 2020-01-01 00:00:11 12.099667994624955 +13 2020-01-01 00:00:13 13.52497918747894 +14 2020-01-01 00:00:14 15.099667994624953 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1.5249791874789398 +2 2020-01-01 00:00:02 2.52497918747894 +3 2020-01-01 00:00:03 3.52497918747894 +4 2020-01-01 00:00:04 4.52497918747894 +5 2020-01-01 00:00:05 5.524979187478941 +6 2020-01-01 00:00:06 6.52497918747894 +7 2020-01-01 00:00:07 7.52497918747894 +8 2020-01-01 00:00:08 8.524979187478941 +9 2020-01-01 00:00:09 9.524979187478941 +10 2020-01-01 00:00:10 10.524979187478941 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 6998628102243875284 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 5437358706373763486 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1.5249791874789398 +4 2020-01-01 00:00:04 2.4919545561453664 +5 2020-01-01 00:00:05 3.248306737913917 +7 2020-01-01 00:00:07 4.25821335155325 +8 2020-01-01 00:00:08 5.116151200593762 +10 2020-01-01 00:00:10 6.184642405239253 +11 2020-01-01 00:00:11 7.12224457526869 +13 2020-01-01 00:00:13 8.251527756309828 +14 2020-01-01 00:00:14 9.258345198781718 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1.5249791874789398 +3 2020-01-01 00:00:03 2.0665557957551988 +4 2020-01-01 00:00:04 2.624647182103896 +5 2020-01-01 00:00:05 3.199138467908942 +6 2020-01-01 00:00:06 3.7898833461901758 +7 2020-01-01 00:00:07 4.396705100666194 +8 2020-01-01 00:00:08 5.019397822553704 +9 2020-01-01 00:00:09 5.657727808870021 +10 2020-01-01 00:00:10 6.311435123918215 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 16624019062377156056 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 6829062704342881462 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 nan +2 2020-01-01 00:00:02 1 +4 2020-01-01 00:00:04 1.5249791874789398 +5 2020-01-01 00:00:05 2.4919545561453664 +7 2020-01-01 00:00:07 3.248306737913917 +8 2020-01-01 00:00:08 4.25821335155325 +10 2020-01-01 00:00:10 5.116151200593762 +11 2020-01-01 00:00:11 6.184642405239253 +13 2020-01-01 00:00:13 7.12224457526869 +14 2020-01-01 00:00:14 8.251527756309828 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 nan +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1.5249791874789398 +4 2020-01-01 00:00:04 2.0665557957551988 +5 2020-01-01 00:00:05 2.624647182103896 +6 2020-01-01 00:00:06 3.199138467908942 +7 2020-01-01 00:00:07 3.7898833461901758 +8 2020-01-01 00:00:08 4.396705100666194 +9 2020-01-01 00:00:09 5.019397822553704 +10 2020-01-01 00:00:10 5.657727808870021 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 2840650869004265920 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 12349134980424805700 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 9988.95009144695 +2 2020-01-01 00:00:02 9988.95009144695 +4 2020-01-01 00:00:04 9988.95009144695 +5 2020-01-01 00:00:05 9988.95009144695 +7 2020-01-01 00:00:07 9988.95009144695 +8 2020-01-01 00:00:08 9988.95009144695 +10 2020-01-01 00:00:10 9988.95009144695 +11 2020-01-01 00:00:11 9988.95009144695 +13 2020-01-01 00:00:13 9988.95009144695 +14 2020-01-01 00:00:14 9988.95009144695 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 48.66779947798152 +2 2020-01-01 00:00:02 48.66779947798152 +3 2020-01-01 00:00:03 48.66779947798152 +4 2020-01-01 00:00:04 48.66779947798152 +5 2020-01-01 00:00:05 48.66779947798152 +6 2020-01-01 00:00:06 48.66779947798152 +7 2020-01-01 00:00:07 48.66779947798152 +8 2020-01-01 00:00:08 48.66779947798152 +9 2020-01-01 00:00:09 48.66779947798152 +10 2020-01-01 00:00:10 48.66779947798152 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 15562077973505474208 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 16458442593046828105 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1.5249791874789398 +4 2020-01-01 00:00:04 3.099667994624956 +5 2020-01-01 00:00:05 4.52497918747894 +7 2020-01-01 00:00:07 6.099667994624955 +8 2020-01-01 00:00:08 7.52497918747894 +10 2020-01-01 00:00:10 9.099667994624957 +11 2020-01-01 00:00:11 10.524979187478941 +13 2020-01-01 00:00:13 12.099667994624955 +14 2020-01-01 00:00:14 13.52497918747894 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1.5249791874789398 +3 2020-01-01 00:00:03 2.52497918747894 +4 2020-01-01 00:00:04 3.52497918747894 +5 2020-01-01 00:00:05 4.52497918747894 +6 2020-01-01 00:00:06 5.524979187478941 +7 2020-01-01 00:00:07 6.52497918747894 +8 2020-01-01 00:00:08 7.52497918747894 +9 2020-01-01 00:00:09 8.524979187478941 +10 2020-01-01 00:00:10 9.524979187478941 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 7297242392081283338 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 7553695442776722505 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 9988.95009144695 +2 2020-01-01 00:00:02 9988.95009144695 +4 2020-01-01 00:00:04 9988.95009144695 +5 2020-01-01 00:00:05 9988.95009144695 +7 2020-01-01 00:00:07 9988.95009144695 +8 2020-01-01 00:00:08 9988.95009144695 +10 2020-01-01 00:00:10 9988.95009144695 +11 2020-01-01 00:00:11 9988.95009144695 +13 2020-01-01 00:00:13 9988.95009144695 +14 2020-01-01 00:00:14 9988.95009144695 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 48.66779947798152 +2 2020-01-01 00:00:02 48.66779947798152 +3 2020-01-01 00:00:03 48.68302837130835 +4 2020-01-01 00:00:04 48.699517020495165 +5 2020-01-01 00:00:05 48.71736284737082 +6 2020-01-01 00:00:06 48.736669997228184 +7 2020-01-01 00:00:07 48.75754970551674 +8 2020-01-01 00:00:08 48.78012067255031 +9 2020-01-01 00:00:09 48.80450944473328 +10 2020-01-01 00:00:10 48.830850800540155 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 9881448387590534551 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 4076788104734413191 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1.5249791874789398 +2 2020-01-01 00:00:02 2.4919545561453664 +4 2020-01-01 00:00:04 3.8179519532644535 +5 2020-01-01 00:00:05 5.491954556145366 +7 2020-01-01 00:00:07 6.817951953264453 +8 2020-01-01 00:00:08 8.491954556145366 +10 2020-01-01 00:00:10 9.817951953264453 +11 2020-01-01 00:00:11 11.491954556145366 +13 2020-01-01 00:00:13 12.817951953264451 +14 2020-01-01 00:00:14 14.491954556145364 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1.5249791874789398 +2 2020-01-01 00:00:02 2.0665557957551988 +3 2020-01-01 00:00:03 3.0665557957551988 +4 2020-01-01 00:00:04 4.066555795755199 +5 2020-01-01 00:00:05 5.066555795755199 +6 2020-01-01 00:00:06 6.066555795755198 +7 2020-01-01 00:00:07 7.066555795755197 +8 2020-01-01 00:00:08 8.066555795755196 +9 2020-01-01 00:00:09 9.066555795755198 +10 2020-01-01 00:00:10 10.066555795755198 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 14429555309796454170 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 15059704790173788829 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 9988.95009144695 +2 2020-01-01 00:00:02 9988.95009144695 +4 2020-01-01 00:00:04 9988.95009144695 +5 2020-01-01 00:00:05 9988.95009144695 +7 2020-01-01 00:00:07 9988.95009144695 +8 2020-01-01 00:00:08 9988.95009144695 +10 2020-01-01 00:00:10 9988.95009144695 +11 2020-01-01 00:00:11 9988.95009144695 +13 2020-01-01 00:00:13 9988.95009144695 +14 2020-01-01 00:00:14 9988.95009144695 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 48.68302837130835 +2 2020-01-01 00:00:02 48.699517020495165 +3 2020-01-01 00:00:03 48.71736284737082 +4 2020-01-01 00:00:04 48.736669997228184 +5 2020-01-01 00:00:05 48.75754970551674 +6 2020-01-01 00:00:06 48.78012067255031 +7 2020-01-01 00:00:07 48.80450944473328 +8 2020-01-01 00:00:08 48.830850800540155 +9 2020-01-01 00:00:09 48.85928813919405 +10 2020-01-01 00:00:10 48.88997386967902 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 13239221788919936536 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ROWS_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 16402863909584675538 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 9988.95009144695 +2 2020-01-01 00:00:02 9988.95009144695 +4 2020-01-01 00:00:04 9988.95009144695 +5 2020-01-01 00:00:05 9988.95009144695 +7 2020-01-01 00:00:07 9988.95009144695 +8 2020-01-01 00:00:08 9988.95009144695 +10 2020-01-01 00:00:10 9988.95009144695 +11 2020-01-01 00:00:11 9988.95009144695 +13 2020-01-01 00:00:13 9988.95009144695 +14 2020-01-01 00:00:14 9988.95009144695 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 48.66779947798152 +2 2020-01-01 00:00:02 48.66779947798152 +3 2020-01-01 00:00:03 48.66779947798152 +4 2020-01-01 00:00:04 48.66779947798152 +5 2020-01-01 00:00:05 48.66779947798152 +6 2020-01-01 00:00:06 48.66779947798152 +7 2020-01-01 00:00:07 48.66779947798152 +8 2020-01-01 00:00:08 48.66779947798152 +9 2020-01-01 00:00:09 48.66779947798152 +10 2020-01-01 00:00:10 48.66779947798152 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 15562077973505474208 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 16458442593046828105 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 9988.95009144695 +2 2020-01-01 00:00:02 9988.95009144695 +4 2020-01-01 00:00:04 9988.95009144695 +5 2020-01-01 00:00:05 9988.95009144695 +7 2020-01-01 00:00:07 9988.95009144695 +8 2020-01-01 00:00:08 9988.95009144695 +10 2020-01-01 00:00:10 9988.95009144695 +11 2020-01-01 00:00:11 9988.95009144695 +13 2020-01-01 00:00:13 9988.95009144695 +14 2020-01-01 00:00:14 9988.95009144695 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 48.66779947798152 +2 2020-01-01 00:00:02 48.66779947798152 +3 2020-01-01 00:00:03 48.66779947798152 +4 2020-01-01 00:00:04 48.66779947798152 +5 2020-01-01 00:00:05 48.66779947798152 +6 2020-01-01 00:00:06 48.66779947798152 +7 2020-01-01 00:00:07 48.66779947798152 +8 2020-01-01 00:00:08 48.66779947798152 +9 2020-01-01 00:00:09 48.66779947798152 +10 2020-01-01 00:00:10 48.66779947798152 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 15562077973505474208 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 16458442593046828105 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 9988.95009144695 +2 2020-01-01 00:00:02 9988.95009144695 +4 2020-01-01 00:00:04 9988.95009144695 +5 2020-01-01 00:00:05 9988.95009144695 +7 2020-01-01 00:00:07 9988.95009144695 +8 2020-01-01 00:00:08 9988.95009144695 +10 2020-01-01 00:00:10 9988.95009144695 +11 2020-01-01 00:00:11 9988.95009144695 +13 2020-01-01 00:00:13 9988.95009144695 +14 2020-01-01 00:00:14 9988.95009144695 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 48.66779947798152 +2 2020-01-01 00:00:02 48.66779947798152 +3 2020-01-01 00:00:03 48.66779947798152 +4 2020-01-01 00:00:04 48.66779947798152 +5 2020-01-01 00:00:05 48.66779947798152 +6 2020-01-01 00:00:06 48.66779947798152 +7 2020-01-01 00:00:07 48.66779947798152 +8 2020-01-01 00:00:08 48.66779947798152 +9 2020-01-01 00:00:09 48.66779947798152 +10 2020-01-01 00:00:10 48.66779947798152 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 15562077973505474208 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 16458442593046828105 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 9988.95009144695 +2 2020-01-01 00:00:02 9988.95009144695 +4 2020-01-01 00:00:04 9988.95009144695 +5 2020-01-01 00:00:05 9988.95009144695 +7 2020-01-01 00:00:07 9988.95009144695 +8 2020-01-01 00:00:08 9988.95009144695 +10 2020-01-01 00:00:10 9988.95009144695 +11 2020-01-01 00:00:11 9988.95009144695 +13 2020-01-01 00:00:13 9988.95009144695 +14 2020-01-01 00:00:14 9988.95009144695 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 48.66779947798152 +2 2020-01-01 00:00:02 48.66779947798152 +3 2020-01-01 00:00:03 48.66779947798152 +4 2020-01-01 00:00:04 48.66779947798152 +5 2020-01-01 00:00:05 48.66779947798152 +6 2020-01-01 00:00:06 48.66779947798152 +7 2020-01-01 00:00:07 48.66779947798152 +8 2020-01-01 00:00:08 48.66779947798152 +9 2020-01-01 00:00:09 48.66779947798152 +10 2020-01-01 00:00:10 48.66779947798152 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 15562077973505474208 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 16458442593046828105 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 5 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 8 +10 2020-01-01 00:00:10 10 +11 2020-01-01 00:00:11 11 +13 2020-01-01 00:00:13 13 +14 2020-01-01 00:00:14 14 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2 +3 2020-01-01 00:00:03 3 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 5 +6 2020-01-01 00:00:06 6 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 8 +9 2020-01-01 00:00:09 9 +10 2020-01-01 00:00:10 10 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 6912631249882696577 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 16831297400999588778 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 9988.95009144695 +2 2020-01-01 00:00:02 9988.95009144695 +4 2020-01-01 00:00:04 9988.95009144695 +5 2020-01-01 00:00:05 9988.95009144695 +7 2020-01-01 00:00:07 9988.95009144695 +8 2020-01-01 00:00:08 9988.95009144695 +10 2020-01-01 00:00:10 9988.95009144695 +11 2020-01-01 00:00:11 9988.95009144695 +13 2020-01-01 00:00:13 9988.95009144695 +14 2020-01-01 00:00:14 9988.95009144695 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 48.66779947798152 +2 2020-01-01 00:00:02 48.68302837130835 +3 2020-01-01 00:00:03 48.699517020495165 +4 2020-01-01 00:00:04 48.71736284737082 +5 2020-01-01 00:00:05 48.736669997228184 +6 2020-01-01 00:00:06 48.75754970551674 +7 2020-01-01 00:00:07 48.78012067255031 +8 2020-01-01 00:00:08 48.80450944473328 +9 2020-01-01 00:00:09 48.830850800540155 +10 2020-01-01 00:00:10 48.85928813919405 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 13495758327850693138 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 799083818724233891 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1.5249791874789398 +2 2020-01-01 00:00:02 1.9999999999999998 +4 2020-01-01 00:00:04 4.524979187478939 +5 2020-01-01 00:00:05 4.999999999999999 +7 2020-01-01 00:00:07 7.524979187478941 +8 2020-01-01 00:00:08 8 +10 2020-01-01 00:00:10 10.52497918747894 +11 2020-01-01 00:00:11 11 +13 2020-01-01 00:00:13 13.524979187478941 +14 2020-01-01 00:00:14 14.000000000000002 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1.5249791874789398 +2 2020-01-01 00:00:02 2.52497918747894 +3 2020-01-01 00:00:03 3.52497918747894 +4 2020-01-01 00:00:04 4.52497918747894 +5 2020-01-01 00:00:05 5.524979187478941 +6 2020-01-01 00:00:06 6.52497918747894 +7 2020-01-01 00:00:07 7.52497918747894 +8 2020-01-01 00:00:08 8.524979187478941 +9 2020-01-01 00:00:09 9.524979187478941 +10 2020-01-01 00:00:10 10.524979187478941 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 6998628102243875284 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 9329909658882923324 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1.5249791874789398 +4 2020-01-01 00:00:04 2.4919545561453664 +5 2020-01-01 00:00:05 3.248306737913917 +7 2020-01-01 00:00:07 4.25821335155325 +8 2020-01-01 00:00:08 5.116151200593762 +10 2020-01-01 00:00:10 6.184642405239253 +11 2020-01-01 00:00:11 7.12224457526869 +13 2020-01-01 00:00:13 8.251527756309828 +14 2020-01-01 00:00:14 9.258345198781718 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1.5249791874789398 +3 2020-01-01 00:00:03 2.0665557957551988 +4 2020-01-01 00:00:04 2.624647182103896 +5 2020-01-01 00:00:05 3.199138467908942 +6 2020-01-01 00:00:06 3.7898833461901758 +7 2020-01-01 00:00:07 4.396705100666194 +8 2020-01-01 00:00:08 5.019397822553704 +9 2020-01-01 00:00:09 5.657727808870021 +10 2020-01-01 00:00:10 6.311435123918215 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 16624019062377156056 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 6829062704342881462 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 nan +2 2020-01-01 00:00:02 1 +4 2020-01-01 00:00:04 1.5249791874789398 +5 2020-01-01 00:00:05 2.4919545561453664 +7 2020-01-01 00:00:07 3.248306737913917 +8 2020-01-01 00:00:08 4.25821335155325 +10 2020-01-01 00:00:10 5.116151200593762 +11 2020-01-01 00:00:11 6.184642405239253 +13 2020-01-01 00:00:13 7.12224457526869 +14 2020-01-01 00:00:14 8.251527756309828 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 nan +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1.5249791874789398 +4 2020-01-01 00:00:04 2.0665557957551988 +5 2020-01-01 00:00:05 2.624647182103896 +6 2020-01-01 00:00:06 3.199138467908942 +7 2020-01-01 00:00:07 3.7898833461901758 +8 2020-01-01 00:00:08 4.396705100666194 +9 2020-01-01 00:00:09 5.019397822553704 +10 2020-01-01 00:00:10 5.657727808870021 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 2840650869004265920 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 12349134980424805700 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 9988.95009144695 +2 2020-01-01 00:00:02 9988.95009144695 +4 2020-01-01 00:00:04 9988.95009144695 +5 2020-01-01 00:00:05 9988.95009144695 +7 2020-01-01 00:00:07 9988.95009144695 +8 2020-01-01 00:00:08 9988.95009144695 +10 2020-01-01 00:00:10 9988.95009144695 +11 2020-01-01 00:00:11 9988.95009144695 +13 2020-01-01 00:00:13 9988.95009144695 +14 2020-01-01 00:00:14 9988.95009144695 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 48.66779947798152 +2 2020-01-01 00:00:02 48.66779947798152 +3 2020-01-01 00:00:03 48.66779947798152 +4 2020-01-01 00:00:04 48.66779947798152 +5 2020-01-01 00:00:05 48.66779947798152 +6 2020-01-01 00:00:06 48.66779947798152 +7 2020-01-01 00:00:07 48.66779947798152 +8 2020-01-01 00:00:08 48.66779947798152 +9 2020-01-01 00:00:09 48.66779947798152 +10 2020-01-01 00:00:10 48.66779947798152 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 15562077973505474208 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 16458442593046828105 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1.5249791874789398 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 4.524979187478939 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 7.524979187478941 +10 2020-01-01 00:00:10 10 +11 2020-01-01 00:00:11 10.52497918747894 +13 2020-01-01 00:00:13 13 +14 2020-01-01 00:00:14 13.524979187478941 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1.5249791874789398 +3 2020-01-01 00:00:03 2.52497918747894 +4 2020-01-01 00:00:04 3.52497918747894 +5 2020-01-01 00:00:05 4.52497918747894 +6 2020-01-01 00:00:06 5.524979187478941 +7 2020-01-01 00:00:07 6.52497918747894 +8 2020-01-01 00:00:08 7.52497918747894 +9 2020-01-01 00:00:09 8.524979187478941 +10 2020-01-01 00:00:10 9.524979187478941 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 7297242392081283338 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 18027367577351402446 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 9988.95009144695 +2 2020-01-01 00:00:02 9988.95009144695 +4 2020-01-01 00:00:04 9988.95009144695 +5 2020-01-01 00:00:05 9988.95009144695 +7 2020-01-01 00:00:07 9988.95009144695 +8 2020-01-01 00:00:08 9988.95009144695 +10 2020-01-01 00:00:10 9988.95009144695 +11 2020-01-01 00:00:11 9988.95009144695 +13 2020-01-01 00:00:13 9988.95009144695 +14 2020-01-01 00:00:14 9988.95009144695 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 48.66779947798152 +2 2020-01-01 00:00:02 48.66779947798152 +3 2020-01-01 00:00:03 48.68302837130835 +4 2020-01-01 00:00:04 48.699517020495165 +5 2020-01-01 00:00:05 48.71736284737082 +6 2020-01-01 00:00:06 48.736669997228184 +7 2020-01-01 00:00:07 48.75754970551674 +8 2020-01-01 00:00:08 48.78012067255031 +9 2020-01-01 00:00:09 48.80450944473328 +10 2020-01-01 00:00:10 48.830850800540155 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 9881448387590534551 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 4541465367653127624 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1.5249791874789398 +2 2020-01-01 00:00:02 1.5249791874789398 +4 2020-01-01 00:00:04 4.524979187478939 +5 2020-01-01 00:00:05 4.524979187478939 +7 2020-01-01 00:00:07 7.524979187478941 +8 2020-01-01 00:00:08 7.524979187478941 +10 2020-01-01 00:00:10 10.52497918747894 +11 2020-01-01 00:00:11 10.52497918747894 +13 2020-01-01 00:00:13 13.524979187478941 +14 2020-01-01 00:00:14 13.524979187478941 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1.5249791874789398 +2 2020-01-01 00:00:02 2.0665557957551988 +3 2020-01-01 00:00:03 3.0665557957551988 +4 2020-01-01 00:00:04 4.066555795755199 +5 2020-01-01 00:00:05 5.066555795755199 +6 2020-01-01 00:00:06 6.066555795755198 +7 2020-01-01 00:00:07 7.066555795755197 +8 2020-01-01 00:00:08 8.066555795755196 +9 2020-01-01 00:00:09 9.066555795755198 +10 2020-01-01 00:00:10 10.066555795755198 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 14429555309796454170 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 15307615954504467838 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 9988.95009144695 +2 2020-01-01 00:00:02 9988.95009144695 +4 2020-01-01 00:00:04 9988.95009144695 +5 2020-01-01 00:00:05 9988.95009144695 +7 2020-01-01 00:00:07 9988.95009144695 +8 2020-01-01 00:00:08 9988.95009144695 +10 2020-01-01 00:00:10 9988.95009144695 +11 2020-01-01 00:00:11 9988.95009144695 +13 2020-01-01 00:00:13 9988.95009144695 +14 2020-01-01 00:00:14 9988.95009144695 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 48.68302837130835 +2 2020-01-01 00:00:02 48.699517020495165 +3 2020-01-01 00:00:03 48.71736284737082 +4 2020-01-01 00:00:04 48.736669997228184 +5 2020-01-01 00:00:05 48.75754970551674 +6 2020-01-01 00:00:06 48.78012067255031 +7 2020-01-01 00:00:07 48.80450944473328 +8 2020-01-01 00:00:08 48.830850800540155 +9 2020-01-01 00:00:09 48.85928813919405 +10 2020-01-01 00:00:10 48.88997386967902 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 13239221788919936536 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedAvg_check_over_ORDER_BY_id_RANGE_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 16402863909584675538 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_DateTime_time_column = r""" +id time w +0 2020-01-01 00:00:00 1 +1 2020-01-01 00:00:01 1.3678794411714423 +2 2020-01-01 00:00:02 1.5032147244080551 +3 2020-01-01 00:00:03 1.553001792775919 +4 2020-01-01 00:00:04 1.5713174316646532 +5 2020-01-01 00:00:05 1.5780553786637386 +6 2020-01-01 00:00:06 1.5805341308404048 +7 2020-01-01 00:00:07 1.5814460128059595 +8 2020-01-01 00:00:08 1.581781475433862 +9 2020-01-01 00:00:09 1.5819048852379487 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_DateTime64_time_column = r""" +id time w +0 2020-01-01 00:00:00.000 1 +1 2020-01-01 00:00:01.000 1.3678794411714423 +2 2020-01-01 00:00:02.000 1.5032147244080551 +3 2020-01-01 00:00:03.000 1.553001792775919 +4 2020-01-01 00:00:04.000 1.5713174316646532 +5 2020-01-01 00:00:05.000 1.5780553786637386 +6 2020-01-01 00:00:06.000 1.5805341308404048 +7 2020-01-01 00:00:07.000 1.5814460128059595 +8 2020-01-01 00:00:08.000 1.581781475433862 +9 2020-01-01 00:00:09.000 1.5819048852379487 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_DateTimeTZ_time_column = r""" +id time w +0 2020-01-01 00:00:00 1 +1 2020-01-01 00:00:01 1.3678794411714423 +2 2020-01-01 00:00:02 1.5032147244080551 +3 2020-01-01 00:00:03 1.553001792775919 +4 2020-01-01 00:00:04 1.5713174316646532 +5 2020-01-01 00:00:05 1.5780553786637386 +6 2020-01-01 00:00:06 1.5805341308404048 +7 2020-01-01 00:00:07 1.5814460128059595 +8 2020-01-01 00:00:08 1.581781475433862 +9 2020-01-01 00:00:09 1.5819048852379487 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_DateTime64TZ_time_column = r""" +id time w +0 2020-01-01 00:00:00.000000000 1 +1 2020-01-01 00:00:01.000000000 1.3678794411714423 +2 2020-01-01 00:00:02.000000000 1.5032147244080551 +3 2020-01-01 00:00:03.000000000 1.553001792775919 +4 2020-01-01 00:00:04.000000000 1.5713174316646532 +5 2020-01-01 00:00:05.000000000 1.5780553786637386 +6 2020-01-01 00:00:06.000000000 1.5805341308404048 +7 2020-01-01 00:00:07.000000000 1.5814460128059595 +8 2020-01-01 00:00:08.000000000 1.581781475433862 +9 2020-01-01 00:00:09.000000000 1.5819048852379487 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_decay_length_with_zero = r""" +id time w +0 2020-01-01 00:00:00 nan +1 2020-01-01 00:00:01 nan +2 2020-01-01 00:00:02 nan +3 2020-01-01 00:00:03 nan +4 2020-01-01 00:00:04 nan +5 2020-01-01 00:00:05 nan +6 2020-01-01 00:00:06 nan +7 2020-01-01 00:00:07 nan +8 2020-01-01 00:00:08 nan +9 2020-01-01 00:00:09 nan +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_decay_length_with_negative = r""" +id time w +0 2020-01-01 00:00:00 22014454.897364672 +1 2020-01-01 00:00:01 22014454.897364672 +2 2020-01-01 00:00:02 22014454.897364672 +3 2020-01-01 00:00:03 22014454.897364672 +4 2020-01-01 00:00:04 22014454.897364672 +5 2020-01-01 00:00:05 22014454.897364672 +6 2020-01-01 00:00:06 22014454.897364672 +7 2020-01-01 00:00:07 22014454.897364672 +8 2020-01-01 00:00:08 22014454.897364672 +9 2020-01-01 00:00:09 22014454.897364672 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_decay_length_overflow = r""" +id time w +0 2020-01-01 00:00:00 10000 +1 2020-01-01 00:00:01 10000 +2 2020-01-01 00:00:02 10000 +3 2020-01-01 00:00:03 10000 +4 2020-01-01 00:00:04 10000 +5 2020-01-01 00:00:05 10000 +6 2020-01-01 00:00:06 10000 +7 2020-01-01 00:00:07 10000 +8 2020-01-01 00:00:08 10000 +9 2020-01-01 00:00:09 10000 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_input_time_with_0 = r""" +id time w +0 2020-01-01 00:00:00 10000 +1 2020-01-01 00:00:01 10000 +2 2020-01-01 00:00:02 10000 +3 2020-01-01 00:00:03 10000 +4 2020-01-01 00:00:04 10000 +5 2020-01-01 00:00:05 10000 +6 2020-01-01 00:00:06 10000 +7 2020-01-01 00:00:07 10000 +8 2020-01-01 00:00:08 10000 +9 2020-01-01 00:00:09 10000 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_toUInt32_decay_length = r""" +id time w +0 2020-01-01 00:00:00 1 +1 2020-01-01 00:00:01 1.9048374180359595 +2 2020-01-01 00:00:02 2.723568171113941 +3 2020-01-01 00:00:03 3.4643863917956588 +4 2020-01-01 00:00:04 4.134706437831298 +5 2020-01-01 00:00:05 4.741237097543931 +6 2020-01-01 00:00:06 5.290048733637957 +7 2020-01-01 00:00:07 5.7866340374293666 +8 2020-01-01 00:00:08 6.235963001546588 +9 2020-01-01 00:00:09 6.642532661287187 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_toUInt64_decay_length = r""" +id time w +0 2020-01-01 00:00:00 1 +1 2020-01-01 00:00:01 1.9048374180359595 +2 2020-01-01 00:00:02 2.723568171113941 +3 2020-01-01 00:00:03 3.4643863917956588 +4 2020-01-01 00:00:04 4.134706437831298 +5 2020-01-01 00:00:05 4.741237097543931 +6 2020-01-01 00:00:06 5.290048733637957 +7 2020-01-01 00:00:07 5.7866340374293666 +8 2020-01-01 00:00:08 6.235963001546588 +9 2020-01-01 00:00:09 6.642532661287187 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_toFloat32_decay_length = r""" +id time w +0 2020-01-01 00:00:00 1 +1 2020-01-01 00:00:01 1.909156442876713 +2 2020-01-01 00:00:02 2.735721880500951 +3 2020-01-01 00:00:03 3.487199173576237 +4 2020-01-01 00:00:04 4.170409596251185 +5 2020-01-01 00:00:05 4.791554753866636 +6 2020-01-01 00:00:06 5.356272875874395 +7 2020-01-01 00:00:07 5.869689994906987 +8 2020-01-01 00:00:08 6.336466476558669 +9 2020-01-01 00:00:09 6.7608393222356185 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_toFloat64_decay_length = r""" +id time w +0 2020-01-01 00:00:00 1 +1 2020-01-01 00:00:01 1.909156442876713 +2 2020-01-01 00:00:02 2.735721880500951 +3 2020-01-01 00:00:03 3.487199173576237 +4 2020-01-01 00:00:04 4.170409596251185 +5 2020-01-01 00:00:05 4.791554753866636 +6 2020-01-01 00:00:06 5.356272875874395 +7 2020-01-01 00:00:07 5.869689994906987 +8 2020-01-01 00:00:08 6.336466476558669 +9 2020-01-01 00:00:09 6.7608393222356185 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_one_row_partitions = r""" +id time w +0 2020-01-01 00:00:00 1 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +6 2020-01-01 00:00:06 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +9 2020-01-01 00:00:09 1 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check__1_time_gap = r""" +id time exponentialTimeDecayedCount(1)(time) OVER () +1 2022-01-01 00:00:00 6.718281828459045 +2 2022-01-01 00:00:00 6.718281828459045 +3 2022-01-01 00:00:00 6.718281828459045 +4 2022-01-01 00:00:01 6.718281828459045 +5 2022-01-01 00:00:00 6.718281828459045 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_0_time_gap = r""" +id time exponentialTimeDecayedCount(1)(time) OVER () +1 2022-01-01 00:00:00 5 +2 2022-01-01 00:00:00 5 +-3 2022-01-01 00:00:00 5 +-4 2022-01-01 00:00:00 5 +-5 2022-01-01 00:00:00 5 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +10 2020-01-01 00:00:10 1 +11 2020-01-01 00:00:11 1 +13 2020-01-01 00:00:13 1 +14 2020-01-01 00:00:14 1 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +6 2020-01-01 00:00:06 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +9 2020-01-01 00:00:09 1 +10 2020-01-01 00:00:10 1 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 12877234599328637328 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 3447050494505378762 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 7.34942642590924 +2 2020-01-01 00:00:02 7.34942642590924 +4 2020-01-01 00:00:04 7.34942642590924 +5 2020-01-01 00:00:05 7.34942642590924 +7 2020-01-01 00:00:07 7.34942642590924 +8 2020-01-01 00:00:08 7.34942642590924 +10 2020-01-01 00:00:10 7.34942642590924 +11 2020-01-01 00:00:11 7.34942642590924 +13 2020-01-01 00:00:13 7.34942642590924 +14 2020-01-01 00:00:14 7.34942642590924 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 10.476517394529663 +2 2020-01-01 00:00:02 10.473171429072192 +3 2020-01-01 00:00:03 10.46947356535571 +4 2020-01-01 00:00:04 10.465386793917245 +5 2020-01-01 00:00:05 10.460870212974633 +6 2020-01-01 00:00:06 10.455878619067724 +7 2020-01-01 00:00:07 10.450362054646963 +8 2020-01-01 00:00:08 10.444265308081446 +9 2020-01-01 00:00:09 10.437527361082362 +10 2020-01-01 00:00:10 10.430080778011437 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 4560842635161322144 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 17878131935679005193 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1.9048374180359595 +2 2020-01-01 00:00:02 1.8187307530779817 +4 2020-01-01 00:00:04 1.9048374180359593 +5 2020-01-01 00:00:05 1.8187307530779817 +7 2020-01-01 00:00:07 1.9048374180359593 +8 2020-01-01 00:00:08 1.8187307530779817 +10 2020-01-01 00:00:10 1.9048374180359593 +11 2020-01-01 00:00:11 1.8187307530779817 +13 2020-01-01 00:00:13 1.9048374180359593 +14 2020-01-01 00:00:14 1.8187307530779817 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1.9048374180359595 +2 2020-01-01 00:00:02 1.9048374180359593 +3 2020-01-01 00:00:03 1.9048374180359593 +4 2020-01-01 00:00:04 1.9048374180359593 +5 2020-01-01 00:00:05 1.9048374180359593 +6 2020-01-01 00:00:06 1.9048374180359593 +7 2020-01-01 00:00:07 1.9048374180359593 +8 2020-01-01 00:00:08 1.9048374180359593 +9 2020-01-01 00:00:09 1.9048374180359593 +10 2020-01-01 00:00:10 1.9048374180359593 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 8495990306826262494 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 10218776842808504744 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1.9048374180359595 +4 2020-01-01 00:00:04 2.5595489737597 +5 2020-01-01 00:00:05 3.3159756847533166 +7 2020-01-01 00:00:07 3.714891269566359 +8 2020-01-01 00:00:08 4.3613726246387525 +10 2020-01-01 00:00:10 4.57078989342418 +11 2020-01-01 00:00:11 5.135821725550794 +13 2020-01-01 00:00:13 5.204855189034461 +14 2020-01-01 00:00:14 5.7095477304970075 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1.9048374180359595 +3 2020-01-01 00:00:03 2.723568171113941 +4 2020-01-01 00:00:04 3.4643863917956588 +5 2020-01-01 00:00:05 4.134706437831298 +6 2020-01-01 00:00:06 4.741237097543931 +7 2020-01-01 00:00:07 5.290048733637957 +8 2020-01-01 00:00:08 5.7866340374293666 +9 2020-01-01 00:00:09 6.235963001546588 +10 2020-01-01 00:00:10 6.642532661287187 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 16737121499884598835 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 11839951592374520142 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 0 +2 2020-01-01 00:00:02 1 +4 2020-01-01 00:00:04 1.9048374180359595 +5 2020-01-01 00:00:05 2.5595489737597 +7 2020-01-01 00:00:07 3.3159756847533166 +8 2020-01-01 00:00:08 3.714891269566359 +10 2020-01-01 00:00:10 4.3613726246387525 +11 2020-01-01 00:00:11 4.57078989342418 +13 2020-01-01 00:00:13 5.135821725550794 +14 2020-01-01 00:00:14 5.204855189034461 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 0 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1.9048374180359595 +4 2020-01-01 00:00:04 2.723568171113941 +5 2020-01-01 00:00:05 3.4643863917956588 +6 2020-01-01 00:00:06 4.134706437831298 +7 2020-01-01 00:00:07 4.741237097543931 +8 2020-01-01 00:00:08 5.290048733637957 +9 2020-01-01 00:00:09 5.7866340374293666 +10 2020-01-01 00:00:10 6.235963001546588 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 6034479436322793231 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 11997635277937493853 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 7.34942642590924 +2 2020-01-01 00:00:02 7.34942642590924 +4 2020-01-01 00:00:04 7.34942642590924 +5 2020-01-01 00:00:05 7.34942642590924 +7 2020-01-01 00:00:07 7.34942642590924 +8 2020-01-01 00:00:08 7.34942642590924 +10 2020-01-01 00:00:10 7.34942642590924 +11 2020-01-01 00:00:11 7.34942642590924 +13 2020-01-01 00:00:13 7.34942642590924 +14 2020-01-01 00:00:14 7.34942642590924 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 10.476517394529663 +2 2020-01-01 00:00:02 10.476517394529663 +3 2020-01-01 00:00:03 10.476517394529663 +4 2020-01-01 00:00:04 10.476517394529663 +5 2020-01-01 00:00:05 10.476517394529663 +6 2020-01-01 00:00:06 10.476517394529663 +7 2020-01-01 00:00:07 10.476517394529663 +8 2020-01-01 00:00:08 10.476517394529663 +9 2020-01-01 00:00:09 10.476517394529663 +10 2020-01-01 00:00:10 10.476517394529663 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 13152195228564121686 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 5718761771932584523 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1.9048374180359595 +4 2020-01-01 00:00:04 1.8187307530779817 +5 2020-01-01 00:00:05 1.9048374180359593 +7 2020-01-01 00:00:07 1.8187307530779817 +8 2020-01-01 00:00:08 1.9048374180359593 +10 2020-01-01 00:00:10 1.8187307530779817 +11 2020-01-01 00:00:11 1.9048374180359593 +13 2020-01-01 00:00:13 1.8187307530779817 +14 2020-01-01 00:00:14 1.9048374180359593 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1.9048374180359595 +3 2020-01-01 00:00:03 1.9048374180359593 +4 2020-01-01 00:00:04 1.9048374180359593 +5 2020-01-01 00:00:05 1.9048374180359593 +6 2020-01-01 00:00:06 1.9048374180359593 +7 2020-01-01 00:00:07 1.9048374180359593 +8 2020-01-01 00:00:08 1.9048374180359593 +9 2020-01-01 00:00:09 1.9048374180359593 +10 2020-01-01 00:00:10 1.9048374180359593 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 16247217040454175541 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 13039042947170329667 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 7.34942642590924 +2 2020-01-01 00:00:02 7.34942642590924 +4 2020-01-01 00:00:04 7.34942642590924 +5 2020-01-01 00:00:05 7.34942642590924 +7 2020-01-01 00:00:07 7.34942642590924 +8 2020-01-01 00:00:08 7.34942642590924 +10 2020-01-01 00:00:10 7.34942642590924 +11 2020-01-01 00:00:11 7.34942642590924 +13 2020-01-01 00:00:13 7.34942642590924 +14 2020-01-01 00:00:14 7.34942642590924 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 10.476517394529663 +2 2020-01-01 00:00:02 10.476517394529663 +3 2020-01-01 00:00:03 10.473171429072192 +4 2020-01-01 00:00:04 10.46947356535571 +5 2020-01-01 00:00:05 10.465386793917245 +6 2020-01-01 00:00:06 10.460870212974633 +7 2020-01-01 00:00:07 10.455878619067724 +8 2020-01-01 00:00:08 10.450362054646963 +9 2020-01-01 00:00:09 10.444265308081446 +10 2020-01-01 00:00:10 10.437527361082362 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 16306225357017751068 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 14734888548198881343 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1.9048374180359595 +2 2020-01-01 00:00:02 2.5595489737597 +4 2020-01-01 00:00:04 2.6456556387176775 +5 2020-01-01 00:00:05 2.5595489737597 +7 2020-01-01 00:00:07 2.6456556387176775 +8 2020-01-01 00:00:08 2.5595489737597 +10 2020-01-01 00:00:10 2.6456556387176775 +11 2020-01-01 00:00:11 2.5595489737597 +13 2020-01-01 00:00:13 2.6456556387176775 +14 2020-01-01 00:00:14 2.5595489737597 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1.9048374180359595 +2 2020-01-01 00:00:02 2.723568171113941 +3 2020-01-01 00:00:03 2.723568171113941 +4 2020-01-01 00:00:04 2.723568171113941 +5 2020-01-01 00:00:05 2.723568171113941 +6 2020-01-01 00:00:06 2.723568171113941 +7 2020-01-01 00:00:07 2.723568171113941 +8 2020-01-01 00:00:08 2.723568171113941 +9 2020-01-01 00:00:09 2.723568171113941 +10 2020-01-01 00:00:10 2.723568171113941 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 12761081601402852228 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 10794675264479804918 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 7.34942642590924 +2 2020-01-01 00:00:02 7.34942642590924 +4 2020-01-01 00:00:04 7.34942642590924 +5 2020-01-01 00:00:05 7.34942642590924 +7 2020-01-01 00:00:07 7.34942642590924 +8 2020-01-01 00:00:08 7.34942642590924 +10 2020-01-01 00:00:10 7.34942642590924 +11 2020-01-01 00:00:11 7.34942642590924 +13 2020-01-01 00:00:13 7.34942642590924 +14 2020-01-01 00:00:14 7.34942642590924 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 10.473171429072192 +2 2020-01-01 00:00:02 10.46947356535571 +3 2020-01-01 00:00:03 10.465386793917245 +4 2020-01-01 00:00:04 10.460870212974633 +5 2020-01-01 00:00:05 10.455878619067724 +6 2020-01-01 00:00:06 10.450362054646963 +7 2020-01-01 00:00:07 10.444265308081446 +8 2020-01-01 00:00:08 10.437527361082362 +9 2020-01-01 00:00:09 10.430080778011437 +10 2020-01-01 00:00:10 10.421851030962417 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 3475391510808484800 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ROWS_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 6845134164707796627 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 7.34942642590924 +2 2020-01-01 00:00:02 7.34942642590924 +4 2020-01-01 00:00:04 7.34942642590924 +5 2020-01-01 00:00:05 7.34942642590924 +7 2020-01-01 00:00:07 7.34942642590924 +8 2020-01-01 00:00:08 7.34942642590924 +10 2020-01-01 00:00:10 7.34942642590924 +11 2020-01-01 00:00:11 7.34942642590924 +13 2020-01-01 00:00:13 7.34942642590924 +14 2020-01-01 00:00:14 7.34942642590924 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 10.476517394529663 +2 2020-01-01 00:00:02 10.476517394529663 +3 2020-01-01 00:00:03 10.476517394529663 +4 2020-01-01 00:00:04 10.476517394529663 +5 2020-01-01 00:00:05 10.476517394529663 +6 2020-01-01 00:00:06 10.476517394529663 +7 2020-01-01 00:00:07 10.476517394529663 +8 2020-01-01 00:00:08 10.476517394529663 +9 2020-01-01 00:00:09 10.476517394529663 +10 2020-01-01 00:00:10 10.476517394529663 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 13152195228564121686 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 5718761771932584523 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 7.34942642590924 +2 2020-01-01 00:00:02 7.34942642590924 +4 2020-01-01 00:00:04 7.34942642590924 +5 2020-01-01 00:00:05 7.34942642590924 +7 2020-01-01 00:00:07 7.34942642590924 +8 2020-01-01 00:00:08 7.34942642590924 +10 2020-01-01 00:00:10 7.34942642590924 +11 2020-01-01 00:00:11 7.34942642590924 +13 2020-01-01 00:00:13 7.34942642590924 +14 2020-01-01 00:00:14 7.34942642590924 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 10.476517394529663 +2 2020-01-01 00:00:02 10.476517394529663 +3 2020-01-01 00:00:03 10.476517394529663 +4 2020-01-01 00:00:04 10.476517394529663 +5 2020-01-01 00:00:05 10.476517394529663 +6 2020-01-01 00:00:06 10.476517394529663 +7 2020-01-01 00:00:07 10.476517394529663 +8 2020-01-01 00:00:08 10.476517394529663 +9 2020-01-01 00:00:09 10.476517394529663 +10 2020-01-01 00:00:10 10.476517394529663 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 13152195228564121686 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 5718761771932584523 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 7.34942642590924 +2 2020-01-01 00:00:02 7.34942642590924 +4 2020-01-01 00:00:04 7.34942642590924 +5 2020-01-01 00:00:05 7.34942642590924 +7 2020-01-01 00:00:07 7.34942642590924 +8 2020-01-01 00:00:08 7.34942642590924 +10 2020-01-01 00:00:10 7.34942642590924 +11 2020-01-01 00:00:11 7.34942642590924 +13 2020-01-01 00:00:13 7.34942642590924 +14 2020-01-01 00:00:14 7.34942642590924 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 10.476517394529663 +2 2020-01-01 00:00:02 10.476517394529663 +3 2020-01-01 00:00:03 10.476517394529663 +4 2020-01-01 00:00:04 10.476517394529663 +5 2020-01-01 00:00:05 10.476517394529663 +6 2020-01-01 00:00:06 10.476517394529663 +7 2020-01-01 00:00:07 10.476517394529663 +8 2020-01-01 00:00:08 10.476517394529663 +9 2020-01-01 00:00:09 10.476517394529663 +10 2020-01-01 00:00:10 10.476517394529663 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 13152195228564121686 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 5718761771932584523 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 7.34942642590924 +2 2020-01-01 00:00:02 7.34942642590924 +4 2020-01-01 00:00:04 7.34942642590924 +5 2020-01-01 00:00:05 7.34942642590924 +7 2020-01-01 00:00:07 7.34942642590924 +8 2020-01-01 00:00:08 7.34942642590924 +10 2020-01-01 00:00:10 7.34942642590924 +11 2020-01-01 00:00:11 7.34942642590924 +13 2020-01-01 00:00:13 7.34942642590924 +14 2020-01-01 00:00:14 7.34942642590924 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 10.476517394529663 +2 2020-01-01 00:00:02 10.476517394529663 +3 2020-01-01 00:00:03 10.476517394529663 +4 2020-01-01 00:00:04 10.476517394529663 +5 2020-01-01 00:00:05 10.476517394529663 +6 2020-01-01 00:00:06 10.476517394529663 +7 2020-01-01 00:00:07 10.476517394529663 +8 2020-01-01 00:00:08 10.476517394529663 +9 2020-01-01 00:00:09 10.476517394529663 +10 2020-01-01 00:00:10 10.476517394529663 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 13152195228564121686 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 5718761771932584523 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +10 2020-01-01 00:00:10 1 +11 2020-01-01 00:00:11 1 +13 2020-01-01 00:00:13 1 +14 2020-01-01 00:00:14 1 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +6 2020-01-01 00:00:06 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +9 2020-01-01 00:00:09 1 +10 2020-01-01 00:00:10 1 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 12877234599328637328 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 3447050494505378762 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 7.34942642590924 +2 2020-01-01 00:00:02 7.34942642590924 +4 2020-01-01 00:00:04 7.34942642590924 +5 2020-01-01 00:00:05 7.34942642590924 +7 2020-01-01 00:00:07 7.34942642590924 +8 2020-01-01 00:00:08 7.34942642590924 +10 2020-01-01 00:00:10 7.34942642590924 +11 2020-01-01 00:00:11 7.34942642590924 +13 2020-01-01 00:00:13 7.34942642590924 +14 2020-01-01 00:00:14 7.34942642590924 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 10.476517394529663 +2 2020-01-01 00:00:02 10.473171429072192 +3 2020-01-01 00:00:03 10.46947356535571 +4 2020-01-01 00:00:04 10.465386793917245 +5 2020-01-01 00:00:05 10.460870212974633 +6 2020-01-01 00:00:06 10.455878619067724 +7 2020-01-01 00:00:07 10.450362054646963 +8 2020-01-01 00:00:08 10.444265308081446 +9 2020-01-01 00:00:09 10.437527361082362 +10 2020-01-01 00:00:10 10.430080778011437 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 4560842635161322144 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 17878131935679005193 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1.9048374180359595 +2 2020-01-01 00:00:02 1 +4 2020-01-01 00:00:04 1.9048374180359595 +5 2020-01-01 00:00:05 1 +7 2020-01-01 00:00:07 1.9048374180359595 +8 2020-01-01 00:00:08 1 +10 2020-01-01 00:00:10 1.9048374180359595 +11 2020-01-01 00:00:11 1 +13 2020-01-01 00:00:13 1.9048374180359595 +14 2020-01-01 00:00:14 1 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1.9048374180359595 +2 2020-01-01 00:00:02 1.9048374180359593 +3 2020-01-01 00:00:03 1.9048374180359593 +4 2020-01-01 00:00:04 1.9048374180359593 +5 2020-01-01 00:00:05 1.9048374180359593 +6 2020-01-01 00:00:06 1.9048374180359593 +7 2020-01-01 00:00:07 1.9048374180359593 +8 2020-01-01 00:00:08 1.9048374180359593 +9 2020-01-01 00:00:09 1.9048374180359593 +10 2020-01-01 00:00:10 1.9048374180359593 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 8495990306826262494 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 13159502210799903048 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1.9048374180359595 +4 2020-01-01 00:00:04 2.5595489737597 +5 2020-01-01 00:00:05 3.3159756847533166 +7 2020-01-01 00:00:07 3.714891269566359 +8 2020-01-01 00:00:08 4.3613726246387525 +10 2020-01-01 00:00:10 4.57078989342418 +11 2020-01-01 00:00:11 5.135821725550794 +13 2020-01-01 00:00:13 5.204855189034461 +14 2020-01-01 00:00:14 5.7095477304970075 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1.9048374180359595 +3 2020-01-01 00:00:03 2.723568171113941 +4 2020-01-01 00:00:04 3.4643863917956588 +5 2020-01-01 00:00:05 4.134706437831298 +6 2020-01-01 00:00:06 4.741237097543931 +7 2020-01-01 00:00:07 5.290048733637957 +8 2020-01-01 00:00:08 5.7866340374293666 +9 2020-01-01 00:00:09 6.235963001546588 +10 2020-01-01 00:00:10 6.642532661287187 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 16737121499884598835 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 11839951592374520142 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 0 +2 2020-01-01 00:00:02 1 +4 2020-01-01 00:00:04 1.9048374180359595 +5 2020-01-01 00:00:05 2.5595489737597 +7 2020-01-01 00:00:07 3.3159756847533166 +8 2020-01-01 00:00:08 3.714891269566359 +10 2020-01-01 00:00:10 4.3613726246387525 +11 2020-01-01 00:00:11 4.57078989342418 +13 2020-01-01 00:00:13 5.135821725550794 +14 2020-01-01 00:00:14 5.204855189034461 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 0 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1.9048374180359595 +4 2020-01-01 00:00:04 2.723568171113941 +5 2020-01-01 00:00:05 3.4643863917956588 +6 2020-01-01 00:00:06 4.134706437831298 +7 2020-01-01 00:00:07 4.741237097543931 +8 2020-01-01 00:00:08 5.290048733637957 +9 2020-01-01 00:00:09 5.7866340374293666 +10 2020-01-01 00:00:10 6.235963001546588 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 6034479436322793231 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 11997635277937493853 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 7.34942642590924 +2 2020-01-01 00:00:02 7.34942642590924 +4 2020-01-01 00:00:04 7.34942642590924 +5 2020-01-01 00:00:05 7.34942642590924 +7 2020-01-01 00:00:07 7.34942642590924 +8 2020-01-01 00:00:08 7.34942642590924 +10 2020-01-01 00:00:10 7.34942642590924 +11 2020-01-01 00:00:11 7.34942642590924 +13 2020-01-01 00:00:13 7.34942642590924 +14 2020-01-01 00:00:14 7.34942642590924 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 10.476517394529663 +2 2020-01-01 00:00:02 10.476517394529663 +3 2020-01-01 00:00:03 10.476517394529663 +4 2020-01-01 00:00:04 10.476517394529663 +5 2020-01-01 00:00:05 10.476517394529663 +6 2020-01-01 00:00:06 10.476517394529663 +7 2020-01-01 00:00:07 10.476517394529663 +8 2020-01-01 00:00:08 10.476517394529663 +9 2020-01-01 00:00:09 10.476517394529663 +10 2020-01-01 00:00:10 10.476517394529663 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 13152195228564121686 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 5718761771932584523 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1.9048374180359595 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1.9048374180359595 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1.9048374180359595 +10 2020-01-01 00:00:10 1 +11 2020-01-01 00:00:11 1.9048374180359595 +13 2020-01-01 00:00:13 1 +14 2020-01-01 00:00:14 1.9048374180359595 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1.9048374180359595 +3 2020-01-01 00:00:03 1.9048374180359593 +4 2020-01-01 00:00:04 1.9048374180359593 +5 2020-01-01 00:00:05 1.9048374180359593 +6 2020-01-01 00:00:06 1.9048374180359593 +7 2020-01-01 00:00:07 1.9048374180359593 +8 2020-01-01 00:00:08 1.9048374180359593 +9 2020-01-01 00:00:09 1.9048374180359593 +10 2020-01-01 00:00:10 1.9048374180359593 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 16247217040454175541 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 5513312825056761428 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 7.34942642590924 +2 2020-01-01 00:00:02 7.34942642590924 +4 2020-01-01 00:00:04 7.34942642590924 +5 2020-01-01 00:00:05 7.34942642590924 +7 2020-01-01 00:00:07 7.34942642590924 +8 2020-01-01 00:00:08 7.34942642590924 +10 2020-01-01 00:00:10 7.34942642590924 +11 2020-01-01 00:00:11 7.34942642590924 +13 2020-01-01 00:00:13 7.34942642590924 +14 2020-01-01 00:00:14 7.34942642590924 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 10.476517394529663 +2 2020-01-01 00:00:02 10.476517394529663 +3 2020-01-01 00:00:03 10.473171429072192 +4 2020-01-01 00:00:04 10.46947356535571 +5 2020-01-01 00:00:05 10.465386793917245 +6 2020-01-01 00:00:06 10.460870212974633 +7 2020-01-01 00:00:07 10.455878619067724 +8 2020-01-01 00:00:08 10.450362054646963 +9 2020-01-01 00:00:09 10.444265308081446 +10 2020-01-01 00:00:10 10.437527361082362 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 16306225357017751068 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 14547466133506082998 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1.9048374180359595 +2 2020-01-01 00:00:02 1.9048374180359595 +4 2020-01-01 00:00:04 1.9048374180359595 +5 2020-01-01 00:00:05 1.9048374180359595 +7 2020-01-01 00:00:07 1.9048374180359595 +8 2020-01-01 00:00:08 1.9048374180359595 +10 2020-01-01 00:00:10 1.9048374180359595 +11 2020-01-01 00:00:11 1.9048374180359595 +13 2020-01-01 00:00:13 1.9048374180359595 +14 2020-01-01 00:00:14 1.9048374180359595 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1.9048374180359595 +2 2020-01-01 00:00:02 2.723568171113941 +3 2020-01-01 00:00:03 2.723568171113941 +4 2020-01-01 00:00:04 2.723568171113941 +5 2020-01-01 00:00:05 2.723568171113941 +6 2020-01-01 00:00:06 2.723568171113941 +7 2020-01-01 00:00:07 2.723568171113941 +8 2020-01-01 00:00:08 2.723568171113941 +9 2020-01-01 00:00:09 2.723568171113941 +10 2020-01-01 00:00:10 2.723568171113941 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 12761081601402852228 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 6161100718130533023 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 7.34942642590924 +2 2020-01-01 00:00:02 7.34942642590924 +4 2020-01-01 00:00:04 7.34942642590924 +5 2020-01-01 00:00:05 7.34942642590924 +7 2020-01-01 00:00:07 7.34942642590924 +8 2020-01-01 00:00:08 7.34942642590924 +10 2020-01-01 00:00:10 7.34942642590924 +11 2020-01-01 00:00:11 7.34942642590924 +13 2020-01-01 00:00:13 7.34942642590924 +14 2020-01-01 00:00:14 7.34942642590924 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 10.473171429072192 +2 2020-01-01 00:00:02 10.46947356535571 +3 2020-01-01 00:00:03 10.465386793917245 +4 2020-01-01 00:00:04 10.460870212974633 +5 2020-01-01 00:00:05 10.455878619067724 +6 2020-01-01 00:00:06 10.450362054646963 +7 2020-01-01 00:00:07 10.444265308081446 +8 2020-01-01 00:00:08 10.437527361082362 +9 2020-01-01 00:00:09 10.430080778011437 +10 2020-01-01 00:00:10 10.421851030962417 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 3475391510808484800 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedCount_check_over_ORDER_BY_id_RANGE_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 6845134164707796627 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_DateTime_time_column = r""" +id time w +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2 +3 2020-01-01 00:00:03 3 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 5 +6 2020-01-01 00:00:06 6 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 8 +9 2020-01-01 00:00:09 9 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_DateTime64_time_column = r""" +id time w +0 2020-01-01 00:00:00.000 0 +1 2020-01-01 00:00:01.000 1 +2 2020-01-01 00:00:02.000 2 +3 2020-01-01 00:00:03.000 3 +4 2020-01-01 00:00:04.000 4 +5 2020-01-01 00:00:05.000 5 +6 2020-01-01 00:00:06.000 6 +7 2020-01-01 00:00:07.000 7 +8 2020-01-01 00:00:08.000 8 +9 2020-01-01 00:00:09.000 9 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_DateTimeTZ_time_column = r""" +id time w +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2 +3 2020-01-01 00:00:03 3 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 5 +6 2020-01-01 00:00:06 6 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 8 +9 2020-01-01 00:00:09 9 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_DateTime64TZ_time_column = r""" +id time w +0 2020-01-01 00:00:00.000000000 0 +1 2020-01-01 00:00:01.000000000 1 +2 2020-01-01 00:00:02.000000000 2 +3 2020-01-01 00:00:03.000000000 3 +4 2020-01-01 00:00:04.000000000 4 +5 2020-01-01 00:00:05.000000000 5 +6 2020-01-01 00:00:06.000000000 6 +7 2020-01-01 00:00:07.000000000 7 +8 2020-01-01 00:00:08.000000000 8 +9 2020-01-01 00:00:09.000000000 9 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_decay_length_with_zero = r""" +id time w +0 2020-01-01 00:00:00 nan +1 2020-01-01 00:00:01 nan +2 2020-01-01 00:00:02 nan +3 2020-01-01 00:00:03 nan +4 2020-01-01 00:00:04 nan +5 2020-01-01 00:00:05 nan +6 2020-01-01 00:00:06 nan +7 2020-01-01 00:00:07 nan +8 2020-01-01 00:00:08 nan +9 2020-01-01 00:00:09 nan +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_decay_length_with_negative = r""" +id time w +0 2020-01-01 00:00:00 21982.456886794735 +1 2020-01-01 00:00:01 21982.456886794735 +2 2020-01-01 00:00:02 21982.456886794735 +3 2020-01-01 00:00:03 21982.456886794735 +4 2020-01-01 00:00:04 21982.456886794735 +5 2020-01-01 00:00:05 21982.456886794735 +6 2020-01-01 00:00:06 21982.456886794735 +7 2020-01-01 00:00:07 21982.456886794735 +8 2020-01-01 00:00:08 21982.456886794735 +9 2020-01-01 00:00:09 21982.456886794735 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_input_value_with_0 = r""" +id time w +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 0 +2 2020-01-01 00:00:02 0 +3 2020-01-01 00:00:03 0 +4 2020-01-01 00:00:04 0 +5 2020-01-01 00:00:05 0 +6 2020-01-01 00:00:06 0 +7 2020-01-01 00:00:07 0 +8 2020-01-01 00:00:08 0 +9 2020-01-01 00:00:09 0 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_input_time_with_0 = r""" +id time w +0 2020-01-01 00:00:00 9999 +1 2020-01-01 00:00:01 9999 +2 2020-01-01 00:00:02 9999 +3 2020-01-01 00:00:03 9999 +4 2020-01-01 00:00:04 9999 +5 2020-01-01 00:00:05 9999 +6 2020-01-01 00:00:06 9999 +7 2020-01-01 00:00:07 9999 +8 2020-01-01 00:00:08 9999 +9 2020-01-01 00:00:09 9999 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_decay_length_overflow = r""" +id time w +0 2020-01-01 00:00:00 9999 +1 2020-01-01 00:00:01 9999 +2 2020-01-01 00:00:02 9999 +3 2020-01-01 00:00:03 9999 +4 2020-01-01 00:00:04 9999 +5 2020-01-01 00:00:05 9999 +6 2020-01-01 00:00:06 9999 +7 2020-01-01 00:00:07 9999 +8 2020-01-01 00:00:08 9999 +9 2020-01-01 00:00:09 9999 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_input_value_with_Int8_type = r""" +id time exponentialTimeDecayedMax(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 3 +1 1970-01-01 03:00:02 3 +2 1970-01-01 03:00:03 3 +3 1970-01-01 03:00:03 3 +3 1970-01-01 03:00:05 3 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_input_value_with_Int16_type = r""" +id time exponentialTimeDecayedMax(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 3 +1 1970-01-01 03:00:02 3 +2 1970-01-01 03:00:03 3 +3 1970-01-01 03:00:03 3 +3 1970-01-01 03:00:05 3 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_input_value_with_Int32_type = r""" +id time exponentialTimeDecayedMax(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 3 +1 1970-01-01 03:00:02 3 +2 1970-01-01 03:00:03 3 +3 1970-01-01 03:00:03 3 +3 1970-01-01 03:00:05 3 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_input_value_with_Int64_type = r""" +id time exponentialTimeDecayedMax(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 3 +1 1970-01-01 03:00:02 3 +2 1970-01-01 03:00:03 3 +3 1970-01-01 03:00:03 3 +3 1970-01-01 03:00:05 3 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_input_value_with_UInt8_type = r""" +id time exponentialTimeDecayedMax(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 3 +1 1970-01-01 03:00:02 3 +2 1970-01-01 03:00:03 3 +3 1970-01-01 03:00:03 3 +3 1970-01-01 03:00:05 3 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_input_value_with_UInt16_type = r""" +id time exponentialTimeDecayedMax(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 3 +1 1970-01-01 03:00:02 3 +2 1970-01-01 03:00:03 3 +3 1970-01-01 03:00:03 3 +3 1970-01-01 03:00:05 3 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_input_value_with_UInt32_type = r""" +id time exponentialTimeDecayedMax(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 3 +1 1970-01-01 03:00:02 3 +2 1970-01-01 03:00:03 3 +3 1970-01-01 03:00:03 3 +3 1970-01-01 03:00:05 3 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_input_value_with_UInt64_type = r""" +id time exponentialTimeDecayedMax(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 3 +1 1970-01-01 03:00:02 3 +2 1970-01-01 03:00:03 3 +3 1970-01-01 03:00:03 3 +3 1970-01-01 03:00:05 3 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_input_value_with_Float32_type = r""" +id time exponentialTimeDecayedMax(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 3 +1 1970-01-01 03:00:02 3 +2 1970-01-01 03:00:03 3 +3 1970-01-01 03:00:03 3 +3 1970-01-01 03:00:05 3 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_input_value_with_Float64_type = r""" +id time exponentialTimeDecayedMax(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 3 +1 1970-01-01 03:00:02 3 +2 1970-01-01 03:00:03 3 +3 1970-01-01 03:00:03 3 +3 1970-01-01 03:00:05 3 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_input_value_with_Decimal32_4__type = r""" +id time exponentialTimeDecayedMax(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 3 +1 1970-01-01 03:00:02 3 +2 1970-01-01 03:00:03 3 +3 1970-01-01 03:00:03 3 +3 1970-01-01 03:00:05 3 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_input_value_with_Decimal64_4__type = r""" +id time exponentialTimeDecayedMax(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 3 +1 1970-01-01 03:00:02 3 +2 1970-01-01 03:00:03 3 +3 1970-01-01 03:00:03 3 +3 1970-01-01 03:00:05 3 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_input_value_with_Decimal128_4__type = r""" +id time exponentialTimeDecayedMax(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 3 +1 1970-01-01 03:00:02 3 +2 1970-01-01 03:00:03 3 +3 1970-01-01 03:00:03 3 +3 1970-01-01 03:00:05 3 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_toUInt32_decay_length = r""" +id time w +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2 +3 2020-01-01 00:00:03 3 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 5 +6 2020-01-01 00:00:06 6 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 8 +9 2020-01-01 00:00:09 9 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_toUInt64_decay_length = r""" +id time w +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2 +3 2020-01-01 00:00:03 3 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 5 +6 2020-01-01 00:00:06 6 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 8 +9 2020-01-01 00:00:09 9 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_toFloat32_decay_length = r""" +id time w +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2 +3 2020-01-01 00:00:03 3 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 5 +6 2020-01-01 00:00:06 6 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 8 +9 2020-01-01 00:00:09 9 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_toFloat64_decay_length = r""" +id time w +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2 +3 2020-01-01 00:00:03 3 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 5 +6 2020-01-01 00:00:06 6 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 8 +9 2020-01-01 00:00:09 9 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_one_row_partitions = r""" +id time w +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2 +3 2020-01-01 00:00:03 3 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 5 +6 2020-01-01 00:00:06 6 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 8 +9 2020-01-01 00:00:09 9 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check__1_time_gap = r""" +id time exponentialTimeDecayedMax(1)(id, time) OVER () +1 2022-01-01 00:00:00 10.87312731383618 +2 2022-01-01 00:00:00 10.87312731383618 +3 2022-01-01 00:00:00 10.87312731383618 +4 2022-01-01 00:00:01 10.87312731383618 +5 2022-01-01 00:00:00 10.87312731383618 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_0_time_gap = r""" +id time exponentialTimeDecayedMax(1)(id, time) OVER () +1 2022-01-01 00:00:00 2 +2 2022-01-01 00:00:00 2 +-3 2022-01-01 00:00:00 2 +-4 2022-01-01 00:00:00 2 +-5 2022-01-01 00:00:00 2 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 5 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 8 +10 2020-01-01 00:00:10 10 +11 2020-01-01 00:00:11 11 +13 2020-01-01 00:00:13 13 +14 2020-01-01 00:00:14 14 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2 +3 2020-01-01 00:00:03 3 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 5 +6 2020-01-01 00:00:06 6 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 8 +9 2020-01-01 00:00:09 9 +10 2020-01-01 00:00:10 10 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 6912631249882696577 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 16831297400999588778 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 9998 +2 2020-01-01 00:00:02 9998 +4 2020-01-01 00:00:04 9998 +5 2020-01-01 00:00:05 9998 +7 2020-01-01 00:00:07 9998 +8 2020-01-01 00:00:08 9998 +10 2020-01-01 00:00:10 9998 +11 2020-01-01 00:00:11 9998 +13 2020-01-01 00:00:13 9998 +14 2020-01-01 00:00:14 9998 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 58 +2 2020-01-01 00:00:02 58 +3 2020-01-01 00:00:03 58 +4 2020-01-01 00:00:04 58 +5 2020-01-01 00:00:05 58 +6 2020-01-01 00:00:06 58 +7 2020-01-01 00:00:07 58 +8 2020-01-01 00:00:08 58 +9 2020-01-01 00:00:09 58 +10 2020-01-01 00:00:10 58 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 7998450438310151220 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 1032201993393357875 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 2 +2 2020-01-01 00:00:02 4 +4 2020-01-01 00:00:04 5 +5 2020-01-01 00:00:05 7 +7 2020-01-01 00:00:07 8 +8 2020-01-01 00:00:08 10 +10 2020-01-01 00:00:10 11 +11 2020-01-01 00:00:11 13 +13 2020-01-01 00:00:13 14 +14 2020-01-01 00:00:14 16 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 2 +2 2020-01-01 00:00:02 3 +3 2020-01-01 00:00:03 4 +4 2020-01-01 00:00:04 5 +5 2020-01-01 00:00:05 6 +6 2020-01-01 00:00:06 7 +7 2020-01-01 00:00:07 8 +8 2020-01-01 00:00:08 9 +9 2020-01-01 00:00:09 10 +10 2020-01-01 00:00:10 11 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 8970933505069997234 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 1920552523620768063 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 5 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 8 +10 2020-01-01 00:00:10 10 +11 2020-01-01 00:00:11 11 +13 2020-01-01 00:00:13 13 +14 2020-01-01 00:00:14 14 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2 +3 2020-01-01 00:00:03 3 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 5 +6 2020-01-01 00:00:06 6 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 8 +9 2020-01-01 00:00:09 9 +10 2020-01-01 00:00:10 10 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 6912631249882696577 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 16831297400999588778 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 nan +2 2020-01-01 00:00:02 1 +4 2020-01-01 00:00:04 2 +5 2020-01-01 00:00:05 4 +7 2020-01-01 00:00:07 5 +8 2020-01-01 00:00:08 7 +10 2020-01-01 00:00:10 8 +11 2020-01-01 00:00:11 10 +13 2020-01-01 00:00:13 11 +14 2020-01-01 00:00:14 13 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 nan +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 2 +4 2020-01-01 00:00:04 3 +5 2020-01-01 00:00:05 4 +6 2020-01-01 00:00:06 5 +7 2020-01-01 00:00:07 6 +8 2020-01-01 00:00:08 7 +9 2020-01-01 00:00:09 8 +10 2020-01-01 00:00:10 9 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 13452262558908146813 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 13709057447670281832 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 9998 +2 2020-01-01 00:00:02 9998 +4 2020-01-01 00:00:04 9998 +5 2020-01-01 00:00:05 9998 +7 2020-01-01 00:00:07 9998 +8 2020-01-01 00:00:08 9998 +10 2020-01-01 00:00:10 9998 +11 2020-01-01 00:00:11 9998 +13 2020-01-01 00:00:13 9998 +14 2020-01-01 00:00:14 9998 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 58 +2 2020-01-01 00:00:02 58 +3 2020-01-01 00:00:03 58 +4 2020-01-01 00:00:04 58 +5 2020-01-01 00:00:05 58 +6 2020-01-01 00:00:06 58 +7 2020-01-01 00:00:07 58 +8 2020-01-01 00:00:08 58 +9 2020-01-01 00:00:09 58 +10 2020-01-01 00:00:10 58 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 7998450438310151220 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 1032201993393357875 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 5 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 8 +10 2020-01-01 00:00:10 10 +11 2020-01-01 00:00:11 11 +13 2020-01-01 00:00:13 13 +14 2020-01-01 00:00:14 14 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2 +3 2020-01-01 00:00:03 3 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 5 +6 2020-01-01 00:00:06 6 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 8 +9 2020-01-01 00:00:09 9 +10 2020-01-01 00:00:10 10 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 6912631249882696577 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 16831297400999588778 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 9998 +2 2020-01-01 00:00:02 9998 +4 2020-01-01 00:00:04 9998 +5 2020-01-01 00:00:05 9998 +7 2020-01-01 00:00:07 9998 +8 2020-01-01 00:00:08 9998 +10 2020-01-01 00:00:10 9998 +11 2020-01-01 00:00:11 9998 +13 2020-01-01 00:00:13 9998 +14 2020-01-01 00:00:14 9998 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 58 +2 2020-01-01 00:00:02 58 +3 2020-01-01 00:00:03 58 +4 2020-01-01 00:00:04 58 +5 2020-01-01 00:00:05 58 +6 2020-01-01 00:00:06 58 +7 2020-01-01 00:00:07 58 +8 2020-01-01 00:00:08 58 +9 2020-01-01 00:00:09 58 +10 2020-01-01 00:00:10 58 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 7998450438310151220 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 1032201993393357875 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 2 +2 2020-01-01 00:00:02 4 +4 2020-01-01 00:00:04 5 +5 2020-01-01 00:00:05 7 +7 2020-01-01 00:00:07 8 +8 2020-01-01 00:00:08 10 +10 2020-01-01 00:00:10 11 +11 2020-01-01 00:00:11 13 +13 2020-01-01 00:00:13 14 +14 2020-01-01 00:00:14 16 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 2 +2 2020-01-01 00:00:02 3 +3 2020-01-01 00:00:03 4 +4 2020-01-01 00:00:04 5 +5 2020-01-01 00:00:05 6 +6 2020-01-01 00:00:06 7 +7 2020-01-01 00:00:07 8 +8 2020-01-01 00:00:08 9 +9 2020-01-01 00:00:09 10 +10 2020-01-01 00:00:10 11 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 8970933505069997234 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 1920552523620768063 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 9998 +2 2020-01-01 00:00:02 9998 +4 2020-01-01 00:00:04 9998 +5 2020-01-01 00:00:05 9998 +7 2020-01-01 00:00:07 9998 +8 2020-01-01 00:00:08 9998 +10 2020-01-01 00:00:10 9998 +11 2020-01-01 00:00:11 9998 +13 2020-01-01 00:00:13 9998 +14 2020-01-01 00:00:14 9998 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 58 +2 2020-01-01 00:00:02 58 +3 2020-01-01 00:00:03 58 +4 2020-01-01 00:00:04 58 +5 2020-01-01 00:00:05 58 +6 2020-01-01 00:00:06 58 +7 2020-01-01 00:00:07 58 +8 2020-01-01 00:00:08 58 +9 2020-01-01 00:00:09 58 +10 2020-01-01 00:00:10 58 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 13451113004697527667 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ROWS_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 10806118902687505865 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 9998 +2 2020-01-01 00:00:02 9998 +4 2020-01-01 00:00:04 9998 +5 2020-01-01 00:00:05 9998 +7 2020-01-01 00:00:07 9998 +8 2020-01-01 00:00:08 9998 +10 2020-01-01 00:00:10 9998 +11 2020-01-01 00:00:11 9998 +13 2020-01-01 00:00:13 9998 +14 2020-01-01 00:00:14 9998 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 58 +2 2020-01-01 00:00:02 58 +3 2020-01-01 00:00:03 58 +4 2020-01-01 00:00:04 58 +5 2020-01-01 00:00:05 58 +6 2020-01-01 00:00:06 58 +7 2020-01-01 00:00:07 58 +8 2020-01-01 00:00:08 58 +9 2020-01-01 00:00:09 58 +10 2020-01-01 00:00:10 58 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 7998450438310151220 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 1032201993393357875 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 9998 +2 2020-01-01 00:00:02 9998 +4 2020-01-01 00:00:04 9998 +5 2020-01-01 00:00:05 9998 +7 2020-01-01 00:00:07 9998 +8 2020-01-01 00:00:08 9998 +10 2020-01-01 00:00:10 9998 +11 2020-01-01 00:00:11 9998 +13 2020-01-01 00:00:13 9998 +14 2020-01-01 00:00:14 9998 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 58 +2 2020-01-01 00:00:02 58 +3 2020-01-01 00:00:03 58 +4 2020-01-01 00:00:04 58 +5 2020-01-01 00:00:05 58 +6 2020-01-01 00:00:06 58 +7 2020-01-01 00:00:07 58 +8 2020-01-01 00:00:08 58 +9 2020-01-01 00:00:09 58 +10 2020-01-01 00:00:10 58 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 7998450438310151220 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 1032201993393357875 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 9998 +2 2020-01-01 00:00:02 9998 +4 2020-01-01 00:00:04 9998 +5 2020-01-01 00:00:05 9998 +7 2020-01-01 00:00:07 9998 +8 2020-01-01 00:00:08 9998 +10 2020-01-01 00:00:10 9998 +11 2020-01-01 00:00:11 9998 +13 2020-01-01 00:00:13 9998 +14 2020-01-01 00:00:14 9998 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 58 +2 2020-01-01 00:00:02 58 +3 2020-01-01 00:00:03 58 +4 2020-01-01 00:00:04 58 +5 2020-01-01 00:00:05 58 +6 2020-01-01 00:00:06 58 +7 2020-01-01 00:00:07 58 +8 2020-01-01 00:00:08 58 +9 2020-01-01 00:00:09 58 +10 2020-01-01 00:00:10 58 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 7998450438310151220 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 1032201993393357875 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 9998 +2 2020-01-01 00:00:02 9998 +4 2020-01-01 00:00:04 9998 +5 2020-01-01 00:00:05 9998 +7 2020-01-01 00:00:07 9998 +8 2020-01-01 00:00:08 9998 +10 2020-01-01 00:00:10 9998 +11 2020-01-01 00:00:11 9998 +13 2020-01-01 00:00:13 9998 +14 2020-01-01 00:00:14 9998 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 58 +2 2020-01-01 00:00:02 58 +3 2020-01-01 00:00:03 58 +4 2020-01-01 00:00:04 58 +5 2020-01-01 00:00:05 58 +6 2020-01-01 00:00:06 58 +7 2020-01-01 00:00:07 58 +8 2020-01-01 00:00:08 58 +9 2020-01-01 00:00:09 58 +10 2020-01-01 00:00:10 58 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 7998450438310151220 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 1032201993393357875 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 5 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 8 +10 2020-01-01 00:00:10 10 +11 2020-01-01 00:00:11 11 +13 2020-01-01 00:00:13 13 +14 2020-01-01 00:00:14 14 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2 +3 2020-01-01 00:00:03 3 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 5 +6 2020-01-01 00:00:06 6 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 8 +9 2020-01-01 00:00:09 9 +10 2020-01-01 00:00:10 10 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 6912631249882696577 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 16831297400999588778 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 9998 +2 2020-01-01 00:00:02 9998 +4 2020-01-01 00:00:04 9998 +5 2020-01-01 00:00:05 9998 +7 2020-01-01 00:00:07 9998 +8 2020-01-01 00:00:08 9998 +10 2020-01-01 00:00:10 9998 +11 2020-01-01 00:00:11 9998 +13 2020-01-01 00:00:13 9998 +14 2020-01-01 00:00:14 9998 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 58 +2 2020-01-01 00:00:02 58 +3 2020-01-01 00:00:03 58 +4 2020-01-01 00:00:04 58 +5 2020-01-01 00:00:05 58 +6 2020-01-01 00:00:06 58 +7 2020-01-01 00:00:07 58 +8 2020-01-01 00:00:08 58 +9 2020-01-01 00:00:09 58 +10 2020-01-01 00:00:10 58 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 7998450438310151220 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 1032201993393357875 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 2 +2 2020-01-01 00:00:02 2 +4 2020-01-01 00:00:04 5 +5 2020-01-01 00:00:05 5 +7 2020-01-01 00:00:07 8 +8 2020-01-01 00:00:08 8 +10 2020-01-01 00:00:10 11 +11 2020-01-01 00:00:11 11 +13 2020-01-01 00:00:13 14 +14 2020-01-01 00:00:14 14 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 2 +2 2020-01-01 00:00:02 3 +3 2020-01-01 00:00:03 4 +4 2020-01-01 00:00:04 5 +5 2020-01-01 00:00:05 6 +6 2020-01-01 00:00:06 7 +7 2020-01-01 00:00:07 8 +8 2020-01-01 00:00:08 9 +9 2020-01-01 00:00:09 10 +10 2020-01-01 00:00:10 11 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 8970933505069997234 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 16812556211937367765 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 5 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 8 +10 2020-01-01 00:00:10 10 +11 2020-01-01 00:00:11 11 +13 2020-01-01 00:00:13 13 +14 2020-01-01 00:00:14 14 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2 +3 2020-01-01 00:00:03 3 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 5 +6 2020-01-01 00:00:06 6 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 8 +9 2020-01-01 00:00:09 9 +10 2020-01-01 00:00:10 10 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 6912631249882696577 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 16831297400999588778 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 nan +2 2020-01-01 00:00:02 1 +4 2020-01-01 00:00:04 2 +5 2020-01-01 00:00:05 4 +7 2020-01-01 00:00:07 5 +8 2020-01-01 00:00:08 7 +10 2020-01-01 00:00:10 8 +11 2020-01-01 00:00:11 10 +13 2020-01-01 00:00:13 11 +14 2020-01-01 00:00:14 13 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 nan +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 2 +4 2020-01-01 00:00:04 3 +5 2020-01-01 00:00:05 4 +6 2020-01-01 00:00:06 5 +7 2020-01-01 00:00:07 6 +8 2020-01-01 00:00:08 7 +9 2020-01-01 00:00:09 8 +10 2020-01-01 00:00:10 9 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 13452262558908146813 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 13709057447670281832 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 9998 +2 2020-01-01 00:00:02 9998 +4 2020-01-01 00:00:04 9998 +5 2020-01-01 00:00:05 9998 +7 2020-01-01 00:00:07 9998 +8 2020-01-01 00:00:08 9998 +10 2020-01-01 00:00:10 9998 +11 2020-01-01 00:00:11 9998 +13 2020-01-01 00:00:13 9998 +14 2020-01-01 00:00:14 9998 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 58 +2 2020-01-01 00:00:02 58 +3 2020-01-01 00:00:03 58 +4 2020-01-01 00:00:04 58 +5 2020-01-01 00:00:05 58 +6 2020-01-01 00:00:06 58 +7 2020-01-01 00:00:07 58 +8 2020-01-01 00:00:08 58 +9 2020-01-01 00:00:09 58 +10 2020-01-01 00:00:10 58 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 7998450438310151220 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 1032201993393357875 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 5 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 8 +10 2020-01-01 00:00:10 10 +11 2020-01-01 00:00:11 11 +13 2020-01-01 00:00:13 13 +14 2020-01-01 00:00:14 14 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2 +3 2020-01-01 00:00:03 3 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 5 +6 2020-01-01 00:00:06 6 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 8 +9 2020-01-01 00:00:09 9 +10 2020-01-01 00:00:10 10 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 6912631249882696577 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 16831297400999588778 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 9998 +2 2020-01-01 00:00:02 9998 +4 2020-01-01 00:00:04 9998 +5 2020-01-01 00:00:05 9998 +7 2020-01-01 00:00:07 9998 +8 2020-01-01 00:00:08 9998 +10 2020-01-01 00:00:10 9998 +11 2020-01-01 00:00:11 9998 +13 2020-01-01 00:00:13 9998 +14 2020-01-01 00:00:14 9998 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 58 +2 2020-01-01 00:00:02 58 +3 2020-01-01 00:00:03 58 +4 2020-01-01 00:00:04 58 +5 2020-01-01 00:00:05 58 +6 2020-01-01 00:00:06 58 +7 2020-01-01 00:00:07 58 +8 2020-01-01 00:00:08 58 +9 2020-01-01 00:00:09 58 +10 2020-01-01 00:00:10 58 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 7998450438310151220 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 1032201993393357875 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 2 +2 2020-01-01 00:00:02 2 +4 2020-01-01 00:00:04 5 +5 2020-01-01 00:00:05 5 +7 2020-01-01 00:00:07 8 +8 2020-01-01 00:00:08 8 +10 2020-01-01 00:00:10 11 +11 2020-01-01 00:00:11 11 +13 2020-01-01 00:00:13 14 +14 2020-01-01 00:00:14 14 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 2 +2 2020-01-01 00:00:02 3 +3 2020-01-01 00:00:03 4 +4 2020-01-01 00:00:04 5 +5 2020-01-01 00:00:05 6 +6 2020-01-01 00:00:06 7 +7 2020-01-01 00:00:07 8 +8 2020-01-01 00:00:08 9 +9 2020-01-01 00:00:09 10 +10 2020-01-01 00:00:10 11 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 8970933505069997234 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 16812556211937367765 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 9998 +2 2020-01-01 00:00:02 9998 +4 2020-01-01 00:00:04 9998 +5 2020-01-01 00:00:05 9998 +7 2020-01-01 00:00:07 9998 +8 2020-01-01 00:00:08 9998 +10 2020-01-01 00:00:10 9998 +11 2020-01-01 00:00:11 9998 +13 2020-01-01 00:00:13 9998 +14 2020-01-01 00:00:14 9998 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 58 +2 2020-01-01 00:00:02 58 +3 2020-01-01 00:00:03 58 +4 2020-01-01 00:00:04 58 +5 2020-01-01 00:00:05 58 +6 2020-01-01 00:00:06 58 +7 2020-01-01 00:00:07 58 +8 2020-01-01 00:00:08 58 +9 2020-01-01 00:00:09 58 +10 2020-01-01 00:00:10 58 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 13451113004697527667 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedMax_check_over_ORDER_BY_id_RANGE_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 10806118902687505865 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_DateTime_time_column = r""" +id time w +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2.3678794411714423 +3 2020-01-01 00:00:03 3.8710941655794975 +4 2020-01-01 00:00:04 5.424095958355417 +5 2020-01-01 00:00:05 6.99541339002007 +6 2020-01-01 00:00:06 8.573468768683808 +7 2020-01-01 00:00:07 10.154002899524214 +8 2020-01-01 00:00:08 11.735448912330172 +9 2020-01-01 00:00:09 13.317230387764035 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_DateTime64_time_column = r""" +id time w +0 2020-01-01 00:00:00.000 0 +1 2020-01-01 00:00:01.000 1 +2 2020-01-01 00:00:02.000 2.3678794411714423 +3 2020-01-01 00:00:03.000 3.8710941655794975 +4 2020-01-01 00:00:04.000 5.424095958355417 +5 2020-01-01 00:00:05.000 6.99541339002007 +6 2020-01-01 00:00:06.000 8.573468768683808 +7 2020-01-01 00:00:07.000 10.154002899524214 +8 2020-01-01 00:00:08.000 11.735448912330172 +9 2020-01-01 00:00:09.000 13.317230387764035 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_DateTimeTZ_time_column = r""" +id time w +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2.3678794411714423 +3 2020-01-01 00:00:03 3.8710941655794975 +4 2020-01-01 00:00:04 5.424095958355417 +5 2020-01-01 00:00:05 6.99541339002007 +6 2020-01-01 00:00:06 8.573468768683808 +7 2020-01-01 00:00:07 10.154002899524214 +8 2020-01-01 00:00:08 11.735448912330172 +9 2020-01-01 00:00:09 13.317230387764035 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_DateTime64TZ_time_column = r""" +id time w +0 2020-01-01 00:00:00.000000000 0 +1 2020-01-01 00:00:01.000000000 1 +2 2020-01-01 00:00:02.000000000 2.3678794411714423 +3 2020-01-01 00:00:03.000000000 3.8710941655794975 +4 2020-01-01 00:00:04.000000000 5.424095958355417 +5 2020-01-01 00:00:05.000000000 6.99541339002007 +6 2020-01-01 00:00:06.000000000 8.573468768683808 +7 2020-01-01 00:00:07.000000000 10.154002899524214 +8 2020-01-01 00:00:08.000000000 11.735448912330172 +9 2020-01-01 00:00:09.000000000 13.317230387764035 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_decay_length_with_zero = r""" +id time w +0 2020-01-01 00:00:00 nan +1 2020-01-01 00:00:01 nan +2 2020-01-01 00:00:02 nan +3 2020-01-01 00:00:03 nan +4 2020-01-01 00:00:04 nan +5 2020-01-01 00:00:05 nan +6 2020-01-01 00:00:06 nan +7 2020-01-01 00:00:07 nan +8 2020-01-01 00:00:08 nan +9 2020-01-01 00:00:09 nan +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_decay_length_with_negative = r""" +id time w +0 2020-01-01 00:00:00 21993454503.620567 +1 2020-01-01 00:00:01 21993454503.620567 +2 2020-01-01 00:00:02 21993454503.620567 +3 2020-01-01 00:00:03 21993454503.620567 +4 2020-01-01 00:00:04 21993454503.620567 +5 2020-01-01 00:00:05 21993454503.620567 +6 2020-01-01 00:00:06 21993454503.620567 +7 2020-01-01 00:00:07 21993454503.620567 +8 2020-01-01 00:00:08 21993454503.620567 +9 2020-01-01 00:00:09 21993454503.620567 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_input_value_with_0 = r""" +id time w +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 0 +2 2020-01-01 00:00:02 0 +3 2020-01-01 00:00:03 0 +4 2020-01-01 00:00:04 0 +5 2020-01-01 00:00:05 0 +6 2020-01-01 00:00:06 0 +7 2020-01-01 00:00:07 0 +8 2020-01-01 00:00:08 0 +9 2020-01-01 00:00:09 0 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_input_time_with_0 = r""" +id time w +0 2020-01-01 00:00:00 49995000 +1 2020-01-01 00:00:01 49995000 +2 2020-01-01 00:00:02 49995000 +3 2020-01-01 00:00:03 49995000 +4 2020-01-01 00:00:04 49995000 +5 2020-01-01 00:00:05 49995000 +6 2020-01-01 00:00:06 49995000 +7 2020-01-01 00:00:07 49995000 +8 2020-01-01 00:00:08 49995000 +9 2020-01-01 00:00:09 49995000 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_decay_length_overflow = r""" +id time w +0 2020-01-01 00:00:00 49995000 +1 2020-01-01 00:00:01 49995000 +2 2020-01-01 00:00:02 49995000 +3 2020-01-01 00:00:03 49995000 +4 2020-01-01 00:00:04 49995000 +5 2020-01-01 00:00:05 49995000 +6 2020-01-01 00:00:06 49995000 +7 2020-01-01 00:00:07 49995000 +8 2020-01-01 00:00:08 49995000 +9 2020-01-01 00:00:09 49995000 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_input_value_with_Int8_type = r""" +id time exponentialTimeDecayedSum(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 5.432501378740614 +1 1970-01-01 03:00:02 5.432501378740614 +2 1970-01-01 03:00:03 5.432501378740614 +3 1970-01-01 03:00:03 5.432501378740614 +3 1970-01-01 03:00:05 5.432501378740614 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_input_value_with_Int16_type = r""" +id time exponentialTimeDecayedSum(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 5.432501378740614 +1 1970-01-01 03:00:02 5.432501378740614 +2 1970-01-01 03:00:03 5.432501378740614 +3 1970-01-01 03:00:03 5.432501378740614 +3 1970-01-01 03:00:05 5.432501378740614 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_input_value_with_Int32_type = r""" +id time exponentialTimeDecayedSum(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 5.432501378740614 +1 1970-01-01 03:00:02 5.432501378740614 +2 1970-01-01 03:00:03 5.432501378740614 +3 1970-01-01 03:00:03 5.432501378740614 +3 1970-01-01 03:00:05 5.432501378740614 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_input_value_with_Int64_type = r""" +id time exponentialTimeDecayedSum(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 5.432501378740614 +1 1970-01-01 03:00:02 5.432501378740614 +2 1970-01-01 03:00:03 5.432501378740614 +3 1970-01-01 03:00:03 5.432501378740614 +3 1970-01-01 03:00:05 5.432501378740614 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_input_value_with_UInt8_type = r""" +id time exponentialTimeDecayedSum(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 5.432501378740614 +1 1970-01-01 03:00:02 5.432501378740614 +2 1970-01-01 03:00:03 5.432501378740614 +3 1970-01-01 03:00:03 5.432501378740614 +3 1970-01-01 03:00:05 5.432501378740614 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_input_value_with_UInt16_type = r""" +id time exponentialTimeDecayedSum(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 5.432501378740614 +1 1970-01-01 03:00:02 5.432501378740614 +2 1970-01-01 03:00:03 5.432501378740614 +3 1970-01-01 03:00:03 5.432501378740614 +3 1970-01-01 03:00:05 5.432501378740614 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_input_value_with_UInt32_type = r""" +id time exponentialTimeDecayedSum(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 5.432501378740614 +1 1970-01-01 03:00:02 5.432501378740614 +2 1970-01-01 03:00:03 5.432501378740614 +3 1970-01-01 03:00:03 5.432501378740614 +3 1970-01-01 03:00:05 5.432501378740614 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_input_value_with_UInt64_type = r""" +id time exponentialTimeDecayedSum(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 5.432501378740614 +1 1970-01-01 03:00:02 5.432501378740614 +2 1970-01-01 03:00:03 5.432501378740614 +3 1970-01-01 03:00:03 5.432501378740614 +3 1970-01-01 03:00:05 5.432501378740614 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_input_value_with_Float32_type = r""" +id time exponentialTimeDecayedSum(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 5.432501378740614 +1 1970-01-01 03:00:02 5.432501378740614 +2 1970-01-01 03:00:03 5.432501378740614 +3 1970-01-01 03:00:03 5.432501378740614 +3 1970-01-01 03:00:05 5.432501378740614 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_input_value_with_Float64_type = r""" +id time exponentialTimeDecayedSum(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 5.432501378740614 +1 1970-01-01 03:00:02 5.432501378740614 +2 1970-01-01 03:00:03 5.432501378740614 +3 1970-01-01 03:00:03 5.432501378740614 +3 1970-01-01 03:00:05 5.432501378740614 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_input_value_with_Decimal32_4__type = r""" +id time exponentialTimeDecayedSum(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 5.432501378740614 +1 1970-01-01 03:00:02 5.432501378740614 +2 1970-01-01 03:00:03 5.432501378740614 +3 1970-01-01 03:00:03 5.432501378740614 +3 1970-01-01 03:00:05 5.432501378740614 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_input_value_with_Decimal64_4__type = r""" +id time exponentialTimeDecayedSum(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 5.432501378740614 +1 1970-01-01 03:00:02 5.432501378740614 +2 1970-01-01 03:00:03 5.432501378740614 +3 1970-01-01 03:00:03 5.432501378740614 +3 1970-01-01 03:00:05 5.432501378740614 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_input_value_with_Decimal128_4__type = r""" +id time exponentialTimeDecayedSum(2.2)(id, time) OVER () +1 1970-01-01 03:00:01 5.432501378740614 +1 1970-01-01 03:00:02 5.432501378740614 +2 1970-01-01 03:00:03 5.432501378740614 +3 1970-01-01 03:00:03 5.432501378740614 +3 1970-01-01 03:00:05 5.432501378740614 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_toUInt32_decay_length = r""" +id time w +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2.9048374180359593 +3 2020-01-01 00:00:03 5.628405589149901 +4 2020-01-01 00:00:04 9.09279198094556 +5 2020-01-01 00:00:05 13.227498418776857 +6 2020-01-01 00:00:06 17.96873551632079 +7 2020-01-01 00:00:07 23.258784249958747 +8 2020-01-01 00:00:08 29.045418287388113 +9 2020-01-01 00:00:09 35.281381288934696 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_toUInt64_decay_length = r""" +id time w +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2.9048374180359593 +3 2020-01-01 00:00:03 5.628405589149901 +4 2020-01-01 00:00:04 9.09279198094556 +5 2020-01-01 00:00:05 13.227498418776857 +6 2020-01-01 00:00:06 17.96873551632079 +7 2020-01-01 00:00:07 23.258784249958747 +8 2020-01-01 00:00:08 29.045418287388113 +9 2020-01-01 00:00:09 35.281381288934696 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_toFloat32_decay_length = r""" +id time w +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2.909156442876713 +3 2020-01-01 00:00:03 5.6448783233776645 +4 2020-01-01 00:00:04 9.132077496953901 +5 2020-01-01 00:00:05 13.302487093205086 +6 2020-01-01 00:00:06 18.094041847071722 +7 2020-01-01 00:00:07 23.450314722946118 +8 2020-01-01 00:00:08 29.320004717853106 +9 2020-01-01 00:00:09 35.65647119441178 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_toFloat64_decay_length = r""" +id time w +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2.909156442876713 +3 2020-01-01 00:00:03 5.6448783233776645 +4 2020-01-01 00:00:04 9.132077496953901 +5 2020-01-01 00:00:05 13.302487093205086 +6 2020-01-01 00:00:06 18.094041847071722 +7 2020-01-01 00:00:07 23.450314722946118 +8 2020-01-01 00:00:08 29.320004717853106 +9 2020-01-01 00:00:09 35.65647119441178 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_one_row_partitions = r""" +id time w +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2 +3 2020-01-01 00:00:03 3 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 5 +6 2020-01-01 00:00:06 6 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 8 +9 2020-01-01 00:00:09 9 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check__1_time_gap = r""" +id time exponentialTimeDecayedSum(1)(id, time) OVER () +1 2022-01-01 00:00:00 21.87312731383618 +2 2022-01-01 00:00:00 21.87312731383618 +3 2022-01-01 00:00:00 21.87312731383618 +4 2022-01-01 00:00:01 21.87312731383618 +5 2022-01-01 00:00:00 21.87312731383618 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_0_time_gap = r""" +id time exponentialTimeDecayedSum(1)(id, time) OVER () +1 2022-01-01 00:00:00 -9 +2 2022-01-01 00:00:00 -9 +-3 2022-01-01 00:00:00 -9 +-4 2022-01-01 00:00:00 -9 +-5 2022-01-01 00:00:00 -9 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 5 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 8 +10 2020-01-01 00:00:10 10 +11 2020-01-01 00:00:11 11 +13 2020-01-01 00:00:13 13 +14 2020-01-01 00:00:14 14 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2 +3 2020-01-01 00:00:03 3 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 5 +6 2020-01-01 00:00:06 6 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 8 +9 2020-01-01 00:00:09 9 +10 2020-01-01 00:00:10 10 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 6912631249882696577 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 16831297400999588778 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 73413.05376916874 +2 2020-01-01 00:00:02 73413.05376916874 +4 2020-01-01 00:00:04 73413.05376916874 +5 2020-01-01 00:00:05 73413.05376916874 +7 2020-01-01 00:00:07 73413.05376916874 +8 2020-01-01 00:00:08 73413.05376916874 +10 2020-01-01 00:00:10 73413.05376916874 +11 2020-01-01 00:00:11 73413.05376916874 +13 2020-01-01 00:00:13 73413.05376916874 +14 2020-01-01 00:00:14 73413.05376916874 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 509.86904778455505 +2 2020-01-01 00:00:02 509.8657018190976 +3 2020-01-01 00:00:03 509.8583060916646 +4 2020-01-01 00:00:04 509.8460457773492 +5 2020-01-01 00:00:05 509.8279794535788 +6 2020-01-01 00:00:06 509.8030214840442 +7 2020-01-01 00:00:07 509.76992209751967 +8 2020-01-01 00:00:08 509.7272448715611 +9 2020-01-01 00:00:09 509.6733412955684 +10 2020-01-01 00:00:10 509.60632204793006 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 14732497617511578970 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 15259948609631945224 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 2.9048374180359593 +2 2020-01-01 00:00:02 5.637461506155963 +4 2020-01-01 00:00:04 8.619349672143837 +5 2020-01-01 00:00:05 11.093653765389908 +7 2020-01-01 00:00:07 14.333861926251714 +8 2020-01-01 00:00:08 16.549846024623854 +10 2020-01-01 00:00:10 20.048374180359595 +11 2020-01-01 00:00:11 22.006038283857798 +13 2020-01-01 00:00:13 25.762886434467468 +14 2020-01-01 00:00:14 27.46223054309174 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 2.9048374180359593 +2 2020-01-01 00:00:02 4.809674836071919 +3 2020-01-01 00:00:03 6.714512254107878 +4 2020-01-01 00:00:04 8.619349672143837 +5 2020-01-01 00:00:05 10.524187090179797 +6 2020-01-01 00:00:06 12.429024508215756 +7 2020-01-01 00:00:07 14.333861926251714 +8 2020-01-01 00:00:08 16.238699344287674 +9 2020-01-01 00:00:09 18.143536762323635 +10 2020-01-01 00:00:10 20.048374180359595 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 17864546820017591112 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 9697155262113833297 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2.9048374180359593 +4 2020-01-01 00:00:04 6.378279726837681 +5 2020-01-01 00:00:05 10.771306159542913 +7 2020-01-01 00:00:07 15.818799603636073 +8 2020-01-01 00:00:08 22.313441789782324 +10 2020-01-01 00:00:10 28.26870100031019 +11 2020-01-01 00:00:11 36.57857842435122 +13 2020-01-01 00:00:13 42.9480070598911 +14 2020-01-01 00:00:14 52.86096381786202 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2.9048374180359593 +3 2020-01-01 00:00:03 5.628405589149901 +4 2020-01-01 00:00:04 9.09279198094556 +5 2020-01-01 00:00:05 13.227498418776857 +6 2020-01-01 00:00:06 17.96873551632079 +7 2020-01-01 00:00:07 23.258784249958747 +8 2020-01-01 00:00:08 29.045418287388113 +9 2020-01-01 00:00:09 35.281381288934696 +10 2020-01-01 00:00:10 41.92391395022189 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 8177272607092946026 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 13193147715685283094 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 0 +2 2020-01-01 00:00:02 1 +4 2020-01-01 00:00:04 2.9048374180359593 +5 2020-01-01 00:00:05 6.378279726837681 +7 2020-01-01 00:00:07 10.771306159542913 +8 2020-01-01 00:00:08 15.818799603636073 +10 2020-01-01 00:00:10 22.313441789782324 +11 2020-01-01 00:00:11 28.26870100031019 +13 2020-01-01 00:00:13 36.57857842435122 +14 2020-01-01 00:00:14 42.9480070598911 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 0 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 2.9048374180359593 +4 2020-01-01 00:00:04 5.628405589149901 +5 2020-01-01 00:00:05 9.09279198094556 +6 2020-01-01 00:00:06 13.227498418776857 +7 2020-01-01 00:00:07 17.96873551632079 +8 2020-01-01 00:00:08 23.258784249958747 +9 2020-01-01 00:00:09 29.045418287388113 +10 2020-01-01 00:00:10 35.281381288934696 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 207461051412820560 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 7493948266116429797 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 73413.05376916874 +2 2020-01-01 00:00:02 73413.05376916874 +4 2020-01-01 00:00:04 73413.05376916874 +5 2020-01-01 00:00:05 73413.05376916874 +7 2020-01-01 00:00:07 73413.05376916874 +8 2020-01-01 00:00:08 73413.05376916874 +10 2020-01-01 00:00:10 73413.05376916874 +11 2020-01-01 00:00:11 73413.05376916874 +13 2020-01-01 00:00:13 73413.05376916874 +14 2020-01-01 00:00:14 73413.05376916874 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 509.86904778455505 +2 2020-01-01 00:00:02 509.86904778455505 +3 2020-01-01 00:00:03 509.86904778455505 +4 2020-01-01 00:00:04 509.86904778455505 +5 2020-01-01 00:00:05 509.86904778455505 +6 2020-01-01 00:00:06 509.86904778455505 +7 2020-01-01 00:00:07 509.86904778455505 +8 2020-01-01 00:00:08 509.86904778455505 +9 2020-01-01 00:00:09 509.86904778455505 +10 2020-01-01 00:00:10 509.86904778455505 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 5249260101878613678 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 4835566242294516990 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2.9048374180359593 +4 2020-01-01 00:00:04 5.637461506155963 +5 2020-01-01 00:00:05 8.619349672143837 +7 2020-01-01 00:00:07 11.093653765389908 +8 2020-01-01 00:00:08 14.333861926251714 +10 2020-01-01 00:00:10 16.549846024623854 +11 2020-01-01 00:00:11 20.048374180359595 +13 2020-01-01 00:00:13 22.006038283857798 +14 2020-01-01 00:00:14 25.762886434467468 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2.9048374180359593 +3 2020-01-01 00:00:03 4.809674836071919 +4 2020-01-01 00:00:04 6.714512254107878 +5 2020-01-01 00:00:05 8.619349672143837 +6 2020-01-01 00:00:06 10.524187090179797 +7 2020-01-01 00:00:07 12.429024508215756 +8 2020-01-01 00:00:08 14.333861926251714 +9 2020-01-01 00:00:09 16.238699344287674 +10 2020-01-01 00:00:10 18.143536762323635 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 4153748905038498474 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 15879506471867999892 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 73413.05376916874 +2 2020-01-01 00:00:02 73413.05376916874 +4 2020-01-01 00:00:04 73413.05376916874 +5 2020-01-01 00:00:05 73413.05376916874 +7 2020-01-01 00:00:07 73413.05376916874 +8 2020-01-01 00:00:08 73413.05376916874 +10 2020-01-01 00:00:10 73413.05376916874 +11 2020-01-01 00:00:11 73413.05376916874 +13 2020-01-01 00:00:13 73413.05376916874 +14 2020-01-01 00:00:14 73413.05376916874 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 509.86904778455505 +2 2020-01-01 00:00:02 509.86904778455505 +3 2020-01-01 00:00:03 509.8657018190976 +4 2020-01-01 00:00:04 509.8583060916646 +5 2020-01-01 00:00:05 509.8460457773492 +6 2020-01-01 00:00:06 509.8279794535788 +7 2020-01-01 00:00:07 509.8030214840442 +8 2020-01-01 00:00:08 509.76992209751967 +9 2020-01-01 00:00:09 509.7272448715611 +10 2020-01-01 00:00:10 509.6733412955684 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 1854751238588869498 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 8578878731513821275 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 2.9048374180359593 +2 2020-01-01 00:00:02 6.378279726837681 +4 2020-01-01 00:00:04 10.100986113507272 +5 2020-01-01 00:00:05 14.056926648116779 +7 2020-01-01 00:00:07 18.037953029660304 +8 2020-01-01 00:00:08 21.73557356939588 +10 2020-01-01 00:00:10 25.974919945813337 +11 2020-01-01 00:00:11 29.41422049067498 +13 2020-01-01 00:00:13 33.911886861966366 +14 2020-01-01 00:00:14 37.092867411954074 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 2.9048374180359593 +2 2020-01-01 00:00:02 5.628405589149901 +3 2020-01-01 00:00:03 8.351973760263842 +4 2020-01-01 00:00:04 11.075541931377783 +5 2020-01-01 00:00:05 13.799110102491724 +6 2020-01-01 00:00:06 16.522678273605663 +7 2020-01-01 00:00:07 19.246246444719603 +8 2020-01-01 00:00:08 21.969814615833542 +9 2020-01-01 00:00:09 24.693382786947485 +10 2020-01-01 00:00:10 27.416950958061427 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 17177218217075523913 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 4973018359321984867 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 73413.05376916874 +2 2020-01-01 00:00:02 73413.05376916874 +4 2020-01-01 00:00:04 73413.05376916874 +5 2020-01-01 00:00:05 73413.05376916874 +7 2020-01-01 00:00:07 73413.05376916874 +8 2020-01-01 00:00:08 73413.05376916874 +10 2020-01-01 00:00:10 73413.05376916874 +11 2020-01-01 00:00:11 73413.05376916874 +13 2020-01-01 00:00:13 73413.05376916874 +14 2020-01-01 00:00:14 73413.05376916874 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 509.8657018190976 +2 2020-01-01 00:00:02 509.8583060916646 +3 2020-01-01 00:00:03 509.8460457773492 +4 2020-01-01 00:00:04 509.8279794535788 +5 2020-01-01 00:00:05 509.8030214840442 +6 2020-01-01 00:00:06 509.76992209751967 +7 2020-01-01 00:00:07 509.7272448715611 +8 2020-01-01 00:00:08 509.6733412955684 +9 2020-01-01 00:00:09 509.60632204793006 +10 2020-01-01 00:00:10 509.5240245774399 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 4967222295646687783 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ROWS_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 2316297849340719971 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 73413.05376916874 +2 2020-01-01 00:00:02 73413.05376916874 +4 2020-01-01 00:00:04 73413.05376916874 +5 2020-01-01 00:00:05 73413.05376916874 +7 2020-01-01 00:00:07 73413.05376916874 +8 2020-01-01 00:00:08 73413.05376916874 +10 2020-01-01 00:00:10 73413.05376916874 +11 2020-01-01 00:00:11 73413.05376916874 +13 2020-01-01 00:00:13 73413.05376916874 +14 2020-01-01 00:00:14 73413.05376916874 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 509.86904778455505 +2 2020-01-01 00:00:02 509.86904778455505 +3 2020-01-01 00:00:03 509.86904778455505 +4 2020-01-01 00:00:04 509.86904778455505 +5 2020-01-01 00:00:05 509.86904778455505 +6 2020-01-01 00:00:06 509.86904778455505 +7 2020-01-01 00:00:07 509.86904778455505 +8 2020-01-01 00:00:08 509.86904778455505 +9 2020-01-01 00:00:09 509.86904778455505 +10 2020-01-01 00:00:10 509.86904778455505 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 5249260101878613678 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 4835566242294516990 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 73413.05376916874 +2 2020-01-01 00:00:02 73413.05376916874 +4 2020-01-01 00:00:04 73413.05376916874 +5 2020-01-01 00:00:05 73413.05376916874 +7 2020-01-01 00:00:07 73413.05376916874 +8 2020-01-01 00:00:08 73413.05376916874 +10 2020-01-01 00:00:10 73413.05376916874 +11 2020-01-01 00:00:11 73413.05376916874 +13 2020-01-01 00:00:13 73413.05376916874 +14 2020-01-01 00:00:14 73413.05376916874 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 509.86904778455505 +2 2020-01-01 00:00:02 509.86904778455505 +3 2020-01-01 00:00:03 509.86904778455505 +4 2020-01-01 00:00:04 509.86904778455505 +5 2020-01-01 00:00:05 509.86904778455505 +6 2020-01-01 00:00:06 509.86904778455505 +7 2020-01-01 00:00:07 509.86904778455505 +8 2020-01-01 00:00:08 509.86904778455505 +9 2020-01-01 00:00:09 509.86904778455505 +10 2020-01-01 00:00:10 509.86904778455505 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 5249260101878613678 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 4835566242294516990 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 73413.05376916874 +2 2020-01-01 00:00:02 73413.05376916874 +4 2020-01-01 00:00:04 73413.05376916874 +5 2020-01-01 00:00:05 73413.05376916874 +7 2020-01-01 00:00:07 73413.05376916874 +8 2020-01-01 00:00:08 73413.05376916874 +10 2020-01-01 00:00:10 73413.05376916874 +11 2020-01-01 00:00:11 73413.05376916874 +13 2020-01-01 00:00:13 73413.05376916874 +14 2020-01-01 00:00:14 73413.05376916874 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 509.86904778455505 +2 2020-01-01 00:00:02 509.86904778455505 +3 2020-01-01 00:00:03 509.86904778455505 +4 2020-01-01 00:00:04 509.86904778455505 +5 2020-01-01 00:00:05 509.86904778455505 +6 2020-01-01 00:00:06 509.86904778455505 +7 2020-01-01 00:00:07 509.86904778455505 +8 2020-01-01 00:00:08 509.86904778455505 +9 2020-01-01 00:00:09 509.86904778455505 +10 2020-01-01 00:00:10 509.86904778455505 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 5249260101878613678 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 4835566242294516990 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 73413.05376916874 +2 2020-01-01 00:00:02 73413.05376916874 +4 2020-01-01 00:00:04 73413.05376916874 +5 2020-01-01 00:00:05 73413.05376916874 +7 2020-01-01 00:00:07 73413.05376916874 +8 2020-01-01 00:00:08 73413.05376916874 +10 2020-01-01 00:00:10 73413.05376916874 +11 2020-01-01 00:00:11 73413.05376916874 +13 2020-01-01 00:00:13 73413.05376916874 +14 2020-01-01 00:00:14 73413.05376916874 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 509.86904778455505 +2 2020-01-01 00:00:02 509.86904778455505 +3 2020-01-01 00:00:03 509.86904778455505 +4 2020-01-01 00:00:04 509.86904778455505 +5 2020-01-01 00:00:05 509.86904778455505 +6 2020-01-01 00:00:06 509.86904778455505 +7 2020-01-01 00:00:07 509.86904778455505 +8 2020-01-01 00:00:08 509.86904778455505 +9 2020-01-01 00:00:09 509.86904778455505 +10 2020-01-01 00:00:10 509.86904778455505 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 5249260101878613678 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 4835566242294516990 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 5 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 8 +10 2020-01-01 00:00:10 10 +11 2020-01-01 00:00:11 11 +13 2020-01-01 00:00:13 13 +14 2020-01-01 00:00:14 14 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2 +3 2020-01-01 00:00:03 3 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 5 +6 2020-01-01 00:00:06 6 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 8 +9 2020-01-01 00:00:09 9 +10 2020-01-01 00:00:10 10 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 6912631249882696577 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 16831297400999588778 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 73413.05376916874 +2 2020-01-01 00:00:02 73413.05376916874 +4 2020-01-01 00:00:04 73413.05376916874 +5 2020-01-01 00:00:05 73413.05376916874 +7 2020-01-01 00:00:07 73413.05376916874 +8 2020-01-01 00:00:08 73413.05376916874 +10 2020-01-01 00:00:10 73413.05376916874 +11 2020-01-01 00:00:11 73413.05376916874 +13 2020-01-01 00:00:13 73413.05376916874 +14 2020-01-01 00:00:14 73413.05376916874 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 509.86904778455505 +2 2020-01-01 00:00:02 509.8657018190976 +3 2020-01-01 00:00:03 509.8583060916646 +4 2020-01-01 00:00:04 509.8460457773492 +5 2020-01-01 00:00:05 509.8279794535788 +6 2020-01-01 00:00:06 509.8030214840442 +7 2020-01-01 00:00:07 509.76992209751967 +8 2020-01-01 00:00:08 509.7272448715611 +9 2020-01-01 00:00:09 509.6733412955684 +10 2020-01-01 00:00:10 509.60632204793006 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 14732497617511578970 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 15259948609631945224 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 2.9048374180359593 +2 2020-01-01 00:00:02 1.9999999999999998 +4 2020-01-01 00:00:04 8.619349672143837 +5 2020-01-01 00:00:05 4.999999999999999 +7 2020-01-01 00:00:07 14.333861926251718 +8 2020-01-01 00:00:08 8 +10 2020-01-01 00:00:10 20.048374180359595 +11 2020-01-01 00:00:11 11 +13 2020-01-01 00:00:13 25.762886434467475 +14 2020-01-01 00:00:14 14.000000000000002 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 2.9048374180359593 +2 2020-01-01 00:00:02 4.809674836071919 +3 2020-01-01 00:00:03 6.714512254107878 +4 2020-01-01 00:00:04 8.619349672143837 +5 2020-01-01 00:00:05 10.524187090179797 +6 2020-01-01 00:00:06 12.429024508215756 +7 2020-01-01 00:00:07 14.333861926251714 +8 2020-01-01 00:00:08 16.238699344287674 +9 2020-01-01 00:00:09 18.143536762323635 +10 2020-01-01 00:00:10 20.048374180359595 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 17864546820017591112 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 6808652788102822622 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2.9048374180359593 +4 2020-01-01 00:00:04 6.378279726837681 +5 2020-01-01 00:00:05 10.771306159542913 +7 2020-01-01 00:00:07 15.818799603636073 +8 2020-01-01 00:00:08 22.313441789782324 +10 2020-01-01 00:00:10 28.26870100031019 +11 2020-01-01 00:00:11 36.57857842435122 +13 2020-01-01 00:00:13 42.9480070598911 +14 2020-01-01 00:00:14 52.86096381786202 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2.9048374180359593 +3 2020-01-01 00:00:03 5.628405589149901 +4 2020-01-01 00:00:04 9.09279198094556 +5 2020-01-01 00:00:05 13.227498418776857 +6 2020-01-01 00:00:06 17.96873551632079 +7 2020-01-01 00:00:07 23.258784249958747 +8 2020-01-01 00:00:08 29.045418287388113 +9 2020-01-01 00:00:09 35.281381288934696 +10 2020-01-01 00:00:10 41.92391395022189 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 8177272607092946026 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 13193147715685283094 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 0 +2 2020-01-01 00:00:02 1 +4 2020-01-01 00:00:04 2.9048374180359593 +5 2020-01-01 00:00:05 6.378279726837681 +7 2020-01-01 00:00:07 10.771306159542913 +8 2020-01-01 00:00:08 15.818799603636073 +10 2020-01-01 00:00:10 22.313441789782324 +11 2020-01-01 00:00:11 28.26870100031019 +13 2020-01-01 00:00:13 36.57857842435122 +14 2020-01-01 00:00:14 42.9480070598911 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 0 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 2.9048374180359593 +4 2020-01-01 00:00:04 5.628405589149901 +5 2020-01-01 00:00:05 9.09279198094556 +6 2020-01-01 00:00:06 13.227498418776857 +7 2020-01-01 00:00:07 17.96873551632079 +8 2020-01-01 00:00:08 23.258784249958747 +9 2020-01-01 00:00:09 29.045418287388113 +10 2020-01-01 00:00:10 35.281381288934696 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 207461051412820560 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 7493948266116429797 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 73413.05376916874 +2 2020-01-01 00:00:02 73413.05376916874 +4 2020-01-01 00:00:04 73413.05376916874 +5 2020-01-01 00:00:05 73413.05376916874 +7 2020-01-01 00:00:07 73413.05376916874 +8 2020-01-01 00:00:08 73413.05376916874 +10 2020-01-01 00:00:10 73413.05376916874 +11 2020-01-01 00:00:11 73413.05376916874 +13 2020-01-01 00:00:13 73413.05376916874 +14 2020-01-01 00:00:14 73413.05376916874 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 509.86904778455505 +2 2020-01-01 00:00:02 509.86904778455505 +3 2020-01-01 00:00:03 509.86904778455505 +4 2020-01-01 00:00:04 509.86904778455505 +5 2020-01-01 00:00:05 509.86904778455505 +6 2020-01-01 00:00:06 509.86904778455505 +7 2020-01-01 00:00:07 509.86904778455505 +8 2020-01-01 00:00:08 509.86904778455505 +9 2020-01-01 00:00:09 509.86904778455505 +10 2020-01-01 00:00:10 509.86904778455505 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 5249260101878613678 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 4835566242294516990 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__basic_check = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2.9048374180359593 +4 2020-01-01 00:00:04 4 +5 2020-01-01 00:00:05 8.619349672143837 +7 2020-01-01 00:00:07 7 +8 2020-01-01 00:00:08 14.333861926251718 +10 2020-01-01 00:00:10 10 +11 2020-01-01 00:00:11 20.048374180359595 +13 2020-01-01 00:00:13 13 +14 2020-01-01 00:00:14 25.762886434467475 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 2.9048374180359593 +3 2020-01-01 00:00:03 4.809674836071919 +4 2020-01-01 00:00:04 6.714512254107878 +5 2020-01-01 00:00:05 8.619349672143837 +6 2020-01-01 00:00:06 10.524187090179797 +7 2020-01-01 00:00:07 12.429024508215756 +8 2020-01-01 00:00:08 14.333861926251714 +9 2020-01-01 00:00:09 16.238699344287674 +10 2020-01-01 00:00:10 18.143536762323635 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 4153748905038498474 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_CURRENT_ROW__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 3478934939339647691 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 73413.05376916874 +2 2020-01-01 00:00:02 73413.05376916874 +4 2020-01-01 00:00:04 73413.05376916874 +5 2020-01-01 00:00:05 73413.05376916874 +7 2020-01-01 00:00:07 73413.05376916874 +8 2020-01-01 00:00:08 73413.05376916874 +10 2020-01-01 00:00:10 73413.05376916874 +11 2020-01-01 00:00:11 73413.05376916874 +13 2020-01-01 00:00:13 73413.05376916874 +14 2020-01-01 00:00:14 73413.05376916874 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 509.86904778455505 +2 2020-01-01 00:00:02 509.86904778455505 +3 2020-01-01 00:00:03 509.8657018190976 +4 2020-01-01 00:00:04 509.8583060916646 +5 2020-01-01 00:00:05 509.8460457773492 +6 2020-01-01 00:00:06 509.8279794535788 +7 2020-01-01 00:00:07 509.8030214840442 +8 2020-01-01 00:00:08 509.76992209751967 +9 2020-01-01 00:00:09 509.7272448715611 +10 2020-01-01 00:00:10 509.6733412955684 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 1854751238588869498 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 6699689522961227875 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 2.9048374180359593 +2 2020-01-01 00:00:02 2.9048374180359593 +4 2020-01-01 00:00:04 8.619349672143837 +5 2020-01-01 00:00:05 8.619349672143837 +7 2020-01-01 00:00:07 14.333861926251718 +8 2020-01-01 00:00:08 14.333861926251718 +10 2020-01-01 00:00:10 20.048374180359595 +11 2020-01-01 00:00:11 20.048374180359595 +13 2020-01-01 00:00:13 25.762886434467475 +14 2020-01-01 00:00:14 25.762886434467475 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 2.9048374180359593 +2 2020-01-01 00:00:02 5.628405589149901 +3 2020-01-01 00:00:03 8.351973760263842 +4 2020-01-01 00:00:04 11.075541931377783 +5 2020-01-01 00:00:05 13.799110102491724 +6 2020-01-01 00:00:06 16.522678273605663 +7 2020-01-01 00:00:07 19.246246444719603 +8 2020-01-01 00:00:08 21.969814615833542 +9 2020-01-01 00:00:09 24.693382786947485 +10 2020-01-01 00:00:10 27.416950958061427 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 17177218217075523913 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_1_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 320451493116528259 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__basic_check = r""" +id time w +1 2020-01-01 00:00:01 73413.05376916874 +2 2020-01-01 00:00:02 73413.05376916874 +4 2020-01-01 00:00:04 73413.05376916874 +5 2020-01-01 00:00:05 73413.05376916874 +7 2020-01-01 00:00:07 73413.05376916874 +8 2020-01-01 00:00:08 73413.05376916874 +10 2020-01-01 00:00:10 73413.05376916874 +11 2020-01-01 00:00:11 73413.05376916874 +13 2020-01-01 00:00:13 73413.05376916874 +14 2020-01-01 00:00:14 73413.05376916874 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__check_with_partition_by_minute = r""" +id time w +1 2020-01-01 00:00:01 509.8657018190976 +2 2020-01-01 00:00:02 509.8583060916646 +3 2020-01-01 00:00:03 509.8460457773492 +4 2020-01-01 00:00:04 509.8279794535788 +5 2020-01-01 00:00:05 509.8030214840442 +6 2020-01-01 00:00:06 509.76992209751967 +7 2020-01-01 00:00:07 509.7272448715611 +8 2020-01-01 00:00:08 509.6733412955684 +9 2020-01-01 00:00:09 509.60632204793006 +10 2020-01-01 00:00:10 509.5240245774399 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__full_table = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 4967222295646687783 +""" + +_window_functions_tests_time_decayed_funcs_exponentialTimeDecayedSum_check_over_ORDER_BY_id_RANGE_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING__full_table_with_partition_by_minute = r""" +anyLast(id) anyLast(time) cityHash64(groupArray(w)) +9999 2020-01-01 02:46:39 2316297849340719971 +""" + +_window_functions_tests_non_negative_derivative_func_check_intervals_valid_SECOND = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +6 2020-01-01 00:00:06 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +9 2020-01-01 00:00:09 1 +""" + +_window_functions_tests_non_negative_derivative_func_check_intervals_valid_MINUTE = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 60 +2 2020-01-01 00:00:02 60 +3 2020-01-01 00:00:03 60 +4 2020-01-01 00:00:04 60 +5 2020-01-01 00:00:05 60 +6 2020-01-01 00:00:06 60 +7 2020-01-01 00:00:07 60 +8 2020-01-01 00:00:08 60 +9 2020-01-01 00:00:09 60 +""" + +_window_functions_tests_non_negative_derivative_func_check_intervals_valid_HOUR = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 3600 +2 2020-01-01 00:00:02 3600 +3 2020-01-01 00:00:03 3600 +4 2020-01-01 00:00:04 3600 +5 2020-01-01 00:00:05 3600 +6 2020-01-01 00:00:06 3600 +7 2020-01-01 00:00:07 3600 +8 2020-01-01 00:00:08 3600 +9 2020-01-01 00:00:09 3600 +""" + +_window_functions_tests_non_negative_derivative_func_check_intervals_valid_DAY = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 86400 +2 2020-01-01 00:00:02 86400 +3 2020-01-01 00:00:03 86400 +4 2020-01-01 00:00:04 86400 +5 2020-01-01 00:00:05 86400 +6 2020-01-01 00:00:06 86400 +7 2020-01-01 00:00:07 86400 +8 2020-01-01 00:00:08 86400 +9 2020-01-01 00:00:09 86400 +""" + +_window_functions_tests_non_negative_derivative_func_check_intervals_valid_WEEK = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 604800 +2 2020-01-01 00:00:02 604800 +3 2020-01-01 00:00:03 604800 +4 2020-01-01 00:00:04 604800 +5 2020-01-01 00:00:05 604800 +6 2020-01-01 00:00:06 604800 +7 2020-01-01 00:00:07 604800 +8 2020-01-01 00:00:08 604800 +9 2020-01-01 00:00:09 604800 +""" + +_window_functions_tests_non_negative_derivative_func_check_one_row_per_partition = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +6 2020-01-01 00:00:06 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +9 2020-01-01 00:00:09 1 +""" + +_window_functions_tests_non_negative_derivative_func_check_over_windows_ROWS_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +6 2020-01-01 00:00:06 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +9 2020-01-01 00:00:09 1 +""" + +_window_functions_tests_non_negative_derivative_func_check_over_windows_ROWS_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +6 2020-01-01 00:00:06 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +9 2020-01-01 00:00:09 1 +""" + +_window_functions_tests_non_negative_derivative_func_check_over_windows_ROWS_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +6 2020-01-01 00:00:06 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +9 2020-01-01 00:00:09 1 +""" + +_window_functions_tests_non_negative_derivative_func_check_over_windows_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +6 2020-01-01 00:00:06 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +9 2020-01-01 00:00:09 1 +""" + +_window_functions_tests_non_negative_derivative_func_check_over_windows_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +6 2020-01-01 00:00:06 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +9 2020-01-01 00:00:09 1 +""" + +_window_functions_tests_non_negative_derivative_func_check_over_windows_ROWS_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +6 2020-01-01 00:00:06 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +9 2020-01-01 00:00:09 1 +""" + +_window_functions_tests_non_negative_derivative_func_check_over_windows_ROWS_BETWEEN_1_PRECEDING_AND_CURRENT_ROW = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +6 2020-01-01 00:00:06 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +9 2020-01-01 00:00:09 1 +""" + +_window_functions_tests_non_negative_derivative_func_check_over_windows_ROWS_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +6 2020-01-01 00:00:06 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +9 2020-01-01 00:00:09 1 +""" + +_window_functions_tests_non_negative_derivative_func_check_over_windows_ROWS_BETWEEN_1_PRECEDING_AND_1_FOLLOWING = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +6 2020-01-01 00:00:06 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +9 2020-01-01 00:00:09 1 +""" + +_window_functions_tests_non_negative_derivative_func_check_over_windows_ROWS_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +6 2020-01-01 00:00:06 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +9 2020-01-01 00:00:09 1 +""" + +_window_functions_tests_non_negative_derivative_func_check_over_windows_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +6 2020-01-01 00:00:06 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +9 2020-01-01 00:00:09 1 +""" + +_window_functions_tests_non_negative_derivative_func_check_over_windows_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +6 2020-01-01 00:00:06 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +9 2020-01-01 00:00:09 1 +""" + +_window_functions_tests_non_negative_derivative_func_check_over_windows_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +6 2020-01-01 00:00:06 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +9 2020-01-01 00:00:09 1 +""" + +_window_functions_tests_non_negative_derivative_func_check_over_windows_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +6 2020-01-01 00:00:06 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +9 2020-01-01 00:00:09 1 +""" + +_window_functions_tests_non_negative_derivative_func_check_over_windows_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_CURRENT_ROW = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +6 2020-01-01 00:00:06 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +9 2020-01-01 00:00:09 1 +""" + +_window_functions_tests_non_negative_derivative_func_check_over_windows_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_UNBOUNDED_FOLLOWING = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +6 2020-01-01 00:00:06 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +9 2020-01-01 00:00:09 1 +""" + +_window_functions_tests_non_negative_derivative_func_check_over_windows_ORDER_BY_id_RANGE_BETWEEN_CURRENT_ROW_AND_1_FOLLOWING = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +6 2020-01-01 00:00:06 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +9 2020-01-01 00:00:09 1 +""" + +_window_functions_tests_non_negative_derivative_func_check_over_windows_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_CURRENT_ROW = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +6 2020-01-01 00:00:06 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +9 2020-01-01 00:00:09 1 +""" + +_window_functions_tests_non_negative_derivative_func_check_over_windows_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_1_PRECEDING = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +6 2020-01-01 00:00:06 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +9 2020-01-01 00:00:09 1 +""" + +_window_functions_tests_non_negative_derivative_func_check_over_windows_ORDER_BY_id_RANGE_BETWEEN_UNBOUNDED_PRECEDING_AND_UNBOUNDED_FOLLOWING = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +6 2020-01-01 00:00:06 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +9 2020-01-01 00:00:09 1 +""" + +_window_functions_tests_non_negative_derivative_func_check_over_windows_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_CURRENT_ROW = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +6 2020-01-01 00:00:06 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +9 2020-01-01 00:00:09 1 +""" + +_window_functions_tests_non_negative_derivative_func_check_over_windows_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_UNBOUNDED_FOLLOWING = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +6 2020-01-01 00:00:06 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +9 2020-01-01 00:00:09 1 +""" + +_window_functions_tests_non_negative_derivative_func_check_over_windows_ORDER_BY_id_RANGE_BETWEEN_1_PRECEDING_AND_1_FOLLOWING = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +6 2020-01-01 00:00:06 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +9 2020-01-01 00:00:09 1 +""" + +_window_functions_tests_non_negative_derivative_func_check_over_windows_ORDER_BY_id_RANGE_BETWEEN_1_FOLLOWING_AND_UNBOUNDED_FOLLOWING = r""" +id f_timestamp nnd +0 2020-01-01 00:00:00 0 +1 2020-01-01 00:00:01 1 +2 2020-01-01 00:00:02 1 +3 2020-01-01 00:00:03 1 +4 2020-01-01 00:00:04 1 +5 2020-01-01 00:00:05 1 +6 2020-01-01 00:00:06 1 +7 2020-01-01 00:00:07 1 +8 2020-01-01 00:00:08 1 +9 2020-01-01 00:00:09 1 +""" + +_window_functions_tests_non_negative_derivative_func_division_by_tiny_numerator_is_non_zero = r""" +id metric ts nnd +1 1 2022-12-12 00:00:00.000000000 0 +2 2 2022-12-12 00:00:00.000000001 0 +3 3 2022-12-12 00:00:00.000000002 0 +""" + +_window_functions_tests_non_negative_derivative_func_division_by_tiny_numerator_is_zero = r""" +id metric ts nnd +1 1 2022-12-12 00:00:00.000000000 0 +2 1 2022-12-12 00:00:00.000000001 0 +3 1 2022-12-12 00:00:00.000000002 0 +""" + +_window_functions_tests_non_negative_derivative_func_division_by_zero = r""" +id metric ts nnd +1 1 2022-12-12 00:00:00 0 +2 1 2022-12-12 00:00:00 0 +""" + +_window_functions_tests_non_negative_derivative_func_empty_table = r""" +id metric ts nnd +""" + +_window_functions_tests_non_negative_derivative_func_valid_metric_types_Float32 = r""" +id metric ts nnd +1 1 2022-12-12 00:00:00 0 +2 2 2022-12-12 00:00:01 1 +3 3 2022-12-12 00:00:02 1 +""" + +_window_functions_tests_non_negative_derivative_func_valid_metric_types_Float64 = r""" +id metric ts nnd +1 1 2022-12-12 00:00:00 0 +2 2 2022-12-12 00:00:01 1 +3 3 2022-12-12 00:00:02 1 +""" + +_window_functions_tests_non_negative_derivative_func_valid_metric_types_UInt8 = r""" +id metric ts nnd +1 1 2022-12-12 00:00:00 0 +2 2 2022-12-12 00:00:01 1 +3 3 2022-12-12 00:00:02 1 +""" + +_window_functions_tests_non_negative_derivative_func_valid_metric_types_UInt16 = r""" +id metric ts nnd +1 1 2022-12-12 00:00:00 0 +2 2 2022-12-12 00:00:01 1 +3 3 2022-12-12 00:00:02 1 +""" + +_window_functions_tests_non_negative_derivative_func_valid_metric_types_UInt32 = r""" +id metric ts nnd +1 1 2022-12-12 00:00:00 0 +2 2 2022-12-12 00:00:01 1 +3 3 2022-12-12 00:00:02 1 +""" + +_window_functions_tests_non_negative_derivative_func_valid_metric_types_UInt64 = r""" +id metric ts nnd +1 1 2022-12-12 00:00:00 0 +2 2 2022-12-12 00:00:01 1 +3 3 2022-12-12 00:00:02 1 +""" + +_window_functions_tests_non_negative_derivative_func_valid_metric_types_Int8 = r""" +id metric ts nnd +1 1 2022-12-12 00:00:00 0 +2 2 2022-12-12 00:00:01 1 +3 3 2022-12-12 00:00:02 1 +""" + +_window_functions_tests_non_negative_derivative_func_valid_metric_types_Int16 = r""" +id metric ts nnd +1 1 2022-12-12 00:00:00 0 +2 2 2022-12-12 00:00:01 1 +3 3 2022-12-12 00:00:02 1 +""" + +_window_functions_tests_non_negative_derivative_func_valid_metric_types_Int32 = r""" +id metric ts nnd +1 1 2022-12-12 00:00:00 0 +2 2 2022-12-12 00:00:01 1 +3 3 2022-12-12 00:00:02 1 +""" + +_window_functions_tests_non_negative_derivative_func_valid_metric_types_Int64 = r""" +id metric ts nnd +1 1 2022-12-12 00:00:00 0 +2 2 2022-12-12 00:00:01 1 +3 3 2022-12-12 00:00:02 1 +""" + +_window_functions_tests_non_negative_derivative_func_valid_metric_types_Decimal32_4_ = r""" +id metric ts nnd +1 1 2022-12-12 00:00:00 0 +2 2 2022-12-12 00:00:01 1 +3 3 2022-12-12 00:00:02 1 +""" + +_window_functions_tests_non_negative_derivative_func_valid_metric_types_Decimal64_4_ = r""" +id metric ts nnd +1 1 2022-12-12 00:00:00 0 +2 2 2022-12-12 00:00:01 1 +3 3 2022-12-12 00:00:02 1 +""" + +_window_functions_tests_non_negative_derivative_func_valid_metric_types_Decimal128_4_ = r""" +id metric ts nnd +1 1 2022-12-12 00:00:00 0 +2 2 2022-12-12 00:00:01 1 +3 3 2022-12-12 00:00:02 1 +""" + +_window_functions_tests_non_negative_derivative_func_valid_timestamp_types_DateTime = r""" +id metric ts nnd +1 1 2022-12-12 00:00:00 0 +2 2 2022-12-12 00:00:01 1 +3 3 2022-12-12 00:00:02 1 +""" + +_window_functions_tests_non_negative_derivative_func_valid_timestamp_types_DateTime64_0_ = r""" +id metric ts nnd +1 1 2022-12-12 00:00:00 0 +2 2 2022-12-12 00:00:01 1 +3 3 2022-12-12 00:00:02 1 +""" + +_window_functions_tests_non_negative_derivative_func_valid_timestamp_types_DateTime64_3__EST_ = r""" +id metric ts nnd +1 1 2022-12-12 00:00:00.000 0 +2 2 2022-12-12 00:00:01.000 1 +3 3 2022-12-12 00:00:02.000 1 +""" + +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__studentTTest_salary__1__ = r""" func (nan,0) (nan,0) @@ -796,7 +6235,7 @@ func (nan,0) """ -func__welchTTest_salary__1__ = r""" +_window_functions_tests_aggregate_funcs_aggregate_funcs_over_rows_frame_func__welchTTest_salary__1__ = r""" func (nan,0) (nan,0) @@ -810,17 +6249,3 @@ func (nan,0) """ -func__median_salary__ = r""" -func -4800 -4800 -4900 -5000 -5100 -5200 -5200 -5200 -5600 -6000 -""" - diff --git a/tests/testflows/window_functions/tests/time_decayed_funcs.py b/tests/testflows/window_functions/tests/time_decayed_funcs.py new file mode 100644 index 000000000000..e6045fb6e203 --- /dev/null +++ b/tests/testflows/window_functions/tests/time_decayed_funcs.py @@ -0,0 +1,387 @@ +from testflows.core import * + +from window_functions.requirements import * +from window_functions.tests.common import * + + +@TestOutline +def exponentialTimeDecayedFunc( + self, funcname, extremely_large_number_of_arguments=1000 +): + """Check exponentialTimeDecayed functions such as + exponentialTimeDecayedSum, exponentialTimeDecayedMax and exponentialTimeDecayedAvg. + """ + decay_datatypes = ["toUInt32", "toUInt64", "toFloat32", "toFloat64"] + + value_datatypes = [ + "Int8", + "Int16", + "Int32", + "Int64", + "UInt8", + "UInt16", + "UInt32", + "UInt64", + "Float32", + "Float64", + "Decimal32(4)", + "Decimal64(4)", + "Decimal128(4)", + ] + + unsupported_value_datatypes = [ + "DateTime", + "DateTime64", + "Nullable(Int8)", + "Nullable(Float32)", + ] + + time_columns = [ + ("DateTime", "f_timestamp"), + ("DateTime64", "f_timestamp64"), + ("DateTimeTZ", "f_timestamptz"), + ("DateTime64TZ", "f_timestamp64tz"), + ] + + for time_datatype, time_column in time_columns: + with Example(f"{time_datatype} time column"): + execute_query( + f"SELECT id, {time_column} AS time, {funcname}(1)(id, {time_column}) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS w FROM datetimes2 ORDER BY id LIMIT 10" + ) + + with Example("check decay length with zero"): + execute_query( + f"SELECT id, f_timestamp AS time, {funcname}(0)(id, f_timestamp) OVER () AS w FROM datetimes2 ORDER BY id LIMIT 10" + ) + + with Example("check decay length with INTERVAL"): + for interval_period in interval_periods: + r = execute_query( + f"SELECT id, f_timestamp AS time, {funcname}(INTERVAL 5 {interval_period})(id, f_timestamp) OVER () AS w FROM datetimes2 ORDER BY id LIMIT 10", + no_checks=True, + ) + assert r.exitcode != 0, error("should return an error") + + with Example("check decay length with negative"): + execute_query( + f"SELECT id, f_timestamp AS time, {funcname}(-1000)(id, f_timestamp) OVER () AS w FROM datetimes2 ORDER BY id LIMIT 10" + ) + + with Example("check decay length with invalid number of arguments"): + execute_query( + f"SELECT id, f_timestamp AS time, {funcname}(1,2)(id, f_timestamp) OVER () AS w FROM datetimes2 ORDER BY id LIMIT 10", + exitcode=36, + message=f"Exception: Function {funcname} takes exactly one parameter", + ) + + with Example("check decay length with extremely large invalid number of arguments"): + execute_query( + f"SELECT id, f_timestamp AS time, {funcname}({','.join(['1']*extremely_large_number_of_arguments)})(id, f_timestamp) OVER () AS w FROM datetimes2 ORDER BY id LIMIT 10", + exitcode=36, + message=f"Exception: Function {funcname} takes exactly one parameter", + ) + + with Example("check decay length with column argument"): + execute_query( + f"SELECT id, f_timestamp AS time, {funcname}(id)(id, f_timestamp) OVER () AS w FROM datetimes2 ORDER BY id LIMIT 10", + exitcode=47, + message=f"Exception: ", + ) + + with Example("check input value with 0"): + execute_query( + f"SELECT id, f_timestamp AS time, {funcname}(1)(0, f_timestamp) OVER () AS w FROM datetimes2 ORDER BY id LIMIT 10", + ) + + with Example("check input time with 0"): + execute_query( + f"SELECT id, f_timestamp AS time, {funcname}(1)(id, 0) OVER () AS w FROM datetimes2 ORDER BY id LIMIT 10", + ) + + with Example("check invalid number of arguments"): + execute_query( + f"SELECT id, f_timestamp AS time, {funcname}(1)(id, id, f_timestamp) OVER () AS w FROM datetimes2 ORDER BY id LIMIT 10", + exitcode=36, + message=f"DB::Exception: Function {funcname} takes exactly two arguments", + ) + + with Example("check extremely large invalid number of arguments"): + execute_query( + f"SELECT id, f_timestamp AS time, {funcname}(1)(id, f_timestamp, {','.join(['id']*extremely_large_number_of_arguments)}) OVER () AS w FROM datetimes2 ORDER BY id LIMIT 10", + exitcode=36, + message=f"DB::Exception: Function {funcname} takes exactly two arguments", + ) + + with Example("check decay length overflow"): + execute_query( + f"SELECT id, f_timestamp AS time, {funcname}(1000000000000000000000000000000000000000000000000000000000000000)(id, f_timestamp) OVER () AS w FROM datetimes2 ORDER BY id LIMIT 10" + ) + + with Example(f"check using as a non-window aggregate function"): + execute_query( + f"SELECT anyLast(id), anyLast(time), {funcname}(10)(id, time) FROM values('id Int8, time DateTime', (1,1),(1,2),(2,3),(3,3),(3,5))", + exitcode=36, + message=f"Exception: The function '{funcname}' can only be used as a window function, not as an aggregate function", + ) + + for data_type in [ + "Nullable(DateTime)", + "Nullable(DateTime64)", + "Nullable(Int8)", + "Nullable(Float32)", + ]: + with Example(f"check input time with unsupported {data_type} type"): + execute_query( + f"SELECT id, time, {funcname}(1)(id, time) OVER () FROM values('id Int8, time Nullable(DateTime)', (1,1),(1,2),(2,3),(3,NULL),(3,5))", + message="Exception: Argument 1 must be DateTime, DateTime64 or a number", + exitcode=36, + ) + + for data_type in value_datatypes: + with Example(f"check input value with {data_type} type"): + execute_query( + f"SELECT id, time, {funcname}(2.2)(id, time) OVER () FROM values('id {data_type}, time DateTime', (1,1),(1,2),(2,3),(3,3),(3,5))", + ) + + for data_type in unsupported_value_datatypes: + with Example(f"check input value with unsupported {data_type} type"): + execute_query( + f"SELECT id, time, {funcname}(2.2)(id, time) OVER () FROM values('id {data_type}, time DateTime', (1,1),(1,2),(2,3),(3,4),(3,5))", + exitcode=36, + message="Exception: Argument 0 must be a number", + ) + + for decay_datatype in decay_datatypes: + with Example(f"{decay_datatype} decay length"): + execute_query( + f"SELECT id, f_timestamp AS time, {funcname}({decay_datatype}(10.5))(id, f_timestamp) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS w FROM datetimes2 ORDER BY id LIMIT 10", + ) + + with Example("check one row partitions"): + execute_query( + f"SELECT id, time, w FROM (SELECT id, f_timestamp AS time, {funcname}(10)(id, f_timestamp) OVER (PARTITION BY id) AS w FROM (SELECT * FROM datetimes2 ORDER BY id LIMIT 10))" + ) + + with Example(f"check -1 time gap"): + execute_query( + f"SELECT id, time, {funcname}(1)(id, time) OVER () FROM values('id Int8, time DateTime', (1,'2022-01-01 00:00:00'),(2,'2022-01-01 00:00:00'),(3,'2022-01-01 00:00:00'),(4,'2022-01-01 00:00:01'),(5,'2022-01-01 00:00:00'))", + ) + + with Example(f"check 0 time gap"): + execute_query( + f"SELECT id, time, {funcname}(1)(id, time) OVER () FROM values('id Int8, time DateTime', (1,'2022-01-01 00:00:00'),(2,'2022-01-01 00:00:00'),(-3,'2022-01-01 00:00:00'),(-4,'2022-01-01 00:00:00'),(-5,'2022-01-01 00:00:00'))", + ) + + for window in windows(order_by="id"): + with Check(f"check over({window})"): + with Example("basic check"): + execute_query( + f"SELECT id, time, w FROM (SELECT id, f_timestamp AS time, {funcname}(10)(id, f_timestamp) OVER ({window}) AS w FROM datetimes2 WHERE id % 3 ORDER BY id LIMIT 10)" + ) + with Example("check with partition by minute"): + execute_query( + f"SELECT id, time, w FROM (SELECT id, f_timestamp AS time, {funcname}(10)(id, f_timestamp) OVER (PARTITION BY toStartOfMinute(f_timestamp) {window}) AS w FROM datetimes2 WHERE id % 59 ORDER BY id LIMIT 10)" + ) + with Example("full table"): + execute_query( + f"SELECT anyLast(id), anyLast(time), cityHash64(groupArray(w)) FROM (SELECT id, f_timestamp AS time, {funcname}(1)(id, f_timestamp) OVER ({window}) AS w FROM datetimes2 ORDER BY id)" + ) + with Example("full table with partition by minute"): + execute_query( + f"SELECT anyLast(id), anyLast(time), cityHash64(groupArray(w)) FROM (SELECT id, f_timestamp AS time, {funcname}(1)(id, f_timestamp) OVER (PARTITION BY toStartOfMinute(f_timestamp) {window}) AS w FROM datetimes2 WHERE id % 59 ORDER BY id)" + ) + + +@TestScenario +def exponentialTimeDecayedSum(self): + """Check exponentialTimeDecayedSum.""" + exponentialTimeDecayedFunc(funcname="exponentialTimeDecayedSum") + + +@TestScenario +def exponentialTimeDecayedMax(self): + """Check exponentialTimeDecayedMax.""" + exponentialTimeDecayedFunc(funcname="exponentialTimeDecayedMax") + + +@TestScenario +def exponentialTimeDecayedAvg(self): + """Check exponentialTimeDecayedAvg.""" + exponentialTimeDecayedFunc(funcname="exponentialTimeDecayedAvg") + + +@TestScenario +def exponentialTimeDecayedCount(self, extremely_large_number_of_arguments=1000): + """Check exponentialTimeDecayedCount function.""" + funcname = "exponentialTimeDecayedCount" + + decay_datatypes = ["toUInt32", "toUInt64", "toFloat32", "toFloat64"] + + value_datatypes = [ + "Int8", + "Int16", + "Int32", + "Int64", + "UInt8", + "UInt16", + "UInt32", + "UInt64", + "Float32", + "Float64", + "Decimal32(4)", + "Decimal64(4)", + "Decimal128(4)", + ] + + unsupported_value_datatypes = [ + "DateTime", + "DateTime64", + "Nullable(Int8)", + "Nullable(Float32)", + ] + + time_columns = [ + ("DateTime", "f_timestamp"), + ("DateTime64", "f_timestamp64"), + ("DateTimeTZ", "f_timestamptz"), + ("DateTime64TZ", "f_timestamp64tz"), + ] + + for time_datatype, time_column in time_columns: + with Example(f"{time_datatype} time column"): + execute_query( + f"SELECT id, {time_column} AS time, {funcname}(1)({time_column}) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS w FROM datetimes2 ORDER BY id LIMIT 10" + ) + + with Example("check decay length with zero"): + execute_query( + f"SELECT id, f_timestamp AS time, {funcname}(0)(f_timestamp) OVER () AS w FROM datetimes2 ORDER BY id LIMIT 10" + ) + + with Example("check decay length with INTERVAL"): + for interval_period in interval_periods: + r = execute_query( + f"SELECT id, f_timestamp AS time, {funcname}(INTERVAL 5 {interval_period})(f_timestamp) OVER () AS w FROM datetimes2 ORDER BY id LIMIT 10", + no_checks=True, + ) + assert r.exitcode != 0, error("should return an error") + + with Example("check decay length with negative"): + execute_query( + f"SELECT id, f_timestamp AS time, {funcname}(-1000)(f_timestamp) OVER () AS w FROM datetimes2 ORDER BY id LIMIT 10" + ) + + with Example("check decay length with invalid number of arguments"): + execute_query( + f"SELECT id, f_timestamp AS time, {funcname}(1,2)(f_timestamp) OVER () AS w FROM datetimes2 ORDER BY id LIMIT 10", + exitcode=36, + message=f"Exception: Function {funcname} takes exactly one parameter", + ) + + with Example("check decay length with extremely large invalid number of arguments"): + execute_query( + f"SELECT id, f_timestamp AS time, {funcname}({','.join(['1']*extremely_large_number_of_arguments)})(f_timestamp) OVER () AS w FROM datetimes2 ORDER BY id LIMIT 10", + exitcode=36, + message=f"Exception: Function {funcname} takes exactly one parameter", + ) + + with Example("check invalid number of arguments"): + execute_query( + f"SELECT id, f_timestamp AS time, {funcname}(1)(id, f_timestamp) OVER () AS w FROM datetimes2 ORDER BY id LIMIT 10", + exitcode=36, + message=f"DB::Exception: Function {funcname} takes exactly one argument", + ) + + with Example("check extremely large invalid number of arguments"): + execute_query( + f"SELECT id, f_timestamp AS time, {funcname}(1)(f_timestamp, {','.join(['f_timestamp']*extremely_large_number_of_arguments)}) OVER () AS w FROM datetimes2 ORDER BY id LIMIT 10", + exitcode=36, + message=f"DB::Exception: Function {funcname} takes exactly one argument", + ) + + with Example("check decay length overflow"): + execute_query( + f"SELECT id, f_timestamp AS time, {funcname}(1000000000000000000000000000000000000000000000000000000000000000)(f_timestamp) OVER () AS w FROM datetimes2 ORDER BY id LIMIT 10" + ) + + with Example("check decay length with column argument"): + execute_query( + f"SELECT id, f_timestamp AS time, {funcname}(id)(f_timestamp) OVER () AS w FROM datetimes2 ORDER BY id LIMIT 10", + exitcode=47, + message=f"Exception: ", + ) + + with Example("check input time with 0"): + execute_query( + f"SELECT id, f_timestamp AS time, {funcname}(1)(0) OVER () AS w FROM datetimes2 ORDER BY id LIMIT 10", + ) + + with Example(f"check using as a non-window aggregate function"): + execute_query( + f"SELECT anyLast(id), anyLast(time), {funcname}(10)(time) FROM values('id Int8, time DateTime', (1,1),(1,2),(2,3),(3,3),(3,5))", + exitcode=36, + message=f"Exception: The function '{funcname}' can only be used as a window function, not as an aggregate function", + ) + + for data_type in [ + "Nullable(DateTime)", + "Nullable(DateTime64)", + "Nullable(Int8)", + "Nullable(Float32)", + ]: + with Example(f"check input time with unsupported {data_type} type"): + execute_query( + f"SELECT id, time, {funcname}(1)(time) OVER () FROM values('id Int8, time Nullable(DateTime)', (1,1),(2,2),(2,3),(3,NULL),(3,5))", + message="Exception: Argument 0 must be DateTime, DateTime64 or a number", + exitcode=36, + ) + + for decay_datatype in decay_datatypes: + with Example(f"{decay_datatype} decay length"): + execute_query( + f"SELECT id, f_timestamp AS time, {funcname}({decay_datatype}(10.5))(f_timestamp) OVER (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS w FROM datetimes2 ORDER BY id LIMIT 10", + ) + + with Example("check one row partitions"): + execute_query( + f"SELECT id, time, w FROM (SELECT id, f_timestamp AS time, {funcname}(10)(f_timestamp) OVER (PARTITION BY id) AS w FROM (SELECT * FROM datetimes2 ORDER BY id LIMIT 10))" + ) + + with Example(f"check -1 time gap"): + execute_query( + f"SELECT id, time, {funcname}(1)(time) OVER () FROM values('id Int8, time DateTime', (1,'2022-01-01 00:00:00'),(2,'2022-01-01 00:00:00'),(3,'2022-01-01 00:00:00'),(4,'2022-01-01 00:00:01'),(5,'2022-01-01 00:00:00'))", + ) + + with Example(f"check 0 time gap"): + execute_query( + f"SELECT id, time, {funcname}(1)(time) OVER () FROM values('id Int8, time DateTime', (1,'2022-01-01 00:00:00'),(2,'2022-01-01 00:00:00'),(-3,'2022-01-01 00:00:00'),(-4,'2022-01-01 00:00:00'),(-5,'2022-01-01 00:00:00'))", + ) + + for window in windows(order_by="id"): + with Check(f"check over({window})"): + with Example("basic check"): + execute_query( + f"SELECT id, time, w FROM (SELECT id, f_timestamp AS time, {funcname}(10)(f_timestamp) OVER ({window}) AS w FROM datetimes2 WHERE id % 3 ORDER BY id LIMIT 10)" + ) + with Example("check with partition by minute"): + execute_query( + f"SELECT id, time, w FROM (SELECT id, f_timestamp AS time, {funcname}(10)(f_timestamp) OVER (PARTITION BY toStartOfMinute(f_timestamp) {window}) AS w FROM datetimes2 WHERE id % 59 ORDER BY id LIMIT 10)" + ) + with Example("full table"): + execute_query( + f"SELECT anyLast(id), anyLast(time), cityHash64(groupArray(w)) FROM (SELECT id, f_timestamp AS time, {funcname}(1)(f_timestamp) OVER ({window}) AS w FROM datetimes2 ORDER BY id)" + ) + with Example("full table with partition by minute"): + execute_query( + f"SELECT anyLast(id), anyLast(time), cityHash64(groupArray(w)) FROM (SELECT id, f_timestamp AS time, {funcname}(1)(f_timestamp) OVER (PARTITION BY toStartOfMinute(f_timestamp) {window}) AS w FROM datetimes2 WHERE id % 59 ORDER BY id)" + ) + + +@TestFeature +@Name("time decayed funcs") +@Requirements(RQ_SRS_019_ClickHouse_WindowFunctions_ExponentialTimeDecayed("1.0")) +@Skipped("only for >=22.3", check_clickhouse_version("<22.3.10")) +def feature(self): + """Check time decayed window functions.""" + for scenario in loads(current_module(), Scenario): + Scenario(run=scenario, flags=TE) diff --git a/tests/testflows/window_functions/window_functions_env/clickhouse-service.yml b/tests/testflows/window_functions/window_functions_env/clickhouse-service.yml index 9162d06bf27d..cb2cbc78cd56 100755 --- a/tests/testflows/window_functions/window_functions_env/clickhouse-service.yml +++ b/tests/testflows/window_functions/window_functions_env/clickhouse-service.yml @@ -2,14 +2,16 @@ version: '2.3' services: clickhouse: - image: altinityinfra/integration-test + image: ${IMAGE_DEPENDENCY_PROXY}clickhouse/clickhouse-integration-test:28741 expose: - "9000" - "9009" - "8123" volumes: - - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d:/etc/clickhouse-server/config.d" - - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/users.d:/etc/clickhouse-server/users.d" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/logs.xml:/etc/clickhouse-server/config.d/logs.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/remote.xml:/etc/clickhouse-server/config.d/remote.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/zookeeper.xml:/etc/clickhouse-server/config.d/zookeeper.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/macros.xml:/etc/clickhouse-server/config.d/macros.xml" - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.xml:/etc/clickhouse-server/config.xml" - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/users.xml:/etc/clickhouse-server/users.xml" - "${CLICKHOUSE_TESTS_SERVER_BIN_PATH:-/usr/bin/clickhouse}:/usr/bin/clickhouse" diff --git a/tests/testflows/window_functions/window_functions_env/docker-compose.yml b/tests/testflows/window_functions/window_functions_env/docker-compose.yml index 29f2ef524706..c9ae33d78848 100755 --- a/tests/testflows/window_functions/window_functions_env/docker-compose.yml +++ b/tests/testflows/window_functions/window_functions_env/docker-compose.yml @@ -48,7 +48,7 @@ services: # dummy service which does nothing, but allows to postpone # 'docker-compose up -d' till all dependecies will go healthy all_services_ready: - image: hello-world + image: ${IMAGE_DEPENDENCY_PROXY}hello-world depends_on: clickhouse1: condition: service_healthy diff --git a/tests/testflows/window_functions/window_functions_env/zookeeper-service.yml b/tests/testflows/window_functions/window_functions_env/zookeeper-service.yml index f3df33358be7..60c0e4e7de37 100755 --- a/tests/testflows/window_functions/window_functions_env/zookeeper-service.yml +++ b/tests/testflows/window_functions/window_functions_env/zookeeper-service.yml @@ -2,7 +2,7 @@ version: '2.3' services: zookeeper: - image: zookeeper:3.4.12 + image: ${IMAGE_DEPENDENCY_PROXY}zookeeper:3.6.2 expose: - "2181" environment: diff --git a/tests/testflows/window_functions/window_functions_env_arm64/clickhouse-service.yml b/tests/testflows/window_functions/window_functions_env_arm64/clickhouse-service.yml new file mode 100755 index 000000000000..7ff8dc1c14b7 --- /dev/null +++ b/tests/testflows/window_functions/window_functions_env_arm64/clickhouse-service.yml @@ -0,0 +1,30 @@ +version: '2.3' + +services: + clickhouse: + image: registry.gitlab.com/altinity-public/container-images/test/clickhouse-integration-test:21.12 + privileged: true + expose: + - "9000" + - "9009" + - "8123" + volumes: + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/logs.xml:/etc/clickhouse-server/config.d/logs.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/remote.xml:/etc/clickhouse-server/config.d/remote.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/zookeeper.xml:/etc/clickhouse-server/config.d/zookeeper.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.d/macros.xml:/etc/clickhouse-server/config.d/macros.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/config.xml:/etc/clickhouse-server/config.xml" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse/users.xml:/etc/clickhouse-server/users.xml" + - "${CLICKHOUSE_TESTS_SERVER_BIN_PATH:-/usr/bin/clickhouse}:/usr/bin/clickhouse" + - "${CLICKHOUSE_TESTS_ODBC_BRIDGE_BIN_PATH:-/usr/bin/clickhouse-odbc-bridge}:/usr/bin/clickhouse-odbc-bridge" + entrypoint: bash -c "clickhouse server --config-file=/etc/clickhouse-server/config.xml --log-file=/var/log/clickhouse-server/clickhouse-server.log --errorlog-file=/var/log/clickhouse-server/clickhouse-server.err.log" + healthcheck: + test: clickhouse client --query='select 1' + interval: 10s + timeout: 10s + retries: 3 + start_period: 300s + cap_add: + - SYS_PTRACE + security_opt: + - label:disable diff --git a/tests/testflows/window_functions/window_functions_env_arm64/docker-compose.yml b/tests/testflows/window_functions/window_functions_env_arm64/docker-compose.yml new file mode 100755 index 000000000000..c9ae33d78848 --- /dev/null +++ b/tests/testflows/window_functions/window_functions_env_arm64/docker-compose.yml @@ -0,0 +1,60 @@ +version: '2.3' + +services: + zookeeper: + extends: + file: zookeeper-service.yml + service: zookeeper + + clickhouse1: + extends: + file: clickhouse-service.yml + service: clickhouse + hostname: clickhouse1 + volumes: + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse1/database/:/var/lib/clickhouse/" + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse1/logs/:/var/log/clickhouse-server/" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse1/config.d/macros.xml:/etc/clickhouse-server/config.d/macros.xml" + depends_on: + zookeeper: + condition: service_healthy + + clickhouse2: + extends: + file: clickhouse-service.yml + service: clickhouse + hostname: clickhouse2 + volumes: + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse2/database/:/var/lib/clickhouse/" + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse2/logs/:/var/log/clickhouse-server/" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse2/config.d/macros.xml:/etc/clickhouse-server/config.d/macros.xml" + depends_on: + zookeeper: + condition: service_healthy + + clickhouse3: + extends: + file: clickhouse-service.yml + service: clickhouse + hostname: clickhouse3 + volumes: + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse3/database/:/var/lib/clickhouse/" + - "${CLICKHOUSE_TESTS_DIR}/_instances/clickhouse3/logs/:/var/log/clickhouse-server/" + - "${CLICKHOUSE_TESTS_DIR}/configs/clickhouse3/config.d/macros.xml:/etc/clickhouse-server/config.d/macros.xml" + depends_on: + zookeeper: + condition: service_healthy + + # dummy service which does nothing, but allows to postpone + # 'docker-compose up -d' till all dependecies will go healthy + all_services_ready: + image: ${IMAGE_DEPENDENCY_PROXY}hello-world + depends_on: + clickhouse1: + condition: service_healthy + clickhouse2: + condition: service_healthy + clickhouse3: + condition: service_healthy + zookeeper: + condition: service_healthy diff --git a/tests/testflows/window_functions/window_functions_env_arm64/zookeeper-service.yml b/tests/testflows/window_functions/window_functions_env_arm64/zookeeper-service.yml new file mode 100755 index 000000000000..60c0e4e7de37 --- /dev/null +++ b/tests/testflows/window_functions/window_functions_env_arm64/zookeeper-service.yml @@ -0,0 +1,18 @@ +version: '2.3' + +services: + zookeeper: + image: ${IMAGE_DEPENDENCY_PROXY}zookeeper:3.6.2 + expose: + - "2181" + environment: + ZOO_TICK_TIME: 500 + ZOO_MY_ID: 1 + healthcheck: + test: echo stat | nc localhost 2181 + interval: 3s + timeout: 2s + retries: 5 + start_period: 2s + security_opt: + - label:disable From 45363c7aa13f1780860d8a0dcb982d02fff31a46 Mon Sep 17 00:00:00 2001 From: Vitaliy Zakaznikov Date: Wed, 24 Aug 2022 21:42:43 -0400 Subject: [PATCH 38/72] Enabling all testflows suites in regression.py --- tests/testflows/regression.py | 62 +++++++++++++++++------------------ 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/tests/testflows/regression.py b/tests/testflows/regression.py index faa2982d8da5..9eb03e313800 100755 --- a/tests/testflows/regression.py +++ b/tests/testflows/regression.py @@ -23,42 +23,42 @@ def regression(self, local, clickhouse_binary_path, clickhouse_version, stress=N with Pool(4) as pool: try: - # Feature( - # test=load("aes_encryption.regression", "regression"), - # parallel=True, - # executor=pool, - # )(**args) - # Feature( - # test=load("datetime64_extended_range.regression", "regression"), - # parallel=True, - # executor=pool, - # )(**args) + Feature( + test=load("aes_encryption.regression", "regression"), + parallel=True, + executor=pool, + )(**args) + Feature( + test=load("datetime64_extended_range.regression", "regression"), + parallel=True, + executor=pool, + )(**args) Feature( test=load("example.regression", "regression"), parallel=True, executor=pool, )(**args) - # Feature( - # test=load("extended_precision_data_types.regression", "regression"), - # parallel=True, - # executor=pool, - # )(**args) - # Feature( - # test=load("ldap.regression", "regression"), parallel=True, executor=pool - # )(**args) - # Feature( - # test=load("map_type.regression", "regression"), - # parallel=True, - # executor=pool, - # )(**args) - # Feature( - # test=load("rbac.regression", "regression"), parallel=True, executor=pool - # )(**args) - # Feature( - # test=load("window_functions.regression", "regression"), - # parallel=True, - # executor=pool, - # )(**args) + Feature( + test=load("extended_precision_data_types.regression", "regression"), + parallel=True, + executor=pool, + )(**args) + Feature( + test=load("ldap.regression", "regression"), parallel=True, executor=pool + )(**args) + Feature( + test=load("map_type.regression", "regression"), + parallel=True, + executor=pool, + )(**args) + Feature( + test=load("rbac.regression", "regression"), parallel=True, executor=pool + )(**args) + Feature( + test=load("window_functions.regression", "regression"), + parallel=True, + executor=pool, + )(**args) finally: join() From 18775124276ad79b181a2b99d3fdf34bb742bffc Mon Sep 17 00:00:00 2001 From: Vitaliy Zakaznikov Date: Thu, 25 Aug 2022 08:11:35 -0400 Subject: [PATCH 39/72] Cleaning aes_encryption _instances --- .../_instances/mysql1/database/ca.pem | Bin 1112 -> 0 bytes .../_instances/mysql1/database/client-cert.pem | Bin 1112 -> 0 bytes .../_instances/mysql1/database/public_key.pem | Bin 452 -> 0 bytes .../_instances/mysql1/database/server-cert.pem | Bin 1112 -> 0 bytes 4 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 tests/testflows/aes_encryption/_instances/mysql1/database/ca.pem delete mode 100644 tests/testflows/aes_encryption/_instances/mysql1/database/client-cert.pem delete mode 100644 tests/testflows/aes_encryption/_instances/mysql1/database/public_key.pem delete mode 100644 tests/testflows/aes_encryption/_instances/mysql1/database/server-cert.pem diff --git a/tests/testflows/aes_encryption/_instances/mysql1/database/ca.pem b/tests/testflows/aes_encryption/_instances/mysql1/database/ca.pem deleted file mode 100644 index 36ecccedaea80bd521d9acd85aad5b44575cd007..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1112 zcmZvc%g&=X5QS&WQ_LRy3L!wkq8A1m0s(U~gm}j#5E2OFLQHu2p?fi#8B0g9zB*Es z%cc7HS&oKG3;(U7kXVML1A>0TAI`Nbs9kj(oRu0dO9S;H0Of$0PnqvumhYnTDTKDBFDDzcW!og+Zdir5YhS(%~c;0wv3{Ba3k`IN67uT9;3O zL4Y(Z0I;r+58z@5dVRTTzeUxLBbBSxRKt?9EylO{-m^UJ z@R)o*kni}VGm4}5w8TX6U^Hc2v+roMzg79hgey6Wys74-RoY2i>b<6i^%VQQO_R+W z<1+PwZRPDA*oLJ^tp~kVUpS)ArY@xJc%LzK_pW#CxeHux4I!Y8khOUQ1M%ZquRGGq z?djn5*0VA@A%5#QlrAez^75qV7JenTfC|K7O4?Ja0BS1Es90mG@j7ccyfFZzv%q6ad7-Y+Yg#rP=ex-q!EkSKH|Ib7MZ}}Yr z9(kG=28Bt;?hl{BeWs=GH6{%Xg8Tqu2?yw_6=qR6au-d7oR_rmp`qg%F&SC;xb7|su;2Ys? VpPQsf>tDaPKWhSE_}|s>*FT5lWN-ig diff --git a/tests/testflows/aes_encryption/_instances/mysql1/database/client-cert.pem b/tests/testflows/aes_encryption/_instances/mysql1/database/client-cert.pem deleted file mode 100644 index 9f76e77d4a4895a398971d846d90058ce38a05e2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1112 zcmZXUOV@Kq5XWcjQ_S9Z4?=>)EV{KzCn4^{E(j4sL}+~a@jQ#!OrP`L{Hp78UApSe zpY1?Z$JXD9>iSs23h>n*_=hqvMsQUWaHmT^Fa+hM6bT%C{eux%tlE-Dr zm(t~4?CwgmGgG>v!#wau-|yL3&150H@a0`pPeh1V>N#;7OaT1~Beoj)snU#_>#0E+ z)cz}P@m>{=m@ClRXfDzY4y6B(7)_F7V=|xDib_`byl8u=GC*QBNpMNwRRU|A8t;>R zv}_Brcl`oK%pAjy>V#Ku6lG6uqXBaVa52GepL3O{kRgs@0zi;+J)XJ*Kc>tA zj(Ua;KwD=vpXJ5?&82kU!pL7sjrf_z073q{Y;V*<&k}bHh66 zLb5aqtBl6SGB1tp3xKH_3o*fmi${3Vhp}5Z9RMZt@XO%xdsaRq)163v5nAb+ zuG_>oQhcUz_Mom``#n@Lyk$iVWLx1m-<3o-XgTThM&Pk@F8cO%5ZK}{-x(vrU*Q#%^5hDQ`VK2;>B>Sq2z_})3ztTQ`h8~y z+*9ms?~KutE(7G;cTz1HA@3#@<4!bbWfI%jpv<_xa`m`+2~M)dkhgwO^b#s^Vx6g4 a|6WQzn$vop`_Jn?)bE;5E%g7@@z=kX{$xe~ diff --git a/tests/testflows/aes_encryption/_instances/mysql1/database/public_key.pem b/tests/testflows/aes_encryption/_instances/mysql1/database/public_key.pem deleted file mode 100644 index 776da37dfe8c29cf90894da2a9034efd791c8da2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 452 zcmZXR%W|qf07SFqE3&67AjoSuBOqjik>>zA1O%LD0th&M{WW)+UUl_Zb)C)T6=>~U z|NWT2gS&f2R{pzfIL~#VdjURd>Cn-BZ>%B&fn@`LcOcNgFWnqXmNuOrix-YjSQna=R>=cl;gU9EE*({Z+ksyhnrs` ziY3lC%MKC7RX3BeEoq{A>cP?a5E+j)6RLAG?t)aoPSMn4#AEhspqz{3b}!awu40En zBmmHpf;8!?h8!7xO(Wftek`!CrYKJLZ~cOgCVMnBiul4CZl)kkB4%lAn%m{V@xIUs z+OTCPnLi2t?J{wSJE~SmhEFQZ?(wB==pona>0zKAmvlebap05KRub4Y(g)Q4*4mLJ!7DLRQ+(JkQgailyhy41F?!#!LDd|;7PnA@g zR9|1SK}<~dZyo!gX_z_)@elkGsA(ej(RFYZ3&2eXidhwKVR7rOw~}nR1Oe*}fWU@^ z4fntv#f%|@4EG$nz7QJQ@Q_-{tcx3Hs#8&(N`{=1?ZsE(VdmGI#J5y(;jH@f@Y}<^ zuJ);4Q>di^3eAb4f_upP&`-pv(WEG(zswE)Wt<@_Qp5?6BMdl~!?X^g=zS3)C7$wo zJf)v#a3}3UjV;Vme@vyk&Sw5o>F}Q}*-t*H@5jWQJBzqb6u6{7B6`q~z|2&{@H}$_ zfM!C(0iWYGXy&~zebLBcNzNeKye-&TWf(H66jD=6adrs0gsamLHDv-D@76Badi&jK z&hkni?6lq){U`X4&e?k;oK^<)c-h&SfduP7+`x(w$ywlax3gt@ao@&JVb@!5wRGv) zP+$|(Y4yoQtbRw9cv@zab{V0rr1nx6>Y?m*R4nm8%)aP%?yWvO!;dzGuR%lEXV4Ae4f@IMBM?ZO0OoUkJyYS8Es(-EnaFPK&Cr1Kccw3btYIrQ|IXm!G zm3ViRV^*}>M(Bjz$IjzBlnCptHYL`EiLtkVdZOtXhw=NoRCX6@2Np-TdWIg_j6@tP zDQ~-BW!g$HCTrL-Ta#r06chqLDvW?f!S9`O%rRVWrc(eoVtdPO4(4?{xqx8@GJxjG zKw#dr$JrQ!%%j?}UMQQ#?U7%W29-=B&qcz)(tx>FNS6S}#?jUd+*qSL>Wr zvzwwqsJGfed9x3JiOZ{JxiDJg$yI;KCg0J--pcmK>XlcQ5G&RqHt)E1q>+i&s6rZ9ce4hKuU@gf4moi-5rDIe0u!babSpQWhk)bGcln3!`7+Fcgdg>DOU z#V%#K4J;Lrj2$+Pe9TFc)Y>xS8ol)wpVyxRIs4-t=&w>{nHLSJ@f6v+a)X>Pxi#aJ-_*pnHMWKI?5C-R_6m7~+NExxzYbyJm$f VZobcMa{5R3eG}M4|9>5S{Q>)8V}<|# From 835197beeb857c477ebcf787197a245bc40faae6 Mon Sep 17 00:00:00 2001 From: Vitaliy Zakaznikov Date: Thu, 25 Aug 2022 08:15:06 -0400 Subject: [PATCH 40/72] Updating regression.py to have manual control over parallel tests. --- tests/testflows/regression.py | 62 +++++++++++++---------------------- 1 file changed, 22 insertions(+), 40 deletions(-) diff --git a/tests/testflows/regression.py b/tests/testflows/regression.py index 9eb03e313800..eecd6d755c6b 100755 --- a/tests/testflows/regression.py +++ b/tests/testflows/regression.py @@ -21,46 +21,28 @@ def regression(self, local, clickhouse_binary_path, clickhouse_version, stress=N self.context.stress = stress - with Pool(4) as pool: - try: - Feature( - test=load("aes_encryption.regression", "regression"), - parallel=True, - executor=pool, - )(**args) - Feature( - test=load("datetime64_extended_range.regression", "regression"), - parallel=True, - executor=pool, - )(**args) - Feature( - test=load("example.regression", "regression"), - parallel=True, - executor=pool, - )(**args) - Feature( - test=load("extended_precision_data_types.regression", "regression"), - parallel=True, - executor=pool, - )(**args) - Feature( - test=load("ldap.regression", "regression"), parallel=True, executor=pool - )(**args) - Feature( - test=load("map_type.regression", "regression"), - parallel=True, - executor=pool, - )(**args) - Feature( - test=load("rbac.regression", "regression"), parallel=True, executor=pool - )(**args) - Feature( - test=load("window_functions.regression", "regression"), - parallel=True, - executor=pool, - )(**args) - finally: - join() + Feature(test=load("aes_encryption.regression", "regression"), parallel=True)(**args) + Feature( + test=load("datetime64_extended_range.regression", "regression"), parallel=True + )(**args) + Feature(test=load("example.regression", "regression"), parallel=True)(**args) + Feature( + test=load("extended_precision_data_types.regression", "regression"), + parallel=True, + )(**args) + join() + + Feature(test=load("ldap.regression", "regression"))(**args) + + Feature( + test=load("map_type.regression", "regression"), + parallel=True, + executor=pool, + )(**args) + Feature(test=load("rbac.regression", "regression"), parallel=True)(**args) + Feature(test=load("window_functions.regression", "regression"), parallel=True)( + **args + ) if main(): From 25c3c74a3f2e6a96527befddb7b3fe17e34f3f64 Mon Sep 17 00:00:00 2001 From: Vitaliy Zakaznikov Date: Thu, 25 Aug 2022 09:30:06 -0400 Subject: [PATCH 41/72] Fixing xfail. --- tests/testflows/aes_encryption/regression.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testflows/aes_encryption/regression.py b/tests/testflows/aes_encryption/regression.py index edf46a9f5663..560ea660651b 100755 --- a/tests/testflows/aes_encryption/regression.py +++ b/tests/testflows/aes_encryption/regression.py @@ -18,7 +18,7 @@ xfails = { # decrypt - "/aes encryption/decrypt/invalid parameters/null in ciphertext": [ + "decrypt/invalid parameters/null in ciphertext": [ (Fail, issue_39987) ], # encrypt From 1666f37d571f45b9cea955865a5c831c23898e3e Mon Sep 17 00:00:00 2001 From: Vitaliy Zakaznikov Date: Thu, 25 Aug 2022 09:31:44 -0400 Subject: [PATCH 42/72] Updating testflows-runner image to use classic output mode and --test-to-end option. --- docker/test/testflows/runner/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/test/testflows/runner/Dockerfile b/docker/test/testflows/runner/Dockerfile index 3e82288088b2..08948015fdc5 100644 --- a/docker/test/testflows/runner/Dockerfile +++ b/docker/test/testflows/runner/Dockerfile @@ -79,4 +79,4 @@ RUN set -x \ VOLUME /var/lib/docker EXPOSE 2375 ENTRYPOINT ["dockerd-entrypoint.sh"] -CMD ["sh", "-c", "python3 regression.py --no-color -o new-fails --local --clickhouse-binary-path ${CLICKHOUSE_TESTS_SERVER_BIN_PATH} --log test.log ${TESTFLOWS_OPTS}; cat test.log | tfs report results --format json > results.json; /usr/local/bin/process_testflows_result.py || echo -e 'failure\tCannot parse results' > check_status.tsv; find * -type f | grep _instances | grep clickhouse-server | xargs -n1 tar -rvf clickhouse_logs.tar; gzip -9 clickhouse_logs.tar"] +CMD ["sh", "-c", "python3 regression.py --test-to-end --no-color -o classic --local --clickhouse-binary-path ${CLICKHOUSE_TESTS_SERVER_BIN_PATH} --log test.log ${TESTFLOWS_OPTS}; cat test.log | tfs report results --format json > results.json; /usr/local/bin/process_testflows_result.py || echo -e 'failure\tCannot parse results' > check_status.tsv; find * -type f | grep _instances | grep clickhouse-server | xargs -n1 tar -rvf clickhouse_logs.tar; gzip -9 clickhouse_logs.tar"] From 9695e2d1b079a84653a6c4988fdb75c8cac4864c Mon Sep 17 00:00:00 2001 From: Vitaliy Zakaznikov Date: Thu, 25 Aug 2022 09:37:42 -0400 Subject: [PATCH 43/72] Updating testflows-runner to include all necessary Python3 modules. --- docker/test/testflows/runner/Dockerfile | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docker/test/testflows/runner/Dockerfile b/docker/test/testflows/runner/Dockerfile index 08948015fdc5..d87ac0b5f320 100644 --- a/docker/test/testflows/runner/Dockerfile +++ b/docker/test/testflows/runner/Dockerfile @@ -76,6 +76,15 @@ RUN set -x \ && echo 'dockremap:165536:65536' >> /etc/subuid \ && echo 'dockremap:165536:65536' >> /etc/subgid +RUN pip3 install pytz==2020.1 \ + python-dateutil==2.8.1 \ + minio==7.1.3 \ + boto3==1.24.52 \ + numpy==1.21.4 \ + pyarrow==7.0.0 \ + pandas==1.4.1 \ + awscli==1.24.9 + VOLUME /var/lib/docker EXPOSE 2375 ENTRYPOINT ["dockerd-entrypoint.sh"] From 07b00d59489c6c251dd5a92624cac6694d428d52 Mon Sep 17 00:00:00 2001 From: Vitaliy Zakaznikov Date: Thu, 25 Aug 2022 16:39:25 -0400 Subject: [PATCH 44/72] Updating ldap tests and how they are launched. --- tests/testflows/aes_encryption/regression.py | 4 +- .../tests/authentications.py | 2 +- tests/testflows/regression.py | 65 ++++++++++++------- 3 files changed, 45 insertions(+), 26 deletions(-) diff --git a/tests/testflows/aes_encryption/regression.py b/tests/testflows/aes_encryption/regression.py index 560ea660651b..d97502069779 100755 --- a/tests/testflows/aes_encryption/regression.py +++ b/tests/testflows/aes_encryption/regression.py @@ -18,9 +18,7 @@ xfails = { # decrypt - "decrypt/invalid parameters/null in ciphertext": [ - (Fail, issue_39987) - ], + "decrypt/invalid parameters/null in ciphertext": [(Fail, issue_39987)], # encrypt "encrypt/invalid key or iv length for mode/mode=\"'aes-???-gcm'\", key_len=??, iv_len=12, aad=True/iv is too short": [ (Fail, "known issue") diff --git a/tests/testflows/ldap/external_user_directory/tests/authentications.py b/tests/testflows/ldap/external_user_directory/tests/authentications.py index 83daa175a248..e2d77c9a1b11 100644 --- a/tests/testflows/ldap/external_user_directory/tests/authentications.py +++ b/tests/testflows/ldap/external_user_directory/tests/authentications.py @@ -1274,7 +1274,7 @@ def repeat_requests(self, server, iterations, vcd_value, rbac=False): "1.0" ) ) -def verification_cooldown_performance(self, server, rbac=False, iterations=5000): +def verification_cooldown_performance(self, server, rbac=False, iterations=500): """Check login performance when the verification cooldown parameter is set to a positive value when comparing to the case when the verification cooldown parameter is turned off. diff --git a/tests/testflows/regression.py b/tests/testflows/regression.py index eecd6d755c6b..c4729296e02b 100755 --- a/tests/testflows/regression.py +++ b/tests/testflows/regression.py @@ -21,28 +21,49 @@ def regression(self, local, clickhouse_binary_path, clickhouse_version, stress=N self.context.stress = stress - Feature(test=load("aes_encryption.regression", "regression"), parallel=True)(**args) - Feature( - test=load("datetime64_extended_range.regression", "regression"), parallel=True - )(**args) - Feature(test=load("example.regression", "regression"), parallel=True)(**args) - Feature( - test=load("extended_precision_data_types.regression", "regression"), - parallel=True, - )(**args) - join() - - Feature(test=load("ldap.regression", "regression"))(**args) - - Feature( - test=load("map_type.regression", "regression"), - parallel=True, - executor=pool, - )(**args) - Feature(test=load("rbac.regression", "regression"), parallel=True)(**args) - Feature(test=load("window_functions.regression", "regression"), parallel=True)( - **args - ) + try: + Feature(test=load("aes_encryption.regression", "regression"), parallel=True)( + **args + ) + + Feature( + test=load("datetime64_extended_range.regression", "regression"), + parallel=True, + )(**args) + + Feature(test=load("example.regression", "regression"), parallel=True)(**args) + + Feature( + test=load("extended_precision_data_types.regression", "regression"), + parallel=True, + )(**args) + + Feature( + test=load("ldap.authentication.regression", "regression"), parallel=True + )(**args) + + join() + + Feature( + test=load("ldap.external_user_directory.regression", "regression"), + parallel=True, + )(**args) + + join() + + Feature(test=load("ldap.role_mapping.regression", "regression"), parallel=True)( + **args + ) + + Feature(test=load("map_type.regression", "regression"), parallel=True)(**args) + + Feature(test=load("rbac.regression", "regression"), parallel=True)(**args) + + Feature(test=load("window_functions.regression", "regression"), parallel=True)( + **args + ) + finally: + join() if main(): From 314bf35836478b744eec5c6e45f5f71d7e0b6f2d Mon Sep 17 00:00:00 2001 From: Vitaliy Zakaznikov Date: Thu, 25 Aug 2022 17:05:42 -0400 Subject: [PATCH 45/72] Updating interations count to improve test stability. --- tests/testflows/ldap/authentication/tests/authentications.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testflows/ldap/authentication/tests/authentications.py b/tests/testflows/ldap/authentication/tests/authentications.py index 8f98adce7463..bdfa03ff4532 100644 --- a/tests/testflows/ldap/authentication/tests/authentications.py +++ b/tests/testflows/ldap/authentication/tests/authentications.py @@ -900,7 +900,7 @@ def repeat_requests(self, server, iterations, vcd_value, rbac=False, timeout=600 @TestScenario @Tags("verification_cooldown") @Requirements(RQ_SRS_007_LDAP_Authentication_VerificationCooldown_Performance("1.0")) -def verification_cooldown_performance(self, server, rbac=False, iterations=5000): +def verification_cooldown_performance(self, server, rbac=False, iterations=500): """Check that login performance is better when the verification cooldown parameter is set to a positive value when comparing to the case when the verification cooldown parameter is turned off. From a5c8cc19a8c1dfb30c4cf0ace7a4354d310c91ba Mon Sep 17 00:00:00 2001 From: Vasily Nemkov Date: Sun, 18 Sep 2022 21:41:09 +0400 Subject: [PATCH 46/72] Revert "Merge pull request #194 from Enmk/revert_27531_constants_in_with" This reverts commit f44ba73630c7415c1745501e1978e554d1d5f585, reversing changes made to d912f2e7d40d51af840aee6eb4e3d72b7d71f4ae. --- src/Interpreters/QueryNormalizer.cpp | 3 ++ .../RequiredSourceColumnsVisitor.cpp | 11 ++++++ ...use_constants_in_with_and_select.reference | 5 +++ ...02006_use_constants_in_with_and_select.sql | 36 +++++++++++++++++++ 4 files changed, 55 insertions(+) create mode 100644 tests/queries/0_stateless/02006_use_constants_in_with_and_select.reference create mode 100644 tests/queries/0_stateless/02006_use_constants_in_with_and_select.sql diff --git a/src/Interpreters/QueryNormalizer.cpp b/src/Interpreters/QueryNormalizer.cpp index ea61ade2b49d..7c820622c379 100644 --- a/src/Interpreters/QueryNormalizer.cpp +++ b/src/Interpreters/QueryNormalizer.cpp @@ -256,6 +256,9 @@ void QueryNormalizer::visit(ASTPtr & ast, Data & data) visit(*node_select, ast, data); else if (auto * node_param = ast->as()) throw Exception("Query parameter " + backQuote(node_param->name) + " was not set", ErrorCodes::UNKNOWN_QUERY_PARAMETER); + else if (auto * node_function = ast->as()) + if (node_function->parameters) + visit(node_function->parameters, data); /// If we replace the root of the subtree, we will be called again for the new root, in case the alias is replaced by an alias. if (ast.get() != initial_ast.get()) diff --git a/src/Interpreters/RequiredSourceColumnsVisitor.cpp b/src/Interpreters/RequiredSourceColumnsVisitor.cpp index 2f2a68656bc4..21ec94a6917d 100644 --- a/src/Interpreters/RequiredSourceColumnsVisitor.cpp +++ b/src/Interpreters/RequiredSourceColumnsVisitor.cpp @@ -123,6 +123,17 @@ void RequiredSourceColumnsMatcher::visit(const ASTSelectQuery & select, const AS data.addColumnAliasIfAny(*node); } + if (const auto & with = select.with()) + { + for (auto & node : with->children) + { + if (const auto * identifier = node->as()) + data.addColumnIdentifier(*identifier); + else + data.addColumnAliasIfAny(*node); + } + } + std::vector out; for (const auto & node : select.children) { diff --git a/tests/queries/0_stateless/02006_use_constants_in_with_and_select.reference b/tests/queries/0_stateless/02006_use_constants_in_with_and_select.reference new file mode 100644 index 000000000000..bbf008ffdf20 --- /dev/null +++ b/tests/queries/0_stateless/02006_use_constants_in_with_and_select.reference @@ -0,0 +1,5 @@ +1 [1] +[1] +99.9 +0.1 99.9 +[99.9] diff --git a/tests/queries/0_stateless/02006_use_constants_in_with_and_select.sql b/tests/queries/0_stateless/02006_use_constants_in_with_and_select.sql new file mode 100644 index 000000000000..daca6c5d0c79 --- /dev/null +++ b/tests/queries/0_stateless/02006_use_constants_in_with_and_select.sql @@ -0,0 +1,36 @@ +SELECT + 1 AS max_size, + groupArray(max_size)(col) +FROM + (SELECT col FROM ( + SELECT 1 AS col + UNION ALL + SELECT 2 + ) ORDER BY col); + +WITH 1 AS max_size +SELECT groupArray(max_size)(col) +FROM + (SELECT col FROM ( + SELECT 1 as col + UNION ALL + SELECT 2 + ) ORDER BY col); + +WITH 0.1 AS level +SELECT quantile(level)(number) +FROM numbers(1000); + +SELECT 0.1 AS level, quantile(level)(number) +FROM numbers(1000); + +WITH + 0.1 AS level, + 1 AS max_size +SELECT groupArray(max_size)(col) +FROM + ( + SELECT quantile(level)(number) AS col + FROM numbers(1000) + ); + From 4a2888d707ba05b303f33f87997999b3e08319ff Mon Sep 17 00:00:00 2001 From: Amos Bird Date: Wed, 13 Jul 2022 10:27:43 +0800 Subject: [PATCH 47/72] Fix unused unknown columns introduced by WITH statement #39131 By cherry-picking a8d8293466dcb7874ca692b4a8b437b617303828: Fix unused columns introduced by with stmt --- src/Interpreters/RequiredSourceColumnsVisitor.cpp | 11 ----------- .../02354_with_statement_non_exist_column.reference | 1 + .../02354_with_statement_non_exist_column.sql | 9 +++++++++ 3 files changed, 10 insertions(+), 11 deletions(-) create mode 100644 tests/queries/0_stateless/02354_with_statement_non_exist_column.reference create mode 100644 tests/queries/0_stateless/02354_with_statement_non_exist_column.sql diff --git a/src/Interpreters/RequiredSourceColumnsVisitor.cpp b/src/Interpreters/RequiredSourceColumnsVisitor.cpp index 21ec94a6917d..2f2a68656bc4 100644 --- a/src/Interpreters/RequiredSourceColumnsVisitor.cpp +++ b/src/Interpreters/RequiredSourceColumnsVisitor.cpp @@ -123,17 +123,6 @@ void RequiredSourceColumnsMatcher::visit(const ASTSelectQuery & select, const AS data.addColumnAliasIfAny(*node); } - if (const auto & with = select.with()) - { - for (auto & node : with->children) - { - if (const auto * identifier = node->as()) - data.addColumnIdentifier(*identifier); - else - data.addColumnAliasIfAny(*node); - } - } - std::vector out; for (const auto & node : select.children) { diff --git a/tests/queries/0_stateless/02354_with_statement_non_exist_column.reference b/tests/queries/0_stateless/02354_with_statement_non_exist_column.reference new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/tests/queries/0_stateless/02354_with_statement_non_exist_column.reference @@ -0,0 +1 @@ +1 diff --git a/tests/queries/0_stateless/02354_with_statement_non_exist_column.sql b/tests/queries/0_stateless/02354_with_statement_non_exist_column.sql new file mode 100644 index 000000000000..1a989c1d9529 --- /dev/null +++ b/tests/queries/0_stateless/02354_with_statement_non_exist_column.sql @@ -0,0 +1,9 @@ +WITH x AS y SELECT 1; + +DROP TEMPORARY TABLE IF EXISTS t1; +DROP TEMPORARY TABLE IF EXISTS t2; + +CREATE TEMPORARY TABLE t1 (a Int64); +CREATE TEMPORARY TABLE t2 (a Int64, b Int64); + +WITH b AS bb SELECT bb FROM t2 WHERE a IN (SELECT a FROM t1); From 2f09476cccaad3f431701b22b62dcb2cab01ded8 Mon Sep 17 00:00:00 2001 From: Maksim Kita Date: Thu, 15 Sep 2022 19:30:47 +0200 Subject: [PATCH 48/72] Merge pull request #40901 from ClickHouse/backport/22.3/40732 Backport #40732 to 22.3: Fix memory leak while pushing to MVs w/o query context (from Kafka/...) --- src/Disks/IO/ThreadPoolReader.cpp | 18 ++++---- src/Disks/IO/ThreadPoolRemoteFSReader.cpp | 20 +++++---- src/IO/WriteBufferFromS3.cpp | 1 - src/Interpreters/ThreadStatusExt.cpp | 5 +++ .../Transforms/buildPushingToViewsChain.cpp | 45 ++++++++++--------- 5 files changed, 49 insertions(+), 40 deletions(-) diff --git a/src/Disks/IO/ThreadPoolReader.cpp b/src/Disks/IO/ThreadPoolReader.cpp index 21f73c3129e5..224b7d03b987 100644 --- a/src/Disks/IO/ThreadPoolReader.cpp +++ b/src/Disks/IO/ThreadPoolReader.cpp @@ -201,9 +201,9 @@ std::future ThreadPoolReader::submit(Request reques ProfileEvents::increment(ProfileEvents::ThreadPoolReaderPageCacheMiss); - ThreadGroupStatusPtr running_group = CurrentThread::isInitialized() && CurrentThread::get().getThreadGroup() - ? CurrentThread::get().getThreadGroup() - : MainThreadStatus::getInstance().getThreadGroup(); + ThreadGroupStatusPtr running_group; + if (CurrentThread::isInitialized() && CurrentThread::get().getThreadGroup()) + running_group = CurrentThread::get().getThreadGroup(); ContextPtr query_context; if (CurrentThread::isInitialized()) @@ -213,12 +213,17 @@ std::future ThreadPoolReader::submit(Request reques { ThreadStatus thread_status; - if (query_context) - thread_status.attachQueryContext(query_context); + SCOPE_EXIT({ + if (running_group) + thread_status.detachQuery(); + }); if (running_group) thread_status.attachQuery(running_group); + if (query_context) + thread_status.attachQueryContext(query_context); + setThreadName("ThreadPoolRead"); Stopwatch watch(CLOCK_MONOTONIC); @@ -253,9 +258,6 @@ std::future ThreadPoolReader::submit(Request reques ProfileEvents::increment(ProfileEvents::ThreadPoolReaderPageCacheMissElapsedMicroseconds, watch.elapsedMicroseconds()); ProfileEvents::increment(ProfileEvents::DiskReadElapsedMicroseconds, watch.elapsedMicroseconds()); - if (running_group) - thread_status.detachQuery(); - return Result{ .size = bytes_read, .offset = request.ignore }; }); diff --git a/src/Disks/IO/ThreadPoolRemoteFSReader.cpp b/src/Disks/IO/ThreadPoolRemoteFSReader.cpp index bdb012a6376f..7652b4211aa0 100644 --- a/src/Disks/IO/ThreadPoolRemoteFSReader.cpp +++ b/src/Disks/IO/ThreadPoolRemoteFSReader.cpp @@ -42,9 +42,9 @@ ThreadPoolRemoteFSReader::ThreadPoolRemoteFSReader(size_t pool_size, size_t queu std::future ThreadPoolRemoteFSReader::submit(Request request) { - ThreadGroupStatusPtr running_group = CurrentThread::isInitialized() && CurrentThread::get().getThreadGroup() - ? CurrentThread::get().getThreadGroup() - : MainThreadStatus::getInstance().getThreadGroup(); + ThreadGroupStatusPtr running_group; + if (CurrentThread::isInitialized() && CurrentThread::get().getThreadGroup()) + running_group = CurrentThread::get().getThreadGroup(); ContextPtr query_context; if (CurrentThread::isInitialized()) @@ -54,14 +54,19 @@ std::future ThreadPoolRemoteFSReader::submit(Reques { ThreadStatus thread_status; - /// Save query context if any, because cache implementation needs it. - if (query_context) - thread_status.attachQueryContext(query_context); + SCOPE_EXIT({ + if (running_group) + thread_status.detachQuery(); + }); /// To be able to pass ProfileEvents. if (running_group) thread_status.attachQuery(running_group); + /// Save query context if any, because cache implementation needs it. + if (query_context) + thread_status.attachQueryContext(query_context); + setThreadName("VFSRead"); CurrentMetrics::Increment metric_increment{CurrentMetrics::Read}; @@ -74,9 +79,6 @@ std::future ThreadPoolRemoteFSReader::submit(Reques ProfileEvents::increment(ProfileEvents::RemoteFSReadMicroseconds, watch.elapsedMicroseconds()); ProfileEvents::increment(ProfileEvents::RemoteFSReadBytes, bytes_read); - if (running_group) - thread_status.detachQuery(); - return Result{ .size = bytes_read, .offset = offset }; }); diff --git a/src/IO/WriteBufferFromS3.cpp b/src/IO/WriteBufferFromS3.cpp index eda7bb6f8aed..de7130319a7f 100644 --- a/src/IO/WriteBufferFromS3.cpp +++ b/src/IO/WriteBufferFromS3.cpp @@ -86,7 +86,6 @@ void WriteBufferFromS3::nextImpl() temporary_buffer->write(working_buffer.begin(), offset()); ProfileEvents::increment(ProfileEvents::S3WriteBytes, offset()); - last_part_size += offset(); /// Data size exceeds singlepart upload threshold, need to use multipart upload. diff --git a/src/Interpreters/ThreadStatusExt.cpp b/src/Interpreters/ThreadStatusExt.cpp index 2ea371d3d033..1f2a8e4681a8 100644 --- a/src/Interpreters/ThreadStatusExt.cpp +++ b/src/Interpreters/ThreadStatusExt.cpp @@ -408,6 +408,11 @@ void ThreadStatus::detachQuery(bool exit_if_already_detached, bool thread_exits) query_context.reset(); thread_trace_context.trace_id = 0; thread_trace_context.span_id = 0; + + /// Avoid leaking of ThreadGroupStatus::finished_threads_counters_memory + /// (this is in case someone uses system thread but did not call getProfileEventsCountersAndMemoryForThreads()) + thread_group->getProfileEventsCountersAndMemoryForThreads(); + thread_group.reset(); thread_state = thread_exits ? ThreadState::Died : ThreadState::DetachedFromQuery; diff --git a/src/Processors/Transforms/buildPushingToViewsChain.cpp b/src/Processors/Transforms/buildPushingToViewsChain.cpp index 3cb5b16a2d0d..0d47494d22be 100644 --- a/src/Processors/Transforms/buildPushingToViewsChain.cpp +++ b/src/Processors/Transforms/buildPushingToViewsChain.cpp @@ -237,29 +237,30 @@ Chain buildPushingToViewsChain( ASTPtr query; Chain out; - /// If the materialized view is executed outside of a query, for example as a result of SYSTEM FLUSH LOGS or - /// SYSTEM FLUSH DISTRIBUTED ..., we can't attach to any thread group and we won't log, so there is no point on collecting metrics - std::unique_ptr view_thread_status_ptr = nullptr; - - ThreadGroupStatusPtr running_group = current_thread && current_thread->getThreadGroup() - ? current_thread->getThreadGroup() - : MainThreadStatus::getInstance().getThreadGroup(); - if (running_group) + ThreadGroupStatusPtr running_group; + if (current_thread && current_thread->getThreadGroup()) + running_group = current_thread->getThreadGroup(); + else + running_group = std::make_shared(); + + /// We are creating a ThreadStatus per view to store its metrics individually + /// Since calling ThreadStatus() changes current_thread we save it and restore it after the calls + /// Later on, before doing any task related to a view, we'll switch to its ThreadStatus, do the work, + /// and switch back to the original thread_status. + auto * original_thread = current_thread; + SCOPE_EXIT({ current_thread = original_thread; }); + + std::unique_ptr view_thread_status_ptr = std::make_unique(); + /// Disable query profiler for this ThreadStatus since the running (main query) thread should already have one + /// If we didn't disable it, then we could end up with N + 1 (N = number of dependencies) profilers which means + /// N times more interruptions + view_thread_status_ptr->disableProfiling(); + /// view_thread_status_ptr will be moved later (on and on), so need to capture raw pointer. + view_thread_status_ptr->deleter = [thread_status = view_thread_status_ptr.get(), running_group] { - /// We are creating a ThreadStatus per view to store its metrics individually - /// Since calling ThreadStatus() changes current_thread we save it and restore it after the calls - /// Later on, before doing any task related to a view, we'll switch to its ThreadStatus, do the work, - /// and switch back to the original thread_status. - auto * original_thread = current_thread; - SCOPE_EXIT({ current_thread = original_thread; }); - - view_thread_status_ptr = std::make_unique(); - /// Disable query profiler for this ThreadStatus since the running (main query) thread should already have one - /// If we didn't disable it, then we could end up with N + 1 (N = number of dependencies) profilers which means - /// N times more interruptions - view_thread_status_ptr->disableProfiling(); - view_thread_status_ptr->attachQuery(running_group); - } + thread_status->detachQuery(); + }; + view_thread_status_ptr->attachQuery(running_group); auto runtime_stats = std::make_unique(); runtime_stats->target_name = database_table.getFullTableName(); From 79f9974bbab977fa952efcad20456eb8d27f3321 Mon Sep 17 00:00:00 2001 From: Kruglov Pavel <48961922+Avogar@users.noreply.github.com> Date: Thu, 1 Sep 2022 19:40:40 +0200 Subject: [PATCH 49/72] Merge pull request #40485 from arthurpassos/fix-parquet-chunked-array-deserialization Add support for extended (chunked) arrays for Parquet format --- .../Formats/Impl/ParquetBlockInputFormat.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Processors/Formats/Impl/ParquetBlockInputFormat.cpp b/src/Processors/Formats/Impl/ParquetBlockInputFormat.cpp index 548bf0138f58..1749b5b62aa9 100644 --- a/src/Processors/Formats/Impl/ParquetBlockInputFormat.cpp +++ b/src/Processors/Formats/Impl/ParquetBlockInputFormat.cpp @@ -51,7 +51,16 @@ Chunk ParquetBlockInputFormat::generate() return res; std::shared_ptr table; - arrow::Status read_status = file_reader->ReadRowGroup(row_group_current, column_indices, &table); + + std::unique_ptr<::arrow::RecordBatchReader> rbr; + std::vector row_group_indices { row_group_current }; + arrow::Status get_batch_reader_status = file_reader->GetRecordBatchReader(row_group_indices, column_indices, &rbr); + + if (!get_batch_reader_status.ok()) + throw ParsingException{"Error while reading Parquet data: " + get_batch_reader_status.ToString(), ErrorCodes::CANNOT_READ_ALL_DATA}; + + arrow::Status read_status = rbr->ReadAll(&table); + if (!read_status.ok()) throw ParsingException{"Error while reading Parquet data: " + read_status.ToString(), ErrorCodes::CANNOT_READ_ALL_DATA}; From e20ecad29209a82a9dae460244d33d7026bd85df Mon Sep 17 00:00:00 2001 From: Vasily Nemkov Date: Thu, 14 Jul 2022 20:54:40 +0300 Subject: [PATCH 50/72] Disabled upstream's release workflow Starting a release from release_branches.yml Fixed error in build_report_check.py --- .github/workflows/release.yml | 128 ++++++++++++------------- .github/workflows/release_branches.yml | 11 ++- tests/ci/build_report_check.py | 2 - 3 files changed, 71 insertions(+), 70 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b2ba6e51c774..29f036323a7e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,66 +1,66 @@ -name: ReleaseWorkflow -# - Gets artifacts from S3 -# - Sends it to JFROG Artifactory -# - Adds them to the release assets +# name: ReleaseWorkflow +# # - Gets artifacts from S3 +# # - Sends it to JFROG Artifactory +# # - Adds them to the release assets -on: # yamllint disable-line rule:truthy - release: - types: - - published +# on: # yamllint disable-line rule:truthy +# release: +# types: +# - published -jobs: - ReleasePublish: - runs-on: [self-hosted, style-checker] - steps: - - name: Set envs - run: | - cat >> "$GITHUB_ENV" << 'EOF' - JFROG_API_KEY=${{ secrets.JFROG_KEY_API_PACKAGES }} - TEMP_PATH=${{runner.temp}}/release_packages - REPO_COPY=${{runner.temp}}/release_packages/ClickHouse - EOF - - name: Check out repository code - uses: actions/checkout@v2 - with: - # Always use the most recent script version - ref: master - - name: Download packages and push to Artifactory - run: | - rm -rf "$TEMP_PATH" && mkdir -p "$TEMP_PATH" - cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" - cd "$REPO_COPY" - python3 ./tests/ci/push_to_artifactory.py --release "${{ github.ref }}" \ - --commit '${{ github.sha }}' --all - - name: Upload packages to release assets - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ${{runner.temp}}/push_to_artifactory/* - overwrite: true - tag: ${{ github.ref }} - file_glob: true - ############################################################################################ - ##################################### Docker images ####################################### - ############################################################################################ - DockerServerImages: - runs-on: [self-hosted, style-checker] - steps: - - name: Clear repository - run: | - sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" - - name: Check out repository code - uses: actions/checkout@v2 - with: - fetch-depth: 0 # otherwise we will have no version info - - name: Check docker clickhouse/clickhouse-server building - run: | - cd "$GITHUB_WORKSPACE/tests/ci" - python3 docker_server.py --release-type auto --version "${{ github.ref }}" - python3 docker_server.py --release-type auto --version "${{ github.ref }}" --no-ubuntu \ - --image-repo clickhouse/clickhouse-keeper --image-path docker/keeper - - name: Cleanup - if: always() - run: | - docker kill "$(docker ps -q)" ||: - docker rm -f "$(docker ps -a -q)" ||: - sudo rm -fr "$TEMP_PATH" +# jobs: +# ReleasePublish: +# runs-on: [self-hosted, style-checker] +# steps: +# - name: Set envs +# run: | +# cat >> "$GITHUB_ENV" << 'EOF' +# JFROG_API_KEY=${{ secrets.JFROG_KEY_API_PACKAGES }} +# TEMP_PATH=${{runner.temp}}/release_packages +# REPO_COPY=${{runner.temp}}/release_packages/ClickHouse +# EOF +# - name: Check out repository code +# uses: actions/checkout@v2 +# with: +# # Always use the most recent script version +# ref: master +# - name: Download packages and push to Artifactory +# run: | +# rm -rf "$TEMP_PATH" && mkdir -p "$TEMP_PATH" +# cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" +# cd "$REPO_COPY" +# python3 ./tests/ci/push_to_artifactory.py --release "${{ github.ref }}" \ +# --commit '${{ github.sha }}' --all +# - name: Upload packages to release assets +# uses: svenstaro/upload-release-action@v2 +# with: +# repo_token: ${{ secrets.GITHUB_TOKEN }} +# file: ${{runner.temp}}/push_to_artifactory/* +# overwrite: true +# tag: ${{ github.ref }} +# file_glob: true +# ############################################################################################ +# ##################################### Docker images ####################################### +# ############################################################################################ +# DockerServerImages: +# runs-on: [self-hosted, style-checker] +# steps: +# - name: Clear repository +# run: | +# sudo rm -fr "$GITHUB_WORKSPACE" && mkdir "$GITHUB_WORKSPACE" +# - name: Check out repository code +# uses: actions/checkout@v2 +# with: +# fetch-depth: 0 # otherwise we will have no version info +# - name: Check docker clickhouse/clickhouse-server building +# run: | +# cd "$GITHUB_WORKSPACE/tests/ci" +# python3 docker_server.py --release-type auto --version "${{ github.ref }}" +# python3 docker_server.py --release-type auto --version "${{ github.ref }}" --no-ubuntu \ +# --image-repo clickhouse/clickhouse-keeper --image-path docker/keeper +# - name: Cleanup +# if: always() +# run: | +# docker kill "$(docker ps -q)" ||: +# docker rm -f "$(docker ps -a -q)" ||: +# sudo rm -fr "$TEMP_PATH" diff --git a/.github/workflows/release_branches.yml b/.github/workflows/release_branches.yml index ec78e7715a4c..d0175ae02166 100644 --- a/.github/workflows/release_branches.yml +++ b/.github/workflows/release_branches.yml @@ -12,10 +12,13 @@ on: # yamllint disable-line rule:truthy - opened branches: - '**/22.3*' - push: - branches: - # Anything/22.3 (e.g customizations/22.3) - - '**/22.3*' + release: + types: + - published + # push: + # branches: + # # Anything/22.3 (e.g customizations/22.3) + # - '**/22.3*' jobs: DockerHubPushAarch64: diff --git a/tests/ci/build_report_check.py b/tests/ci/build_report_check.py index a25307ffc446..dbf5adfe1747 100644 --- a/tests/ci/build_report_check.py +++ b/tests/ci/build_report_check.py @@ -286,8 +286,6 @@ def main(): if some_builds_are_missing: addition = f"({len(build_reports)} of {required_builds} builds are OK)" - description = f"{ok_builds}/{total_builds} builds are OK {addition}" - description = f"{ok_groups}/{total_groups} artifact groups are OK {addition}" commit = get_commit(gh, pr_info.sha) From 1c23368380b56c16bd061c7556a80b78fa80ce73 Mon Sep 17 00:00:00 2001 From: Vasily Nemkov Date: Thu, 28 Jul 2022 15:47:38 +0300 Subject: [PATCH 51/72] Proper version with proper VERSION_REVISION Reverted VERSION_REVISION to value that was there before changes from Altinity's size Added VERSION_TWEAK, VERSION_FLAVOUR with proper values Utilizing tweak and flavour in version from autogenerated_versions.txt --- cmake/autogenerated_versions.txt | 8 +++++--- tests/ci/version_helper.py | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/cmake/autogenerated_versions.txt b/cmake/autogenerated_versions.txt index 7a1f566cf86c..4379b55821e2 100644 --- a/cmake/autogenerated_versions.txt +++ b/cmake/autogenerated_versions.txt @@ -2,11 +2,13 @@ # NOTE: has nothing common with DBMS_TCP_PROTOCOL_VERSION, # only DBMS_TCP_PROTOCOL_VERSION should be incremented on protocol changes. -SET(VERSION_REVISION 6) +SET(VERSION_REVISION 54460) SET(VERSION_MAJOR 22) SET(VERSION_MINOR 3) SET(VERSION_PATCH 10) SET(VERSION_GITHASH 7976930b82eed26e8728897d530e044774e0cded) -SET(VERSION_DESCRIBE v22.3.10.19-lts) -SET(VERSION_STRING 22.3.10.19) +SET(VERSION_TWEAK 20) +SET(VERSION_FLAVOUR altinitystable) +SET(VERSION_DESCRIBE v22.3.10.20-altinitystable) +SET(VERSION_STRING 22.3.10.20.altinitystable) # end of autochange diff --git a/tests/ci/version_helper.py b/tests/ci/version_helper.py index f8e93c582ce9..a82fcd97ea76 100755 --- a/tests/ci/version_helper.py +++ b/tests/ci/version_helper.py @@ -214,7 +214,8 @@ def get_version_from_repo( versions["revision"], git, # Explicitly use tweak value from version file - tweak=versions["revision"] + tweak=versions.get("tweak", versions["revision"]), + flavour=versions["flavour"] ) From f46c4373864e2b20f03251b01d2b888a7b9b3310 Mon Sep 17 00:00:00 2001 From: Vasily Nemkov Date: Thu, 4 Aug 2022 13:37:45 +0300 Subject: [PATCH 52/72] Version 22.3.10.23.altinitystable --- cmake/autogenerated_versions.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/autogenerated_versions.txt b/cmake/autogenerated_versions.txt index 4379b55821e2..3c235b85c3c7 100644 --- a/cmake/autogenerated_versions.txt +++ b/cmake/autogenerated_versions.txt @@ -7,8 +7,8 @@ SET(VERSION_MAJOR 22) SET(VERSION_MINOR 3) SET(VERSION_PATCH 10) SET(VERSION_GITHASH 7976930b82eed26e8728897d530e044774e0cded) -SET(VERSION_TWEAK 20) +SET(VERSION_TWEAK 23) SET(VERSION_FLAVOUR altinitystable) -SET(VERSION_DESCRIBE v22.3.10.20-altinitystable) -SET(VERSION_STRING 22.3.10.20.altinitystable) +SET(VERSION_DESCRIBE v22.3.10.23-altinitystable) +SET(VERSION_STRING 22.3.10.23.altinitystable) # end of autochange From 5b36a45244c247a342388645e0bbb91f3da58120 Mon Sep 17 00:00:00 2001 From: Vasily Nemkov Date: Thu, 4 Aug 2022 13:46:55 +0300 Subject: [PATCH 53/72] Removed dependecny on BuilderDebAarch64 --- .github/workflows/release_branches.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release_branches.yml b/.github/workflows/release_branches.yml index d0175ae02166..8ce8b2ccd427 100644 --- a/.github/workflows/release_branches.yml +++ b/.github/workflows/release_branches.yml @@ -179,7 +179,7 @@ jobs: DockerServerImages: needs: - BuilderDebRelease - - BuilderDebAarch64 + # - BuilderDebAarch64 - currently we do not build aarch images runs-on: [self-hosted, style-checker] steps: - name: Clear repository From 5c3647dba102bd9d6ddf7643b04e8f71e329f020 Mon Sep 17 00:00:00 2001 From: Vasily Nemkov Date: Fri, 5 Aug 2022 15:52:47 +0300 Subject: [PATCH 54/72] attempt to fix DockerServerImages --- tests/ci/version_helper.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ci/version_helper.py b/tests/ci/version_helper.py index a82fcd97ea76..8451b3dc4288 100755 --- a/tests/ci/version_helper.py +++ b/tests/ci/version_helper.py @@ -172,9 +172,9 @@ class VersionType: def validate_version(version: str): parts = version.split(".") - if len(parts) != 4: + if len(parts) < 4: raise ValueError(f"{version} does not contain 4 parts") - for part in parts: + for part in parts[:4]: int(part) @@ -222,7 +222,7 @@ def get_version_from_repo( def get_version_from_string(version: str) -> ClickHouseVersion: validate_version(version) parts = version.split(".") - return ClickHouseVersion(parts[0], parts[1], parts[2], -1, git, parts[3]) + return ClickHouseVersion(parts[0], parts[1], parts[2], -1, git, parts[3], parts[4] if len(parts) >= 4 else None) def get_version_from_tag(tag: str) -> ClickHouseVersion: From cd98d6c3d3be574a296a2f4e35b6cd9e65894bd4 Mon Sep 17 00:00:00 2001 From: Vasily Nemkov Date: Wed, 10 Aug 2022 12:32:20 +0300 Subject: [PATCH 55/72] Revert "Merge pull request #39761 from ClickHouse/backport/22.3/39687" This reverts commit 201daa11686033f06dffff0c5a6cda03e86cbdb5, reversing changes made to d7208cac195a3bf19a978f5cb864bb114c269d4f. Reverts encrypted disk changes that introduce bugs --- src/IO/FileEncryptionCommon.h | 1 - src/IO/ReadBufferFromEncryptedFile.cpp | 8 +-- src/IO/tests/gtest_file_encryption.cpp | 51 ------------------- tests/integration/test_encrypted_disk/test.py | 18 ------- 4 files changed, 4 insertions(+), 74 deletions(-) diff --git a/src/IO/FileEncryptionCommon.h b/src/IO/FileEncryptionCommon.h index 496c9e66b206..bb6c8d14893e 100644 --- a/src/IO/FileEncryptionCommon.h +++ b/src/IO/FileEncryptionCommon.h @@ -80,7 +80,6 @@ class Encryptor /// the initialization vector is increased by an index of the current block /// and the index of the current block is calculated from this offset. void setOffset(size_t offset_) { offset = offset_; } - size_t getOffset() const { return offset; } /// Encrypts some data. /// Also the function moves `offset` by `size` (for successive encryptions). diff --git a/src/IO/ReadBufferFromEncryptedFile.cpp b/src/IO/ReadBufferFromEncryptedFile.cpp index c1a87283917c..7aec6dcde02b 100644 --- a/src/IO/ReadBufferFromEncryptedFile.cpp +++ b/src/IO/ReadBufferFromEncryptedFile.cpp @@ -21,6 +21,7 @@ ReadBufferFromEncryptedFile::ReadBufferFromEncryptedFile( , encryptor(header_.algorithm, key_, header_.init_vector) { offset = offset_; + encryptor.setOffset(offset_); need_seek = true; } @@ -59,6 +60,9 @@ off_t ReadBufferFromEncryptedFile::seek(off_t off, int whence) assert(!hasPendingData()); } + /// The encryptor always needs to know what the current offset is. + encryptor.setOffset(new_pos); + return new_pos; } @@ -90,10 +94,6 @@ bool ReadBufferFromEncryptedFile::nextImpl() /// The used cipher algorithms generate the same number of bytes in output as it were in input, /// so after deciphering the numbers of bytes will be still `bytes_read`. working_buffer.resize(bytes_read); - - /// The decryptor needs to know what the current offset is (because it's used in the decryption algorithm). - encryptor.setOffset(offset); - encryptor.decrypt(encrypted_buffer.data(), bytes_read, working_buffer.begin()); pos = working_buffer.begin(); diff --git a/src/IO/tests/gtest_file_encryption.cpp b/src/IO/tests/gtest_file_encryption.cpp index cae40afbb385..3a114f94ee05 100644 --- a/src/IO/tests/gtest_file_encryption.cpp +++ b/src/IO/tests/gtest_file_encryption.cpp @@ -4,13 +4,6 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include using namespace DB; @@ -217,48 +210,4 @@ INSTANTIATE_TEST_SUITE_P(All, }) ); -TEST(FileEncryptionPositionUpdateTest, Decryption) -{ - String tmp_path = std::filesystem::current_path() / "test_offset_update"; - if (std::filesystem::exists(tmp_path)) - std::filesystem::remove(tmp_path); - - String key = "1234567812345678"; - FileEncryption::Header header; - header.algorithm = Algorithm::AES_128_CTR; - header.key_id = 1; - header.key_hash = calculateKeyHash(key); - header.init_vector = InitVector::random(); - - auto lwb = std::make_unique(tmp_path); - WriteBufferFromEncryptedFile wb(10, std::move(lwb), key, header); - auto data = getRandomASCIIString(20); - wb.write(data.data(), data.size()); - wb.finalize(); - - auto lrb = std::make_unique(tmp_path); - ReadBufferFromEncryptedFile rb(10, std::move(lrb), key, header); - rb.ignore(5); - rb.ignore(5); - rb.ignore(5); - ASSERT_EQ(rb.getPosition(), 15); - - String res; - readStringUntilEOF(res, rb); - ASSERT_EQ(res, data.substr(15)); - res.clear(); - - rb.seek(0, SEEK_SET); - ASSERT_EQ(rb.getPosition(), 0); - res.resize(5); - rb.read(res.data(), res.size()); - ASSERT_EQ(res, data.substr(0, 5)); - res.clear(); - - rb.seek(1, SEEK_CUR); - ASSERT_EQ(rb.getPosition(), 6); - readStringUntilEOF(res, rb); - ASSERT_EQ(res, data.substr(6)); -} - #endif diff --git a/tests/integration/test_encrypted_disk/test.py b/tests/integration/test_encrypted_disk/test.py index 17a30676f7f7..4e6d1db9e99f 100644 --- a/tests/integration/test_encrypted_disk/test.py +++ b/tests/integration/test_encrypted_disk/test.py @@ -252,21 +252,3 @@ def make_storage_policy_with_keys(policy_name, keys): # Detach the part encrypted with the wrong key and check that another part containing "(2,'data'),(3,'data')" still can be read. node.query("ALTER TABLE encrypted_test DETACH PART '{}'".format(FIRST_PART_NAME)) assert node.query(select_query) == "(2,'data'),(3,'data')" - - -def test_read_in_order(): - node.query( - "CREATE TABLE encrypted_test(`a` UInt64, `b` String(150)) ENGINE = MergeTree() ORDER BY (a, b) SETTINGS storage_policy='encrypted_policy'" - ) - - node.query( - "INSERT INTO encrypted_test SELECT * FROM generateRandom('a UInt64, b FixedString(150)') LIMIT 100000" - ) - - node.query( - "SELECT * FROM encrypted_test ORDER BY a, b SETTINGS optimize_read_in_order=1 FORMAT Null" - ) - - node.query( - "SELECT * FROM encrypted_test ORDER BY a, b SETTINGS optimize_read_in_order=0 FORMAT Null" - ) From aa4377bb00ab541252572b71811bd19909080e8e Mon Sep 17 00:00:00 2001 From: Vasily Nemkov Date: Wed, 10 Aug 2022 22:59:22 +0300 Subject: [PATCH 56/72] Revert "Merge pull request #27531 from abel-cheng/with-constants" This reverts commit a020fe3b1bcd74d361be38bbc331b3c33dd83864, reversing changes made to 8c06abee739f4cf30fbdfe1b08ed4af566e46a9f. Reverted to fix #37812 missing columns in case of using constants in CTE --- src/Interpreters/QueryNormalizer.cpp | 3 -- .../RequiredSourceColumnsVisitor.cpp | 11 ------ ...use_constants_in_with_and_select.reference | 5 --- ...02006_use_constants_in_with_and_select.sql | 36 ------------------- 4 files changed, 55 deletions(-) delete mode 100644 tests/queries/0_stateless/02006_use_constants_in_with_and_select.reference delete mode 100644 tests/queries/0_stateless/02006_use_constants_in_with_and_select.sql diff --git a/src/Interpreters/QueryNormalizer.cpp b/src/Interpreters/QueryNormalizer.cpp index 7c820622c379..ea61ade2b49d 100644 --- a/src/Interpreters/QueryNormalizer.cpp +++ b/src/Interpreters/QueryNormalizer.cpp @@ -256,9 +256,6 @@ void QueryNormalizer::visit(ASTPtr & ast, Data & data) visit(*node_select, ast, data); else if (auto * node_param = ast->as()) throw Exception("Query parameter " + backQuote(node_param->name) + " was not set", ErrorCodes::UNKNOWN_QUERY_PARAMETER); - else if (auto * node_function = ast->as()) - if (node_function->parameters) - visit(node_function->parameters, data); /// If we replace the root of the subtree, we will be called again for the new root, in case the alias is replaced by an alias. if (ast.get() != initial_ast.get()) diff --git a/src/Interpreters/RequiredSourceColumnsVisitor.cpp b/src/Interpreters/RequiredSourceColumnsVisitor.cpp index 21ec94a6917d..2f2a68656bc4 100644 --- a/src/Interpreters/RequiredSourceColumnsVisitor.cpp +++ b/src/Interpreters/RequiredSourceColumnsVisitor.cpp @@ -123,17 +123,6 @@ void RequiredSourceColumnsMatcher::visit(const ASTSelectQuery & select, const AS data.addColumnAliasIfAny(*node); } - if (const auto & with = select.with()) - { - for (auto & node : with->children) - { - if (const auto * identifier = node->as()) - data.addColumnIdentifier(*identifier); - else - data.addColumnAliasIfAny(*node); - } - } - std::vector out; for (const auto & node : select.children) { diff --git a/tests/queries/0_stateless/02006_use_constants_in_with_and_select.reference b/tests/queries/0_stateless/02006_use_constants_in_with_and_select.reference deleted file mode 100644 index bbf008ffdf20..000000000000 --- a/tests/queries/0_stateless/02006_use_constants_in_with_and_select.reference +++ /dev/null @@ -1,5 +0,0 @@ -1 [1] -[1] -99.9 -0.1 99.9 -[99.9] diff --git a/tests/queries/0_stateless/02006_use_constants_in_with_and_select.sql b/tests/queries/0_stateless/02006_use_constants_in_with_and_select.sql deleted file mode 100644 index daca6c5d0c79..000000000000 --- a/tests/queries/0_stateless/02006_use_constants_in_with_and_select.sql +++ /dev/null @@ -1,36 +0,0 @@ -SELECT - 1 AS max_size, - groupArray(max_size)(col) -FROM - (SELECT col FROM ( - SELECT 1 AS col - UNION ALL - SELECT 2 - ) ORDER BY col); - -WITH 1 AS max_size -SELECT groupArray(max_size)(col) -FROM - (SELECT col FROM ( - SELECT 1 as col - UNION ALL - SELECT 2 - ) ORDER BY col); - -WITH 0.1 AS level -SELECT quantile(level)(number) -FROM numbers(1000); - -SELECT 0.1 AS level, quantile(level)(number) -FROM numbers(1000); - -WITH - 0.1 AS level, - 1 AS max_size -SELECT groupArray(max_size)(col) -FROM - ( - SELECT quantile(level)(number) AS col - FROM numbers(1000) - ); - From 7cc4deb13f988aa2bead82083a3c7c35d3819bba Mon Sep 17 00:00:00 2001 From: Vladimir Chebotaryov Date: Sun, 31 Jul 2022 23:36:20 +0300 Subject: [PATCH 57/72] Fixed using `column_after_join` for handling `WINDOW` expressions in `ExpressionAnalyzer`, shall be `aggregated_columns`. --- src/Interpreters/ExpressionAnalyzer.cpp | 4 ++-- .../reuseStorageOrderingForWindowFunctions.cpp | 2 +- ...optimizations_optimize_read_in_window_order.reference | 9 +++++++++ ...5_plan_optimizations_optimize_read_in_window_order.sh | 9 +++++++++ 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/Interpreters/ExpressionAnalyzer.cpp b/src/Interpreters/ExpressionAnalyzer.cpp index 61fd9c0baf20..bc2745ce19d2 100644 --- a/src/Interpreters/ExpressionAnalyzer.cpp +++ b/src/Interpreters/ExpressionAnalyzer.cpp @@ -677,7 +677,7 @@ void ExpressionAnalyzer::makeWindowDescriptionFromAST(const Context & context_, with_alias->getColumnName(), 1 /* direction */, 1 /* nulls_direction */)); - auto actions_dag = std::make_shared(columns_after_join); + auto actions_dag = std::make_shared(aggregated_columns); getRootActions(column_ast, false, actions_dag); desc.partition_by_actions.push_back(std::move(actions_dag)); } @@ -698,7 +698,7 @@ void ExpressionAnalyzer::makeWindowDescriptionFromAST(const Context & context_, order_by_element.direction, order_by_element.nulls_direction)); - auto actions_dag = std::make_shared(columns_after_join); + auto actions_dag = std::make_shared(aggregated_columns); getRootActions(column_ast, false, actions_dag); desc.order_by_actions.push_back(std::move(actions_dag)); } diff --git a/src/Processors/QueryPlan/Optimizations/reuseStorageOrderingForWindowFunctions.cpp b/src/Processors/QueryPlan/Optimizations/reuseStorageOrderingForWindowFunctions.cpp index c68ec47edff0..547e29106a4f 100644 --- a/src/Processors/QueryPlan/Optimizations/reuseStorageOrderingForWindowFunctions.cpp +++ b/src/Processors/QueryPlan/Optimizations/reuseStorageOrderingForWindowFunctions.cpp @@ -30,7 +30,7 @@ size_t tryReuseStorageOrderingForWindowFunctions(QueryPlan::Node * parent_node, { /// Find the following sequence of steps, add InputOrderInfo and apply prefix sort description to /// SortingStep: - /// WindowStep <- SortingStep <- [Expression] <- [SettingQuotaAndLimits] <- ReadFromMergeTree + /// WindowStep <- SortingStep <- [Expression] <- ReadFromMergeTree auto * window_node = parent_node; auto * window = typeid_cast(window_node->step.get()); diff --git a/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.reference b/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.reference index 7fcd29b5faf9..00eb03bd5f02 100644 --- a/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.reference +++ b/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.reference @@ -10,3 +10,12 @@ No sorting plan optimize_read_in_window_order=1 Prefix sort description: n ASC, x ASC Result sort description: n ASC, x ASC +Complex ORDER BY + optimize_read_in_window_order=0 +3 3 1 +4 5 2 +5 7 3 + optimize_read_in_window_order=1 +3 3 1 +4 5 2 +5 7 3 diff --git a/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.sh b/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.sh index 418baea81136..328d181fadd7 100755 --- a/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.sh +++ b/tests/queries/0_stateless/01655_plan_optimizations_optimize_read_in_window_order.sh @@ -31,6 +31,15 @@ $CLICKHOUSE_CLIENT -q "explain plan actions=1, description=1 select n, sum(x) OV echo ' optimize_read_in_window_order=1' $CLICKHOUSE_CLIENT -q "explain plan actions=1, description=1 select n, sum(x) OVER (ORDER BY n, x ROWS BETWEEN 100 PRECEDING AND CURRENT ROW) from ${name}_n_x SETTINGS optimize_read_in_window_order=1" | grep -i "sort description" +echo 'Complex ORDER BY' +$CLICKHOUSE_CLIENT -q "CREATE TABLE ${name}_complex (unique1 Int32, unique2 Int32, ten Int32) ENGINE=MergeTree ORDER BY tuple() SETTINGS index_granularity = 8192" +$CLICKHOUSE_CLIENT -q "INSERT INTO ${name}_complex VALUES (1, 2, 3), (2, 3, 4), (3, 4, 5)" +echo ' optimize_read_in_window_order=0' +$CLICKHOUSE_CLIENT -q "SELECT ten, sum(unique1) + sum(unique2) AS res, rank() OVER (ORDER BY sum(unique1) + sum(unique2) ASC) AS rank FROM ${name}_complex GROUP BY ten ORDER BY ten ASC SETTINGS optimize_read_in_window_order=0" +echo ' optimize_read_in_window_order=1' +$CLICKHOUSE_CLIENT -q "SELECT ten, sum(unique1) + sum(unique2) AS res, rank() OVER (ORDER BY sum(unique1) + sum(unique2) ASC) AS rank FROM ${name}_complex GROUP BY ten ORDER BY ten ASC SETTINGS optimize_read_in_window_order=1" + $CLICKHOUSE_CLIENT -q "drop table ${name}" $CLICKHOUSE_CLIENT -q "drop table ${name}_n" $CLICKHOUSE_CLIENT -q "drop table ${name}_n_x" +$CLICKHOUSE_CLIENT -q "drop table ${name}_complex" From 6f30e98c8e436aaa97d8c7ae139ca06afd6601be Mon Sep 17 00:00:00 2001 From: Vladimir Chebotaryov Date: Tue, 26 Jul 2022 09:05:31 +0300 Subject: [PATCH 58/72] Fixed point of origin for exponential decay window functions to the last value in window. --- src/Processors/Transforms/WindowTransform.cpp | 224 ++++++++++-------- src/Processors/Transforms/WindowTransform.h | 14 ++ .../02020_exponential_smoothing.reference | 34 +-- 3 files changed, 150 insertions(+), 122 deletions(-) diff --git a/src/Processors/Transforms/WindowTransform.cpp b/src/Processors/Transforms/WindowTransform.cpp index 2a2fed1cc078..b7e02c27adf0 100644 --- a/src/Processors/Transforms/WindowTransform.cpp +++ b/src/Processors/Transforms/WindowTransform.cpp @@ -965,9 +965,6 @@ void WindowTransform::updateAggregationState() } } } - - prev_frame_start = frame_start; - prev_frame_end = frame_end; } void WindowTransform::writeOutCurrentRow() @@ -1209,6 +1206,9 @@ void WindowTransform::appendChunk(Chunk & chunk) return; } + prev_frame_start = frame_start; + prev_frame_end = frame_end; + // Move to the next row. The frame will have to be recalculated. // The peer group start is updated at the beginning of the loop, // because current_row might now be past-the-end. @@ -1614,16 +1614,12 @@ struct StatefulWindowFunction : public WindowFunction struct ExponentialTimeDecayedSumState { - RowNumber previous_frame_start; - RowNumber previous_frame_end; Float64 previous_time; Float64 previous_sum; }; struct ExponentialTimeDecayedAvgState { - RowNumber previous_frame_start; - RowNumber previous_frame_end; Float64 previous_time; Float64 previous_sum; Float64 previous_count; @@ -1682,40 +1678,43 @@ struct WindowFunctionExponentialTimeDecayedSum final : public StatefulWindowFunc auto & state = getState(workspace); Float64 result = 0; - Float64 curr_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, transform->current_row); - if (state.previous_frame_start <= transform->frame_start - && transform->frame_start < state.previous_frame_end - && state.previous_frame_end <= transform->frame_end) + if (transform->frame_start < transform->frame_end) { - for (RowNumber i = state.previous_frame_start; i < transform->frame_start; transform->advanceRowNumber(i)) - { - Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); - Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); - result -= std::exp((prev_t - curr_t) / decay_length) * prev_val; - } - result += std::exp((state.previous_time - curr_t) / decay_length) * state.previous_sum; - for (RowNumber i = state.previous_frame_end; i < transform->frame_end; transform->advanceRowNumber(i)) + RowNumber frame_back = transform->prevRowNumber(transform->frame_end); + Float64 back_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, frame_back); + + if (transform->prev_frame_start <= transform->frame_start + && transform->frame_start < transform->prev_frame_end + && transform->prev_frame_end <= transform->frame_end) { - Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); - Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); - result += std::exp((prev_t - curr_t) / decay_length) * prev_val; + for (RowNumber i = transform->prev_frame_start; i < transform->frame_start; transform->advanceRowNumber(i)) + { + Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + result -= std::exp((prev_t - back_t) / decay_length) * prev_val; + } + result += std::exp((state.previous_time - back_t) / decay_length) * state.previous_sum; + for (RowNumber i = transform->prev_frame_end; i < transform->frame_end; transform->advanceRowNumber(i)) + { + Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + result += std::exp((prev_t - back_t) / decay_length) * prev_val; + } } - } - else - { - for (RowNumber i = transform->frame_start; i < transform->frame_end; transform->advanceRowNumber(i)) + else { - Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); - Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); - result += std::exp((prev_t - curr_t) / decay_length) * prev_val; + for (RowNumber i = transform->frame_start; i < transform->frame_end; transform->advanceRowNumber(i)) + { + Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + result += std::exp((prev_t - back_t) / decay_length) * prev_val; + } } - } - state.previous_sum = result; - state.previous_time = curr_t; - state.previous_frame_start = transform->frame_start; - state.previous_frame_end = transform->frame_end; + state.previous_sum = result; + state.previous_time = back_t; + } WindowFunctionHelpers::setValueToOutputColumn(transform, function_index, result); } @@ -1773,18 +1772,24 @@ struct WindowFunctionExponentialTimeDecayedMax final : public WindowFunction void windowInsertResultInto(const WindowTransform * transform, size_t function_index) override { - Float64 result = std::numeric_limits::lowest(); - Float64 curr_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, transform->current_row); + Float64 result = std::numeric_limits::quiet_NaN(); - for (RowNumber i = transform->frame_start; i < transform->frame_end; transform->advanceRowNumber(i)) + if (transform->frame_start < transform->frame_end) { - Float64 value = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); - Float64 t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + result = std::numeric_limits::lowest(); + RowNumber frame_back = transform->prevRowNumber(transform->frame_end); + Float64 back_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, frame_back); - /// Avoiding extra calls to `exp` and multiplications. - if (value > result || t > curr_t || result < 0) + for (RowNumber i = transform->frame_start; i < transform->frame_end; transform->advanceRowNumber(i)) { - result = std::max(std::exp((t - curr_t) / decay_length) * value, result); + Float64 value = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); + Float64 t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + + /// Avoiding extra calls to `exp` and multiplications. + if (value > result || t > back_t || result < 0) + { + result = std::max(std::exp((t - back_t) / decay_length) * value, result); + } } } @@ -1839,37 +1844,40 @@ struct WindowFunctionExponentialTimeDecayedCount final : public StatefulWindowFu auto & state = getState(workspace); Float64 result = 0; - Float64 curr_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, transform->current_row); - if (state.previous_frame_start <= transform->frame_start - && transform->frame_start < state.previous_frame_end - && state.previous_frame_end <= transform->frame_end) + if (transform->frame_start < transform->frame_end) { - for (RowNumber i = state.previous_frame_start; i < transform->frame_start; transform->advanceRowNumber(i)) - { - Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); - result -= std::exp((prev_t - curr_t) / decay_length); - } - result += std::exp((state.previous_time - curr_t) / decay_length) * state.previous_sum; - for (RowNumber i = state.previous_frame_end; i < transform->frame_end; transform->advanceRowNumber(i)) + RowNumber frame_back = transform->prevRowNumber(transform->frame_end); + Float64 back_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, frame_back); + + if (transform->prev_frame_start <= transform->frame_start + && transform->frame_start < transform->prev_frame_end + && transform->prev_frame_end <= transform->frame_end) { - Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); - result += std::exp((prev_t - curr_t) / decay_length); + for (RowNumber i = transform->prev_frame_start; i < transform->frame_start; transform->advanceRowNumber(i)) + { + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + result -= std::exp((prev_t - back_t) / decay_length); + } + result += std::exp((state.previous_time - back_t) / decay_length) * state.previous_sum; + for (RowNumber i = transform->prev_frame_end; i < transform->frame_end; transform->advanceRowNumber(i)) + { + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + result += std::exp((prev_t - back_t) / decay_length); + } } - } - else - { - for (RowNumber i = transform->frame_start; i < transform->frame_end; transform->advanceRowNumber(i)) + else { - Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); - result += std::exp((prev_t - curr_t) / decay_length); + for (RowNumber i = transform->frame_start; i < transform->frame_end; transform->advanceRowNumber(i)) + { + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + result += std::exp((prev_t - back_t) / decay_length); + } } - } - state.previous_sum = result; - state.previous_time = curr_t; - state.previous_frame_start = transform->frame_start; - state.previous_frame_end = transform->frame_end; + state.previous_sum = result; + state.previous_time = back_t; + } WindowFunctionHelpers::setValueToOutputColumn(transform, function_index, result); } @@ -1932,55 +1940,61 @@ struct WindowFunctionExponentialTimeDecayedAvg final : public StatefulWindowFunc Float64 count = 0; Float64 sum = 0; - Float64 curr_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, transform->current_row); + Float64 result = std::numeric_limits::quiet_NaN(); - if (state.previous_frame_start <= transform->frame_start - && transform->frame_start < state.previous_frame_end - && state.previous_frame_end <= transform->frame_end) + if (transform->frame_start < transform->frame_end) { - for (RowNumber i = state.previous_frame_start; i < transform->frame_start; transform->advanceRowNumber(i)) - { - Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); - Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); - Float64 decay = std::exp((prev_t - curr_t) / decay_length); - sum -= decay * prev_val; - count -= decay; - } + RowNumber frame_back = transform->prevRowNumber(transform->frame_end); + Float64 back_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, frame_back); + if (transform->prev_frame_start <= transform->frame_start + && transform->frame_start < transform->prev_frame_end + && transform->prev_frame_end <= transform->frame_end) { - Float64 decay = std::exp((state.previous_time - curr_t) / decay_length); - sum += decay * state.previous_sum; - count += decay * state.previous_count; - } + for (RowNumber i = transform->prev_frame_start; i < transform->frame_start; transform->advanceRowNumber(i)) + { + Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + Float64 decay = std::exp((prev_t - back_t) / decay_length); + sum -= decay * prev_val; + count -= decay; + } - for (RowNumber i = state.previous_frame_end; i < transform->frame_end; transform->advanceRowNumber(i)) - { - Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); - Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); - Float64 decay = std::exp((prev_t - curr_t) / decay_length); - sum += decay * prev_val; - count += decay; + { + Float64 decay = std::exp((state.previous_time - back_t) / decay_length); + sum += decay * state.previous_sum; + count += decay * state.previous_count; + } + + for (RowNumber i = transform->prev_frame_end; i < transform->frame_end; transform->advanceRowNumber(i)) + { + Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + Float64 decay = std::exp((prev_t - back_t) / decay_length); + sum += decay * prev_val; + count += decay; + } } - } - else - { - for (RowNumber i = transform->frame_start; i < transform->frame_end; transform->advanceRowNumber(i)) + else { - Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); - Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); - Float64 decay = std::exp((prev_t - curr_t) / decay_length); - sum += decay * prev_val; - count += decay; + for (RowNumber i = transform->frame_start; i < transform->frame_end; transform->advanceRowNumber(i)) + { + Float64 prev_val = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_VALUE, i); + Float64 prev_t = WindowFunctionHelpers::getValue(transform, function_index, ARGUMENT_TIME, i); + Float64 decay = std::exp((prev_t - back_t) / decay_length); + sum += decay * prev_val; + count += decay; + } } - } - state.previous_sum = sum; - state.previous_count = count; - state.previous_time = curr_t; - state.previous_frame_start = transform->frame_start; - state.previous_frame_end = transform->frame_end; + state.previous_sum = sum; + state.previous_count = count; + state.previous_time = back_t; - WindowFunctionHelpers::setValueToOutputColumn(transform, function_index, sum/count); + result = sum/count; + } + + WindowFunctionHelpers::setValueToOutputColumn(transform, function_index, result); } private: diff --git a/src/Processors/Transforms/WindowTransform.h b/src/Processors/Transforms/WindowTransform.h index d536c8780d21..5bedfa1fb29a 100644 --- a/src/Processors/Transforms/WindowTransform.h +++ b/src/Processors/Transforms/WindowTransform.h @@ -198,6 +198,13 @@ class WindowTransform final : public IProcessor ++x.block; } + RowNumber nextRowNumber(const RowNumber & x) const + { + RowNumber result = x; + advanceRowNumber(result); + return result; + } + void retreatRowNumber(RowNumber & x) const { if (x.row > 0) @@ -219,6 +226,13 @@ class WindowTransform final : public IProcessor #endif } + RowNumber prevRowNumber(const RowNumber & x) const + { + RowNumber result = x; + retreatRowNumber(result); + return result; + } + auto moveRowNumber(const RowNumber & _x, int64_t offset) const; auto moveRowNumberNoCheck(const RowNumber & _x, int64_t offset) const; diff --git a/tests/queries/0_stateless/02020_exponential_smoothing.reference b/tests/queries/0_stateless/02020_exponential_smoothing.reference index 334d32e1c163..5481bfe80f8d 100644 --- a/tests/queries/0_stateless/02020_exponential_smoothing.reference +++ b/tests/queries/0_stateless/02020_exponential_smoothing.reference @@ -654,23 +654,23 @@ exponentialTimeDecayedAvg 0 48 0.201 ████████████████████ 0 49 0.196 ███████████████████▌ Check `exponentialTimeDecayed.*` supports sliding windows -2 1 3.010050167084 2 3.030251507111 0.993333444442 -1 2 7.060905027605 4.080805360107 4.02030134086 1.756312382816 -0 3 12.091654548833 5.101006700134 5.000500014167 2.418089094006 -4 4 11.050650848754 5.050250835421 5.000500014167 2.209909172572 -5 5 9.970249502081 5 5.000500014167 1.993850509716 -1 6 20.07305726224 10.202013400268 5.000500014167 4.014210020072 -0 7 15.991544871125 10.100501670842 3.98029867414 4.017674596889 +2 1 2.950447180363 1.960397346614 2.970248507056 0.993333444442 +1 2 6.921089740404 4 3.940694040604 1.756312382816 +0 3 11.85222374685 5 4.901483479757 2.418089094006 +4 4 10.831833301125 4.950249168746 4.901483479757 2.209909172572 +5 5 9.772825334477 4.900993366534 4.901483479757 1.993850509716 +1 6 19.675584097659 10 4.901483479757 4.014210020072 +0 7 15.832426341049 10 3.940694040604 4.017674596889 10 8 10.980198673307 10 2.970248507056 3.696727276261 Check `exponentialTimeDecayedMax` works with negative values -2 1 -1.010050167084 -1 2 -1 -10 3 -0.990049833749 -4 4 -0.980198673307 -5 5 -1.010050167084 -1 6 -1 -10 7 -0.990049833749 -10 8 -0.980198673307 -10 9 -9.801986733068 -9.81 10 -9.801986733068 +2 1 -0.990049833749 +1 2 -0.980198673307 +10 3 -0.970445533549 +4 4 -0.960789439152 +5 5 -0.990049833749 +1 6 -0.980198673307 +10 7 -0.970445533549 +10 8 -0.960789439152 +10 9 -9.607894391523 +9.81 10 -9.704455335485 9.9 11 -9.712388869079 From aaece10f3a5074afea8f3bcb3f88b2ec32338a19 Mon Sep 17 00:00:00 2001 From: Vladimir Chebotaryov Date: Thu, 4 Aug 2022 18:08:32 +0300 Subject: [PATCH 59/72] Fixed tests on Debug build type. --- src/Processors/Transforms/WindowTransform.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Processors/Transforms/WindowTransform.h b/src/Processors/Transforms/WindowTransform.h index 5bedfa1fb29a..dedc8c9941cc 100644 --- a/src/Processors/Transforms/WindowTransform.h +++ b/src/Processors/Transforms/WindowTransform.h @@ -207,6 +207,10 @@ class WindowTransform final : public IProcessor void retreatRowNumber(RowNumber & x) const { +#ifndef NDEBUG + auto original_x = x; +#endif + if (x.row > 0) { --x.row; @@ -220,9 +224,9 @@ class WindowTransform final : public IProcessor x.row = blockAt(x).rows - 1; #ifndef NDEBUG - auto xx = x; - advanceRowNumber(xx); - assert(xx == x); + auto advanced_retreated_x = x; + advanceRowNumber(advanced_retreated_x); + assert(advanced_retreated_x == original_x); #endif } From 29db766d76888774f7825a2775925d3c4dcd1af0 Mon Sep 17 00:00:00 2001 From: Arthur Passos Date: Thu, 18 Aug 2022 15:28:43 -0300 Subject: [PATCH 60/72] Use gh-data.checks table instead of default.checks for docker_server --- tests/ci/docker_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ci/docker_server.py b/tests/ci/docker_server.py index 4a03b49c5aa6..83c4a0089cdf 100644 --- a/tests/ci/docker_server.py +++ b/tests/ci/docker_server.py @@ -357,7 +357,7 @@ def main(): NAME, ) ch_helper = ClickHouseHelper() - ch_helper.insert_events_into(db="default", table="checks", events=prepared_events) + ch_helper.insert_events_into(db="gh-data", table="checks", events=prepared_events) if __name__ == "__main__": From 83643f71c906f48350c6c716d3ee59a8664d2fe5 Mon Sep 17 00:00:00 2001 From: Vitaliy Zakaznikov Date: Thu, 25 Aug 2022 16:39:25 -0400 Subject: [PATCH 61/72] Updating ldap tests and how they are launched. --- tests/testflows/aes_encryption/regression.py | 3 + .../tests/authentications.py | 2 +- tests/testflows/regression.py | 97 +++++++++---------- 3 files changed, 49 insertions(+), 53 deletions(-) diff --git a/tests/testflows/aes_encryption/regression.py b/tests/testflows/aes_encryption/regression.py index c12aaca861d5..80747df5d11e 100755 --- a/tests/testflows/aes_encryption/regression.py +++ b/tests/testflows/aes_encryption/regression.py @@ -13,8 +13,11 @@ issue_18250 = "https://github.com/ClickHouse/ClickHouse/issues/18250" issue_18251 = "https://github.com/ClickHouse/ClickHouse/issues/18251" issue_24029 = "https://github.com/ClickHouse/ClickHouse/issues/24029" +issue_39987 = "https://github.com/ClickHouse/ClickHouse/issues/39987" xfails = { + # decrypt + "decrypt/invalid parameters/null in ciphertext": [(Fail, issue_39987)], # encrypt "encrypt/invalid key or iv length for mode/mode=\"'aes-???-gcm'\", key_len=??, iv_len=12, aad=True/iv is too short": [ (Fail, "known issue") diff --git a/tests/testflows/ldap/external_user_directory/tests/authentications.py b/tests/testflows/ldap/external_user_directory/tests/authentications.py index 83daa175a248..e2d77c9a1b11 100644 --- a/tests/testflows/ldap/external_user_directory/tests/authentications.py +++ b/tests/testflows/ldap/external_user_directory/tests/authentications.py @@ -1274,7 +1274,7 @@ def repeat_requests(self, server, iterations, vcd_value, rbac=False): "1.0" ) ) -def verification_cooldown_performance(self, server, rbac=False, iterations=5000): +def verification_cooldown_performance(self, server, rbac=False, iterations=500): """Check login performance when the verification cooldown parameter is set to a positive value when comparing to the case when the verification cooldown parameter is turned off. diff --git a/tests/testflows/regression.py b/tests/testflows/regression.py index bce8274c5cc0..c4729296e02b 100755 --- a/tests/testflows/regression.py +++ b/tests/testflows/regression.py @@ -10,9 +10,7 @@ @TestModule @Name("clickhouse") @ArgumentParser(argparser) -def regression( - self, local, clickhouse_binary_path, clickhouse_version=None, stress=None -): +def regression(self, local, clickhouse_binary_path, clickhouse_version, stress=None): """ClickHouse regression.""" args = { "local": local, @@ -22,55 +20,50 @@ def regression( } self.context.stress = stress - self.context.clickhouse_version = clickhouse_version - - with Pool(8) as pool: - try: - Feature( - test=load("example.regression", "regression"), - parallel=True, - executor=pool, - )(**args) - Feature( - test=load("ldap.regression", "regression"), parallel=True, executor=pool - )(**args) - Feature( - test=load("rbac.regression", "regression"), parallel=True, executor=pool - )(**args) - Feature( - test=load("aes_encryption.regression", "regression"), - parallel=True, - executor=pool, - )( - **args - ) # TODO: fix it! - # Feature(test=load("map_type.regression", "regression"), parallel=True, executor=pool)(**args) # TODO: fix it! - Feature( - test=load("window_functions.regression", "regression"), - parallel=True, - executor=pool, - )( - **args - ) # TODO: fix it! - Feature( - test=load("datetime64_extended_range.regression", "regression"), - parallel=True, - executor=pool, - )(**args) - Feature( - test=load("kerberos.regression", "regression"), - parallel=True, - executor=pool, - )(**args) - Feature( - test=load("extended_precision_data_types.regression", "regression"), - parallel=True, - executor=pool, - )( - **args - ) # TODO: fix it! - finally: - join() + + try: + Feature(test=load("aes_encryption.regression", "regression"), parallel=True)( + **args + ) + + Feature( + test=load("datetime64_extended_range.regression", "regression"), + parallel=True, + )(**args) + + Feature(test=load("example.regression", "regression"), parallel=True)(**args) + + Feature( + test=load("extended_precision_data_types.regression", "regression"), + parallel=True, + )(**args) + + Feature( + test=load("ldap.authentication.regression", "regression"), parallel=True + )(**args) + + join() + + Feature( + test=load("ldap.external_user_directory.regression", "regression"), + parallel=True, + )(**args) + + join() + + Feature(test=load("ldap.role_mapping.regression", "regression"), parallel=True)( + **args + ) + + Feature(test=load("map_type.regression", "regression"), parallel=True)(**args) + + Feature(test=load("rbac.regression", "regression"), parallel=True)(**args) + + Feature(test=load("window_functions.regression", "regression"), parallel=True)( + **args + ) + finally: + join() if main(): From 63f4a0ecbc6a6ab5f0a4c90f93847bd28834fea2 Mon Sep 17 00:00:00 2001 From: Vitaliy Zakaznikov Date: Thu, 25 Aug 2022 17:05:42 -0400 Subject: [PATCH 62/72] Updating interations count to improve test stability. --- tests/testflows/ldap/authentication/tests/authentications.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testflows/ldap/authentication/tests/authentications.py b/tests/testflows/ldap/authentication/tests/authentications.py index 8f98adce7463..bdfa03ff4532 100644 --- a/tests/testflows/ldap/authentication/tests/authentications.py +++ b/tests/testflows/ldap/authentication/tests/authentications.py @@ -900,7 +900,7 @@ def repeat_requests(self, server, iterations, vcd_value, rbac=False, timeout=600 @TestScenario @Tags("verification_cooldown") @Requirements(RQ_SRS_007_LDAP_Authentication_VerificationCooldown_Performance("1.0")) -def verification_cooldown_performance(self, server, rbac=False, iterations=5000): +def verification_cooldown_performance(self, server, rbac=False, iterations=500): """Check that login performance is better when the verification cooldown parameter is set to a positive value when comparing to the case when the verification cooldown parameter is turned off. From dfd19b6a2ea1041a24d702edf12947cc294069d1 Mon Sep 17 00:00:00 2001 From: Vasily Nemkov Date: Sun, 18 Sep 2022 21:41:09 +0400 Subject: [PATCH 63/72] Revert "Merge pull request #194 from Enmk/revert_27531_constants_in_with" This reverts commit f44ba73630c7415c1745501e1978e554d1d5f585, reversing changes made to d912f2e7d40d51af840aee6eb4e3d72b7d71f4ae. --- src/Interpreters/QueryNormalizer.cpp | 3 ++ .../RequiredSourceColumnsVisitor.cpp | 11 ++++++ ...use_constants_in_with_and_select.reference | 5 +++ ...02006_use_constants_in_with_and_select.sql | 36 +++++++++++++++++++ 4 files changed, 55 insertions(+) create mode 100644 tests/queries/0_stateless/02006_use_constants_in_with_and_select.reference create mode 100644 tests/queries/0_stateless/02006_use_constants_in_with_and_select.sql diff --git a/src/Interpreters/QueryNormalizer.cpp b/src/Interpreters/QueryNormalizer.cpp index ea61ade2b49d..7c820622c379 100644 --- a/src/Interpreters/QueryNormalizer.cpp +++ b/src/Interpreters/QueryNormalizer.cpp @@ -256,6 +256,9 @@ void QueryNormalizer::visit(ASTPtr & ast, Data & data) visit(*node_select, ast, data); else if (auto * node_param = ast->as()) throw Exception("Query parameter " + backQuote(node_param->name) + " was not set", ErrorCodes::UNKNOWN_QUERY_PARAMETER); + else if (auto * node_function = ast->as()) + if (node_function->parameters) + visit(node_function->parameters, data); /// If we replace the root of the subtree, we will be called again for the new root, in case the alias is replaced by an alias. if (ast.get() != initial_ast.get()) diff --git a/src/Interpreters/RequiredSourceColumnsVisitor.cpp b/src/Interpreters/RequiredSourceColumnsVisitor.cpp index 2f2a68656bc4..21ec94a6917d 100644 --- a/src/Interpreters/RequiredSourceColumnsVisitor.cpp +++ b/src/Interpreters/RequiredSourceColumnsVisitor.cpp @@ -123,6 +123,17 @@ void RequiredSourceColumnsMatcher::visit(const ASTSelectQuery & select, const AS data.addColumnAliasIfAny(*node); } + if (const auto & with = select.with()) + { + for (auto & node : with->children) + { + if (const auto * identifier = node->as()) + data.addColumnIdentifier(*identifier); + else + data.addColumnAliasIfAny(*node); + } + } + std::vector out; for (const auto & node : select.children) { diff --git a/tests/queries/0_stateless/02006_use_constants_in_with_and_select.reference b/tests/queries/0_stateless/02006_use_constants_in_with_and_select.reference new file mode 100644 index 000000000000..bbf008ffdf20 --- /dev/null +++ b/tests/queries/0_stateless/02006_use_constants_in_with_and_select.reference @@ -0,0 +1,5 @@ +1 [1] +[1] +99.9 +0.1 99.9 +[99.9] diff --git a/tests/queries/0_stateless/02006_use_constants_in_with_and_select.sql b/tests/queries/0_stateless/02006_use_constants_in_with_and_select.sql new file mode 100644 index 000000000000..daca6c5d0c79 --- /dev/null +++ b/tests/queries/0_stateless/02006_use_constants_in_with_and_select.sql @@ -0,0 +1,36 @@ +SELECT + 1 AS max_size, + groupArray(max_size)(col) +FROM + (SELECT col FROM ( + SELECT 1 AS col + UNION ALL + SELECT 2 + ) ORDER BY col); + +WITH 1 AS max_size +SELECT groupArray(max_size)(col) +FROM + (SELECT col FROM ( + SELECT 1 as col + UNION ALL + SELECT 2 + ) ORDER BY col); + +WITH 0.1 AS level +SELECT quantile(level)(number) +FROM numbers(1000); + +SELECT 0.1 AS level, quantile(level)(number) +FROM numbers(1000); + +WITH + 0.1 AS level, + 1 AS max_size +SELECT groupArray(max_size)(col) +FROM + ( + SELECT quantile(level)(number) AS col + FROM numbers(1000) + ); + From 082f79d8e558c1ad281bc08f3551577a61027622 Mon Sep 17 00:00:00 2001 From: Amos Bird Date: Wed, 13 Jul 2022 10:27:43 +0800 Subject: [PATCH 64/72] Fix unused unknown columns introduced by WITH statement #39131 By cherry-picking a8d8293466dcb7874ca692b4a8b437b617303828: Fix unused columns introduced by with stmt --- src/Interpreters/RequiredSourceColumnsVisitor.cpp | 11 ----------- .../02354_with_statement_non_exist_column.reference | 1 + .../02354_with_statement_non_exist_column.sql | 9 +++++++++ 3 files changed, 10 insertions(+), 11 deletions(-) create mode 100644 tests/queries/0_stateless/02354_with_statement_non_exist_column.reference create mode 100644 tests/queries/0_stateless/02354_with_statement_non_exist_column.sql diff --git a/src/Interpreters/RequiredSourceColumnsVisitor.cpp b/src/Interpreters/RequiredSourceColumnsVisitor.cpp index 21ec94a6917d..2f2a68656bc4 100644 --- a/src/Interpreters/RequiredSourceColumnsVisitor.cpp +++ b/src/Interpreters/RequiredSourceColumnsVisitor.cpp @@ -123,17 +123,6 @@ void RequiredSourceColumnsMatcher::visit(const ASTSelectQuery & select, const AS data.addColumnAliasIfAny(*node); } - if (const auto & with = select.with()) - { - for (auto & node : with->children) - { - if (const auto * identifier = node->as()) - data.addColumnIdentifier(*identifier); - else - data.addColumnAliasIfAny(*node); - } - } - std::vector out; for (const auto & node : select.children) { diff --git a/tests/queries/0_stateless/02354_with_statement_non_exist_column.reference b/tests/queries/0_stateless/02354_with_statement_non_exist_column.reference new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/tests/queries/0_stateless/02354_with_statement_non_exist_column.reference @@ -0,0 +1 @@ +1 diff --git a/tests/queries/0_stateless/02354_with_statement_non_exist_column.sql b/tests/queries/0_stateless/02354_with_statement_non_exist_column.sql new file mode 100644 index 000000000000..1a989c1d9529 --- /dev/null +++ b/tests/queries/0_stateless/02354_with_statement_non_exist_column.sql @@ -0,0 +1,9 @@ +WITH x AS y SELECT 1; + +DROP TEMPORARY TABLE IF EXISTS t1; +DROP TEMPORARY TABLE IF EXISTS t2; + +CREATE TEMPORARY TABLE t1 (a Int64); +CREATE TEMPORARY TABLE t2 (a Int64, b Int64); + +WITH b AS bb SELECT bb FROM t2 WHERE a IN (SELECT a FROM t1); From 9c86916a1444d9532a2d020e46feccffe9dc0bb5 Mon Sep 17 00:00:00 2001 From: Kruglov Pavel <48961922+Avogar@users.noreply.github.com> Date: Thu, 1 Sep 2022 19:40:40 +0200 Subject: [PATCH 65/72] Merge pull request #40485 from arthurpassos/fix-parquet-chunked-array-deserialization Add support for extended (chunked) arrays for Parquet format --- .../Formats/Impl/ParquetBlockInputFormat.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Processors/Formats/Impl/ParquetBlockInputFormat.cpp b/src/Processors/Formats/Impl/ParquetBlockInputFormat.cpp index 548bf0138f58..1749b5b62aa9 100644 --- a/src/Processors/Formats/Impl/ParquetBlockInputFormat.cpp +++ b/src/Processors/Formats/Impl/ParquetBlockInputFormat.cpp @@ -51,7 +51,16 @@ Chunk ParquetBlockInputFormat::generate() return res; std::shared_ptr table; - arrow::Status read_status = file_reader->ReadRowGroup(row_group_current, column_indices, &table); + + std::unique_ptr<::arrow::RecordBatchReader> rbr; + std::vector row_group_indices { row_group_current }; + arrow::Status get_batch_reader_status = file_reader->GetRecordBatchReader(row_group_indices, column_indices, &rbr); + + if (!get_batch_reader_status.ok()) + throw ParsingException{"Error while reading Parquet data: " + get_batch_reader_status.ToString(), ErrorCodes::CANNOT_READ_ALL_DATA}; + + arrow::Status read_status = rbr->ReadAll(&table); + if (!read_status.ok()) throw ParsingException{"Error while reading Parquet data: " + read_status.ToString(), ErrorCodes::CANNOT_READ_ALL_DATA}; From 5fd3586ef8a6f4019181728d2d7b9eb11ff5e1d3 Mon Sep 17 00:00:00 2001 From: Maksim Kita Date: Thu, 15 Sep 2022 19:30:47 +0200 Subject: [PATCH 66/72] Merge pull request #40901 from ClickHouse/backport/22.3/40732 Backport #40732 to 22.3: Fix memory leak while pushing to MVs w/o query context (from Kafka/...) --- src/Disks/IO/ThreadPoolReader.cpp | 18 ++++---- src/Disks/IO/ThreadPoolRemoteFSReader.cpp | 20 +++++---- src/IO/WriteBufferFromS3.cpp | 1 - src/Interpreters/ThreadStatusExt.cpp | 5 +++ .../Transforms/buildPushingToViewsChain.cpp | 45 ++++++++++--------- 5 files changed, 49 insertions(+), 40 deletions(-) diff --git a/src/Disks/IO/ThreadPoolReader.cpp b/src/Disks/IO/ThreadPoolReader.cpp index 21f73c3129e5..224b7d03b987 100644 --- a/src/Disks/IO/ThreadPoolReader.cpp +++ b/src/Disks/IO/ThreadPoolReader.cpp @@ -201,9 +201,9 @@ std::future ThreadPoolReader::submit(Request reques ProfileEvents::increment(ProfileEvents::ThreadPoolReaderPageCacheMiss); - ThreadGroupStatusPtr running_group = CurrentThread::isInitialized() && CurrentThread::get().getThreadGroup() - ? CurrentThread::get().getThreadGroup() - : MainThreadStatus::getInstance().getThreadGroup(); + ThreadGroupStatusPtr running_group; + if (CurrentThread::isInitialized() && CurrentThread::get().getThreadGroup()) + running_group = CurrentThread::get().getThreadGroup(); ContextPtr query_context; if (CurrentThread::isInitialized()) @@ -213,12 +213,17 @@ std::future ThreadPoolReader::submit(Request reques { ThreadStatus thread_status; - if (query_context) - thread_status.attachQueryContext(query_context); + SCOPE_EXIT({ + if (running_group) + thread_status.detachQuery(); + }); if (running_group) thread_status.attachQuery(running_group); + if (query_context) + thread_status.attachQueryContext(query_context); + setThreadName("ThreadPoolRead"); Stopwatch watch(CLOCK_MONOTONIC); @@ -253,9 +258,6 @@ std::future ThreadPoolReader::submit(Request reques ProfileEvents::increment(ProfileEvents::ThreadPoolReaderPageCacheMissElapsedMicroseconds, watch.elapsedMicroseconds()); ProfileEvents::increment(ProfileEvents::DiskReadElapsedMicroseconds, watch.elapsedMicroseconds()); - if (running_group) - thread_status.detachQuery(); - return Result{ .size = bytes_read, .offset = request.ignore }; }); diff --git a/src/Disks/IO/ThreadPoolRemoteFSReader.cpp b/src/Disks/IO/ThreadPoolRemoteFSReader.cpp index bdb012a6376f..7652b4211aa0 100644 --- a/src/Disks/IO/ThreadPoolRemoteFSReader.cpp +++ b/src/Disks/IO/ThreadPoolRemoteFSReader.cpp @@ -42,9 +42,9 @@ ThreadPoolRemoteFSReader::ThreadPoolRemoteFSReader(size_t pool_size, size_t queu std::future ThreadPoolRemoteFSReader::submit(Request request) { - ThreadGroupStatusPtr running_group = CurrentThread::isInitialized() && CurrentThread::get().getThreadGroup() - ? CurrentThread::get().getThreadGroup() - : MainThreadStatus::getInstance().getThreadGroup(); + ThreadGroupStatusPtr running_group; + if (CurrentThread::isInitialized() && CurrentThread::get().getThreadGroup()) + running_group = CurrentThread::get().getThreadGroup(); ContextPtr query_context; if (CurrentThread::isInitialized()) @@ -54,14 +54,19 @@ std::future ThreadPoolRemoteFSReader::submit(Reques { ThreadStatus thread_status; - /// Save query context if any, because cache implementation needs it. - if (query_context) - thread_status.attachQueryContext(query_context); + SCOPE_EXIT({ + if (running_group) + thread_status.detachQuery(); + }); /// To be able to pass ProfileEvents. if (running_group) thread_status.attachQuery(running_group); + /// Save query context if any, because cache implementation needs it. + if (query_context) + thread_status.attachQueryContext(query_context); + setThreadName("VFSRead"); CurrentMetrics::Increment metric_increment{CurrentMetrics::Read}; @@ -74,9 +79,6 @@ std::future ThreadPoolRemoteFSReader::submit(Reques ProfileEvents::increment(ProfileEvents::RemoteFSReadMicroseconds, watch.elapsedMicroseconds()); ProfileEvents::increment(ProfileEvents::RemoteFSReadBytes, bytes_read); - if (running_group) - thread_status.detachQuery(); - return Result{ .size = bytes_read, .offset = offset }; }); diff --git a/src/IO/WriteBufferFromS3.cpp b/src/IO/WriteBufferFromS3.cpp index eda7bb6f8aed..de7130319a7f 100644 --- a/src/IO/WriteBufferFromS3.cpp +++ b/src/IO/WriteBufferFromS3.cpp @@ -86,7 +86,6 @@ void WriteBufferFromS3::nextImpl() temporary_buffer->write(working_buffer.begin(), offset()); ProfileEvents::increment(ProfileEvents::S3WriteBytes, offset()); - last_part_size += offset(); /// Data size exceeds singlepart upload threshold, need to use multipart upload. diff --git a/src/Interpreters/ThreadStatusExt.cpp b/src/Interpreters/ThreadStatusExt.cpp index 2ea371d3d033..1f2a8e4681a8 100644 --- a/src/Interpreters/ThreadStatusExt.cpp +++ b/src/Interpreters/ThreadStatusExt.cpp @@ -408,6 +408,11 @@ void ThreadStatus::detachQuery(bool exit_if_already_detached, bool thread_exits) query_context.reset(); thread_trace_context.trace_id = 0; thread_trace_context.span_id = 0; + + /// Avoid leaking of ThreadGroupStatus::finished_threads_counters_memory + /// (this is in case someone uses system thread but did not call getProfileEventsCountersAndMemoryForThreads()) + thread_group->getProfileEventsCountersAndMemoryForThreads(); + thread_group.reset(); thread_state = thread_exits ? ThreadState::Died : ThreadState::DetachedFromQuery; diff --git a/src/Processors/Transforms/buildPushingToViewsChain.cpp b/src/Processors/Transforms/buildPushingToViewsChain.cpp index 3cb5b16a2d0d..0d47494d22be 100644 --- a/src/Processors/Transforms/buildPushingToViewsChain.cpp +++ b/src/Processors/Transforms/buildPushingToViewsChain.cpp @@ -237,29 +237,30 @@ Chain buildPushingToViewsChain( ASTPtr query; Chain out; - /// If the materialized view is executed outside of a query, for example as a result of SYSTEM FLUSH LOGS or - /// SYSTEM FLUSH DISTRIBUTED ..., we can't attach to any thread group and we won't log, so there is no point on collecting metrics - std::unique_ptr view_thread_status_ptr = nullptr; - - ThreadGroupStatusPtr running_group = current_thread && current_thread->getThreadGroup() - ? current_thread->getThreadGroup() - : MainThreadStatus::getInstance().getThreadGroup(); - if (running_group) + ThreadGroupStatusPtr running_group; + if (current_thread && current_thread->getThreadGroup()) + running_group = current_thread->getThreadGroup(); + else + running_group = std::make_shared(); + + /// We are creating a ThreadStatus per view to store its metrics individually + /// Since calling ThreadStatus() changes current_thread we save it and restore it after the calls + /// Later on, before doing any task related to a view, we'll switch to its ThreadStatus, do the work, + /// and switch back to the original thread_status. + auto * original_thread = current_thread; + SCOPE_EXIT({ current_thread = original_thread; }); + + std::unique_ptr view_thread_status_ptr = std::make_unique(); + /// Disable query profiler for this ThreadStatus since the running (main query) thread should already have one + /// If we didn't disable it, then we could end up with N + 1 (N = number of dependencies) profilers which means + /// N times more interruptions + view_thread_status_ptr->disableProfiling(); + /// view_thread_status_ptr will be moved later (on and on), so need to capture raw pointer. + view_thread_status_ptr->deleter = [thread_status = view_thread_status_ptr.get(), running_group] { - /// We are creating a ThreadStatus per view to store its metrics individually - /// Since calling ThreadStatus() changes current_thread we save it and restore it after the calls - /// Later on, before doing any task related to a view, we'll switch to its ThreadStatus, do the work, - /// and switch back to the original thread_status. - auto * original_thread = current_thread; - SCOPE_EXIT({ current_thread = original_thread; }); - - view_thread_status_ptr = std::make_unique(); - /// Disable query profiler for this ThreadStatus since the running (main query) thread should already have one - /// If we didn't disable it, then we could end up with N + 1 (N = number of dependencies) profilers which means - /// N times more interruptions - view_thread_status_ptr->disableProfiling(); - view_thread_status_ptr->attachQuery(running_group); - } + thread_status->detachQuery(); + }; + view_thread_status_ptr->attachQuery(running_group); auto runtime_stats = std::make_unique(); runtime_stats->target_name = database_table.getFullTableName(); From 5499706ce64430a42199aa844d7e12de1ffe50bc Mon Sep 17 00:00:00 2001 From: Vasily Nemkov Date: Tue, 6 Dec 2022 18:59:20 +0400 Subject: [PATCH 67/72] Update release_branches.yml --- .github/workflows/release_branches.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release_branches.yml b/.github/workflows/release_branches.yml index 074eed6aafbc..d7fa0f2b7cf6 100644 --- a/.github/workflows/release_branches.yml +++ b/.github/workflows/release_branches.yml @@ -249,7 +249,7 @@ jobs: MarkReleaseReady: needs: - BuilderDebRelease - - BuilderDebAarch64 + # - BuilderDebAarch64 runs-on: [self-hosted, style-checker] steps: - name: Clear repository From bc03de2b0469000a29e8f630c864beff23b6077d Mon Sep 17 00:00:00 2001 From: Vasily Nemkov Date: Wed, 7 Dec 2022 15:06:11 +0400 Subject: [PATCH 68/72] Updated checks info db to gh-data, fixed error reporting in clickhouse_helper.py --- docker/test/performance-comparison/compare.sh | 2 +- tests/ci/clickhouse_helper.py | 10 +++------- tests/ci/docker_images_check.py | 2 +- tests/ci/docker_manifests_merge.py | 2 +- tests/ci/fast_test_check.py | 2 +- tests/ci/functional_test_check.py | 2 +- tests/ci/keeper_jepsen_check.py | 2 +- tests/ci/stress_check.py | 2 +- tests/ci/style_check.py | 2 +- 9 files changed, 11 insertions(+), 15 deletions(-) diff --git a/docker/test/performance-comparison/compare.sh b/docker/test/performance-comparison/compare.sh index cdfa080a4752..54f71ce05bb4 100755 --- a/docker/test/performance-comparison/compare.sh +++ b/docker/test/performance-comparison/compare.sh @@ -1378,7 +1378,7 @@ $REF_SHA $SHA_TO_TEST $(numactl --hardware | sed -n 's/^available:[[:space:]]\+/ EOF # Also insert some data about the check into the CI checks table. - "${client[@]}" --query "INSERT INTO "'"'"default"'"'".checks FORMAT TSVWithNamesAndTypes" \ + "${client[@]}" --query "INSERT INTO "'"'"gh-data"'"'".checks FORMAT TSVWithNamesAndTypes" \ < ci-checks.tsv set -x diff --git a/tests/ci/clickhouse_helper.py b/tests/ci/clickhouse_helper.py index 8b8593c0bb76..abc756ff1fef 100644 --- a/tests/ci/clickhouse_helper.py +++ b/tests/ci/clickhouse_helper.py @@ -37,12 +37,8 @@ def _insert_json_str_info_impl(url, auth, db, table, json_str): url, params=params, data=json_str, headers=auth ) except Exception as e: - logging.warning( - "Received exception while sending data to %s on %s attempt: %s", - url, - i, - e, - ) + error = f"Received exception while sending data to {url} on {i} attempt: {e}" + logging.error(error) continue logging.info("Response content '%s'", response.content) @@ -201,7 +197,7 @@ def mark_flaky_tests(clickhouse_helper, check_name, test_results): AND pull_request_number = 0 """ - tests_data = clickhouse_helper.select_json_each_row("default", query) + tests_data = clickhouse_helper.select_json_each_row("gh-data", query) master_failed_tests = {row["test_name"] for row in tests_data} logging.info("Found flaky tests: %s", ", ".join(master_failed_tests)) diff --git a/tests/ci/docker_images_check.py b/tests/ci/docker_images_check.py index f572f7fd2df3..a95cf63f3578 100644 --- a/tests/ci/docker_images_check.py +++ b/tests/ci/docker_images_check.py @@ -509,7 +509,7 @@ def main(): NAME, ) ch_helper = ClickHouseHelper() - ch_helper.insert_events_into(db="default", table="checks", events=prepared_events) + ch_helper.insert_events_into(db="gh-data", table="checks", events=prepared_events) if status == "error": sys.exit(1) diff --git a/tests/ci/docker_manifests_merge.py b/tests/ci/docker_manifests_merge.py index 7899d9822077..0d061bb0db33 100644 --- a/tests/ci/docker_manifests_merge.py +++ b/tests/ci/docker_manifests_merge.py @@ -234,7 +234,7 @@ def main(): NAME, ) ch_helper = ClickHouseHelper() - ch_helper.insert_events_into(db="default", table="checks", events=prepared_events) + ch_helper.insert_events_into(db="gh-data", table="checks", events=prepared_events) if __name__ == "__main__": diff --git a/tests/ci/fast_test_check.py b/tests/ci/fast_test_check.py index 315723107ccb..0a8c657fe374 100644 --- a/tests/ci/fast_test_check.py +++ b/tests/ci/fast_test_check.py @@ -207,7 +207,7 @@ def process_results(result_folder): report_url, NAME, ) - ch_helper.insert_events_into(db="default", table="checks", events=prepared_events) + ch_helper.insert_events_into(db="gh-data", table="checks", events=prepared_events) # Refuse other checks to run if fast test failed if state != "success": diff --git a/tests/ci/functional_test_check.py b/tests/ci/functional_test_check.py index 91b9877988fc..2b8a52b4db43 100644 --- a/tests/ci/functional_test_check.py +++ b/tests/ci/functional_test_check.py @@ -291,7 +291,7 @@ def process_results(result_folder, server_log_path): report_url, check_name_with_group, ) - ch_helper.insert_events_into(db="default", table="checks", events=prepared_events) + ch_helper.insert_events_into(db="gh-data", table="checks", events=prepared_events) if state != "success": if "force-tests" in pr_info.labels: diff --git a/tests/ci/keeper_jepsen_check.py b/tests/ci/keeper_jepsen_check.py index be4d7048a731..98af355ab6d1 100644 --- a/tests/ci/keeper_jepsen_check.py +++ b/tests/ci/keeper_jepsen_check.py @@ -273,5 +273,5 @@ def get_run_command( report_url, CHECK_NAME, ) - ch_helper.insert_events_into(db="default", table="checks", events=prepared_events) + ch_helper.insert_events_into(db="gh-data", table="checks", events=prepared_events) clear_autoscaling_group() diff --git a/tests/ci/stress_check.py b/tests/ci/stress_check.py index bf102c23dcfb..7c110daf03e4 100644 --- a/tests/ci/stress_check.py +++ b/tests/ci/stress_check.py @@ -174,7 +174,7 @@ def process_results(result_folder, server_log_path, run_log_path): report_url, check_name, ) - ch_helper.insert_events_into(db="default", table="checks", events=prepared_events) + ch_helper.insert_events_into(db="gh-data", table="checks", events=prepared_events) if state == "error": sys.exit(1) diff --git a/tests/ci/style_check.py b/tests/ci/style_check.py index d2d27fe19912..2260c4d7f924 100644 --- a/tests/ci/style_check.py +++ b/tests/ci/style_check.py @@ -117,7 +117,7 @@ def process_result(result_folder): report_url, NAME, ) - ch_helper.insert_events_into(db="default", table="checks", events=prepared_events) + ch_helper.insert_events_into(db="gh-data", table="checks", events=prepared_events) if state == "error": sys.exit(1) From 9658b6dfee2dd4a03f900c51521f251f68d6f871 Mon Sep 17 00:00:00 2001 From: Vasily Nemkov Date: Wed, 7 Dec 2022 16:55:13 +0400 Subject: [PATCH 69/72] Attempt to fix build --- .github/workflows/release_branches.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release_branches.yml b/.github/workflows/release_branches.yml index d7fa0f2b7cf6..4ea7fb996af3 100644 --- a/.github/workflows/release_branches.yml +++ b/.github/workflows/release_branches.yml @@ -152,7 +152,7 @@ jobs: - name: Build run: | git -C "$GITHUB_WORKSPACE" submodule sync - git -C "$GITHUB_WORKSPACE" submodule update --single-branch --depth=1 --init --jobs=10 + git -C "$GITHUB_WORKSPACE" submodule update --depth=1 --init --jobs=10 sudo rm -fr "$TEMP_PATH" mkdir -p "$TEMP_PATH" cp -r "$GITHUB_WORKSPACE" "$TEMP_PATH" From 9e13c827014751507e5f567eec5c2b3388ae04d1 Mon Sep 17 00:00:00 2001 From: Vladimir C Date: Wed, 10 Aug 2022 11:40:40 +0200 Subject: [PATCH 70/72] Merge pull request #40037 from arthurpassos/fix_arrow_column_dictionary_to_ch_lc Fix arrow column dictionary to ch lc --- .../Formats/Impl/ArrowColumnToCHColumn.cpp | 88 ++++++++++++++++--- ...ow_dict_of_nullable_string_to_lc.reference | 9 ++ ...381_arrow_dict_of_nullable_string_to_lc.sh | 31 +++++++ .../02381_arrow_dict_to_lc.reference | 7 ++ .../0_stateless/02381_arrow_dict_to_lc.sh | 36 ++++++++ 5 files changed, 161 insertions(+), 10 deletions(-) create mode 100644 tests/queries/0_stateless/02381_arrow_dict_of_nullable_string_to_lc.reference create mode 100755 tests/queries/0_stateless/02381_arrow_dict_of_nullable_string_to_lc.sh create mode 100644 tests/queries/0_stateless/02381_arrow_dict_to_lc.reference create mode 100755 tests/queries/0_stateless/02381_arrow_dict_to_lc.sh diff --git a/src/Processors/Formats/Impl/ArrowColumnToCHColumn.cpp b/src/Processors/Formats/Impl/ArrowColumnToCHColumn.cpp index 5c367bb69f0d..eebb65879695 100644 --- a/src/Processors/Formats/Impl/ArrowColumnToCHColumn.cpp +++ b/src/Processors/Formats/Impl/ArrowColumnToCHColumn.cpp @@ -322,6 +322,69 @@ static std::shared_ptr getNestedArrowColumn(std::shared_ptr return std::make_shared(array_vector); } +static ColumnWithTypeAndName createLCColumnFromArrowDictionaryValues( + const std::shared_ptr & dict_values, + const ColumnPtr & indexes_column, + const String & column_name +) +{ + auto lc_type = std::make_shared(dict_values->type); + + auto lc_column = lc_type->createColumn(); + + for (auto i = 0u; i < indexes_column->size(); i++) + { + Field f; + dict_values->column->get(indexes_column->getUInt(i), f); + lc_column->insert(f); + } + + return {std::move(lc_column), std::move(lc_type), column_name}; +} + +/* + * Dictionary(Nullable(X)) in ArrowColumn format is composed of a nullmap, dictionary and an index. + * It doesn't have the concept of null or default values. + * An empty string is just a regular value appended at any position of the dictionary. + * Null values have an index of 0, but it should be ignored since the nullmap will return null. + * In ClickHouse LowCardinality, it's different. The dictionary contains null and default values at the beginning. + * [null, default, ...]. Therefore, null values have an index of 0 and default values have an index of 1. + * No nullmap is used. + * */ +static ColumnWithTypeAndName createLCOfNullableColumnFromArrowDictionaryValues( + const std::shared_ptr & dict_values, + const ColumnPtr & indexes_column, + const ColumnPtr & nullmap_column, + const String & column_name +) +{ + /* + * ArrowColumn format handles nulls by maintaining a nullmap column, there is no nullable type. + * Therefore, dict_values->type is the actual data type/ non-nullable. It needs to be transformed into nullable + * so LC column is created from nullable type and a null value at the beginning of the collection + * is automatically added. + * */ + auto lc_type = std::make_shared(makeNullable(dict_values->type)); + + auto lc_column = lc_type->createColumn(); + + for (auto i = 0u; i < indexes_column->size(); i++) + { + if (nullmap_column && nullmap_column->getBool(i)) + { + lc_column->insertDefault(); + } + else + { + Field f; + dict_values->column->get(indexes_column->getUInt(i), f); + lc_column->insert(f); + } + } + + return {std::move(lc_column), std::move(lc_type), column_name}; +} + static ColumnWithTypeAndName readColumnFromArrowColumn( std::shared_ptr & arrow_column, const std::string & column_name, @@ -331,7 +394,8 @@ static ColumnWithTypeAndName readColumnFromArrowColumn( bool read_ints_as_dates) { if (!is_nullable && arrow_column->null_count() && arrow_column->type()->id() != arrow::Type::LIST - && arrow_column->type()->id() != arrow::Type::MAP && arrow_column->type()->id() != arrow::Type::STRUCT) + && arrow_column->type()->id() != arrow::Type::MAP && arrow_column->type()->id() != arrow::Type::STRUCT && + arrow_column->type()->id() != arrow::Type::DICTIONARY) { auto nested_column = readColumnFromArrowColumn(arrow_column, column_name, format_name, true, dictionary_values, read_ints_as_dates); auto nullmap_column = readByteMapFromArrowColumn(arrow_column); @@ -439,12 +503,6 @@ static ColumnWithTypeAndName readColumnFromArrowColumn( } auto arrow_dict_column = std::make_shared(dict_array); auto dict_column = readColumnFromArrowColumn(arrow_dict_column, column_name, format_name, false, dictionary_values, read_ints_as_dates); - - /// We should convert read column to ColumnUnique. - auto tmp_lc_column = DataTypeLowCardinality(dict_column.type).createColumn(); - auto tmp_dict_column = IColumn::mutate(assert_cast(tmp_lc_column.get())->getDictionaryPtr()); - static_cast(tmp_dict_column.get())->uniqueInsertRangeFrom(*dict_column.column, 0, dict_column.column->size()); - dict_column.column = std::move(tmp_dict_column); dict_values = std::make_shared(std::move(dict_column)); } @@ -457,9 +515,19 @@ static ColumnWithTypeAndName readColumnFromArrowColumn( auto arrow_indexes_column = std::make_shared(indexes_array); auto indexes_column = readColumnWithIndexesData(arrow_indexes_column); - auto lc_column = ColumnLowCardinality::create(dict_values->column, indexes_column); - auto lc_type = std::make_shared(dict_values->type); - return {std::move(lc_column), std::move(lc_type), column_name}; + + const auto contains_null = arrow_column->null_count() > 0; + + if (contains_null) + { + auto nullmap_column = readByteMapFromArrowColumn(arrow_column); + + return createLCOfNullableColumnFromArrowDictionaryValues(dict_values, indexes_column, nullmap_column, column_name); + } + else + { + return createLCColumnFromArrowDictionaryValues(dict_values, indexes_column, column_name); + } } # define DISPATCH(ARROW_NUMERIC_TYPE, CPP_NUMERIC_TYPE) \ case ARROW_NUMERIC_TYPE: \ diff --git a/tests/queries/0_stateless/02381_arrow_dict_of_nullable_string_to_lc.reference b/tests/queries/0_stateless/02381_arrow_dict_of_nullable_string_to_lc.reference new file mode 100644 index 000000000000..a6c4a5b13a22 --- /dev/null +++ b/tests/queries/0_stateless/02381_arrow_dict_of_nullable_string_to_lc.reference @@ -0,0 +1,9 @@ +lc_nullable_string +LowCardinality(Nullable(String)) +one +\N +three + +\N + +six diff --git a/tests/queries/0_stateless/02381_arrow_dict_of_nullable_string_to_lc.sh b/tests/queries/0_stateless/02381_arrow_dict_of_nullable_string_to_lc.sh new file mode 100755 index 000000000000..2d854c956b25 --- /dev/null +++ b/tests/queries/0_stateless/02381_arrow_dict_of_nullable_string_to_lc.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# Tags: no-fasttest +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +# shellcheck source=../shell_config.sh +. "$CURDIR"/../shell_config.sh + +# ## reading ArrowStream file from python +# import pyarrow as pa +# stream = pa.ipc.open_stream("test.arrows") +# x = stream.read_all() +# print(x) + +## writing ArrowStream file from python +# import pyarrow as pa +# data = [ +# pa.array(["one", None, "three", "", None, "", "six"]).dictionary_encode(), +# ] +# batch = pa.record_batch(data, names=['id', 'lc_nullable', 'lc_int_nullable', 'bool_nullable']) +# writer = pa.ipc.new_stream("test4.arrows", batch.schema) +# writer.write_batch(batch) +# writer.close() + +# cat data.arrow | gzip | base64 + +cat < Date: Fri, 25 Nov 2022 18:13:27 +0100 Subject: [PATCH 71/72] Merge pull request #43297 from arthurpassos/fix_arrow_list_column_parsing Flatten list type arrow chunks on parsing --- .../Formats/Impl/ArrowColumnToCHColumn.cpp | 42 +++++++++++++++--- ...parquet_int_list_multiple_chunks.reference | 3 ++ .../02481_parquet_int_list_multiple_chunks.sh | 42 ++++++++++++++++++ ...monotonically_increasing_offsets.reference | 3 ++ ...t_list_monotonically_increasing_offsets.sh | 16 +++++++ .../int-list-zero-based-chunked-array.parquet | Bin 0 -> 1014463 bytes ...t_monotonically_increasing_offsets.parquet | Bin 0 -> 123806 bytes 7 files changed, 101 insertions(+), 5 deletions(-) create mode 100644 tests/queries/0_stateless/02481_parquet_int_list_multiple_chunks.reference create mode 100755 tests/queries/0_stateless/02481_parquet_int_list_multiple_chunks.sh create mode 100644 tests/queries/0_stateless/02481_parquet_list_monotonically_increasing_offsets.reference create mode 100755 tests/queries/0_stateless/02481_parquet_list_monotonically_increasing_offsets.sh create mode 100644 tests/queries/0_stateless/data_parquet/int-list-zero-based-chunked-array.parquet create mode 100644 tests/queries/0_stateless/data_parquet/list_monotonically_increasing_offsets.parquet diff --git a/src/Processors/Formats/Impl/ArrowColumnToCHColumn.cpp b/src/Processors/Formats/Impl/ArrowColumnToCHColumn.cpp index eebb65879695..1ae6e9b6652c 100644 --- a/src/Processors/Formats/Impl/ArrowColumnToCHColumn.cpp +++ b/src/Processors/Formats/Impl/ArrowColumnToCHColumn.cpp @@ -281,14 +281,31 @@ static ColumnPtr readOffsetsFromArrowListColumn(std::shared_ptr &>(*offsets_column).getData(); offsets_data.reserve(arrow_column->length()); - for (size_t chunk_i = 0, num_chunks = static_cast(arrow_column->num_chunks()); chunk_i < num_chunks; ++chunk_i) + uint64_t start_offset = 0u; + + for (int chunk_i = 0, num_chunks = arrow_column->num_chunks(); chunk_i < num_chunks; ++chunk_i) { arrow::ListArray & list_chunk = dynamic_cast(*(arrow_column->chunk(chunk_i))); auto arrow_offsets_array = list_chunk.offsets(); auto & arrow_offsets = dynamic_cast(*arrow_offsets_array); - auto start = offsets_data.back(); + + /* + * It seems like arrow::ListArray::values() (nested column data) might or might not be shared across chunks. + * When it is shared, the offsets will be monotonically increasing. Otherwise, the offsets will be zero based. + * In order to account for both cases, the starting offset is updated whenever a zero-based offset is found. + * More info can be found in: https://lists.apache.org/thread/rrwfb9zo2dc58dhd9rblf20xd7wmy7jm and + * https://github.com/ClickHouse/ClickHouse/pull/43297 + * */ + if (list_chunk.offset() == 0) + { + start_offset = offsets_data.back(); + } + for (int64_t i = 1; i < arrow_offsets.length(); ++i) - offsets_data.emplace_back(start + arrow_offsets.Value(i)); + { + auto offset = arrow_offsets.Value(i); + offsets_data.emplace_back(start_offset + offset); + } } return offsets_column; } @@ -316,8 +333,23 @@ static std::shared_ptr getNestedArrowColumn(std::shared_ptr for (size_t chunk_i = 0, num_chunks = static_cast(arrow_column->num_chunks()); chunk_i < num_chunks; ++chunk_i) { arrow::ListArray & list_chunk = dynamic_cast(*(arrow_column->chunk(chunk_i))); - std::shared_ptr chunk = list_chunk.values(); - array_vector.emplace_back(std::move(chunk)); + + /* + * It seems like arrow::ListArray::values() (nested column data) might or might not be shared across chunks. + * Therefore, simply appending arrow::ListArray::values() could lead to duplicated data to be appended. + * To properly handle this, arrow::ListArray::values() needs to be sliced based on the chunk offsets. + * arrow::ListArray::Flatten does that. More info on: https://lists.apache.org/thread/rrwfb9zo2dc58dhd9rblf20xd7wmy7jm and + * https://github.com/ClickHouse/ClickHouse/pull/43297 + * */ + auto flatten_result = list_chunk.Flatten(); + if (flatten_result.ok()) + { + array_vector.emplace_back(flatten_result.ValueOrDie()); + } + else + { + throw Exception(ErrorCodes::INCORRECT_DATA, "Failed to flatten chunk '{}' of column of type '{}' ", chunk_i, arrow_column->type()->id()); + } } return std::make_shared(array_vector); } diff --git a/tests/queries/0_stateless/02481_parquet_int_list_multiple_chunks.reference b/tests/queries/0_stateless/02481_parquet_int_list_multiple_chunks.reference new file mode 100644 index 000000000000..285856e363a1 --- /dev/null +++ b/tests/queries/0_stateless/02481_parquet_int_list_multiple_chunks.reference @@ -0,0 +1,3 @@ +Parquet +3d94071a2fe62a3b3285f170ca6f42e5 - +70000 diff --git a/tests/queries/0_stateless/02481_parquet_int_list_multiple_chunks.sh b/tests/queries/0_stateless/02481_parquet_int_list_multiple_chunks.sh new file mode 100755 index 000000000000..c2c6f6898510 --- /dev/null +++ b/tests/queries/0_stateless/02481_parquet_int_list_multiple_chunks.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +# Tags: no-ubsan, no-fasttest + +CUR_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +# shellcheck source=../shell_config.sh +. "$CUR_DIR"/../shell_config.sh + +echo "Parquet" + +# File generated with the below script + +#import pyarrow as pa +#import pyarrow.parquet as pq +#import random +# +# +#def gen_array(offset): +# array = [] +# array_length = random.randint(0, 9) +# for i in range(array_length): +# array.append(i + offset) +# +# return array +# +# +#def gen_arrays(number_of_arrays): +# list_of_arrays = [] +# for i in range(number_of_arrays): +# list_of_arrays.append(gen_array(i)) +# return list_of_arrays +# +#arr = pa.array(gen_arrays(70000)) +#table = pa.table([arr], ["arr"]) +#pq.write_table(table, "int-list-zero-based-chunked-array.parquet") + +DATA_FILE=$CUR_DIR/data_parquet/int-list-zero-based-chunked-array.parquet +${CLICKHOUSE_CLIENT} --query="DROP TABLE IF EXISTS parquet_load" +${CLICKHOUSE_CLIENT} --query="CREATE TABLE parquet_load (arr Array(Int64)) ENGINE = Memory" +cat "$DATA_FILE" | ${CLICKHOUSE_CLIENT} -q "INSERT INTO parquet_load FORMAT Parquet" +${CLICKHOUSE_CLIENT} --query="SELECT * FROM parquet_load" | md5sum +${CLICKHOUSE_CLIENT} --query="SELECT count() FROM parquet_load" +${CLICKHOUSE_CLIENT} --query="drop table parquet_load" \ No newline at end of file diff --git a/tests/queries/0_stateless/02481_parquet_list_monotonically_increasing_offsets.reference b/tests/queries/0_stateless/02481_parquet_list_monotonically_increasing_offsets.reference new file mode 100644 index 000000000000..2db066c0f875 --- /dev/null +++ b/tests/queries/0_stateless/02481_parquet_list_monotonically_increasing_offsets.reference @@ -0,0 +1,3 @@ +Parquet +e1cfe4265689ead763b18489b363344d - +39352 diff --git a/tests/queries/0_stateless/02481_parquet_list_monotonically_increasing_offsets.sh b/tests/queries/0_stateless/02481_parquet_list_monotonically_increasing_offsets.sh new file mode 100755 index 000000000000..47245eeb9401 --- /dev/null +++ b/tests/queries/0_stateless/02481_parquet_list_monotonically_increasing_offsets.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# Tags: no-ubsan, no-fasttest + +CUR_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +# shellcheck source=../shell_config.sh +. "$CUR_DIR"/../shell_config.sh + +echo "Parquet" + +DATA_FILE=$CUR_DIR/data_parquet/list_monotonically_increasing_offsets.parquet +${CLICKHOUSE_CLIENT} --query="DROP TABLE IF EXISTS parquet_load" +${CLICKHOUSE_CLIENT} --query="CREATE TABLE parquet_load (list Array(Int64), json Nullable(String)) ENGINE = Memory" +cat "$DATA_FILE" | ${CLICKHOUSE_CLIENT} -q "INSERT INTO parquet_load FORMAT Parquet" +${CLICKHOUSE_CLIENT} --query="SELECT * FROM parquet_load" | md5sum +${CLICKHOUSE_CLIENT} --query="SELECT count() FROM parquet_load" +${CLICKHOUSE_CLIENT} --query="drop table parquet_load" \ No newline at end of file diff --git a/tests/queries/0_stateless/data_parquet/int-list-zero-based-chunked-array.parquet b/tests/queries/0_stateless/data_parquet/int-list-zero-based-chunked-array.parquet new file mode 100644 index 0000000000000000000000000000000000000000..2eb3ba3ab150aae978c6023f657d0d09b86e4cc2 GIT binary patch literal 1014463 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

w3dvn%o2Q;U)=-Cx7OYGW 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℞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

@?SR+QnF!M8^6=2l$R2rqcQMHI?Hf4fh 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 k;zt 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�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%0Ar@>Xlfpg4W^_1wN$unpmDnESgKeV zJzam)x)!JKXyZ)RXQ?|X+3C_z^E$D!^Eu;eS6!-HnLfQ1Ck-mx`MlBWI-6QqX`bG2 z)W42|LtDh`I+yCHTr|DusAfHRMmNOxs_VP<;6P>0^w3Y)_3AUa7#v@INWD;5GhO`D zx}J2=4K=>r3fK*57u_(NXRf9G7>EGqsvBWkh7-@#%KGWGpSFSCk>8>nu9 zi00JmmCe&bk1@GoH|LAS<*xhELaV}N=#N=(1k$}^e78LaN$Xe@KErT~%~iYWVvQEp zgK1P%^o*=y8XSRi2ICr+CarT-%nS?8L~0M+XdIHXX^~awGir}1d16mzoG}-Nq=8lD z84bt$c~mcFJPumVqz$jinL!M{;%-i_+Bx(UV|uF-A5IGo;wr7f!pG2Ioht}4Pryk1*tN-LmY3fHp6Z6HQDAxrIw zPs^)fOeV~=P`LKT#Xg%>R25^gco`AZUfx*dnvf<{aVC$~DiWQOH;H!HAYMPF0nd$O*PU?c+={?m_A2d{wo{f|ID&&pEjjkQ3elv9EKY z@k7_5w5wJ1Eo*Ed{dAL!AGzL0yHw>hNhi#ksQx(3E>P zz73qyjYnM8w9dO@W?D`%Me0D^4C8T^Ev@hF%m5IhacK6s3epDdHqUH0=`W(5hg#1G zS0Rqoi)J>R)D)BFohIYSR-hD#&pBrqPve}Ou)BDs;gnLWKBt?FW3@eP?Cz48lPaS z=Q@_QYN9q;I#{;|Rl7rJ7E}ZxKQnKphM)j&#r0X*j@@Ci^q*NblOeiSjo-RX zr%AgbP!aHMR)^?bLy5qb=H8vz9+*D!Z>D0LuLVtuWr_ z4oUC0Cw#U6)eUi!bA>U)-66fxp0L^a)8_5eDAYeXxF1RHfpUj*+PtF`PaTojzwz{p{M?YlOD~rBoc_TxWd79h1IfPxEZg8EYvO=Ui{>>K>84 z5~Z7lGyYOB&bh(Z-5s00dQbQqL!Dw56Yc?2yffDr5r_zzqp!2tNrG;p@mcqTbm>mY zMp$NYkjzTIy~al4@+fyw`j$PhbF%6*62&@g(9F6O;;FMrBvH4?*xx-31))q-h!jao zbZ#>Cb5BV>yvI5x@(aI2B{>U?(e9b)XZGZvWW+kuBwevl@1BR0{5krz;KH8bIR+G! z#IeDM3wx^OWPPD2Bja>ij6>Zor(fFRog>`=i_)*|sh?B(g;FMtcWyI|bibDVNwq2<8b%V^qYG^%#@FXJ7O#F&uRF=e>>%^^!q&;bEMA^)H0_(iKoh(rD~k_ z0JB2j>cePsSEOfnyk>){C?at>jj8UcV2W2!iOywA3r0w)*-51Yz@GH^p37!c^*YrQ zl;P6dd(#(s>dYS1>Le*nw{eoYI(>T?A zFnuMeZmkg+?jz}|J>hc=XU#4u4fg;u-JhlJKp9RtYj(E-Bu%&5ILm!HUGl`v70t=?6SHbEz+_sQ&3bFuvlxm|pA2pR51UT5%8Xy48ug z4^iyY3sG|vq^$L>_8=nkSBcq9F$CuAOV&b*VFih|)=?!VK2MM<&Y zOaD%48tQ{N?hX^1JdJakzSQg@({zW8%iZ@)4Be}l7kSRSi^^~wHm*XAvGd-Td6shw z6y%(>t$>{K?ou;!M^Hi31|qV~X(~x3MCaDHpF+_wcAj{StyD8nmbAK`nAmr3Cd!iD zN@}|EsByi!D#Z!~bLJ+~Z=(|36MEOT#Yh&RKFrU1kc! zSX$Q@m&P^bEV;LGY0lg)DHX+_8BsBZa#>CNxItBjm3D$wk-|cA zMyluKP?KGZGK5ma7n;oJsjfONGh3o$2(>D*1z1gX33`dpsp5r|?h!9DHwCRF45}p9 zKIv1XmqX3=6tsE!lp?%pE>Bfl4lQT_VqvYhC{=dZzd(_yyi63RW(yr=YiizQaRGal z@)}X9nk#%@u1~dJwieiDq1TBcs`+rU+Xj@u&F*t^W2)=2*J18#^agQUwOYt72$

    Yz8&XE4ZW+mfD`)f` zab9&0_H{WkL$11klennT!liW0OzBmxBkXx_DP5=13zG}#W?HW1A5r|K{F}J0G73`* z>Sx-oT94R&Lmv@0R3|e`rxY}@pJs_)AqPlofc-3cQw8%w2#!A@0D)x&q%> z+-ufi`vUYSaZ_~~uD%0iNw0a8a5GRB;d~!BRA+=c3xeV5JG6wIp?pd-s2HKHK)Va_U~|8{!n#ISXPiV%kM97DSHuszg5@Uf+pf0RlTsHAZJ#{ zU-_kqMaqAPXKsSowlzQvD-*TwtA*=N>^{5ieCQgfCkZiY3bbh<55{LRUfKEZ1LN z$GA(;|A_xo?ZQ0|d(RHMo^_174E>1r26$fgKc-l!Y|rbU_6qNLIB2%)x__A>vlaNx z=3ciRvoAwC@H(rzg!efdJX>)+w9KBV{D{{{?HxY&Fge?D-K&h72}PGIb&v4C!=bbD zu8Ygq%VAmYiMnr#BEL+rT={W}B7L^&x>tFtV*KH(*?u>~<=o}y$GlI~{lZ5b&TuQZ zE6`57CEe9u!NwumEn&-G!;7eggik#T55?WcFXzfo7Viu7&~VY=qS>+=UdOpB(XKon z^@#Aa!ws_?HyVz!SEHZ6PGWTUqQf3(?E26X_SL92udjM+__D)ZX@2$M6YMq0ZaiQ0 z#PCgr{nJEm6n^R{;X4lpr-jt#pHQq(e#RT54uic$K$^7P%fih<;RCZpp{Q>-!Onuc zhQFE!*TJI0@m9ePHyut-bJcsDY#C_-)+!O+5OrjD;o*!l&zt@y6>F6}dEcm`!!3ui z(*kd1o#d`Vd+>&-eu_V5>!uehT+sf! zvFgR)?F+rq{cgd?MrA)9_d@@4(XAH6kLuO1X!lMJyp;t@8*~8grxpeGmi4rK z6Z#czqI!LJ|H9Dpyj$W5H<%1fsCZ#)y8V{5f}IU}o=`OvKCDH--VAG<*Fc=^fDt#C zrrrgLW{_W@*sT1THyu_(lMB<+UAG!4*jr%n6Q!Grdk#8+m#V%Ht}2X$C6>67y&YCmDeAiL!orL> zo_GAuDt0JG@#eMw$L)qn_73GpUb^~rcy(d^9M^5Hv)mo%DBc|PUD#^r-;BIfi0(9; zW$%JD*PD^RJ6UJhJC#AadFqGZH((K#cSn4Vz56|&*s1)U_nZ1rxTCOUj_FR;IqoiW z4DWaKv+#R`b#p9t^3N%Dqhn!r_9EO>*f_^^$E!-QTloWTsrprT`y#Koes{%H?7gr% zTdDpeq8YI7hF014pyPSV)tzA77BE+O*Q=VFs~pc;0qeG&ML}~z?&eo1_9}nmt!@FL zyA4(BT;&8_mKurZUlckw@29U;dP{4XdDDrfL^tH;4I(OVkWkOhlCbULgGC$t5> z@N(4?BR0XhQPj|Ift?Q_mHbxV9{GiLKs`M|Q4}DRHh9^&`CwL5s;5QlEee(9!Frty zUle$1btKde!#2Gk%g$AzJe~&jkA+1UQctJVZdamwo=zPfQCXA;8_H0|PNO7PW>O-q z7G+8OoMMKp0#l7yJu~74EF|9pInoek{#%8;NH3K-y)JUqXaw(wdVYkfs7NY%3(_O* z71c>C&isoCO{=0&>T-Hr;%b!co>ny#(I57pdH2P&Yy*@n+tgcO?U_E$b>FMbu16DKG5Q+V z?^|o_1~id(PQ5E)JS;~29*FC>Ml_js9$q5l9?{Q}KJdD%Fe;PbtKnG7<$Ei7)@^j;Ecn{*}Qu7$%sv`j}<-iy23q- zrtxm7PlJ^y;O)p;MZ*<#5$t2%D5MX)u5yb|DbLw5!hLAHVlPDJ^6o)BwdzPL9NIF{ zpk^X;M}mHnJ@mh-C{jv!_tjMqc}K!v4IFyaUWCqrCGVApk|UYFK76Bj@12T`a262)aVl4f9iIT zp2hXeBiGr-U}No}=@{9)*lWJuBk^^`G37E|2iR$ME%u!s@+kkhqD;A**9kz+;(+#FG;ImQjFH|)pJ)x7Q+pU6?g8S_0K``0T@ zpsRSFYWjg9b3XfVXubUengzBrEOLBt)_lLm;(E3P*5KVWUqwzW&V@yJvqHHB9@X`a zoKc()z*=v&ple}KjzvZkm(N!`4!zj|hDW9r*Uy*1Ze4LwxsKNxR_P0hn_eqUqU(8m zG@~Pvi|gi_9%tR;o753BUCf)$eq)0_^zyfKbmxF6X6(0h$MZs;f-m+JqTX=&t zzeesX4u&0nsKahWDOlKtN0zh&&5A?Ca6!?S<>1;{72*Ys#s&xbjB+b4K$8$zSe&uI z^NAPi_|a{=Z#7A<5S=6xV~3znw62=OZ+qZo{I0EP}C^%cvCbvk)uj7 zGCW=4d+hVzSD2>R6*(RZ41TVxd))Kr0p8D=J&_?L+2DDw-m{-a4}zm%N9352OmH-W zHrOw~v&`Jq5pe_C4ldXknthQ|OLF0i7C>m!kufFKjJ!91tDz$!O7t00mzR^vpemkF za~M1iMHw>iO(+UHMC7^>UocWw@3Sv~ks?-eGIA5RHbwt5+~;0Gb-cJX3VW?m z$BPGd#qcR<4f_7&w}Ub?ggxwDV)lSJS|vpDoS$}h5T!E+24Z9MeKh=|FPdk z>%hH}5@jjPUKIFm)<25-%37XLBaW&qEm|Z4vkv z>PD$`QQp7e|FQp8UgMQ&7P~WfWqdAL1z*eMieF&WID7a72d%W}TSUmTrezEjrXp{Y^@*eL3IFSTLix$gX`v1#z zfqBWUp`#*>mM<2)Y-nQtqrA_n)0o{r@iO$^R*(kfCDY5Sf4Tod|K=_EOLHu$nK)iH z{L6j@{-$f1(x_!ey_Pfs`~T2Kyz83ss5M7@mV`7Lo}QtPc{equ!5AK}B=A+%3+{9D z3Ga@k3JgzSZiV8x@(J&*rpB$1zVdp>Z9-ioo-hEA7)Xt;9 zZ^1pyg(%h0*d_K?)|d8w(Pz8{4HKmU7nS2x!%KS;`Va4c=1Nq_(M&K+g}$=CXa%no zihq^=<2};UM^zlnSrYPJ!%OxHuXB zj@B))fLBZL5`7C4|Aqc%e}%r{z0|yjdU@1iDZ3r#HTYNaEXsAXaf$0cuXaQ`?5)E7 zpY=cce`q^?du@m4uE$yx?fL&{+JSk?d#PtTv>nxsZqM%k=B;KB*iPP#Xpeow|5)1% z%vwGhz#>L@(ky-t zZ9sJLvD~HH_BId36YIifYq98vWBR4i_NbEq$5GgVmAf0t#Fk?gp)j z0Bu5a$+1lEzi~P;U9cYfVc>tOIF`LE@FRIMXbv!>L|;8tzAOYBa#UBk7k?xe;_e-* zTV`^MN6wFtZgf9>uy$p1_p+vCp5U^hy3zgl6CgLyzRWAruY;r$(j9zw6SeE3 zJD2%nhICMNqPo)qAV{$`x=&efrlJFl%1*1;JF-*mWm!$CeCzp zaCSyMqX+V*X?H~jmZfL9IZ6m(>M|{!N*476jD&;#Z$Vi-mHe~GqT%!9J}<@S$l zpE6%!lyeTBV~tvfRSn*^T9uVSW_=dN&d zVtFH9(UbVu+JP}Mz?UiNjCxaFVUzirwS!`~<@y!U&aBVC$~l?81$>!;@}d8bAk!Vlx{|IiKv=Vr|cQ)inuH|Z zW-#_EjD*H)D-V*%z(Gp+gXwg?c3RBd@=#fxmxPU=7?cWYr^n>IRdD&V8-&SBUUD#< zVm!VEehNH|)fPm@Sjw|y7B3~6LTMgf3zkw-dAiKy#X<-S6Y$mANKoX+LRdEY26q5G;~kqFosCvfN`O zyUT~5$NP#N$TxH(f61E}7mL+{7>dR4k84*$ddq!C4`wJ9%P-Tei0ORXXJtqiNe^Tg z9m_wdT_4l`c<9Q!E^;u;VsZRa+Kn-O$HP{dx+r^4!{|8vX|Tf%K2EN*bg}hdhGFsi z3V8SnKOVbM+J)7V2*5;qn|3Q0X46;7x}ZI&05IKJwN%V7Fw8o-IC~<)>7)-q&HIWo z+MJkC$1_%Xc13-t;dCcX`XJxa^T7OjAx3pPY?bK~c^~3?>^J`3+UsEc zC0AL%FYH!4)Lx4zJD#=5?^8)%WDJbF1-3rS_t<>?W9_Y&%Hz4KxSu-vAfxH|{6_8V zm})Q(i#~PsZ39}*$l(8@eHc@5JZDwNr^>$682WeqGwofl80%L_KV^MMjKvo5|J6Qw zA3Tb29Ishr`jqn}GZtG6j^Y6pYUJJ?#AiIjKeZpfvB6a9}-NjfOp_tD_TJZomXs6 z7-kI z*yIy+t1WNA=-5RkJl2@H%LfoYVH@~g>c+(`IMKA)(;NMY`3c(y2I~p2>%fBT=Pmh) znnZ8ne+3roZ6|`($eKObld;YGuXWSGlO483>do>aLZSR=kS;9t5SV8a-W)$B6r=dK zE2o=}y#glnpLKh{lb#JGbqrQIsMuFr(`4+o?B0!vetCwwANdyE`Pvv|t`5jjQ(FatkPR~5U?;w$GO zHjEKqMlj2tj#XF!+JXzQDoZR_;ae4FWA|D@<#~t%M}+Q?n%F}YvIT&@ex~kPY?&oX z?)OErq7}H$z?rw8Sz&7i1^n4yr>}TDGK31J3;A=wl3!~nlFPn8zd<6vl0Q%PF!lx* z?(@Eoe1k-_fU9poDSxr&5CBw6#LxLB=_ur4yB^$G61h)+n;O$RzUc- z{YkI2>>iwweOIt*D25C=r7l=daRri}P~>=TIt!KF(jQ^M;2c$+glR ztYJhfyiWNBD0*;)QL(gzzgFiHH|nH#t)qu?7!nUjfNWjgxbct#@axG6AmXtK{$|}E z$QM8%0NW{+Zx9Nt*rJk%`YwyYVmPx?Q@^|V+$1OVP0eJ(?a3m2D3A@~9G_CdYaej*= z(N+9h-Nd*}5QY}{IES}@;J7s>z1Ic$$cGcj*m?eewhA#kkeaU}-j2Kmp>f+z2Cb7p zh=CG=VUN~Li`#oLbX}g0BoL86%t5W24nc^pbtWHqAR)mn@|S3I(eILGaOfnt&f=pC zq$Ko3umSSpbP%3!_&5WR6o^LXbqR6R?}NgV8S7j=tPw;CR?9c&V&f`K=Bx|p#Tmh* zV093lNQt|8vV5JQ7w5ZoKu9m;2r8An+@jF0llF3sKxWcc_(i&TagQO9A?t;HN6n<^vn3>o$eu-|Od&JSp`5iF}`-^{6w-h28 zjq6;!SR;|y^mTqYWHvgV@?IaGUnWRY%1QJ0)K4=5ADeP7|=pV8&Cju3OOJN$FHJ#itYveyUp zkwbQ*4RG~l1;L?r`8B%zaWhWkuNU=k1`%_yd;IgdgU}P9UoY*$`ktAKH9!`gZUMF+ zMvC3%*XoRMsi*4K+xytQ{{SHKAdph0GsmT!YFMx6!}*?>hdqSIN}G}2=m-2Ox}vxR zr<&G#_C-fi^XbQ}zW`*KDz^IO2WQ{Ec_`^q8Zf<58i(N)Fe zoeFy&_-^3#wT-40&`1RfIWpA%!N4Bso)KYzML^c2Ik^B-5?f{ zGLkXK@ANgybXETo_D8+Etis!wG?Qid~@S%@`3^8Y?$X|gv2ekq4= z4fZepf4WC;jt@c22GhHWnho49ZDW~5*b7M8JdJyBs(yp$OXpZ*G5wU z#q>-53&`W#1BLxd+YihVtev2}zC(Q1(+wLO?*iYAt}j{Rh^1J2!AJT|km>Q*$nM7( z$1KG>Ai~on-t)Bg#=w5^al|t0Bf-b+GkiC4``N}Z%did*;prIP9a2A{e$H`7Cfz~c z5t*i&*SmaVH-{TihsUR$uHR_yZ~Kv1g>@73(T|Q_blPJR+m|zeS&exM`s&BVFM|k^pRZ&B zvWE5+4Ack5uQ~0rDa2Pffm%aH7UJ}KUQTEEF}fMg=F4jL&&=x4^?IGwX8WPoxa zwT|v37^zQ>uZHxMXn=DfvL3=#LHc>|k6}bQfb~-w&;a=?&QHvGtS`i|=EgrbUBAgb z!1fcf0qZ9iuU`Qp4VxSToIfEO>Ar%o`i1c?PkUsuzv4_{He$X|gt0ijeT7%H=U3+N|X|Bqcs9zu7vmz*42B9x1oAwh- z0ni^(V0m9jCL^2afr4rJjq!sk$ZQLwz^Kji*MjMKDt;KGz#LyWCnH-RCl;pPA3x)L zFsdRW+tUvXrMAHHcE9R(#gDJZg5VhE4akodR1{^){7?=>K@T@qPsc}8ltYq?!(k|F z$cKQ?tK(xTtl4>f5)MMqn1Ixq^D#{1=)suBZ$%;gNU2=W|tpp3b7R%3K6#x@#`Q2_gX;*2om%s z<2P0KZx#)dPa(Ep-wG1-Rq=|7fX&i@tf@o}7ATPDFG1$54QN*6RfKIe4U|tMwqxH3 zX6diTTPk8V+XvdFGTZ49f|>d&@g)_Ro7rD;rZGF{?*y|U6<1M_vpM8zp* zD{w1D3Fhh>;;Sq2H;djX8slp#iZ;t2YDev&#|RecAH_Q=YBuM6E%_PQO^+2U*1w8x zZ}r;ZH>edni+5EtZgzdG{F&NK{~%cURxusf3(YAj_3cESR_`r=gE-TfJy@_nrtc`~ zZuQ?H8swag+hQ6dhwvTtlVF3spD3gS zFuB-7i0Abdjj?8KVGrhnG5g%WM>NWsvBh&R8bArwAzh5K#=o1oPmPJ|+*F0-3+zqEu`B z78$&9O6{ko3U)vUaDlaHivk|4WcFjz1UdQ=&cBZ#%baZ1}&TV>7q4OA4Ldc z4=H#POK=dPhU=_QW8D@UvXTm}zwKA%5C%QOaK=t+Fr*VXTqYmBPN?+Z?}Fwt@~mMB zlfN9&i5O3y(MO9cZXn?z5d9FS^^u}7YplZVZ-e3jxKq~Y<3*KLvBKf+mOX7juL1nsBsi_lhWF$BDG}!6BkSGJ06mJ0~8fFty4$LV)jBZ6U2%{{J#iR^$qM!=GnSr+SH;UI_vMq-S!IdOs z!jc5l`hB9Qwp?fyvXP7lOM&?2TY#7#&&lZ3q8Ny8=D9~`NgLoK5wm;5C`z@}Q+C`Y zWD2m^f;zoflxAz79JoY?6wosTS0EF*z}7^04nf1I0(zFf3ulEBhp{xl zP5o)nGRTDb4UvQ+g%A?Gp+6y7XY+-Ss63n~#O4U@=&K+k8uo{2h%%fiq~{3k>d%Pw z+Cm{{%86i#uz7+9`YWOmTjn2mLnIN%5qh4$slNm%()2%ELs*eSaT|qwh%JISg3T8^ z)L#>oLC)0g8%ZQmLT3p6)?ar6_BWhJrWnf*xb(L~m9|_+qS_*v5^SMh$rJruk=>^M zL;4LX%02Us{-NlG&H6{)H;bW7Gkxw80I*(8b*+WVQ0i!9YdWl$O&2|=wj%R5O^ki zt7|ALmN<^B5`1dt2RYm9H;UB)Z^OWZF=sNjvWIbEnG;wR1aF%aSzG;vNn#NT1aA?; zkc6pca-p2c7R$T=F=wn>^M*;{kW-jk@P%P$g6K@qR@pE#jyg%N75ErNB&3~b*yjb?FBNL=&8n?QJvErGN*m^;4Nb@c@)3nty0MfjtunmGQ4dWoq>%FZRP^all zg0G+gYulNiZL+suLc+Q;zT3C~ws@uj+aefXn3SM66R=Ghz!D)9P@^@_Fs&63R!rdr zd(VV!%L{PEBUZ=^`x~Yw+hCALE_$}l(K!I}EDBN?p<;vBY9@V#NaTVem! zmdKpL_6WuUaGhz~=K7YEL{wpW1wR^AzYi8BygcKP!ye8_VyduQXc1eS(7w_u$8WeK z38{ws^F+h?wqTDS*swC8d!>JlXt*;8sewTBdtk3%0(6!2tPIKt8LmvCs_8!klMLAj zgDc4#OIy&akPc@h6E#?#fMeL0;8z)zV;U|`CeC9AAa=ba0k4eBu@AQ;Q#Ev+U<%~0 zN4d{vR?zze(+s;3#zV^5FHj<;&eI13(;=8WrZO{!9mo+g7vSv_u7OU7a066KU7!!a z>niyPg39ciz(9FhMR|?_0MQ(I=NYsR?$+l>16dM+p_KwY08wQPC^!;^!PJ5!q`{n! zR@so_2y{x2OSDQ5ZYWAvP}!7Yf$TPQk=6*J4dn@IDt)$xjA#a!Mi6Bmgf}aK zw#!DKsZMD+ zxrUVr78sr;JgBVSZg+!LMbmcAk#b18V@Cx`4DAv<&wB50eCM2nT!-j-rlDhE_p=^5 z*dx)|)OET{umZB|J~fm4;6e`$POa1QPSqYtSXJk9&lbM$}^# zL6!kY#Lvb;K;D*y+=NX1dPCpD@eq)AjC7_Ux3E)!4TgS+W6owmVxE;w+`=jZn+<~? z53k=L9mSgS9^j-iw=gTT>|u!!XUlgeMsemK4%!Nzy<4t0k4|w~!*-g2`q)+ka7@g?@mFvzd_X=ged7 zV|49>2x0#s!|X)I*_xfE@8!P{ z53#F)BZm2juCt9h^S+n-hWt%m6%<2*!1J?BJ3U9E^8s9get?CEFVA}HVvpv`r~an@ z5*#%wg^mI5U4f(J^NGjUO~EO{#&qg@h|p2!QY1Kpdfc!#wr(4|I+^xJTlZL>duLwmch1&`4@X8 zcxsi7gU@?7q2?hm%b%uDQr;JM*x zi^91Wc}2evG#UPZo=W|0=?| zaF#HyuvdbYhDV8xb2V?k>yZcN>UUdyP%feVqyH1MGkPR-uJYMq|G~C|`44MHwl{W2 zdU?)c4|^PEDbo)3aD%Q@zI(XiY)hH;cn8wM*fFVlmH!^mILT6^Jv2&mG=7rQzbbT3 z$T;Ovs=ewX($m-_sZUk#9>trH&Mk_(aja#82kuF-j6I-pB7Ki*94pfeyo^ZFFesrg zjgv1!KEgjHKQ;D)nu_c_f#cICkGfOB`K-` z_67#ap+5unCjE?4l6F=FLw^QG2H-RDYvc5!ZB;>gWx=S7@=~!$%t$5`eh6NVFkUzU z9US3FB~_X3Gu*)6NF?c?w!;xDks&MNUk~PR@P;4^IxGQOVbylulNHf3(l2eU4lEzeLLiGtJ%RTaQtAdq9 ze2xz$e=#0}W@-0G7WKJmF!?K#qX?>ta%CZsEQAdew1ius&y|MQvKTgwK^KZKDYd#h zR}sRIGYCF}jBtbcTnm(=P+zFNA)}!!WkGdQuIEH_Ez(0Zl#Df=OxgtXDIx$=57jU- z-e^r)Q|yn#ftl_~0YRRO6K-UA6d6$3*8kq_=7$nGUTjh1D5ruZw&=(FgyY{LT0Xie75|H2oys@Bx5BN0al7jY;>a>;AMr6%6&I>IZV^ zI{+U;{%(Ah2^S(1(TwE>Pv&P~Wys!3#aTg7WIFnMxK&c2Yz%52I{#UZyE1CpoKcI;&k;>_W+MYtQ^*|S2mnRTl|colFou54x1DB$u4eJiz{0vJ?4UHIIB~yp~*c zKI?$r&yroOz;r(SfNPp+wW$q(Fsvzr-< zFNTt*7s)TrdmOa?Y}-x!pjtwD4Oovg9PKt z$d62 c4=9^_89?P114>r+Ql7jd5p!3P!7IeVF4d?mC#_5Kiyd0*joA?%=Ox_mG3 zBfg6K)YK1}qdoxH2Lop)_fkKqR*~JHN^0_joP!}yHsuCO-V*0tWP)ln>FqwGc}AW~ zgy3?ryXh~ESNUUR|cP)L|^A2nIEnH*r6BvxDqI3x{Y94FqL>S)uD(ptFpFKEV@hhhpzzdC_z~MDPJ3P|H z{DQ*+QYK0~%r1s!+?<_4>Ja&> zDPJtG7v;;isFEU7D(DHL#S!*$C=TO5cM`5936okJW4Go@xhy3?;83>&% zi@8U_O-14b_NIJKLITAmP%Rc|DitrY*XP^0HVCZYT4*CX0exkS`7SPtCcw-{#+i!6 zi|igsHWbVjfh-6LK~KPXRbQJfYl>;Y{RXTcTRR%H%3d|1KNhsD*LJ5+pfE1e;u?{-V98e2{gsTpd64NEI$)4_3 z5YU@M&N5vWTkIK1Pbhhzpn!v%>jv4%z_*}5Tw%{qhCs~+5&^Af^GpxLH=s8yk0;R( z5%`f-AcNE`6N#5XP1+0bOS=aoYa|eegqpO)rdQ&}_6CTv*&vFAA0wBVJS3eNA36kr zG!*0{$V^j5Nq5Gd7V({0CK@j%Wu`8YK1?uluW@utY%6%JKw_b4ZAq31kqm>bwdNT& zc>^5anIU3va08lpNCKI3+T}h(Wg*v^d?cfoSnz|{bWEJ;B)Q(yS2CW-f_=V3kBD$E z;+XnDfm=2mD3I%!I2Y9?PL5<1PuOsK0XGR=dRC-f?*kX1m; z#IKTO(@e=#rd*{Eatc}%kD=kN889>PYtV4FQ1X)TP_x6^fCo%H6x!Jem|6IBvdpwX z()HpSpt?qunifmiU-VM@g-Z@2X{vhiWLq#BTJp-F1F!Q%A9YB$@-UUAx=EgPpW&3^{q*bXyk_${)+v;~^>0@TuQ7S!h94$@}YD(QJKNG%I@9!Anz6!^tZbzZon z5SatTdS^|$B;zlJsZHVXLShcy+_bktGUj5YnjOI@0&s^s*9I_i@dkKbsCgty?HPd< zQFB%I$P1=?iQr<9TIL4$J@UNiAhh-6s6!%@MU+(KBric*U-HFVH8;Xm#7N;a55}aH z#6X{4UWDXG8%3R38o@e3%)=j$mraME;IC1wh~OMy=HU;?tEOT%a7D0+iTU{7O<<(+2r^&wh^#lAlx%_uKvAT#xD9Al$e}e5e?s0got7vr`a)}1rEu|K!o_`3QnyyGnE@ndWAg7f19e+kXGSy2eF6L-LnyUyG;?Kz^ zrn~TV$!mZtg7(6HOb;cs7mGBqDD)_`SoM-z@;}ofXe+E~A^1!3U(++mgNyYVdz9@c zvjl$y1&1%7zOYf_ieeojmg4`B|C!pQcwTDKct)egsHLj*!VYGSl+KrYv?0)VNG-$L z3qLYu32z|^WQn;7&T4@aHII$A%D*VDcG(~i&Mr(?ZA8%E> z0mLf&Q{k5|l6I*<>xgk4M^>pm5%x8YO<4w2jDE4o|6DlO{44w}iiLhh zn}v}>1Ejy1NYPyq>m0F83zY@Gi!d`k#dIlM=Za;WB-Y|Rp#d^HrSMXQ&NB`@Nv&1& z6b?6wQ)*j)Tk)+q2|6P6I=?u{Nn{^%qVsuT1G)>#rBZJ5M9o zFcM;3o5HRQ&`aZ46+|}f2ful@LV4vIpc){Y1Z|dsYe~H#o>ReO;{%0L%{x-Y)Mn~U z@p5Rh#J?8)Y~BNHma%$!yt0DYtom9w-JF{;sy0LKDMGE(7S-SuMJ5zra;(f2+#d=s z>68el$n+CQtcU_1Ed0fM5Z?CD>!of0FP0HzbxI6WWafz^Hbn6rAQU(%rnTZZ3s-1pCxwU-wT(_HrJgtV{rFVjcJs(oXX7Oe;X!j)>Y+N)WNGep&4<@gm1aJ)zowh=#1cEAg!WgpITA)< zq3qRWXOwtzz9v4k@z6A-*=9#HC>RQfF;o;CRJ2&+~UA{>qaWaTn6)r4-k*js(CWVA^kx}E3!V>q0 z)s!b;T_n_agz$)YKD5v_np_g}BBfSE2#cX~_IX{C$ukAL)H1R#^<|xh*&dCH4%N1JobZ%+Woq}! z{$^2%^Ae(mu3M{lbt?OEfLWTtswMR9GuEc|xg2a(q;P5(J)QtR7o9TQi+9(S^XhsJ|r+z4lTUkV!LSGpE~1m zzFCxNt7A+miSUwHn<~0&HRq*DE+Yl1nZj#kV`}QY~dY1?-ueD@*}C zTX-FseAis|D)5^rxq=kJgKM|VXH)lH_AUsVDZfG#;&X&|%vI3j8}=65f@AZpzLA7s#nMu__*Kadm3h}wZd*%zNs>`tj_L;V;%#rs1QG`q3um5)x^M#Mimr_la z(+gZPS=WeSe1Y(Zxh}Qvaz=sYEc6<6MD?5S5%dL@T+W0>Va_$C7z%_N&9_slFXtDC zW;w4RrK*MR0XzdLhVQ3dyvDa98H|1m#Iz43o+oqJqU_2d7OZ!=|DO5sV$ zNWa%?T*ggNyf95A@hGV&i7AsEC2{vn(V;MJ zpyLQ~hjH2AIvAI6`@NZnpuazRK3=bPJ$hKMo%eY?!3c3?2)}BME;}J``10^X5Ep2> z-1BSaqheX^#Ke?xN4Y*kQgv9Dof!Cn%prJsxr^L4l&+d{C3|b2gKTQ>Xu>U&Up?n4 zm?@qrdpUUi^I)|+GgMr?;i|{>z?WpRg5Apfz|^s!y5y?Ij_+Yut`85w}vunH(Vc2 zB*~q_*tI3sJyO7+^1s1;a;n@bOkBG`?;#F^WDA3Nlfef-x0fRqhB50lUx2$@O5A`EY*rzQ8wS-oY{DHaSBeuG?_KV}Ib=vSq=e6;l{p zH|J*dfk0Q;%HV_c1gm}ma921B*!72RW*-cE*Pd`!@C0=IoI;P^1K*de1;fsMid=!% zw87v(JP#Jg)`L%&K?# zv;PQOBKs-0s61;T5CoT&D;3FLV0zA->|=r6vdG|0FhOk!ryJ+o1B=t6ptZafj8q#M zOYVA{3{3n~CJOGK81a$q1i+?XM6jCdk>#=-!80qIlwJ{HFl_DdSK#Ne-NDWku1fz1 zLsLnS$7wK1y*GGXg^w~Vg56wF3=;p79RRb{-pb?%R`cQF?99L~?Z8i&8zBa>)gFHb zel1H4UQiLB)JI5~58u!JJ8-S+5crfHq%=ktnoI86E9t=>SBR7h;dtdFSh`LfOL&|O zTqipnyrx2+v_>$^8y zXm&?QwMQNp9G?-wsB}_$MT@~kxyQA!gr`~<&FtJz>!A-6+biyBUNqe?rxql3$!3MP zRr*a#JO;VZ;!dzpemyWn_F9NXWsuq!&F`F3mz^K@o9wNST% ziOJ(u;8EE}A-@6%C@+S#%xM5i=;^Y>A>7J5by^I&tE9o>4wyz?7{aS8R_B6w^}`L> zx9y3rO0(J&LwC(-%Dxx)hinO$SGTCGG0g5cjoEhsPslzC*#`F3-9-HEInB?5;=n&; z%R=^4x@dgC-a1GW2cD6A6>NbVS!(~|u#Fh_=kT&+yfILESkN*;rs zLW+Rmlow0)%xUu|4g5#830P5cG{RVB?}j#yvcO!~FCi6`d789Xc5g{rc3I$MS#U^A zWuYcB*3eVZ>QNr3kVQ;LcvOOE_=u3!%36&v*3etho?Q{BmWe|8D{UG^9INkeM|RC5 zu%X?fDo`ie8p5b@(t5>>Ry?XF0CC@(4zMPlC)*h^yUJbb7RT?K(+Sq(^JTw<%&&6Q z`o|giHgtN_1>TVD4VhQvtxb+&_HVFwn7|i-M?$=+1X^nx)AkHt_m@~a8Uk<2j)g3( zN&=hp;{FX?9*u!_Whb9jx;>f$@5@evEU!w@y2XPt9)q*Mxt*gG#xn=(pjdV$WF1(w z=f(4FbFA6szz4t*`(2e%>mP5hl~_Gm0!w9=LVm5P)f)dlU|>!Un7J=|niweQ_2>XI z_m@M$s?1tbJUuX{FS{eKRwf6__!g}-o;kRo&!aQ2PIl7{CM5Dh4pzDAcnK2{X;q#& zVFGh#yfO;h68M92`oaEwgX~VosVY}6w{I9M>G$XkY?eI;$$nC?c~}E!**!3(@2AU6 zU=1C93?7E$Rta?01o6;@0gv9mc3Cyp-%r(fC5ngbpiNd8ar)8`vSM z4JoKf*ZC$&h7S*A4~&DcM1Nqn%p6i$m7()bGz^yvc?^Pu{uTg~y5vOG$l+m7=>wDf z%~fSOJ=pL+JOW1jZL*#*z#o}2l06(aEE^7)QeCSvCK^UcMm%2NOp)6{`m1a@##Yt~ z#0%0XoW}{rD{fo)FF;eI4xFj-(aI#?5IViu<%;iC$>Yk4a>vlQ)!tWP6U*<1GJoD7QNAiq|%=1ImDz97w(}bR94e^S03r&{W_fa2cy41E&EoRXUUNroE!P zG68sp##9$y$=xRYKlCN|6;9$C@+G0dYReVtHs*`yGJ;EoFfb@4{5oe5NNB2)fI5Lam6`#&0GGn! zgxhxhRA?rUD|pI(3N5P6y2{&5PbFqZ=W;%hZwW1}R$fisE}2Tql)k}PBL5|{qB;-Q z7T7OQFT-zfmdfKoYpM&cW^U)d1ib=$44=spLZ{R`16r$VuNt=-UZP%s-{CBmM+5zW z<*Ic%a~e8pyyBQ=+D^X&%>sf3ANv`Od2T!S)1cY*%8t;PHLiK1#s*@RbUsMz44qx$ zp2yokPlKF*!D03DV0WlQFd3xx_HKII5@_6Dz=<=GBJhx=N<5*=BY{@h2pr;eBO5f+K1)hjiHR*ZQ9ZW~` zHTVP0I{E3)H8mMP9AR*zoTVRd{N!gs*8!7+{S0TRJ9r;T8oIHjFwZMl?1(ypH>Obe zg-~8iah@-bPY}*h7Ux@ePUz;EvOK*Vj3<`8kcLnJ0&xSxxjf5B+m~$SmS<;x08OH_z`Hgk3LO&$k`;n2~6E%iMi5I zP#k)$Cg7TICvzt10(*f+J`Y24YXsMlcd}*@E>bVfX8D8A?3$o!#+`;&K7w| z=*5~X*GxOM*N;Y7(hLThRYuVwDy10Y?>iIew)b^?Eg zX&3zp^bY(P@OKP`4%IkZcizpOMZE+2JP(p5C8Yo5Yz4}X>9sD`eRoS{LGMVH0p-Vw zVKZx8ulw&d%%bMQD>&Qb)52!gx?kt*re~q^;mx?}tC*ob{D$XIG8u_kPIT`rYre62jBc7ZX1*jX$<`D9QVe5d5gtv!wg5Cq} zk#zauF#p<|>%u+ER|z*Mn{x!%Nw~Fn*OT|KUM1d>uHhV$djp|K@%7w2;#bl4;WeB; z`OiG-Pe-;*B>!5c|VZTOw0Q&)z`vkgnsLmah&wTI;P(PGLDi3xG%E@fkk) zv=p{8NPNRNEB6aKR~w+$r_irK?(llfIr)0<^fE|q1Qo&^Jc>Lo4+vA#iu4RI+nI8Q zH-P8%oUw%18D+s>F}G zO>Ln*Q_P1TM9SeP<&j~XwPw9Z%!Vi!#yA>zT-Z>pL%#D~djjKVKyZ}V&?1U zLU<$RiaZG@V4U*3_KJb=1^$7PC*K)1yUr!wcdz7iXpuCClP~`*Y<}J9{LH=lxx^yr z51bqF17U7;e)+k3nRD$Jt7ih8c7fzY{a#&bhPQGv- z^9^({{43{?TpG3!RMPgb-=G%5!JG=YI*eCWoS(Z-{090TID}IrzY?~&t}I^<+%&|0 zq#>Xp4-?i|o`Pz5o*h{CG2gT&M!}vs2Oz6qzez2D1ss$7R@m`6pBrhvvF8!q;6Zc? z@Y^WrL^l}wS#J@aP5}DfB=d+*fbgbGUKysTOS<8_pZyl~DJ%kho0hPOF@Syw$8fsj zO<~5moEvWY`ENl>r6Nv`ydkWpF6#zwKm8W;nKX`LllP1RbSWIm>6Lc?i%#*4-2LLW z#}l1Et7FKbHKRYFfn1f!8pKr zhgc@vHeT_40$v0vp1JkjH@yys-$9qd+c_^OrU{(uJ#PvRFz2H_a58u@|M~l~^I&!+8xDfF^*^Gh79$>eFvJA7r~yE8rBM1bSDnral8G zfefycFT9sCSMibH*U8}D0&acY&9sBGE3{I&m*b*XEC{PN-!vVh|MeU|SHk-^Zzz@s zg!RQYa}SDvX9(WUd0VkekX&DOQ-4tMFXDgF{hW6cK7u{f1Q#tP`z7m|OcPj8o6~7D3o!#;E>VnKvz8kd)KE(Mz z;U_o;EP(u=$G(O4x;#8ps!CY z@cLc+9=ZnpgY%!2vtQ`=G)PcTpI+b#L`TFL=^vaWiXR2H>oW@ce>c2Gt$~kmJ_4en z()z3d-tY8#&{xu99B)OWpc9CWMghZh;m3*yL2G?&f$?|4`_L3uc=ikLQeR7tb3Rpw z1pPBzJ2q2a!zVe*6>);0dWS;iL+lTzwQvTmo zKI=LAg=vJJbjpX(M+9CbL80{!(;fW={+si);-uhXlcTPeohhr3mqxoo8^9t! zfa0vc-;`4*Ok=Xp4RAK&!}VNKUSV3A!JYDl!D4{olHgZUZJ}}Qhtn<#!Wga{t5_@o z1i{l9IgmhE3UkxMz+nTE99(fl5N0wLn$l<%^exatVG6LRZgMaw=t!0ndA%?fb!5Zs8- z1F;klD3yUH@MQvpNn|h{Hh55hF!(8@s1#f^r5M}@J`8bzdMZ-UHU`)*#etO^p`t}l zVafw8DmF3!WEpq_4MUhz%@HYj1f8ZFgOFe%C{pWfhd%k7AGA-WO1 z#@VHq74FvHcgz2XVIj221z2Aliy)u-#&$vpym=NsGpT@u=ZL>Sy>K=agba_`U|$+y3c>$BEO5)!|1Qyl*F` z^FM+%ON%*Y6kmm(0xGV|biUW~fIlh0nEmgP35A)nUyyz_`-NF&6@KC87_MvCUep%2 zgp;k{glig-fXj=$nEJ(g_6u^95B?00itm90Omv&^2Wv6(i}WEUNAVK?skgoU5CaJq zT*~=Ju?a}Pa&FI@ec%yCrq~i*+Mv9h{0HklM6k4ib6F7_Uei!`JM$0zf1nU)B}Y4R zwzF%fS|JE;ZYaB*`-k{HXb4=zQ3G>We}nBd;}~lRV|K?XonkA{f>~}`|6ndbL*W|E zHAO=Blt#xpZpZjbpfG7QCr`06d^T{6@s80;Ac3@=Q>fS-?%e2kM|h0+F)Dyf9KB+1 z_`JsHv%6oOLWRNgoB~B^_=3iOJNjdikBM-piE~Sl9{zEo=nmsJ%bSRhHgj$(j)gC6 zOaiJgwl@_4n}MC|WVml*>K(7+VsBIkbY;bgGvTWm)9?5K85tpz(wv6?tZU4=!#hrU zLqe&UQ=*WDZ)_~Qlle4}6TZ2z?2hm_^Aj`@Zsj~u$isz=mOE+3*`H96a2uyWp$?C4 zbhzt$g8vB=1q^4E3T^nd#@ai^YjnN)955ZHKY^m9?VM^wUih9y zm%F||n?}TdO5$iIxzXva*9q~bXbjxNsa52MA8d37U?%uC+^D!`|119!hEJ&&xEnaq z9)url^uC*Xg8mc|NxL~_kjQNGyPJE0y_6EcJ)BlWdH6*jUNfDbmqKwst7cI&gcmh} zKP=tqK1HMGE(7S*h&X9Kr(0nTzX}9w&VRB$qvGIxPM4x7+}N0N*XvI)5U{}moL)so zxT&%DuJ50c$Dl{i7GBd>cvtu*`@d9zbci#k=mUy2^Ig-Q^nZy&=?Lcqr9;F}qr*Mt zlgwr4R(J~c|CG}r7)?(1yiT&0QCndLE<-sZVqTLE0ODonwsGKjPk55K98H25+?SNI zfacBbUhYZua&YI5!JVOWiC6@@Z>E#KGY7_k^8dpt%&7K zDfiq?@qM6Vpm}>m`3~^DrQY*8CH6s+VI~)v1i%?i%J(AH0W;iqWsVY#Sksho&;OLc zhZ;R&6l`oNyq9^3|2ecv%H+Nd>~fo%%I@h;F+WFl!ZW#VD3<`;oCVnB?1`7TZz(^E z2m^LG<0-@E)Gl}yu*|u2M413K0^OI2q@BLNPG#g6yh#a21Q&19z0V9?F;RdzRrb}Sj5$)lp?pk_$#4( zQWx&x=Rxpzg4zqaaJ`hjL{v286{YN0AldXtxn)L;7;5h)l#a*h5ix_Hh zD0V*0{(?FHyFLrv=Kfc?HG<<5xN&F;m#)AScms`TAS!2Wkx0i=I(?TOI}{4RHu^0$cj&8v$u zPxDvVE31@;B0QRdijAiYtEk^$H*i_?NQ76jpxAnvxf(qLFL)Nb2gHHt5g#{;iW!-# z)x;s`0`7X{iHPORDaCG?{Ku8VZv{F`%%g_%q?dKers8Q42n5#Kc{i<2{1Y~nDu zHnJX=11D6#JrbpSa)P5&MsS<+fS-`frVhg}m#e%0WP`=Uxlhg*O~f?YiZe68<&@FH zC=fPVimjQ@TS!uqPK+UWrIQF%RL zU$g6dw=?`Np`$=)_=EChL|U`weP5t7B#ufyid~z_-mj)U@tCT`7k24S#Ur1j2Hl5K@3(_MqF)9 zx$pM3VGZ>Myo4Jz4)|X|$AK0xQrTutIRDN5iaG{+b0d`Ih|*@|{p7z{UlS*!OSy4& zkaJ)7H}h-s1iX|RtL%%gPE15AEfLMlWx%z_ep0bT^f%k?=l(7J8vPUgFE>&7f{jy7f(O>8iMNEyfj83a9N!Phl&IGNjpoz`UgyMq zXeQWoB9!hzt~u{P+BxPo=oxqo_YdU~;bwE$1N}M4H`E#UEADaSr@|Ps?Exc;wVwJL zUJFE$D}?*Zu15bX!+Pi}P)%kiR|=EOPDZyZ{(9&f?8nVieknX|_A#bqvHhtmsUP=@ z@+;vf^J-&e7T=#ZC;f(dR_P}^2QGx`vn2l1Ie0ygQLY7cNK!G{k z=qr(IAi!gEu3RY)-Zp0#{UrtgWvI|(E;c4hSSTTt za=B_C#q2lRjA;@!N=acLHB-h3hs+Ko&ga?R+QE|qqQ%c;j`2hrYagqB#nI&99 zXO#GZ_dBRu7~G{*UK0NLJjemp^_k$NEc`3?ky0w$NEeo5J_T~2kS?}g^nzr-BU-7{ z3bz3%sGC#_?nl5O+$!Z2UwI)KtkC0t4gM{--0)n}sHR5FZgB^g%_9`(L6XAymaw8P_YUgTK_*FXi_5c0)HcPO}dl&vT9D`qLwWWO&92m z&~@o{i@VlJdw7|ClXewcev`~!O9d9Z&{0?G$kSr^sf$S|Ol zHC<%=K;%pJao<#VN5(uoV?X!p@dW(?bW?hO>#ABgR+#_}wpbooFEW2bZ^8$Fns!Cx zz81$)w;cYDP=PenUOCv}Ug`_vwM2pRckTx&zsPef0j2&qh99W{_z*X7fogr^@fM%b zv>YZ6_-zky-NCyq7lGe4Gl$QE3_y4btFXxImY`B&j)6xR;3M2csvqqMQx468ZjB|b zwxpE0{X_H6TkuithpJ7H#+Doa_&-6nrRm(os-Gf@TCz%c|Ij}{ccjO--m1tkAo+>7 zE&YSL1Xy%STa=~AK$=V3ksjxMsuD%^PX?ipCZN^L{D=QDbay;qD`i}g{7l@H{>fdY zN{XD`>Qd%=NwSG3ngp1e(0lMH?&sqP{wAnMn!#PE`Ym#Ht9u#m61@p3mY(LWRvnI9 zGzP#Ke{sJ6iTSOo%Q7$VH$(T~)7(|6)W`*`0cHA2lFh_@>EGP7_R5wr(w z`?cz1q;G3#nOClO3u?3{CIVOj{)5kmT-BOh<_mnkgb_Z+{YG^*(!VultO92NMeup> z|A@S@v|KiyDuJcXg3&WBMDkjT%W`wYe3XJo?zg}yyt%ckOrI;^6D8ofFb7zLe{HQT zGv*rj6a`mySx&4(UJFK~lY`H=@ff^nJk3-M5Tk^7^nF!E@t z_oGpOK7=oFe^A|wOl$RgB)rW2g_@X1zAXL)ErtK#{ybJmzRdcSD6@lmk(sT2k8&@I ze?=d`m$;jOs`%VfAmfIpsv|G9Zh2(7EcumqB$aVPRppVI)}%+yGIlUkZU?oI`qtD( zZZdu_Q~~V9k*c=Hn%2TcUNSMT8^cPjP}LGy(VF)tO(qE@Dx@lIw8|3M+*Ms2+%!Q&sOpt!v9F=P76bS`Qa+f475-a({(H zK$yUfog={Zyt%E+o`4#px449AAy79Lm**GY8lWyJ5`tzbU4)r-{YQAt%%y!<_eV2h6t+J4t%2aw7FEcDftml zv$U9dMzu2PV4HgdPbmftY4|?(Z`E2Lg!X$8vfF~} zfG|sq-1Dk{C`Fs7f}vtX5-pQ}ScuXv#U)kWPfV0@F9FN6rY)($S;dZ|THrEnuIiVl zif2JVTY81BN-{B#QQ@yL0ChF|h$~Zt0#kKyMXpL5MYKvEapfvORCAlMB3Z?XBHE-C zW1z4iQ^k*h+JU-Ssfvv11afPWijJb%;7YC<$gTUw0MRb3;$8th?CI^c3Wk~$4RuJX zf!umW)Xa95N?+i^COY9-u3oh_YF@h!&|e#(sZO|#dqZ^q*slF5bJgM)$Re%h7N`zI zd9(*r8r6mvsta!77Cj3Lsv}X0+69$XH5~(W1BG_6>P*x+AjsxvXc5sZZRS2uWkr40 zo?ht-oZ4WU)yy@j&V%1v@+#BpKuU8Ts&b+>x0hAwH3ku7gcxb>5CRF<2ZXoV{ z5S85?1WewBc*q8H;AT}x)W!BKRVHBnCTvpc1VG1AHn;}}!^_7KhIncK?&Ef<>Z5MA zX8`%PAz=(uN9o&BtDJT01ZoiO=XR+YqKewHs(3m&0UDCpxIL=2sG9b|s!W|Eff#}Z zxV__mp9qaehq=S5VS6G?$4;b%;bHEOsynK+y|yY>CkBdfI~a@_YIgvNarRc~1!ROf z0xDBF9IF{uSX+q~$SK&1z&Sn_0JkgrtO8FJg}1jnzqj zp`H;vufwxic!jx*nu5HDO;bBXFX#vW-g5Rf$^n@Qyyb63FYWNF&b=buhW;M_iI2&a zYSR^Z8}uSM4Rcbz7rhRs%_o879VyjrSNTa01Gvs-tKW@Y(~(i_f7OsgF_0NRe*PGE zT@@#x41|e6>V?s~j^b+HtCA#wK{Bz|)$Y;XbttQoud=oiQ^}dwo9cf@b35{YK%Kpv znu@$UUdg>G-j2S6yaFno(VIKUs`WszPP|0Uz+BXeqr*DP)uyZTc4!(o%MO0+sI4|$ zHEf4oB45VdQhye`4M^DC^7uQT>7>&$0GWl&SFecP*Wp^@oX6flO+%b8H}#j%$2)wU zRAyuEs#ixJ?eMNi&SNFp&v;M$RrDz!a?i{&?4YJ2uVD++>%p7DqX}j*>WDaFEcLh1 zx%LD*nQ|nZv45)rq7@yY8pbu&PGSZ*2ZPmE^wkbQjWv(C6P5?#@eQsZ`wzZ04X?C^`#KStl~$f)tZX4py1MBcPliaN4tc-QEi(91w5 z?`^N-)ucTI5z(z3wZJfM*hRg9yn}tBj*hl=SZb`-n7h$ekhih_s<#5={1});zKtyd z%K7P?F13v7tlh*caz3_V47gqA?}lcRuGr`5-9Sq3Su4EG++znT)xSm02ikhxb$U1C z1f=wH>VdYNc$It~TMIn) zt2)!4g3-#-&Lm*EH|(J%CVcgh6vCNgjaSCOw_|6x>G>&;GwBXysHM>xI}2;Q^x_oM z8F9z_)#t%yvb@?fJzER_3w$QKU{8z!Y7PS4=u*q0g`JjKtDd1AwT!;m!^T!kD>&PPP2ldV9G<#)2;$WwH9WS4`cS6Fm&N-jGmwFxX#CYmk(Z@S| z>fG}Ad!f0c=hzve6?!k^LVkp88B3()v-eRhwIrWek10Q zOE7`DKKgcNMxFl+@o(rnKa$NuV#<+G(yc-JthF?~u!|B=yvo*%tTuNnohcq2Bo> z`vCP0;)5ltXT;32OaR-|FUHKYxYqkmPWaTP-Q*vD=950yPW7}HXNzaO@Fw#h>WcV+ z*YzD^=32b#lW(#P67$K=vEAyIW9C~{*Js}3AB6rzuEc&*zZJ9G4%i1NS7ar&PyJ@h zQcFO+{-$JNBBkD~fS-!~3;7?m-=0XScP?P3qVFQBu;0}m#Q0lM>%Bk%awAt`hwUJz z-oL<*O1U9lVn@~gj^SGJ>eC9?zfmA7Dtm?A^#Axfb_%8sK2rU=R)=&>T%+Wm^6#0 zNm$5CL){U7P#FcTCjUaiA?iuxlx4Lkvyh(#WM1E3f2-HVWLo^5RMunX)azriEkPz@ zp&^Z8k?XN6uy}CMvc+DZ+>s4fw))$cTmVdkbQ%OaX5eu(7IW2-VsbO^4?`Yg07k06 zkI`FFO$-C;FahS3u}kVLF}E!lCVzwBFg2REtPYN;u@ssz4Sa&|AOkVES`gE0DKqH} z5`ywTz=L{q#CQTiNDR}e6Jn-x6`OJmVgf}F9Mh?{#V@VYHdN4>z(J*B=7!|N(;$ejrMyK)+Yx0!#Si;-WkM{0S@cU{Vc zpM5;7R8RBL0lby*s$k3lF_tIm%(*yY~HyTd$&E1zqWlzIP=jh^1ug^Pp2* zA9K4aqtXAa0h~(4VBKnSOlg<0G5Ie41hkYCVLju3^(XNe8IKLBTVg7@@*30bvj3!( zB5_#1x;v)z3HS_2z=qU4F`ZrJM$=vTPiPsr6?4$^#aO#6jn=!&ljt&J8}@%1TTFkK zt&wq$b&^;{Zo`Jv!!c939h=6%iyBAK+-~otZPND_f0ZsaQl2fP;k_<*f zUKe@U6XH|o=g2PX70o-MrQJzQj3QPBv4Y%#y{2&&eb=4R> zV)lI?IZLb~|H8hoCnkX%qM24#v)2RhS#%wef%$6oiso5;%xMqU=K%N%`=923$j$0$ z7CvB}L;a9Uuo!bh?3{HraTT{$#Mt&CLPiA2oG$%#A)>N~XQJh7rC(mK) z?UgLEuThdkJWfbO8?A-rOe0?cp`;X4CIJcdttLmb*;;1S8<`Sx1CkBikC%@DiG)B& z(hh`Hi`i;q0;@7|5nPv5i{h;gv~vk9fq;k^3)19^4%z`5$Oe%dERmRcjQ0VCQY&EoHc;vmC)y*fC(VEgq2MMfEk`&G-V=%RYWr=mXzQ;2N19? zpeYw=tVy&p#gku{6vQM42SKld=v^Pop$wC2!4ibH1t^HTj-_bk#LnySX-Rv?zDNZj`PgoaQ|yACfEN8j)#<%vf)?vT zW)AuzQZNqm4<#3gAIN;{H_cnI%X?B<+)DX55Rbfp9niSOuIfo|@hz3)KtGZ3^V2$ZhNo&5~GQ&uC(- z@~37Q0G1YOso@{$XXGAsO0yz1zQ>`}xr~1a+DsN>XEa~Mp6XfMnpq~kgl=y-F0}oUP7DeQtKC|1*x z)av|*eVO_NDZ_F#zrqDQwMr#Fs%KWF@B6B*aeXEpE+yB$lBeWCfamJrn_D?w4uz4mSiWX!ETh+{&8u82N5hah z?5bu*?95)*Hve*ioC-tgu{_Pr*x9}AZMNGcF)1HD3tVVM^c3-b+yK^O5O+_OX%%r&$d%V}DJ?#m2T=8rdRx&lH2(evwMTu zjgJ!*qhTXtYYdW z0kr*~XB$JN# ztJ)9Z{QGh`{A&z(R5CIHp9}0&+`hbyv>NucG4N5`uTQ{Eoa%c)`+j6yO3A#x3x>+gngC{YYp={x|@6%e@pvW+_pZ)PPf{z%6#pLxcEMY zPUl+ob!s=_gu7{%#U3`Xqy;?DK?b>`NJ^{5zB{;ds&Q}l^CjkN|n zB}QJy7ioWtyWN-3>0dVn7HWgy3i{GJed{Fo=w4(l{-Ji09eCA=^U;0C8~A^;TjEOl zl%2dfIv@Itd=vN9M#fe2<#ndjv2RenA#dSJwV`pQzT(c@I`Iv3Kk_#IU#%dnxv#8K zUnhBzh>Pp&Gk2Qm=o`=hkVyQmc557?-^pUFW8Op$AoKBM+N8MY{Vo>YdiG7~068CD zp-qXK+wW~ju4ffM2T50arS`YD`TeUc{`H2N)IsuJ_!rvLxCQ+I7JWUl08K^Qz(n7X zIIn(z#R@7!D(QxQsXYWH`hqOE_2L5bcjSEl(&Ikv7g-o4NdfUY`98i@n-RCFKi%SM zG89n1BOl=Fw5Q|N^k-Q7O>_ZtXcAx*5{JkI_zY>wTgM*1#v zl3a!-jRChN{=G3UHGa0uy_@&AvK-%`b&Q{D^X|4MPLV$NPVJKfeGkeYSKukyIb)Rx zV3GZdaWEr(p3SFQ*u*SCGZ0@~tbIM+%O>cyJ_Szk3v2=1`X)&c@fZ0&{D9Uqew8h# z+t_3%qW(huhwsq5WRh(Bh}JWHv#soT;NNU0rZSN)@uS+0;(xW(b_<)C_t7)R8vGCK zl6axb(ruj#3cE9#`S*!4FkW$^#}XH`LGcB)bgM5d0h@uKlB4}8 zzQ~qk<zt*-ILtHDkXAA z9-goLEq?yM>YmINekt@1`4fIadm!Fzz^^B_MO;edAV1-^wa4O@4kYzBw~9;A(Zn6? ziTLFMf*xxNT?$)!7rab+IX-N_++zY2>M|0HS7_Do z@dFOM&TZ^+kO;x6wR!P-23&f5+a%?b3<CDw;9SIIT?!AYV*evgn|sm z>$ErH(*`_yg>B3VREY?2`g!nhEMX|86i5W#sJ$1TIpEiu`=rvWeGs2L5Y%gIGgMG2 z`xy`8a|Z+Iec${EYQ&*5AoS~bMFlErO&#i-B4P7OZ@Ts6O zebA-Pw?k4*TqU>T({xS~fW6|FFn7?qFS&zNL*#*3-5KDFd4oQEX&vkuDi7I-CysU& z3sjEao0G6;a7$lqhqwm4hU@}|xFmc$DC%Q$vTBLzvxQwx-9%DxH*iMsppzXCH_3zeySmj0 zM+aT{eJzrDqJaDz|3K%LaD33GKh456QH97Me1UF#LiS+L6L1)J*KJ6+IJl+XWT8#a zE%FHNIT0Ah!#J$N60Q!W^t*NO8=%{Ez)8>yiuxH{tOnv1c@+Oh_kDtXFty+7DcF=? z9L(t#b}<{#J7hY(SQng7I;iYV?qW4UcgSP7w=OcFbI{yx>Y_mcIgWp#i%4i4tnD{; z85*g($Vq&;ZpX7AI>9)C@0LsiDK$CJCthk zvWm^95vXz3=}srC8OpHvTMcH)h@8j28waCjAX&Kos7-fBX-l@UXrcs+S#rR2)Qv-h zwoEIZhDyluKnZg>fj3la%e9JWltM^6PzPRzA2QoaR+@$$k{58UPMr`x5H{?1oaVz2Ykkf!ykGKUbMgGBm*5xN09C9DOCS7sD zIWTUm?=iGclYyj_c!XTSH|rjNLF}LbV~?Q~Ekkl~z8#EK?1{_xue$OC%}~;Sb1%D% zDkm@F!Me(Xt3xRRZoT|A;t?ssLv^(Y1w-irzP*w*sGOAJ0$oEw(NNX^ua|CvDxU}H zXA^2XT-TIf9LgCGf-{H;M1?0t={gcBhVll|dfDw%C8EY-blnN2q2hteUVb}NMQZT` zT~9*ika@t=OSePSLMD*+xRh^cVf)2ZIEGObw2|@rIEaYpU{1lxNX>R$j!#@ zeh#3G$X)zT-Lj`=G?7L4X;4WXb{g`siM!Eerv19T76Mwa8bx?d72hVzEf2H3q+8&Zj1 z)&-9PdqSy;Oza#sKTT+Kafw624#SxP{9dS?ti-jt=tS$VWym_f>_gj;DqN@An#dS& z8ul6#_o1C+6@EpRlsJ9FW!QO;-A8pGwYXllH*wyG&v4pPurqP?i2JbbprnuJBYO`KV?Dc@dDkUM30f6VdJ2opXx%I@Iu|;F(B?oyO2iw zuI^-_??~#f*N~*2=pq~Od%BFoRU_%cra`(N>L#1;V%?dP{!R5rT5%aKVh#nw%$X2{ur%a3) zu?;hZ#RF(B(vDZ@t|TUpIE{D>O9qHuav~59qWwr0UaiYZ+%w`b;yWxEB>Kq?yjGW= zcyPphgf~nN5`APRUau=mJUZe%l03{Bg8E4d-k`gam^R`$A{=H8p*F;72d73>k7R-h zWFxzAGuYt2II_iFA#7lSzeQJ;s2CBAFh*EI6M)}H?yz_W9YA{V#P+d{X_y{@2FX6$ zqHC}pG7egG<%yb+q!H&4_AoUFI_0==z5XF=2wt%_1DEib{M=X}4vo+DzC$;|Wo^z`i06Hov1bc_S) zsCxzax7>MpZB7^_&Ox;pmYEoX(v>u z^Qwq)h;hhwusU##kTdG8^Qe$Gq&cV^WR8>*?ScY2pd!p6+o9D#;mA2bLe$e<$#v*( zP&$GFT8Mh-!ud0Xfrn090Xvj9bUBcYhyZfs&j_wabtrcbI?9|VXWHFfNq0~=h#dhZ z#2Io&{dI8_3mh~KQb(l|=t6s-V%>;}Sq|+E`Hog6&IJMn6Nemf9JNlA3oSxHT~ftz zhx0EI65ZH}6%JP&${Ya!BIGO2q190#;DnGD8m60AvB^Q;*x{%YfUdL`8m`N*S9%<+ z0?rlkK|^&DDmFMgbPzfXLKL5vAf#IwUw2J1LwwH^>i-)hQ}= zIb=JwIw_q&ciIn?>SkB$aforucd|Nj?ohz6gu;dMfW&CxP-PHC>gH7Jb;xt)(mE3P>0aKKS4 zPzX5y3Pw|O%8H9T6bo!Z(2EX5C+Ny5?l|0h4k74x-KvUf4rd%&1qxTr3kpNibn7eb zJKS^Z7XWTNgrn)YjTMg_1Wv3#>xy_o5$GgcMTLu_zmweA>dJXTk;4FRLwukpG+S3! z5$KrgROT#mqkQNnbc(LJ!qYLvDc{-Z#`!`c&>6ah3W;NyliFG4PWeGG=rmnRMXY0< zQ$D{Lg`(K9L6`772C#lO&<I398;a{)wy&{?tE@w^ku10seJ(L&vq6-yk?ICZ&@A|wbJgKk-<>#A7o zc+;uR#U=tlIuTu@`>JAt<3lH*PzF#S4gU@7oqymFZ6av`wNU0s z1=A_0Qg^Q6h-3M1_@QDSzg~nDDm=MhNRF=1UEnK(phsx+qw}kbcPeyd-L$?`j2*zr6sJ<> zemB66i-vO0MqOxSx|7OT?5^Vt1P#D5VwXdpddLYNtcaW$u9gAS6{T zcRKIf;jZ)tN7A#=-MWm*N+*$v)Z-;!-L?MINcuJOLtR#7y;F!wj>iy8tlZ=za1nbb z0=PJ69=g|l#sJ6ys5odY+OC^kxx*>hrOX2eL`FdiUWPpM6WyH3y-s;9T^_aoa1=Bj z-KU#hdC-Y)>G7}zatRQEGJgYpg^gB+R33IJ9iAwvJn7W#k}n#n6jdH`Qn`plfEbZM z1?VB&vdXhg8W*WZDFzdV;E+q12na$((*@|~_QW70LWSrTx>c3eoCd%q21nD2(68)p z!lgr`3)>mrsoxqgP|gH%W>Vim5I*|c_zno4V4l>noteM zBvcZ;1hwkkdlgaz<$q743UY-VfKmb`)5Yj1-R{bGL7|WZv=SZuF{Q|$0@nutEVD_lk;cv>N+cz3(gB!Pi+V_j$VOY(0x_8 zLGVy0^pb^A(8g)Jqx4C*DFY>kO6kf)Lk!PaqT-53NG4=uDN3f*9BQ zXO-3H&$@3bcM8&6)n1BFZalOGy{5BP?&2#sUfNJ9l`chp)%{Sp&t9>Ha;eZ-^t$fH z%7X%eKf^jaQRvEg*+Rhy^jh==ze$M^l(@EfDZ;o3{H`UN?p);&LAk5YTNX}Dq|4CT zx=WRMfyPzpZ4Kknp!Mh--OrWB1S(gtw<4UI2)%>e*V!sB4gtL$y{qf1JS{ljTI_8L z2Q%o6gV5%h+n`afRpIW=o8(ON=IjrTbDN(flPw7pigxURW8o{ZmhR9 zf|^8c8LEhVtP$KKr~-A=J6Cx+KXeuP$Rer9bS3Jn_ox~;L+Yc9L?%O(sFPk;?w1Xrqv9s226sM^;UB-tH#%C8Lq4b_lP^bk?{@eU;JR zRJsoJ(@U#nIJddw_-do6sk8d>z7rXbw1=?=C723v*`Cw`49jjk=b@wS#`zvgnNg- zawPZ~)P|1Jud2G}Y;NWZU zbh5sx%H1WzBPT!`N6mc+z&Z4XXqH}G|i%29DD~BhNMP*`zj6Ok6uIh+OxkwoF5_E`_ ziD2R16Qij`P#3yFf1&D_i%KL8QjA9UeHZ9k`pZ@3XJ_yU`Utw#4%_VsZV|*!kkH$z zF1i>+@*r|F$WM?!%k;OauDF~Kbp$E-8x~#xh<_MS|9jOnmouWSAaV@C->8a~>mO9z zcey9(4+8l260d?DRq5SS!xDOQv;Ikyqfi70K^gxypVp(*dJmP0&>xWVzX}`*8PFO% zpb8Ua1FfI}=9lOhP_^Dm6(!6CIzS~%VYCsg)BCC7g@phM+8_{zOlZA6fZwV|3|1f( zZPbUV(uFE}1te$|ZPJIUGKCtT3nXELfGlXUK2kMRxE+v7{stvLA83QYBHDts=%ZCL z_%p;3MG{v89Y?q8<5aVR?LfZ7n#3)EtmwP?QK|yrQ9vz`B~!)pNpy!^rdljC0<987 zGBq$cfYK+amJ81VtVEj(7SmRA%X@mcYPIkt&?m7aBgN23v`s%=wN7{j=#r4h$WrJ# z^aDFQOHfPc@6ZqRS*m(rh-Xf)b}Y4=KJzkskA9?|qG}Yzc;*LN$8yW*)9Aa_5HXK@H1NB#~rIi;j~(L?%WsnRGEsbfv)k(9Vu0cYpGX%C@=vmrTfrR z`rWEbSB;l6OgRBuHwdY&(f_PE>Dum{A7RblHqj5zyZS!WY1aeZ@(3~m*#tdA|Ipu1 z^|~IlC#X&IL-c|EuIjq$dGE3aU=p$!dW=5QKTut9J>lIEp?m@NUGI7KM*x$l&GcjR zk^V2$pRNzRg^{w!RK+U*JwYGqAE_R@3Viw^Y?HtW+5vMiJW)Bi`TNKt$;n71B*0t^ zKGm^qd9Om(i-bqD#4XLIIMOy5tfHMUH-lewyj!kMN2D?nRMA4r%Mi{(p${9W&7@Sc zD<(34)hTYJKK+qE7E%qlWBz{wzOrSYlj)}M>5H^wg4MJe=4X&r&viTCQyldY2%}_K z)U!k4su#E&@+pfVvyd7{gasL(>LqSxe7d5@?3cin1*&NP3pU8BSG(Qx>5Brg5jEtA zg&4+HuXDTS(;o#)c@^pg6D!=V`t(Fuv$MMl*SPun3ZrFHs5;u$UTJiT@y(C6K7$y;^y(dM$-X(!+9^~$?T^J7=2h?G*Q2s8 zLvgfi3fMpgV51EA)r?z-Z{KqmsO)vi^X-UMP6aiz7#nSPqgwB#@$HHxry?3C2peNS z`AVs8e>9MTkdOp}40!c%w>ICL7;O&KNQYqK4Qr~e4~MaaH>-Qyj{2%&WYef7Col$zio`Mv)zzNv zuRx4nUyN-U*g{8QSq62ruX}=D@rai|I6^j^YN4aBDg2@C?je3TBec`0t#r(=3OOCw z3dLa43=P#1_cXt<5x@*&8x+g$kENs(ZPgFjh8`dY6vJ<{RFxPIp)NiDMNrxp$#BY_8$s>RImXe)+N1ncNOK4x4A_ zsGjS7z^^#g_Pnycy1>27FDF(zliC4|!WI~`{1hFdUu&!)7uf+N@I!N+L0K%2i?l+C z7-HzGUhaP0kB!ylQmyPGh%|9mN+Kqv^t2t#-E4)OJnc{vA@~EU=B1W6KObRPS>y^k=2A zS=4*{3SOn*+#qOYQEl`%Y!$zxmvJxg?~{_VkTz%>_Lkvtwb^~UzkDP)`z5f>;@Y6` z*lNS2YQ4M0Upi7b8{7q@Vx@*(tIxV0@-MRkJpo&5xKZ8fe$-z*QuZ3Pn};&P?dmJ; zC;U4`DqjOXpfg^DEt?Hbh9byrC><*|JgC0!e$T&uBru0RBpusm_^bL)_lN$%IN2O- zHGGCZn&>@Elp$0_D;AHD?02hb#Ji@~AB#lt^99!JjMK7b};mBRqp17%~i2B#W# zj}Uvp4%G&a8i_|*fI3b#m->jFijglvt-+@z)*~;VD~_Ctv_sRdI)h(LyhmXG8>gL1 zwbRqE27b{o>`@ZX7iXIber!J@xF*%3JfJ7eI+tsQrej+TBkUlIm(8O-re|R98p3Nb zJv0H*7ci=3hDTdKPP}#=wU^GtC__@sERXhp{CMj;ZZD+3cJRZTFpm=ft?`OHt^=Bl z?J`WP+2kPz?1)$9fuGQ`u{Oi_nspxc0{Y{D`N*fxT&(?7m|nAiuZTy<=2M^2bFq&N zQ)(JLLIQI}Y3GCc=y_O&VP?%vkF-GbDA@vPKfM6c@*D48fju6%fgQsWM?CTZyGD@< zxcyK*c95TIe9S`?C{9r1b6RNOOTa%4LmTj#;~s5+ISJZ)ieG&;1Vfd>hN7C29_@kh z1Tr7tSM6e77*^F>^EeaOWe0j8)@4{(bH(FCU~z&iAN-77gncy#9sGoyK`5)a<8d>v zFG2P?#Si7dzBas5^XIcOUPu1L-y!=99tH|!GK8XO3^Umi17Llf`xk!&U2mwadF&w& zi)9LgqahqK8^{_*kw`3+DG`vNam;9_s|ge(i_2s{0n!OA!A`si)bIp>Sqv>TVWMnt zt4vYAbwb5hw_#gNyr@vj%47x9voolg6j7SX3hJli3Qu&j%q> zq!CLKl?$mu^iu45!$&nUL~VQpScrTMEgyoMMD0TAFhARi;}f$)?c)4IE5EiKQer!Hhf>RSCki2HX2xrdk-VXgX_PgPyn&ToOsAsg5pN#_X69f!D*PIl!2lesOQ^0|d0(T9U zYV;yakd%LT7d%G4gZ10b*d8PwL-LQwK^w3?3^!_eMMs0wW3>DeOf>&IisAQ~Yoaqj zUHmQB)Hn1d>@P!q%^lIrpuRD-e}Fn39vS|s`BQW?sE5A?g8MgAfjJtT)tAy`cVQmFJKE7W3aWB07k_I%;%N2Lj1p8Kgit9aM$67yFQcD29w-D^NQ+TMx)$>>M%$qaA~D1W zsWDGus5%``fqjq-15I=t=4X_u=K=@7eh9!36I74+8{^cOfCiMpGMqBe^;p1Qq8KJ| z#0+Wpd2yrE1;9~I4a*3Mr5mwOqfEURFoLbHg5X%F2@5kOsh0!i!46nSfEKz53pZw{ zD*=&2n)DK|u$G`KbTeO>sNMt!B;q7R5!VehV-d#5!=VL>GEPx9K0BibJWg-JqK!G~ ztw4gLILTH7eoJq~Mi^(RcLHe=b&_m}Js~yDR__3kpC{hMM)BL{cLCXw)+EIezA^yw z)cb%!37e!{LY<)hX-_afiKH*dwgj}&J207Xq523=E)gcniYY7I$`5FNL#+oi5^1us z7(7Y8HwZ(Co!D3-sx}WLpq*H?3@k-X(F2J$)xE$`i8@)flsW~yhou;m z>WhF;(weMT%AMj5l^a*8uK*_`9m&e2)JeJx8*f~rz7Cw1u*uq`;3@ijEY-MHeG9lM z=}ERO<-UVH#3mTa)OUcJlD=fyQt*5FLoCg>UVR_9C+SZHmVw{VyRi)8X0@ZID0rxn zX{=Uzdd36~Rk8*X(y_{A;A#3JY_hRR?d};8oHJItj58TUQ( z5jMrxpq6;11?P{oF5^x^A7eSjCUul&Zg2;m0DquA#%3B@)v#xFaO+sba_$GH1Dj>s zrjGY43}(k_ms4k;y;!c1Qm1&92KSFuE(g!h9oTH+ZoX0y+&9*?9Q={~6q{%4P|tlC zGCeiH(iG*J;8{MgDeBVKhF9bZ0ggV z2SSR+*;ay==&!Ic<8Ad7&qE<)2^*GsE)Ft{Dw$b>P`cKaj zAsyqCZ-M`%zri*e`_*?mZ-(@Zv%LjghQ7fz86T-1dkR9u;}xs8%aGw!sChZD#pqD$ z;x(*NV+45c50#Ert^%*n225@As*Uo>4V8~4S0Ps*BUWqlsg3o@3(Xm?T}54?jaZ%0 zuQuMRFf@O>brm-p)Z=BVsh?;wMjC@_Q@zSVg{cDonK6wKtWEJM4ecKftl@rwSgg?) zTAS{r3Kgd+)^I;V-@XbvUQX;VhSz3#X+ou`$~E9M`dh5U7+pKVt1UDqRknuunLdua zYaBHQ+BMWQ`UL+kb5iYcdxBhpT!T(v|1?6iOS}$*7N^?Q@c%MDgT-FP(AHE%DcDC_ zvGE%z(*!ypVLPGjxHjM_>sQJ8dsaxHkB{sH^Ym{nWv6=JVYztX3%kBvFC zTfGv(iYJh3k?YVI>=Wah+Pz+RVfhoRYq{&tkJvur{Mv(F1fQ_21#b*NZdk_zt zbQU{cL~0Lvm4@|C0Ll;>)Po)57hD|kQib(Qu&(27@(V8hWkl^DPE(YDHk!jajm5R6 zy)`UXC+Us72!phQs zw~^b>MeL|?eeHd(d#^(O^Td{CkrQDZY09_3-{?!&SH`m1J6<=#y3)wEk>8;IV*fT) z)Vg^4hqGzgx2fOg|6)30Rjs>sNO;b~Ver^X5H6mmSkL_q{e+o~WNnzYC|o*GxgNYj z|Aeu|rrId)Tpq~v$Q|e^W-+$Z#(F1&7f-aU2k+89W8WHEYhmw_7tn3o#wYT^yC#ww zkbBTCgNed$cA|Czb&vi9vl`#4P4O-b@1F>~gZ#lKzBBHw&GgoU3)5xqP=C@qQ zuuH~oYAd}(5z-9hM(|JiF4kuUL4-I%v5|WK-NmjMt+l(n{UhWVY+Ws)gJaP ziRjC)Z2}+B53sw&zS?8nstEBU#b)ji^cQyDc)8Z>y*)xciQJ4lh8|-7GyYV2+`BCz zXOea^^_YH$Juv=Ud(yi-B7c%~Gxr#Jg#Bs!qxRO{;YMw*_t6OTB-s|~3H=y*WclW@Q0V~Vbu;nNnGlc}wuTW|pHjSz)@HrZ(&XlPrA>)aMnv&{P``nD|%e1LLH^v7KGfk}9MU6e7<5LpVmu0H~0S3gAOi0~fpVFxQEI^F_umn#wEv!4@!%uj40cf4sXM2=9n|z*F zR(IB?Eh;BltEN1e5PYoZ&AMKnqfzQ?SuNrThv4H(%DRg_#;CGvpce6h!|*iI`nqdA zXQH~Y$y&sF5bpcji|Wq?>JT3|%AOd2o@{F^=M6{VnWpMGPv2*WL8z#6@fAf&rzq<{ zUnUyQF*Vgi`Q{D+9F0#kk#%9d+0m_26!n}R9D`3YwbaG>=0$f+vDJZo%m{p@skIKa z16hyw!z1z6Ods-465TgNTTl5jBk?(=kLqUlwngVm)iw-4y00o)JXO)a1;C?*64I&4 z1~8Brh3A<*ty|!GD7tJaph3iNB0k@=zplXdXta8&Ohbv8M0|ls%MX$smMAoRS+~yj z-iyQ{(^qvHd>=*&b7UkE1dqZ0Vfwm`e`w7PAjm*CZer`U`X0`ck_0785?*XNRkz1CH>M*;*+@y4WPF+Fhq`_CgshPYW>Rpa>0I5B!2}or%kdSa z3w4KmOJn+SY>iw9JRVysJrER)t=zO;S79}=~3NdU%?3Rbg~(FR(VqA=qDP{H_g@z zMlzG}3X@a4yI;tNoax#YDvF0HlXJbNU(AU7>DCr53Z8=3n*!<+{SrnLPq(#z(aaRQ z!6dGS{gOwN{T)h1sHe-eQqjy*oHPa3r}~wTXq~Ru%0qz+mVs*YxoDI z3H2NN9*z*sl)X#EF>~?Vri}W^K>*{JIrtval=?=$m{{pd<-1@!GY{`D<@Kt{m}@B=2K{;*$ZY(JlP2^2fH zQE)zfkl)I6%uf~DHIv+dB=AePw5EmiNBl}+`)1m9fHDTbw{#9h#JP%AP6jXJ=e?ly zX20#R@?5eNNrVgWFNOgnV+!!kO>frs`W=l`=PFylL}nr0Wm;K(#qUIHPOi3*e=a~F&|u;@aHC;< z)Q@Rn{bN6YRIDKXiHw2&f&bf7QU5%_S1{aYs;dw5e;6xN$S4Y8Fx+HnsF(QrOXUhB z1;PxDv!&{`2Ep1gO zc5+E@F@DnYeto)suCzm;+zBQ##rP@H?)pssJZYDL+=(Q^OYsvXsy@ZPRNAiq-m@p3 zRa8>(EX8}=Solr+to;m?J%Nmcm*d}?KB}ML-zLqOrG3v1XH1{gFYrGkEqhVX)-U!q zO69Z2_mC901pg0z-D8RW8EMxnvTYa~m8xgS+CVv@#LtR0>Ul=jWCwSnV#IB)v0 zex3h4Y5zcF9Q+pkUz4uB#@~OWd^Y*M9WI%^sju`Gjg-!|S61OyOs4uq|Co{av#sxQ zsmyBpXVbU!JN?rJ0bYyunNHR3@y{LEFLMUrgWE@Ac0c*)^No1x{er;x|k^ z^^AYX$iCUOUC0D@9e&fq)gSgR9oat{*o{nl6*k~D)4BR1{^cWuugP{(6Zu1Ln=aMs z{WT+{uPJwf>G0e5Z>G!jX8-LY<U;f<4knm)@P5RdrNbNXKTW^aU-LgRvh_8^2V4fc8Gmg0qyCov)sa1~DL()wF`MxxrYH4| z0irnR9OZ|v05S>Qfl)!9xe-3W@*FRfCF*Ga}^(PQ{V{@nEyOVfEh=6B@996LB4LmF?g(rU?%>XEam>oQdn2OSU7^>}O19*bs0v zu4k^botp-?;8Er&4eJ8##r4g#wS&`{7ChRV)37xlA-;ItD*$|qOozAPG3MzFI|4-U z(s{~{!5PdpTxy=(uqPllUOtcf7?}ZY!(+{ghFt;4@n!RDAA>WQckxl?ISqRQ^5VPZ z$@Wq+;T?E_8EH5eK*aaVv+m_`VG4)MMGYqhfy(7)T|s8N;dnq>d`_OWgPO&(;p5F~ z20_^Y&SKuf$C}@4=nXg;pPy%ak+>dkKAz3fenP!ASa|{P`*^B(ZNsgAtMNT~)=#+C znD_As=CX!60XO6O@)VzNv*F!%x_RSJ0(lMo0H0*8XmAPiA0?kpeu~U}3Aoqb5AjTM zb%SG|Xq0rm@>6gw^AVn7u4-@(3>gkh4N-x)&(HV>pK5MskOZcUQqPy|qvpZw_%w4% zLu_E)sIvLMK4c!e7tb|sYlsgl9L3I8?BnLa9r!Hs`wi)Vs!`$viv5G|UPEeN`6%H6 z*?ww1vk#wb-rbNHs2SBiAJ~t~hxg%m=1=V)U7*|#E@1ZK`R0!rW(2m4${9FgKD-}a zVAeJ)4m6HxU7+1hE#Mb-Am+}7<$;Grl`Q}cAo=h?ywLnv0~UBE6p-|fbHg_oLtZ8#fvD530i zfFHXG^P|Lu1Ha+}f7|?f!?nPp3F_Bn{{r7&zQ*4%_cz=LyqVDVy6t)8LBsvPGYMU< zlmFu0fcfzd<^i~u(EmC>Bm7;LxXSFN2^1&G$`Bb%p^P4{HhX9!;xw5Wkui4IQf(G$ ze8mZ}97Ib~7-Pii%zlF%fPeo1n{f?)6*^3uEo(&-42Qucyxtt3NfZ;Z9>mIU1Y^P* zh64_>c%wN~lP*@t#082@kYHH6$sDf96qm^Q5E}y)G2QsKVF{!NK8|lU$7vRbx69-O zWGA?U`4(?AkJ1!~kIK{qvO`ocbCQSA8ccjb)={851TJMx;gmT^vs`>$#ug|JamDZ{ ze3yBmW|R1;tfxSGh+4{ghks}XL1KRa@Hx1Q`5xb6o}#H2ha~1K)P7DaXU^d5=INRp z;^f4#g}`CtP38x@!#rEFN1U73u~2!KS`Pn+?=#QW9266YJqxXexf13_e7`wg!-z`~ z`xYt=b8o^u_(Ag`%`vemQCz6#dIi7|rU(CwpTKumT$dba4&wyyi9X;2>79W zH2>#(T-=tJQ>g8tRxs!P4oapM|J+=xIW0brSX^l90#`EU@GkR8%@y&9#EwF17qqz9=+ix(@tvR4KG4k{Vlx5#z`BMJ>BQ)c z#mb}L+stkJ(%->`pEGx9R`XkCx)zg15q=i~{$I0BQxkNa-|wO2moqTG;TO$cYc>T5 z#)#ig@FTlne!TLlV0}4Z)l>$F_{r(YuR(qQGk(Qv(liFe@B^5wUvnFnJNVD$Z#6rE z(#ELYkR7up`pl;^d-$o&#m^G=@L$b8X!Zpa^0OUf`~XqrK5jFg(;NvZAJf54!vxEj ze*Cuil13k-86*A2OThBO4XJYGfB5f%6(K*qi{j@d;eVJfJg@LmfS3pPU2~u2bkKo; zGX~%O`h ziC|VvuI4X%_hGi@l?mhq@F66`WhSbQ@g+vE6Uj}30P+`_+l2C`jrcloqg=2`4hx&7`B`Jj@bW3)Rd-5R=*U zx&Wt4Hu&w-`Q%O=^k?lD6E-WQxvG0@VL%_rl^X#BX5-(AF%e@PaA{MZ_ z$xO)*#1naJ2RT=AASr)|^;>QS|CM?H`#D)4Ihv$iB0E8~GKs|NEKM$voOup1Vj-Vc zA-S5=v&4FW`zH($h3uF7+QDIfu_hkwCG{@>tOy0e1i^Nb8zc{tgvBx|MKLf@#C}We zl%ys94QeF*$?{^-itOYoTLwBiB+1VunOMqxPwtiECFc}tt<+8?i6~~ROE!vIP$QhG`YVRIEB0qrxHr`9C<`ip4?Y#I|;tej3?Hxmr1i^ zd$N2fc?#JDrx9KY3ANOm1DO_>S8RXAtYzyX1Ar`DAvf_B-kWW)e{`4D5+b>?87y&t>h6QJj40YuZNq@t z%jEL6X(Tl+4mOT$U9LF8b-=TUUF^h0EcnE87^sYITo-(AZ2xlLN90px7V$nip>ad- z!?D6Qt!KDT;5oz|c1mMoa7;@6o3=CHr_3DUBQ~qCJ~$*L=S}U8)IKJU_>`UZD(rhX zu`jrgKg0SXw-26A>|^IQ9t=)SDSHz*i|mIN5C_=VjeCM~Q#xJ%(s($yG^PJdpa;>y z1;inCVdIhD@)Ti-tcTJv1;ppp(_PnyJ@oey+ zl(G_K4|tGSNOZ9)2NT*J>L61{e95k9>C`xn2=jbA^~ct7}FN`DE!AvAw`r;&Z9@z3CgDSaij|9~|ANREM3 zH~NMM@*>bs(cPN#;;GtIl zE+USz?>5GU%#c!fzY;iyd=4)s&aj_0 zE(kgF0={QIYMc?$CeK+RJ4b!aEGK?s_cs=V9F?nA$j%RgfrR!Pb(ksP@9XJoTprRc z&tG9Z#~tSH>*-}bZ(I>_)eiQ=mVpQsazfs^LUEq!f>#n3*{>Tng$TxpS1K-mUob0) zOYAp|8$uq+g)3zjs4tnfh|8>@v3@YYeF?82uCdm}T_M@ylsKqgA0wvLR90# zZz(QuU&HSZzpi@ni!F~m5o^)2AP$T#pN;vsvt@p{PlaqL^#|5E>Ewh#^$ zr=~k0H^=q8W&1DkZ+Hvwlyzuw34J(DxJq`J(lHf;lSSC%8=5e_c$IAcDhYwbttl`x zd3??)?PW^Os0cTUUsJq2VZF@h;cCL&63~TEfQ?-83V#ZG8VK;3vcc*AxDh zxTefd&3Nf*iMu#m~bMA*nfQY*)cB~4~Z4g5ZzzyUK{0Z@z<-?|_VcRFj*O52ul{uD= znr4KxO~_fNy+NIYKPBc`K5m*7);>YJPH}_#5#CQMuxOhW4_1(~%syhi<UTBfvty-X)jY&kUq_C%3|ZQ2@^kXBq~vw`QB&xtoJJukzz zO*_NV($r1^2c zH2K@)ZR8?+lz7|nd(*YBGp|C<+uGaIMdm26-g39;df53i_HFHN+(r0nVuR(6rdwfG z(^}tF{Kj2^zachR9yL7<6HFAZ9{}bUvC;BZ)1P4v(|X>v{>J?m))AE!=Vs6Fn2CLF z+kOKtGdiNm@}$WzTr^R-9{3%(4C@J%#jQCoJb7Z-dfD&PWkyd_TZGNN;RzFS)@y%9 zuE0j3*5cDF2~Xn_z#Zf#*i2}6hz-x1*tMR#gIt9zM6)HbIX=8_BD-FDhq}sGh!%^u z84fR**tgzx2mG1o=07k5H>VDVCQEp8X1Hcz|9arAJ+aLqZJrx`U}EtG#a-@a_*-I! zMbmBkwaufa!@h|?-P;+>B zy0BcF!vz@w3GO%eGNHFrH+x3JWaMwOJ^+7bE)xce zy4fYdKSREee1P16uM$Q}U2|YWaz@!k;7{Z(e2p+!8k!{$X&LH`vOn#KEfz~lb8JLj zM)5}51Mn{MGx4pZwHb~m$>{rk;7{-md*a>Z#0VmzXQT2@@E+4goU-h0&WzAxNH;0} z0`Ct&X-5A>;4kD4_&RaM@@aE=gepV4X&6k6*q$NZME=G70pB3bTD1I*X=6s~CdEUp zpSeNwSUwvD@NMFvmxLiq+5U|$Rqeq;-2N#=CcupCY9L}L(n!UXN&d;_?Y>N z=(pT%z7la_QU?#zW9A|8z;d^_H{$3d^%mO`@CoyX_{-AYd?(`Oq`ob-r^pldG4asy zp!t5pJ-(uR3O;2X6OSx^HUAm$a1y&k`xJQ!KP8@89yLFX5KI6-oz*W2aM*u-m!CGcsm!eudUyr(=?!$hF(2B{ni|a#w}TLE@C;T;$#z(2^KQ zOzx?$Ium*g_GF|tz)lKlCa3LJG3P|Qe}sof=<^WpYG_E8If(1b1Jot zFN4$HKv*er+UeZsUKH@6@(ldD<60I(9-3TM2?z>=NghRk-Lm1}Sro#<;z;A<)=Ha` zMA!)w1$WC^u*egWJ1S*@ox)DfqR{RMEgK>q@}Lw*Tsysr!n-qCDkINNW-GPBL6j-2 zQaTs7CV3Y{bWd#A6e-9QS1Fu(-IDx@V!IW5z))) z6h8z?XL~~0J-cO3WNv0h73o~yp5$LNvU_IB&d9V(b(PFzr$=XC(X+%bi0hu$vM;hQ zv$aa$((92VE=ufP)N(9R#h;;ck%&6QMWeglXwgS%GP|lsmjY2zP|=ufq~&mAX=Z;F zAnX+-fkkk4QOn85_DrElCfo^hN`@d`Wqk%)#&*Bi(i?d+Q>~J@?)2g#&!9bXAkyoZ z6k3$pt!%j%Y0PX@DO`KKlERD9yH~bci9C_np;Ed^ygS2-GP*anT#G!D*`*>~3%rve ziY9cIwcLrknb|iy@eFLP5}(eMA@lqIcJx=DPK zqKdM+>skV%lC#RH0ry^?r0All-DFExRCZQtwZdKE+ZkPy)7{h(6_uOSQLS{B_;*T+ zX1@x%Ury|9$&AX&>Z&H)3;dHt7QNQp+5$(FWc5|s+$8~>aYggGsg{(e(yab!z+*7+ zVawF0?OE~~t9!41Qe4s8?vGp2qf}YK8kxt=fX;Y3%!+Ey%CE6LgZbV2TMD9%W~pmr zqMc$s@pniXRkWa6+p;+7Kvr>$%|jyYloc)P?rK>bb@K(h-c7eGi85xj)+j{1;-t|< zi@U#W*%T$n7ONG&5DZlMYHXr{prkQHZ*+gtQW+)6ma1(c3D^l0;oWS@)~JMRp;`v) z1UuoPqHaS=eN;$xj#>-slyoK+E$jZl4staK6iAYi`NX#^JEPLF)oPjN&fw0Jq9xtd zmR(WF*=1_LvmiJr#a`JLRhZ4H6`sApN%Eo<-4|Mp*+J{MGo*7|(W>sBT8{Hq7kkuJ z&)$%v@kOh<&$S$hD$f?y%Di@lcBU4s?Y`V%j@q6ruO*+ujh5c1quJ_Ot=G=5&a|TS z-FI8AN1e}RYpq_rVM!Bg?hQ}MDB9HhsO3u3iR_MArMD!aGoxs8 z_mdXK=vP2AWhhbJ{h;N3)Ya^sTCMlah|Wnx72Qr--J?UMhQY&ZVV%rpXJlt) zQFXV&R+nhe6ltBpr#CVwtEi?M*cvuikwkT76{)+uwnjzgPU)z#`ADKWrxZ1Gi?_nj zB~#ROGT)uiNmGk7-Qd=g=+Y_w$JTkrHF5ucyo#odV5=zK9*!y_G6L#dh%yx!4uT7b z9;mG~cNaCd(5T=>aS*H!2wWgcMJ>U$cageV7bVGMP%7==s-iduS{JRN_4gqnqW=DU zK3=c)0|d$4=ktEQ-mSa116WsZK$}rB#I9)CW$RD6QIjP5YKCQoxu!?(c5?@y?(h^z zW{rDhjH@z2xLYv*^?;)#xi!4ZnXVNP_-@+(Fd#Hll2gOaobP%*LbY2vfb^iEC3!V) z2M{^2Jb0Q!QxlxI(e-jf^=_>bIg*+#DXy8Ax!bidqW*pGB%*aUcOdEs&y$NIc!<4Ea&>BqwU*nU`GYNO?NT)#=**k$%*C$*G#;%;&CcksO8Mf9NRqE1=kr`O4KH%2^@$f*lQi zBf0n?__=0FW=}V#D4{~}1v&;^B>A;wTV`*!!BJ5P*%uJ#43JdU?9J@&<`ac0Y+pby zB>^?|W{z-+jw)BkAPAvwiMggQ)5|R(s#>9iNQ5FJ)|%4HDR!WM(6@>(xAdqcg$;rP zlvMItO?hUBTWXYZhRB5#z)K{TYP6Z{3SNfH1@fns*cCI~Dx&ZV+dIX4xARe|4AEd# z*kNf0;ta-x^@n8=wnmqk=+aIazr8UQCr?$lT_4|FQ1Rg$I}b7qNK^dx+*Z3q-dt(M%csn687g-=rL z6%Azr?KOVStaOW=RJB(z6b+)}k_R={GS9jtO={iC9fk%|YbAfz+yo$f67O5tFerpt zCwW|RE7RmwHc9*~GmH&^H%Ok>G-cMhRZPOawGD$psST2+HTN^wuHZ#YD*z`arG3l) zk_@FbO8%+&JM*?%-K45-6l@>08^E&;)9Wq`STyI81il*9kaT{oQ>g^Y+PxLt)f5iG#jZ)*yFe zvV0$R1UeDkCh4i?WD~rXQXa>8_kC%v89dk#M@? zfBMnygU#&(bI+d~m8o@wBB?zRXT4umoI5>Pp2>AXqu>lXSm|?>;4|Vvuy6r)Gi8MmdqWRLS;#K`e|7u?$J|(SqgV_3apfj)KAXJaYv@e zv$!7UR5(ZC1Fp^90&)talz8fAWgT`8pQ6eVd9YJqmBd#+FYB~>svVG1sW+CR?nzTx zv$#A|1d|fJK0fO`fOJ@#l_DN1g4JN1;n!Ig-IY@~Ny;hw zN0QShjU-qv&wAwEJf%r#8wt&ziX{{E$yv|c+orTCxt{0@xI_}J-<37U1DVRn=6jMe zs1iwpep^;=kHJ%U*)mUPCR{3s)GM+ekD#e(*?cc@7QA0FMW3DJ>k&P*DqG=&#=-|B z)AhyRqH*cerfi!RG@CjknWZn!^7lxZ+M3PvMrXr^CDZhUSz#XOQ+YWuZzzuXP9nC0 z)Tz!nqBn|I{h_Ql4|=LRhwDSmrVdMH>%YrFJx)$d%i;U5v*GU~ae8gme2?=}RXHLb zR{SA|gS8S=uggmGXq;LPwkNZ5J^-i~u9PI`k7cd#crvv$hs#Ij!bc?Y^{28ndN@RP zSbmUvrT-;shlf+NP^I8QbEzLd^KNw~Kv9^sgA5P1Xt9dnvnZ^SV0tEt^azShQ}KPt zcau2Ly{(FFLYWU2notfL-DZ^2I;mXv5tF5i!wM;(_W>aS#- z_DGF(&K3Ew^WYPbHFnTNv08sWi+x{V%ev~39?i>@`9brk6OvW>rmR|zifBC7<_9gH zPDzsVce5^e(CdD#B3b`9>z+qlbXBfm6be>aNH*&~QTF6HiG+C{ zgV7%nlou{i<%vKa5g;HBK?%l)Me;oE z7l!1wCijk=GLgh+c^BX`=W=WwwRJoPcHwF|%pf?~;xt`Z1;;8uoQcBf! zDtKy{fF)q7q(mQ~%;34jh}DdMUEBe7^9IL6skH)fF;yoyq>oZ&@_b@&wM_smp?;Tq zrx)8nhvj$4Vf}2Sh8G^AQj7fAC2+l@Qa?|5+79^sI96CO@yyVj_X$|Ti#bBE1&b)x>{B# z@A2wls`3?M(dFZu}7ZB{{3# zu5=vfHcec>jAK{AwI;|vHVM8hF@hEf|B*?cMu&ybtCayGQ>Jk={6KOw)g&?N3zcCb)2Hz? zvOs7Jb-xp|7H|X6)$lz@o&JzAZe;2-XN@S3T@BxtT+){+r;Joi6KWKJ=o+|L@`wHh zB{s5bnpndGvTHz40ISz26Gv7|!`}yu)9N+0KuAv6Bn@^;-Lxu=A_$ekEt1RnW6Cun zpG<4jaD&lxREy+_{)BS<$hK*mLZDa+Ka^b6pHgld=`h{7P!!CrgC9$7>zVh#U;1;( z-6IE2k1Es#lk2I!B{%ie%FK~I)A2%EFqBL^?F1c)Cz8!~^#&z5GH7~QAwPsnhM!38 z=&i~Foq&1tX1V4S5F5;1$azKwGKqn1i8L_8?DWhP;>? zhHe9ZW9XMX!ZUhCd9e(1&`=IoFT;RrFVBP-)y3K{XdBf7>tk@v9^<)mMpLm3Sm2&m zU&9xj3Nn@Y6#LxZnmxsH(Tw_HCX7vmdtv8YH-DdA5f zw^O~bfd*doOwWoLc!_Nyltyte7lUVZoF_d)UcwDWcfftI{~1PSW1e+g6>#=q&&xBa zOSIu+8s&%$F$89>_iUTNDdk6yyWoD<&<@a4Vhe|MQJ-V3hN$dJPoJ5sCESSjfODxR zg53oVz&s41Y>j95OqCr_{V{jL)NIl-Xl7a|KN8wa4ZwJYY1t*7(KF?x+(>i}JP7kK z%*{Uf#uCZy0ZmBW266U}o=ayomD(a91vLoc8{)H%d#23flxZW$bg(|!&#)l-tY`X6 zUYRTk%Aj1ZQHHOxFM29x3do$5Q8-Pk|#Y=UdEkduOT!n z%f8`hoGC72qSy?02o_+FWnc9?KT}m^i-Pu2L$E-DJo}Mn^UNk-LHEK#u^_{`?0cS< zXI7VKCz0P$Ut$wF6}CyxK594?Y)H<2?%6hzv!6ei+((VTBHjb=m)Lm2mh7HhPP1Ce zxRcR+@CYp2uq%6z7c#4hWm|S{ufemT_G>4TnUotg(U6wizblw*$jbKe@|lJ2w@rq! zD0ghCL6r@814xI$NH66q;Q_@|H0M2Fn*wE1p4coyd3Kmr`Yhf7*;FWp^2Wr5 z((EZ-`Lm)9Xs41nln*x7Ky`o)#b&YLNcKW6ZkMpdvc zh8lF)i@h$-sy?8NCUe0yzpoAFvJ<@;XVo8IqS;&+L>n27Wv}siGOP6fR|Im3{ICUv zGud0c`o{9gI~5nQGrZhl#pSkWs6EN(8$)$=rk77FUd|PfdDIw8VlZTrUO}Y1adtR4gs}E|YcPd)E8e{7ZGSi_#Di}*P zv}Ql@YL0C>Xqyfde+1wVEXD9|_A{?1v8@NWGtgo<>|?OO@FM%USDU>kSwxM;wivqS z^!9Fxc z%{wfc1s$NKVmStW&V29lvsH&hv1~aUjpZ6f=V0D-v#SoOe}j4o`IDZB62dk-Qrpgb7!N6;F(yNVRBB6 zHxeiRjysz?M9swZ8>Z%v-a&C`-|^$f?>az-A`blyj>9Spb2}C6VR$xn*f2Y%67*Zv zf5*hJ74TdznD7?J;-Csjj2$s7$T{ns9#{RHHjb>I=3qYs1`g5{|KI*-vCC;Rsuj9q&b(o={R`>cMf_4p8pr$Q{ORK%l4 z;U(B5gEnWr&v~(`Qk1|}QA@CT!;zeYK1MNKX^V%dC>hq!0m>_73D8fl3}X$toJ60- z4hy^tD30Z<@p&R{t>n%_kHIUktA#R zldwAm>-(V5P?MA6gUpe?Qy6kcpP)HuNBHx}6Vw{4$zaaW_=L|<9TClEPr!1l1=Kj; z6FsN=i0qBRW~k5k(P!zLrXz~^=t+1j_R#QG&QYHv0BrN2Q`9=_v7sra)~8|){yldA zdJ0~TJu%$Px#W{Rhxff~0d$&5###-xa!fvDbHv{>3)s`}2JETfeh%w%a!%U!+6Clk zDg`JW^cNT`fiU(^Ir2fcJ^kW8=X~S_)F(Dfdmrh z0=3I-@#iPaZT*4!4SE3vv%AJ&sxW@~Tpq}vK-(1qj6Bs8e*WAjFwjO`q|&i30GP?I zn2Y~t`v$6}6qpNWT>J>Ed|$y=&F7z=tNKv{x*uWCs$}$2E#w>Lmj5UN-EP!gY?v|h zW3csog+P_aZv+ic?G}5DVAV#x1L{nPz=<>bE%v2xf@%lf2^CTbaFhcp-vdw$wGVSO zMyWFSJ}3?@*GOzh8qyf&tJd7fhh98cqz#nMV z0IS{uq@Ge@p2k_K!*&Z7JQYycn72`^`jNjBZF+0j>}#B-I?Yc-ooN{c87UR!XI!8< z3mVqzDF$PWa2__sC{dY!raSaMlDDU;)O}sunxo6OfrIzyghO)gyj0+Ctu^TH+~s5$+%DD z;Ts!YrBf_sYvKLa6l1o^*LP8Ty^dK7{YLG_rW&(UUcL#SUthbJ{Ea$*iHvGhfNx4X z=O}*(`x{)2#dHDW@6=&zwy{_h>8p$v9#t$sFToYq9ODlk1I)KBzUruAsa+v99#Jjy zJsF>Nl)sd$2SIkIQKw4uZH%u!$}C0e;YuvQ4lc)6AJr};{{TCh=NnI{Hu^dwI9D-C z*+0PQn1#j*stjMZ1aTE3gZ`v`z`ik7t1^9k67VXUjQo@O5tA4VD$+M7L0F}bp)5>e zgwd)x;2WC|Ri%}YEJb5d<8P|Nz6o{<)IjO5B}TKV#5X#jyh@e`U8btAWyWhCgZ3I# zRfYJ3%TE$(4UV)EeNyaOx z)4r(*&Odcg)cRHcAX>&=flpv-j5d|tH$NfjC+#xwDs>WDXMC)>_aSJv;6K@xLD#5L zcFR@Y^9ib-M9bN0@EL5Q@ulh`aND;oq3S2aa`ZaYudB$?Mb^ zY!j&Q%C{|nbBw=&{EIq^Z83Jw?d>;sUeqz|3i1YZ9@}c{k?ZK^HcxzvS;5|bFJRk@ zoZS9?KJ)NnwiVD#VA*c$lMDF;y|e5x4#@TLOPE*v*0S3;FxS^_(LDJv?n?9~d=X1G zev#|%mozW!7=I;sle&m0j4rtWekt=f$3F(E-cwW^Q>;WA;a{=9?O;$Xk>i%QkxFqJAgmr5)!d*}>-bNE2+pa*X`kI6r!x{5Z3Uy$v&1 zu5olO=2thb>bN2ay#rgYqK^TbyV&pYyz=9+BopD7N}Nnb&%pyBfU<*J5SH$+^&3`hoR(YS7d^lHec(1a07PKD9gPH6vC4VIod+8n9jH)m-Rb2 zKkX!cE!09aU{%Itxi|di`SO$8wde!*Dt6Mi4p`35SDh5CWgo!Tu+zpBxwrl5=2rm= z`GC5LoiZlpKDPtKTJ#}&9sAk1CAa4&rv+6f-vf>F>rXOk*@y67pvH#WSAGr)oKK0? zv46uiv5Uq{x!p$fUBEjfTL(R&ZeqU}x8?R8HF!bPDeXG)5!Hy*bbwApwQ+B*<0!WU z;#15z_7Qvw(;Ii?4jP3lkiP@jxxS-<7NnixuZJE}w=jcImFqDoc0tuC#d`D!d>blE*Z65!CkD@xIZ^$RM`UY zX?`;Kl)8ugZu~A69i?0#JgrDZpLKwlqbe5Qr?tuCGpZT;(?|iu$pvYr`6*;8)q-6y zp2(d)%DABXv@8YshkAfrGwN~^M>Q^}e+SOwZXMP4E8ZE|h7QFWP=A_9VgG?2V%Lqo zd^p)@oe*^h1^$5FVypWqQ%Iz!h8D<0fFZ>w0ZDevaqr$&Zoe^zhUx4YZCZjpG zWK{H5%G0ON=IoP5Ly{7)dHvb0} zn%)NMe5=5MTNG?(#a1f_#E^ zV|wH{f<=+yUzm0PHdr#f@&JD8Ajd>{0{dou&`d`Bwtg_Q;d6F3{boW{6{B! z-TDi6TNg#y*W$CxHnwM{r5*6Mkzm6TKG0sHQ_=oNB>+X+fnpol6X}Z&V)%LUN1y*% z1y-nl8gCSdZvhAChYw}O<*gb00t{G4yKkm*<&D#yu2nf$9Kx{tZgpXuq2$yF2AkJXwFh1`%2$ya;XWIdB=`ZYxvmi{E2ZC?e zzF-5*80MQi{pfs<9;;0wK?Es2j#CWJ zoo7JMDFX5{nbja3vTtf*SWmUj`wJqMd9%1f(G{8}eS+fp#YZ!VmFK zW>a3bF@3+`f!H;u|3?7%5+BcO$?G}B=^G(PNJIN0ZXW^=f5&Xka~$LLjrals((aHE zcsR2wZ_pS7v!5fbcm%U24*~&TX+XjDN8Ir#Om?2{m_^^zUto4aAS25Tyg(#W z^#v`+0HR0YB1WAT0Fre$ASVWOLfVscit{4JD8CV2RP3=UW-z6BQ^w?j^bsz|jX}Kd zSUY&5KnEc{_#Ea3@K9c7w?H7|0~a$#@)nM%_y)gd+XFe%d_111%0oe9McPGvI_XUN z;Bz}HRUip~bw)rm0COU5J($36x@ZG~?(``9YvxSe)-eu?KytZ){DK~XOPK1s%rQQT zqN=rEq!f&0Vhod~8G|g6gXu>U4ENznnLqQ6g3->jYJLU@=J0SS^IP8GF$rMYO`Aau zrUm#CrXEbC#)82-1sFs_#&%ee-YUl8E0~*Lz9%2dv)CtpcoxI^5DXzh;8eS%Q!x{+kl=cL7U@n; z!Lu1}5`|BKeh?YxZs-Ei^n4`%Q*jmJM=k^xzUAPy5*qm);DY;IBnHoC#*u5@DW>6t zOenb+zAUNMYuhbvz*cZi!UL!Nkk0kRXK`zrgGv9l>8GG5F61jiJ8*v)OyWFJHTY3x383_=>^M_E01o z{76OMTR?E!#OxzIkXXEmQM^<5BT3*NTSh_%jpJsfkPJf-KrJmvz5_{kE5lVo0$Pgy z#*~vYkqS`ErbY$GV!VzyM8*NB+{DdCg&iOUDZ|AkM$P(z&~%p3k%@MTU6F`iW{!~< zQioTW6#3{_WI2AF`Gwr^F=)n{OtySz9K8a+$yAe>$P>KP#4TXQA}jD4c8e1sG%E_w z0PsEBU@njuh#Mg`iwf9r^h$j5Eye)8i6A1)%-28x^eViGF_R@oG>|X_Yyh$vzsLMe zR)RVA`j3IF01Bj6sQVC~^sE`dt zlJN)3HS#QyPPCf2g=jFn4u8xvk+q1D5Ly(4Xb6&m|HIrRFCjD`wlIZk2(kfx&O9Ys z0FYa_MQA9p5pQGuC9fjq36(`u1clNY@E1%gc^j!CssLa^kxlq303IRDM3co<1WmAm zm*lH=U^D)j`9$4Q;3O4V6~*j$WDDNS)I;qkaFdFy{9Z9&07%YvlYKzG* zIu&;?^->QKAaB7ooMY+-ehDyyi3orvtf`y|iZ>QC0@;Q4H@T}L1xl%~R#A#ZBD--Xlczck{I+#~bo_s&(P~UkCKcB* z?TR-56`Yi&)$+^8C^`clVuIC+1(&7Owc0Xrk{tx9*9+RDoZtBS$;rrGe5ff{y;0z> z*!efnes(gF)d`wvZDr78dLQm;ic)6^d=|IXa`$(DiR#^g!Hc7Q)9xpyAep$EX|g&8 z{MHM9Q|w2lAWD3sX@=c`|7P0{O{KGOZ_`vYDF|BJ0hG9>X_or1AYpOUZy-w{npWX{ zrUmM=g7n3_I_&{6n$GP4WaVr$Sl>6sBvG3LWs5m={Blx6leoaNM9tbQvT`VfR^vj` zGW89CaJ~xc;`%z~AT*6GzynQk^&>&^;-;>a=YqP$RdtGk=yaqAA8*>C?&*Jdadn;c zAUT~bvKM_Oc(S;)j(Z55ft28(rcLT@{(YD5ewQ7BX40j2R43pcB4^Mgc!X(>8uAZX zlJ-0QFgXJ$#Uo9McVHHM02i6|sz>-oFDd_Bb{LAK%kgQZ!jFN6f9#T~-xY_^SmXd6 z4bmrk{TD5%e*+HU(@ko1h=1x5=S!mR*xAT;c$`VA4)af6!n-8<4xNo0#$!!~)N%gw zlBi4C@5ng10^d9r99Xs!`6cE%HV)B(1Ir^|Q-g6y`6XEeDW)s&d8QNUME}Mm^_Q3m zb`DslywG$(z23iV38$W~?F1SAZcD}Wj24;;wz_;{s#a(E`z*!lZCWVa4lbzQD%iR0 z?ad~Gn)DA^npV%RB;)C$xa>o)*z~))5@c(X*UKuQ1iA`eX1b<6>z}@~x?WpJCeT0O zD@-@ldjI^TQGaNUbO9+#Ie+kvK=bJ1_-fN#^(B9LsrV1(2sF~o>xnRxj#h5525R!foK(hgUd{J`heNtvAG zf{%T~gy);aNtn z=fbv+0q4pG3IL9gE4o-XR}?>?%aQB&&mA>dFLRHfE07!bdDE`^L1P`3IbRWV1y0L^ zR}{z4l}IE0t7%`p$JkgqfL78s@oLkad}u7POn!xXoL!0B!u6)?eBZH)mepV3A17DQ zw{ga#&JO^Vsw;})XcBS$r3GR{@*33N4b53d6t zW^CCq@m1yow1&QqUovU)=Z`(VOm$Usl9eM5@T*M#IcRb>aQ{<*|mVY z0mfzJS7j%mwe$o0nyD&(<=EzBO;>Fvp>_1%_)SxFCqUOC5Ao}!U-Ea1by_aGrZ`2e zr61yd*+Jjsylb*k(0cl@-I6)hXF2{BG?~o#C1azPmjesB9y}A>Fl zBfzdlp5pgRzvowuU9`OZ8uv7sj6B1eO@F?#q#*y`_f7TrKaO3xyy=?lG_-;K7k~U7 z0C_$wrYrfU$EGfKzAie$ZlGK72c~QJXUC>@wKUlQ@4D;^w2^)R9@ZY@UmUAkF1)Td zgKk9H@PACV@=asQmW!`5XV{I%OZ>U1DZh4X1$Y9qoq;ydFYyz zBJD5!S#lfVKzwTcyuiZ_xM$Hf3g?0`{3N zOBcn`l}$Ho7e4^}i{u_;5a9zdqmJ4w+KXg5{Xb%~`I`d$xJ4`LZ!#CzblRElGcPDO zJ1%`CuMyNxfN)9Jj2G07t5}IQ+NwK1yCr{RRHL?<%%BI`6{c}zE5(gWHJX78CdQgq z6x;@H2=YemujpQ6C=p~{SMY4y6FY$R(nE+qv%KKZIOEFlM%k~>xAZU~)V!&nTR`2) zsz$}H=(ot1#CY?Tf|hZOE9)DXU)gVw;Y6@Gx#0P@ww0V){2Fo}J%Wh%5NwVBo}K|t ztAw`{HE1SyRheYoUf>wuwkqnDwua24-H6HNtb#!S$SV0QuD%m^1thGhzNOWZ$_|BB zK=i8eTOvK1MSBn;v$`N4AY~QjHs3&IBRnF;Tu=}akh-e=7SnDSNz5=87K8<)uj1X7 z8K7+1lbB^LFPLcuA_JR^coT7EZNdD2^Q%<1MMfxx_69FVM+)M=8D?J+D*Wl zn44NE=Q|$*Gn7vYiDl+%1!n`& zlX!PzW;7oeODs44Rd6vtnIycUFr%7Ikdnl?!#9%!bO5p1d>_0)zOi%x`AJcCv}RI6 z2NCPcj|*x8Dw6O!HZxR62NP?|wt}kx=aW=-L>5+q1QYAcPYYTC8k5rQ@GWE^9YSm{ zzbJSf(Dp`Ah=dXw%`XdH1vsoOzaz6iMO_q}yL>BIOow$^+5uaHOdz(HyK8y}4qhE~ zS8F9p=x}1IxySopySWc|lMPxezROtI5+s7yZ60U`X?OXxWGNj9a@kxoBLbsWm*17u zLS=LWu?Hx80~f8XzsuCJWgUuuz?9XTdx~1L44FiHYaXtN3|zXp$*w>q6PadrO-x|f z>ejp5-_ZTY6hdis)l3Ogt`^>t{RSPNrxGf&pJpLYi0?7Ku?N5!v>fo7K0omMYSlea z9b1k}BXZ57HCR`mF$Zfl20E0dKf#GYS?rVQ157V=Va`QAz zNnrGv^82z&Pz4=J95T=93dF<_^8(G;cZ#3`y1jSI6oO0 z6fRfUL@fFvIF$Ld3t%Y_{`aeSpT;97R$gV}vM7aMgu$!=0fI~AO*R_~>43syF3^Mo zrOKUKL=7ZO6NJ@Vs+kg$FK@MR8&Dmxn5Z)sYa)Y`b_J_LmJpZB`!z8^W%8&NZ3B6f zUINa7w3_)r=jEyv(Pg#@Sw>tnAJKr-dR@UO(3DaquWC_TMt?$<6W7gEnw3G#@}?Hs zTkwl!N08H6;RD4L^tfH|9!MhoGN04z4jQ~R>VftOd5m63+%g+9WKhssOA>L%Y}I52 z`K-ks*see)=+(r1bG;^~{eY<}&;*6AO?$w< zTHZt1HRv>*Ogu8*(wKtE)`}m3W2|<|Q}cZd8+3B5@S)-wdKyU~{xScpx!s{)Pa_+M z=jNxHOF{Hn`9tn?^bE3*XalEbSA)*4RXr44XU`y;h*#zZnj1mJwdD_G*P);3O~ilp z8g*S2FEy`%9M(Di-KqFb(=E8~I^N%k>*&wOX5zK^lfs_C&1;(;GS}Ikku5|wOOHaw zV7GPRziro{U+67FcMGSmf3OdzA^MB`1=&XQv~UZD28XXx{XgIabe7&ma4h`_M*s`{ zx9uXq8@2)k{9W8VxYwv z93Gxrm-dL?NLJfx_;m#t#O5z7aN*+M%j>EiX&cdMB!d`i5fml{H?FIH#5A(MBKrUs zSGXql$-34@ibnKTY13=UeK_LzU0G|)MO2RQg_2lB_< z+o%D_A$%+|3J(OwuCIEmxQ!Z-T*AvTyRZ@jeU?9#-G+>`3Ml3l9t}=f-};!_gfa+8 z@GbF$$AeSWbDr>WQM~Qys$R7Vm&u_W?w}?lpBQUdQFuGJZhh4g#U0f05dbyD zSymO^d#5NO##^=&J_>GL-}JyU%QbW3qzWQa0Z_)L3` ztfLPSGc2WrQ$q5So1WV4LBG?7h*_5M!kHlz$@sJPKx(q{GtqtYcjPb;YdKUHXRo2S z&;E{lN5omQh4VwolEu%M`|KsKs}!~9063qldM0XS>yabGe9NiAjUmS5@@KMU=nwh` zu>dF%LmHFopE1qs9|+iMY^f?-8Pc5G^vu=_{b^UUTO3lHTSYeZPx^b}YsbQR+kb74tHbV_-vtmR{H*{)y;H6h_C zs#Z}8+WE zq*(qfeD!{uV_T5(+1(cioekt;DTbOrB~6Q(4Y-z|MDM^H|Yz+ z9*c9)n9!vgn*Oytf^N|l35Dg0BLC2&4XyujAEUPrJ)yLCb^@}I{+0OFGQ21?$E&-qh4sAb^&c0I4`(Q(Yr`3QD&K3lmjkX-^LxVP^z2HA1@6(q++Hic4CNzAb>V@bTdmpJMDlPMhPIrP1#qrQZ z8|z;%&)8<H<43C5x>3K*Z#p+N2u7E5taKP*2KEgWy$RQ?t;SgMLvj&I&n^-9r({)0Rqu3AnNZ5;2gx&9T?#{Pr+ zO%^f~f` zxMQ&vWsG;*Eba;pjE@C?dC5LUo)Y&gzZX@GM>fm<|75SA zHoBE~0BW>bs{T{FLfepkIxKGh`vT-u+bj)5$H%8^=DgSzaxw$y)O>elcE3@Hwokh;aWTSm8d2f?S(e%4{dVH47~@VX^-Z*UOwlJ>W{7spH} z+am7f*gd|7ptsb?>RB8&f!-qT=G$G}L)Tk6(CS@`PB^(Gt(&id9keTEPN)DDQ}>3R z0S>KA&Y0k~ zRovawqoKE8pw!Dcy8~qOi0=)qfJa(q6d#xnyS3`Ag=d{sTrwegYkBvU9xi=!1Et

    #2Jhr2T*ulhUaO_a96HabTbMXCC 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
    yetnhlu;^^b3T+V4(V1btQFZ9m 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

    lKb~0MGlJ6#s4JrK!Yx#YplSAsV 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%0Abh6uTHEeQA{O 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 Date: Tue, 15 Mar 2022 14:05:56 +0000 Subject: [PATCH 72/72] Skip files if no .columns or parquet-reader in 00900_long_parquet_load (cherry picked from commit 120dc30bfdf980fe6017316844df0d52902ec944) --- tests/queries/0_stateless/00900_long_parquet_load.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/queries/0_stateless/00900_long_parquet_load.sh b/tests/queries/0_stateless/00900_long_parquet_load.sh index e47185261fb6..8e6ea24edb49 100755 --- a/tests/queries/0_stateless/00900_long_parquet_load.sh +++ b/tests/queries/0_stateless/00900_long_parquet_load.sh @@ -40,11 +40,13 @@ DATA_DIR=$CUR_DIR/data_parquet # Code: 349. DB::Ex---tion: Can not insert NULL data into non-nullable column "phoneNumbers": data for INSERT was parsed from stdin for NAME in $(find "$DATA_DIR"/*.parquet -print0 | xargs -0 -n 1 basename | LC_ALL=C sort); do - echo === Try load data from "$NAME" - JSON=$DATA_DIR/$NAME.json COLUMNS_FILE=$DATA_DIR/$NAME.columns + ([ -z "$PARQUET_READER" ] || [ ! -s "$PARQUET_READER" ]) && [ ! -s "$COLUMNS_FILE" ] && continue + + echo === Try load data from "$NAME" + # If you want change or add .parquet file - rm data_parquet/*.json data_parquet/*.columns [ -n "$PARQUET_READER" ] && [ ! -s "$COLUMNS_FILE" ] && [ ! -s "$JSON" ] && "$PARQUET_READER" --json "$DATA_DIR"/"$NAME" > "$JSON" [ ! -s "$COLUMNS_FILE" ] && "$CUR_DIR"/helpers/00900_parquet_create_table_columns.py "$JSON" > "$COLUMNS_FILE"