Ответ
Плейсхолдеры (placeholders) — это специальные маркеры в SQL-запросе, которые используются для безопасной подстановки пользовательских данных. Этот механизм является частью Prepared Statements
(подготовленных выражений) и является стандартом для работы с базами данных.
Главная цель — предотвращение SQL-инъекций
SQL-инъекция — это атака, при которой злоумышленник внедряет вредоносный SQL-код в запрос, чтобы обойти логику приложения или получить несанкционированный доступ к данным.
Неправильно (уязвимо к инъекциям):
// email может содержать вредоносный код, например: ' OR 1=1; --
email := "' OR 1=1; --"
query := fmt.Sprintf("SELECT * FROM users WHERE email = '%s'", email)
db.QueryRow(query) // Опасно!
Правильно (безопасно):
email := "user@example.com"
// Используем плейсхолдер `?` (синтаксис зависит от драйвера)
query := "SELECT * FROM users WHERE email = ?"
// Драйвер БД сам безопасно подставит значение
db.QueryRow(query, email)
При использовании плейсхолдеров данные передаются на сервер БД отдельно от самого SQL-запроса. Сервер сначала получает и парсит шаблон запроса, а затем применяет к нему данные, обрабатывая их как литералы, а не как часть исполняемого кода. Это полностью исключает возможность SQL-инъекции.
Дополнительные преимущества
- Производительность: СУБД может кешировать план выполнения для подготовленного запроса. При повторном вызове того же запроса с другими параметрами не тратится время на его повторный парсинг и анализ.
- Читаемость и поддержка: Код становится чище, так как логика запроса отделена от данных.
- Корректная обработка типов: Драйвер
database/sql
берет на себя заботу о правильном экранировании спецсимволов и преобразовании типов Go в соответствующие типы SQL.
Важно: Синтаксис плейсхолдеров
Стиль плейсхолдеров зависит от используемой базы данных и ее драйвера:
- PostgreSQL: Позиционные,
$1
,$2
, и т.д.INSERT INTO users (name, age) VALUES ($1, $2)
- MySQL, SQLite: Знак вопроса
?
.UPDATE users SET name = ? WHERE id = ?
- Oracle: Именованные,
:name
,:id
.SELECT * FROM users WHERE name = :name