Skip to content

Commit

Permalink
fix: add tests for SQL fragment generation
Browse files Browse the repository at this point in the history
  • Loading branch information
gajus committed Feb 11, 2019
1 parent f430245 commit c93251b
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 21 deletions.
34 changes: 23 additions & 11 deletions src/templateTags/createRawSqlSqlFragment.js
Expand Up @@ -4,28 +4,40 @@ import type {
RawSqlTokenType,
SqlFragmentType
} from '../types';
import {
UnexpectedStateError
} from '../errors';

export default (token: RawSqlTokenType, greatestParameterPosition: number): SqlFragmentType => {
let sql = '';

const parameters = [];
let leastMatchedParameterPosition = Infinity;
let greatestMatchedParameterPosition = 0;

if (Array.isArray(token.values) && token.values.length) {
const fragmentValues = token.values;
sql += token.sql.replace(/\$(\d+)/g, (match, g1) => {
const parameterPosition = parseInt(g1, 10);

sql += token.sql.replace(/\$(\d+)/, (match, g1) => {
return '$' + (parseInt(g1, 10) + greatestParameterPosition);
});
if (parameterPosition > greatestMatchedParameterPosition) {
greatestMatchedParameterPosition = parameterPosition;
}

for (const fragmentValue of fragmentValues) {
parameters.push(fragmentValue);
if (parameterPosition < leastMatchedParameterPosition) {
leastMatchedParameterPosition = parameterPosition;
}
} else {
sql += token.sql;

return '$' + (parameterPosition + greatestParameterPosition);
});

if (greatestMatchedParameterPosition > token.values.length) {
throw new UnexpectedStateError('The greatest parameter position is greater than the number of parameter values.');
}

if (leastMatchedParameterPosition !== Infinity && leastMatchedParameterPosition !== 1) {
throw new UnexpectedStateError('Parameter position must start at 1.');
}

return {
parameters,
parameters: token.values,
sql
};
};
Expand Up @@ -4,13 +4,20 @@ import type {
SqlFragmentType,
TupleSqlTokenType
} from '../types';
import {
UnexpectedStateError
} from '../errors';

export default (token: TupleSqlTokenType, greatestParameterPosition: number): SqlFragmentType => {
const parameters = [];
const placeholders = [];

let placeholderIndex = greatestParameterPosition;

if (token.values.length === 0) {
throw new UnexpectedStateError('Tuple must have values.');
}

for (const tupleValue of token.values) {
placeholders.push('$' + ++placeholderIndex);

Expand Down
File renamed without changes.
20 changes: 10 additions & 10 deletions src/templateTags/sql.js
Expand Up @@ -15,11 +15,11 @@ import type {
import isPrimitiveValueExpression from '../utilities/isPrimitiveValueExpression';
import Logger from '../Logger';
import createRawSqlSqlFragment from './createRawSqlSqlFragment';
import careateIdentifierSqlFragment from './careateIdentifierSqlFragment';
import careateValueListSqlFragment from './careateValueListSqlFragment';
import careateTupleSqlFragment from './careateTupleSqlFragment';
import careateTupleListSqlFragment from './careateTupleListSqlFragment';
import careateUnnestSqlFragment from './careateUnnestSqlFragment';
import createIdentifierSqlFragment from './createIdentifierSqlFragment';
import createValueListSqlFragment from './createValueListSqlFragment';
import createTupleSqlFragment from './createTupleSqlFragment';
import createTupleListSqlFragment from './createTupleListSqlFragment';
import createUnnestSqlFragment from './createUnnestSqlFragment';

const log = Logger.child({
namespace: 'sql'
Expand Down Expand Up @@ -56,15 +56,15 @@ const sql = (
} else if (token && token.type === 'RAW_SQL') {
appendSqlFragment(createRawSqlSqlFragment(token, parameters.length));
} else if (token && token.type === 'IDENTIFIER') {
appendSqlFragment(careateIdentifierSqlFragment(token));
appendSqlFragment(createIdentifierSqlFragment(token));
} else if (token && token.type === 'VALUE_LIST') {
appendSqlFragment(careateValueListSqlFragment(token, parameters.length));
appendSqlFragment(createValueListSqlFragment(token, parameters.length));
} else if (token && token.type === 'TUPLE') {
appendSqlFragment(careateTupleSqlFragment(token, parameters.length));
appendSqlFragment(createTupleSqlFragment(token, parameters.length));
} else if (token && token.type === 'TUPLE_LIST') {
appendSqlFragment(careateTupleListSqlFragment(token, parameters.length));
appendSqlFragment(createTupleListSqlFragment(token, parameters.length));
} else if (token && token.type === 'UNNEST') {
appendSqlFragment(careateUnnestSqlFragment(token, parameters.length));
appendSqlFragment(createUnnestSqlFragment(token, parameters.length));
} else {
log.error({
constructedSql: rawSql,
Expand Down
53 changes: 53 additions & 0 deletions test/slonik/templateTags/createRawSqlSqlFragment.js
@@ -0,0 +1,53 @@
// @flow

import test from 'ava';
import createRawSqlSqlFragment from '../../../src/templateTags/createRawSqlSqlFragment';

test('creates a tuple with a single parameter', (t) => {
const sqlFragment = createRawSqlSqlFragment({
sql: 'foo',
type: 'RAW_SQL',
values: []
}, 0);

t.true(sqlFragment.sql === 'foo');
t.deepEqual(sqlFragment.parameters, []);
});

test('offsets parameter position', (t) => {
const sqlFragment = createRawSqlSqlFragment({
sql: '($1)',
type: 'RAW_SQL',
values: [
'foo'
]
}, 1);

t.true(sqlFragment.sql === '($2)');
t.deepEqual(sqlFragment.parameters, ['foo']);
});

test('throws an erorr if the greatest parameter position is greater than the number of parameter values', (t) => {
t.throws(() => {
createRawSqlSqlFragment({
sql: '($1, $2)',
type: 'RAW_SQL',
values: [
'foo'
]
}, 0);
}, 'The greatest parameter position is greater than the number of parameter values.');
});

test('throws an erorr if least parameter is greater than 1', (t) => {
t.throws(() => {
createRawSqlSqlFragment({
sql: '($2)',
type: 'RAW_SQL',
values: [
'foo',
'bar'
]
}, 0);
}, 'Parameter position must start at 1.');
});
43 changes: 43 additions & 0 deletions test/slonik/templateTags/createTupleSqlFragment.js
@@ -0,0 +1,43 @@
// @flow

import test from 'ava';
import createTupleSqlFragment from '../../../src/templateTags/createTupleSqlFragment';

test('creates a tuple with a single parameter', (t) => {
const sqlFragment = createTupleSqlFragment({
type: 'TUPLE',
values: ['foo']
}, 0);

t.true(sqlFragment.sql === '($1)');
t.deepEqual(sqlFragment.parameters, ['foo']);
});

test('creates a tuple multiple parameters', (t) => {
const sqlFragment = createTupleSqlFragment({
type: 'TUPLE',
values: ['foo', 'bar', 'baz']
}, 0);

t.true(sqlFragment.sql === '($1, $2, $3)');
t.deepEqual(sqlFragment.parameters, ['foo', 'bar', 'baz']);
});

test('offsets parameter', (t) => {
const sqlFragment = createTupleSqlFragment({
type: 'TUPLE',
values: ['foo']
}, 1);

t.true(sqlFragment.sql === '($2)');
t.deepEqual(sqlFragment.parameters, ['foo']);
});

test('throws an error if tuple is empty', (t) => {
t.throws(() => {
createTupleSqlFragment({
type: 'TUPLE',
values: []
}, 1);
}, 'Tuple must have values.');
});

0 comments on commit c93251b

Please sign in to comment.