Здесь было много полезных ответов, в целом достигающих высшей точки в два пункта.
BACKTICKS(
) `используются вокруг имен идентификаторов. QUOTES(')
используются вокруг значений. И как @MichaelBerkowski сказал
Backticks должны использоваться для идентификаторов таблиц и столбцов, но необходимы только тогда, когда идентификатор является
blockquote>MySQL
зарезервированным ключевое слово или когда идентификатор содержит символы пробела или символы за пределами ограниченного набора (см. ниже) Часто рекомендуется избегать использования зарезервированных ключевых слов в качестве идентификаторов столбцов или таблиц, если это возможно, во избежание проблемы с кавычками.Существует случай, когда идентификатор не может быть зарезервированным ключевым словом или содержать символы whitespace или за пределами ограниченного набора , но обязательно требует обратных ссылок вокруг их.
ПРИМЕР
123E10
является допустимым именем идентификатора, а также действительнымINTEGER
литералом.[Не вдаваясь в подробности, как вы могли бы получить такое имя идентификатора] Предположим, что я хочу создать временную таблицу с именем
123456e6
.Нет ОШИБКИ на обратных циклах.
DB [XXX]> create temporary table `123456e6` (`id` char (8)); Query OK, 0 rows affected (0.03 sec)
ОШИБКА, если вы не используете обратные ссылки.
DB [XXX]> create temporary table 123451e6 (`id` char (8)); ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '123451e6 (`id` char (8))' at line 1
Однако
123451a6
[* g15]DB [XXX]> create temporary table 123451a6 (`id` char (8)); Query OK, 0 rows affected (0.03 sec)
Это полностью, потому что
1234156e6
также является показательным номером.
Я думаю, что «вариант 2» (ручная проверка ограничения перед попыткой вставить) ужасен не только из-за опасности гонки (чего можно избежать с помощью блокировки чтения ), но также (как вы примечание) из-за дополнительной нагрузки на базу данных: в конце концов, ручная проверка ограничений полностью отрицает цель и преимущество использования ограничений в базе данных.
Я согласен, что строки сообщений об ошибках синтаксического анализа чувствуют себя «грязными», , но строки определены правильно . Можно даже ссылаться на основные errmsg.txt
или исходные файлы заголовков.
Как только вы извлекли имя ключа из сообщения об ошибке, можно использовать информационную схему KEY_COLUMN_USAGE
для идентификации злоумышленников:
public static final int ER_DUP_ENTRY = 1062;
public static final int ER_DUP_ENTRY_WITH_KEY_NAME = 1586;
public static final String REGEX_DUP_ENTRY_WITH_KEY_NAME =
"Duplicate entry '(.*)' for key '(.*)'";
// ...
try {
// ...
} catch (MySQLIntegrityConstraintViolationException e) {
switch (e.getErrorCode()) {
case ER_DUP_ENTRY:
case ER_DUP_ENTRY_WITH_KEY_NAME:
Pattern p = Pattern.compile(REGEX_DUP_ENTRY_WITH_KEY_NAME);
Matcher m = p.matcher(e.getMessage());
SQLQuery query = session.createSQLQuery(
" SELECT COLUMN_NAME" +
" FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE" +
" WHERE CONSTRAINT_SCHEMA = :schema" +
" AND CONSTRAINT_NAME = :key"
);
query.setString("schema", "my_schema");
query.setString("key" , m.group(2));
showDuplicateError(query.list());
break;
}
}
Вот PHP-версия ответа eggyal , используя MySQLi.
// Error: 1062 SQLSTATE: 23000 (ER_DUP_ENTRY) Message: Duplicate entry '%s' for key %d
// Error: 1586 SQLSTATE: 23000 (ER_DUP_ENTRY_WITH_KEY_NAME) Message: Duplicate entry '%s' for key '%s'
if($mysqli->errno === 1062 || $mysqli->errno === 1586)
{
if(preg_match("/Duplicate entry '(.*)' for key '(.*)'/", $mysqli->error, $matchArray) === 1)
{
$duplicatedValue = $matchArray[1];
$uniqueKeyName = $matchArray[2];
if(!($stmt = $mysqli->prepare('SELECT COLUMN_NAME'
. ' FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE'
. ' WHERE CONSTRAINT_SCHEMA = ?'
. ' AND CONSTRAINT_NAME = ?')))
{
die; // Error? Check $mysqli->errno and $mysqli->error;
}
$schemaName = // Name of the schema (string).
if(!$stmt->bind_param('ss', $schemaName, $uniqueKeyName))
{
die; // Error? Check $mysqli->errno and $mysqli->error;
}
if(!$stmt->execute())
{
die; // Error? Check $mysqli->errno and $mysqli->error;
}
$res = $stmt->get_result();
if(!$res)
{
die; // Error? Check $mysqli->errno and $mysqli->error;
}
$row = $res->fetch_assoc();
if($row === null)
{
die; // No results?
}
$columnName = $row['COLUMN_NAME'];
}
}