MySQL prepared statement не переносит изменение таблицы

    Upd: описанный ниже эффект проявляется только в MySQL ниже 5.1.25 — спасибо pharod.

    Случайно обнаружился интересный эффект, приводивший к багу в приложении:
    mysql> create table test(a int,b int);
    Query OK, 0 rows affected (0.11 sec)

    mysql> prepare ps from "select * from test";
    Query OK, 0 rows affected (0.00 sec)
    Statement prepared

    mysql> alter table test drop column b;
    Query OK, 0 rows affected (0.27 sec)
    Records: 0 Duplicates: 0 Warnings: 0

    mysql> execute ps;
    ERROR 1054 (42S22): Unknown column 'testdb.test.b' in 'field list'

    Кажется, что запрос не завязан на конкретную схему таблицы и может быть выполнен и после изменения схемы. В действительности prepared statement закладывается на список колонок, который был на момент создания statement.

    В реальной жизни проблема обнаружилась так: класс, отвечающий за общение с базой, кэширует prepared statements. Закэшированные statements поломались, когда потребовалось во время выполнения менять схему базы (не спрашивайте, зачем потребовалось: не всё в жизни делается так, как нам хочется). Будьте осторожнее!
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 9
    • +4
      Вполне очевидное поведение, достаточно лишь иметь понимание, что такое DDL и что такое Prepared
      • 0
        Такое поведение можно понять (хотя я бы не сказал, что оно очевидное), однако явных упоминаний этого я на сайте MySQL не нашёл. Например, тут про подобное поведение не сказано. Возможно, я плохо искал. Вы можете показать, где сказано, что prepare запоминает список колонок на момент создания statement?
        • 0
          извиняюсь, не туда нажал «ответить» и камент ушел выше уровнем.
      • +2
        вообще, prepare присутствует почти во всех СУБД (мы говорим про СУБД, а не про записные книжки с поддрежкой SQL-запросов). Его задача призвести один раз компиляцию запроса и хранить полученый байт-код в памяти (плюс еще обвесы разные в виде планов запроса), дабы не тратить CPU на эту, достаточно емкую, операцию. Так вот эта компиляция привязывается не к названиям столбцов и таблиц, а к внутренним идентификаторам. Плюс к этому, нотификация кеша об производимых DDL также весьма не быстрая вещь, потому и отсутствующая даже в самом оракле (ну не в полной мере конечно, но в большинстве случаев такое действо отсутствует). Засим имея простое понимание принципа работы prepare, зачем он вообще нужен и как он работает, ответы на подобные вопросы возникают сами собой.
        • 0
          Всё, что вы написали, действительно очевидно. Неочевидным, на мой взгляд, является тот факт, что при компиляции звёздочка разворачивается в полный список колонок. Вполне мог бы существовать байт-кодовый вариант, семантически эквивалентный выбору всех колонок.
          • 0
            Семантически это эквивалентно тому, что вы сами кешировали бы запросы в виде строк где-то у себя. И при изменении схемы Ваши кешированные запросы точно так же перестали бы работать.

            Байт-код — это форма хранения для быстрого выполнения кешированных запросов, а не способ хранения.
            • 0
              С астериском надо быть аккуратней. Вот, сделал репринт одной статьи «4 простых правила о NULL»

              halturin.blogspot.com/2010/07/four-rules-for-nulls.html
          • +1
            У Вас MySQL ниже 5.1.25. Ваш кэп.
            • 0
              Ага, спасибо! То есть авторы MySQL согласны, что такое поведение неочевидно. А то меня тут выше пытались убедить в обратном :-)

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