Skip to content

fix(main): reject setting values that would wrap, and map threads=0 - #1453

Open
555aaditya wants to merge 1 commit into
LadybugDB:mainfrom
555aaditya:fix/settings-validate-ranges
Open

555aaditya wants to merge 1 commit into
LadybugDB:mainfrom
555aaditya:fix/settings-validate-ranges

Conversation

@555aaditya

Copy link
Copy Markdown
Contributor

This work was produced with the help of language models.

Description

Bug 1: CALL threads=0 hangs the next parallel query.

CALL threads=0;
UNWIND range(1, 100000) AS x RETURN sum(x);   -- main: never returns (I killed the shell after 15 s)

ThreadsSetting::setContext stored 0 as the connection's thread count. ClientContext::setMaxNumThreadForExec(0), the C++ API equivalent, already maps 0 to the database's maxNumThreads. The setting now does the same.

Bug 2: four INT64 settings silently wrap. These settings take an INT64 but store it unsigned, two of them as 32 bits:

CALL … then current_setting(…) main this PR
checkpoint_threshold=-1 18446744073709551615 Runtime exception: checkpoint_threshold must be >= 0, got -1.
var_length_extend_max_depth=-1 4294967295 … must be >= 0, got -1.
var_length_extend_max_depth=4294967296 truncated to 32 bits (from the code: uint32_t field; not run) … must be <= 4294967295, got 4294967296.
recursive_pattern_factor=-5 4294967291 … must be >= 0, got -5.
sparse_frontier_threshold=-1 wraps the same way (from the code: uint64_t field; not run) … must be >= 0, got -1.

checkpoint_threshold=-1 effectively turns auto-checkpoint off without saying so.

Fix. One helper in settings.cpp, toUnsignedSetting<T>, rejects a value below 0 or above T's maximum. The wording matches the existing physical_plan_pool_per_thread must be >= 0, got N. check. The upper-bound check is compiled only for 32-bit fields (if constexpr), so GCC's -Wtype-limits doesn't see an always-false comparison. Valid values, including 0 (used by checkpoint_threshold=0 in several tests), behave as before. A rejected value leaves the previous setting in place.

I didn't find an existing issue for this.

Tests. New test/test_files/function/call/setting_ranges.test:

  • ThreadsZeroUsesDatabaseThreadCount: CALL threads=0, then a parallel UNWIND … sum(x). On main this case hangs instead of failing.
  • UnsignedSettingsRejectValuesThatWrap: the five rejections above, plus valid values still being applied and read back. On main the rejection statements succeed (Which is: "" against each expected error).

Types of changes

  • Bug fix
  • New feature
  • Breaking change
  • Documentation Update

Checklist

  • I have changed storage version if on disk format has changed. (No on-disk format change.)
  • I have requested a review from a maintainer.
  • I have updated the documentation (if needed). (Not needed.)

Validation

macOS 27.0 arm64, Apple clang 21.0.0, CMake 4.4.4, Ninja 1.13.2, release build (make test-build-release with OpenSSL 3).

  • e2e_test function/call/setting_ranges.test: 2/2 passed. On main, UnsignedSettingsRejectValuesThatWrap fails (each rejection succeeds), and ThreadsZeroUsesDatabaseThreadCount hangs.
  • Shell on main: checkpoint_threshold=-1 → 18446744073709551615, var_length_extend_max_depth=-1 → 4294967295, recursive_pattern_factor=-5 → 4294967291. CALL threads=0; UNWIND range(1,100000) AS x RETURN sum(x) produced no output until I killed it after 15 s (exit 142, SIGALRM).
  • e2e_test --gtest_filter="function~call~*:transaction~*:recursive_join~*:function~gds~*": 569/569 passed.
  • ctest --test-dir build/release/test -j10: 3228/3229 passed. The one failure, dictionary_bug~orb383_relationship_projection_obfuscated.AnonymousParquetDeleteReload, needs a parquet fixture that CI generates (scripts/generate-dictionary-bug-fixture.py, gitignored) and I didn't generate locally. It is unrelated.
  • clang-format 18.1.8 (Homebrew llvm@18) --dry-run --Werror on settings.cpp: clean. I did not run scripts/run-clang-format-docker.sh.
  • Based on origin/main @ 9e29cf5ef.
  • Not run: Linux/GCC (hence the if constexpr guard, written for -Wtype-limits but not compiled with GCC here), sanitizers.

Note: #1435 edits the EnableMVCCSetting functions next to RecursivePatternFactorSetting in settings.cpp, so whichever lands second may need a trivial rebase. The changes don't interact.

`CALL threads=0` stored 0 threads, after which the next parallel query
never finished. Connection::setMaxNumThreadForExec already treats 0 as
the database's thread count; the setting now does the same.

checkpoint_threshold, sparse_frontier_threshold,
var_length_extend_max_depth and recursive_pattern_factor take INT64
but are stored unsigned (the last two as 32 bits), so negative or
oversized values wrapped: checkpoint_threshold=-1 read back as
18446744073709551615 (silently disabling auto-checkpoint) and
var_length_extend_max_depth=-1 as 4294967295. Reject them with the
same "must be >= 0" error physical_plan_pool_per_thread uses.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant