Проблемы с моей попыткой реализовать UPSERT

У меня возникла эта проблема при проверке условия для обновления таблицы в PostgreSQL. Он должен проверить, загрузил ли пользователь это один раз, и если да, добавить +1 в acessos.

<?php
$result2 = pg_query("SELECT * from downloads WHERE (nome = $_POST[nome_download] AND email = $_POST[email_download])");
if (pg_num_rows($result2) == 0){
$result = pg_query("INSERT INTO downloads (nome, email, estado, arquivo, acessos) VALUES ('$_POST[nome_download]','$_POST[email_download]','$_POST[estado_download]','$_SESSION[nome_arquivo_download]','1')");
}else{
$arr[acessos] = $arr[acessos] + 1;
$result = pg_query("UPDATE downloads SET acessos = $arr[acessos] WHERE (nome = $_POST[nome_download] AND email = $_POST[email_download])");
}


if (!$result){
echo "Não foi possível realizar o cadastro. Tente fazer o download mais tarde.";
}
else
{
echo "soft_bd";
pg_close();
}
?>

person Léo Eduardo Silva    schedule 03.01.2016    source источник
comment
Я не уверен, почему это не работает. В противном случае я даже не выполняю и меня беспокоит, что я не могу вставить одни и те же данные в PostgreSQL, если пользователь загрузит их более одного раза.   -  person Léo Eduardo Silva    schedule 03.01.2016
comment
У вас тоже проблема с SQL-инъекцией. Никогда никогда не вставляйте пользовательский ввод в инструкцию SQL. Кроме того, pg_num_rows может возвращать -1.   -  person user3427419    schedule 03.01.2016
comment
Вы убедились, что ваш оператор Select возвращает ожидаемое количество строк?   -  person ChrisF    schedule 03.01.2016
comment
Я ничего не знаю о PHP, но я бы убрал ненужные select. Запустите update в качестве первого оператора. Если ни одна строка не была обновлена, запустите вставку. Или если всего несколько пользователей скачивают что-то более одного раза, то сначала запустить вставку, поймать ошибку уникального ключа и сделать обновление. Какой из них быстрее, зависит от того, что происходит чаще: обновление или вставка. Если вы можете перейти на Postgres 9.5, вы можете использовать insert .. on conflict update, который делает все это безопасным для транзакций способом.   -  person a_horse_with_no_name    schedule 03.01.2016
comment
Я использовал if (!$result2) и получил ту же проблему. Но я проверю это, @ChrisF. Спасибо!   -  person Léo Eduardo Silva    schedule 03.01.2016
comment
используя это $rows = pg_num_rows($result2); эхо $строки. строки возвращены.\n; Я ничего не получил. Нет номера.   -  person Léo Eduardo Silva    schedule 03.01.2016


Ответы (1)


Вы ссылаетесь на $arr, но из вашего опубликованного кода не видно, где это назначено. В любом случае, если вы хотите увеличить текущее значение acessos на 1, этот подход совершенно небезопасен в многопользовательской среде.

Вы также полностью открыты для инъекций SQL. Вместо этого используйте подготовленные операторы.

В Postgres 9.5 это можно сделать даже с помощью одного оператора с новым Реализация UPSERT INSERT ... ON CONFLICT ON ... DO UPDATE — при условии наличия ограничения UNIQUE или PRIMARY KEY для (nome, email):

$sql = 'INSERT INTO downloads AS d (nome, email, estado, arquivo, acessos)
        VALUES ($1, $2, $3, $4, 1)
        ON CONFLICT ON (nome, email) DO UPDATE 
        SET    acessos = EXCLUDED.acessos + 1';

Для повторных вызовов вы можете использовать pg_prepare и pg_execute. Для одного вызова используйте pg_query_params:

pg_query_params($sql, array($_POST[nome_download]
                          , $_POST[email_download]
                          , $_POST[estado_download]
                          , $_SESSION[nome_arquivo_download]));
person Erwin Brandstetter    schedule 03.01.2016
comment
следовал вашим советам и теперь все работает. Спасибо вам всем! - person Léo Eduardo Silva; 03.01.2016