Skip to content

Substring: more doc, more examples - #6

Open
k5e wants to merge 1 commit into
AlaSQL:masterfrom
k5e:substring
Open

Substring: more doc, more examples#6
k5e wants to merge 1 commit into
AlaSQL:masterfrom
k5e:substring

Conversation

@k5e

@k5e k5e commented May 27, 2026

Copy link
Copy Markdown

Hi!
I found that the SUBSTRING documentation was wrong and I made a fix and added some more examples.
HTH.

@mathiasrw

Copy link
Copy Markdown
Member

Oh. This is odd.

Hmm. i think we need to lok deeper into this

+++++++++++++++++++

Bug: SUBSTRING() incorrectly evaluates 0 and negative start indices

Description

The current implementation of the SUBSTRING() function evaluates 0 and negative start indices incorrectly compared to both standard ANSI SQL and popular dialects like MySQL.

This was highlighted during a documentation PR review, which documented the current behavior where 0 acts as the last character of the string.

Environment Details

To help us debug, please provide:

  • AlaSQL Version: (e.g., is this happening in a newer release or does it exist in versions earlier than early AlaSQL releases?)
  • Compatibility Options Selected: (e.g., have you set any specific dialects in alasql.options, like alasql.options.mysql = true or alasql.options.postgres = true?)

Current Behavior

Currently, AlaSQL counts backwards from the right, but incorrectly treats 0 as the last character:
```sql
SELECT SUBSTRING('abcd', 0); -- Returns 'd'
SELECT SUBSTRING('abcd', -1); -- Returns 'cd'
```

Expected Behavior

Depending on which SQL dialect compatibility option AlaSQL is currently targeting, the behavior should be:

Option A: ANSI SQL Standard (e.g., PostgreSQL, SQL Server)
Indices are treated mathematically on a number line. 0 and negative numbers represent logical positions before the start of the 1-indexed string.

  • SELECT SUBSTRING('abcd', 0, 2); -> Starts at index 0 (empty), takes 2 characters (index 0 and index 1). Returns 'a'.

Option B: MySQL / SQLite Standard (Negative Wrap-around)
Negative numbers count backwards from the end of the string, where -1 is the last character. 0 generally returns an empty string.

  • SELECT SUBSTRING('abcd', 0); -> Returns '' (empty string).
  • SELECT SUBSTRING('abcd', -1); -> Returns 'd' (last character).

Root Cause Hypothesis

Because AlaSQL maps SQL's 1-based indexing to JavaScript's 0-based methods (like String.prototype.slice()), there appears to be an off-by-one mapping error when evaluating non-positive integers.
If the JS index calculation for backwards wrapping is implemented similarly to string.length + start - 1, then:

  • Passing 0 results in length - 1 (the JS index of the last character).
  • Passing -1 results in length - 2 (the JS index of the second-to-last character).

Proposed Action Items

  • Investigate how the SUBSTRING logic handles different alasql.options compatibility flags.
  • Refactor the SUBSTRING logic in the JavaScript source to map start <= 0 correctly based on the active compatibility standard.
  • Add unit tests covering 0, negative indices, and out-of-bounds start/length combinations across different compatibility modes.
  • Update the SUBSTRING.md documentation to reflect the correct behavior based on the chosen dialect.

@k5e

k5e commented Aug 21, 2026

Copy link
Copy Markdown
Author

Let me know if I can help. I could at least take the suggested last step. After all, I did it once.

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.

2 participants