Запрос MySQL Build динамически из объекта JSON

Есть ли способ динамически построить запрос MySQL из объекта JSON, значения которого, вероятно, будут пустыми.

Например, из такого объекта:

{
  "a": 1
  "b": ""
  "c": "foo"
}

создайте запрос, подобный этому ("b" пусто, его не следует принимать во внимание):

SELECT * FROM db.table
WHERE a = 1
AND c = "foo"

или

SELECT * FROM db.table
WHERE a = 1
AND b = ????
AND c = "foo"

Обновлено: это, вероятно, действительно дублируется. Но я подумал, что для этого есть более SQL-способ, например, с использованием переменных и операторов IF.

Редактировать 2: я нашел способ (работает в API node.js, но он должен быть похож на другие языки):

const jsonObj = {
"a": 1,
"b": "",
"c": "foo"
}
const query = `
SELECT * FROM db.table
WHERE
IF('${jsonObj.a}' != '', a = '${jsonObj.a}', 1=1)
AND
IF('${jsonObj.b}' != '', b = '${jsonObj.b}', 1=1)
AND
IF('${jsonObj.c}' != '', c = '${jsonObj.c}', 1=1)
`

Конечно, этот код нельзя использовать в его нынешнем виде, его необходимо адаптировать с учетом проблем с внедрением.

Откуда берется объект JSON... я предполагаю, что здесь задействован какой-то клиентский язык программирования?

Raymond Nijland 27.06.2019 17:35

Да, он отправляется клиентом веб-браузера в теле запроса POST.

Joulss 27.06.2019 17:43

Жаль, я не могу опубликовать ответ... Кажется, это работает в гораздо большей степени SQL, чем предыдущий дубликат (мы находимся в среде node.js): const jsonObj = { "a": 1 "b ": "" "c": "foo" } const query = ` SELECT * FROM db.table WHERE IF(${jsonObj.a} != "", a = ${jsonObj.a}, 1=1) И ЕСЛИ(${jsonObj.b} != "", b = ${jsonObj.b}, 1=1) и т.д.

Joulss 27.06.2019 18:26

Похоже, это очень подвержено SQL-инъекциям.

Raymond Nijland 27.06.2019 18:27

Да, конечно, это включает в себя проверку данных и подготовленные запросы.

Joulss 27.06.2019 18:30

Бармар прав, проблема и ее решение одинаковы независимо от языка.

Joulss 27.06.2019 18:33

Справедливости ради, я на самом деле не заметил, что он не указал язык, и PHP, как правило, является наиболее распространенным.

Barmar 27.06.2019 18:35

Я повторно открыл вопрос, пожалуйста, добавьте языковой тег и покажите свое решение. Вы должны быть в состоянии адаптировать алгоритм в связанном вопросе к своему языку.

Barmar 27.06.2019 18:36
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
3
8
1 199
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

ВАЖНЫЙ: эта стратегия открыта для Атаки SQL-инъекций. Вы должен экранируете значения - предпочтительно используя подготовленные запросы. Без дополнительных знаний о вашем клиенте базы данных невозможно направить вас, как это сделать.

КРОМЕ ТОГО: Я настоятельно рекомендую иметь белый список разрешенных столбцов и разрешать использование в запросе только тех ключей столбцов, которые находятся в белом списке. Пример ниже включает белый список, чтобы продемонстрировать это.

Вот MVP, который будет обрабатывать произвольный/динамический объект и строить оператор SQL по вашему запросу:

const obj = {
  "a": 1,
  "b": "",
  "c": "foo",
  "bad": "disallowed"
}

// example of how to use a whitelist
const whitelist = ['a', 'c'];

// set up an empty array to contain the WHERE conditions
let where = [];

// Iterate over each key / value in the object
Object.keys(obj).forEach(function (key) {
    // if the key is not whitelisted, do not use
    if ( ! whitelist.includes(key) ) {
        return;
    }

    // if the value is an empty string, do not use
	  if ( '' === obj[key] ) {
        return;
    }

    // if we've made it this far, add the clause to the array of conditions
    where.push(`\`${key}\` = "${obj[key]}"`);
});

// convert the where array into a string of AND clauses
where = where.join(' AND ');

// if there IS a WHERE string, prepend with WHERE keyword
if (where) {
    where = `WHERE ${where}`;
}

const sql = `SELECT * FROM table ${where}`;

console.info(sql);
// SELECT * FROM table WHERE `a` = "1" AND `c` = "foo"

ПРИМЕЧАНИЯ:

  1. Предоставление вам ЛЮБОЙ формы экранирования символов выходит за рамки этого вопроса/ответа. Это, безусловно, не удастся, если значения содержат символы двойных кавычек (например, ")
  2. Предоставление вам средств для «обнаружения», является ли значение числовым и НЕ заключает его в кавычки в запросе, также выходит за рамки этого вопроса/ответа. Обратите внимание, что, по моему опыту, многие базы данных правильно обрабатывают числовое значение, заключенное в кавычки.

Давайте попробуем создать функцию, которая может обрабатывать много сложных запросов.

function prepareStmtFromObject(params) {
    const constraints = [];
    const data = [];
    Object.keys(params).forEach((item) => {
        if (!params[item] || params[item] == "") {
            return;
        }
        if (Array.isArray(params[item])) {
            constraints.push(`${item} in (?)`);
            data.push(params[item]);
        } else if (typeof params[item] === "string" && params[item].indexOf(",") > -1) {
            constraints.push(`${item} in (?)`);
            data.push(params[item].split(","));
        } else if (params[item] instanceof RegExp) {
            constraints.push(`${item} REGEXP ?`);
            data.push(params[item]);
        } else if (params[item] && typeof params[item] === "object") {
            Object.keys(params[item]).forEach((value) => {
                if (value === "$gte") {
                    constraints.push(`${item} >= ?`);
                    data.push(params[item][value]);
                } else if (value === "$lte") {
                    constraints.push(`${item} <= ?`);
                    data.push(params[item][value]);
                } else if (value === "$gt") {
                    constraints.push(`${item} > ?`);
                    data.push(params[item][value]);
                } else if (value === "$lt") {
                    constraints.push(`${item} < ?`);
                    data.push(params[item][value]);
                } else if (value === "$like") {
                    if (Array.isArray(params[item][value])) {
                        const localConstraints = [];
                        params[item][value].forEach((likeValues) => {
                            localConstraints.push(`${item} LIKE ?`);
                            data.push(`%${likeValues}%`);
                        });
                        constraints.push(`(${localConstraints.join(" OR ")})`);
                    } else if (typeof params[item][value] === "string" && params[item][value].indexOf(",") > -1) {
                        const localConstraints = [];
                        params[item][value] = params[item][value].split(",");
                        params[item][value].forEach((likeValues) => {
                            localConstraints.push(`${item} LIKE ?`);
                            data.push(`%${likeValues}%`);
                        });
                        constraints.push(`(${localConstraints.join(" OR ")})`);
                    } else {
                        constraints.push(`${item} LIKE ?`);
                        data.push(`%${params[item][value]}%`);
                    }
                }
            });
        } else {
            constraints.push(`${item} = ?`);
            data.push(params[item]);
        }
    });
    return { constraints, data };
}

const data = {
    userId: 1,
    company: ["google", "microsoft"],
    username: { $like: "Test" },
    name: { $like: [ "Test1", "Test2" ] },
    age: { $gt: 10 }
}
const stmt = prepareStmtFromObject(data);
console.info("SELECT * FROM user WHERE ", stmt.constraints.join(" and "));
console.info(stmt.data);

Приведенная выше функция возвращает ограничения и массив запросов, которые можно использовать для экранирования символов. Таким образом, вы также можете предотвратить внедрение SQL. Я предполагаю, что вы используете пакет mysql

Другие вопросы по теме