DiscofyAPI/node_modules/mariadb/lib/misc/parse.js

1026 lines
30 KiB
JavaScript

const Errors = require('../misc/errors');
const State = {
Normal: 1 /* inside query */,
String: 2 /* inside string */,
SlashStarComment: 3 /* inside slash-star comment */,
Escape: 4 /* found backslash */,
EOLComment: 5 /* # comment, or // comment, or -- comment */,
Backtick: 6 /* found backtick */,
Placeholder: 7 /* found placeholder */
};
/**
* Split query according to parameters (question mark).
* Question mark in comment are not taken in account
*
* @returns {Array} query separated by parameters
*/
module.exports.splitQuery = function (sql) {
let partList = [];
let state = State.Normal;
let lastChar = '\0';
let singleQuotes = false;
let lastParameterPosition = 0;
let idx = 0;
let car = sql.charAt(idx++);
while (car !== '') {
if (
state === State.Escape &&
!((car === "'" && singleQuotes) || (car === '"' && !singleQuotes))
) {
state = State.String;
car = sql.charAt(idx++);
continue;
}
switch (car) {
case '*':
if (state === State.Normal && lastChar == '/') state = State.SlashStarComment;
break;
case '/':
if (state === State.SlashStarComment && lastChar == '*') state = State.Normal;
break;
case '#':
if (state === State.Normal) state = State.EOLComment;
break;
case '-':
if (state === State.Normal && lastChar == '-') {
state = State.EOLComment;
}
break;
case '\n':
if (state === State.EOLComment) {
state = State.Normal;
}
break;
case '"':
if (state === State.Normal) {
state = State.String;
singleQuotes = false;
} else if (state === State.String && !singleQuotes) {
state = State.Normal;
} else if (state === State.Escape && !singleQuotes) {
state = State.String;
}
break;
case "'":
if (state === State.Normal) {
state = State.String;
singleQuotes = true;
} else if (state === State.String && singleQuotes) {
state = State.Normal;
} else if (state === State.Escape && singleQuotes) {
state = State.String;
}
break;
case '\\':
if (state === State.String) state = State.Escape;
break;
case '?':
if (state === State.Normal) {
partList.push(sql.substring(lastParameterPosition, idx - 1));
lastParameterPosition = idx;
}
break;
case '`':
if (state === State.Backtick) {
state = State.Normal;
} else if (state === State.Normal) {
state = State.Backtick;
}
break;
}
lastChar = car;
car = sql.charAt(idx++);
}
if (lastParameterPosition === 0) {
partList.push(sql);
} else {
partList.push(sql.substring(lastParameterPosition));
}
return partList;
};
/**
* Split query according to parameters using placeholder.
*
* @param sql sql with placeholders
* @param info connection information
* @param initialValues placeholder object
* @param displaySql display sql function
* @returns {{parts: Array, values: Array}}
*/
module.exports.splitQueryPlaceholder = function (sql, info, initialValues, displaySql) {
let partList = [];
let values = [];
let state = State.Normal;
let lastChar = '\0';
let singleQuotes = false;
let lastParameterPosition = 0;
let idx = 0;
let car = sql.charAt(idx++);
let placeholderName;
while (car !== '') {
if (
state === State.Escape &&
!((car === "'" && singleQuotes) || (car === '"' && !singleQuotes))
) {
state = State.String;
car = sql.charAt(idx++);
continue;
}
switch (car) {
case '*':
if (state === State.Normal && lastChar == '/') state = State.SlashStarComment;
break;
case '/':
if (state === State.SlashStarComment && lastChar == '*') state = State.Normal;
break;
case '#':
if (state === State.Normal) state = State.EOLComment;
break;
case '-':
if (state === State.Normal && lastChar == '-') {
state = State.EOLComment;
}
break;
case '\n':
if (state === State.EOLComment) {
state = State.Normal;
}
break;
case '"':
if (state === State.Normal) {
state = State.String;
singleQuotes = false;
} else if (state === State.String && !singleQuotes) {
state = State.Normal;
} else if (state === State.Escape && !singleQuotes) {
state = State.String;
}
break;
case "'":
if (state === State.Normal) {
state = State.String;
singleQuotes = true;
} else if (state === State.String && singleQuotes) {
state = State.Normal;
singleQuotes = false;
} else if (state === State.Escape && singleQuotes) {
state = State.String;
}
break;
case '\\':
if (state === State.String) state = State.Escape;
break;
case ':':
if (state === State.Normal) {
partList.push(sql.substring(lastParameterPosition, idx - 1));
placeholderName = '';
while (
((car = sql.charAt(idx++)) !== '' && car >= '0' && car <= '9') ||
(car >= 'A' && car <= 'Z') ||
(car >= 'a' && car <= 'z') ||
car === '-' ||
car === '_'
) {
placeholderName += car;
}
idx--;
const val = initialValues[placeholderName];
if (val === undefined) {
throw Errors.createError(
"Placeholder '" + placeholderName + "' is not defined\n" + displaySql.call(),
false,
info,
'HY000',
Errors.ER_PLACEHOLDER_UNDEFINED
);
}
values.push(val);
lastParameterPosition = idx;
}
break;
case '`':
if (state === State.Backtick) {
state = State.Normal;
} else if (state === State.Normal) {
state = State.Backtick;
}
}
lastChar = car;
car = sql.charAt(idx++);
}
if (lastParameterPosition === 0) {
partList.push(sql);
} else {
partList.push(sql.substring(lastParameterPosition));
}
return { parts: partList, values: values };
};
/**
* Split query according to parameters (question mark).
*
* The only rewritten queries follow these notation: INSERT [LOW_PRIORITY | DELAYED |
* HIGH_PRIORITY] [IGNORE] [INTO] tbl_name [PARTITION (partition_list)] [(col,...)] {VALUES |
* VALUE} (...) [ ON DUPLICATE KEY UPDATE col=expr [, col=expr] ... ] With expr without
* parameter.
*
* Query with INSERT ... SELECT / containing LAST_INSERT_ID() will not be rewritten
*
* query parts will be split this way :
* - pre-value part
* - after value part
* [- after parameter part] (after each parameter)
* - ending part
*
* example : INSERT INTO MyTABLE VALUES (9, ?, 5, ?, 8) ON DUPLICATE KEY UPDATE col2=col2+10
* will result in :
* - pre-value : "INSERT INTO MyTABLE VALUES"
* - after value : " (9, "
* - after parameter : ", 5, "
* - after parameter : ", 8)"
* - ending : " ON DUPLICATE KEY UPDATE col2=col2+10"
*
*
* @returns {JSON} query separated by parameters
*/
module.exports.splitRewritableQuery = function (sql) {
let reWritablePrepare = true;
let multipleQueriesPrepare = true;
let partList = [];
let lastChar = '\0';
let lastParameterPosition = 0;
let preValuePart1 = null;
let preValuePart2 = null;
let postValuePart = null;
let singleQuotes = false;
let isInParenthesis = 0;
let isFirstChar = true;
let isInsert = false;
let semicolon = false;
let hasParam = false;
let state = State.Normal;
let idx = 0;
let car = sql.charAt(idx++);
while (car !== '') {
if (
state === State.Escape &&
!((car === "'" && singleQuotes) || (car === '"' && !singleQuotes))
) {
state = State.String;
car = sql.charAt(idx++);
continue;
}
switch (car) {
case '*':
if (state === State.Normal && lastChar == '/') {
state = State.SlashStarComment;
}
break;
case '/':
if (state === State.SlashStarComment && lastChar == '*') {
state = State.Normal;
}
break;
case '#':
if (state === State.Normal) {
state = State.EOLComment;
}
break;
case '-':
if (state === State.Normal && lastChar == '-') {
state = State.EOLComment;
}
break;
case '\n':
if (state === State.EOLComment) {
state = State.Normal;
}
break;
case '"':
if (state === State.Normal) {
state = State.String;
singleQuotes = false;
} else if (state === State.String && !singleQuotes) {
state = State.Normal;
} else if (state === State.Escape && !singleQuotes) {
state = State.String;
}
break;
case ';':
if (state === State.Normal) {
semicolon = true;
multipleQueriesPrepare = false;
}
break;
case "'":
if (state === State.Normal) {
state = State.String;
singleQuotes = true;
} else if (state === State.String && singleQuotes) {
state = State.Normal;
} else if (state === State.Escape && singleQuotes) {
state = State.String;
}
break;
case '\\':
if (state === State.String) {
state = State.Escape;
}
break;
case '?':
if (state === State.Normal) {
hasParam = true;
let part = sql.substring(lastParameterPosition, idx - 1);
lastParameterPosition = idx;
if (preValuePart1 === null) {
preValuePart1 = part;
preValuePart2 = '';
} else if (preValuePart2 === null) {
preValuePart2 = part;
} else {
if (postValuePart) {
//having parameters after the last ")" of value is not rewritable
reWritablePrepare = false;
partList.push(postValuePart + part);
postValuePart = null;
} else partList.push(part);
}
}
break;
case '`':
if (state === State.Backtick) {
state = State.Normal;
} else if (state === State.Normal) {
state = State.Backtick;
}
break;
case 's':
case 'S':
if (
state === State.Normal &&
postValuePart === null &&
sql.length > idx + 5 &&
(sql.charAt(idx) === 'e' || sql.charAt(idx) === 'E') &&
(sql.charAt(idx + 1) === 'l' || sql.charAt(idx + 1) === 'L') &&
(sql.charAt(idx + 2) === 'e' || sql.charAt(idx + 2) === 'E') &&
(sql.charAt(idx + 3) === 'c' || sql.charAt(idx + 3) === 'C') &&
(sql.charAt(idx + 4) === 't' || sql.charAt(idx + 4) === 'T')
) {
//field/table name might contain 'select'
if (
idx > 1 &&
sql.charAt(idx - 2) > ' ' &&
'();><=-+,'.indexOf(sql.charAt(idx - 2)) === -1
) {
break;
}
if (sql.charAt(idx + 5) > ' ' && '();><=-+,'.indexOf(sql.charAt(idx + 5)) === -1) {
break;
}
//SELECT queries, INSERT FROM SELECT not rewritable
reWritablePrepare = false;
}
break;
case 'v':
case 'V':
if (
state === State.Normal &&
!preValuePart1 &&
(lastChar == ')' || lastChar <= ' ') &&
sql.length > idx + 6 &&
(sql.charAt(idx) === 'a' || sql.charAt(idx) === 'A') &&
(sql.charAt(idx + 1) === 'l' || sql.charAt(idx + 1) === 'L') &&
(sql.charAt(idx + 2) === 'u' || sql.charAt(idx + 2) === 'U') &&
(sql.charAt(idx + 3) === 'e' || sql.charAt(idx + 3) === 'E') &&
(sql.charAt(idx + 4) === 's' || sql.charAt(idx + 4) === 'S') &&
(sql.charAt(idx + 5) === '(' || sql.charAt(idx + 5) <= ' ')
) {
idx += 5;
preValuePart1 = sql.substring(lastParameterPosition, idx);
lastParameterPosition = idx;
}
break;
case 'l':
case 'L':
if (
state === State.Normal &&
sql.length > idx + 13 &&
(sql.charAt(idx) === 'a' || sql.charAt(idx) === 'A') &&
(sql.charAt(idx + 1) === 's' || sql.charAt(idx + 1) === 'S') &&
(sql.charAt(idx + 2) === 't' || sql.charAt(idx + 2) === 'T') &&
sql.charAt(idx + 3) === '_' &&
(sql.charAt(idx + 4) === 'i' || sql.charAt(idx + 4) === 'I') &&
(sql.charAt(idx + 5) === 'n' || sql.charAt(idx + 5) === 'N') &&
(sql.charAt(idx + 6) === 's' || sql.charAt(idx + 6) === 'S') &&
(sql.charAt(idx + 7) === 'e' || sql.charAt(idx + 7) === 'E') &&
(sql.charAt(idx + 8) === 'r' || sql.charAt(idx + 8) === 'R') &&
(sql.charAt(idx + 9) === 't' || sql.charAt(idx + 9) === 'T') &&
sql.charAt(idx + 10) === '_' &&
(sql.charAt(idx + 11) === 'i' || sql.charAt(idx + 11) === 'I') &&
(sql.charAt(idx + 12) === 'd' || sql.charAt(idx + 12) === 'D') &&
sql.charAt(idx + 13) === '('
) {
reWritablePrepare = false;
idx += 13;
}
break;
case '(':
if (state === State.Normal) {
isInParenthesis++;
}
break;
case ')':
if (state === State.Normal) {
isInParenthesis--;
if (isInParenthesis === 0 && preValuePart2 !== null && postValuePart === null) {
postValuePart = sql.substring(lastParameterPosition, idx);
lastParameterPosition = idx;
}
}
break;
default:
if (state === State.Normal && isFirstChar && car > ' ') {
if (
(car === 'I' || car === 'i') &&
sql.length > idx + 6 &&
(sql.charAt(idx) === 'n' || sql.charAt(idx) === 'N') &&
(sql.charAt(idx + 1) === 's' || sql.charAt(idx + 1) === 'S') &&
(sql.charAt(idx + 2) === 'e' || sql.charAt(idx + 2) === 'E') &&
(sql.charAt(idx + 3) === 'r' || sql.charAt(idx + 3) === 'R') &&
(sql.charAt(idx + 4) === 't' || sql.charAt(idx + 4) === 'T') &&
(sql.charAt(idx + 5) === '(' || sql.charAt(idx + 5) <= ' ')
) {
isInsert = true;
}
isFirstChar = false;
}
//multiple queries
if (state === State.Normal && semicolon && car >= ' ') {
reWritablePrepare = false;
multipleQueriesPrepare = true;
}
break;
}
lastChar = car;
car = sql.charAt(idx++);
}
if (state === State.EOLComment) multipleQueriesPrepare = false;
if (!hasParam) {
//permit to have rewrite without parameter
if (preValuePart1 === null) {
partList.unshift('');
partList.unshift(sql);
} else {
partList.unshift(sql.substring(lastParameterPosition, idx));
partList.unshift(preValuePart1);
}
lastParameterPosition = idx;
} else {
partList.unshift(preValuePart2 !== null ? preValuePart2 : '');
partList.unshift(preValuePart1 !== null ? preValuePart1 : '');
}
if (!isInsert) {
reWritablePrepare = false;
}
//postValuePart is the value after the last parameter and parenthesis
//if no param, don't add to the list.
if (hasParam) {
partList.push(postValuePart !== null ? postValuePart : '');
}
partList.push(sql.substring(lastParameterPosition, idx));
return {
partList: partList,
reWritable: reWritablePrepare,
multipleQueries: multipleQueriesPrepare
};
};
module.exports.searchPlaceholder = function (sql, info, initialValues, displaySql) {
let sqlPlaceHolder = '';
const rowNumber = initialValues.length;
let values = new Array(rowNumber);
for (let i = 0; i < rowNumber; i++) values[i] = [];
let state = State.Normal;
let lastChar = '\0';
let singleQuotes = false;
let lastParameterPosition = 0;
let idx = 0;
let car = sql.charAt(idx++);
let placeholderName;
while (car !== '') {
if (
state === State.Escape &&
!((car === "'" && singleQuotes) || (car === '"' && !singleQuotes))
) {
state = State.String;
lastChar = car;
car = sql.charAt(idx++);
continue;
}
switch (car) {
case '*':
if (state === State.Normal && lastChar == '/') state = State.SlashStarComment;
break;
case '/':
if (state === State.SlashStarComment && lastChar == '*') state = State.Normal;
break;
case '#':
if (state === State.Normal) state = State.EOLComment;
break;
case '-':
if (state === State.Normal && lastChar == '-') {
state = State.EOLComment;
}
break;
case '\n':
if (state === State.EOLComment) {
state = State.Normal;
}
break;
case '"':
if (state === State.Normal) {
state = State.String;
singleQuotes = false;
} else if (state === State.String && !singleQuotes) {
state = State.Normal;
} else if (state === State.Escape && !singleQuotes) {
state = State.String;
}
break;
case "'":
if (state === State.Normal) {
state = State.String;
singleQuotes = true;
} else if (state === State.String && singleQuotes) {
state = State.Normal;
singleQuotes = false;
} else if (state === State.Escape && singleQuotes) {
state = State.String;
}
break;
case '\\':
if (state === State.String) state = State.Escape;
break;
case ':':
if (state === State.Normal) {
sqlPlaceHolder += sql.substring(lastParameterPosition, idx - 1) + '?';
placeholderName = '';
while (
((car = sql.charAt(idx++)) !== '' && car >= '0' && car <= '9') ||
(car >= 'A' && car <= 'Z') ||
(car >= 'a' && car <= 'z') ||
car === '-' ||
car === '_'
) {
placeholderName += car;
}
idx--;
for (let i = 0; i < rowNumber; i++) {
const val = initialValues[i][placeholderName];
if (val !== undefined) {
values[i].push(val);
} else {
values[i].push(null);
}
}
lastParameterPosition = idx;
}
break;
case '`':
if (state === State.Backtick) {
state = State.Normal;
} else if (state === State.Normal) {
state = State.Backtick;
}
}
lastChar = car;
car = sql.charAt(idx++);
}
if (lastParameterPosition === 0) {
sqlPlaceHolder = sql;
} else {
sqlPlaceHolder += sql.substring(lastParameterPosition);
}
return { sql: sqlPlaceHolder, values: values };
};
/**
* Split query according to named parameters.
*
* The only rewritten queries follow these notation: INSERT [LOW_PRIORITY | DELAYED |
* HIGH_PRIORITY] [IGNORE] [INTO] tbl_name [PARTITION (partition_list)] [(col,...)] {VALUES |
* VALUE} (...) [ ON DUPLICATE KEY UPDATE col=expr [, col=expr] ... ] With expr without
* parameter.
*
* Query with INSERT ... SELECT / containing LAST_INSERT_ID() will not be rewritten
*
* query parts will be split this way :
* - pre-value part
* - after value part
* [- after parameter part] (after each parameter)
* - ending part
*
* example : INSERT INTO MyTABLE VALUES (9, :param1, 5, :param2, 8) ON DUPLICATE KEY UPDATE col2=col2+10
* will result in :
* - pre-value : "INSERT INTO MyTABLE VALUES"
* - after value : " (9, "
* - after parameter : ", 5, "
* - after parameter : ", 8)"
* - ending : " ON DUPLICATE KEY UPDATE col2=col2+10"
*
*
* @returns {JSON} query separated by parameters
*/
module.exports.splitRewritableNamedParameterQuery = function (sql, initialValues) {
let reWritablePrepare = true;
let multipleQueriesPrepare = true;
let partList = [];
let values = new Array(initialValues.length);
for (let i = 0; i < values.length; i++) values[i] = [];
let lastChar = '\0';
let lastParameterPosition = 0;
let preValuePart1 = null;
let preValuePart2 = null;
let postValuePart = null;
let singleQuotes = false;
let isInParenthesis = 0;
let isFirstChar = true;
let isInsert = false;
let semicolon = false;
let hasParam = false;
let placeholderName;
let state = State.Normal;
let idx = 0;
let car = sql.charAt(idx++);
while (car !== '') {
if (
state === State.Escape &&
!((car === "'" && singleQuotes) || (car === '"' && !singleQuotes))
) {
state = State.String;
car = sql.charAt(idx++);
continue;
}
switch (car) {
case '*':
if (state === State.Normal && lastChar == '/') {
state = State.SlashStarComment;
}
break;
case '/':
if (state === State.SlashStarComment && lastChar == '*') {
state = State.Normal;
}
break;
case '#':
if (state === State.Normal) {
state = State.EOLComment;
}
break;
case '-':
if (state === State.Normal && lastChar == '-') {
state = State.EOLComment;
}
break;
case '\n':
if (state === State.EOLComment) {
state = State.Normal;
}
break;
case '"':
if (state === State.Normal) {
state = State.String;
singleQuotes = false;
} else if (state === State.String && !singleQuotes) {
state = State.Normal;
} else if (state === State.Escape && !singleQuotes) {
state = State.String;
}
break;
case ';':
if (state === State.Normal) {
semicolon = true;
multipleQueriesPrepare = false;
}
break;
case "'":
if (state === State.Normal) {
state = State.String;
singleQuotes = true;
} else if (state === State.String && singleQuotes) {
state = State.Normal;
} else if (state === State.Escape && singleQuotes) {
state = State.String;
}
break;
case '\\':
if (state === State.String) {
state = State.Escape;
}
break;
case ':':
if (state === State.Normal) {
let part = sql.substring(lastParameterPosition, idx - 1);
placeholderName = '';
while (
((car = sql.charAt(idx++)) !== '' && car >= '0' && car <= '9') ||
(car >= 'A' && car <= 'Z') ||
(car >= 'a' && car <= 'z') ||
car === '-' ||
car === '_'
) {
placeholderName += car;
}
idx--;
hasParam = true;
initialValues.forEach((row, idx) => {
if (row[placeholderName] !== undefined) {
values[idx].push(row[placeholderName]);
} else {
values[idx].push(null);
}
});
lastParameterPosition = idx;
if (preValuePart1 === null) {
preValuePart1 = part;
preValuePart2 = '';
} else if (preValuePart2 === null) {
preValuePart2 = part;
} else {
if (postValuePart) {
//having parameters after the last ")" of value is not rewritable
reWritablePrepare = false;
partList.push(postValuePart + part);
postValuePart = null;
} else partList.push(part);
}
}
break;
case '`':
if (state === State.Backtick) {
state = State.Normal;
} else if (state === State.Normal) {
state = State.Backtick;
}
break;
case 's':
case 'S':
if (
state === State.Normal &&
postValuePart === null &&
sql.length > idx + 5 &&
(sql.charAt(idx) === 'e' || sql.charAt(idx) === 'E') &&
(sql.charAt(idx + 1) === 'l' || sql.charAt(idx + 1) === 'L') &&
(sql.charAt(idx + 2) === 'e' || sql.charAt(idx + 2) === 'E') &&
(sql.charAt(idx + 3) === 'c' || sql.charAt(idx + 3) === 'C') &&
(sql.charAt(idx + 4) === 't' || sql.charAt(idx + 4) === 'T')
) {
//field/table name might contain 'select'
if (
idx > 1 &&
sql.charAt(idx - 2) > ' ' &&
'();><=-+,'.indexOf(sql.charAt(idx - 2)) === -1
) {
break;
}
if (sql.charAt(idx + 5) > ' ' && '();><=-+,'.indexOf(sql.charAt(idx + 5)) === -1) {
break;
}
//SELECT queries, INSERT FROM SELECT not rewritable
reWritablePrepare = false;
}
break;
case 'v':
case 'V':
if (
state === State.Normal &&
!preValuePart1 &&
(lastChar == ')' || lastChar <= ' ') &&
sql.length > idx + 6 &&
(sql.charAt(idx) === 'a' || sql.charAt(idx) === 'A') &&
(sql.charAt(idx + 1) === 'l' || sql.charAt(idx + 1) === 'L') &&
(sql.charAt(idx + 2) === 'u' || sql.charAt(idx + 2) === 'U') &&
(sql.charAt(idx + 3) === 'e' || sql.charAt(idx + 3) === 'E') &&
(sql.charAt(idx + 4) === 's' || sql.charAt(idx + 4) === 'S') &&
(sql.charAt(idx + 5) === '(' || sql.charAt(idx + 5) <= ' ')
) {
idx += 5;
preValuePart1 = sql.substring(lastParameterPosition, idx);
lastParameterPosition = idx;
}
break;
case 'l':
case 'L':
if (
state === State.Normal &&
sql.length > idx + 13 &&
(sql.charAt(idx) === 'a' || sql.charAt(idx) === 'A') &&
(sql.charAt(idx + 1) === 's' || sql.charAt(idx + 1) === 'S') &&
(sql.charAt(idx + 2) === 't' || sql.charAt(idx + 2) === 'T') &&
sql.charAt(idx + 3) === '_' &&
(sql.charAt(idx + 4) === 'i' || sql.charAt(idx + 4) === 'I') &&
(sql.charAt(idx + 5) === 'n' || sql.charAt(idx + 5) === 'N') &&
(sql.charAt(idx + 6) === 's' || sql.charAt(idx + 6) === 'S') &&
(sql.charAt(idx + 7) === 'e' || sql.charAt(idx + 7) === 'E') &&
(sql.charAt(idx + 8) === 'r' || sql.charAt(idx + 8) === 'R') &&
(sql.charAt(idx + 9) === 't' || sql.charAt(idx + 9) === 'T') &&
sql.charAt(idx + 10) === '_' &&
(sql.charAt(idx + 11) === 'i' || sql.charAt(idx + 11) === 'I') &&
(sql.charAt(idx + 12) === 'd' || sql.charAt(idx + 12) === 'D') &&
sql.charAt(idx + 13) === '('
) {
reWritablePrepare = false;
idx += 13;
}
break;
case '(':
if (state === State.Normal) {
isInParenthesis++;
}
break;
case ')':
if (state === State.Normal) {
isInParenthesis--;
if (isInParenthesis === 0 && preValuePart2 !== null && postValuePart === null) {
postValuePart = sql.substring(lastParameterPosition, idx);
lastParameterPosition = idx;
}
}
break;
default:
if (state === State.Normal && isFirstChar && car > ' ') {
if (
(car === 'I' || car === 'i') &&
sql.length > idx + 6 &&
(sql.charAt(idx) === 'n' || sql.charAt(idx) === 'N') &&
(sql.charAt(idx + 1) === 's' || sql.charAt(idx + 1) === 'S') &&
(sql.charAt(idx + 2) === 'e' || sql.charAt(idx + 2) === 'E') &&
(sql.charAt(idx + 3) === 'r' || sql.charAt(idx + 3) === 'R') &&
(sql.charAt(idx + 4) === 't' || sql.charAt(idx + 4) === 'T') &&
(sql.charAt(idx + 5) === '(' || sql.charAt(idx + 5) <= ' ')
) {
isInsert = true;
}
isFirstChar = false;
}
//multiple queries
if (state === State.Normal && semicolon && car >= ' ') {
reWritablePrepare = false;
multipleQueriesPrepare = true;
}
break;
}
lastChar = car;
car = sql.charAt(idx++);
}
if (state === State.EOLComment) multipleQueriesPrepare = false;
if (!hasParam) {
//permit to have rewrite without parameter
if (preValuePart1 === null) {
partList.unshift('');
partList.unshift(sql);
} else {
partList.unshift(sql.substring(lastParameterPosition, idx));
partList.unshift(preValuePart1);
}
lastParameterPosition = idx;
} else {
partList.unshift(preValuePart2 !== null ? preValuePart2 : '');
partList.unshift(preValuePart1 !== null ? preValuePart1 : '');
}
if (!isInsert) {
reWritablePrepare = false;
}
//postValuePart is the value after the last parameter and parenthesis
//if no param, don't add to the list.
if (hasParam) {
partList.push(postValuePart !== null ? postValuePart : '');
}
partList.push(sql.substring(lastParameterPosition, idx));
return {
partList: partList,
reWritable: reWritablePrepare,
multipleQueries: multipleQueriesPrepare,
values: values
};
};
/**
* Ensure that filename requested by server corresponds to query
* protocol : https://mariadb.com/kb/en/library/local_infile-packet/
*
* @param sql query
* @param parameters parameters if any
* @param fileName server requested file
* @returns {boolean} is filename corresponding to query
*/
module.exports.validateFileName = function (sql, parameters, fileName) {
let queryValidator = new RegExp(
"^(\\s*\\/\\*([^\\*]|\\*[^\\/])*\\*\\/)*\\s*LOAD\\s+DATA\\s+((LOW_PRIORITY|CONCURRENT)\\s+)?LOCAL\\s+INFILE\\s+'" +
fileName +
"'",
'i'
);
if (queryValidator.test(sql)) return true;
if (parameters != null) {
queryValidator = new RegExp(
'^(\\s*\\/\\*([^\\*]|\\*[^\\/])*\\*\\/)*\\s*LOAD\\s+DATA\\s+((LOW_PRIORITY|CONCURRENT)\\s+)?LOCAL\\s+INFILE\\s+\\?',
'i'
);
if (queryValidator.test(sql) && parameters.length > 0) {
return parameters[0].toLowerCase() === fileName.toLowerCase();
}
}
return false;
};