• Почему возникает SQLSTATE[HY000]: General error: 2014 при прямой вставке данных в MySQL таблицы через DBAL?

    @popov654 Автор вопроса
    Ипатьев,

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

    С чего это вы взяли, что мы ровесники? Вы вообще кто? Контрибьютоор в PHP? Как давно вы тут?

    мне кажется, что его нерегулярные подходы к этому вопросу имеют довольно банальное объяснение - он к нему возвращается сильно заложив за воротник

    Не надо судить людей по себе :) И кстати, где нерегулярность? Я не пропадал неделями, вроде.

    То есть "патч", который делает то, что и так есть

    И опять вы не правы. Этого нет. Точнее, есть, но только в PDO::exec(), для подготовленных запросов этого точно нет.

    Идеальный патч, на мой взгляд, хранил бы в драйвере пул всех открытых соединений, и закрывал все старые соединения при попытке открыть новое, даже если программист не закрыл курсор и не получил результаты. Это позволило бы не беспокоиться о получении ответа, когда он не нужен, и не ловить SQL state error 2014.

    Но это не самый простой патч, и потенциально он может привести к росту потребления памяти и не только. Поэтому вряд ли его когда-то реализуют.

    Что может быть проще, чем простой алгоритм: выполнил мультизапрос? Получи результаты всех последующих, и можешь двигаться дальше.

    Есть вероятность, что там доступен результат только последнего подзапроса. Но это опять же не точно, нужна проверка.

    Вообще вы оба с Камилем пропустили очевиднейшую вещь. Если бы вы её нашли, этот тред можно было бы закрыть в первый же день.

    А именно, это не особенность мультизапросов в PDO. Это так со всеми prepared запросами.

    Нельзя открывать новый, не закрыв старый, сделав unset() переменной или прочитав его результаты, и всё тут.

    Можно было сразу так и ответить - но вы не в ту степь все ушли. Ну и я конечно дурак, не проверил кейс с одиночными подготовленными запросами без очистки переменных.
    Написано
  • Почему возникает SQLSTATE[HY000]: General error: 2014 при прямой вставке данных в MySQL таблицы через DBAL?

    @popov654 Автор вопроса
    Akina, к сожалению разработчик в итоге обиделся и отправил меня гулять в путешествие. А жаль, я правда пытался понять, почему они так сделали.

    Пока что я в его ответах вижу, что он сам толком не понимает, как оно работает и почему так. Он всё время повторяет примерно это:

    а) Это глупая ненужная фича, которая всё усложняет, не используйте её. Она устроена хитро, но как, я точно не помню
    б) Программист должен вручную получать результат запроса и статус код и никогда не полагаться на то, что драйвер сделает авто-фетч с очисткой, автоочистка нужна по внутренним причинам и вообще сделана не для программистов
    в) Всё работает так, как задумано по дизайну, ошибок нет

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

    Может, конечно, я глупый, но по-моему, они сами не помнят, как оно у них работает сейчас и почему они оставили именно такую реализацию.

    Я пытался вчера модифицировать локально код драйвера так, чтобы он всегда вычитывал прошлый результат перед исполнением подготовленного запроса. Проблема в том, что это бесполезно в том случае, когда программист создаёт новое подготовленное выражение, не прочитав статус старого, и начинает его исполнять. А очистить сразу после выполнения - действительно нельзя, надо дать возможность получить разработчику результаты и статус.

    Другое дело, что в случае мультизапроса там скорее всего будет доступен статус только последнего SQL запроса, и драйвер не хранит все статусы массивом, то есть толку в этом кейсе не так много, но лучше так, чем не хранить ничего, плюс это упрощает реализацию драйвера и делает код простым и универсальным.

    В итоге я и сам не вижу способа это поправить для себя, сделав кастомную версию расширения. Кажется, это невозможно в принципе.
    Написано
  • Почему возникает SQLSTATE[HY000]: General error: 2014 при прямой вставке данных в MySQL таблицы через DBAL?

    @popov654 Автор вопроса
    Akina, спасибо :)
    Жаль что мои познания в C не позволят предложить патч, чтобы это пофиксить - я сейчас даже не представляю сходу, в каких кейсах это поведение может быть полезно, раз они считают его корректным и хотят сохранять дальше.

    We ask MySQL to prepare the statement, save the allocated resources internally, execute the statement immediately and then discard the PDOStatement object that was received from prepare(). When the object is discarded, there is still one status message pending on the connection, but mysqlnd silently fetches it and discards it.


    Если то, что объект моментально уничтожается, если не записать его в переменную, достаточно очевидно, и я видимо должен был это знать, то вот про "but mysqlnd silently fetches it and discards it" это большой сюрприз, про который не сказано даже в мануале.

    И я даже догадываюсь, почему до версии 8 поведение отличалось: там по дефолту скорее всего использовался не mysqlnd, а что-то иное (но это тоже надо гуглить, а не гадать).
    Написано
  • Почему возникает SQLSTATE[HY000]: General error: 2014 при прямой вставке данных в MySQL таблицы через DBAL?

    @popov654 Автор вопроса
    Ипатьев, он написал
    Fourth is to never assign the result object to any PHP variable.

    Что, конечно же, полный идиотизм. Вы в Java, например, такое видели где-нибудь, чтобы результат вызова двух методов зависел от того, присвоили ли мы результат первого вызова переменной?

    Я не буду говорить за MySQL драйвер, там теоретически может быть что-то похожее, раз есть этот нюанс с мультизапросами (хотя и то не факт, если драйвер написан на Java, а не на C/C++ через NDK). Но я больше про идею в целом.
    Написано
  • Почему возникает SQLSTATE[HY000]: General error: 2014 при прямой вставке данных в MySQL таблицы через DBAL?

    @popov654 Автор вопроса
    Ипатьев,
    объект, на который нет ссылок, уничтожается

    Он действительно уничтожается сборщиками мусора, как правило (почти всегда, за редкими исключениями). Вы не знали что ли об этом?
    Написано
  • Почему возникает SQLSTATE[HY000]: General error: 2014 при прямой вставке данных в MySQL таблицы через DBAL?

    @popov654 Автор вопроса
    Ипатьев, ну я же говорю, вы тут упорно изображаете клоуна уже который день подряд. Про то, что "зависит от настроек" - это опять ваш больной бред. Возьмите да проверьте на дефолтных (!) настройках там и там.
    Написано
  • Почему возникает SQLSTATE[HY000]: General error: 2014 при прямой вставке данных в MySQL таблицы через DBAL?

    @popov654 Автор вопроса
    Akina, включил логирование.

    В обоих сценариях логи идентичны:

    2024-06-27T13:26:48.661692Z	    8 Connect	dev@localhost on test_db using TCP/IP
    2024-06-27T13:26:48.662365Z	    8 Query	CREATE TABLE IF NOT EXISTS `test` (`id` INT NOT NULL, `name` VARCHAR(45) NULL, PRIMARY KEY (`id`));
    2024-06-27T13:26:48.671968Z	    8 Query	DELETE FROM `test`;
    2024-06-27T13:26:48.674903Z	    8 Query	INSERT INTO `test` VALUES(1, 'A'), (2, 'B')
    2024-06-27T13:26:48.676598Z	    8 Query	CREATE TABLE IF NOT EXISTS `test_a` (`id` INT NOT NULL, `name` VARCHAR(45) NULL, PRIMARY KEY (`id`));
    2024-06-27T13:26:48.679305Z	    8 Query	DELETE FROM `test_a`;
    2024-06-27T13:26:48.681422Z	    8 Query	INSERT INTO `test_a` VALUES(1, 'A'), (2, 'B')
    2024-06-27T13:26:48.683191Z	    8 Query	CREATE TABLE IF NOT EXISTS `test_b` (`id` INT NOT NULL, `name` VARCHAR(45) NULL, PRIMARY KEY (`id`));
    2024-06-27T13:26:48.684910Z	    8 Query	DELETE FROM `test_b`;
    2024-06-27T13:26:48.686423Z	    8 Query	INSERT INTO `test_b` VALUES(1, 'A'), (2, 'B')
    2024-06-27T13:26:48.687933Z	    8 Quit	
    
    2024-06-27T13:27:22.843952Z	    9 Connect	root@localhost on  using SSL/TLS
    2024-06-27T13:27:22.844173Z	    9 Query	set autocommit=1
    2024-06-27T13:27:22.844346Z	    9 Query	SELECT current_user()
    2024-06-27T13:27:22.844551Z	    9 Query	SET CHARACTER SET utf8
    2024-06-27T13:27:22.844694Z	    9 Query	SET NAMES utf8
    2024-06-27T13:27:22.844829Z	    9 Query	SELECT CONNECTION_ID()
    2024-06-27T13:27:22.845003Z	    9 Query	show character set where charset = 'utf8mb4'
    2024-06-27T13:27:22.848080Z	    9 Query	SET NAMES 'utf8mb4'
    2024-06-27T13:27:22.848289Z	    9 Query	SHOW SESSION STATUS LIKE 'Ssl_cipher'
    2024-06-27T13:27:22.850108Z	    9 Query	USE `catquest_prod`
    2024-06-27T13:27:22.850256Z	    9 Query	set autocommit=1
    2024-06-27T13:27:22.850925Z	    9 Query	DROP TABLE `catquest_prod`.`test`, `catquest_prod`.`test_a`, `catquest_prod`.`test_b`
    
    2024-06-27T13:28:42.488351Z	   10 Connect	dev@localhost on test_db using TCP/IP
    2024-06-27T13:28:42.488678Z	   10 Query	CREATE TABLE IF NOT EXISTS `test` (`id` INT NOT NULL, `name` VARCHAR(45) NULL, PRIMARY KEY (`id`));
    2024-06-27T13:28:42.504759Z	   10 Query	DELETE FROM `test`;
    2024-06-27T13:28:42.505702Z	   10 Query	INSERT INTO `test` VALUES(1, 'A'), (2, 'B')
    2024-06-27T13:28:42.507564Z	   10 Quit


    Нас интересуют ID 8 и 10 - ID 9 это удаление созданных таблиц через MySQL Workbench.
    Написано
  • Почему возникает SQLSTATE[HY000]: General error: 2014 при прямой вставке данных в MySQL таблицы через DBAL?

    @popov654 Автор вопроса
    При этом я вполне понимаю, что поскольку метод prepare() расширения нативный, а его исходников я не видел - технически он может вести учёт созданных им же Statement объектов и видеть, есть ли на них ссылки. То есть я понимаю, откуда такое поведение могло взяться.

    Но это же не означает, что это не есть глупость, к тому же не прописанная в доках :)
    Написано
  • Почему возникает SQLSTATE[HY000]: General error: 2014 при прямой вставке данных в MySQL таблицы через DBAL?

    @popov654 Автор вопроса
    Если кому-то всё ещё кажется, что проблема в моём незнании чего-либо, то вот два простых вопроса, чтобы понять, так ли это:

    1. Где эта разница в поведении описана в официальной документации?
    2. В других языках программирования такие случаи имеют место быть (Java, C#, Python, JS)?

    Если хотя бы на один вопрос можно ответить "да" - то окей, можно действительно говорить о том, что есть некая особенность, и багом это не является.

    P.S. На Java и JS я вполне себе пишу, если что. А говоря "такие случаи" я говорю не о том, как можно и нельзя отправлять SQL запросы, а о том, что поведение некого метода класса зависит от того, существуют ли ссылки на объект, которому он принадлежит :)
    Написано
  • Почему возникает SQLSTATE[HY000]: General error: 2014 при прямой вставке данных в MySQL таблицы через DBAL?

    @popov654 Автор вопроса
    Ипатьев,
    проверять свои выкладки

    Проверьте сами - весь код выложен и сюда, и на багтрекер. Более того, за вас уже по сути всё проверено - в каких случаях оно работает корректно, а в каких нет.

    Более того, описано даже, что будет, если поставить PHP 5/7 вместо PHP 8 (спойлер: будет плохо, но иначе).
    Написано
  • Почему возникает SQLSTATE[HY000]: General error: 2014 при прямой вставке данных в MySQL таблицы через DBAL?

    @popov654 Автор вопроса
    Ипатьев,
    Чувак забронзовел

    Это исключено, потому что я в "сыром" виде PDO вообще никогда в жизни не использовал - так что это не сформировавшаяся привычка, а просто здравый смысл.
    Написано
  • Почему возникает SQLSTATE[HY000]: General error: 2014 при прямой вставке данных в MySQL таблицы через DBAL?

    @popov654 Автор вопроса
    Akina, сейчас правда понял, что есть маленькая разница: без создания переменной мы не сможем написать так

    $conn->prepare($sql)->execute($params1)->execute($params2);

    потому что execute() возвращает bool, а не Statement.

    Но это ничего не значит, по большому счёту - в том плане, что из этого не следует, что интерпретатор должен эти случаи обрабатывать существенно разным образом.
    Написано
  • Почему возникает SQLSTATE[HY000]: General error: 2014 при прямой вставке данных в MySQL таблицы через DBAL?

    @popov654 Автор вопроса
    Akina, там один и тот же режим, потому что одна и та же версия PHP и MySQL и одна и та же строка запроса к СУБД, передающаяся методу. Если я конечно нигде ничего не упускаю :)

    Поведение точно не должно зависеть от того, сохранили ли мы Statement в переменную или нет - потому что в любом случае мы вызываем у этого объекта один и тот же метод с одним и тем же аргументом.
    Написано
  • Почему возникает SQLSTATE[HY000]: General error: 2014 при прямой вставке данных в MySQL таблицы через DBAL?

    @popov654 Автор вопроса
    Ипатьев,
    чтобы ему разжевали, как работает сборка мусора при удалении переменной

    Либо у вас с английским совсем плохо, либо вы не читали тред, либо опять дурачка включили - иначе я даже не знаю, как объяснить эти ваши выводы выше.

    Кстати, там в итоге оказалось, что оно вообще работает нормально без создания переменной - вот где реальный фейспалм.
    Написано
  • Экранирование sql запросов, достаточно ли функции?

    Ну вообще-то `real_escape_string()` как раз предназначена для экранирования строк в SQL запросах, что и относится к защите от инъекций. Должно помогать, возможно здесь не помогает потому, что параметр не заключён в одиночные кавычки.

    ИМХО гораздо надёжнее для одиночных целочисленных параметров использовать (int) $_GET['id']. Сам всегда так делал и ни разу не получил проблем.
    Написано
  • Почему возникает SQLSTATE[HY000]: General error: 2014 при прямой вставке данных в MySQL таблицы через DBAL?

    @popov654 Автор вопроса
    Akina, а вы можете внести ясность? Почему нет ошибки, и почему при мультизапросе без prepared() поведение иное?

    Раз мой пример отрабатывает с вызовом unset() - значит, действительно используется режим эмуляции. Если он используется - MySQL сервер получает несколько обычных запросов. То же самое происходит при использовании query(). Но результат разный, и это, кажется, можно объяснить только реализацией драйвера mysqlnd.
    Написано
  • Почему возникает SQLSTATE[HY000]: General error: 2014 при прямой вставке данных в MySQL таблицы через DBAL?

    @popov654 Автор вопроса
    Ипатьев,
    что чтобы получить ошибку, результат надо получить

    Я это делал, ошибки нет. Кроме того, все строки вставляются. Вы вообще читаете что я пишу?

    ваши инсерты работают потому что в режиме эмуляции

    Если в режиме эмуляции, там тем более не должно быть никакой ошибки (т.к. они для MySQL сервера уже обычные, а не prepared).

    потому что вы не запросили результаты своих запросов, и они остались ждать на сервере

    Но для единичного запроса без точек с запятой, который не prepared(), я могу ничего не получать, и при этом я ранее не ловил на нём ошибку. Это мне так везло (я обычно переиспользовал старую переменную), или там действительно её нет? Мне почему-то кажется, что второе, т.к. в ряде случае я использовал и разные переменные в древнем коде ещё с mysql расширением.

    которое пытаетесь формулировать через какой-то детский лепет про "параллельное выполнение"

    Вы вообще как, нормально себя чувствуете? Где я такое писал?

    по завершении работы функции всем её переменным делается unset

    Я и сам об этом догадался. На самом деле, это не совсем очевидно, теоретически в языке со сборщиком мусора переменные могут быть сброшены позже, во время плановой очистки памяти. Ну ок, теперь буду знать, что локальные переменные в PHP очищаются сразу.

    при этом куда проще выполнять запросы по одному: тогда не нужно проверять результат каждого вручную

    Ну так вы самое главное и не объяснили. В чём отличие одиночного запроса? Там тоже может прийти результат - но там его вручную проверять не нужно.
    Написано
  • Почему возникает SQLSTATE[HY000]: General error: 2014 при прямой вставке данных в MySQL таблицы через DBAL?

    @popov654 Автор вопроса
    Ипатьев, а, я перепутал ресурс, в самом деле. Извините.

    Но вы плохо прочитали, наверное, там именно человек пишет, что результат - это не только результат в виде набора строк, но в том числе и код ошибки, например. Где я что переврал?
    Написано
  • Почему возникает SQLSTATE[HY000]: General error: 2014 при прямой вставке данных в MySQL таблицы через DBAL?

    @popov654 Автор вопроса
    Ипатьев, так я всё ещё не понимаю. Если prepared запросы нельзя делать составными, как мои INSERT-ы работают, ещё и без ошибки?
    Написано