Skip to content

fix: add termination guards to nanoid_optimized()#33

Merged
nik-sta merged 2 commits into
mainfrom
fix/32-nanoid-optimized-termination-guards
Jul 20, 2026
Merged

fix: add termination guards to nanoid_optimized()#33
nik-sta merged 2 commits into
mainfrom
fix/32-nanoid-optimized-termination-guards

Conversation

@nik-sta

@nik-sta nik-sta commented Jul 14, 2026

Copy link
Copy Markdown
Member

Closes #32

What this PR does

Adds minimal termination guards to nanoid_optimized(). The only exit of its generation loop is reached after a character has been appended, so a direct call with a non-positive size, an empty or NULL alphabet, or a NULL or non-positive mask or step never terminates: it burns a connection and CPU until the backend is killed. These inputs now raise a clear exception up front instead.

The "no checks" performance contract stays intact:

  • Only the four scalar arguments are guarded (three IFs before the loop, nothing in the per-byte path).
  • The mask is still not validated against the alphabet, the alphabet length is not range-checked, and no step/size cross-checks are added. nanoid_optimized() remains the root function for callers who know what they are doing.
  • With the guards in place every remaining input terminates, since index 1 is accepted with a probability of at least 1/256 per byte.

Also corrects the mask parameter description in the README and nanoid.sql: the mask is (2^k) - 1 where 2^k is the smallest power of two greater than or equal to the alphabet size (the old wording called n itself "a power of 2"), and updates the nanoid_optimized() warning wording to reflect the guards.

Same fix as shipped in the MySQL/MariaDB port (viascom/nanoid-mysql-mariadb#6).

Verification

  • dev/test/run_tests.sh (PostgreSQL 9.6 through 18, official Docker images): all versions PASS, including the new regression test that asserts the guard fires for nanoid_optimized(0, ...).
  • gitleaks over the branch: no leaks.

Summary by CodeRabbit

  • Bug Fixes

    • Prevented nanoid_optimized() from entering infinite loops when given invalid generation parameters.
    • Added validation for size, alphabet, mask, and step values, with clear exceptions for invalid inputs.
    • Added regression coverage for termination behavior.
  • Documentation

    • Clarified validation limits and the expected mask calculation for nanoid_optimized().

@nik-sta nik-sta added the bug Something isn't working label Jul 14, 2026
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 24 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0deeb534-4364-44f4-becc-8ba5e753fae5

📥 Commits

Reviewing files that changed from the base of the PR and between e56ecfb and 7d2216a.

📒 Files selected for processing (1)
  • nanoid.sql
📝 Walkthrough

Walkthrough

nanoid_optimized() now validates termination-related inputs, raises exceptions for invalid values, and has regression coverage. README and SQL documentation describe the guards and corrected mask formula.

Changes

Optimized NanoID termination guards

Layer / File(s) Summary
Termination guards and regression coverage
nanoid.sql, dev/test/regression_tests.sql
nanoid_optimized() rejects invalid size, alphabet, mask, and step values; regression coverage verifies that invalid input raises an exception.
Documentation alignment
nanoid.sql, README.md
Documentation describes the limited validation behavior and defines the mask using the smallest power of two greater than or equal to the alphabet size.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related issues

  • Issue 32 — Directly addressed by the termination guards, documentation corrections, and regression test.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding termination guards to nanoid_optimized().
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/32-nanoid-optimized-termination-guards

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@nanoid.sql`:
- Around line 105-118: Update the alphabet validation in the nanoid guard to
align with the minimal checks it performs: either remove the unsupported “bigger
than 255 symbols” claim from the exception message, or add the corresponding
upper-bound condition if that constraint is required. Preserve the existing null
and empty-alphabet validation and the documented termination-guard scope.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 607fea14-c7b3-4f7e-90cb-cad30dce1219

📥 Commits

Reviewing files that changed from the base of the PR and between 131cc7a and e56ecfb.

📒 Files selected for processing (3)
  • README.md
  • dev/test/regression_tests.sql
  • nanoid.sql

Comment thread nanoid.sql
…check #32

The alphabet termination guard message claimed the alphabet could not be
"bigger than 255 symbols", but the guard only checks for NULL or empty
alphabet (a >255 alphabet does not cause the generation loop to spin, so
it is out of the termination-guard scope). Removed the stale upper-bound
claim so the message matches the condition.
@nik-sta
nik-sta merged commit 032d53c into main Jul 20, 2026
1 check passed
@nik-sta
nik-sta deleted the fix/32-nanoid-optimized-termination-guards branch July 20, 2026 18:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Guard nanoid_optimized() against inputs that make the generation loop spin forever

1 participant