Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: interpolate SQL tokens in sql.raw
- Loading branch information
Showing
20 changed files
with
268 additions
and
192 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// @flow | ||
|
||
import { | ||
UnexpectedStateError | ||
} from '../errors'; | ||
import type { | ||
PrimitiveValueExpressionType | ||
} from '../types'; | ||
|
||
export default (values: $ReadOnlyArray<*>): $ReadOnlyArray<PrimitiveValueExpressionType> => { | ||
const primitiveValueExpressions = []; | ||
|
||
for (const value of values) { | ||
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' || value === null) { | ||
primitiveValueExpressions.push(value); | ||
} else { | ||
throw new UnexpectedStateError('Unexpected value expression.'); | ||
} | ||
} | ||
|
||
return primitiveValueExpressions; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
// @flow | ||
|
||
import { | ||
difference | ||
} from 'lodash'; | ||
import Logger from '../Logger'; | ||
import { | ||
UnexpectedStateError | ||
} from '../errors'; | ||
import type { | ||
NamedParameterValuesType | ||
} from '../types'; | ||
import interpolatePositionalParameterReferences from './interpolatePositionalParameterReferences'; | ||
|
||
const log = Logger.child({ | ||
namespace: 'interpolateNamedParameterReferences' | ||
}); | ||
|
||
/** | ||
* @see https://regex101.com/r/KrEe8i/2 | ||
*/ | ||
const namedPlaceholderRegex = /[\s,(]:([a-z_]+)/g; | ||
|
||
/** | ||
* @see https://github.com/mysqljs/sqlstring/blob/f946198800a8d7f198fcf98d8bb80620595d01ec/lib/SqlString.js#L73 | ||
*/ | ||
export default ( | ||
inputSql: string, | ||
inputValues: NamedParameterValuesType = {}, | ||
greatestParameterPosition: number | ||
) => { | ||
const resultValues = []; | ||
const parameterNames = Object.keys(inputValues); | ||
|
||
for (const parameterName of parameterNames) { | ||
const parameterValue = inputValues[parameterName]; | ||
|
||
resultValues.push(parameterValue); | ||
} | ||
|
||
const usedParamterNames = []; | ||
|
||
const resultSql = inputSql.replace(namedPlaceholderRegex, (match, g1) => { | ||
if (!parameterNames.includes(g1)) { | ||
throw new UnexpectedStateError('Named parameter reference does not have a matching value.'); | ||
} | ||
|
||
usedParamterNames.push(g1); | ||
|
||
const parameterIndex = parameterNames.indexOf(g1) + 1; | ||
|
||
return match.slice(0, -g1.length - 1) + '$' + parameterIndex; | ||
}); | ||
|
||
const unusedParameterNames = difference(parameterNames, usedParamterNames); | ||
|
||
if (unusedParameterNames.length > 0) { | ||
log.warn({ | ||
unusedParameterNames | ||
}, 'unused parameter names'); | ||
|
||
throw new UnexpectedStateError('Values object contains value(s) not present as named parameter references in the query.'); | ||
} | ||
|
||
return interpolatePositionalParameterReferences(resultSql, resultValues, greatestParameterPosition); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
// @flow | ||
|
||
import { | ||
UnexpectedStateError | ||
} from '../errors'; | ||
import type { | ||
PositionalParameterValuesType | ||
} from '../types'; | ||
import { | ||
createSqlTokenSqlFragment | ||
} from '../factories'; | ||
import isSqlToken from './isSqlToken'; | ||
|
||
/** | ||
* @see https://github.com/mysqljs/sqlstring/blob/f946198800a8d7f198fcf98d8bb80620595d01ec/lib/SqlString.js#L73 | ||
*/ | ||
export default ( | ||
inputSql: string, | ||
inputValues: PositionalParameterValuesType = [], | ||
greatestParameterPosition: number | ||
) => { | ||
const resultValues = []; | ||
|
||
const bindingNames = (inputSql.match(/\$(\d+)/g) || []) | ||
.map((match) => { | ||
return parseInt(match.slice(1), 10); | ||
}) | ||
.sort(); | ||
|
||
if (bindingNames[bindingNames.length - 1] > inputValues.length) { | ||
throw new UnexpectedStateError('The greatest parameter position is greater than the number of parameter values.'); | ||
} | ||
|
||
if (bindingNames.length > 0 && bindingNames[0] !== 1) { | ||
throw new UnexpectedStateError('Parameter position must start at 1.'); | ||
} | ||
|
||
const resultSql = inputSql.replace(/\$(\d+)/g, (match, g1) => { | ||
const parameterPosition = parseInt(g1, 10); | ||
const boundValue = inputValues[parameterPosition - 1]; | ||
|
||
if (isSqlToken(boundValue)) { | ||
// $FlowFixMe | ||
const sqlFragment = createSqlTokenSqlFragment(boundValue, resultValues.length + greatestParameterPosition); | ||
|
||
resultValues.push(...sqlFragment.values); | ||
|
||
return sqlFragment.sql; | ||
} else { | ||
resultValues.push(inputValues[parameterPosition - 1]); | ||
|
||
return '$' + (resultValues.length + greatestParameterPosition); | ||
} | ||
}); | ||
|
||
return { | ||
sql: resultSql, | ||
values: resultValues | ||
}; | ||
}; |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.