Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mode parameter (#29) #30

Merged
merged 1 commit into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@ It ignores non-SELECT queries. It understands CTE statements. It understands str
- `sqlText` - SQL text to enforce limits on. Multiple statements allowed. Only `SELECT` statements are targeted.
- `limitStrategies` - Keyword or array of strategies used to restrict rows. Must be either `limit`, `first`, `top`, `fetch` for `FETCH NEXT`/`FETCH FIRST`.
- `limitNumber` - Number of rows to allow. If number in statement is lower, it is untouched. If higher it is lowered to limit. If missing it is added.
- `offsetNumber` - Number of rows to skip before beginning to return rows from the query. If number in statement is defined, it is untouched. If missing it is added.
- `offsetNumber` - Number of rows to skip before beginning to return rows from the query. If number in statement is defined, it is untouched. If missing it is added. (optional)
- `mode` - Mode for enforcing `limitNumber` or `offsetNumber`. Must be either `replace`, `insert`
or `cap`. The default is `cap` if not defined.
- "replace": Replace existing value. If not existing, it will be inserted.
- "insert": Insert if limit or offset are not existing.
- "cap": Insert if not existing and if higher it is lowered to the defined value.

Returns `sqlText` with limits enforced.

Expand Down
13 changes: 10 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,16 @@ const getStatements = require("./get-statements");
* @param {Array<String>|String} limitStrategies -- First strategy value takes priority if no limit exists
* @param {number} limitNumber -- number to enforce for limit keyword
* @param {number} [offsetNumber] -- offset number to enforce
* @param {boolean} [mode] -- Mode for enforcing `limitNumber` or `offsetNumber`
* @returns {string}
*/
function limit(sqlText, limitStrategies, limitNumber, offsetNumber) {
function limit(
sqlText,
limitStrategies,
limitNumber,
offsetNumber,
mode = "cap"
) {
if (typeof sqlText !== "string") {
throw new Error("sqlText must be string");
}
Expand All @@ -36,9 +43,9 @@ function limit(sqlText, limitStrategies, limitNumber, offsetNumber) {

return getStatements(sqlText)
.map((statement) => {
statement.enforceLimit(strategies, limitNumber);
statement.enforceLimit(strategies, limitNumber, mode);
if (typeof offsetNumber === "number") {
statement.injectOffset(offsetNumber);
statement.enforceOffset(offsetNumber, mode);
}
return statement.toString();
})
Expand Down
52 changes: 35 additions & 17 deletions src/statement.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,30 @@ class Statement {
}
}

updateExistingNumberToken({ mode, tokens, numberToken, value }) {
if (
(mode === "cap" && parseInt(numberToken.value, 10) > value) ||
mode === "replace"
) {
const firstHalf = tokens.slice(0, numberToken.index);
const secondhalf = tokens.slice(numberToken.index + 1);
this.tokens = [
...firstHalf,
{ ...numberToken, text: value, value },
...secondhalf,
];
return;
}
return;
}

/**
*
* @param {Array<String>} strategiesToEnforce
* @param {Number} limitNumber
* @param {string} [mode]
*/
enforceLimit(strategiesToEnforce, limitNumber) {
enforceLimit(strategiesToEnforce, limitNumber, mode) {
const { statementToken, tokens } = this;

strategiesToEnforce.forEach((s) => {
Expand All @@ -110,19 +128,13 @@ class Statement {
statementToken.index
);

// If number token, check to see if over the limit and reset it if it is
// Otherwise return early
if (numberToken) {
if (parseInt(numberToken.value, 10) > limitNumber) {
const firstHalf = tokens.slice(0, numberToken.index);
const secondhalf = tokens.slice(numberToken.index + 1);
this.tokens = [
...firstHalf,
{ ...numberToken, text: limitNumber, value: limitNumber },
...secondhalf,
];
return;
}
this.updateExistingNumberToken({
mode,
tokens,
numberToken,
value: limitNumber,
});
return;
}
}
Expand All @@ -141,14 +153,20 @@ class Statement {

/**
* @param {number} offsetNumber
* @param {string} [mode]
*/
injectOffset(offsetNumber) {
enforceOffset(offsetNumber, mode) {
const { statementToken, tokens } = this;

if (statementToken && statementToken.value === "select") {
const offsetToken = offset.has(tokens, statementToken.index);
// If offset token exists already, return early
if (offsetToken) {
const numberToken = offset.has(tokens, statementToken.index);
if (numberToken) {
this.updateExistingNumberToken({
mode,
tokens,
numberToken,
value: offsetNumber,
});
return;
}

Expand Down
22 changes: 22 additions & 0 deletions test/api-validations.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,28 @@ describe("api: limit", function () {
const res = sqlLimiter.limit(original, "limit", 100);
assert.equal(res, expected);
});

it("limit, offset with insert mode", function () {
const res = sqlLimiter.limit(
`SELECT * from something limit 10000`,
["limit"],
100,
10,
"insert"
);
assert.equal(res, `SELECT * from something limit 10000 offset 10`);
});

it("limit with replace mode", function () {
const res = sqlLimiter.limit(
`SELECT * from something limit 10000 offset 10`,
["limit"],
100,
0,
"replace"
);
assert.equal(res, `SELECT * from something limit 100 offset 0`);
});
});

describe("api: getStatementType", function () {
Expand Down