Skip to content

Commit

Permalink
Fix parsing trailing non-alphanumerics in mssql parameters (#72)
Browse files Browse the repository at this point in the history
  • Loading branch information
azmy60 authored Oct 4, 2023
1 parent 03c91c2 commit 58e526d
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 10 deletions.
11 changes: 5 additions & 6 deletions src/tokenizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,12 +283,7 @@ function scanParameter(state: State, dialect: Dialect): Token {
}

if (dialect === 'mssql') {
let nextChar: Char;
do {
nextChar = read(state);
} while (!isWhitespace(nextChar) && nextChar !== null);

if (isWhitespace(nextChar)) unread(state);
while(isAlphaNumeric(peek(state))) read(state);

Check failure on line 286 in src/tokenizer.ts

View workflow job for this annotation

GitHub Actions / build (10.x)

Insert `·`

Check failure on line 286 in src/tokenizer.ts

View workflow job for this annotation

GitHub Actions / build (12.x)

Insert `·`

Check failure on line 286 in src/tokenizer.ts

View workflow job for this annotation

GitHub Actions / build (14.x)

Insert `·`

Check failure on line 286 in src/tokenizer.ts

View workflow job for this annotation

GitHub Actions / build (16.x)

Insert `·`

const value = state.input.slice(state.start, state.position + 1);
return {
Expand Down Expand Up @@ -409,6 +404,10 @@ function isWhitespace(ch: Char): boolean {
return ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r';
}

function isAlphaNumeric(ch: Char): boolean {
return ch !== null && /\w/.test(ch);
}

function isString(ch: Char, dialect: Dialect): boolean {
const stringStart: Char[] = dialect === 'mysql' ? ["'", '"'] : ["'"];
return stringStart.includes(ch);
Expand Down
27 changes: 23 additions & 4 deletions test/identifier/single-statement.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1368,18 +1368,18 @@ describe('identifier', () => {
});

it('Should extract positional Parameters with trailing commas', () => {
const actual = identify('SELECT $1,$2 FROM foo', {
const actual = identify('SELECT $1,$2 FROM foo WHERE foo.id in ($3, $4)', {
dialect: 'psql',
strict: true,
});
const expected = [
{
start: 0,
end: 20,
text: 'SELECT $1,$2 FROM foo',
end: 45,
text: 'SELECT $1,$2 FROM foo WHERE foo.id in ($3, $4)',
type: 'SELECT',
executionType: 'LISTING',
parameters: ['$1', '$2'],
parameters: ['$1', '$2', '$3', '$4'],
},
];

Expand All @@ -1405,6 +1405,25 @@ describe('identifier', () => {
expect(actual).to.eql(expected);
});

it('Should extract named Parameters with trailing commas', () => {
const actual = identify('SELECT * FROM Persons where x in (:one, :two, :three)', {
dialect: 'mssql',
strict: true,
});
const expected = [
{
start: 0,
end: 52,
text: 'SELECT * FROM Persons where x in (:one, :two, :three)',
type: 'SELECT',
executionType: 'LISTING',
parameters: [':one', ':two', ':three'],
},
];

expect(actual).to.eql(expected);
});

it('Should extract question mark Parameters', () => {
const actual = identify('SELECT * FROM Persons where x = ? and y = ? and a = ?', {
dialect: 'mysql',
Expand Down
24 changes: 24 additions & 0 deletions test/tokenizer/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { expect } from 'chai';
import { scanToken } from '../../src/tokenizer';
import type { Dialect } from '../../src/defines';
import { parse } from '../../src/parser';

Check warning on line 4 in test/tokenizer/index.spec.ts

View workflow job for this annotation

GitHub Actions / build (10.x)

'parse' is defined but never used

Check warning on line 4 in test/tokenizer/index.spec.ts

View workflow job for this annotation

GitHub Actions / build (12.x)

'parse' is defined but never used

Check warning on line 4 in test/tokenizer/index.spec.ts

View workflow job for this annotation

GitHub Actions / build (14.x)

'parse' is defined but never used

Check warning on line 4 in test/tokenizer/index.spec.ts

View workflow job for this annotation

GitHub Actions / build (16.x)

'parse' is defined but never used

describe('scan', () => {
const initState = (input: string) => ({
Expand Down Expand Up @@ -353,6 +354,29 @@ describe('scan', () => {
};
expect(actual).to.eql(expected);
});

it('should not include trailing non-alphanumerics for mssql', () => {
[
{
actual: scanToken(initState(':one,'), 'mssql'),
expected: {
type: 'parameter',
value: ':one',
start: 0,
end: 3,
},
},
{
actual: scanToken(initState(':two)'), 'mssql'),
expected: {
type: 'parameter',
value: ':two',
start: 0,
end: 3,
},
},
].forEach(({ actual, expected }) => expect(actual).to.eql(expected));
});
});
});
});

0 comments on commit 58e526d

Please sign in to comment.