Fix SUBSTRING with a non-positive start position - #2521
Conversation
SUBSTRING/SUBSTR/MID compiled 'SUBSTRING(str, pos, len)' to
str.substr(pos - 1, len). When pos was 0 or negative, pos - 1 became a
negative index and JavaScript's substr() counts that from the end of the
string, so 'SUBSTRING("abcdef", 0, 3)' returned 'f' and
'SUBSTRING("abcdef", -1, 3)' returned 'ef'.
Follow MySQL semantics instead: a start of 0 yields an empty string, and
a negative start counts from the end (clamped to empty when it reaches
past the beginning). Positive start positions are unchanged.
mathiasrw
left a comment
There was a problem hiding this comment.
I really love that you fix something like this.
This will be a breaking change and will be included in the next major version update.
| '(__alasql_tmp=(' + | ||
| b + | ||
| '),__alasql_tmp==0?"":(__alasql_tmp<0?(y.length+__alasql_tmp<0?"":y.substr(y.length+__alasql_tmp)):y.substr(__alasql_tmp-1)))' |
There was a problem hiding this comment.
I feel like we are introducing complexity by writing it like this. Are you able to make it a bit easier to understand what is happening?
|
Thanks, Mathias, glad it's useful! Agreed it's a breaking change, so holding it for the next major sounds right. I can rebase whenever that milestone opens, and if a non-breaking version would help sooner, the new behavior could sit behind an opt-in flag. Just let me know what works best. |
|
I think an option flag would work well, but then again we need to keep the optionflag alive. I hope to release a major version in september. Lets keep it like this for now. Please have a look at the code comment. |
Problem
SUBSTRING/SUBSTR/MIDcompileSUBSTRING(str, pos, len)tostr.substr(pos - 1, len). Whenposis0or negative,pos - 1becomes a negative index, and JavaScript'sString.prototype.substr()counts a negative start from the end of the string. The result is neither correct nor consistent with any SQL dialect:So a computed start position that lands on
0or goes negative silently returns wrong characters.Fix
Follow MySQL semantics (verified against MySQL 8), which alasql already matches for positive positions:
= 0yields an empty string;SUBSTRING('abcdef', -2)->'ef'), clamped to an empty string when it reaches past the beginning (SUBSTRING('abcdef', -7)->'');SUBSTRING('abcdef', 0, 3)'f'''SUBSTRING('abcdef', -1, 3)'ef''f'SUBSTRING('abcdef', -2, 3)'def''ef'SUBSTRING('abcdef', -2)'def''ef'SUBSTRING('abcdef', -7, 3)'bcd'''SUBSTRING('abcdef', 2, 3)'bcd''bcd'Test
Added
test/test-substring-negative-start.js.yarn testpasses (2193 tests).