Skip to content

fix: substring window is wrong for start positions below 1 - #480

Open
spokodev wants to merge 1 commit into
oguimbal:masterfrom
spokodev:fix/substring-start-position
Open

fix: substring window is wrong for start positions below 1#480
spokodev wants to merge 1 commit into
oguimbal:masterfrom
spokodev:fix/substring-start-position

Conversation

@spokodev

@spokodev spokodev commented Aug 3, 2026

Copy link
Copy Markdown

Problem

substring(str from S for L) selects the window [S, S+L) clipped to valid (>= 1) positions. When S is below 1, pg-mem applies the length from the clamped start (position 1) instead of the original S, so it returns too many characters:

select substring('012345678' from 0 for 3);   -- pg-mem: '012', postgres: '01'
select substring('012345678' from -1 for 3);  -- pg-mem: '012', postgres: '0'
select substr('012345678', 0, 3);              -- pg-mem: '012', postgres: '01'

The same sqlSubstring helper backs the substring/substr functions and the substring(... from ... for ...) form.

Ref: Postgres string functions

Cause

sqlSubstring clamped the start to 0 but still passed the full length to substr(start, len), so the window's end shifted right by the amount that was clamped off the front.

Fix

Compute the window end from the original start (end = from + len), clamp only the start, and return an empty string when the window ends at or before the start. Also default an omitted FROM to position 1 (so substring(x for n) is unchanged) and update overlay(), which passed from = 0 relying on the old clamp. Tests added in simple-queries.spec.ts.

substring(str from S for L) selects the window [S, S+L) clipped to valid
positions. When S was below 1 the length was measured from the clamped
start instead of the original one, so too many characters were returned:

  substring('012345678' from 0 for 3);   -- was '012', postgres '01'
  substring('012345678' from -1 for 3);  -- was '012', postgres '0'
  substr('012345678', 0, 3);             -- was '012', postgres '01'

Also default an omitted FROM to position 1 (so 'substring(x for n)' is
unchanged) and update overlay(), which relied on the old clamped call.
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