RussianLDP Рейтинг@Mail.ru
WebMoney: 
WMZ Z294115950220 
WMR R409981405661 
WME E134003968233 
Visa 
4274 3200 2453 6495 

Глава 26. Расширение MySQL

26.1. MySQL изнутри

Эта глава описывает много вещей, которые Вы должны знать, работая над кодом MySQL. Чтобы отследить или способствовать развитию MySQL, следуйте инструкциям в разделе 2.8.3. Если Вы интересуетесь внутренностями MySQL, Вы должны также подписаться на наш список рассылки internals. У этого списка относительно низкий трафик. Для деталей о том, как подписаться, пожалуйста, см. раздел 1.6.1. Много разработчиков MySQL в Oracle Corporation читают список internals и мы помогаем другим людям, которые работают над кодом MySQL. Не стесняйтесь использовать этот список и чтобы задать вопросы о коде и послать участки, которые Вы хотели бы внести в проект MySQL!

Исходный код MySQL содержит внутреннюю документацию, использующую Doxygen. Эта документация полезна для понимания, как MySQL работает с точки зрения разработчика. Произведенный контент Doxygen доступен в http://dev.mysql.com/doc/dev/mysql-server/latest/. Также возможно произвести этот контент локально из исходного дистрибутива MySQL, используя инструкции в разделе 2.8.7 .

26.1.1. Потоки MySQL

Сервер MySQL создает следующие потоки:

  • Менеджер соединений распараллеливает запросы соединения клиента на сетевых интерфейсах. На всех платформах один поток менеджера обрабатывает запросы соединения TCP/IP. На Unix этот поток менеджера также обрабатывает запросы соединения файла сокета Unix. В Windows поток менеджера обрабатывает запросы соединения совместно используемой памяти и соединение именованного канала. Сервер не создает потоки, чтобы обработать интерфейсы, которые он не слушает. Например, Windows server, у которого нет поддержки соединений по именованному каналу, не создает поток, чтобы обработать их.

  • Менеджер соединений распараллеливает каждое соединение клиента с потоком, посвященным этому, который обрабатывает аутентификацию и запросы для этого соединения. Потоки менеджера создают новую ветвь когда необходимо, но пытаются избежать этого, сначала связываясь с кэшем потока, чтобы видеть, содержит ли он поток, который может использоваться для соединения. Когда соединение заканчивается, его поток возвращен к кэшу потока, если кэш не полон.
  • На основном сервере репликации соединения от ведомых серверов обработаны как соединения клиента: есть один поток на соединенное ведомое устройство.
  • На ведомом сервере поток ввода/вывода запущен, чтобы соединиться с главным сервером и считать с него обновления. Поток SQL запущен, чтобы применить обновления, считанные от ведущего устройства. Эти два потока работают независимо и могут быть запущены и остановлены независимо.
  • Поток сигнала обрабатывает все сигналы. Этот поток также обычно обрабатывает аварийные сигналы и требования process_alarm() вызвать тайм-ауты на соединениях, которые были неактивны слишком долго.
  • Если применяется InnoDB, будут дополнительные потоки чтения и записи по умолчанию. Числом их управляют параметры innodb_read_io_threads и innodb_write_io_threads. См. раздел 16.13.
  • Если сервер запущен с опцией --flush_time=val , специализированный поток создается, чтобы сбрасывать все таблицы каждые val секунд.
  • Если планировщик событий является активным, есть один поток для планировщика и поток для каждого случая, в настоящее время работающего. См. раздел 21.4.1.

mysqladmin processlist показывает потоки соединений, репликации и событий.

26.1.2. MySQL Test Suite

Испытательная система, которая включена в двоичные дистрибутивы, позволяет пользователям и разработчикам выполнить тесты на коде MySQL. Эти тесты могут быть выполнены на Unix.

Вы можете также написать свои собственные прецеденты. Для информации об испытательной структуре MySQL, включая системные требования, см. руководство, доступное на http://dev.mysql.com/doc/mysqltest/2.0/en/.

Текущий набор прецедентов не проверяет все в MySQL, но это должно поймать большинство очевидных ошибок в SQL, обрабатывающем коде, операционной системе или проблемы библиотек и довольно полно в тестировании репликации. Наша цель состоит в том, чтобы иметь испытательное покрытие 100% кода. Мы приветствуем вклады в наш испытательный набор. Вы можете особенно хотеть внести тесты, которые исследуют функциональность, важную по отношению к Вашей системе, потому что это гарантирует, что весь будущий MySQL будет хорошо работать с Вашими приложениями.

Испытательная система состоит из испытательного языкового интерпретатора (mysqltest), Perl-скрипта, чтобы выполнить все тесты (mysql-test-run.pl), фактические прецеденты, написанные на специальном испытательном языке, и их ожидаемые результаты. Чтобы выполнить испытательный набор на Вашей системе после создания, введите make test из корневого каталога исходных текстов или перейдите в каталог mysql-test и введите ./mysql-test-run.pl. Если Вы установили двоичный дистрибутив, измените местоположение на каталог mysql-test (например, /usr/local/mysql/mysql-test) и запустите ./mysql-test-run.pl. Все тесты должны пройти успешно. Если что-либо не работает, не стесняйтесь пытаться узнать, почему и сообщите о проблеме, если она указывает на ошибку в MySQL. См. раздел 1.7.

Если один тест терпит неудачу, Вы должны выполнить mysql-test-run.pl с опцией --force, чтобы проверить, терпят ли какие-либо другие тесты неудачу.

Если у Вас есть копия mysqld , работающего на машине, где Вы хотите выполнить испытательный набор, Вы не должны останавливать его, пока это не использует порты 9306 или 9307. Если любой из этих портов занят, Вы должны установить переменную окружения MTR_BUILD_THREAD к соответствующему значению, и испытательный набор будут использовать иной набор портов для ведущего, ведомого устройства и NDB. Например:

shell> export MTR_BUILD_THREAD=31
shell> ./mysql-test-run.pl [options] [test_name]
В каталоге mysql-test Вы можете выполнить отдельный прецедент с помощью ./mysql-test-run.pl test_name.

Если Вы имеете вопрос об испытательном наборе или полезный прецедент, пошлите электронное письмо в список рассылки MySQL internals. См. раздел 1.6.1.

26.2. MySQL Plugin API

MySQL поддерживает API, который позволяет создание серверных компонентов. Плагины могут быть загружены при запуске сервера или загружены и выгружены во время выполнения, не перезапуская сервер. API является глобальным и не определяет то, что могут сделать плагины. Компоненты, поддержанные этим интерфейсом, включают, но не ограничены, механизмы хранения, полнотекстовые плагины анализатора и расширения сервера.

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

Интерфейс является более общим, чем более старый интерфейс определяемых пользователем функций (UDF).

Интерфейс использует таблицу plugin в базе данных mysql, чтобы сделать запись информации о плагинах, которые были установлены надолго оператором INSTALL PLUGIN. Эта таблица составлена как часть процесса установки MySQL. Плагины могут также быть установлены кратко опцией --plugin-load. Плагины, установленные этим путем, не зарегистрированы в таблице plugin. См. раздел 6.6.2.

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

Исходные тексты MySQL содержат внутреннюю документацию, созданную с помощью Doxygen. Эта документация полезна для понимания, как MySQL работает с точки зрения разработчика. Произведенный контент Doxygen доступен на http://dev.mysql.com/doc/dev/mysql-server/latest/. Также возможно воспроизвести этот контент локально, используя инструкции в разделе 2.8.7.

26.2.1. Типы плагинов

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

Следующие разделы обеспечивают краткий обзор этих типов.

Плагины механизма хранения

Архитектура механизма хранения, используемая сервером MySQL, позволяет механизмам хранения быть написанными как плагины и загруженными в процессе работы сервера. Для описания этой архитектуры, см. раздел 17.11.

Для информации о том, как использовать API, чтобы написать механизм хранения, см. MySQL Internals: Writing a Custom Storage Engine.

Полнотекстовые плагины анализатора

У MySQL есть встроенный анализатор, который он использует по умолчанию для полнотекстовых операций (разбор текста, который будет индексирован, или разбор строки запроса, чтобы определить параметры, которые будут использоваться для поиска). Встроенный полнотекстовый анализатор поддержан для таблиц InnoDB и MyISAM.

Символьно-ориентированный полнотекстовый анализатор, который поддерживает китайский, японский и корейский (CJK), и основанный на слове плагин анализатора MeCab, который поддерживает японский язык, для использования с таблицами InnoDB и MyISAM.

"Для полнотекстовой обработки" означает извлечение слов (или токенов, в случае символьно-ориентированного анализатора) из текста или строки запроса, основываясь на правилах, которые определяют, какие символьные последовательности составляют слово и где границы слова.

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

Свойства парсинга встроенного полнотекстового анализатора описаны в разделе 13.9. Эти свойства включают правила для того, чтобы определить, как извлечь слова из текста. Анализатор под влиянием определенных системных переменных исключает слова короче или длиннее определенного размера и обрабатывает список стоп-слов, который идентифицирует общие слова, которые будут проигнорированы. Для получения дополнительной информации, см. разделы 13.9.4 и 13.9.6.

API позволяет Вам использовать свой полнотекстовый анализатор. Например, если Вы работаете с японским языком, Вы можете хотеть использовать полнотекстовый анализатор MeCab. API также позволяет Вам обеспечить полнотекстовый собственный анализатор так, чтобы Вы управляли основными режимами работы анализатора. Плагин анализатора может работать в любой из двух ролей:

  • Плагин может заменить встроенный анализатор. В этой роли плагин читает ввод, который будет разобран, разделяет это на слова и передает слова серверу (для того, чтобы индексировать или для маркерного накопления). Парсеры ngram и MeCab действуют в качестве замены для встроенного полнотекстового анализатора.

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

  • Плагин может действовать в соединении со встроенным анализатором, служа фронтэндом для него. В этой роли плагин извлекает текст из ввода и передает его анализатору, который разделяет текст на слова, используя нормальные правила парсинга. Этот парсинг затронут системными переменными innodb_ft_xxx или ft_xxx и списком стоп-слов.

    Одна причина использовать анализатор, состоит в том, что Вы должны индексировать контент, такой как документы в формате PDF, XML-документы или файлы .doc. Встроенный анализатор не предназначен для этих типов ввода, но плагин может вытащить текст из этих входных источников и передать его встроенному анализатору.

Для плагина анализатора также возможно работать в обеих ролях. Таким образом, это может извлечь текст из ввода какого-то сложного формата (роль фронтэнда), а также разобрать текст на слова (таким образом заменяя встроенный анализатор).

Полнотекстовый плагин связан с полнотекстовым индексированием. Таким образом, когда Вы устанавливаете плагин анализатора первоначально, он не будет использоваться для любых полнотекстовых операций. Это просто становится доступным. Например, полнотекстовый плагин анализатора становится доступным для вызова в предложении WITH PARSER для создания конкретного индекса FULLTEXT. Чтобы создать такой индекс во время создания таблицы, сделайте это:

CREATE TABLE t
(
  doc CHAR(255),
  FULLTEXT INDEX (doc) WITH PARSER parser_name
) ENGINE=InnoDB;
Или Вы можете добавить индексирование после того, как таблица была составлена:
ALTER TABLE t ADD FULLTEXT INDEX (doc) WITH PARSER parser_name;
Единственное изменение SQL для того, чтобы связать анализатор с индексированием: предложение WITH PARSER. Поиски определены как прежде, без изменений, необходимых для запросов.

Когда Вы связываете плагин анализатора с индексом FULLTEXT, плагин требуется для того, чтобы использовать индексирование. Если плагин анализатора удален, любой связанный с ним индекс, становится непригодным. Любая попытка использовать таблицу, для которой плагин недоступен, приведет к ошибке, хотя DROP TABLE все еще возможно.

Демоны

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

Плагины INFORMATION_SCHEMA

Плагины INFORMATION_SCHEMA позволяют создание таблиц, содержащих метаданные о сервере, которые выставлены пользователям через базу данных INFORMATION_SCHEMA. Например, InnoDB применяет плагин INFORMATION_SCHEMA, чтобы обеспечить таблицы, которые содержат информацию о текущих транзакциях и блокировках.

Плагины репликации

Репликация MySQL является асинхронной по умолчанию. С полусинхронной передача выполненного на ведущей стороне делается прежде, чем возвратиться к сеансу, который выполнил транзакцию, пока по крайней мере одно ведомое устройство не признает, что получило и зарегистрировало события. Полусинхронная репликация осуществлена через дополнительные плагины ведущего устройства и клиента. См. раздел 19.3.10.

Плагины аудита

Сервер MySQL обеспечивает контрольный интерфейс, который позволяет сообщать информацию об операциях сервера заинтересованным сторонам. Контрольное уведомление происходит для этих операций (хотя интерфейс является общим, и сервер мог быть изменен, чтобы сообщить о других):

  • Запись сообщения в общий журнал запроса (если журнал включен).

  • Запись сообщения в журнал ошибок.
  • Отправка результата запроса клиенту.

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

Этот интерфейс позволяет контрольным плагинам получить уведомления только об операциях в классах событий, которые они считают существенными и проигнорировать другие. Интерфейс предусматривает классификацию операций в классы событий и дальнейшее разделение на подклассы событий в пределах каждого класса.

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

Плагины аутентификации

MySQL поддерживает подключаемую аутентификацию. Плагины аутентификации существуют на стороне сервера и на стороне клиента. Плагины на стороне сервера осуществляют методы аутентификации для использования клиентами, когда они соединяются с сервером. Плагин на стороне клиента общается с серверным плагином, чтобы предоставить информацию аутентификации, которую это запрашивает. Клиентский плагин может взаимодействовать с пользователем, выполняя задачи, такие как запрос пароля или других параметров аутентификации, которые будут посланы серверу. См. раздел 7.3.9.

Плагины проверки допустимости пароля

Сервер MySQL обеспечивает интерфейс для того, чтобы созадть плагин контроля паролей. Такой плагин осуществляет две задачи:

  • Отклонение слабых паролей в запросах, которые назначают пароли (CREATE USER, GRANT и SET PASSWORD) и паролей, заданных как параметр функции PASSWORD().

  • Оценка силы потенциальных паролей для функции SQL VALIDATE_PASSWORD_STRENGTH().

Плагины трассировки протокола

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

Плагины перезаписи запроса

Сервер MySQL поддерживает плагины перезаписи запроса, которые могут исследовать и возможно изменить запросы, полученные сервером прежде, чем сервер выполнит их. Плагин перезаписи запроса берет запросы прежде или после того, как сервер разобрал их.

У плагина перезаписи запроса перед разбором есть эти характеристики:

  • Плагин позволяет переписать запросы SQL, достигающие сервера прежде, чем сервер обработает их.

  • Плагин получает строку запроса и может возвратить иную строку.

У плагина перезаписи запроса после разбора есть эти характеристики:

  • Плагин позволяет перезапись, основанную на деревьях разбора.

  • Сервер разбирает каждый запрос и передает его дерево разбора к плагину, который может переделать дерево. Плагин может возвратить оригинальное дерево серверу для дальнейшей обработки или создать иное дерево и вернуть его.
  • Плагин может использовать сервис mysql_parser в этих целях:
    • Активировать вычисление обзора запроса и получить нормализованную версию запросов, независимых от того, производит ли Performance Schema обзоры.

    • Переделывать деревья разбора.
    • Разобрать запрос. Это полезно, если плагин создает новую строку запроса из дерева разбора. У плагина может быть серверный разбор строки, чтобы произвести новое дерево, затем возвратить то дерево как представление переписанного запроса.

Плагины перезаписи запроса до и после разбора имеют эти характеристики:

  • Если плагин перезаписи запроса установлен, опция --log-raw затрагивает протоколирование запросов так:

    • Без --log-raw сервер регистрирует запрос, возвращенный плагином перезаписи запроса. Это может отличаться от изначального запроса.

    • С --log-raw сервер регистрирует оригинальный запрос как получен.

  • Если плагин переписывает запрос, сервер решает, написать ли это в двоичный журнал регистрации (и таким образом каким-либо ведомым устройствам). Если плагин переписывает только запросы SELECT в запросы SELECT, нет никакого воздействия на двоичное журналирование, потому что сервер не пишет в него запросы SELECT.

  • Если плагин переписывает запрос, сервер производит сообщение Note, что клиент может рассмотреть, применяя SHOW WARNINGS. У сообщений есть формат, где stmt_in оригинал запроса, а stmt_out переписанный запрос:
    Query 'stmt_in' rewritten to 'stmt_out' by a query rewrite plugin
    

Дистрибутивы MySQL включают плагин Rewriter перезаписи запроса после разбора. Этот плагин базируется на правилах. Вы можете добавить строки к его таблице правил, чтобы вызвать перезапись запроса SELECT.

Плагины перезаписи запроса используют тот же самый API, что и контрольные плагины. Для получения дополнительной информации о контрольных плагинах см. раздел 26.2.4.8.

Плагины ключей

Сервер MySQL поддерживает плагины ключей, которые позволяют внутренним серверным компонентам и плагинам надежно хранить чувствительную информацию для последующего извлечения.

Все дистрибутивы MySQL включают такой плагин keyring_file. См. раздел 7.5.3.

26.2.2. Характеристики API

У API плагинов сервера есть эти характеристики:

  • У всех плагинов есть несколько общих черт.

    У каждого плагина есть имя, по которому он может быть упомянут в запросах SQL, а также другие метаданные (например, автор и описание), которые предоставляют другую информацию. Эта информация может быть исследована в таблице INFORMATION_SCHEMA.PLUGINS или SHOW PLUGINS.

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

    Хотя некоторые аспекты API характерны для всех типов плагинов, API также разрешает определенные для типа элементы интерфейса так, чтобы различные типы плагинов могли быть созданы. У плагина с одной целью может быть интерфейс, самый соответствующий его собственным требованиям а не требованиям некоторого другого типа.

    Интерфейсы для нескольких типов плагинов существуют, такие как механизмы хранения, полнотекстовый анализатор и таблицы INFORMATION_SCHEMA. Другие могут быть добавлены.

  • Плагины могут выставить информацию пользователям.

    Плагин может осуществить систему и переменные состояния, которые доступны через операторы SHOW VARIABLES и SHOW STATUS.

  • API включает информацию о версии.

    Информация о версии, включенная в API, позволяет библиотеке и каждому плагину, который ее содержит самоидентифицироваться относительно версии API, которая использовалась, чтобы создать библиотеку. Если API изменится в течение долгого времени, то номера версии изменятся, но сервер может исследовать информацию о версии данной библиотеки, чтобы определить, поддерживает ли он плагины в библиотеке.

    Есть два типа номеров версии. Первой является версия для общей сменной структуры непосредственно. Каждая сменная библиотека включает этот вид номера версии. Второй тип версии относится к отдельным плагинам. У каждого определенного типа плагина есть версия для интерфейса, таким образом, у каждого плагина в библиотеке есть определенный для типа номер версии. Например, у библиотеки, содержащей полнотекстовый плагин анализатора, есть общий сменный номер версии API, и у плагина есть номер версии, определенный для полнотекстового интерфейса.

  • API осуществляет ограничения безопасности.

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

  • У плагинов есть доступ к службам сервера.

    Интерфейс служб выставляет функциональность сервера, так что плагины могут получить доступ к обычным вызовам функций. Для деталей см. раздел 26.3.

В некотором отношении, у плагина сервера, API подобен более старой определяемой пользователем функции (UDF), но у API есть несколько преимуществ. Например, у UDFs не было никакой информации о версии. Кроме того, более новый интерфейс устраняет вопросы безопасности. Более старый интерфейс для того, чтобы написать UDF разрешал библиотекам быть загруженными из любого каталога, просматриваемого динамическим компоновщиком системы, а символы, которые идентифицировали библиотеку UDF, были относительно неопределенными.

У плагина клиента есть подобные архитектурные характеристики, но плагины клиента не имеют никакого прямого доступа к серверу.

26.2.3. Компоненты Plugin API

Реализация плагина сервера включает несколько компонентов.

Запросы SQL

  • INSTALL PLUGIN регистрирует плагин в таблице mysql.plugin и загружает код.

  • UNINSTALL PLUGIN отменяет регистрацию в таблице mysql.plugin и выгружает код.
  • Предложение WITH PARSER для полнотекстового индексирования создает связь полнотекстового плагина анализатора с данным индексом FULLTEXT.
  • SHOW PLUGINS показывает сведения о плагинах сервера.

Параметры командной строки и системные переменные:

  • --plugin-load позволяет плагинам быть загруженными во время запуска сервера.

  • Системная переменная plugin_dir указывает на местоположение каталога, где все плагины должны быть установлены. Значение этой переменной может быть определено при запуске сервера с помощью опции --plugin_dir=dir_name . mysql_config --plugindir отображает путь к каталогу плагинов по умолчанию.

Связанные с плагинами таблицы:

  • Таблица INFORMATION_SCHEMA.PLUGINS содержит информацию о плагинах.

  • Таблица mysql.plugin приводит каждый плагин, который был установлен с INSTALL PLUGIN и требуется для его использования. Для новых установок MySQL эта таблица составлена во время процесса установки.

Выполнение плагина клиента более просто:

  • Для функции C API mysql_options() опции MYSQL_DEFAULT_AUTH и MYSQL_PLUGIN_DIR позволяют программам клиента загрузить плагины аутентификации.

  • Есть C API функции, которые включают управлению плагинами клиента.

Чтобы исследовать, как MySQL осуществляет плагины, консультируйтесь со следующими исходными файлами в дистрибутиве исходых текстов MySQL:

  • В каталоге include/mysql plugin.h выставляет общественный API. Этот файл должен быть исследован любым, кто хочет написать библиотеку плагинов. Файлы plugin_xxx .h обеспечивают дополнительную информацию, которая принадлежит к определенным типам плагинов. client_plugin.h содержит информацию, определенную для плагинов клиента.

  • В каталоге sql файлы sql_plugin.h и sql_plugin.cc реализуют плагины. sql_acl.cc то место, где сервер использует плагины аутентификации. С этими файлами не должны консультироваться разработчики. Они могут представлять интерес для тех, кто хочет знать больше о том, как сервер обрабатывает плагины.
  • В каталоге sql-common client_plugin.h реализует функции C API плагина клиента и client.c реализует поддержку аутентификации клиента. С этими файлами не должны консультироваться разработчики. Они могут представлять интерес для тех, кто хочет знать больше о том, как сервер обрабатывает плагины.

26.2.4. Написание плагинов

Чтобы создать библиотеку плагинов, Вы должны предоставить запрошенную дескрипторную информацию, которая указывает на то, какие плагины файл библиотеки содержит, и написать функции интерфейса для каждого плагина.

У каждого плагина сервера должен быть общий дескриптор, который предоставляет информацию API, и определенный для типа дескриптор, который предоставляет информацию об интерфейсе для данного типа плагина. Структура общего дескриптора одинакова для всех типов плагинов. Структура определенного для типа дескриптора изменяется от типа к типу и определена требованиями того, что должен сделать плагин. Интерфейс плагина сервера также позволяет плагинам выставить системные переменные и состояние. Эти переменные становятся видимыми через операторы SHOW STATUS и SHOW VARIABLES, а также соответствующие таблицы в INFORMATION_SCHEMA.

Для клиентских плагинов архитектура немного отличается. У каждого плагина должен быть дескриптор, но нет никакого подразделения на отдельные общие и определенные для типа дескрипторы. Вместо этого дескриптор начинается с фиксированного набора членов, характерных для всех типов плагина клиента, общие члены сопровождаются дополнительными, требуемыми, чтобы осуществить определенный тип плагина.

Вы можете написать плагины на C или C++ (или другом языке, который может использовать соглашение о вызовах C). Плагины загружены и выгружены динамически, таким образом, Ваша операционная система должна поддерживать динамическую загрузку, и Вы должны скомпилировать вызывающее приложение динамически (не статически). Для плагинов сервера это означает, что mysqld должен быть собран динамически.

Плагин сервера содержит код, который становится частью рабочего сервера, так что, когда Вы пишете плагин, Вы связаны всеми ограничениями, которые относятся к написанию кода сервера. Например, у Вас могут быть проблемы, если Вы пытаетесь использовать функции из библиотеки libstdc++. Эти ограничения могут измениться в будущих версиях сервера, таким образом, возможно, что обновления сервера потребуют пересмотра плагинов, первоначально написанных для более старых серверов. Для информации об этих ограничениях см. разделы 2.8.4 и 2.8.5.

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

26.2.4.1. Обзор разработки плагинов

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

  1. В исходном файле включайте заголовочные файлы, в которых нуждается библиотека. Файл plugin.h требуется, и библиотека могла бы потребовать других файлов также. Например:

    #include <stdlib.h>
    #include <ctype.h>
    #include <mysql/plugin.h>
    
  2. Настройте информацию о дескрипторе для файла библиотеки. Для плагинов сервера напишите дескриптор библиотеки, который должен содержать общий дескриптор для каждого плагина сервера в файле. Для получения дополнительной информации, см. раздел 26.2.4.2.1. Кроме того, настройте определенный для типа дескриптор для каждого плагина сервера в библиотеке. Общий дескриптор каждого плагина указывает на его определенный для типа дескриптор.

    Для плагинов клиента напишите дескриптор клиента. Для получения дополнительной информации, см. раздел 26.2.4.2.3.

  3. Напишите функции интерфейса для каждого плагина. Например, общий дескриптор каждого плагина указывает на функции инициализации и завершения, которые сервер должен вызвать, когда загружает и выгружает плагин. Определенное для типа описание плагина может также указать на функции интерфейса.
  4. Для плагинов сервера установите состояние и системные переменные, если они есть.
  5. Соберите библиотеку как совместно используемую библиотеку и установите ее в каталоге плагинов. Для получения дополнительной информации, см. раздел 26.2.4.3.
  6. Для плагинов сервера зарегистрируйте плагин на сервере. Для получения дополнительной информации, см. раздел 6.6.2.
  7. Протестируйте плагин, чтобы проверить, что он работает должным образом.

26.2.4.2. Структуры данных плагинов

Файл библиотеки плагинов включает информацию об описателе (дескрипторе), чтобы указать на то, какие плагины это содержит.

Если библиотека содержит какие-либо плагины сервера, она должна включать следующую информацию об описателе (дескрипторе):

  • Описатель библиотеки указывает на общий номер версии API плагина сервера, используемый библиотекой, и содержит общий дескриптор для каждого плагина сервера в библиотеке. Чтобы предоставить основу для этого описателя, вызовите два макроопределения из файла заголовка plugin.h:

    mysql_declare_plugin(name)
     ... один или более дескрипторов здесь ...
    mysql_declare_plugin_end;
    
    Макроопределение расширяется, чтобы обеспечить декларацию для версии API автоматически. Вы должны обеспечить описатели (дескрипторы).
  • В пределах описателя библиотеки каждый общий плагин сервера описан структурой st_mysql_plugin. Эта сменная дескрипторная структура содержит информацию, которая характерна для каждого типа плагина сервера: значение, которое указывает на тип; имя, автор, описание и тип лицензии; указатели на функции инициализации и завершения, которые вызывает сервер, когда загружает и выгружает плагин, и указатели на любое состояние или системные переменные плагина.
  • Каждый общий описатель плагина сервера в пределах описателя библиотеки также содержит указатель на определенный для типа описатель. Структура определенных для типа описателей изменяется от одного типа к другому, потому что у каждого типа плагина может быть свой собственный API. Определенный для типа описатель содержит определенный для типа номер версии API и указатели на функции, которые необходимы, чтобы осуществить этот тип. Например, у полнотекстового плагина анализатора есть функции инициализации и завершения и основная функция парсинга. Сервер вызывает эти функции, когда он использует плагин, чтобы разобрать текст.

Библиотека также содержит функции интерфейса, на которые ссылаются общие и определенные для типа описатели для каждого плагина в библиотеке.

Если библиотека содержит плагин клиента, она должна включать описатель для плагина. Описатель начинается с фиксированного набора членов, характерных для всех плагинов клиента, сопровождаемых любыми членами, определенными для конкретного типа плагина. Чтобы создать основу дескриптора, вызовите два макроопределения из файла заголовка client_plugin.h:

mysql_declare_client_plugin(plugin_type)
   ... члены для всех клиентских плагинов ...
   ... Дополнительные члены для конкретного типа ...
mysql_end_client_plugin;
Библиотека также содержит любые функции интерфейса, на которые ссылается описатель клиента.

Макросы mysql_declare_plugin() и mysql_declare_client_plugin() отличается несколько в том, как они могут быть вызваны, у кого есть значения для содержания библиотек. Следующие замечания суммируют правила:

  • mysql_declare_plugin() и mysql_declare_client_plugin() могут оба использоваться в том же самом исходном файле, что означает, что библиотека может содержать серверные и клиентские плагины. Однако, каждый из mysql_declare_plugin() и mysql_declare_client_plugin() может использоваться самое большее однажды.

  • Многократные декларации плагина сервера mysql_declare_plugin() разрешены, таким образом, библиотека может содержать много плагинов сервера.
  • Допустима только единственная декларация плагина клиента mysql_declare_client_plugin(). Чтобы создать много плагинов клиента, нужно пользоваться разными библиотеками.

Когда программа клиента ищет плагин клиента, который находится в библиотеке и не создан в libmysqlclient, это ищет файл с базовым именем, которое является тем же самым, что и имя плагина. Например, если программа должна использовать плагин аутентификации клиента, названный auth_xxx в системе, которая использует суффикс библиотеки .so, будет проводиться поиск файла с именем auth_xxx.so. В OS X программа сначала ищет auth_xxx.dylib, а уже потом auth_xxx.so. Поэтому, если библиотека содержит плагин клиента, у библиотеки должно быть то же самое базовое имя, как и у плагина.

То же самое неверно для библиотеки, которая содержит плагины сервера. Опция --plugin-load и оператор INSTALL PLUGIN обеспечивает имя файла библиотеки явно, таким образом, нет никаких явных отношений между именем библиотеки и названием любых плагинов сервера, которые она содержит.

26.2.4.2.1. Библиотека плагина сервера и дескрипторы плагинов

Каждая библиотека, которая содержит плагины сервера, должна включать описатель библиотеки, который содержит общий дескриптор для каждого плагина сервера в файле. Этот раздел обсуждает, как написать библиотеку и общий дескриптор для плагинов сервера.

Описатель библиотеки должен определить два символа:

  • _mysql_plugin_interface_version_ определяет номер версии общей структуры. Это дано символом MYSQL_PLUGIN_INTERFACE_VERSION, который определен в файле plugin.h.

  • _mysql_plugin_declarations_ определяет массив деклараций плагинов, законченных декларацией со всеми членами, установленными в 0. Каждая декларация является экземпляром структуры st_mysql_plugin (тоже определена в plugin.h). Должен быть один экземпляр для каждого плагина сервера в библиотеке.

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

Обычный способ определить два необходимых символа при использовании макросов mysql_declare_plugin() и mysql_declare_plugin_end из файла plugin.h:

mysql_declare_plugin(name)
 ... Один или более дескрипторов плагинов ...
mysql_declare_plugin_end;
У каждого плагина сервера должен быть общий описатель, который предоставляет информацию API плагинов сервера. У общего описателя есть та же самая структура для всех типов плагинов. Структура st_mysql_plugin в файле plugin.h определяет этот дескриптор:
struct st_mysql_plugin
{
  int type;             /* the plugin type (a MYSQL_XXX_PLUGIN value)   */
  void *info;           /* pointer to type-specific plugin descriptor   */
  const char *name;     /* plugin name  */
  const char *author;   /* plugin author (for I_S.PLUGINS)  */
  const char *descr;    /* general descriptive text (for I_S.PLUGINS)   */
  int license;          /* the plugin license (PLUGIN_LICENSE_XXX)*/
  int (*init)(void *);  /* the function to invoke when plugin is loaded */
  int (*deinit)(void *);/* the function to invoke when plugin is unloaded */
  unsigned int version; /* plugin version (for I_S.PLUGINS) */
  struct st_mysql_show_var *status_vars;
  struct st_mysql_sys_var **system_vars;
  void * __reserved1;   /* reserved for dependency checking */
  unsigned long flags;  /* flags for plugin */
};
Члены структуры st_mysql_plugin используются следующим образом. Члены char * должны быть определены как законченные нулем строки.

  • type: Тип плагина. Это должно быть одним из значений типа из plugin.h:

    /*
      The allowable types of plugins
    */
    #define MYSQL_UDF_PLUGIN 0              /* User-defined function*/
    #define MYSQL_STORAGE_ENGINE_PLUGIN  1  /* Storage Engine   */
    #define MYSQL_FTPARSER_PLUGIN2          /* Full-text parser plugin*/
    #define MYSQL_DAEMON_PLUGIN  3          /* The daemon/raw plugin type */
    #define MYSQL_INFORMATION_SCHEMA_PLUGIN  4     /* The I_S plugin type */
    #define MYSQL_AUDIT_PLUGIN   5          /* The Audit plugin type*/
    #define MYSQL_REPLICATION_PLUGIN 6      /* The replication plugin type */
    #define MYSQL_AUTHENTICATION_PLUGIN  7  /* The authentication plugin type */
    ...
    
    Например, для полнотекстового плагина анализатора значение type MYSQL_FTPARSER_PLUGIN.
  • info: Указатель на определенный для типа описатель для плагина. Структура этого описателя зависит от конкретного типа плагина, в отличие от общей структуры дескриптора плагина. В целях управления версиями первый член определенного для типа описателя для каждого типа будет версией интерфейса для типа. Это позволяет серверу проверить определенную для типа версию каждого плагина, независимо от его типа. После номера версии описатель включает любых других необходимых членов, таких как функции обратного вызова и другую информацию, необходимую серверу, чтобы вызвать плагин должным образом. Более поздние разделы по написанию типов плагинов сервера описывают структуру определенных для типа описателей.
  • name: Строка, которая задает имя плагина. Это имя, которое будет перечислено в таблице mysql.plugin и которым Вы отсылаете к плагину в запросах SQL, таких как INSTALL PLUGIN и UNINSTALL PLUGIN или опцией --plugin-load . Имя также видимо в таблице INFORMATION_SCHEMA.PLUGINS или в выводе SHOW PLUGINS .

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

  • author: Строка с автором плагина. Это может быть тем, что Вы любите.
  • desc: Строка, которая обеспечивает общее описание плагина. Это может быть тем, что Вы любите.
  • license: Тип лицензии. Значение может быть одним из PLUGIN_LICENSE_PROPRIETARY, PLUGIN_LICENSE_GPL или PLUGIN_LICENSE_BSD.
  • init: Функция инициализации или NULL, если нет такой функции. Сервер выполняет эту функцию, когда загружает плагин командой INSTALL PLUGIN или через таблицу mysql.plugin при запуске сервера. Функция берет один параметр, который указывает на внутреннюю структуру, используемую, чтобы идентифицировать плагин. Это возвращает ноль для успеха и отличное от нуля значение для отказа.
  • deinit: Функция завершения или NULL, если нет такой функции. Сервер выполняет эту функцию, когда выгружает плагин командой UNINSTALL PLUGIN или через таблицу mysql.plugin при завершении сервера. Функция берет один параметр, который указывает на внутреннюю структуру, используемую, чтобы идентифицировать плагин. Это возвращает ноль для успеха и отличное от нуля значение для отказа.
  • version: Номер версии. Когда плагин установлен, это значение может быть получено из таблицы INFORMATION_SCHEMA.PLUGINS. Значение включает главные и незначительные числа. Если Вы пишете значение как шестнадцатеричную постоянную, формат 0xMMNN, где MM и NN главные и незначительные числа, соответственно. Например, 0x0302 определяет версию 3.2.
  • status_vars: Указатель на структуру для переменных состояния плагина или NULL, если нет таких переменных. Когда плагин установлен, эти переменные выведены на экран в выводе команды SHOW STATUS.

    Член status_vars, если не NULL, указывает на массив структур st_mysql_show_var, которые описывают переменные состояния. См. раздел 26.2.4.2.2.

  • system_vars: Указатель на структуру для системных переменных плагина или NULL, если нет таких переменных. Эти системные переменные могут использоваться, чтобы помочь инициализировать переменные в пределах плагина.

    Член system_vars, если не NULL, указывает на массив структур st_mysql_sys_var, которые описывают системные переменные. См. раздел 26.2.4.2.2.

  • __reserved1: Заполнитель для будущего. Это должно быть установлено в NULL.
  • flags: Флаги плагина. Отдельные биты соответствуют различным флагам. Значение должно быть установлено в ИЛИ применимых флагов. Доступны флаги:
    #define PLUGIN_OPT_NO_INSTALL   1UL   /* Not dynamically loadable */
    #define PLUGIN_OPT_NO_UNINSTALL 2UL   /* Not dynamically unloadable */
    
    PLUGIN_OPT_NO_INSTALL указывает, что плагин не может быть загружен во время выполнения с помощью INSTALL PLUGIN. Это является подходящим для плагинов, которые должны быть загружены при запуске сервера через опцию --plugin-load . PLUGIN_OPT_NO_UNINSTALL указывает, что плагин не может быть выгружен во время выполнения с помощью UNINSTALL PLUGIN.

Сервер вызывает функции init и deinit в общем описателе только, когда загружает и выгружает плагин.

Например, информация об описателе для библиотеки, которая содержит единственный полнотекстовый плагин анализатора simple_parser похожа на это:

mysql_declare_plugin(ftexample)
{
  MYSQL_FTPARSER_PLUGIN,          /* type */
  &simple_parser_descriptor,  /* descriptor */
  "simple_parser",                /* name */
  "Oracle Corporation",           /* author */
  "Simple Full-Text Parser",      /* description */
  PLUGIN_LICENSE_GPL,             /* plugin license */
  simple_parser_plugin_init,      /* init function (when loaded) */
  simple_parser_plugin_deinit,    /* deinit function (when unloaded) */
  0x0001,                         /* version */
  simple_status,                  /* status variables */
  simple_system_variables,        /* system variables */
  NULL, 0
}
mysql_declare_plugin_end;
Для полнотекстового плагина анализатора тип должен быть MYSQL_FTPARSER_PLUGIN. Это значение, которое идентифицирует плагин, как являющийся законным для использования в предложении WITH PARSER при создании индекса FULLTEXT. Никакой другой тип не является законным для этого.

plugin.h определяет макросы mysql_declare_plugin() и mysql_declare_plugin_end так:

#ifndef MYSQL_DYNAMIC_PLUGIN
#define __MYSQL_DECLARE_PLUGIN(NAME, VERSION, PSIZE, DECLS) \
MYSQL_PLUGIN_EXPORT int VERSION= MYSQL_PLUGIN_INTERFACE_VERSION; \
MYSQL_PLUGIN_EXPORT int PSIZE= sizeof(struct st_mysql_plugin); \
MYSQL_PLUGIN_EXPORT struct st_mysql_plugin DECLS[]= {
#else
#define __MYSQL_DECLARE_PLUGIN(NAME, VERSION, PSIZE, DECLS) \
MYSQL_PLUGIN_EXPORT int _mysql_plugin_interface_version_= MYSQL_PLUGIN_INTERFACE_VERSION; \
MYSQL_PLUGIN_EXPORT int _mysql_sizeof_struct_st_plugin_= sizeof(struct st_mysql_plugin); \
MYSQL_PLUGIN_EXPORT struct st_mysql_plugin _mysql_plugin_declarations_[]= {
#endif

#define mysql_declare_plugin(NAME) \
__MYSQL_DECLARE_PLUGIN(NAME, \
 builtin_ ## NAME ## _plugin_interface_version, \
 builtin_ ## NAME ## _sizeof_struct_st_plugin, \
 builtin_ ## NAME ## _plugin)
#define mysql_declare_plugin_end ,{0,0,0,0,0,0,0,0,0,0,0,0,0}}

Эти декларации определяют символ _mysql_plugin_interface_version_ , только если определен символ MYSQL_DYNAMIC_PLUGIN. Это означает, что -DMYSQL_DYNAMIC_PLUGIN должна быть обеспечена как часть команды компиляции, чтобы создать плагин как совместно используемую библиотеку.

Когда макроопределение используется как показано, оно расширяется до следующего кода, который определяет оба из необходимых символов (_mysql_plugin_interface_version_ и _mysql_plugin_declarations_):

int _mysql_plugin_interface_version_= MYSQL_PLUGIN_INTERFACE_VERSION;
int _mysql_sizeof_struct_st_plugin_= sizeof(struct st_mysql_plugin);
struct st_mysql_plugin _mysql_plugin_declarations_[]= {
{
  MYSQL_FTPARSER_PLUGIN,          /* type*/
  &simple_parser_descriptor,  /* descriptor  */
  "simple_parser",                /* name*/
  "Oracle Corporation",           /* author  */
  "Simple Full-Text Parser",      /* description */
  PLUGIN_LICENSE_GPL,             /* plugin license*/
  simple_parser_plugin_init,      /* init function (when loaded) */
  simple_parser_plugin_deinit,    /* deinit function (when unloaded) */
  0x0001,                         /* version */
  simple_status,                  /* status variables*/
  simple_system_variables,        /* system variables*/
  NULL, 0}, {0,0,0,0,0,0,0,0,0,0,0,0}}
};
Предыдущий пример объявляет единственный плагин в общем описателе, но возможно объявить много плагинов. Перечислите декларации одну за другой между mysql_declare_plugin() и mysql_declare_plugin_end, разделяя их запятыми.

Плагины сервера MySQL могут быть написаны на C или C++ (или другом языке, который может использовать C-соглашения о вызове). Если Вы пишете C++ плагин, одной возможностью C++, которую Вы не должны использовать, являются непостоянные переменные, чтобы инициализировать глобальные структуры. Члены структур такие, как st_mysql_plugin должны быть инициализированы только с постоянными. Описатель simple_parser, показанный ранее, допустим в C++ плагине, потому что удовлетворяет правилам:

mysql_declare_plugin(ftexample)
{
  MYSQL_FTPARSER_PLUGIN,          /* type*/
  &simple_parser_descriptor,  /* descriptor  */
  "simple_parser",                /* name*/
  "Oracle Corporation",           /* author  */
  "Simple Full-Text Parser",      /* description */
  PLUGIN_LICENSE_GPL,             /* plugin license*/
  simple_parser_plugin_init,      /* init function (when loaded) */
  simple_parser_plugin_deinit,    /* deinit function (when unloaded) */
  0x0001,                         /* version */
  simple_status,                  /* status variables*/
  simple_system_variables,        /* system variables*/
  NULL, 0
}
mysql_declare_plugin_end;
Вот другой допустимый способ написать общий описатель. Это использует постоянные, чтобы указать на имя, автора и описание:
const char *simple_parser_name = "simple_parser";
const char *simple_parser_author = "Oracle Corporation";
const char *simple_parser_description = "Simple Full-Text Parser";

mysql_declare_plugin(ftexample)
{
  MYSQL_FTPARSER_PLUGIN,          /* type*/
  &simple_parser_descriptor,  /* descriptor  */
  simple_parser_name,             /* name*/
  simple_parser_author,           /* author  */
  simple_parser_description,      /* description */
  PLUGIN_LICENSE_GPL,             /* plugin license*/
  simple_parser_plugin_init,      /* init function (when loaded) */
  simple_parser_plugin_deinit,    /* deinit function (when unloaded) */
  0x0001,                         /* version */
  simple_status,                  /* status variables*/
  simple_system_variables,        /* system variables*/
  NULL, 0
}
mysql_declare_plugin_end;
Однако, следующий общий описатель недопустим. Это использует членов структуры, чтобы указать на имя, автора и описание, но структуры не считаются постоянными инициализаторами в C++:
typedef struct
{
  const char *name;
  const char *author;
  const char *description;
} plugin_info;

plugin_info parser_info = {
  "simple_parser",
  "Oracle Corporation",
  "Simple Full-Text Parser"
};

mysql_declare_plugin(ftexample)
{
  MYSQL_FTPARSER_PLUGIN,          /* type*/
  &simple_parser_descriptor,  /* descriptor  */
  parser_info.name,               /* name*/
  parser_info.author,             /* author  */
  parser_info.description,        /* description */
  PLUGIN_LICENSE_GPL,             /* plugin license*/
  simple_parser_plugin_init,      /* init function (when loaded) */
  simple_parser_plugin_deinit,    /* deinit function (when unloaded) */
  0x0001,                         /* version */
  simple_status,                  /* status variables*/
  simple_system_variables,        /* system variables*/
  NULL, 0
}
mysql_declare_plugin_end;
26.2.4.2.2. Статусы и системные переменные серверных плагинов

Интерфейс плагина сервера позволяет плагинам выставить состояние и системные переменные, используя члены status_vars и system_vars общего описателя.

Член status_vars общего дескриптора, если не 0, указывает на массив структур st_mysql_show_var, каждая из которых описывает одну переменную состояния, сопровождаемую структурой со всеми членами установленными в 0. У структуры st_mysql_show_var есть это определение:

struct st_mysql_show_var {
  const char *name;
  char *value;
  enum enum_mysql_show_type type;
};
Когда плагин установлен, имя плагина и переменная name присоединяются с подчеркиванием, чтобы сформировать имя, выведенное на экран SHOW STATUS.

Следующая таблица показывает допустимые переменные состояния type и какова соответствующая переменная должна быть.

Таблица 26.1. Типы статусных переменных серверных плагинов

Тип переменнойЗначение
SHOW_BOOL Указатель на логическую переменную
SHOW_INTУказатель на integer
SHOW_LONGУказатель на long integer
SHOW_LONGLONGУказатель на longlong integer
SHOW_CHARСтрока
SHOW_CHAR_PTRУказатель на string
SHOW_ARRAYУказатель на другой массив st_mysql_show_var
SHOW_FUNCУказатель на функцию
SHOW_DOUBLEУказатель на double

Для типа SHOW_FUNC функция вызвана и заполняет параметр out, который тогда предоставляет информацию о переменной, которая будет выведена на экран. У функции есть эта сигнатура:

#define SHOW_VAR_FUNC_BUFF_SIZE 1024
typedef int (*mysql_show_var_func) (void *thd, struct st_mysql_show_var *out,
                                    char *buf);
Член system_vars, если не 0, указывает на массив из структур st_mysql_sys_var, каждая из которых описывает одну системную переменную (которая также может быть установлена из командной строки или конфигурационного файла), сопровождаемый структурой со всем членами, установленными в 0. Структура st_mysql_sys_var определена следующим образом:
struct st_mysql_sys_var {
  int flags;
  const char *name, *comment;
  int (*check) (THD*, struct st_mysql_sys_var *, void*, st_mysql_value*);
  void (*update) (THD*, struct st_mysql_sys_var *, void*, const void*);
};
Дополнительные поля прилагают как требуется, в зависимости от флагов.

Для удобства много макроопределений определены, которые делают создание новые системных переменных в пределах плагина намного более простым.

В макросах следующие поля доступны:

  • name: Идентификатор для системной переменной.

  • varname: Идентификатор для статической переменной. Где недоступен, это то же самое, что и поле name.
  • opt: Дополнительное использование флагов для системной переменной. Следующая таблица показывает допустимые флаги.

    Таблица 26.2. Флаги системных переменных серверных плагинов

    Значение флагаОписание
    PLUGIN_VAR_READONLY Системная переменная только для чтения
    PLUGIN_VAR_NOSYSVAR Системная переменная во время выполнения не видна пользователю
    PLUGIN_VAR_NOCMDOPT Системная переменная не конфигурируема из командной строки
    PLUGIN_VAR_NOCMDARG Никакой параметр не требуется в командной строке (как правило используется для логических переменных)
    PLUGIN_VAR_RQCMDARG Параметр требуется в командной строке (это значение по умолчанию)
    PLUGIN_VAR_OPCMDARG Параметр является дополнительным в командной строке
    PLUGIN_VAR_MEMALLOC Используется для строковых переменных: указывает, что память должна быть выделена для хранения строки
  • comment: Описательный комментарий, который будет выведен на экран в серверном сообщении помощи. NULL, если эта переменная должна быть скрыта.

  • check: Функция проверки, NULL для значения по умолчанию.
  • update: Функция обновления, NULL для значения по умолчанию.
  • default: Значение переменной по умолчанию.
  • minimum: Минимальное значение переменной.
  • maximum: Максимальное значение переменной.
  • blocksize: Размер блока. Когда значение установлено, оно округлено к самому близкому числу, кратному blocksize.

К системной переменной можно получить доступ при использовании статической переменной непосредственно или при использовании макроса SYSVAR(). Макрос SYSVAR() обеспечен для законченности. Обычно это должно использоваться только, когда код не может непосредственно получить доступ к основной переменной.

Например:

static int my_foo;
static MYSQL_SYSVAR_INT(foo_var, my_foo,
                        PLUGIN_VAR_RQCMDARG, "foo comment",
                        NULL, NULL, 0, 0, INT_MAX, 0);
   ...
   SYSVAR(foo_var)= value;
   value= SYSVAR(foo_var);
   my_foo= value;
   value= my_foo;
К переменным сеанса можно получить доступ только через макрос THDVAR(). Например:
static MYSQL_THDVAR_BOOL(some_flag, PLUGIN_VAR_NOCMDARG, "flag comment",
                         NULL, NULL, FALSE);
   ...
   if (THDVAR(thd, some_flag)) {
      do_something();
      THDVAR(thd, some_flag)= FALSE;
   }
Все глобальные и сеансовые системные переменные должны быть опубликованы mysqld перед использованием. Это сделано, создавая NULL-завершенный массив переменных и связи с ним в общем интерфейсе. Например:
static struct st_mysql_sys_var *my_plugin_vars[] = {
  MYSQL_SYSVAR(foo_var), MYSQL_SYSVAR(some_flag), NULL
};

mysql_declare_plugin(fooplug)
{
  MYSQL_..._PLUGIN, &plugin_data, "fooplug", "foo author",
  "This does foo!", PLUGIN_LICENSE_GPL, foo_init, foo_fini, 0x0001,
  NULL, my_plugin_vars, NULL, 0
}
mysql_declare_plugin_end;
Следующее макроопределение позволяет Вам объявить различные типы системных переменных:

  • Булевы системные переменные типа my_bool, которые занимают 1 байт (0 = FALSE, 1 = TRUE).

    MYSQL_THDVAR_BOOL(name, opt, comment, check, update, default)
    MYSQL_SYSVAR_BOOL(name, varname, opt, comment, check, update, default)
    
  • Строковые системные переменные типа char*, которые являются указателем на законченную нулем строку.
    MYSQL_THDVAR_STR(name, opt, comment, check, update, default)
    MYSQL_SYSVAR_STR(name, varname, opt, comment, check, update, default)
    
  • Системные переменные Integer, для которых есть несколько вариантов.

    • Системные переменные int, как правило, является 4 байтами (словом со знаком).

      MYSQL_THDVAR_INT(name, opt, comment, check, update, default, min, max, blk)
      MYSQL_SYSVAR_INT(name, varname, opt, comment, check, update, default,
                       minimum, maximum, blocksize)
      
    • Системные переменные unsigned int, как правило, является 4 байтами (словом без знака).
      MYSQL_THDVAR_UINT(name, opt, comment, check, update, default, min, max, blk)
      MYSQL_SYSVAR_UINT(name, varname, opt, comment, check, update, default,
                        minimum, maximum, blocksize)
      
    • Системные переменные long, как правило, является 4 или 8 байтами (словом со знаком).
      MYSQL_THDVAR_LONG(name, opt, comment, check, update, default, min, max, blk)
      MYSQL_SYSVAR_LONG(name, varname, opt, comment, check, update, default,
                        minimum, maximum, blocksize)
      
    • Системные переменные unsigned long, как правило, является 4 или 8 байтами (словом без знака).
      MYSQL_THDVAR_ULONG(name, opt, comment, check, update, default, min, max, blk)
      MYSQL_SYSVAR_ULONG(name, varname, opt, comment, check, update, default,
                         minimum, maximum, blocksize)
      
    • Системные переменные long long, как правило, является 8 байтами (словом со знаком).
      MYSQL_THDVAR_LONGLONG(name, opt, comment, check, update,
                            default, minimum, maximum, blocksize)
      MYSQL_SYSVAR_LONGLONG(name, varname, opt, comment, check, update,
                            default, minimum, maximum, blocksize)
      
    • Системные переменные unsigned long long, как правило, является 8 байтами (словом без знака).
      MYSQL_THDVAR_ULONGLONG(name, opt, comment, check, update,
                             default, minimum, maximum, blocksize)
      MYSQL_SYSVAR_ULONGLONG(name, varname, opt, comment, check, update,
                             default, minimum, maximum, blocksize)
      
    • Системные переменные double, как правило, является 8 байтами (словом со знаком).
      MYSQL_THDVAR_DOUBLE(name, opt, comment, check, update,
                          default, minimum, maximum, blocksize)
      MYSQL_SYSVAR_DOUBLE(name, varname, opt, comment, check, update,
                          default, minimum, maximum, blocksize)
      
    • Системные переменные unsigned long, как правило, является 4 или 8 байтами (словом без знака). Диапазон возможных значений ординал числа элементов в typelib, начиная с 0.
      MYSQL_THDVAR_ENUM(name, opt, comment, check, update, default, typelib)
      MYSQL_SYSVAR_ENUM(name, varname, opt, comment, check, update,
                        default, typelib)
      
    • Системные переменные unsigned long long, как правило, является 8 байтами (словом без знака). Каждый бит представляет элемент в typelib.

      MYSQL_THDVAR_SET(name, opt, comment, check, update, default, typelib)
      MYSQL_SYSVAR_SET(name, varname, opt, comment, check, update,
                       default, typelib)
      

Внутренне, все изменчивые системные переменные сохранены в структуре HASH. Отображение текста справки командной строки сервера обработано, собирая DYNAMIC_ARRAY из всех переменных, относящихся к параметрам командной строки м сортируя их.

Когда параметр командной строки был обработан, он будет удален из argv функцией handle_option() (my_getopt.c).

Сервер обрабатывает параметры командной строки во время процесса установки плагина, немедленно после того, как плагин был успешно загружен, но прежде, чем функция инициализации была вызвана.

Плагины, загруженные во время выполнения, не извлекают ничего ни из каких параметров конфигурации и должны иметь применимые значения по умолчанию. Как только они установлены, они загружены в mysqld и параметры конфигурации могут быть установлены в командной строке или в пределах my.cnf.

Плагины должны рассмотреть параметр thd, чтобы быть только для чтения.

26.2.4.2.3. Описатели плагина клиента

У каждого плагина клиента должен быть описатель, который предоставляет информацию API клиентского плагина. Дескрипторная структура начинается с фиксированной группы членов, характерных для всех плагинов клиента, сопровождаемых любыми членами, определенными для типа плагина.

Структура st_mysql_client_plugin в файле client_plugin.h определяет описатель generic, который содержит общие члены:

struct st_mysql_client_plugin
{
  int type;
  unsigned int interface_version;
  const char *name;
  const char *author;
  const char *desc;
  unsigned int version[3];
  const char *license;
  void *mysql_api;
  int (*init)(char *, size_t, int, va_list);
  int (*deinit)();
  int (*options)(const char *option, const void *);
};
Общие члены структуры st_mysql_client_plugin используются следующим образом. Члены char * должны быть определены как законченные нулем строки.

  • type: Тип плагина. Это должно быть одним из значений типа из client_plugin.h, например, MYSQL_CLIENT_AUTHENTICATION_PLUGIN.

  • interface_version: Версия интерфейса плагина. Например, MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION для плагина аутентификации.
  • name: Строка, которая дает имя. Это то имя, которым Вы обращаетесь к плагину, когда Вы вызываете mysql_options() с опцией MYSQL_DEFAULT_AUTH или определяете опцию --default-auth в клиентской программе MySQL.
  • author: Строка с именем автора. Это может быть тем, что Вы любите.
  • desc: Строка, которая обеспечивает общее описание плагина. Это может быть тем, что Вы любите.
  • version: Версия как массив трех целых чисел, указывающих на старший, средний и младший номера версии. Например, {1,2,3} указывает на версию 1.2.3.
  • license: Строка, которая определяет тип лицензии.
  • mysql_api: Для внутреннего пользования. Определите это как NULL в описателе плагина.
  • init: Функция инициализации или NULL, если нет такой функции. Библиотека клиента выполняет эту функцию, когда загружает плагин. Функция возвращает ноль для успеха и отличное от нуля значение для отказа.

    Функция init использует первые два параметра, чтобы возвратить сообщение об ошибке, если ошибка происходит. Первый параметр указатель на буфер char, второй параметр указывает на буферную длину. Любое сообщение, возвращенное init, должна быть закончена нулем, таким образом, максимальная длина сообщения это длина буфера минус один. Следующие параметры передаются в mysql_load_plugin(). Первое указывает, сколько там параметров (0, если ни одного), сопровождается любыми остающимися параметрами.

  • deinit: Функция завершения или NULL, если нет такой функции. Библиотека клиента выполняет эту функцию, когда выгружает плагин. Функция не берет параметров. Это возвращает ноль для успеха и отличное от нуля значение для отказа.
  • options: Функция для того, чтобы обработать опции плагина или NULL, если нет такой функции. Функция берет два параметра, представляющие имя опции и указатель на ее значение. Функция возвращает ноль для успеха и отличное от нуля значение для отказа.

Для данного типа плагина клиента общие члены дескриптора могут сопровождаться дополнительными членами, необходимыми, чтобы осуществить плагины того типа. Например, структура st_mysql_client_plugin_AUTHENTICATION для плагинов аутентификации имеет функцию в конце, который библиотека клиента вызывает, чтобы выполнить аутентификацию.

Чтобы объявить плагин, используйте макросы mysql_declare_client_plugin() и mysql_end_client_plugin:

mysql_declare_client_plugin(plugin_type)
   ... Общие члены для всех клиентских плагинов ...
   ... Специфические для типа плагина члены ...
mysql_end_client_plugin;
Не определяйте члены type или interface_version явно. Макрос mysql_declare_client_plugin() использует параметр plugin_type, чтобы произвести их значения автоматически. Например, объявите плагин клиента аутентификации так:
mysql_declare_client_plugin(AUTHENTICATION)
  "my_auth_plugin", "Author Name", "My Client Authentication Plugin",
  {1,0,0}, "GPL", NULL, my_auth_init, my_auth_deinit, my_auth_options,
  my_auth_main
mysql_end_client_plugin;
Эта декларация использует параметр AUTHENTICATION для задания членам type и interface_version значений MYSQL_CLIENT_AUTHENTICATION_PLUGIN и MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION.

В зависимости от типа плагина, у описателя могут быть другие члены после общих. Например, для плагина аутентификации, есть функция (my_auth_main() в описателе, показанном выше), которая обрабатывает коммуникацию с сервером. См. раздел 26.2.4.9.

Обычно программа клиента, которая поддерживает использование плагинов аутентификации, заставляет плагин быть загруженным, вызывая mysql_options(), чтобы установить опции MYSQL_DEFAULT_AUTH и MYSQL_PLUGIN_DIR:

char *plugin_dir = "path_to_plugin_dir";
char *default_auth = "plugin_name";
/* ... process command-line options ... */
mysql_options(&mysql, MYSQL_PLUGIN_DIR, plugin_dir);
mysql_options(&mysql, MYSQL_DEFAULT_AUTH, default_auth);
Как правило, программа также примет опции --plugin-dir и --default-auth, которые позволяют пользователям переопределить значения по умолчанию.

Если программа клиента требует управления низшего уровня, библиотека клиента содержит функции, которые берут аргумент st_mysql_client_plugin. См. раздел 25.8.14.

26.2.4.3. Компилирование и установка библиотек

После того, как Ваш плагин написан, Вы должны собрать и установить его. Процедура для того, чтобы собрать совместно используемые объекты изменяется от системы к системе. Если Вы создаете свою библиотеку, используя CMake, это должно быть в состоянии произвести правильные команды компиляции для Вашей системы. Если библиотеку называют somepluglib, Вы должны получить совместно используемый файл библиотеки, у которого есть имя что-то вроде somepluglib.so. Суффикс имени файла .so в Вашей системе может быть другим.

Чтобы использовать CMake, Вы должны будете настроить конфигурационные файлы, чтобы позволить плагину быть собранным и установленным. Используйте примеры в подкаталоге plugin дистрибутива исходных текстов MySQL как руководство.

Создать CMakeLists.txt, который должен выглядеть примерно так:

MYSQL_ADD_PLUGIN(somepluglib somepluglib.c
  MODULE_ONLY MODULE_OUTPUT_NAME "somepluglib")
Когда CMake производит Makefile, это должно позаботиться о прохождении к компилятору флага -DMYSQL_DYNAMIC_PLUGIN, а к компоновщику флага -lmysqlservices, который необходимы, чтобы скомпоновать с любыми функциями интерфейса службы плагинов. См. раздел 26.3.

Запустите CMake, затем выполните make:

shell> cmake .
shell> make
Если Вы должны определить параметры конфигурации для CMake, см. раздел 2.8.4. Например, Вы могли бы хотеть определить CMAKE_INSTALL_PREFIX, чтобы указать на базовый каталог MySQL, в соответствии с которым должен быть установлен плагин. Вы можете увидеть, какое значение использовать для этой опции с помощью запроса SHOW VARIABLES:
mysql> SHOW VARIABLES LIKE 'basedir';
+---------------+------------------+
| Variable_name | Value            |
+---------------+------------------+
| base          | /usr/local/mysql |
+---------------+------------------+
Местоположение каталога, где Вы должны установить библиотеку, дано в системной переменной plugin_dir. Например:
mysql> SHOW VARIABLES LIKE 'plugin_dir';
+---------------+-----------------------------------+
| Variable_name | Value                             |
+---------------+-----------------------------------+
| plugin_dir    | /usr/local/mysql/lib/mysql/plugin |
+---------------+-----------------------------------+
Чтобы установить библиотеку, примените make:
shell> make install
Проверьте, что make install установил библиотеку в надлежащем каталоге. После установки удостоверьтесь, что права доступа библиотеки разрешают ей быть выполненной сервером.

26.2.4.4. Написание полнотекстовых плагинов анализатора

MySQL поддерживает полнотекстовые плагины анализатора на стороне сервера для MyISAM и InnoDB.

Полнотекстовый плагин анализатора может использоваться, чтобы заменить или изменить встроенный полнотекстовый анализатор. Этот раздел описывает, как написать полнотекстовый плагин simple_parser. Этот плагин выполняет парсинг, основанный на более простых правилах, чем используемый MySQL встроенный полнотекстовый анализатор: слова считаются непустыми цепочками символов, разделенных пробелами.

Инструкции используют исходный код в каталоге plugin/fulltext исходных текстов MySQL, так что перейдите в этот каталог. Следующая процедура описывает, как библиотека создается:

  1. Чтобы написать полнотекстовый плагин анализатора, включите следующий заголовочный файл в исходный файл плагина. Другие общие заголовочные файлы также могут быть необходимы, в зависимости от способностей и требований плагина.

    #include <mysql/plugin.h>
    
    plugin.h определяет тип плагина сервера MYSQL_FTPARSER_PLUGIN и структуры данных, которые должны объявить плагин.
  2. Настройте описатель библиотеки для файла библиотеки.

    Этот описатель содержит общий описатель для плагина сервера. Для полнотекстового плагина анализатора тип должен быть MYSQL_FTPARSER_PLUGIN. Это значение, которое идентифицирует плагин, как легальный для использования в предложении WITH PARSER при создании индекса FULLTEXT. Никакой другой тип плагина не является законным для этого.

    Например, описатель для библиотеки, которая содержит единственный полнотекстовый плагин анализатора simple_parser, похож на это:

    mysql_declare_plugin(ftexample)
    {
      MYSQL_FTPARSER_PLUGIN,          /* type*/
      &simple_parser_descriptor,  /* descriptor  */
      "simple_parser",                /* name*/
      "Oracle Corporation",           /* author  */
      "Simple Full-Text Parser",      /* description */
      PLUGIN_LICENSE_GPL,             /* plugin license*/
      simple_parser_plugin_init,      /* init function (when loaded) */
      simple_parser_plugin_deinit,    /* deinit function (when unloaded) */
      0x0001,                         /* version */
      simple_status,                  /* status variables*/
      simple_system_variables,        /* system variables*/
      NULL, 0
    }
    mysql_declare_plugin_end;
    
    Член name (simple_parser) указывает на имя, чтобы использовать для ссылок на плагин в запросах INSTALL PLUGIN или UNINSTALL PLUGIN. Это имя также, выведено на экран SHOW PLUGINS или INFORMATION_SCHEMA.PLUGINS .

  3. Настройте определенный для типа описатель.

    Каждый общий описатель библиотеки указывает на определенный для типа описатель. Для полнотекстового плагина анализатора определенный для типа описатель является экземпляром структуры st_mysql_ftparser в файле plugin.h:

    struct st_mysql_ftparser
    {
      int interface_version;
      int (*parse)(MYSQL_FTPARSER_PARAM *param);
      int (*init)(MYSQL_FTPARSER_PARAM *param);
      int (*deinit)(MYSQL_FTPARSER_PARAM *param);
    };
    
    Как показано в определении структуры, описатель имеет номер версии интерфейса и содержит указатели на три функции.

    Номер версии интерфейса определен, используя символ, который находится в форме: MYSQL_xxx_INTERFACE_VERSION. Для полнотекстовых плагинов анализатора символ MYSQL_FTPARSER_INTERFACE_VERSION. В исходном коде Вы сочтете фактический номер версии интерфейса для полнотекстового плагина анализатора определенным в include/mysql/plugin_ftparser.h. Текущий номер версии интерфейса 0x0101.

    Члены init и deinit должны указать на функцию или быть установлены в 0, если функция не необходима. Член parse должен указать на функцию, которая выполняет парсинг.

    В декларации simple_parser этот описатель обозначен как &simple_parser_descriptor. Описатель определяет номер версии для полнотекстового интерфейса (как дано MYSQL_FTPARSER_INTERFACE_VERSION) и функции плагина, инициализация, парсинг и завершения:

    static struct st_mysql_ftparser simple_parser_descriptor =
    {
      MYSQL_FTPARSER_INTERFACE_VERSION, /* interface version*/
      simple_parser_parse,  /* parsing function */
      simple_parser_init,   /* parser init function   */
      simple_parser_deinit  /* parser deinit function */
    };
    
    Полнотекстовый плагин анализатора используется в двух различных контекстах, индексируя и ища. В обоих контекстах сервер вызывает функции инициализации и завершения в начале и в конце обработки каждого запроса SQL, которое вызывает плагин. Однако, во время обработки запроса сервер вызывает основную функцию парсинга зависящим от контекста способом:

    • Для того, чтобы индексировать, сервер вызывает анализатор для каждого значения столбца, которое будет индексировано.

    • Для того, чтобы искать, сервер вызывает анализатор, чтобы разобрать строку поиска. Анализатор можно было бы также вызвать для строк, обработанных запросом. В режиме естественного языка нет никакой потребности в сервере, чтобы вызвать анализатор. Для булевых поисков фразы режима или поисков естественного языка с расширением запроса, анализатор используется, чтобы разобрать значения столбцов для информации, которая не находится в индексе. Кроме того, если режим булева поиска сделан для столбца, который не имеет индекса FULLTEXT, встроенный анализатор вызовут. Плагины связаны с определенным индексом. Если нет индекса, никакой плагин не используется.

    Декларация в общем сменном описателе имеет члены init и deinit, которые указывают на функции инициализации и завершения, так же определен специфичный для типа описатель. Однако, эти пары функций имеют различные цели и вызваны по различным причинам:

    • Для декларации плагина в общем описателе вызваны функции инициализации и завершения, когда плагин загружен и выгружен.

    • Для определенного для типа описателя вызваны функции инициализации и завершения, когда плагин при запросе SQL, для которого используется плагин.

    Каждая функция интерфейса, названная в описателе, должна возвратить ноль для успеха или отличное от нуля значение для отказа, каждая из этих функций получает параметр, который указывает на структуру MYSQL_FTPARSER_PARAM, содержащую контекст парсинга. У структуры есть это определение:

    typedef struct st_mysql_ftparser_param
    {
      int (*mysql_parse) (struct st_mysql_ftparser_param *,
                          char *doc, int doc_len);
      int (*mysql_add_word) (struct st_mysql_ftparser_param *,
                             char *word, int word_len,
                             MYSQL_FTPARSER_BOOLEAN_INFO *boolean_info);
                             void *ftparser_state;
                             void *mysql_ftparam;
                             struct charset_info_st *cs;
                             char *doc;
                             int length;
                             int flags;
                             enum enum_ftparser_mode mode;
    } MYSQL_FTPARSER_PARAM;
    
    Члены структуры используются следующим образом:

    • mysql_parse: Указатель на функцию обратного вызова, которая вызывает встроенный анализатор сервера. Используйте этот вызов, когда плагин действует как фронтэнд к встроенному анализатору. Таким образом, когда функция парсинга плагина вызвана, она должна обработать ввод, чтобы извлечь текст и передать текст в mysql_parse.

      Первый параметр для этой функции обратного вызова должен быть param:

      param->mysql_parse(param, ...);
      
      Плагин фронтэнда может извлечь текст и передать все это сразу встроенному анализатору, или извлечь и передать текст встроенному анализатору частями. Однако, в этом случае, встроенный анализатор обрабатывает части текста, как если бы есть неявные разрывы слова между ними.
    • mysql_add_word: Указатель на функцию обратного вызова, которая добавляет слово к полнотекстовому индексу или к списку критериев поиска. Используйте этот вызов, когда плагин анализатора заменяет встроенный анализатор. Таким образом, когда функция парсинга плагина вызвана, она должна разобрать ввод на слова и вызвать для каждого слова mysql_add_word.

      Первый параметр для этой функции обратного вызова должен быть param:

      param->mysql_add_word(param, ...);
      
    • ftparser_state: Это общий указатель. Плагин может установить это, чтобы указать на информацию, которая будет использоваться внутренне в его собственных целях.
    • mysql_ftparam: Это установлено сервером. Это передают как первый параметр mysql_parse или mysql_add_word.
    • cs: Указатель на информацию о наборе символов текста или 0, если никакая информация не доступна.
    • doc: Указатель на текст, который будет разобран.
    • length: Длина текста, который будет разобран, в байтах.
    • flags: Флаги анализатора. Это 0, если нет никаких специальных флагов. Единственный флаг отличный от нуля MYSQL_FTFLAGS_NEED_COPY, означает что mysql_add_word() должна сохранить копию слова (то есть, она не может использовать указатель на слово, потому что слово находится в буфере, который будет перезаписан.

      Этот флаг мог бы быть установлен или сброшен MySQL прежде, чем вызвать плагин анализатора, плагином анализатора непосредственно или функцией mysql_parse().

    • mode: Режим парсинга. Это значение будет одной из следующих констант:

      • MYSQL_FTPARSER_SIMPLE_MODE: Разбор в быстром и простом режиме, который используется для того, чтобы индексировать и для запросов естественного языка. Анализатор должен передать к серверу только те слова, которые должны быть индексированы. Если анализатор использует пределы длины или список стоп-слов, чтобы определить, какие слова проигнорировать, он не должен передать такие слова серверу.

      • MYSQL_FTPARSER_WITH_STOPWORDS: Разбор в режиме стоп-слов. Это используется в булевых поисках соответствия фразы. Анализатор должен передать все слова серверу, даже стоп-слова или слова, которые вне любых нормальных пределов длины.
      • MYSQL_FTPARSER_FULL_BOOLEAN_INFO: Разбор в булевом режиме. Это используется для того, чтобы разобрать булевы строки запроса. Анализатор должен признать не только слова, но также и операторы булева режима и передать их серверу как маркеры, используя mysql_add_word. Чтобы сказать сервер, какой маркер передается, плагин должен заполнить структуру MYSQL_FTPARSER_BOOLEAN_INFO и передать указатель на нее.

    Для MyISAM список стоп-слов, ft_min_word_len и ft_max_word_len проверены в токенизаторе. Для InnoDB список стоп-слов, и эквивалентные настройки переменной длины слова ( innodb_ft_min_token_size и innodb_ft_max_token_size) проверены за пределами токенизатора. В результате InnoDB-анализаторы не должны проверять список стоп-слов, innodb_ft_min_token_size или innodb_ft_max_token_size. Вместо этого рекомендуется, чтобы все слова были возвращены InnoDB. Однако, если Вы хотите проверить стоп-слова в пределах своего анализатора, надо использовать MYSQL_FTPARSER_SIMPLE_MODE для полнотекстового поиска, индексироваиня и поиска естественного языка. Для режимов MYSQL_FTPARSER_WITH_STOPWORDS и MYSQL_FTPARSER_FULL_BOOLEAN_INFO рекомендуется, чтобы все слова были возвращены InnoDB, включая стоп-слова, в случае поиска фразы.

    Если анализатор вызывают в булевом режиме, значение param->mode будет MYSQL_FTPARSER_FULL_BOOLEAN_INFO . Структура MYSQL_FTPARSER_BOOLEAN_INFO, которую парсер использует для передачи информации о маркере серверу похожа на это:

    typedef struct st_mysql_ftparser_boolean_info
    {
      enum enum_ft_token_type type;
      int yesno;
      int weight_adjust;
      char wasign;
      char trunc;
      int position;
      /* These are parser state and must be removed. */
      char prev;
      char *quot;
    } MYSQL_FTPARSER_BOOLEAN_INFO;
    
    Анализатор должен заполнить члены структуры следующим образом:

    • type: Маркерный тип. Следующая таблица показывает допустимые типы.

      Таблица 26.3. Полнотекстовые маркерные типы анализатора

      Значение маркераСмысл
      FT_TOKEN_EOFКонец данных
      FT_TOKEN_WORDРегулярное слово
      FT_TOKEN_LEFT_PAREN Начало группы или подвыражения
      FT_TOKEN_RIGHT_PAREN Конец группы или подвыражения
      FT_TOKEN_STOPWORDСтоп-слово
    • yesno: Должно ли слово присутствовать для соответствия. 0 значит, что слово является дополнительным, но увеличивает уместность соответствия, если присутствует. Значения, больше 0 значат, что слово должно присутствовать. Значения, меньше 0 указывают, что слово не должно присутствовать.

    • weight_adjust: Фактор надбавки, который определяет сколько стоит слово. Это может использоваться, чтобы увеличить или уменьшить важность слова в вычислениях уместности. Значение 0 не указывает ни на какую корректировку веса. Значения, больше чем или меньше чем ноль, означают более высокий или низкий вес, соответственно. Примеры в разделе 13.9.2, операторы < и > иллюстрируют, как надбавка работает.
    • wasign: Знак фактора надбавки. Отрицательная величина действует как оператор ~ булева поиска, который заставляет вклад слова в уместность быть отрицательным.
    • trunc: Должно ли соответствие быть сделано, как будто булев режим * задан оператору усечения.
    • position: Стратовая позиция слова в документе, в байтах. Используется InnoDB full-text search (FTS). Для существующих плагинов, которые вызывают в булевом режиме, поддержка должна быть добавлена для члена position.

    Плагины не должны использовать члены prev и quot структуры MYSQL_FTPARSER_BOOLEAN_INFO.

    Структура анализатора не поддерживает:

    • Булев оператор @distance.

    • Ведущий знак "плюс" (+) или знак "минус" (-) булев оператор, сопровождаемый пробелом и затем словом ('+ apple' или '- apple'). Знак "плюс" или "минус" должны быть непосредственно смежным со словом, например: '+apple' или '-apple'.

  4. Настройте функции интерфейса.

    Общий описатель плагина в описателе библиотеки называет функции инициализации и завершения, которые должен вызвать сервер, когда загружает и выгружает плагин. Для simple_parser эти функции действительно только возвращают ноль, чтобы указать, что они преуспели:

    static int simple_parser_plugin_init(void *arg __attribute__((unused)))
    {
      return(0);
    }
    
    static int simple_parser_plugin_deinit(void *arg __attribute__((unused)))
    {
      return(0);
    }
    
    Поскольку те функции ничего фактически не делают, Вы могли опустить их и определить 0 для каждой из них в декларации.

    Определенный для типа описатель для simple_parser называет функции инициализации и завершения, которые должен вызвать сервер, когда использует плагин. Для simple_parser эти функции ничего не делают:

    static int simple_parser_init(MYSQL_FTPARSER_PARAM *param
           __attribute__((unused)))
    {
      return(0);
    }
    
    static int simple_parser_deinit(MYSQL_FTPARSER_PARAM *param
           __attribute__((unused)))
    {
      return(0);
    }
    
    Здесь также, поскольку те функции ничего не делают, Вы могли опустить их и определить 0 для каждой из них в описателе.

    Основная функция парсинга, simple_parser_parse(), действует как замена для встроенного полнотекстового анализатора, таким образом, она должна разделить текст на слова и передать каждое слово серверу. Первый параметр функции парсинга указатель на структуру, которая содержит контекст парсинга. У этой структуры есть члены doc, который указывает на текст, который будет разобран, и length, который указывает, какой длины текст. Простой парсинг, сделанный плагином, полагает, что непустые цепочки символов, разделенные пробелами, это слова, таким образом, это идентифицирует слова так:

    static int simple_parser_parse(MYSQL_FTPARSER_PARAM *param)
    {
      char *end, *start, *docend= param->doc + param->length;
    
      for (end= start= param->doc;; end++) {
        if (end == docend) {
           if (end > start) add_word(param, start, end - start);
           break;
        }
        else if (isspace(*end)) {
          if (end > start) add_word(param, start, end - start);
          start= end + 1;
        }
      }
      return(0);
    }
    
    Когда анализатор находит каждое слово, он вызывает функцию add_word(), чтобы передать слово серверу. add_word() только вспомогательная функция, это не часть интерфейса плагина. Анализатор передает указатель контекста парсинга в add_word(), так же как указатель на слово и значение длины:
    static void add_word(MYSQL_FTPARSER_PARAM *param, char *word, size_t len)
    {
      MYSQL_FTPARSER_BOOLEAN_INFO bool_info =
        {FT_TOKEN_WORD, 0, 0, 0, 0, 0, ' ', 0};
      param->mysql_add_word(param, word, len, &bool_info);
    }
    
    Для парсинга булева режима add_word() заполняет членов структуры bool_info как описано ранее в обсуждении структуры st_mysql_ftparser_boolean_info.
  5. Настройте переменные состояния. Для плагина simple_parser следующий массив переменных состояния настраивает одну переменную состояния со значением, которое является статическим текстом, а другую со значением, которое сохранено в переменной long integer:
    long number_of_calls = 0;
    struct st_mysql_show_var simple_status[] =
    {
      {"static", (char *)"just a static text", SHOW_CHAR},
      {"called", (char *)&number_of_calls, SHOW_LONG},
      {0,0,0}
    };
    
    Когда плагин установлен, его имя и значение name присоединяются с подчеркиванием, чтобы сформировать имя, выведенное на экран SHOW STATUS. Для массива получающиеся имена переменной состояния simple_parser_static и simple_parser_called. Это соглашение означает, что Вы можете легко вывести на экран переменные для плагина, используя его имя:
    mysql> SHOW STATUS LIKE 'simple_parser%';
    +----------------------+--------------------+
    | Variable_name        | Value              |
    +----------------------+--------------------+
    | simple_parser_static | just a static text |
    | simple_parser_called | 0                  |
    +----------------------+--------------------+
    
  6. Чтобы собрать и установить файл библиотеки, используйте инструкции в разделе 26.2.4.3. Чтобы сделать файл библиотеки доступным для использования, установите его в каталоге плагинов (каталог, названный в системной переменной plugin_dir). Для плагина simple_parser, все собрано и установлено, когда Вы создаете MySQL из исходных текстов. Это также включено в двоичные дистрибутивы. Процесс сборки производит совместно используемую библиотеку объекта с названием mypluglib.so (суффикс .so зависит от системы).
  7. Чтобы использовать плагин, зарегистрируйте его на сервере. Например, чтобы зарегистрировать плагин во время выполнения, используйте этот запрос (корректируйте суффикс .so для Вашей платформы по мере необходимости):
    INSTALL PLUGIN simple_parser SONAME 'mypluglib.so';
    
  8. Чтобы проверить установку, исследуйте таблицу INFORMATION_SCHEMA.PLUGINS или используйте команду SHOW PLUGINS. См. раздел 6.6.3.
  9. Проверьте плагин, чтобы проверить, что он работает должным образом.

    Составьте таблицу, которая содержит строковый столбец и свяжите плагин анализатора с индексом FULLTEXT на столбце:

    mysql> CREATE TABLE t (c VARCHAR(255),
        ->   FULLTEXT (c) WITH PARSER simple_parser
        -> ) ENGINE=MyISAM;
    Query OK, 0 rows affected (0.01 sec)
    
    Вставьте некоторый текст в таблицу и попробуйте некоторые поиски. Они должны проверить, что плагин анализатора обрабатывает все непробельные символы как символы слова:
    mysql> INSERT INTO t VALUES
        ->   ('latin1_general_cs is a case-sensitive collation'),
        ->   ('I\'d like a case of oranges'),
        ->   ('this is sensitive information'),
        ->   ('another row'),
        ->   ('yet another row');
    Query OK, 5 rows affected (0.02 sec)
    Records: 5  Duplicates: 0  Warnings: 0
    
    mysql> SELECT c FROM t;
    +-------------------------------------------------+
    | c                                               |
    +-------------------------------------------------+
    | latin1_general_cs is a case-sensitive collation |
    | I'd like a case of oranges                      |
    | this is sensitive information                   |
    | another row                                     |
    | yet another row                                 |
    +-------------------------------------------------+
    5 rows in set (0.00 sec)
    
    mysql> SELECT MATCH(c) AGAINST('case') FROM t;
    +--------------------------+
    | MATCH(c) AGAINST('case') |
    +--------------------------+
    |0                         |
    |  1.2968142032623         |
    |0                         |
    |0                         |
    |0                         |
    +--------------------------+
    5 rows in set (0.00 sec)
    
    mysql> SELECT MATCH(c) AGAINST('sensitive') FROM t;
    +-------------------------------+
    | MATCH(c) AGAINST('sensitive') |
    +-------------------------------+
    | 0                             |
    | 0                             |
    |   1.3253291845322             |
    | 0                             |
    | 0                             |
    +-------------------------------+
    5 rows in set (0.01 sec)
    
    mysql> SELECT MATCH(c) AGAINST('case-sensitive') FROM t;
    +------------------------------------+
    | MATCH(c) AGAINST('case-sensitive') |
    +------------------------------------+
    |1.3109166622162                     |
    |  0                                 |
    |  0                                 |
    |  0                                 |
    |  0                                 |
    +------------------------------------+
    5 rows in set (0.01 sec)
    
    mysql> SELECT MATCH(c) AGAINST('I\'d') FROM t;
    +--------------------------+
    | MATCH(c) AGAINST('I\'d') |
    +--------------------------+
    |0                         |
    |  1.2968142032623         |
    |0                         |
    |0                         |
    |0                         |
    +--------------------------+
    5 rows in set (0.01 sec)
    
    Ни case, ни insensitive не соответствуют case-insensitive способу, которым они были бы обработаны встроенным анализатором.

26.2.4.5. Написание плагинов демона

Плагин демона простой тип плагина, используемого для кода, который должен быть выполнен сервером, но это не общается с сервером. Этот раздел описывает, как написать плагин сервера демона, используя в качестве примера плагин, найденный в каталоге plugin/daemon_example исходных текстов MySQL. Этот каталог содержит исходный файл daemon_example.cc для плагина демона daemon_example, который пишет строку в файл mysql-heartbeat.log в каталоге данных.

Чтобы написать плагин демона, включайте следующий заголовочный файл в исходный файл плагина.

#include <mysql/plugin.h>
plugin.h определяет MYSQL_DAEMON_PLUGIN тип плагина сервера и структуры данных, которые должны объявить плагин.

Файл daemon_example.cc настраивает описатель библиотеки следующим образом. Описатель библиотеки включает единственный общий описатель плагина сервера.

mysql_declare_plugin(daemon_example)
{
  MYSQL_DAEMON_PLUGIN,
  &daemon_example_plugin,
  "daemon_example",
  "Brian Aker",
  "Daemon example, creates a heartbeat beat file in mysql-heartbeat.log",
  PLUGIN_LICENSE_GPL,
  daemon_example_plugin_init,   /* Plugin Init */
  daemon_example_plugin_deinit, /* Plugin Deinit */
  0x0100  /* 1.0 */,
  NULL,   /* status variables*/
  NULL,   /* system variables*/
  NULL,   /* config options*/
  0,      /* flags   */
}
mysql_declare_plugin_end;
Член name (daemon_example) указывает на имя, чтобы использовать для ссылок на плагин в командах INSTALL PLUGIN или UNINSTALL PLUGIN. Это также имя, выведенное на экран SHOW PLUGINS или INFORMATION_SCHEMA.PLUGINS .

Второй член описателя daemon_example_plugin указатель на определенный для типа описатель плагина демона. Эта структура состоит только из определенного для типа номера версии API:

struct st_mysql_daemon daemon_example_plugin =
  {MYSQL_DAEMON_INTERFACE_VERSION};
У определенной для типа структуры нет никаких функций интерфейса. Нет никакой связи между сервером и плагином, за исключением того, что сервер вызывает функции инициализации и завершения из общего описателя, чтобы запустить и остановить плагин:

  • daemon_example_plugin_init() открывает файл и порождает поток, который периодически просыпается и пишет следующее сообщение в файл.

  • daemon_example_plugin_deinit() закрывает файл и выполняет другую уборку.

Чтобы собрать и установить файл библиотеки, используйте инструкции в разделе 26.2.4.3. Чтобы сделать файл библиотеки доступным для использования, установите его в каталоге плагинов (каталог, названный в системной переменной plugin_dir). Для плагина daemon_example это собрано и установлено, когда Вы создаете MySQL из исходных текстов. Это также включено в двоичные дистрибутивы. Процесс сборки производит совместно используемую библиотеку объекта с названием libdaemon_example.so.

Чтобы использовать плагин, зарегистрируйте его на сервере. Например, чтобы зарегистрировать плагин во время выполнения, используйте этот запрос (корректируйте суффикс .so для Вашей платформы по мере необходимости):

INSTALL PLUGIN daemon_example SONAME 'libdaemon_example.so';

Чтобы проверить установку, исследуйте таблицу INFORMATION_SCHEMA.PLUGINS или используйте команду SHOW PLUGINS. См. раздел 6.6.3.

В то время как плагин загружен, он пишет строку равномерно в файл mysql-heartbeat.log в каталоге данных. Этот файл растет без предела, таким образом после того, как у Вас есть уверенность, что плагин работает правильно, выгрузите его:

UNINSTALL PLUGIN daemon_example;

26.2.4.6. Написание плагинов INFORMATION_SCHEMA

Этот раздел описывает, как написать плагин INFORMATION_SCHEMA. Для примера кода, который осуществляет такие плагины, см. файл sql/sql_show.cc в исходных текстах MySQL. Вы можете также в качестве примера смотреть плагины в исходных текстах InnoDB. См. файлы handler/i_s.cc и handler/ha_innodb.cc в дереве исходных текстов InnoDB (каталог storage/innobase ).

Чтобы написать плагин INFORMATION_SCHEMA включите следующие заголовочные файлы в исходный файл:

#include <sql_class.h>
#include <table.h>
Эти заголовочные файлы расположены в каталоге sql исходных текстов MySQL. Они содержат C++ структуры, таким образом, исходный файл для плагина INFORMATION_SCHEMA должен быть собран как C++ (не C) код.

Исходный файл для плагина в качестве примера, развитого здесь, называется simple_i_s_table.cc. Это создает простую таблицу INFORMATION_SCHEMA с именем SIMPLE_I_S_TABLE с двумя столбцами NAME и VALUE. Общий описатель для библиотеки, которая осуществляет таблицу, похож на это:

mysql_declare_plugin(simple_i_s_library)
{
  MYSQL_INFORMATION_SCHEMA_PLUGIN,
  &simple_table_info,              /* type-specific descriptor */
  "SIMPLE_I_S_TABLE",                  /* table name */
  "Author Name",                       /* author */
  "Simple INFORMATION_SCHEMA table",   /* description */
  PLUGIN_LICENSE_GPL,                  /* license type */
  simple_table_init,                   /* init function */
  NULL,
  0x0100,                              /* version = 1.0 */
  NULL,                                /* no status variables */
  NULL,                                /* no system variables */
  NULL,                                /* no reserved information */
  0                                    /* no flags */
}
mysql_declare_plugin_end;
Член name member (SIMPLE_I_S_TABLE) указывает на имя, чтобы использовать для ссылок на плагин в запросе INSTALL PLUGIN или UNINSTALL PLUGIN. Это также имя, выведенное на экран SHOW PLUGINS или INFORMATION_SCHEMA.PLUGINS.

Член simple_table_info общего описателя указывает на определенный для типа описатель, который состоит только из определенного для типа номера версии API:

static struct st_mysql_information_schema simple_table_info =
  {MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION};
Общий описатель указывает на функции инициализации и завершения:

  • Функция инициализации предоставляет информацию о структуре таблицы и функции, которая заполняет таблицу.

  • Функция завершения выполняет любую необходимую уборку. Если никакая уборка не необходима, этот дескрипторный член может быть NULL (как в показанном примере).

Функция инициализации должна возвратить 0 для успеха, 1, если ошибка происходит. Функция получает указатель, который она должна интерпретировать как указатель на структуру таблицы:

static int table_init(void *ptr)
{
  ST_SCHEMA_TABLE *schema_table = (ST_SCHEMA_TABLE*) ptr;
  schema_table->fields_info = simple_table_fields;
  schema_table->fill_table = simple_fill_table;
  return 0;
}
Функция должна установить эти двух членов структуры таблицы:

  • fields_info: Массив структур ST_FIELD_INFO, которые содержат информацию о каждом столбце.

  • fill_table: Функция, которая заполняет таблицу.

Массив, который указывают fields_info, должен содержать один элемент на столбец INFORMATION_SCHEMA плюс заканчивающий элемент. Следующий массив simple_table_fields для плагина в качестве примера указывает на таблицу SIMPLE_I_S_TABLE, которая имеет два столбца. NAME это строка с длиной 10, а VALUE integer с шириной отображения 20. Последняя структура отмечает конец массива.

static ST_FIELD_INFO simple_table_fields[] =
{
  {"NAME", 10, MYSQL_TYPE_STRING, 0, 0 0, 0},
  {"VALUE", 6, MYSQL_TYPE_LONG, 0, MY_I_S_UNSIGNED, 0, 0},
  {0, 0, MYSQL_TYPE_NULL, 0, 0, 0, 0}
};
Для получения дополнительной информации об информационной структуре столбца, см. определение ST_FIELD_INFO в файле заголовка table.h. Допустимое значения MYSQL_TYPE_xxx используемые в C API: см. раздел 25.8.5.

Член fill_table должен быть установлен в функцию, которая заполняет таблицу и возвращает 0 для успеха, 1, если ошибка происходит. Для плагина в качестве примера, функция simple_fill_table() похожа на это:

static int simple_fill_table(THD *thd, TABLE_LIST *tables, Item *cond)
{
  TABLE *table= tables->table;

  table->field[0]->store("Name 1", 6, system_charset_info);
  table->field[1]->store(1);
  if (schema_table_store_record(thd, table)) return 1;
  table->field[0]->store("Name 2", 6, system_charset_info);
  table->field[1]->store(2);
  if (schema_table_store_record(thd, table)) return 1;
  return 0;
}
Для каждой строки таблицы INFORMATION_SCHEMA эта функция инициализирует каждый столбец, затем вызывает schema_table_store_record(), чтобы установить строку. Параметры метода store() зависят от типа значения, которое будет сохранено. Для столбца 0 (NAME, строка), store() берет указатель на строку, ее длину, и информацию о наборе символов строки:
store(const char *to, uint length, CHARSET_INFO *cs);
Для столбца 1 (VALUE, integer), store() берет значение и флаг, указывающий, есть ли знак:
store(longlong nr, bool unsigned_value);
Для других примеров того, как заполнить таблицы INFORMATION_SCHEMA ищите schema_table_store_record() в sql_show.cc.

Чтобы собрать и установить файл библиотеки, используйте инструкции в разделе 26.2.4.3. Чтобы сделать файл библиотеки доступным для использования, установите его в каталог плагинов (указан в системной переменной plugin_dir).

Чтобы проверить плагин, установите это:

mysql> INSTALL PLUGIN SIMPLE_I_S_TABLE SONAME 'simple_i_s_table.so';
Проверьте, что таблица присутствует:
mysql> SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
    -> WHERE TABLE_NAME = 'SIMPLE_I_S_TABLE';
+------------------+
| TABLE_NAME       |
+------------------+
| SIMPLE_I_S_TABLE |
+------------------+
Попытайтесь выбрать данные из нее:
mysql> SELECT * FROM INFORMATION_SCHEMA.SIMPLE_I_S_TABLE;
+--------+-------+
| NAME   | VALUE |
+--------+-------+
| Name 1 | 1     |
| Name 2 | 2     |
+--------+-------+
Удалите это:
mysql> UNINSTALL PLUGIN SIMPLE_I_S_TABLE;

26.2.4.7. Написание полусинхронных плагинов репликации

Этот раздел описывает, как написать полусинхронные плагины репликации, используя в качестве примера плагины в каталоге plugin/semisync дистрибутива исходных текстов MySQL. Этот каталог содержит исходные файлы для основных и ведомых систем под именами rpl_semi_sync_master и rpl_semi_sync_slave. Информация здесь покрывает только, как настроить структуру. Для деталей о том, как плагины осуществляют функции репликации, см. исходный текст.

Чтобы написать полусинхронный плагин, включите следующий заголовочный файл в исходный файл плагина.

#include <mysql/plugin.h>
plugin.h определяет тип плагина сервера MYSQL_REPLICATION_PLUGIN и структуры данных

Для основной стороны semisync_master_plugin.cc содержит этот общий описатель для плагина rpl_semi_sync_master:

mysql_declare_plugin(semi_sync_master)
{
  MYSQL_REPLICATION_PLUGIN,
  &semi_sync_master_plugin,
  "rpl_semi_sync_master",
  "He Zhenxing",
  "Semi-synchronous replication master",
  PLUGIN_LICENSE_GPL,
  semi_sync_master_plugin_init,   /* Plugin Init */
  semi_sync_master_plugin_deinit, /* Plugin Deinit */
  0x0100 /* 1.0 */,
  semi_sync_master_status_vars, /* status variables */
  semi_sync_master_system_vars, /* system variables */
  NULL,    /* config options */
  0,       /* flags */
}
mysql_declare_plugin_end;
Для ведомой стороны semisync_slave_plugin.cc содержит этот общий описатель для плагина rpl_semi_sync_slave:
mysql_declare_plugin(semi_sync_slave)
{
  MYSQL_REPLICATION_PLUGIN,
  &semi_sync_slave_plugin,
  "rpl_semi_sync_slave",
  "He Zhenxing",
  "Semi-synchronous replication slave",
  PLUGIN_LICENSE_GPL,
  semi_sync_slave_plugin_init,   /* Plugin Init */
  semi_sync_slave_plugin_deinit, /* Plugin Deinit */
  0x0100 /* 1.0 */,
  semi_sync_slave_status_vars,   /* status variables */
  semi_sync_slave_system_vars,   /* system variables */
  NULL,   /* config options */
  0,      /* flags */
}
mysql_declare_plugin_end;
Для основного и для ведомого плагинов у общего описателя есть указатели на определенный для типа описатель, функции инициализации и завершения и переменные состояния и системные, осуществленные плагином. Для информации об установке переменных, см. раздел 26.2.4.2.2. Следующие замечания обсуждают определенный для типа описатель и функции инициализации и завершения для основного плагина, но применяются также к ведомому плагину.

Член semi_sync_master_plugin описателя общего дескриптора указывает на определенный для типа описатель, который состоит только из определенного для типа номера версии API:

struct Mysql_replication semi_sync_master_plugin = {
  MYSQL_REPLICATION_INTERFACE_VERSION
};
Функции инициализации и завершения похожи на это:
static int semi_sync_master_plugin_init(void *p);
static int semi_sync_master_plugin_deinit(void *p);
Функция инициализации использует указатель, чтобы зарегистрировать наблюдатель транзакции и двоичного протокола на сервере. После успешной инициализации сервер заботится о вызове наблюдателей в подходящее время. Для деталей о наблюдателях см. исходные файлы. Функция завершения вычеркивает наблюдателей из списка. Каждая функция возвращает 0 для успеха или 1, если ошибка происходит.

Чтобы собрать и установить файл библиотеки, используйте инструкции в разделе 26.2.4.3. Чтобы сделать файл библиотеки доступным для использования, установите его в каталог плагинов, указанный в системной переменной plugin_dir. Для плагинов rpl_semi_sync_master и rpl_semi_sync_slave они собраны и установлены, когда Вы создаете MySQL из исходных текстов. Они также включены в двоичные дистрибутивы. Процесс сборки производит совместно используемые библиотеки объекта с названиями semisync_master.so и semisync_slave.so.

26.2.4.8. Написание плагинов аудита

Этот раздел описывает, как написать плагин аудита, используя в качестве примера плагин в каталоге plugin/audit_null исходных текстов MySQL. Исходные файлы audit_null.c и audit_null_variables.h в этом каталоге реализуют плагин NULL_AUDIT.

Другими примерами плагинов, которые используют контрольный плагин API, является плагин перезаписи запроса (см. раздел 6.6.4) и плагин маркеров версий (см. раздел 6.6.5).

В пределах сервера контрольный интерфейс осуществлен в файлах sql_audit.h и sql_audit.cc каталога sql дистрибутива исходных текстов MySQL. Дополнительно несколько мест в сервере называют контрольный интерфейс, когда событие аудита имеет место, так, чтобы зарегистрированные контрольные плагины могли быть уведомлены в случае необходимости. Чтобы видеть, где такие требования происходят, ищите исходные файлы сервера с вызовами функций mysql_audit_xxx(). Контрольное уведомление происходит для операций сервера, таких как:

  • Клиент соединяется и отсоединяется.

  • Запись сообщения в общий журнал запроса (если журнал включен).
  • Запись сообщения в журнал ошибок.
  • Отправка результата запроса клиенту.

Чтобы написать контрольный плагин, включите следующий заголовочный файл в исходный файл:

#include <mysql/plugin_audit.h>
plugin_audit.h включает plugin.h, таким образом, Вы не должны включать последний файл явно. plugin.h определяет тип серверного плагина MYSQL_AUDIT_PLUGIN и структуры данных. plugin_audit.h определяет структуры данных для плагина аудита.

Общий описатель плагина аудита

У контрольного плагина, как у любого плагина сервера MySQL, есть общий описатель (см. раздел 26.2.4.2.1) и определенный для типа описатель. В audit_null.c общий описатель похож на это:

mysql_declare_plugin(audit_null)
{
  MYSQL_AUDIT_PLUGIN,         /* type*/
  &audit_null_descriptor, /* descriptor  */
  "NULL_AUDIT",               /* name*/
  "Oracle Corp",              /* author  */
  "Simple NULL Audit",        /* description */
  PLUGIN_LICENSE_GPL,
  audit_null_plugin_init,     /* init function (when loaded) */
  audit_null_plugin_deinit,   /* deinit function (when unloaded) */
  0x0003,                     /* version */
  simple_status,              /* status variables*/
  system_variables,           /* system variables*/
  NULL, 0,
}
mysql_declare_plugin_end;
Первый член, MYSQL_AUDIT_PLUGIN, идентифицирует этот плагин как контрольный плагин.

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

Член name (NULL_AUDIT) указывает на имя, чтобы использовать для ссылок на плагин в INSTALL PLUGIN или UNINSTALL PLUGIN. Это также имя, выведенное на экран INFORMATION_SCHEMA.PLUGINS или SHOW PLUGINS.

Функция инициализации audit_null_plugin_init выполняет инициализацию, когда плагин загружен. Функция audit_null_plugin_deinit завершает плагин.

Общий описатель также обращается к simple_status и system_variables, структурам, которые выставляют несколько состояний и системных переменных. Когда плагин включен, эти переменные могут быть просмотрены, используя SHOW (SHOW STATUS, SHOW VARIABLES) или соответствующие таблицы Performance Schema.

Структура simple_status объявляет несколько переменных состояния с названиями формы Audit_null_xxx. NULL_AUDIT увеличивает переменную состояния Audit_null_called для каждого полученного уведомления. Другие переменные состояния являются более специфичными и NULL_AUDIT постепенно увеличивает их только для уведомлений об определенных событиях.

system_variables массив системных переменных, каждая из которых определена, используя макрос MYSQL_THDVAR_xxx. У этих системных переменных есть названия формы null_audit_xxx. Эти переменные могут использоваться, чтобы общаться с плагином во время выполнения.

Определенный для типа описатель

Значение audit_null_descriptor в общем описателе указывает на определенный для типа описатель. Для контрольных плагинов у этого описателя есть следующая структура (определенный в plugin_audit.h):

struct st_mysql_audit
{
  int interface_version;
  void (*release_thd)(MYSQL_THD);
  int (*event_notify)(MYSQL_THD, mysql_event_class_t, const void *);
  unsigned long class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE];
};
У определенного для типа описателя для контрольных плагинов есть эти члены:

  • interface_version: В соответствии с соглашением, определенные для типа описатели начинаются с версии интерфейса для данного типа плагинов. Сервер проверяет interface_version когда загружает плагин, чтобы видеть, совместим ли плагин с ним. Для контрольных плагинов значение члена interface_version MYSQL_AUDIT_INTERFACE_VERSION (определено в plugin_audit.h).

  • release_thd: Функция, которую сервер вызывает, чтобы сообщить плагину, что он отделяется от контекста потока. Это должно быть NULL, если нет такой функции.
  • event_notify: Функция, которую сервер вызывает, чтобы уведомить плагин, что событие имело место. Эта функция не должна быть NULL: это не имело бы смысла, потому что аудит не произойдет.
  • class_mask: Массив элементов MYSQL_AUDIT_CLASS_MASK_SIZE. Каждый элемент определяет битовую маску для данного класса событий, чтобы указать на подклассы, для которых плагин хочет получить уведомление. Это то, как плагин подписывается на интересные события. Элемент должен быть 0, чтобы проигнорировать события для соответствующего класса событий.

Сервер использует функции event_notify и release_thd вместе. Их вызывают в пределах контекста определенного потока, и поток производит несколько уведомлений событий. В первый раз, когда сервер вызывает event_notify для потока, это создает привязку плагина к потоку. Плагин не может быть удален, в то время как эта привязка существует. Когда больше событий для потока не будет, сервер сообщает плагину об этом, вызывая функцию release_thd и и затем разрушает привязку. Например, когда клиент делает запрос, поток, обрабатывающий запрос, может уведомить контрольные плагины о наборе результатов, произведенном запросом и о зарегистрированном запросе. После того, как эти уведомления происходят, сервер освобождает плагин прежде, чем поместить поток в спячку, пока клиент не делает другой запрос.

Этот проект позволяет плагину выделить ресурсы, необходимые для данного потока, в первом вызове функции event_notify и освободить их в функции release_thd:

event_notify function:
  if memory is needed to service the thread
allocate memory
  ... rest of notification processing ...

release_thd function:
  if memory was allocated
release memory
  ... rest of release processing ...
Это более эффективно чем выделение и освобождение памяти неоднократно в функции уведомления.

Для плагина NULL_AUDIT определенный для типа описатель похож на это:

static struct st_mysql_audit audit_null_descriptor =
{
  MYSQL_AUDIT_INTERFACE_VERSION,  /* interface version*/
  NULL,                           /* release_thd function */
  audit_null_notify,              /* notify function*/
  {(unsigned long) MYSQL_AUDIT_GENERAL_ALL,
   (unsigned long) MYSQL_AUDIT_CONNECTION_ALL,
   (unsigned long) MYSQL_AUDIT_PARSE_ALL,
   (unsigned long) MYSQL_AUDIT_AUTHORIZATION_ALL,
   (unsigned long) MYSQL_AUDIT_TABLE_ACCESS_ALL,
   (unsigned long) MYSQL_AUDIT_GLOBAL_VARIABLE_ALL,
   (unsigned long) MYSQL_AUDIT_SERVER_STARTUP_ALL,
   (unsigned long) MYSQL_AUDIT_SERVER_SHUTDOWN_ALL,
   (unsigned long) MYSQL_AUDIT_COMMAND_ALL,
   (unsigned long) MYSQL_AUDIT_QUERY_ALL,
   (unsigned long) MYSQL_AUDIT_STORED_PROGRAM_ALL }
};
Сервер вызывает audit_null_notify(), чтобы передать контрольную информацию о событии плагину. Функции release_thd нет.

Член class_mask массив, который указывает, на какие классы событий плагин подписывается. Как показано, содержание массива подписывается на все подклассы всех классов событий, которые доступны. Чтобы проигнорировать все уведомления для данного класса событий, определите соответствующий элемент class_mask как 0.

Число элементов class_mask соответствует числу классов событий, каждый из которых перечислен в mysql_event_class_t перечислении определенном в plugin_audit.h:

typedef enum
{
  MYSQL_AUDIT_GENERAL_CLASS  = 0,
  MYSQL_AUDIT_CONNECTION_CLASS = 1,
  MYSQL_AUDIT_PARSE_CLASS= 2,
  MYSQL_AUDIT_AUTHORIZATION_CLASS= 3,
  MYSQL_AUDIT_TABLE_ACCESS_CLASS = 4,
  MYSQL_AUDIT_GLOBAL_VARIABLE_CLASS  = 5,
  MYSQL_AUDIT_SERVER_STARTUP_CLASS   = 6,
  MYSQL_AUDIT_SERVER_SHUTDOWN_CLASS  = 7,
  MYSQL_AUDIT_COMMAND_CLASS  = 8,
  MYSQL_AUDIT_QUERY_CLASS= 9,
  MYSQL_AUDIT_STORED_PROGRAM_CLASS   = 10,
  /* This item must be last in the list. */
  MYSQL_AUDIT_CLASS_MASK_SIZE
} mysql_event_class_t;
Для любого данного класса событий plugin_audit.h определяет символы битовой маски для подклассов одиночных событий, так же как символ xxx_ALL, который является объединением всех битовых масок подкласса. Например, для MYSQL_AUDIT_CONNECTION_CLASS (класс, который покрывает события соединения и отсоединения) plugin_audit.h определяет эти символы:
typedef enum
{
  /** occurs after authentication phase is completed. */
  MYSQL_AUDIT_CONNECTION_CONNECT  = 1 << 0,
  /** occurs after connection is terminated. */
  MYSQL_AUDIT_CONNECTION_DISCONNECT = 1 << 1,
  /** occurs after COM_CHANGE_USER RPC is completed. */
  MYSQL_AUDIT_CONNECTION_CHANGE_USER= 1 << 2,
  /** occurs before authentication. */
  MYSQL_AUDIT_CONNECTION_PRE_AUTHENTICATE = 1 << 3
} mysql_event_connection_subclass_t;

#define MYSQL_AUDIT_CONNECTION_ALL (MYSQL_AUDIT_CONNECTION_CONNECT | \
        MYSQL_AUDIT_CONNECTION_DISCONNECT | \
        MYSQL_AUDIT_CONNECTION_CHANGE_USER | \
        MYSQL_AUDIT_CONNECTION_PRE_AUTHENTICATE)
Чтобы подписаться на все подклассы класса соединения событий (как делает плагин NULL_AUDIT), плагин определяет MYSQL_AUDIT_CONNECTION_ALL в соответствующем class_mask элементе (class_mask[1] в этом случае). Чтобы подписаться только на некоторые подклассы, плагин устанавливает элемент class_mask в объединение подклассов. Например, чтобы подписаться только на подклассы соединения и изменения пользователя, надо установить class_mask[1] к этому значению:
MYSQL_AUDIT_CONNECTION_CONNECT | MYSQL_AUDIT_CONNECTION_CHANGE_USER
Функция уведомления плагина аудита

Большая часть работы для контрольного плагина происходит в функции уведомления (член event_notify определенного для типа описателя). Сервер вызывает эту функцию для каждого события. У функций уведомления есть этот прототип:

int (*event_notify)(MYSQL_THD, mysql_event_class_t, const void *);
Второй и третий параметры event_notify представляют класс событий и указатель на структуру событий. У событий в различных классах есть различные структуры. Функция уведомления может использовать значение класса событий, чтобы определить, какая структура событий применяется. Функция обрабатывает событие и возвращает состояние, указывающее, должен ли сервер продолжить обрабатывать событие или закончить.

Для NULL_AUDIT функция уведомления audit_null_notify(). Эта функция постепенно увеличивает глобальный счетчик событий (который плагин выставляет как значение состояния Audit_null_called) и затем исследует класс событий, чтобы определить, как обработать структуру событий:

static int audit_null_notify(MYSQL_THD thd __attribute__((unused)),
                             mysql_event_class_t event_class,
                             const void *event)
{
  ...
  number_of_calls++;

  if (event_class == MYSQL_AUDIT_GENERAL_CLASS) {
     const struct mysql_event_general *event_general =
           (const struct mysql_event_general *)event;
  ...
  }
  else if (event_class == MYSQL_AUDIT_CONNECTION_CLASS) {
    const struct mysql_event_connection *event_connection =
          (const struct mysql_event_connection *) event;
  ...
  }
  else if (event_class == MYSQL_AUDIT_PARSE_CLASS) {
    const struct mysql_event_parse *event_parse =
          (const struct mysql_event_parse *)event;
  ...
  }
  ...
}
Функция уведомления интерпретирует параметр event согласно значению event_class. Параметр event указатель на запись события, структура которого отличается в зависимости от класса события. Файл plugin_audit.h содержит структуры, которые определяют содержание каждого класса событий. Для каждого класса audit_null_notify() бросает случай к соответствующей определенной для класса структуре и затем проверяет ее подкласс, чтобы определить, который счетчик увеличить. Например, код, чтобы обработать события в классе соединения похож на это:
else if (event_class == MYSQL_AUDIT_CONNECTION_CLASS)
{
  const struct mysql_event_connection *event_connection =
        (const struct mysql_event_connection *) event;
  switch (event_connection->event_subclass)
  {
    case MYSQL_AUDIT_CONNECTION_CONNECT:
      number_of_calls_connection_connect++;
      break;
    case MYSQL_AUDIT_CONNECTION_DISCONNECT:
      number_of_calls_connection_disconnect++;
      break;
    case MYSQL_AUDIT_CONNECTION_CHANGE_USER:
      number_of_calls_connection_change_user++;
      break;
    case MYSQL_AUDIT_CONNECTION_PRE_AUTHENTICATE:
      number_of_calls_connection_pre_authenticate++;
      break;
    default:
      break;
  }
}

Общий класс событий (MYSQL_AUDIT_GENERAL_CLASS) устарел в MySQL 5.7.9 и будет удален в будущем выпуске MySQL. Чтобы уменьшить накладные расходы плагина, предпочтительно подписаться только на классы более определенного события.

Для некоторых классов событий плагин NULL_AUDIT выполняет другую обработку в дополнение к постепенному увеличению счетчика. В любом случае когда функция уведомления заканчивает обрабатывать событие, она должна возвратить состояние, указывающее, должен ли сервер продолжить обрабатывать событие или закончить.

Обработка ошибок плагина

Функция уведомления может сообщить значение состояния для текущего события двумя путями:

  • Использовать возвращаемое значение функции уведомления. В этом случае функция возвращает ноль, если сервер должен продолжить обрабатывать событие, или отличное от нуля значение, если сервер должен закончить.

  • Вызвать my_message(), чтобы установить статус ошибки прежде, чем возвратиться из функции уведомления. В этом случае возвращаемое значение функции уведомления проигнорировано, и сервер заканчивает обработку событий с ошибкой. Параметры my_message() указывают, какую ошибку объявить и ее сообщение. Например:
    my_message(ER_AUDIT_API_ABORT, "This is my error message.", MYF(0));
    
    Некоторые события не могут быть прерваны. Возвращаемое значение, отличное от нуля, не будет учтено и вызов my_message() должен следовать is_error(). Например:
    if (!thd->get_stmt_da()->is_error())
    {
      my_message(ER_AUDIT_API_ABORT, "This is my error message.", MYF(0));
    }
    

Некоторые события не могут быть закончены:

  • MYSQL_AUDIT_CONNECTION_DISCONNECT: Сервер не может препятствовать тому, чтобы клиент отсоединился.

  • MYSQL_AUDIT_COMMAND_END: Этот случай обеспечивает состояние команды, которая закончила выполнение, таким образом нет никакой причины завершать ее явно.

Если контрольный плагин возвращает состояние отличное от нуля для непрерываемого события, сервер игнорирует состояние и продолжает его обрабатывать. Это также истина, если контрольный плагин использует функцию my_message(), чтобы закончить непрерываемое событие.

Использование плагина аудита

Чтобы собрать и установить файл библиотеки, используйте инструкции в раздел 26.2.4.3. Чтобы сделать файл библиотеки доступным для использования, установите его в каталог плагинов (на него указывает системная переменная plugin_dir). Для плагина NULL_AUDIT это собрано и установлено, когда Вы создаете MySQL из исходных текстов. Это также включено в двоичные дистрибутивы. Процесс сборки производит совместно используемую библиотеку объекта с названием adt_null.so.

Чтобы зарегистрировать плагин во время выполнения, используйте этот запрос (корректируйте суффикс .so для Вашей платформы по мере необходимости):

INSTALL PLUGIN NULL_AUDIT SONAME 'adt_null.so';

Чтобы проверить установку, исследуйте таблицу INFORMATION_SCHEMA.PLUGINS или используйте SHOW PLUGINS . См. раздел 6.6.3.

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

mysql> SHOW STATUS LIKE 'Audit_null%';
+----------------------------------------+--------+
| Variable_name                          | Value  |
+----------------------------------------+--------+
| Audit_null_authorization_column        | 0      |
| Audit_null_authorization_db            | 0      |
| Audit_null_authorization_procedure     | 0      |
| Audit_null_authorization_proxy         | 0      |
| Audit_null_authorization_table         | 0      |
| Audit_null_authorization_user          | 0      |
| Audit_null_called                      | 185547 |
| Audit_null_command_end                 | 20999  |
| Audit_null_command_start               | 21001  |
| Audit_null_connection_change_user      | 0      |
| Audit_null_connection_connect          | 5823   |
| Audit_null_connection_disconnect       | 5818   |
| Audit_null_connection_pre_authenticate | 5823   |
| Audit_null_general_error               | 1      |
| Audit_null_general_log                 | 26559  |
| Audit_null_general_result              | 19922  |
| Audit_null_general_status              | 21000  |
| Audit_null_global_variable_get         | 0      |
| Audit_null_global_variable_set         | 0      |
| Audit_null_parse_postparse             | 14648  |
| Audit_null_parse_preparse              | 14648  |
| Audit_null_query_nested_start          | 6      |
| Audit_null_query_nested_status_end     | 6      |
| Audit_null_query_start                 | 14648  |
| Audit_null_query_status_end            | 14647  |
| Audit_null_server_shutdown             | 0      |
| Audit_null_server_startup              | 1      |
| Audit_null_table_access_delete         | 104    |
| Audit_null_table_access_insert         | 2839   |
| Audit_null_table_access_read           | 97842  |
| Audit_null_table_access_update         | 278    |
+----------------------------------------+--------+
Audit_null_called считает все события, другие переменные считают подклассы определенного события. Например, предыдущий запрос SHOW STATUS заставляет сервер послать результат клиенту и записать сообщение в общий журнал запроса, если этот журнал включен. Таким образом, клиент, который делает запрос неоднократно, вызывает увеличение каждый раз Audit_null_called, Audit_null_general_result и Audit_null_general_log. Уведомления происходят независимо от того, включен ли журнал.

Значения переменных состояния едины для всех сеансов. Нет никаких счетчиков для отдельных сеансов.

NULL_AUDIT выставляет несколько системных переменных, которые включают коммуникации с плагином во время выполнения:

mysql> SHOW VARIABLES LIKE 'null_audit%';
+------------------------------------+-------+
| Variable_name                      | Value |
+------------------------------------+-------+
| null_audit_abort_message           |       |
| null_audit_abort_value             | 1     |
| null_audit_event_order_check       |       |
| null_audit_event_order_check_exact | 1     |
| null_audit_event_order_started     | 0     |
| null_audit_event_record            |       |
| null_audit_event_record_def        |       |
+------------------------------------+-------+
Чтобы проверить порядок вызова API аудита, установите переменную null_audit_event_order_check к ожидаемому порядку событий. Например:
SET null_audit_event_order_check =
    'MYSQL_AUDIT_CONNECTION_PRE_AUTHENTICATE;;;'
    'MYSQL_AUDIT_GENERAL_LOG;;;'
    'MYSQL_AUDIT_CONNECTION_CONNECT;;';
Запрос использует синтаксис SQL, который связывает смежные строки в единственную строку.

Формат значения:

'event_name;event_data;
 command' [';event_name;
 event_data;command'] ...
После того, как порядок событий соответствует, значение null_audit_event_order_check заменено значением EVENT-ORDER-OK.

Определение значения команды ABORT_RET позволяет прервать аудит для указанного случая. Следующий пример аварийно прекращает работу при выполнении запроса INSERT, когда событие MYSQL_AUDIT_QUERY_STATUS_END имеет место:

SET null_audit_event_order_check =
   'MYSQL_AUDIT_COMMAND_START;command_id="3";;'
   'MYSQL_AUDIT_GENERAL_LOG;;;'
   'MYSQL_AUDIT_QUERY_START;;;'
   'MYSQL_AUDIT_QUERY_STATUS_END;;ABORT_RET';
После того, как контрольный плагин соответствует предыдущей последовательности, он прерывает обработку событий и посылает сообщение об ошибке клиенту:
ERROR 3164 (HY000): Aborted by Audit API ('MYSQL_AUDIT_QUERY_STATUS_END';1).
Возвращение ненулевого значения из API аудита стандартный способ прервать выполнение события. Также возможно определить пользовательский код ошибки, устанавливая переменную null_audit_abort_value к значению, которое должна возвратить функция уведомления:
SET null_audit_abort_value = 123;
Прерывание последовательности приводит к стандартному сообщению с пользовательским кодом ошибки. Предположите, что Вы устанавливаете контрольные системные переменные журнала так:
SET null_audit_abort_value = 123;
SET null_audit_event_order_check =
    'MYSQL_AUDIT_COMMAND_START;command_id="3";;'
    'MYSQL_AUDIT_GENERAL_LOG;;;'
    'MYSQL_AUDIT_QUERY_START;;ABORT_RET';
Тогда выполнение SELECT 1 приведет к этой ошибке:
ERROR 3164 (HY000): Aborted by Audit API ('MYSQL_AUDIT_QUERY_START';123).
Случай может быть также прерван с пользовательским сообщением, определенным установкой переменной null_audit_abort_message. Предположите, что Вы устанавливаете контрольные системные переменные журнала так:
SET null_audit_abort_message = 'Custom error text.';
SET null_audit_event_order_check =
    'MYSQL_AUDIT_COMMAND_START;command_id="3";;'
    'MYSQL_AUDIT_GENERAL_LOG;;;'
    'MYSQL_AUDIT_QUERY_START;;ABORT_RET';
Тогда прерывание последовательности приводит к следующей ошибке:
ERROR 3164 (HY000): Custom error text.
В испытательных целях возможно сделать запись событий, которые проходят через плагин. Запись запускается, определяя, начало и конец события в переменной null_audit_event_record_def:
SET null_audit_event_record_def =
    'MYSQL_AUDIT_COMMAND_START;MYSQL_AUDIT_COMMAND_END';
Выполнение запроса приводит к хранению в переменной null_audit_event_record событий, которые происходят.

Чтобы отключить плагин после тестирования, используйте этот запрос, чтобы выгрузить плагин:

UNINSTALL PLUGIN NULL_AUDIT;

26.2.4.9. Написание плагина аутентификации

MySQL поддерживает модульную аутентификацию, в которой плагины вызваны, чтобы подтвердить подлинность соединений клиента. Плагины аутентификации включают использование методов аутентификации кроме встроенного метода паролей, сохраненных в таблице mysql.user. Например, плагины могут быть написаны, чтобы получить доступ к внешним методам аутентификации. Кроме того, плагины аутентификации могут поддержать прокси. Для получения дополнительной информации, см. разделы 7.3.9 и 7.3.10.

Плагин аутентификации может быть написан для стороны сервера или стороны клиента. Серверные плагины используют тот же самый API, который используется для других типов плагина сервера, таких как полнотекстовый анализатор или контрольные плагины (хотя с иным определенным для типа описателем). Клиентские плагины используют API плагина клиента.

Несколько заголовочных файлов содержат информацию, относящуюся к плагинам аутентификации:

  • plugin.h: Определяет тип плагина сервера MYSQL_AUTHENTICATION_PLUGIN.

  • client_plugin.h: Определяет API для плагинов клиента. Это включает описатель плагина клиента и функциональные прототипы плагина клиента C API (см. раздел 25.8.14).
  • plugin_auth.h: Определяет часть API плагина сервера, определенный для плагинов аутентификации. Это включает определенный для типа описатель для серверных плагинов аутентификации и структуру MYSQL_SERVER_AUTH_INFO.
  • plugin_auth_common.h: Содержит общие элементы плагинов аутентификации клиента и сервера. Это включает определения возвращаемого значения и структуру MYSQL_PLUGIN_VIO.

Чтобы написать плагин аутентификации, включите следующие заголовочные файлы в исходный файл.

  • Для исходного файла, который осуществляет плагин аутентификации сервера, включите этот файл:

    #include <mysql/plugin_auth.h>
    
  • Для исходного файла, который осуществляет плагин аутентификации клиента, или оба плагина (клиента и сервера), включите эти файлы:
    #include <mysql/plugin_auth.h>
    #include <mysql/client_plugin.h>
    #include <mysql.h>
    

plugin_auth.h включает plugin.h и plugin_auth_common.h, таким образом, Вы не должны включать последние файлы явно.

Этот раздел описывает, как написать пару простых плагинов аутентификации клиента и сервера, которые сотрудничают.

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

Серверные и клиентские плагины оба называются auth_simple. Как написано в разделе 26.2.4.2, у файла библиотеки должно быть то же самое базовое имя, как у плагина клиента, таким образом, имя исходного файла auth_simple.c и библиотеки auth_simple.so (предполагается, что Ваша система использует .so как суффикс для файлов библиотеки).

В дистрибутиве исходных текстов MySQL исходный текст плагина аутентификации расположен в каталоге plugin/auth и может быть исследован как руководство по написанию других плагинов аутентификации. Кроме того, чтобы видеть, как встроенные плагины аутентификации осуществлены, см. sql/sql_acl.cc для плагинов, которые встроены в сервер MySQL и sql-common/client.c для плагинов, которые встроены в клиентскую бибилотеку libmysqlclient. Для встроенных плагинов клиента, отметьте что структуры auth_plugin_t, используемые там, отличаются от структур, используемых с обычным макроопределением декларации плагина клиента. В частности первые два члена указаны явно, а не через макроопределение декларации.

26.2.4.9.1. Написание серверного плагина аутентификации

Объявите серверный плагин с обычным общим дескрипторным форматом, который используется для всех типов плагина сервера (см. раздел 26.2.4.2.1). Для плагина auth_simple описатель похож на это:

mysql_declare_plugin(auth_simple)
{
  MYSQL_AUTHENTICATION_PLUGIN,
  &auth_simple_handler,             /* type-specific descriptor */
  "auth_simple",                        /* plugin name */
  "Author Name",                        /* author */
  "Any-password authentication plugin", /* description */
  PLUGIN_LICENSE_GPL,                   /* license type */
  NULL,                                 /* no init function */
  NULL,                                 /* no deinit function */
  0x0100,                               /* version = 1.0 */
  NULL,                                 /* no status variables */
  NULL,                                 /* no system variables */
  NULL,                                 /* no reserved information */
  0                                     /* no flags */
}
mysql_declare_plugin_end;
Член name (auth_simple) указывает на имя, чтобы использовать для ссылок на плагин в INSTALL PLUGIN или UNINSTALL PLUGIN. Это также имя, выведенное на экран SHOW PLUGINS или INFORMATION_SCHEMA.PLUGINS.

Член auth_simple_handler общего описателя указывает на определенный для типа описатель. Для плагина аутентификации определенный для типа описатель экземпляр структуры st_mysql_auth (определена в plugin_auth.h):

struct st_mysql_auth
{
  int interface_version;
  const char *client_auth_plugin;
  int (*authenticate_user) (MYSQL_PLUGIN_VIO *vio,
                            MYSQL_SERVER_AUTH_INFO *info);
  int (*generate_authentication_string)(char *outbuf, unsigned int *outbuflen,
                                        const char *inbuf,
                                        unsigned int inbuflen);
  int (*validate_authentication_string) (char* const inbuf,
                                         unsigned int buflen);
  int (*set_salt) (const char *password, unsigned int password_len,
                   unsigned char* salt, unsigned char *salt_len);
  const unsigned long authentication_flags;
};
Структура st_mysql_auth имеет члены:

  • interface_version: Определенный для типа номер версии API, всегда MYSQL_AUTHENTICATION_INTERFACE_VERSION.

  • client_auth_plugin: Имя плагина клиента.
  • authenticate_user: Указатель на основную функцию, которая общается с клиентом.
  • generate_authentication_string: Указатель на функцию, которая производит пароль из строки аутентификации.
  • validate_authentication_string: Указатель на функцию, которая утверждает пароль.
  • set_salt: Указатель на функцию, которая преобразует скремблированный пароль в двоичную форму.
  • authentication_flags: Слово флагов.

Член client_auth_plugin должен указать на название плагина клиента, если определенный плагин требуется. Значение NULL определяет "любой плагин". Это полезно, если плагин сервера не заботится о плагине клиента или том, какое имя пользователя или пароль это посылает. Например, это могло бы быть истиной, если плагин сервера подтверждает подлинность только локальных клиентов и использует некоторое свойство операционной системы, а не информацию, посланную плагином клиента.

Для auth_simple определенный для типа описатель похож на это:

static struct st_mysql_auth auth_simple_handler =
{
  MYSQL_AUTHENTICATION_INTERFACE_VERSION,
  "auth_simple",               /* required client-side plugin name */
  auth_simple_server           /* server-side plugin main function */
  generate_auth_string_hash,   /* generate digest from password string */
  validate_auth_string_hash,   /* validate password digest */
  set_salt,                    /* generate password salt value */
  AUTH_FLAG_PRIVILEGED_USER_FOR_PASSWORD_CHANGE
};
Основная функция, auth_simple_server(), берет два параметра, представляющие структуру ввода/вывода и структуру MYSQL_SERVER_AUTH_INFO. Определение структуры, найденное в plugin_auth.h, похоже на это:
typedef struct st_mysql_server_auth_info
{
  char *user_name;
  unsigned int user_name_length;
  const char *auth_string;
  unsigned long auth_string_length;
  char authenticated_as[MYSQL_USERNAME_LENGTH+1];
  char external_user[512];
  int  password_used;
  const char *host_or_ip;
  unsigned int host_or_ip_length;
} MYSQL_SERVER_AUTH_INFO;
Набор символов для строковых членов UTF-8. Если есть член _length, связанный со строкой, это указывает на длину строки в байтах. Строки также закончены нулем.

Когда плагин аутентификации вызван сервером, он должен интерпретировать члены структуры MYSQL_SERVER_AUTH_INFO. Некоторые из них используются, чтобы установить значение функций SQL или системных переменных в пределах сеанса клиента, как обозначено.

  • user_name: Имя пользователя послано клиентом. Значение становится значением функции USER().

  • user_name_length: Длина user_name в байтах.
  • auth_string: Значение столбца authentication_string строки таблицы mysql.user для соответствующего имени учетной записи (то есть, строка, которая соответствует имени пользователя клиента и имени хоста, которые использует сервер, чтобы определить, как подтвердить подлинность клиента).

    Предположите, что Вы создаете учетную запись, используя следующий запрос:

    CREATE USER 'my_user'@'localhost'
           IDENTIFIED WITH my_plugin AS 'my_auth_string';
    
    Когда my_user соединяется с локального хоста, сервер вызывает my_plugin и передает ему 'my_auth_string' как значение auth_string.
  • auth_string_length: Длина в байтах auth_string.
  • authenticated_as: Сервер устанавливает это в имя пользователя (значение user_name). Плагин может изменить это, чтобы указать, что у клиента должны быть привилегии иного пользователя. Например, если плагин поддерживает пользователей по доверенности, начальное значение имя соединяющегося пользователя (по доверенности), и плагин может изменить этого участника на реальное имя пользователя. Сервер тогда обрабатывает пользователя по доверенности с наличием привилегий реального пользователя (предполагается, что другие условия для поддержки пользователей по доверенности удовлетворены, см. раздел 26.2.4.9.4). Значение представлено как строка самое большее MYSQL_USER_NAME_LENGTH байт в длину плюс заканчивающий нуль. Значение становится значением функции CURRENT_USER().
  • external_user: Сервер устанавливает это в пустую строку (законченную нулем). Значение становится значением системной переменной external_user. Если плагин хочет, чтобы у этой системной переменной было иное значение, он должен установить этот член соответственно, например, к имени соединяющегося пользователя. Значение представлено как строка, самое большее 511 байтов длиной, плюс заканчивающий нуль.
  • password_used: Этот член применяется, когда аутентификация терпит неудачу. Плагин может установить или проигнорировать это. Значение используется, чтобы создать сообщение об ошибке отказа Authentication fails. Password used: %s. Значение password_used определяет, как обработан %s, как показано в следующей таблице.

    password_used Обработка %s
    0Нет
    1Да
    2Должно быть не %s
  • host_or_ip: Название хоста клиента, если это может быть найдено, или IP-адрес иначе.

  • host_or_ip_length: Длина в байтах host_or_ip.

Основная функция auth_simple, auth_simple_server(), читает пароль (законченная нулем строка) с клиента и проходит нормально, если пароль непуст (первый байт не нуль):

static int auth_simple_server (MYSQL_PLUGIN_VIO *vio,
                               MYSQL_SERVER_AUTH_INFO *info)
{
  unsigned char *pkt;
  int pkt_len;

  /* read the password as null-terminated string, fail on error */
  if ((pkt_len = vio->read_packet(vio, &pkt)) < 0) return CR_ERROR;
  /* fail on empty password */
  if (!pkt_len || *pkt == '\0') {
     info->password_used= PASSWORD_USED_NO;
     return CR_ERROR;
  }
  /* accept any nonempty password */
  info->password_used= PASSWORD_USED_YES;
  return CR_OK;
}
Основная функция должна возвратить один из кодов ошибки, показанных в следующей таблице.

Код ошибкиЗначение
CR_OKУспешно
CR_OK_HANDSHAKE_COMPLETE Не послан пакет состояния назад к клиенту
CR_ERRORОшибка
CR_AUTH_USER_CREDENTIALS Отказ аутентификации
CR_AUTH_HANDSHAKE Отказ квитирования аутентификации
CR_AUTH_PLUGIN_ERROR Внутренняя ошибка плагина

Для примера того, как квитирование работает, см. исходный файл plugin/auth/dialog.c.

Сервер считает ошибки в таблице host_cache Performance Schema.

auth_simple_server() не использует информационную структуру аутентификации кроме установки члена, который указывает, был ли пароль получен.

Плагин, который поддерживает пользователей по доверенности, должен возвратить серверу имя реального пользователя (пользователь MySQL, права которого пользователь клиента должен получить). Чтобы сделать это, плагин должен установить член info->authenticated_as в имя реального пользователя. Для информации см. разделы 7.3.10 и 26.2.4.9.4.

Член generate_authentication_string описателя берет пароль и производит хеш пароля:

  • Первые два параметра указатели на выходной буфер и его максимальную длину в байтах. Функция должна написать хеш пароля в выходной буфер и сбросить длину к фактической длине хеша.

  • Вторые два параметра указывают на входной буфер пароля и его длину в байтах.
  • Функция возвращается 0 для успеха, 1, если ошибка произошла.

Для плагина auth_simple функция generate_auth_string_hash() осуществляет член generate_authentication_string. Это только делает копию пароля, если он не слишком длинный, чтобы поместиться в выходной буфер.

int generate_auth_string_hash(char *outbuf, unsigned int *buflen,
const char *inbuf, unsigned int inbuflen)
{
  /* fail if buffer specified by server cannot be copied to output buffer
  */
  if (*buflen < inbuflen) return 1;   /* error */
  strncpy(outbuf, inbuf, inbuflen);
  *buflen = strlen(inbuf);
  return 0; /* success */
}
Член validate_authentication_string описателя утверждает хеш пароля:

  • Параметры указатель на хеш пароля и его длину в байтах.

  • Функция возвращает 0 для успеха, 1, если хеш пароля не может быть утвержден.

Для плагина auth_simple функция validate_auth_string_hash() осуществляет член validate_authentication_string. Это возвращает успех безоговорочно:

int validate_auth_string_hash(char* const inbuf  __attribute__((unused)),
    unsigned int buflen __attribute__((unused)))
{
  return 0; /* success */
}
Член set_salt описателя используется только плагином mysql_native_password (см. раздел 7.5.1.1). Для других плагинов аутентификации Вы можете использовать это тривиальное выполнение:
int set_salt(const char* password __attribute__((unused)),
             unsigned int password_len __attribute__((unused)),
             unsigned char* salt __attribute__((unused)),
             unsigned char* salt_len)
{
  *salt_len= 0;
  return 0; /* success */
}
Член authentication_flags описателя содержит флаги для работы плагина аффекта. Разрешенные флаги:

  • AUTH_FLAG_PRIVILEGED_USER_FOR_PASSWORD_CHANGE: Мандатные изменения привилегированная работа. Если этот флаг установлен, сервер требует, чтобы у пользователя была глобальная привилегия CREATE USER или UPDATE для базы данных mysql.

  • AUTH_FLAG_USES_INTERNAL_STORAGE: Использует ли плагин внутреннее хранение (в столбце authentication_string строк mysql.user). Если этот флаг не установлен, попытки установить использование пароля через SET PASSWORD потерпят неудачу, а сервер произведет предупреждение.

26.2.4.9.2. Написание клиентского плагина аутентификации

Объявите клиентский описатель плагина с макросами mysql_declare_client_plugin() и mysql_end_client_plugin (см. раздел 26.2.4.2.3). Для плагина auth_simple описатель похож на это:

mysql_declare_client_plugin(AUTHENTICATION)
  "auth_simple",   /* plugin name */
  "Author Name",   /* author */
  "Any-password authentication plugin", /* description */
  {1,0,0},   /* version = 1.0.0 */
  "GPL",     /* license type */
  NULL,      /* for internal use */
  NULL,      /* no init function */
  NULL,      /* no deinit function */
  NULL,      /* no option-handling function */
  auth_simple_client    /* main function */
mysql_end_client_plugin;
Дескрипторные члены от имени плагина до обрабатывающей опцию функции характерны для всех типов плагина клиента. Для описаний см. раздел 26.2.4.2.3. После общих членов у описателя есть дополнительный член, определенный для плагинов аутентификации. Это функция main, которая обрабатывает коммуникацию с сервером. Функция берет два параметра, представляющие структуру ввода/вывода и обработчик соединения. Для нашего простого плагина любого пароля основная функция действительно только пишет серверу пароль, обеспеченный пользователем:
static int auth_simple_client (MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
{
  int res;

  /* send password as null-terminated string in clear text */
  res = vio->write_packet(vio, (const unsigned char *) mysql->passwd,
                             strlen(mysql->passwd) + 1);
  return res ? CR_ERROR : CR_OK;
}
Основная функция должна возвратить один из кодов ошибки, показанных в следующей таблице.

Код ошибкиЗначение
CR_OKУспешно.
CR_OK_HANDSHAKE_COMPLETE Успешно, клиент готов.
CR_ERROR Ошибка.

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

26.2.4.9.3. Использование плагина аутентификации

Чтобы собрать и установить файл библиотеки, используйте инструкции в разделе 26.2.4.3. Чтобы сделать файл библиотеки доступным для использования, установите его в каталог плагинов (указан в системной переменной plugin_dir.

Зарегистрируйте серверный плагин на сервере. Например, чтобы загрузить плагин при запуске сервера, используйте опцию --plugin-load=auth_simple.so (корректируйте суффикс .so для Вашей платформы по мере необходимости).

Создайте пользователя, для которого сервер будет использовать плагин auth_simple для аутентификации:

mysql> CREATE USER 'x'@'localhost'
    -> IDENTIFIED WITH auth_simple;
Используйте программу клиента, чтобы соединиться с сервером как пользователь x. Серверная часть плагина auth_simple общается с программой клиента, так что это должно использовать клиентскую часть auth_simple и послать пароль серверу. Плагин сервера должен отклонить соединения, которые посылают пустой пароль и принимать соединения, которые посылают непустой пароль. Вызовите программу клиента каждым способом, чтобы проверить это:
shell> mysql --user=x --skip-password
ERROR 1045 (28000): Access denied for user 'x'@'localhost' (using password: NO)

shell> mysql --user=x --password=abc
mysql>
Поскольку плагин сервера принимает любой непустой пароль, это нужно считать небезопасным. После тестирования плагина, чтобы проверить, что это работает, перезапустите сервер без опции --plugin-load , чтобы не оставлять сервер, работающий с загруженным опасным плагином аутентификации. Кроме того, удалите пользователя с помощью DROP USER 'x'@'localhost'.

Если Вы пишете программу клиента, которая поддерживает использование плагинов аутентификации, обычно такая программа загружает плагин, вызывая mysql_options(), чтобы установить опции MYSQL_DEFAULT_AUTH и MYSQL_PLUGIN_DIR:

char *plugin_dir = "path_to_plugin_dir";
char *default_auth = "plugin_name";

/* ... process command-line options ... */

mysql_options(&mysql, MYSQL_PLUGIN_DIR, plugin_dir);
mysql_options(&mysql, MYSQL_DEFAULT_AUTH, default_auth);
Как правило, программа также примет опции --plugin-dir и --default-auth, которые позволяют пользователям переопределить значения по умолчанию.

Если программа клиента требует управления низшего уровня, библиотека клиента содержит функции, которые берут параметр an st_mysql_client_plugin. См. раздел 25.8.14.

26.2.4.9.4. Осуществление доступа пользователей по доверенности в плагинах аутентификации

Одна из способностей, которые аутентификация с помощью плагинов делает возможными, является доступом пользователей по доверенности (см. possible is proxy users (see раздел 7.3.10). Для серверного плагина аутентификации, чтобы участвовать в этом, должны быть удовлетворены эти условия:

  • Когда соединяющийся клиент должен быть обработан как пользователь по доверенности, плагин должен возвратить другое имя в члене authenticated_as структуры MYSQL_SERVER_AUTH_INFO, чтобы указать на реальное имя пользователя. Это может также произвольно установить член external_user, чтобы установить значение системной переменной external_user.

  • Учетные записи пользователя по доверенности должны быть настроены так, чтобы быть авторизованными плагином. Используйте CREATE USER или GRANT, чтобы связать учетные записи с плагинами.
  • Учетные записи пользователя по доверенности должны иметь привилегию PROXY для учетных записей. Используйте GRANT, чтобы предоставить эту привилегию.

Другими словами, единственный аспект пользовательской поддержки по доверенности, требуемой от плагина, установить authenticated_as к реальному имени пользователя. Остальное является дополнительным (установка external_user) или сделано DBA, использующей запросы SQL.

Как плагин аутентификации определяет, имя какого пользователя возвратить, когда пользователь по доверенности соединяется? Это зависит от плагина. Как правило, плагин отображает клиентов, основываясь на строке аутентификации, переданной ему сервером. Эта строка прибывает из части AS предложения IDENTIFIED WITH оператора CREATE USER, которое определяет использование плагина для аутентификации.

Разработчик определяет правила синтаксиса для строки аутентификации и осуществляет плагин согласно тем правилам. Предположите, что плагин берет список разделенных запятой значений пар, которые отображают внешних пользователей на пользователей MySQL. Например:

CREATE USER ''@'%.example.com'
  IDENTIFIED WITH my_plugin AS 'extuser1=mysqlusera, extuser2=mysqluserb'
CREATE USER ''@'%.example.org'
  IDENTIFIED WITH my_plugin AS 'extuser1=mysqluserc, extuser2=mysqluserd'
Когда сервер вызывает плагин, чтобы подтвердить подлинность клиента, он передает соответствующую строку аутентификации плагину. Плагин должен:

  1. Разобрать строку на компоненты, чтобы определить отображение на использование.

  2. Сравнить имя пользователя клиента с отображением.
  3. Вернуть надлежащее имя пользователя MySQL.

Например, если extuser2 соединяется с хоста example.com, сервер передает плагину 'extuser1=mysqlusera, extuser2=mysqluserb', а плагин должен скопировать mysqluserb в authenticated_as с заканчивающим нулевым байтом. Если extuser2 заходит с хоста example.org, сервер передает плагину 'extuser1=mysqluserc, extuser2=mysqluserd', а плагин должен скопировать уже mysqluserd.

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

Следующий пример демонстрирует, как обработать пользователей по доверенности, использующих плагин auth_simple_proxy. Как и описанный ранее плагин auth_simple, auth_simple_proxy принимает любой непустой пароль как допустимый (и таким образом не должен использоваться в производственных средах). Кроме того, он исследует член строки аутентификации auth_string и использует очень простые правила для того, чтобы интерпретировать это:

  • Если строка пуста, плагин возвращает имя пользователя как дано. Таким образом, плагин оставляет значение authenticated_as неизменным.

  • Если строка непуста, плагин обрабатывает ее как реальное имя пользователя и копирует ее в authenticated_as.

Для того, чтобы проверить, настройте одну учетную запись, которая не является действующей как прокси (по доверенности), согласно предыдущим правилам, и ту, которая является. Это означает, что одна учетная запись не имеет предложения AS, а вторая имеет AS, которое определяет другого пользователя:

CREATE USER 'plugin_user1'@'localhost' IDENTIFIED WITH auth_simple_proxy;
CREATE USER 'plugin_user2'@'localhost'
            IDENTIFIED WITH auth_simple_proxy AS 'proxied_user';
Кроме того, создайте учетку на доверенного пользователя plugin_user2 и привилегию PROXY для него:
CREATE USER 'proxied_user'@'localhost' IDENTIFIED BY 'proxied_user_pass';
GRANT PROXY ON 'proxied_user'@'localhost'
            TO 'plugin_user2'@'localhost';
Прежде, чем сервер вызывает плагин аутентификации, он устанавливает authenticated_as к имени пользователя клиента. Чтобы указать, что пользователь имеет полномочия другого, плагин должен установить authenticated_as в имя реального пользователя. Для auth_simple_proxy это означает, что надо смотреть значение auth_string и, если значение непусто, копировать его в член authenticated_as, чтобы возвратить это как реальное имя пользователя. Кроме того, когда это происходит, плагин устанавливает член external_user к имени пользователя клиента: это становится значением системной переменной external_user.
static int auth_simple_proxy_server (MYSQL_PLUGIN_VIO *vio,
                                     MYSQL_SERVER_AUTH_INFO *info)
{
  unsigned char *pkt;
  int pkt_len;

  /* read the password as null-terminated string, fail on error */
  if ((pkt_len = vio->read_packet(vio, &pkt)) < 0) return CR_ERROR;
  /* fail on empty password */
  if (!pkt_len || *pkt == '\0') {
     info->password_used= PASSWORD_USED_NO;
     return CR_ERROR;
  }
  /* accept any nonempty password */
  info->password_used= PASSWORD_USED_YES;
  /* if authentication string is nonempty, use as proxied user name */
  /* and use client name as external_user value */
  if (info->auth_string_length > 0) {
     strcpy (info->authenticated_as, info->auth_string);
     strcpy (info->external_user, info->user_name);
  }
  return CR_OK;
}
После успешного соединения функция USER() должна указать на соединяющегося пользователя клиента и имя хоста, а CURRENT_USER() должна указать на учетную запись, привилегии которой применяются во время сеанса. Последнее значение должно быть соединяющейся учетной записью пользователя, если предоставления привилегий не происходит, или учетной записью другого пользователя, если происходит.

Соберите и установите плагин, затем проверьте его. Во-первых, соединитесь как plugin_user1:

shell> mysql --user=plugin_user1 --password=x
В этом случае не должно быть никакого предоставления привилегий:
mysql> SELECT USER(), CURRENT_USER(), @@proxy_user, @@external_user\G
*************************** 1. row ***************************
 USER(): plugin_user1@localhost
 CURRENT_USER(): plugin_user1@localhost
   @@proxy_user: NULL
@@external_user: NULL
Тогда соединитесь как plugin_user2:
shell> mysql --user=plugin_user2 --password=x
В этом случае plugin_user2 должен быть отображен к proxied_user:
mysql> SELECT USER(), CURRENT_USER(), @@proxy_user, @@external_user\G
*************************** 1. row ***************************
 USER(): plugin_user2@localhost
 CURRENT_USER(): proxied_user@localhost
   @@proxy_user: 'plugin_user2'@'localhost'
@@external_user: 'plugin_user2'@'localhost'

26.2.4.10. Написание пплагинов проверки допустимости пароля

Этот раздел описывает, как написать серверный плагин проверки допустимости пароля. Инструкции основаны на исходном коде в каталоге plugin/password_validation дистрибутива исходных текстов MySQL. Файл validate_password.cc в этом каталоге осуществляет плагин, названный validate_password.

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

#include <mysql/plugin_validate_password.h>
plugin_validate_password.h включает plugin.h, таким образом, Вы не должны включать последний файл явно. plugin.h определяет тип плагина сервера MYSQL_VALIDATE_PASSWORD_PLUGIN и структуры данных. plugin_validate_password.h определяет структуры данных, определенные для плагинов проверки допустимости пароля.

У плагина проверки допустимости пароля, как и у любого плагина сервера MySQL, есть общий описатель (см. раздел 26.2.4.2.1). В validate_password.cc общий описатель похож на это:

mysql_declare_plugin(validate_password)
{
  MYSQL_VALIDATE_PASSWORD_PLUGIN,     /*   type*/
  &validate_password_descriptor,  /*   descriptor  */
  "validate_password",                /*   name*/
  "Oracle Corporation",               /*   author  */
  "check password strength",          /*   description */
  PLUGIN_LICENSE_GPL,
  validate_password_init,             /* init function (when loaded) */
  validate_password_deinit,           /* deinit function (when unloaded) */
  0x0100,                             /*   version */
  NULL,
  validate_password_system_variables, /*   system variables*/
  NULL, 0,
}
mysql_declare_plugin_end;
Член name (validate_password) указывает на имя, чтобы использовать для ссылок на плагин в INSTALL PLUGIN или UNINSTALL PLUGIN. Это также имя, выведенное на экран INFORMATION_SCHEMA.PLUGINS или SHOW PLUGINS.

Общий описатель также обращается к структуре validate_password_system_variables, которая выставляет несколько системных переменных для запроса SHOW VARIABLES:

static struct st_mysql_sys_var* validate_password_system_variables[]= {
  MYSQL_SYSVAR(length),
  MYSQL_SYSVAR(number_count),
  MYSQL_SYSVAR(mixed_case_count),
  MYSQL_SYSVAR(special_char_count),
  MYSQL_SYSVAR(policy),
  MYSQL_SYSVAR(dictionary_file), NULL
};
Функция инициализации validate_password_init читает файл словаря, если он был определен, а функция validate_password_deinit освобождает структуры данных, связанные с файлом.

Значение validate_password_descriptor в общем описателе указывает на определенный для типа описатель. Для плагинов проверки допустимости пароля у этого описателя есть следующая структура:

struct st_mysql_validate_password
{
  int interface_version;
  /*
    This function returns TRUE for passwords which satisfy the password
    policy (as chosen by plugin variable) and FALSE for
    all other password
  */
  int (*validate_password)(mysql_string_handle password);
  /*
    This function returns the password strength (0-100) depending
    upon the policies
  */
  int (*get_password_strength)(mysql_string_handle password);
};
У определенного для типа описателя есть эти члены:

  • interface_version: В соответствии с соглашением, определенные для типа описатели начинаются с версии интерфейса для данного типа. Сервер проверяет interface_version когда загружает плагин, чтобы видеть, совместим ли плагин с ним. Для плагинов проверки допустимости пароля, значение члена interface_version MYSQL_VALIDATE_PASSWORD_INTERFACE_VERSION (определено в plugin_validate_password.h).

  • validate_password: Функция, которую сервер вызывает, чтобы проверить, удовлетворяет ли пароль текущую политику пароля. Это возвращает 1, если пароль хорош и 0 иначе. Параметр пароль, который передают как значение mysql_string_handle. Этот тип данных осуществлен сервисом сервера mysql_string. Детали в файлах исходных текстов string_service.h и string_service.cc каталога sql.
  • get_password_strength: Функция, которую сервер вызывает, чтобы оценить силу пароля. Это возвращает значение от 0 (слабый) до 100 (сильный). Параметр пароль, который передают как значение mysql_string_handle.

Для плагина validate_password определенный для типа описатель похож на это:

static struct st_mysql_validate_password validate_password_descriptor =
{
  MYSQL_VALIDATE_PASSWORD_INTERFACE_VERSION,
  validate_password,     /* validate function  */
  get_password_strength  /* validate strength function */
};
Чтобы собрать и установить файл библиотеки, используйте инструкции в разделе 26.2.4.3. Чтобы сделать файл библиотеки доступным для использования, установите его в каталог плагинов (указан в системной переменной plugin_dir). Для плагина validate_password это собрано и установлено, когда Вы создаете MySQL из исходных текстов. Это также включено в двоичные дистрибутивы. Процесс сборки производит совместно используемую библиотеку объекта с названием validate_password.so (суффикс .so может отличаться, в зависимости от Вашей платформы).

Чтобы зарегистрировать плагин во время выполнения, используйте этот запрос (корректируйте суффикс .so для Вашей платформы по мере необходимости):

INSTALL PLUGIN validate_password SONAME 'validate_password.so';

Чтобы проверить установку, исследуйте таблицу INFORMATION_SCHEMA.PLUGINS или примените SHOW PLUGINS . См. раздел 6.6.3.

В то время как плагин validate_password установлен, он выставляет системные переменные, которые указывают на проверяющие пароль параметры:

mysql> SHOW VARIABLES LIKE 'validate_password%';
+--------------------------------------+--------+
| Variable_name                        | Value  |
+--------------------------------------+--------+
| validate_password_dictionary_file    |        |
| validate_password_length             | 8      |
| validate_password_mixed_case_count   | 1      |
| validate_password_number_count       | 1      |
| validate_password_policy             | MEDIUM |
| validate_password_special_char_count | 1      |
+--------------------------------------+--------+
Для описаний этих переменных, см. раздел 7.5.2.2 .

Чтобы отключить плагин после тестирования, используйте этот запрос:

UNINSTALL PLUGIN validate_password;

26.2.4.11. Написание плагинов трассировки протокола

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

26.2.4.11.1. Использование испытательного плагина трассировки протокола

MySQL включает испытательный плагин трассировки протокола, который служит, чтобы иллюстрировать информацию, доступную от таких плагинов, и как руководство по написанию других плагинов трассировки протокола. Чтобы видеть, как испытательный плагин работает, используйте исходные тексты MySQL.

Включите испытательный плагин трассировки протокола, конфигурируя MySQL с включенной опцией CMake WITH_TEST_TRACE_PLUGIN. Это соберет испытательный плагин трассировки и программы клиента MySQL загрузят его, но плагин не имеет никакого эффекта по умолчанию. Управляйте плагином, используя эти переменные окружения:

  • MYSQL_TEST_TRACE_DEBUG: Установите эту переменную в значение кроме 0, чтобы заставить испытательный плагин производить диагностический вывод на stderr.

  • MYSQL_TEST_TRACE_CRASH: Установите эту переменную в значение кроме 0, чтобы заставить испытательный плагин прерывать программу клиента, если это обнаруживает недопустимый случай трассировки.

Диагностический вывод испытательного плагина трассировки протокола может раскрыть пароли и другую чувствительную информацию.

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

shell> export MYSQL_TEST_TRACE_DEBUG=1
shqll> mysql
test_trace: Test trace plugin initialized
test_trace: Starting tracing in stage CONNECTING
test_trace: stage: CONNECTING, event: CONNECTING
test_trace: stage: CONNECTING, event: CONNECTED
test_trace: stage: WAIT_FOR_INIT_PACKET, event: READ_PACKET
test_trace: stage: WAIT_FOR_INIT_PACKET, event: PACKET_RECEIVED
test_trace: packet received: 87 bytes
  0A 35 2E 37 2E 33 2D 6D  31 33 2D 64 65 62 75 67   .5.7.3-m13-debug
  2D 6C 6F 67 00 04 00 00  00 2B 7C 4F 55 3F 79 67   -log.....+|OU?yg
test_trace: 004: stage: WAIT_FOR_INIT_PACKET, event: INIT_PACKET_RECEIVED
test_trace: 004: stage: AUTHENTICATE, event: AUTH_PLUGIN
test_trace: 004: Using authentication plugin: mysql_native_password
test_trace: 004: stage: AUTHENTICATE, event: SEND_AUTH_RESPONSE
test_trace: 004: sending packet: 188 bytes
  85 A6 7F 00 00 00 00 01  21 00 00 00 00 00 00 00   .?......!.......
  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
...
mysql> quit
test_trace: 008: stage: READY_FOR_COMMAND, event: SEND_COMMAND
test_trace: 008: QUIT
test_trace: 008: stage: READY_FOR_COMMAND, event: PACKET_SENT
test_trace: 008: packet sent: 0 bytes
test_trace: 008: stage: READY_FOR_COMMAND, event: DISCONNECTED
test_trace: 008: Connection  closed
test_trace: 008: Tracing connection has ended
Bye
test_trace: Test trace plugin de-initialized
Чтобы отключить вывод трассировки, сделайте это:
shell> MYSQL_TEST_TRACE_DEBUG=
26.2.4.11.2. Применение Ваших собственных плагинов трассировки протокола

Чтобы использовать Ваш собственный плагин трассировки протокола, Вы должны сконфигурировать MySQL с опцией выключенной CMake WITH_TEST_TRACE_PLUGIN, потому что только один плагин трассировки протокола может быть загружен за один раз, и ошибка происходит для попыток загрузить второй. Если Вы уже создали MySQL с испытательным плагином трассировки протокола, Вы должны восстановить MySQL без этого прежде, чем Вы сможете использовать свои собственные плагины.

Этот раздел обсуждает, как написать плагин simple_trace. Этот плагин служит основой, показывающей, как настроить описатель плагина клиента и создать связанные функции обратного вызова. В simple_trace эти функции являются элементарными и только иллюстрирует требуемые параметры. Чтобы видеть подробно, как плагин трассировки может использовать информацию о событии трассировки, изучите исходный файл для испытательного плагина (test_trace_plugin.cc в каталоге libmysql исходных текстов MySQL. Однако, отметьте что структура st_mysql_client_plugin_TRACE, используемая там, отличается от структур, используемых с обычным макроопределением декларации плагина клиента. В частности, первые два члена определены явно, а не неявно макроопределением декларации.

Несколько заголовочных файлов содержат информацию, относящуюся к плагинам трассировки протокола:

  • client_plugin.h: Определяет API для плагинов клиента. Это включает описатель плагина клиента и прототипы функций для C API плагина клиента (см. раздел 25.8.14 ).

  • plugin_trace.h: Содержит декларации для клиентских плагинов типа MYSQL_CLIENT_TRACE_PLUGIN. Это также содержит описания разрешенных этапов протокола, переходов между этапами и типов событий, разрешенных на каждом этапе.

Чтобы написать плагин, включите следующие заголовочные файлы в исходный файл:

#include <mysql/plugin_trace.h>
#include <mysql.h>
plugin_trace.h включает client_plugin.h, таким образом, Вы не должны включать последний файл явно.

Объявите клиентский описатель плагина с макросами mysql_declare_client_plugin() и mysql_end_client_plugin (см. раздел 26.2.4.2.3). Для плагина simple_trace описатель похож на это:

mysql_declare_client_plugin(TRACE)
  "simple_trace",                 /* plugin name */
  "Author Name",                  /* author */
  "Simple protocol trace plugin", /* description */
  {1,0,0},                        /* version = 1.0.0 */
  "GPL",                          /* license type */
  NULL,                           /* for internal use */
  plugin_init,     /* initialization function */
  plugin_deinit,   /* deinitialization function */
  plugin_options,  /* option-handling function */
  trace_start,     /* start-trace function */
  trace_stop,      /* stop-trace function */
  trace_event      /* event-handling function */
mysql_end_client_plugin;
Члены дескриптора от имени до обрабатывающей опции функции характерны для всех типов плагина клиента. Члены после общих участников осуществляют обработку трассировки событий.

Функциональные члены для которых плагин не нуждается ни в какой обработке, могут быть объявлены как NULL в описателе, когда Вы не должны писать соответствующую функцию. В целях иллюстрации синтаксиса параметров, следующее обсуждение осуществляет все функции, перечисленные в описателе, даже при том, что некоторые из них ничего не делают.

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

static int plugin_init(char *errbuf, size_t errbuf_len, int argc,
                       va_list args)
{
  return 0;
}

static int plugin_deinit()
{
  return 0;
}

static int plugin_options(const char *option, const void *value)
{
  return 0;
}
Определенные для трассировки члены описателя плагина клиента функции обратного вызова. Следующие описания обеспечивают больше деталей о том, как они используются. У каждой есть первый параметр, который является указателем на событие в случае, если Ваше выполнение должно получить доступ к нему.

trace_start(): Эта функция вызвана в начале каждого прослеженного соединения (каждое соединение, которое запускается после того, как плагин загружен). Это передает обработчик соединения и этап протокола, на котором запускается рассмотрение. trace_start() выделяет память, необходимую функции trace_event(), если таковая вообще имеется, и возвращает указатель на это. Если никакая память не нужна, эта функция возвращет NULL.

static void* trace_start(struct st_mysql_client_plugin_TRACE *self,
                         MYSQL *conn, enum protocol_stage stage)
{
  struct st_trace_data *plugin_data= malloc(sizeof(struct st_trace_data));

  fprintf(stderr, "Initializing trace: stage %d\n", stage);
  if (plugin_data) {
     memset(plugin_data, 0, sizeof(struct st_trace_data));
     fprintf(stderr, "Trace initialized\n");
     return plugin_data;
  }
  fprintf(stderr, "Could not initialize trace\n");
  exit(1);
}
trace_stop(): Эта функция вызвана при завершении соединения. Это обычно происходит, когда соединение закрыто, но может произойти ранее. Например, trace_event() может возвратить ненулевое значение в любое время, и это заставляет рассмотрение соединения заканчиваться. trace_stop() тогда вызывается даже при том, что соединение не закончилось.

trace_stop() передан обработчик соединения и указатель на память, выделенную trace_start() (NULL, если нет). Если указатель не-NULL, trace_stop() должна освободить память. Эта функция не возвращает значения.

static void trace_stop(struct st_mysql_client_plugin_TRACE *self,
                       MYSQL *conn, void *plugin_data)
{
  fprintf(stderr, "Terminating trace\n");
  if (plugin_data) free(plugin_data);
}
trace_event(): Эта функция вызвана для каждого возникновения событий. Ей передают указатель на память, выделенную trace_start() (NULL, если нет), обработчик соединения, текущий этап протокола, коды и данные событий. Эта функция возвращает 0, чтобы продолжить прослеживать, отличное от нуля значение, если рассмотрение должно остановиться.
static int trace_event(struct st_mysql_client_plugin_TRACE *self,
                       void *plugin_data, MYSQL *conn,
                       enum protocol_stage stage,
                       enum trace_event event,
                       struct st_trace_event_args args)
{
  fprintf(stderr, "Trace event received: stage %d, event %d\n", stage, event);
  if (event == TRACE_EVENT_DISCONNECTED)
     fprintf(stderr, "Connection closed\n");
  return 0;
}
Структура рассмотрения закрывает рассмотрение соединения, когда соединение заканчивается, таким образом, trace_event() должна возвратить отличное от нуля значение только, если Вы хотите закончить рассмотрение соединения раньше. Предположите, что Вы хотите проследить только соединения для определенной учетной записи MySQL. После аутентификации Вы можете проверить имя пользователя на соединении и прекратить прослеживать, если это оказался не тот пользователь, которым Вы интересуетесь.

Для каждого trace_event() структура st_trace_event_args содержит данные событий. У нее есть это определение:

struct st_trace_event_args
{
  const char   *plugin_name;
  int cmd;
  const unsigned char  *hdr;
  size_thdr_len;
  const unsigned char  *pkt;
  size_tpkt_len;
};
Для различных типов событий структура st_trace_event_args содержит информацию, описанную ниже. Все длины в байтах. Неиспользованные члены установлены в 0/NULL.

Событие AUTH_PLUGIN:

plugin_name  The name of the plugin
Событие SEND_COMMAND:
cmd  The command code
hdr  Pointer to the command packet header
hdr_len Length of the header
pkt  Pointer to the command arguments
pkt_len Length of the arguments
Другие события SEND_xxx и xxx_RECEIVED:
pkt  Pointer to the data sent or received
pkt_len Length of the data
Событие PACKET_SENT:
pkt_len Number of bytes sent
Чтобы собрать и установить файл библиотеки, используйте инструкции в разделе 26.2.4.3. Чтобы сделать файл библиотеки доступным для использования, установите его в каталог плагинов (указан в системной переменной plugin_dir).

После того, как файл библиотеки собран и установлен в каталоге, Вы можете его проверить устанавливая переменную окружения LIBMYSQL_PLUGINS к имени плагина, это затрагивает любую программу клиента, которая использует эту переменную. mysql одна из них:

shell> export LIBMYSQL_PLUGINS=simple_trace
shqll> mysql
Initializing trace: stage 0
Trace initialized
Trace event received: stage 0, event 1
Trace event received: stage 0, event 2
...
Welcome to the MySQL monitor.  Commands end with ; or \g.
Trace event received
Trace event received
...
mysql> SELECT 1;
Trace event received: stage 4, event 12
Trace event received: stage 4, event 16
...
Trace event received: stage 8, event 14
Trace event received: stage 8, event 15
+---+
| 1 |
+---+
| 1 |
+---+
1 row in set (0.00 sec)

mysql> quit
Trace event received: stage 4, event 12
Trace event received: stage 4, event 16
Trace event received: stage 4, event 3
Connection closed
Terminating trace
Bye
Чтобы остановить трассировку, скомандуйте:
shell> LIBMYSQL_PLUGINS=
Также возможно написать программы клиента, которые непосредственно загружают плагин. Вы можете сказать клиенту, где расположен каталог плагинов, вызывая mysql_options(), чтобы установить опцию MYSQL_PLUGIN_DIR:
char *plugin_dir = "path_to_plugin_dir";

/* ... process command-line options ... */

mysql_options(&mysql, MYSQL_PLUGIN_DIR, plugin_dir);
Как правило, программа также примет опцию --plugin-dir, которая позволяет пользователям переопределить значение по умолчанию.

Если программа клиента требует управления низшего уровня, библиотека клиента содержит функции, которые берут параметр st_mysql_client_plugin. См. раздел 25.8.14.

26.2.4.12. Написание плагинов ключей

MySQL Server поддерживает службу, которая позволяет внутренним серверным компонентам и плагинам надежно хранить чувствительную информацию для более позднего извлечения. Этот раздел описывает, как написать серверный плагин, который может использоваться служебными функциями, чтобы выполнить операции ключевого менеджмента. Для общей информации см. раздел 7.5.3.

Инструкции здесь основаны на исходном коде в каталоге plugin/keyring исходных текстов MySQL. Исходные файлы в том каталоге осуществляют плагин keyring_file это использует файл, локальный с точки зрения сервера для хранения данных.

Чтобы написать плагин, включите следующий заголовочный файл в исходный файл.

#include <mysql/plugin_keyring.h>
plugin_keyring.h включает plugin.h, таким образом, Вы не должны включать последний файл явно. plugin.h определяет тип плагина сервера MYSQL_KEYRING_PLUGIN и структуры данных. plugin_keyring.h определяет структуры данных, определенные для плагинов.

Как любой плагин сервера MySQL, плагин ключей имеет общий описатель (см. раздел 26.2.4.2.1). В keyring.cc общий описатель похож на это:

mysql_declare_plugin(keyring_file)
{
  MYSQL_KEYRING_PLUGIN,       /* type */
  &keyring_descriptor,    /* descriptor */
  "keyring_file",             /* name */
  "Oracle Corporation",       /* author   */
  "store/fetch authentication data to/from a flat file", /* description */
  PLUGIN_LICENSE_GPL,
  keyring_init,               /* init function (when loaded)  */
  keyring_deinit,             /* deinit function (when unloaded)  */
  0x0100,                     /* version  */
  NULL,                       /* status variables */
  keyring_system_variables,   /* system variables */
  NULL, 0,
}
mysql_declare_plugin_end;
Член name (keyring_file) указывает на имя, чтобы использовать для ссылок на плагин в INSTALL PLUGIN или UNINSTALL PLUGIN. Это также имя, выведенное на экран INFORMATION_SCHEMA.PLUGINS или SHOW PLUGINS.

Общий описатель также обращается к структуре keyring_system_variables, которая выставляет системную переменную для SHOW VARIABLES :

static struct st_mysql_sys_var *keyring_system_variables[] =
       {MYSQL_SYSVAR(data), NULL};
Функция инициализации keyring_init создает файл с данными, если он не существует, затем читает его и инициализирует хранилище ключей. Функция keyring_deinit освобождает структуры данных, связанные с файлом.

Значение keyring_descriptor в общем описателе указывает на определенный для типа описатель. Для плагинов ключей у этого описателя есть следующая структура:

struct st_mysql_keyring
{
  int interface_version;
  my_bool (*mysql_key_store)(const char *key_id, const char *key_type,
           const char* user_id, const void *key, size_t key_len);
  my_bool (*mysql_key_fetch)(const char *key_id, char **key_type,
           const char *user_id, void **key, size_t *key_len);
  my_bool (*mysql_key_remove)(const char *key_id, const char *user_id);
  my_bool (*mysql_key_generate)(const char *key_id, const char *key_type,
           const char *user_id, size_t key_len);
};
У определенного для типа описателя есть эти члены:

  • interface_version: В соответствии с соглашением, определенные для типа описатели начинаются с версии интерфейса для данного типа. Сервер проверяет interface_version, когда загружает плагин, чтобы видеть, совместим ли плагин с ним. Для плагинов ключей значение члена interface_version MYSQL_KEYRING_INTERFACE_VERSION (определено в plugin_keyring.h).

  • mysql_key_store: Функция, которая шифрует и хранит ключ.
  • mysql_key_fetch: Функция, которая дешифрует и возвращает ключ.
  • mysql_key_remove: Функция, которая удаляет ключ.
  • mysql_key_generate: Функция, которая производит новый случайный ключ.

Для плагина keyring_file определенный для типа описатель похож на это:

static struct st_mysql_keyring keyring_descriptor =
{
  MYSQL_KEYRING_INTERFACE_VERSION,
  mysql_key_store, mysql_key_fetch,
  mysql_key_remove, mysql_key_generate
};
Функции mysql_key_xxx, осуществленные плагином, аналогичны функциям my_key_xxx, выставленным службой keyring API. Для информации о параметрах этих функций и как они используются, см. раздел 26.3.2 .

Чтобы собрать и установить файл библиотеки, используйте инструкции в разделе 26.2.4.3. Чтобы сделать файл библиотеки доступным для использования, установите его в каталог плагинов (указан в системной переменной plugin_dir). Для плагина keyring_file, это собрано и установлено, когда Вы создаете MySQL из исходных текстов. Это также включено в двоичные дистрибутивы. Процесс сборки производит совместно используемую библиотеку объекта с названием keyring_file.so (суффикс .so может отличаться в зависимости от Вашей платформы).

Чтобы проверить установку, исследуйте таблицу INFORMATION_SCHEMA.PLUGINS или примените SHOW PLUGINS (см. раздел 6.6.3 ). Например:

mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS
    -> WHERE PLUGIN_NAME LIKE 'keyring%';
+--------------+---------------+
| PLUGIN_NAME  | PLUGIN_STATUS |
+--------------+---------------+
| keyring_file | ACTIVE        |
+--------------+---------------+
В то время как плагин keyring_file установлен, он выставляет системную переменную, которая указывает на местоположение файла с данными, который он использует для безопасного информационного хранения:
mysql> SHOW VARIABLES LIKE 'keyring_file%';
+-------------------+----------------------------------+
| Variable_name     | Value                            |
+-------------------+----------------------------------+
| keyring_file_data | /usr/local/mysql/keyring/keyring |
+-------------------+----------------------------------+
Для описания переменной keyring_file_data см. раздел 6.1.5.

Чтобы отключить плагин после тестирования, перезапустите сервер без опции --early-plugin-load, которая вызывает плагин.

26.3. Сервисы MySQL для плагинов

У плагинов сервера MySQL есть доступ к сервисам (службам). Интерфейс служб выставляет функциональность сервера, которую могут вызвать плагины. Это дополняет API и имеет эти характеристики:

  • Службы позволяют плагинам доступ к коду сервера, используя обычные вызовы функции. Службы также доступны определяемым пользователем функциям (UDF).

  • Службы портативны и работают под разными платформами.
  • Интерфейс включает механизм версий, чтобы версии службы, поддержанные сервером, могли быть проверены во время загрузки. Это защищает от несовместимостей между версией услуги, которую сервер оказывает и версией службы, ожидаемой или требуемой плагином.
  • Для информации о плагинах для того, чтобы проверить службы, см. Plugins for Testing Plugin Services, в The MySQL Test Framework, Version 2.0.

Интерфейс служб отличается от API плагинов следующим образом:

  • API позволяет плагинам использоваться сервером. Инициатива запроса всегда на стороне с сервера. Он вызывает плагины. Это позволяет плагинам расширить функциональность сервера или регистроваться, чтобы получить уведомления о действиях сервера.

  • Интерфейс служб позволяет плагинам вызвать код в сервере. Инициатива запроса на стороне плагина. Это позволяет функциональности, уже осуществленной в сервере, использоваться многими плагинами: они не должны индивидуально осуществить это непосредственно.

Чтобы определить, какие службы существуют и что они обеспечивают, смотрите каталог include/mysql в дистрибутиве исходных текстов MySQL. Соответствующие файлы:

  • plugin.h включает services.h, который включает все доступные определенные для служб заголовочные файлы.

  • У определенных для служб заголовков есть названия формы service_xxx.h.

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

Текущие службы включают следующее, но другие могут быть добавлены:

  • locking_service: Служба, которая осуществляет блокировки с тремя признаками: пространство имен, имя и режим. Этот интерфейс блокировки доступен на двух уровнях: 1) Как интерфейс языка C, вызываемый как служба от плагинов сервера или определяемых пользователем функций, 2) На уровне SQL, как ряд определяемых пользователем функций, которые отображаются на вызовы сервисов. Для получения дополнительной информации см. раздел 26.3.1.

  • my_plugin_log_service: Служба, которая позволяет плагинам сообщить об ошибках и определить сообщения об ошибках. Сервер пишет сообщения в свой журнал ошибок.
  • my_snprintf: Форматирующая строку служба, которая приводит к последовательным результатам, независимо от платформы.
  • my_thd_scheduler: Служба для плагинов, чтобы выбрать планировщик потока.
  • mysql_keyring: Служба для хранения ключей. Для получения дополнительной информации см. раздел 26.3.2.
  • mysql_password_policy: Служба для проверки допустимости пароля и проверки его силы.
  • mysql_string: Служба для обработки строк.
  • plugin_registry_service: MySQL Server включает основанную на компонентах инфраструктуру для того, чтобы улучшить расширяемость сервера, см. раздел 6.5. Однако, плагины MySQL используют интерфейс, который предшествует компонентному интерфейсу. plugin_registry_service позволяет плагинам получить доступ к компонентной регистрации и ее службам.

  • security_context: Служба, которая позволяет плагинам исследовать или управлять контекстами безопасности потока. Эта служба обеспечивает установку и получение атрибутов получателя, чтобы получить доступ к признакам сервера Security_context, который включает такие признаки, как пользователь входа в систему и хост, доверенный пользователь и хост, IP-адрес клиента.
  • thd_alloc: Служба распределения памяти.
  • thd_wait: Служба для плагинов, чтобы сообщить, когда они засыпают или останавливаются.

Исходный текст MySQL содержит внутреннюю документацию, созданную с помощью Doxygen. Эта документация полезна для понимания, как MySQL работает с точки зрения разработчика. Произведенный контент Doxygen доступен на http://dev.mysql.com/doc/dev/mysql-server/latest/. Также возможно произвести этот контент локально из исходных текстов MySQL, используя инструкции в разделе 2.8.7.

Остаток этого раздела описывает, как плагин использует функциональность сервера, которая доступна как служба. См. также в качестве примера исходный текст для плагина daemon, который использует сервис my_snprintf. В дистрибутиве исходных текстов MySQL плагин находится в каталоге plugin/daemon_example.

Чтобы использовать службу или службы изнутри плагина, исходный файл должен включать заголовочный файл plugin.h для доступа к связанной со службами информации:

#include <mysql/plugin.h>
Это не представляет дополнительных проблем установки. Плагин должен включать тот файл так или иначе, потому что это содержит определения и структуры, в которых нуждается каждый плагин.

Чтобы получить доступ к службе, плагин вызывает служебные функции как любые другие функции. Например, чтобы отформатировать строку в буфер для того, чтобы ее напечатать, вызовите функцию my_snprintf(), обеспеченную службой того же самого имени:

char buffer[BUFFER_SIZE];
my_snprintf(buffer, sizeof(buffer), format_string,
            argument_to_format, ...);
Чтобы сообщить ошибку, которую сервер впишет в журнал ошибок, сначала выберите уровень ошибки. mysql/service_my_plugin_log.h определяет эти уровни:
enum plugin_log_level
{
  MY_ERROR_LEVEL, MY_WARNING_LEVEL, MY_INFORMATION_LEVEL
};
Тогда вызовите my_plugin_log_message():
int my_plugin_log_message(MYSQL_PLUGIN *plugin, enum plugin_log_level level,
                          const char *format, ...);
Например:
my_plugin_log_message(plugin_ptr, MY_ERROR_LEVEL, "Cannot initialize plugin");
Некоторые услуги для плагинов могут быть оказаны плагинами и таким образом доступны, только если обеспечивающий службу плагин загружен. Любой компонент MySQL, который использует такую службу, должен проверить, доступна ли служба.

Когда Вы создаете свой плагин, используйте флаг -lmysqlservices во время компоновки, чтобы скомпоновать с библиотекой libmysqlservices. Например, для CMake поместите это в начало файла CMakeLists.txt:

FIND_LIBRARY(MYSQLSERVICES_LIB mysqlservices
             PATHS "${MYSQL_SRCDIR}/libservices" NO_DEFAULT_PATH)
Поместите это в файл CMakeLists.txt в каталоге, содержащем исходный текст плагина:
# the plugin needs the mysql services library for error logging
TARGET_LINK_LIBRARIES (your_plugin_library_name ${MYSQLSERVICES_LIB})

26.3.1. Служба блокировки

Дистрибутивы обеспечивают интерфейс блокировки, который доступен на двух уровнях:

  • Как интерфейс языка C, вызываемый как служба из плагинов сервера или определяемых пользователем функций.

  • На уровне SQL, как ряд определяемых пользователем функций, которые отображаются на вызовы службы.

У интерфейса блокировки есть эти характеристики:

  • У блокировок есть три признака: пространство имен, имя и режим.

    • Блокировки идентифицированы комбинацией пространства имен и имени. Пространство имен позволяет различным приложениям использовать те же самые имена блокировки, не сталкиваться, создавая блокировки в отдельных пространствах имен. Например, если приложения A и B используют пространства имен ns1 и ns2, соответственно, каждое приложение может использовать имена блокировки lock1 и lock2 не вмешиваясь в другое приложение.

    • Режим блокировки чтение или запись. Блокировки чтения совместно использованы: если у сеанса есть блокировка чтения и заданный идентификатор блокировки, другие сеансы могут получить блокировку чтения, использщуя тот же самый идентификатор. Блокировки записи исключительны: если у сеанса есть блокировка записи, другие сеансы не могут получить блокировку чтения или записи, используя тот же самый идентификатор.

  • Пространство имен и имена блокировки должны быть не-NULL, непустым и иметь максимальную длину 64 символа. Пространство имен или имя блокировки, определенное как NULL, пустая строка или строка длинней 64 символов, приводит к ошибке ER_LOCKING_SERVICE_WRONG_NAME.

  • Интерфейс блокировки обрабатывает пространство имен и имена блокировки как двоичные строки, таким образом, сравнения являются чувствительными к регистру.
  • Интерфейс блокировки обеспечивает функции, чтобы приобрести и освободить блокировки. Никакая специальная привилегия не нужна, чтобы вызывать эти функции. Проверка привилегии ответственность приложения.
  • Блокировки могут ждать ресурс, если немедленно он не доступен. Требования приобретения блокировки берут значение тайм-аута целого числа, которое указывает сколько секунд ждать, чтобы приобрести блокировку перед отказом. Если тайм-аут достигнут без успешного приобретения блокировки, будет ошибка ER_LOCKING_SERVICE_TIMEOUT. Если тайм-аут 0, нет никакого ожидания, и требование производит ошибку, если блокировки не могут быть немедленно приобретены.
  • Интерфейс блокировки обнаруживает тупик между требованиями приобретения блокировки в различных сеансах. В этом случае служба блокировки выбирает вызывающего и заканчивает запрос приобретения блокировки с ошибкой ER_LOCKING_SERVICE_DEADLOCK. Эта ошибка не заставляет транзакции отмениться. Чтобы выбрать сеанс в случае тупика, служба блокировки предпочитает сеансы, которые держат блокировки чтения.
  • Сеанс может приобрести много блокировок единственным требованием приобретения блокировки. Для данного требования приобретение блокировки является атомным: требование успешно, если все блокировки приобретены. Если приобретение какой-либо блокировки терпит неудачу, требование не приобретает блокировок и терпит неудачу целиком, как правило с ошибкой ER_LOCKING_SERVICE_TIMEOUT или ER_LOCKING_SERVICE_DEADLOCK.
  • Сеанс может приобрести много блокировок для того же самого идентификатора блокировки (комбинация пространства имен и имени). Это могут быть блокировки чтения, записи или смесь обоих вариантов.
  • Блокировки, приобретенные в пределах сеанса, освобождаются явно, вызывая функцию освобождения блокировок, или неявно, когда сеанс заканчивается. Блокировки не освобождаются, когда транзакции передаются или откатываются.
  • В пределах сеанса все блокировки для данного пространства имен освобождаются совместно.

Интерфейс, обеспеченный службой блокировки, отличен от обеспеченного функциями SQL GET_LOCK() и связанными с ней (см. раздел 13.18). Например, GET_LOCK() не осуществляет пространства имен и обеспечивает только исключительные блокировки.

26.3.1.1. C Интерфейс службы блокировки

Этот раздел описывает, как использовать интерфейс языка C службы блокировки. Чтобы использовать интерфейс UDF вместо этого, см. раздел 26.3.1.2.

Исходные файлы, которые используют службу блокировки, должны включать этот заголовочный файл:

#include <mysql/service_locking.h>
Чтобы приобрести одну или более блокировок, вызовите эту функцию:
int mysql_acquire_locking_service_locks(MYSQL_THD opaque_thd,
          const char* lock_namespace,
          const char**lock_names,
          size_t lock_num,
          enum enum_locking_service_lock_type lock_type,
          unsigned long lock_timeout);
У параметров есть эти значения:
  • opaque_thd: Дескриптор потока. Если определено как NULL, дескриптор для текущего потока используется.

  • lock_namespace: Законченная нулем строка, которая указывает на пространство имен блокировки.
  • lock_names: Массив законченных нулем строк, который обеспечивает названия блокировок.
  • lock_num: Число имен в массиве lock_names.
  • lock_type: Режим блокировки: LOCKING_SERVICE_READ или LOCKING_SERVICE_WRITE.
  • lock_timeout: Число (целое) секунд, сколько ждать приобретения блокировки перед отказом.

Чтобы освободить блокировки для данного пространства имен, вызовите эту функцию:

int mysql_release_locking_service_locks(MYSQL_THD opaque_thd,
                                        const char* lock_namespace);
У параметров есть эти значения:

  • opaque_thd: Дескриптор потока. Если определено как NULL, дескриптор для текущего потока используется.

  • lock_namespace: Законченная нулем строка, которая указывает на пространство имен блокировки.

Приобретенные блокировки или ждущие службу блокировки, могут быть проверены на уровне SQL, используя Performance Schema. Подробности в разделе 26.3.1.2.3.

26.3.1.2. Служба блокировки и интерфейс UDF

Этот раздел описывает, как использовать службу блокировки с интерфейсом UDF. Чтобы использовать интерфейс языка C вместо этого, см. раздел 26.3.1.1.

26.3.1.2.1. Установка или удаление интерфейса блокировок UDF

Функции службы блокировки, описанные в разделе 26.3.1.1 не должны быть установлены, потому что они встроены в сервер. То же самое не верно для определяемых пользователем функций (UDF), которые отображаются на вызовы службы: UDF должен быть установлен перед использованием. Этот раздел описывает, как сделать это. Для общей информации об установке UDF см. раздел 26.4.2.5.

Служба блокировки UDF осуществлена в файле библиотеки, расположенном в каталоге, названном в системной переменной plugin_dir. Базовое имя файла locking_service. Суффикс имени файла отличается на разных платформах (.so для Unix, .dll для Windows).

Чтобы установить службу блокировки UDF, используйте запрос CREATE FUNCTION (корректируйте суффикс .so для Вашей платформы по мере необходимости):

CREATE FUNCTION service_get_read_locks RETURNS INT SONAME 'locking_service.so';
CREATE FUNCTION service_get_write_locks RETURNS INT SONAME 'locking_service.so';
CREATE FUNCTION service_release_locks RETURNS INT SONAME 'locking_service.so';
Если UDF используются на основном сервере репликации, установите ее на всех ведомых серверах также, чтобы избежать проблем.

После того, как установлена, UDF остается установленной, пока не удалят. Чтобы удалить ее, используйте DROP FUNCTION:

DROP FUNCTION service_get_read_locks;
DROP FUNCTION service_get_write_locks;
DROP FUNCTION service_release_locks;
26.3.1.2.2. Использование интерфейса блоокировок в UDF

Перед использованием службы блокировки UDF, установите их согласно инструкциям, обеспеченным в разделе 26.3.1.2.1.

Чтобы приобрести одну или более блокировок чтения, вызовите эту функцию:

mysql> SELECT service_get_read_locks('mynamespace', 'rlock1', 'rlock2', 10);
+---------------------------------------------------------------+
| service_get_read_locks('mynamespace', 'rlock1', 'rlock2', 10) |
+---------------------------------------------------------------+
| 1                                                             |
+---------------------------------------------------------------+
Первый параметр пространство имен блокировки. Заключительный параметр тайм-аут указывающий сколько секунд ждать, чтобы приобрести блокировки перед отказом. Остальными параметрами являются имена блокировок.

Для показанного примера функция приобретает блокировки с идентификаторами (mynamespace, rlock1) и (mynamespace, rlock2).

Чтобы приобрести блокировки записи, а не чтения, вызывают эту функцию:

mysql> SELECT service_get_write_locks('mynamespace', 'wlock1', 'wlock2', 10);
+----------------------------------------------------------------+
| service_get_write_locks('mynamespace', 'wlock1', 'wlock2', 10) |
+----------------------------------------------------------------+
|  1                                                             |
+----------------------------------------------------------------+
В этом случае идентификаторы блокировки (mynamespace, wlock1) и (mynamespace, wlock2).

Чтобы освободить все блокировки для пространства имен, используйте эту функцию:

mysql> SELECT service_release_locks('mynamespace');
+--------------------------------------+
| service_release_locks('mynamespace') |
+--------------------------------------+
| 1                                    |
+--------------------------------------+
Каждая функция блокировки возвращает отличное от нуля значение для успеха. Если функция терпит неудачу, происходит ошибка. Например, следующая ошибка происходит, потому что имена блокировки не могут быть пустыми:
mysql> SELECT service_get_read_locks('mynamespace', '', 10);
ERROR 3131 (42000): Incorrect locking service lock name ''.
Сеанс может приобрести много блокировок для того же самого идентификатора блокировки. Пока у иного сеанса нет блокировки записи для идентификатора, сеанс может приобрести любое число блокировок чтения или записи. Каждый запрос блокировки об идентификаторе приобретает новую блокировку. Следующие запросы приобретают три блокировки записи с тем же самым идентификатором, а потом три блокировки чтения для того же самого идентификатора:
SELECT service_get_write_locks('ns', 'lock1', 'lock1', 'lock1', 0);
SELECT service_get_read_locks('ns', 'lock1', 'lock1', 'lock1', 0);
Если Вы исследуете таблицу metadata_locks Performance Schema, Вы найдете, что сеанс держит шесть отличных блокировок с тем же самым идентификатором (ns, lock1). Для деталей см. раздел 26.3.1.2.3.

Поскольку сеанс держит минимум одну блокировку записи на (ns, lock1), никакой другой сеанс не может приобрести блокировку чтения или записи. Если сессия хранит только блокировку чтения для идентификатора, другие сеансы могли бы приобрести блокировки чтения, но не записи для него.

Блокировки для единственного требования приобретения блокировки приобретены атомарно, но атомичность не держится между вызовами. Таким образом, для следующего запроса, где service_get_write_locks() вызывают для каждой строки результатов, атомичность держится для каждого отдельного требования, но не для всего запроса в целом:

SELECT service_get_write_locks('ns', 'lock1', 'lock2', 0) FROM t1 WHERE ... ;

Поскольку служба блокировки возвращает отдельную блокировку для каждого успешного запроса о данном идентификаторе блокировки, для единственного запроса возможно приобрести большое количество блокировок. Например:

INSERT INTO ... SELECT service_get_write_locks('ns', t1.col_name, 0) FROM t1;
У этих типов запросов могут быть определенные отрицательные воздействия. Например, если часть запроса провалилась с отменой транзакции, то блокировки, приобретенные на грани отказа, будут все еще существовать. Кроме того, если важно, чтобы блокировки предоставили в определенном порядке, знайте, что порядок набора результатов может отличаться, в зависимости от плана выполнения, выбранного оптимизатором. По этим причинам может быть лучше ограничить приложения единственным требованием приобретения блокировки за запрос.

26.3.1.2.3. Контроль службы блокировки

Служба блокировки осуществлена, используя структуру блокировок метаданных сервера MySQL, таким образом, Вы контролируете приобретенные или ждущие блокировки службы, исследуя таблицу metadata_locks Performance Schema.

Включите инструмент блокировки метаданных:

mysql> UPDATE performance_schema.setup_instruments SET ENABLED = 'YES'
    -> WHERE NAME = 'wait/lock/metadata/sql/mdl';
Приобретите некоторые блокировки и проверьте содержание таблицы metadata_locks:
mysql> SELECT service_get_write_locks('mynamespace', 'lock1', 0);
+----------------------------------------------------+
| service_get_write_locks('mynamespace', 'lock1', 0) |
+----------------------------------------------------+
|  1                                                 |
+----------------------------------------------------+
mysql> SELECT service_get_read_locks('mynamespace', 'lock2', 0);
+---------------------------------------------------+
| service_get_read_locks('mynamespace', 'lock2', 0) |
+---------------------------------------------------+
| 1                                                 |
+---------------------------------------------------+
mysql> SELECT OBJECT_TYPE, OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_STATUS
    -> FROM performance_schema.metadata_locks
    -> WHERE OBJECT_TYPE = 'LOCKING SERVICE'\G
*************************** 1. row ***************************
  OBJECT_TYPE: LOCKING SERVICE
OBJECT_SCHEMA: mynamespace
  OBJECT_NAME: lock1
    LOCK_TYPE: EXCLUSIVE
  LOCK_STATUS: GRANTED
*************************** 2. row ***************************
  OBJECT_TYPE: LOCKING SERVICE
OBJECT_SCHEMA: mynamespace
  OBJECT_NAME: lock2
    LOCK_TYPE: SHARED
  LOCK_STATUS: GRANTED
Блокировки службы имеют значение OBJECT_TYPE LOCKING SERVICE. Это отлично от, например, блокировки, приобретенной с помощью функции GET_LOCK(), у которой OBJECT_TYPE USER LEVEL LOCK.

Пространство имен блокировки, имя и режим появляются в столбцах OBJECT_SCHEMA, OBJECT_NAME и LOCK_TYPE. Блокировки чтения и записи имеют значения LOCK_TYPE соответственно SHARED и EXCLUSIVE.

Значение LOCK_STATUS GRANTED для приобретенной блокировки и PENDING для ждущей. Вы будете видеть PENDING, если один сеанс держит блокировку заииси, а другой пытается приобрести блокировку, имеющую тот же самый идентификатор.

26.3.1.2.4. Интерфейса UDF: служба блокировок

Интерфейс SQL к службе блокировки осуществляет определяемые пользователем функции, описанные в этом разделе. Для примеров использования см. раздел 26.3.1.2.2.

Функции совместно используют эти характеристики:

  • Возвращаемое значение является отличным от нуля для успеха. Иначе происходит ошибка.

  • Пространство имен и имена блокировки должны быть не-NULL, непустыми и иметь максимальную длину в 64 символа.
  • Значения тайм-аута должны быть целыми числами, указывающими сколько секунд ждать, чтобы приобрести блокировки перед отказом с ошибкой. Если тайм-аут 0, нет никакого ожидания, и функция производит ошибку, если блокировки не могут быть немедленно приобретены.

Служба блокировки UDF доступна:

  • service_get_read_locks(namespace, lock_name[, lock_name] ..., timeout)

    Приобретает одну или более блокировок чтения (совместно используемых) в данном пространстве имен, используя данные имена блокировки и тайм-аут с ошибкой, если блокировки не приобретены в пределах данного значения тайм-аута.

  • service_get_write_locks(namespace, lock_name[, lock_name] ..., timeout)

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

  • service_release_locks(namespace)

    Для данного пространства имен освобождает все блокировки, которые были приобретены в пределах текущего использования сеанса service_get_read_locks() и service_get_write_locks().

    Если блокировок нет в пространстве имен, это не ошибка.

26.3.2. Сервис Keyring

MySQL Server поддерживает службу keyring, которая позволяет внутренним серверным компонентам и плагинам надежно хранить чувствительную информацию для более позднего извлечения. Этот раздел описывает, как использовать служебные функции, чтобы сохранить, получить и удалить ключи в MySQL keyring. Интерфейс SQL к служебным функциям также доступен как ряд определяемых пользователем функций (UDF), см. раздел 7.5.3.3.

Запись в хранилище состоит из данных (ключ непосредственно) и уникального идентификатора, через который получают доступ к ключу. У идентификатора есть две части:

  • key_id: ID или имя. Значение key_id, которые начинаются с mysql_, зарезервированы MySQL Server.

  • user_id: Эффективный ID сеанса. Если нет никакого пользовательского контекста, это значение может быть NULL. Значение не должно фактически быть пользователем: например, InnoDB использует ID табличного пространства, чтобы управлять ключами для табличных пространств.

    Функции, которые осуществляют интерфейс UDF, передают значение CURRENT_USER() как значение user_id функциям сервиса keyring.

У служебных функций keyring есть эти характеристики:

  • Каждая функция возвращает 0 для успеха, 1 для отказа.

  • Параметры key_id и user_id формируют уникальную комбинацию, указывающую который ключ использовать.
  • Параметр key_type обеспечивает дополнительную информацию о ключе, такую как его метод шифрования или предназначенное использование.
  • Служебные функции Keyring обрабатывают ID ключей, имена пользователя, типы и значения как двоичные строки , таким образом, сравнения являются чувствительными к регистру. Например, ID MyKey и mykey относятся к различным ключам.

Эти служебные функции доступны:

  • my_key_fetch()

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

    my_bool my_key_fetch(const char *key_id, const char **key_type,
                         const char* user_id, void **key, size_t *key_len)
    
    Параметры:
    • key_id, user_id: Законченные нулем строки, задают как пара уникальный идентификатор, указывающий, который ключ принести.

    • key_type: Адрес буферного указателя. Функция хранит в этом указатель на законченную нулем строку, которая обеспечивает дополнительную информацию о ключе (сохранена, когда ключ был добавлен).
    • key: Адрес буферного указателя. Функция хранит в этом указатель на буфер, содержащий принесенные ключевые данные.
    • key_len: Адрес переменной, в которой функция хранит размер в байтах буфера *key.

    Возвращаемые значения:

    Возвращает 0 для успеха, 1 для отказа.

  • my_key_generate()

    Производит новый случайный ключ длиной key_len и связывает его с идентификатором, сформированным из key_id и user_id. Тип и значения длины должны быть совместимыми со значениями, поддержанными основным плагином (таблица 26.4 показывает разрешенные типы). При успехе произведенный ключ должен быть добавлен в хранилище.

    Таблица 26.4. Ключевые типы плагина

    Имя плагина Допустимые типы ключей
    keyring_file AES, DSA, RSA

    Синтаксис:

    my_bool my_key_generate(const char *key_id, const char *key_type,
                            const char *user_id, size_t key_len)
    
    Параметры:

    • key_id, user_id: Законченные нулем строки, как пара определяют уникальный идентификатор для ключа, который будет произведен.

    • key_type: Законченная нулем строка, которая обеспечивает дополнительную информацию о ключе.
    • key_len: Размер в байтах ключа, который будет произведен.

    Возвращаемые значения:

    Возвращает 0 для успеха, 1 для отказа.

  • my_key_remove()

    Удаляет ключ из хранилища.

    Синтаксис:

    my_bool my_key_remove(const char *key_id, const char* user_id)
    
    Параметры:

    • key_id, user_id: Законченные нулем строки, как пара определяют уникальный идентификатор для ключа, который будет удален.

    Возвращаемые значения:

    Возвращает 0 для успеха, 1 для отказа.

  • my_key_store()

    Шифрует и хранит ключ в хранилище.

    Синтаксис:

    my_bool my_key_store(const char *key_id, const char *key_type,
                         const char* user_id, void *key, size_t key_len)
    
    Параметры:

    • key_id, user_id: Законченные нулем строки, как пара определяют уникальный идентификатор для ключа, который будет сохранен.

    • key_type: Законченная нулем строка, которая обеспечивает дополнительную информацию о ключе.
    • key: Буфер, содержащий ключевые данные, которые будут сохранены.
    • key_len: Размер в байтах key.

    Возвращаемые значения:

    Возвращает 0 для успеха, 1 для отказа.

26.4. Добавление новых функций в MySQL

Есть три способа добавить новые функции к MySQL:

  • Вы можете добавить функции через определяемую пользователем функцию (UDF). Определяемые пользователем функции собраны как файлы библиотеки и затем добавлены или удалены динамически, используя запросы CREATE FUNCTION и DROP FUNCTION. См. раздел 14.7.3.1.

  • Вы можете добавить функции как нативные (встроенные) функции MySQL. Нативные функции собраны в сервере mysqld и становятся доступными на постоянной основе.
  • Другой способ добавить функции, это создать сохраненные функции. Они написаны, используя запросы SQL, а не собирая код объекта. Синтаксис для того, чтобы написать сохраненные функции не покрыт здесь. См. раздел 21.2.

У каждого метода создания функций есть преимущества и недостатки:

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

  • Нативные функции требуют, чтобы Вы изменили исходный текст. Вы можете добавить UDF к двоичному дистрибутиву MySQL. Никакой доступ к исходному тексту MySQL не нужен.
  • Если Вы обновляете MySQL, Вы можете продолжить использовать свои ранее установленные UDF, если Вы не обновляетесь до более новой версии, для которой UDF-интерфейс изменился. Для нативных функций Вы должны повторить свои модификации каждый раз, когда Вы обновляете сервер.

Какой бы ни был метод, который Вы используете, чтобы добавить новые функции, они могут быть вызваны в запросах SQL точно так же, как родные функции, такие как ABS() или SOUNDEX().

См. раздел 10.2.4 для правил, описывающих, как сервер интерпретирует ссылки на различные виды функций.

Следующие разделы описывают особенности интерфейса UDF, обеспечивают инструкции для того, чтобы написать UDF, обсуждают предосторожности безопасности, которые MySQL предпринимает, чтобы предотвратить неправильное употребление UDF, и описывают, как добавить нативные функции MySQL.

Например, исходный код, который иллюстрирует, как написать UDF, можно посмотреть в файле sql/udf_example.cc, который обеспечен в исходных текстах MySQL.

Исходный текст MySQL содержит внутреннюю документацию, созданную с применением Doxygen. Эта документация полезна для понимания, как MySQL работает с точки зрения разработчика. Произведенный контент Doxygen доступен на http://dev.mysql.com/doc/dev/mysql-server/latest/. Также возможно произвести этот контент локально из дистрибутива исходных текстов MySQL, используя инструкции в разделе 2.8.7.

26.4.1. Особенности определяемого пользователем функционального интерфейса

Интерфейс MySQL для определяемых пользователем функций обеспечивает следующие особенности и способности:

  • Функции могут возвратить строку, целое или реальное число и могут принять параметры тех же самых типов.

  • Вы можете определить простые функции, которые воздействуют на единственную строку за один раз, или совокупные функции, которые воздействуют на группы строк.
  • Информация, предоставленная функциям, позволяет им проверить число, типы и названия переданных параметров.
  • Вы можете сказать MySQL привести параметры к заданному типу прежде, чем передать их функции.
  • Вы можете указать, что функция возвращает NULL или что ошибка произошла.

26.4.2. Добавление новой определяемой пользователем функции

Для механизма UDF функции должны быть написаны на C или C++, и Ваша операционная система должна поддерживать динамическую загрузку. Исходные тексты MySQL включают файл sql/udf_example.cc, который определяет пять функций UDF. Консультируйтесь с этим файлом, чтобы видеть, как работают UDF. Заголовочный файл include/mysql_com.h определяет связанные с UDF символы и структуры данных, хотя Вы не должны включать этот заголовочный файл непосредственно: это включено mysql.h.

UDF содержит код, который становится частью рабочего сервера, так что, когда Вы пишете UDF, Вы связаны всеми ограничениями, которые относятся к написанию кода сервера. Например, у Вас могут быть проблемы, если Вы пытаетесь использовать функции из библиотеки libstdc++. Эти ограничения могут измениться в будущих версиях сервера, таким образом, возможно, что обновления сервера потребуют пересмотра UDF, которые были первоначально написаны для более старых серверов. Для информации об этих ограничениях, см. разделы 2.8.4 и 2.8.5.

Чтобы быть в состоянии использовать UDFs, Вы должны скомпоновать mysqld динамически. Если Вы хотите использовать UDF, который должен получить доступ к символам из mysqld (например, функция metaphone в sql/udf_example.cc использует default_charset_info), Вы должны скомпоновать программу с -rdynamic (см. man dlopen).

Для каждой функции, которую Вы хотите использовать в запросах SQL, Вы должны определить соответствующую C (или C++) функцию. В следующем обсуждении имя xxx используется для имени функции в качестве примера. Чтобы различать SQL и C/C++ использование, XXX() (верхний регистр) указывает на вызов функции SQL, а xxx() (нижний регистр) указывает на C/C++ вызов функции.

Используя C++ Вы можете инкапсулировать свои функции C в пределах:

extern "C" { ... }
Это гарантирует, что Ваши C++ имена функций остаются читаемыми в завершенном UDF.

Следующий список описывает C/C++ функции, которые Вы пишете, чтобы осуществить интерфейс для названной функции XXX(). Основная функция, xxx(), обязательна. Кроме того, UDF требует по крайней мере одну из других функций, описанных здесь, по причинам, обсуждаемым в разделе 26.4.2.6.

  • xxx()

    Основная функция. Это то место, где функциональный результат вычислен. Связь между типом данных функции SQL и типом возвращения Вашей C/C++ функции показывают здесь.

    Тип в SQL TypeТип в C/C++
    STRINGchar *
    INTEGER long long
    REAL double

    Также возможно объявить функцию DECIMAL, но в настоящее время значение возвращено как строка, таким образом, Вы должны написать UDF, как если бы это была функция типа STRING. Функции ROW не реализованы.

  • xxx_init()

    Функция инициализации для xxx(). Если существует, это может использоваться в следующих целях:

    • Проверять число параметров XXX().

    • Проверить, что параметры имеют необходимый тип или, альтернативно, чтобы сказать MySQL привести параметры к необходимым типам, когда основная функция вызвана.
    • Выделить любую память, требуемую основной функцией.
    • Определить максимальную длину результата.
    • Определить (для функций REAL) максимальное количество десятичных разрядов в результате.
    • Определить, может ли результат быть NULL.

  • xxx_deinit()

    Функция завершения для xxx(). Если существует, это должно освободить память, выделенную функцией инициализации.

Когда запрос SQL вызывает XXX(), MySQL вызывает функцию инициализации xxx_init(), чтобы выполнить любую необходимую установку, такую как проверка параметров или распределение памяти. Если xxx_init() вернет ошибку, MySQL прерывает запрос SQL с сообщением об ошибке и не вызывает функции завершения или основную. Иначе, MySQL вызывает основную функцию xxx() однажды для каждой строки. После того, как все строки были обработаны, MySQL вызывает функцию завершения xxx_deinit(), чтобы это могло выполнить любую необходимую уборку.

Для совокупных функций, которые работают как SUM(), Вы должны также обеспечить следующие функции:

  • xxx_clear()

    Сбросить текущее совокупное значение, но не вставлять параметр как начальное совокупное значение для новой группы.

  • xxx_add()

    Добавляет параметр к текущему совокупному значению.

MySQL обрабатывает совокупный UDF следующим образом:

  1. Вызывает xxx_init(), чтобы позволить совокупной функции выделить любую память, в которой она нуждается для того, чтобы сохранить результаты.

  2. Сортирует таблицу согласно выражению GROUP BY.
  3. Вызывает xxx_clear() для первой строки в каждой новой группе.
  4. Вызывает xxx_add() для каждой строки, которая принадлежит к той же самой группе.
  5. Вызывает xxx(), чтобы получить результат для совокупности, когда группа изменяется или после того, как последняя строка была обработана.
  6. Повторяет шаги 3-5, пока все строки не будут обработаны.
  7. Вызывает xxx_deinit(), чтобы позволить UDF освободить любую память, которую это выделило.

Все функции должны быть безопасными для потока. Это включает не только основную функцию, но и функции инициализации и завершения, а также дополнительные функции, требуемые совокупными функциями. Последствие этого требования то, что Вам не разрешают выделить глобальные или статические переменные, которые изменяются. Если Вы нуждаетесь в памяти, Вы должны выделить ее в xxx_init() и освободить в xxx_deinit().

26.4.2.1. Вызов UDF для простых функций

Этот раздел описывает различные функции, которые Вы должны определить, когда Вы создаете простой UDF. Раздел 26.4.2 описывает порядок, в котором MySQL вызывает эти функции.

Основная xxx() функция должна быть объявлена как показано в этом разделе. Отметьте, что тип возврата и параметры отличаются, в зависимости от того, объявляете ли Вы функцию SQL XXX(), чтобы возвратить STRING, INTEGER или REAL в запросе CREATE FUNCTION:

Для функций STRING:

char *xxx(UDF_INIT *initid, UDF_ARGS *args,
          char *result, unsigned long *length,
          char *is_null, char *error);
Для функций INTEGER:
long long xxx(UDF_INIT *initid, UDF_ARGS *args,
              char *is_null, char *error);
Для функций REAL:
double xxx(UDF_INIT *initid, UDF_ARGS *args,
           char *is_null, char *error);
Функции DECIMAL строковые значения возвращаются и должны быть объявлены тем же самым путем, как и функции STRING. Функции ROW не реализованы.

Функции инициализации и завершения объявлены так:

my_bool xxx_init(UDF_INIT *initid, UDF_ARGS *args, char *message);
void xxx_deinit(UDF_INIT *initid);
Параметр initid передают всем трем функциям. Это указывает на структуру UDF_INIT, которая используется, чтобы сообщить информацию между функциями. Структура UDF_INIT имеет члены, описанные ниже. Функция инициализации должна заполнить любые члены, которые она хочет изменить. Чтобы использовать значение по умолчанию для члена, оставьте это неизменным.

  • my_bool maybe_null

    xxx_init() должна установить maybe_null в 1, если xxx() может возвратить NULL. Значение по умолчанию 1, если какой-либо из параметров объявлен как maybe_null.

  • unsigned int decimals

    Число десятичных цифр после десятичной запятой. Значение по умолчанию максимальное количество десятичных цифр в параметрах, которые передают основной функции. Например, если функции передают 1.34, 1.345 и 1.3, значение по умолчанию 3, потому что 1.345 имеет 3 десятичных цифры.

    Для параметров, у которых нет никакого постоянного числа десятичных чисел, значение decimals установлено в 31, что на 1 больше максимального количества десятичных чисел, разрешенных для типов DECIMAL, FLOAT и DOUBLE. Это значение доступно как константа NOT_FIXED_DEC в файле mysql_com.h.

    Значение decimals 31 используется для параметров в случаях, таких как FLOAT или DOUBLE, когда столбец объявлен без явного количества десятичных чисел (например, FLOAT вместо FLOAT(10,3)) и для констант с плавающей запятой таких, как 1345E-3. Это также используется для строк и других нечисловых параметров, которые могли бы быть преобразованы в пределах функции в числовую форму.

    Значение, которым инициализирован член decimals, это только значение по умолчанию. Это может быть изменено в пределах функции, чтобы отразить фактическое выполненное вычисление. Значение по умолчанию определено таким образом, что наибольшее количество десятичных чисел параметров используется. Если количество десятичных чисел NOT_FIXED_DEC даже для одного из параметров, это является значением, используемым для decimals.

  • unsigned int max_length

    Максимальная длина результата. Значение по умолчанию max_length отличается в зависимости от типа результата функции. Для строковых функций значение по умолчанию длина самого длинного параметра. Для функций целого числа значение по умолчанию 21 цифра. Для реальных функций значение по умолчанию 13 плюс число десятичных цифр, обозначенных initid->decimals. Для числовых функций длина включает любой знак или символы десятичной запятой.

    Если Вы хотите возвратить значение blob, Вы можете установить max_length в 65KB или 16MB. Эта память не выделена, но значение используется, чтобы решить, который тип данных использовать, если есть потребность временно хранить данные.

  • char *ptr

    Указатель, который функция может использовать в ее собственных целях. Например, функции могут использовать initid->ptr, чтобы делить выделенную память между собой. xxx_init() должна выделить память и назначить ее на этот указатель:

    initid->ptr = allocated_memory;
    
    В xxx() и xxx_deinit() обратитесь к initid->ptr, чтобы использовать или освобождать память.
  • my_bool const_item

    xxx_init() должна установить const_item в 1, если xxx() всегда возвращает то же самое значение, и в 0 в противном случае.

26.4.2.2. Вызовы UDF для совокупных функций

Этот раздел описывает различные функции, которые Вы должны определить, когда Вы создаете совокупный UDF. Раздел 26.4.2 описывает порядок, в котором MySQL вызывает эти функции.

  • xxx_reset()

    Эта функция вызвана, когда MySQL находит первую строку в новой группе. Это должно сбросить любые внутренние итоговые переменные и затем использовать данный параметр UDF_ARGS как первое значение в Вашем внутреннем итоговом значении для группы. Объявите xxx_reset() так:

    void xxx_reset(UDF_INIT *initid, UDF_ARGS *args,
                   char *is_null, char *error);
    
    xxx_reset() не используется в MySQL 8.0, в котором UDF интерфейс использует xxx_clear(). Однако, Вы можете определить xxx_reset() и xxx_clear(), если Вы хотите работать с более старыми версиями сервера. Если Вы действительно включаете обе функции, xxx_reset() во многих случаях может быть осуществлена внутренне, вызывая xxx_clear(), чтобы сбрасывать все переменные, и затем запросить xxx_add(), чтобы добавить параметр UDF_ARGS как первое значение в группе.
  • xxx_clear()

    Эта функция вызвана, когда MySQL должен сбросить итоговые результаты. Это вызывают вначале для каждой новой группы, но может также быть вызвано, чтобы сбросить значения для запроса, где не было никаких строк соответствия. Объявите xxx_clear() так:

    void xxx_clear(UDF_INIT *initid, char *is_null, char *error);
    
    is_null указывает на CHAR(0) перед вызовом xxx_clear().

    Если что-то пошло не так, как надо, Вы можете сохранить значение в переменной, указанной параметром error. error указывает на переменную одного байта, а не строковый буфер.

    xxx_clear() требуется в MySQL 8.0.

  • xxx_add()

    Эта функция вызвана для всех строк, которые принадлежат к той же самой группе. Вы должны использовать это, чтобы добавить значение UDF_ARGS к внутренней итоговой переменной.

    void xxx_add(UDF_INIT *initid, UDF_ARGS *args,
                 char *is_null, char *error);
    

Функция xxx() для совокупного UDF должна быть объявлена тем же самым путем, что касается несовокупного UDF. См. раздел 26.4.2.1.

Для совокупного UDF MySQL вызывает xxx() после обработки всех строк в группе. Вы никогда не должны обычно получать доступ к UDF_ARGS здесь, вместо этого надо возвращать значение, основанное на Ваших внутренних итоговых переменных.

Обработка возвращаемого значения в xxx() должна быть сделана тем же самым путем, что касается несовокупного UDF. См. раздел 26.4.2.4.

Функции xxx_reset() и xxx_add() обрабатывают параметр UDF_ARGS тем же самым путем, как функции для несовокупного UDF. См. раздел 26.4.2.3 .

Параметры указателя is_null и error те же самые для всех вызовов xxx_reset(), xxx_clear(), xxx_add() и xxx(). Вы можете использовать это, чтобы помнить, что Вы получили ошибку или xxx() должна возвратить NULL. Вы не должны сохранять строку в *error! error указывает на однобайтовую переменную, а не на строковый буфер.

*is_null сброшен для каждой группы (прежде, чем вызвать xxx_clear()). *error никогда не сбрасывается.

Если *is_null или *error установлены, когда xxx() возвращает значение, MySQL вернет NULL как результат для групповой функции.

26.4.2.3. Обработка параметров в UDF

Параметр args указывает на на структуру UDF_ARGS, члены которой показаны здесь:

  • unsigned int arg_count

    Число параметров. Проверьте это значение в функции инициализации, если Вы требуете, чтобы Ваша функция была вызвана с особым числом параметров. Например:

    if (args->arg_count != 2) {
       strcpy(message,"XXX() requires two arguments");
       return 1;
    }
    
    Для других значений членов UDF_ARGS, которые в массиве, ссылки начинаются с 0. Таким образом, их индексы от 0 до args->arg_count-1.
  • enum Item_result *arg_type

    Указатель на массив, содержащий типы для каждого параметра. Возможные значения типов: STRING_RESULT, INT_RESULT, REAL_RESULT и DECIMAL_RESULT.

    Чтобы удостовериться, что параметры имеют данный тип и вернуть ошибку, если они не такие, проверьте массив arg_type в функции инициализации. Например:

    if (args->arg_type[0] != STRING_RESULT ||
        args->arg_type[1] != INT_RESULT) {
       strcpy(message,"XXX() requires a string and an integer");
       return 1;
    }
    
    Параметры типа DECIMAL_RESULT переданы как строки, таким образом, Вы должны обработать их тем же самым путем, как STRING_RESULT .

    Как альтернатива требованию, чтобы параметры Вашей функции имели особые типы, Вы можете использовать функцию инициализации, чтобы установить элементы arg_type к типам, которые Вы хотите. Это заставляет MySQL привести параметры к тем типам для каждого вызова xxx(). Например, чтобы определить, что первые два параметра должны быть представлены в виде строки и целого числа, соответственно, сделайте в xxx_init():

    args->arg_type[0] = STRING_RESULT;
    args->arg_type[1] = INT_RESULT;
    
    Параметры десятичного числа точного значения такие, как 1.3 или значения столбцов DECIMAL передают с типом DECIMAL_RESULT. Однако, значения передают как строки. Если Вы хотите получить число, используйте функцию инициализации, чтобы определить, что параметр должен быть приведен к значению REAL_RESULT:
    args->arg_type[2] = REAL_RESULT;
    
  • char **args

    args->args связывает информацию функции инициализации об общем характере параметров с Вашей функцией. Для постоянного параметра i args->args[i] указывает на значение параметра. См. позже для инструкций по тому, как получить доступ к значению должным образом. Для непостоянного параметра args->args[i] 0. Постоянный параметр это выражение, которое использует только константы, такое как 3, 4*7-2 или SIN(3.14). Непостоянный параметр это выражение, которое обращается к значениям, которые могут измениться, такие как имена столбцов или функции, которые вызваны с непостоянными параметрами.

    Для каждого вызова основной функции args->args содержит фактические параметры, которые передают для строки, обрабатываемой в настоящее время.

    Если параметр i представляет NULL, args->args[i] указатель null (0). Если параметр не NULL, функции могут обратиться к этому следующим образом:

    • Параметр типа STRING_RESULT дан как указатель на строку плюс длина, чтобы позволить обработать двоичные данные или данные произвольной длины. Строковое содержимое доступно как args->args[i], строковая длина как args->lengths[i]. Не предполагайте, что строка закончена нулем.

    • Для параметра типа INT_RESULT, Вы должны передать args->args[i] в значение типа long long:
      long long int_val;
      int_val = *((long long*) args->args[i]);
      
    • Для параметра типа REAL_RESULT, Вы должны передать args->args[i] в значение типа double:
      doublereal_val;
      real_val = *((double*) args->args[i]);
      
    • Для параметра типа DECIMAL_RESULT, Вы должны передать значение как строку, и оно должно быть обработано как значение STRING_RESULT.

    • Параметры ROW_RESULT не поддерживаются.

  • unsigned long *lengths

    Для функции инициализации массив lengths указывает на максимальную строковую длину для каждого параметра. Вы не должны изменять их. Для каждого вызова основной функции lengths содержит фактические длины любых строковых параметров, которые передают для строки, в настоящее время обрабатываемой. Для параметров типов INT_RESULT или REAL_RESULT, lengths содержит максимальную длину параметра (что касается функции инициализации).

  • char *maybe_null

    Для функции инициализации массив maybe_null указывает для каждого параметра, может ли значение параметра быть null (0, если нет, 1, если да).

  • char **attributes

    args->attributes сообщает информацию о названиях параметров UDF. Для параметра i название атрибута доступно как строка в args->attributes[i], длина атрибута в args->attribute_lengths[i]. Не предполагайте, что строка закончена нулем.

    По умолчанию, название параметра UDF это текст выражения, используемого, чтобы определить параметр. Для UDF у параметра может также быть дополнительное предложение [AS] alias_name, в этом случае имя параметра alias_name. Значение attributes для каждого параметра, таким образом, зависит от того, был ли псевдоним задан.

    Предположите что UDF my_udf() вызвана следующим образом:

    SELECT my_udf(expr1, expr2 AS alias1, expr3 alias2);
    
    В этом случае у массивов attributes и attribute_lengths будут эти значения:
    args->attributes[0] = "expr1"
    args->attribute_lengths[0] = 5
    args->attributes[1] = "alias1"
    args->attribute_lengths[1] = 6
    args->attributes[2] = "alias2"
    args->attribute_lengths[2] = 6
    
  • unsigned long *attribute_lengths

    Массив attribute_lengths указывает на длину каждого имени параметра.

26.4.2.4. Возвращаемые значения UDF и обработка ошибок

Функция инициализации должна возвратить 0, если никакая ошибка не произошла и 1 иначе. Если ошибка происходит, xxx_init() должна сохранить законченное нулем сообщение об ошибке в параметре message. Сообщение возвращено клиенту. Буфер сообщения длиной MYSQL_ERRMSG_SIZE символов, но Вы должны попытаться сохранить сообщение меньше 80 символов, чтобы оно соответствовало ширине стандартного экрана.

Возвращаемое значение основной функции xxx() это функциональное значение для функций long long и double. Строковая функция должна возвратить указатель на результат и установить *length в длину (в байтах) возвращаемого значения. Например:

memcpy(result, "result string", 13);
*length = 13;
MySQL передает буфер функции xxx() используя параметр result. Этот буфер достаточно длинен, чтобы содержать 255 символов, которые могут быть мультибайтными символами. Функция xxx() может сохранить результат в этом буфере, если это так, тогда возвращаемое значение должно быть указателем на буфер. Если функция хранит результат в ином буфере, она должно возвратить указатель на тот буфер.

Если Ваша строковая функция не использует поставляемый буфер (например, если это должно возвратить строку больше 255 символов), Вы должны выделить место для своего собственного буфера с помощью malloc() в функции xxx_init() или xxx() с обязательным освобождением в xxx_deinit(). Вы можете сохранить выделенную память в слоте ptr структуры UDF_INIT для повторного использования в будущем вызове xxx(). См. раздел 26.4.2.1.

Чтобы указать на возвращаемое значение NULL в основной функции, установите *is_null в 1:

*is_null = 1;
Чтобы указать на ошибку в основной функции, установите *error в 1:
*error = 1;
Если xxx() устанавливает *error в 1 для любой строки, значение функции NULL для текущей строки и для любых последующих строк, обработанных запросом XXX().

26.4.2.5. Компиляция и установка UDF

Файлы, осуществляющие UDF, должны быть собраны и установлены на хосте, где работает сервер. Этот процесс описан ниже для файла примера UDF sql/udf_example.cc, который включен в исходные тексты MySQL.

Если UDF будет упомянут в запросах, которые будут копироваться к ведомым серверам, Вы должны гарантировать, что каждое ведомое устройство также имеет функцию в наличии. Иначе, репликация потерпит неудачу на ведомых устройствах, когда они попытаются вызвать функцию.

Инструкции для Windows даны позже в этом разделе.

Файл udf_example.cc файл содержит следующие функции:

  • metaphon() возвращает metaphon строку строкового параметра. Это что-то подобное строке soundex, но это более настроено для английского языка.

  • myfunc_double() возвращает сумму значений ASCII символов в параметрах, разделенных на сумму длины параметров.
  • myfunc_int() возвращает сумму длины ее параметров.
  • sequence([const int]) возвращает последовательность, начинающуюся с данного числа или 1, если никакое число не было дано.
  • lookup() возвращает IP-адрес для имени хоста.
  • reverse_lookup() возвращает имя хоста для IP-адреса. Функция может быть вызвана с единственным строковым параметром формы 'xxx.xxx.xxx.xxx' или с четырьмя числами.
  • avgcost() возвращает среднюю стоимость. Это совокупная функция.

Динамически загружаемый файл должен быть собран как файл библиотеки с обеспечением совместного доступа, используя команду вроде этой:

shell> gcc -shared -o udf_example.so udf_example.cc
Если Вы используете gcc с CMake (как MySQL сконфигурирован), Вы должны быть в состоянии создать udf_example.so более простой командой:
shell> make udf_example
После того, как Вы собираете совместно используемый объект, содержащий UDF, Вы должны установить его и сказать MySQL об этом. Компилирование совместно используемого объекта из udf_example.cc, используя gcc непосредственно производит файл, названный udf_example.so. Скопируйте совместно используемый объект в каталог плагинов сервера и назовите его udf_example.so. Этот каталог задан значением системной переменной plugin_dir.

На некоторых системах программа ldconfig, которая конфигурирует динамический компоновщик, не признает совместно используемый объект, если его имя не начинается с lib. В этом случае Вы должны переименовать файл udf_example.so в libudf_example.so.

В Windows Вы можете собрать определяемые пользователем функции при использовании следующей процедуры:

  1. Получите исходные тексты MySQL. См. раздел 2.1.2.

  2. Получите утилиту CMake в случае необходимости с http://www.cmake.org. Версия 2.6 или позже.
  3. В дереве исходных текстов, в каталоге sql, есть файлы udf_example.def и udf_example.cc. Скопируйте оба файла из этого каталога в Ваш рабочий каталог.
  4. Создайте CMake makefile (CMakeLists.txt):
    PROJECT(udf_example)
    
    # Path for MySQL include directory
    INCLUDE_DIRECTORIES("c:/mysql/include")
    
    ADD_DEFINITIONS("-DHAVE_DLOPEN")
    ADD_LIBRARY(udf_example MODULE udf_example.cc udf_example.def)
    TARGET_LINK_LIBRARIES(udf_example wsock32)
    
  5. Создайте проект VC и файлы решения:
    cmake -G "<Generator>"
    
    Вызов cmake --help показывает Вам список допустимых Generators.
  6. Создайте udf_example.dll:
    devenv udf_example.sln /build Release
    

После того, как совместно используемый файл библиотеки был установлен, сообщите mysqld о новых функциях. Если у файлов библиотеки есть суффикс, отличающийся от .so на Вашей системе, замените правильным суффиксом повсюду (например, .dll для Windows).

mysql> CREATE FUNCTION metaphon RETURNS STRING SONAME 'udf_example.so';
mysql> CREATE FUNCTION myfunc_double RETURNS REAL SONAME 'udf_example.so';
mysql> CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME 'udf_example.so';
mysql> CREATE FUNCTION sequence RETURNS INTEGER SONAME 'udf_example.so';
mysql> CREATE FUNCTION lookup RETURNS STRING SONAME 'udf_example.so';
mysql> CREATE FUNCTION reverse_lookup
    ->RETURNS STRING SONAME 'udf_example.so';
mysql> CREATE AGGREGATE FUNCTION avgcost
    ->RETURNS REAL SONAME 'udf_example.so';
После того, как установлена, функция остается установленной, пока она не удалена.

Чтобы удалить функции, используйте DROP FUNCTION:

mysql> DROP FUNCTION metaphon;
mysql> DROP FUNCTION myfunc_double;
mysql> DROP FUNCTION myfunc_int;
mysql> DROP FUNCTION sequence;
mysql> DROP FUNCTION lookup;
mysql> DROP FUNCTION reverse_lookup;
mysql> DROP FUNCTION avgcost;
Запросы CREATE FUNCTION и DROP FUNCTION обновляют системную таблицу func в базе данных mysql. Имя функции, тип и имя совместно использованной библиотеки сохранены в таблице. Вы должны иметь привилегии INSERT и DELETE для базы данных mysql, чтобы создать и удалить функции, соответственно.

Вы не должны использовать CREATE FUNCTION, чтобы добавить функцию, которая была ранее создана. Если Вы должны повторно установить функцию, Вы должны удалить ее сначала через DROP FUNCTION и затем повторно установить с CREATE FUNCTION. Вы должны были бы сделать это, например, если Вы повторно собираете новую версию своей функции, так, чтобы сервер mysqld получил новую версию. Иначе, сервер продолжает использовать старую версию.

Активная функция та, которая была загружена CREATE FUNCTION и не удалена DROP FUNCTION. Все активные функции перезагружены каждый раз, когда сервер запускается, если Вы не запускаете mysqld с опцией --skip-grant-tables. В этом случае инициализация UDF пропущена, и UDF недоступны.

26.4.2.6. Предосторожности безопасности UDF

MySQL принимает несколько мер, чтобы предотвратить неправильное употребление определяемых пользователем функций.

Файлы библиотеки UDF не могут быть помещены в произвольные каталоги. Они должны быть расположены в каталоге плагинов сервера. Этот каталог задан значением системной переменной plugin_dir.

Чтобы использовать CREATE FUNCTION или DROP FUNCTION , Вы должны иметь привилегии INSERT или DELETE , соответственно, для базы данных mysql. Это необходимо, потому что эти запросы добавляют и удаляют строки из таблицы mysql.func.

У UDFs должен быть по крайней мере один символ, определенный в дополнение к xxx, который соответствует основноой функции xxx(). Эти вспомогательные символы соответствуют функциям xxx_init(), xxx_deinit(), xxx_reset(), xxx_clear() и xxx_add(). mysqld также поддерживает опцию --allow-suspicious-udfs, которая управляет тем, может ли быть загружена UDF, которая имеет только символ xxx. По умолчанию опция выключена, чтобы предотвратить попытки загрузки функций из совместно используемых файлов библиотеки кроме тех, которые содержат законный UDF. Если у Вас есть более старые UDF, которые содержат только символ xxx и они не могут быть повторно собраны, чтобы включить вспомогательный символ, может быть необходимо определить опцию the --allow-suspicious-udfs. Иначе Вы должны избегать включать это.

26.4.3. Добавление новой нативной функции

Чтобы добавить новую функцию, используйте процедуру, описанную здесь, которая требует, чтобы Вы использовали исходные тексты. Вы не можете добавить нативные функции к двоичному дистрибутива, потому что необходимо изменить исходный код MySQL и собрать MySQL из измененного текста. Если Вы мигрируете на другую версию MySQL (например, когда новая версия выпущена), Вы должны повторить процедуру с новой версией.

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

Чтобы добавить новую функцию, следуйте за этими шагами, чтобы изменить исходные файлы в каталоге sql:

  1. Создайте подкласс для функции в item_create.cc:

    • Если функция берет постоянное число параметров, создайте подкласс Create_func_arg0, Create_func_arg1, Create_func_arg2 или Create_func_arg3, соответственно, в зависимости от того, берет ли функция ноль, один, два или три параметра. Для примеров см. классы Create_func_uuid, Create_func_abs, Create_func_pow и Create_func_lpad.

    • Если функция берет переменное число параметров, создайте подкласс Create_native_func. Для примера см. Create_func_concat.

  2. Чтобы обеспечить имя, которым функция может быть упомянута в запросах SQL, зарегистрируйте имя в item_create.cc, добавляя строку к этому массиву:

    static Native_func_registry func_array[]
    
    Вы можете зарегистрировать несколько названий той же самой функции. Например, см. строки для "LCASE" и "LOWER", которые являются псевдонимами для Create_func_lcase.
  3. В item_func.h объявите класс, наследующий Item_num_func или Item_str_func, в зависимости от того, возвращает ли Ваша функция число или строку.
  4. В item_func.cc добавьте одну из следующих деклараций, в зависимости от того, определяете ли Вы числовую или строковую функцию:
    double   Item_func_newname::val()
    longlong Item_func_newname::val_int()
    String  *Item_func_newname::Str(String *str)
    
    Если Вы наследуете свой объект от какого-либо из стандартных элементов (как Item_num_func), Вы, вероятно, только должны определить одну из этих функций и позволить родительскому объекту заботиться о других функциях. Например, класс Item_str_func определяет функцию val(), которая выполняет atof() на значении, возвращенном ::str().
  5. Если функция недетерминирована, включайте следующее заявление в конструктор элемента, чтобы указать, что функциональные результаты не должны кэшироваться:
    current_thd->lex->safe_to_cache_query=0;
    
    Функция недетерминирована, если, учитывая фиксированные значения для ее параметров, она может возвратить различные результаты для различных вызовов.
  6. Вы должны, вероятно, также определить следующую функцию объекта:
    void Item_func_newname::fix_length_and_dec()
    
    Эта функция должна, по крайней мере, вычислить max_length, основанную на данных параметрах. max_length это максимальное количество символов, которые может возвратить функция. Эта функция должна также установить maybe_null = 0, если основная функция не может возвратить значение NULL. Функция может проверить, может ли какой-либо из функциональных параметров возвратить NULL, проверяя переменную maybe_null параметров. Смотрите Item_func_mod::fix_length_and_dec для типичного примера того, как сделать это.

Все функции должны быть безопасными для потока. Другими словами, не используйте глобальные или статические переменные в функциях, не защищая их с mutexes.

Если Вы хотите возвратить NULL из ::val(), ::val_int() или ::str(), Вы должны установить null_value в 1 и вернуть 0.

Для функций ::str() есть дополнительные соображения:

  • Аргумент String *str обеспечивает строковый буфер, который может использоваться, чтобы сохранить результат. Для получения дополнительной информации о типе String, смотрите файл sql_string.h.

  • Функция ::str() должна возвратить строку, которая содержит результат, или (char*)0, если результат NULL.
  • Все текущие строковые функции пытаются избегать выделять память, если это не абсолютно необходимо!

26.5. Отладка и портирование MySQL

Этот раздел помогает Вам портировать MySQL к другим операционным системам. Проверьте список в настоящее время поддерживаемых операционных систем сначала. См. http://www.mysql.com/support/supportedplatforms/database.html . Если Вы создали новый порт MySQL, пожалуйста, сообщите нам, чтобы мы могли перечислить его здесь и на нашем Веб-сайте (http://www.mysql.com/), рекомендуя это другим пользователям.

Если Вы создаете новый порт MySQL, Вы свободны скопировать и распространить его в соответствии с лицензией GPL, но он не делает Вас правообладателем MySQL.

Рабочая библиотека потоков POSIX необходима для сервера.

Чтобы создать MySQL из исходных текстов, Ваша система должна удовлетворять ряду требований, перечисленных в requirements listed at разделе 2.8 .

Если Вы сталкиваетесь с проблемами с новым портом, Вам, вероятно, придется сделать некоторую отладку MySQL! См. раздел 26.5.1.

Прежде, чем Вы начнете отлаживать mysqld, сначала получите тестовую программу mysys/thr_lock. Это гарантирует, что у Вашей установки библиотеки потоков есть хотя бы отдаленный шанс работать!

26.5.1. Отладка сервера MySQL

Если Вы используете некоторую функциональность, которая очень нова в MySQL, Вы можете попытаться выполнить mysqld с --skip-new (это отключает всю новую, потенциально опасную функциональность). См. раздел B.5.3.3.

Если mysqld не хочет запускаться, Вы должны проверить, что у Вас нет никаких файлов my.cnf, которые вмешиваются в Вашу установку! Вы можете проверить Ваши параметры my.cnf с mysqld --print-defaults и избежать их использования, запускаясь с помощью mysqld --no-defaults ....

Если mysqld начинает съедать центральный процессор или память, Вы можете использовать mysqladmin processlist status, чтобы узнать, выполняет ли кто-то запрос, который занимает много времени. Может быть хорошей идеей выполнить mysqladmin -i10 processlist status в некотором окне, если Вы испытываете исполнительные проблемы или проблемы, когда новые клиенты не могут соединиться с сервером.

Команда mysqladmin debug выводит некоторую информацию о блокировках, использовании памяти и запросах к файлу системного журнала MySQL. Это может помочь решить некоторые проблемы. Эта команда также обеспечивает немного полезной информации, даже если Вы не собрали MySQL для того, чтобы отладить!

Если проблема состоит в том, что некоторые таблицы становятся медленнее и медленнее, Вы должны попытаться оптимизировать таблицу с помощью OPTIMIZE TABLE или myisamchk. См. главу 6. Вы должны также проверить медленные запросы с EXPLAIN.

Вы должны также прочитать определенный для OS раздел в этом руководстве для проблем, которые могут быть уникальными для Вашей среды. См. раздел 2.1.

26.5.1.1. Компиляция MySQL для отладки

Если у Вас есть некоторая очень определенная проблема, Вы можете всегда попытаться отладить MySQL. Чтобы сделать это, Вы должны сконфигурировать MySQL с опцией -DWITH_DEBUG=1. Вы можете проверить, был ли MySQL собран с отладкой, делая mysqld --help . Если флаг --debug перечислен с опциями, у Вас есть включенная отладка. mysqladmin ver также пишет версию mysqld как mysql ... --debug в этом случае.

Если mysqld падает, когда Вы конфигурируете его с опцией CMake -DWITH_DEBUG=1 , Вы, вероятно, нашли ошибку компилятора или ошибку синхронизации в пределах MySQL. В этом случае, Вы можете попытаться добавить -g, используя опции CMake CMAKE_C_FLAGS и CMAKE_CXX_FLAGS и не использовать -DWITH_DEBUG=1. Если mysqld падает, Вы можете, по крайней мере, присоединиться к нему с gdb или применить gdb на файле дампа, чтобы узнать, что произошло.

Когда Вы конфигурируете MySQL для того, чтобы отладить, автоматически включается большое количество дополнительных функций проверки безопасности, которые контролируют здоровье mysqld. Если они находят что-то неожиданное, будет выдана запись в stderr, который mysqld_safe направляет к журналу ошибок! Это также означает, что, если Вы имеете некоторые неожиданные проблемы с MySQL и используете исходные тексты, первая вещь, которую Вы должны сделать, это сконфигурировать MySQL для того, чтобы отладить! Вторая вещь состоит в том, чтобы послать сообщение в список рассылки MySQL и попросить помощи. См. раздел 1.6.1. Если Вы полагаете, что нашли ошибку, пожалуйста, используйте инструкции в разделе 1.7.

В Windows MySQL mysqld.exe по умолчанию собран с поддержкой файлов трассировки.

26.5.1.2. Создание файлов трассировки

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

Чтобы сделать это, у Вас должен быть mysqld, который был собран с поддержкой отладки. Вы можете проверить это, выполняя mysqld -V. Если номер версии заканчивается -debug, это собрано с поддержкой файлов трассировки. В Windows сервер отладки называют mysqld-debug вместо mysqld .

Запустите mysqld с протоколом в /tmp/mysqld.trace под Unix или \mysqld.trace под Windows:

shell> mysqld --debug
В Windows Вы должны также использовать флаг --standalone, чтобы не запустить mysqld как сервис. В консоли используйте эту команду:
C:\> mysqld-debug --debug --standalone
После этого Вы можете использовать инструмент командной строки mysql.exe во второй консоли, чтобы воспроизвести проблему. Вы можете остановить сервер mysqld с помощью mysqladmin shutdown.

Файл трассировки может стать очень большим! Чтобы произвести меньший файл трассировки, Вы можете использовать опции отладки что-то вроде этого:

mysqld --debug=d,info,error,query,general,where:O,/tmp/mysqld.trace

Это выведет только что-нибудь интересное в трассировку.

Если Вы делаете отчет об ошибках, пожалуйста, пошлите в соответствующий список рассылки только те строки файла трассировки, где что-то, кажется, идет не так, как надо! Если Вы не можете определить местонахождение неправильного места, Вы можете открыть отчет об ошибках и загрузить файл трассировки в отчет так, чтобы разработчик MySQL мог смотреть на это. Для инструкций, см. раздел 1.7.

Файл трассировки сделан с пакетом DBUG. См. раздел 26.5.3.

26.5.1.3. Применение WER с PDB, чтобы создать Windows crashdump

Файлы базы данных программы (расширение pdb) включены в ZIP Archive Debug Binaries & Test Suite дистрибутив MySQL. Эти файлы предоставляют информацию для того, чтобы отладить Вашу установку MySQL в случае проблемы. Это отдельная загрузка из стандартного MSI или файла Zip.

Файлы PDB доступны в отдельном файле, маркированном "ZIP Archive Debug Binaries & Test Suite".

Файл PDB содержит более подробную информацию о mysqld и другие инструменты, который позволяют более подробно отлаживать пакет. Вы можете использовать их с WinDbg или Visual Studio, чтобы отладить mysqld .

Старый отладчик Dr. Watson был удален в Microsoft Vista и заменен на WinDbg.

Для получения дополнительной информации о файлах PDB см. Microsoft Knowledge Base Article 121366. Для получения дополнительной информации о доступных опциях отладки см. Debugging Tools for Windows.

Чтобы использовать WinDbg, или установите полный Windows Driver Kit (WDK) или установите автономную версию.

Файлы .exe и .pbd должны точно совпадать (номер версии и выпуск сервера MySQL) или WinDBG будет жаловаться, пытаясь загрузить символы.

  1. Чтобы произвести минидамп mysqld.dmp, включите опцию core-file раздела [mysqld] в my.ini. Перезапустите сервер MySQL после произведения этих изменений.

  2. Создайте каталог, чтобы хранить произведенные файлы, например, c:\symbols
  3. Определите путь к своему windbg.exe используя Find GUI или из командной строки, например: dir /s /b windbg.exe. Общее значение по умолчанию C:\Program Files\Debugging Tools for Windows (x64)\windbg.exe .
  4. Запустите windbg.exe и укажите ему пути к mysqld.exe, mysqld.pdb, mysqld.dmp и исходным текстам. Альтернативно, передайте каждый путь из WinDbg GUI. Например:
    windbg.exe -i "C:\mysql-8.0.1-winx64\bin\"^
        -z "C:\mysql-8.0.1-winx64\data\mysqld.dmp"^
        -srcpath "E:\ade\mysql_archives\8.0\8.0.1\mysql-8.0.1"^
        -y "C:\mysql-8.0.1-winx64\bin;SRV*c:\symbols*http://msdl.microsoft.com/download/symbols"^
        -v -n -c "!analyze -vvvvv"
    

    Символы ^ и новой строки удалены процессором командной строки Windows, так убедитесь, что пробелы остаются неповрежденными.

26.5.1.4. Отладка mysqld под gdb

На большинстве систем Вы можете также запустить mysqld из gdb, чтобы получить больше информации, если mysqld падает.

С некоторыми более старыми версиями gdb в Linux Вы должны использовать run --one-thread, если Вы хотите отладить потоки mysqld . В этом случае у Вас может быть только один активный поток за один раз. Лучше обновить до gdb 5.1, поскольку отладка потоков работает намного лучше с этой версией!

Потоки NPTL (new thread library on Linux) могут вызвать проблемы, работая с mysqld под gdb. Симптомы этого:

  • mysqld зависает во время запуска (прежде, чем напишет ready for connections).

  • mysqld отказывает во время вызова pthread_mutex_lock() или pthread_mutex_unlock().

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

LD_ASSUME_KERNEL=2.4.1
export LD_ASSUME_KERNEL
Запуская mysqld под gdb, Вы должны отключить трассировку стека с --skip-stack-trace, чтобы быть в состоянии поймать segfaults в пределах gdb.

Используйте опцию --gdb mysqld , чтобы установить обработчик прерывания для SIGINT (должен был остановить mysqld с ^C для установки контрольных точек) и отключить рассмотрение стека и основную обработку файла.

Очень трудно отладить MySQL под gdb, если Вы делаете много новых соединений, в то время как gdb не освобождает память для старых потоков. Вы можете избежать этой проблемы, запуская mysqld с thread_cache_size установленной к значению, равному max_connections +1. В большинстве случаев только использование --thread_cache_size=5' помогает!

Если Вы хотите получить дамп памяти в Linux, если mysqld падает с сигналом SIGSEGV, Вы можете запустить mysqld с опцией --core-file . Этот файл может использоваться, чтобы сделать трассировку, которая может помочь Вам узнать, почему падает mysqld:

shell> gdb mysqld core
gdb>   backtrace full
gdb>   quit
См. раздел B.5.3.3.

Если Вы используете gdb 4.17.x или выше на Linux, Вы должны установить файл .gdb со следующей информацией в Вашем текущем каталоге:

set print sevenbit off
handle SIGUSR1 nostop noprint
handle SIGUSR2 nostop noprint
handle SIGWAITING nostop noprint
handle SIGLWP nostop noprint
handle SIGPIPE nostop
handle SIGALRM nostop
handle SIGHUP nostop
handle SIGTERM nostop noprint
Если у Вас есть проблемы, отладки потоков с gdb, Вы должны загрузить gdb 5.x и попробовать его. Новая версия gdb очень улучшила обработку потока!

Вот пример, как отладить mysqld:

shell> gdb /usr/local/libexec/mysqld
gdb> run
...
backtrace full # Do this when mysqld crashes
Включайте предыдущий вывод в отчет об ошибках, который Вы можете зарегистрировать, используя инструкции в разделе 1.7.

Если mysqld зависает, Вы можете попытаться использовать некоторые системные инструменты, например, strace или /usr/proc/bin/pstack, чтобы исследовать, где проблема у mysqld.

strace /tmp/log libexec/mysqld

Если Вы используете интерфейс Perl DBI, Вы можете включить информацию об отладке при использовании метода trace или устанавливая переменную окружения DBI_TRACE.

26.5.1.5. Использование трассировки стека

На некоторых операционных системах журнал ошибок содержит трассировку стека, если mysqld неожиданно вылетает. Вы можете использовать это, чтобы узнать, где (и, возможно, почему) это происходит. См. раздел 6.4.2. Чтобы получить трассировку стека, Вы не должны собирать mysqld с опцией -fomit-frame-pointer для gcc. См. раздел 26.5.1.1.

Трассировка стека в журнале ошибок выглядит примерно так:

mysqld got signal 11;
Attempting backtrace. You can use the following information
to find out where mysqld died. If you see no messages after
this, something went terribly wrong...

stack_bottom = 0x41fd0110 thread_stack 0x40000
mysqld(my_print_stacktrace+0x32)[0x9da402]
mysqld(handle_segfault+0x28a)[0x6648e9]
/lib/libpthread.so.0[0x7f1a5af000f0]
/lib/libc.so.6(strcmp+0x2)[0x7f1a5a10f0f2]
mysqld(_Z21check_change_passwordP3THDPKcS2_Pcj+0x7c)[0x7412cb]
mysqld(_ZN16set_var_password5checkEP3THD+0xd0)[0x688354]
mysqld(_Z17sql_set_variablesP3THDP4ListI12set_var_baseE+0x68)[0x688494]
mysqld(_Z21mysql_execute_commandP3THD+0x41a0)[0x67a170]
mysqld(_Z11mysql_parseP3THDPKcjPS2_+0x282)[0x67f0ad]
mysqld(_Z16dispatch_command19enum_server_commandP3THDPcj+0xbb7[0x67fdf8]
mysqld(_Z10do_commandP3THD+0x24d)[0x6811b6]
mysqld(handle_one_connection+0x11c)[0x66e05e]
Если разрешение имен функций для трассировки терпит неудачу, трассировка содержит меньше информации:
mysqld got signal 11;
Attempting backtrace. You can use the following information
to find out where mysqld died. If you see no messages after
this, something went terribly wrong...

stack_bottom = 0x41fd0110 thread_stack 0x40000
[0x9da402]
[0x6648e9]
[0x7f1a5af000f0]
[0x7f1a5a10f0f2]
[0x7412cb]
[0x688354]
[0x688494]
[0x67a170]
[0x67f0ad]
[0x67fdf8]
[0x6811b6]
[0x66e05e]
В последнем случае Вы можете использовать утилиту resolve_stack_dump , чтобы определить, где mysqld упал при использовании следующей процедуры:

  1. Скопируйте числа из трассировки стека в файл, например, mysqld.stack. Числа не должны включать окружающие квадратные скобки:

    0x9da402
    0x6648e9
    0x7f1a5af000f0
    0x7f1a5a10f0f2
    0x7412cb
    0x688354
    0x688494
    0x67a170
    0x67f0ad
    0x67fdf8
    0x6811b6
    0x66e05e
    
  2. Сделайте файл символа для сервера mysqld:

    shell> nm -n libexec/mysqld > /tmp/mysqld.sym
    
    Если mysqld не скомпонован статически, используйте следующую команду вместо этого:
    shell> nm -D -n libexec/mysqld > /tmp/mysqld.sym
    
    Если Вы хотите расшифровать C++ символы, используйте --demangle, при наличии для nm. Если у Вашей версии nm нет этой опции, то Вы должны будете использовать команду c++filt после того, как дамп стека был произведен для имен C++.
  3. Выполните следующую команду:
    shell> resolve_stack_dump -s /tmp/mysqld.sym -n mysqld.stack
    
    Если Вы не смогли включить имена C++ в Ваш файл символа, обработайте вывод resolve_stack_dump c++filt:
    shell> resolve_stack_dump -s /tmp/mysqld.sym -n mysqld.stack | c++filt
    
    Это распечатывает, где mysqld падает. Если это не помогает Вам узнать, почему так, Вы должны создать отчет об ошибках и включить вывод предыдущей команды.

    Однако, в большинстве случаев это не помогает нам иметь только трассировку стека, чтобы найти причину проблемы. Чтобы быть в состоянии определить местонахождение ошибки или обеспечить обходное решение, в большинстве случаев мы должны знать запрос, который уничтожил mysqld и предпочтительно прецедент так, чтобы мы могли повторить проблему! См. раздел 1.7.

Более новые версии функции трассировки в glibc также печатают адрес относительно объекта. На glibc-системах (Linux), трассировка для катастрофического отказа в пределах плагина выглядит так:

plugin/auth/auth_test_plugin.so(+0x9a6)[0x7ff4d11c29a6]
Преобразовать относительный адрес (+0x9a6) в имя файла и номер строки можно командой:
shell> addr2line -fie auth_test_plugin.so 0x9a6
auth_test_plugin
mysql-trunk/plugin/auth/test_plugin.c:65
Утилита addr2line часть пакета binutils в Linux.

В Solaris процедура подобна. Solaris printstack() уже печатает правильные адреса:

plugin/auth/auth_test_plugin.so:0x1510
Чтобы преобразовать, используйте эту команду:
shell> gaddr2line -fie auth_test_plugin.so 0x1510
mysql-trunk/plugin/auth/test_plugin.c:88
Windows уже печатает адрес, имя функции и строку:
000007FEF07E10A4 auth_test_plugin.dll!auth_test_plugin()[test_plugin.c:72]

26.5.1.6. Использование журнала сервера, чтобы найти причины ошибок в mysqld

Отметьте, что прежде, чем запустить mysqld с включенным общим журналом запросов, Вы должны проверить все свои таблицы с myisamchk. См. главу 6.

Если mysqld падает или виснет, надо запустить mysqld с с включенным общим журналом запросов. Когда mysqld упдает снова, Вы можете исследовать конец файла системного журнала для запроса, который уничтожил mysqld.

Если Вы используете значение по умолчанию, общий файл системного журнала запросов сохранен в каталоге базы данных как host_name.log. В большинстве случаев это последний запрос в файле системного журнала, который уничтожил mysqld, но если возможно Вы должны проверить это, перезапуская mysqld и выполняя найденный запрос из mysql. Если это работает, Вы должны также проверить все сложные запросы, которые не завершались.

Вы можете также попробовать команду EXPLAIN для всех запросов SELECT, которые занимают много времени, чтобы гарантировать, что mysqld использует индексы должным образом. См. раздел 14.8.2.

Вы можете найти запросы, которые занимают много времени, чтобы выполнить, запуская mysqld с включенным журналом медленных запросов. См. раздел 6.4.5.

Если Вы находите текст mysqld restarted в файле журнала ошибок (обычно называемый hostname.err), Вы, вероятно, нашли запрос, который заставляет mysqld падать. Если это происходит, Вы должны проверить все свои таблицы с myisamchk (см. главу 6) и проверить запросы в файлах системного журнала MySQL. Если Вы находите такой запрос, попытайтесь сначала обновиться до новейшей версии MySQL. Если это не помогает, и Вы ничего не можете найти в архиве рассылок mysql, Вы должны сообщить об ошибке в списке рассылки MySQL. Списки рассылки описаны на http://lists.mysql.com/ , там также есть ссылки к архивам списка онлайн.

Если Вы запустили mysqld с --myisam-recover-options, MySQL автоматически проверяет и пытается восстановить таблицы MyISAM, если они отмечены как 'не закрыты должным образом' или 'отказ'. Если это происходит, MySQL пишет метку в файл hostname.err 'Warning: Checking table ...', которая сопровождается Warning: Repairing table, если таблица должна быть восстановлена. Если Вы получаете много этих ошибок, без mysqld вылетевшего неожиданно как раз перед этим, то что-то тут неправильно и должно быть исследовано далее. См. раздел 6.1.4 .

Когда сервер обнаруживает табличное повреждение MyISAM, он пишет дополнительную информацию в журнал ошибок, такую как имя и номер строки исходного файла и список потоков, получающих доступ к таблице. Пример: Got an error from thread_id=1, mi_dynrec.c:368. Это полезная информация, чтобы включать в отчеты об ошибках.

Это не хороший знак, если mysqld падает неожиданно, но в этом случае, Вы не должны заняться расследованиями сообщения Checking table.... Вместо этого надо попытаться узнать, почему mysqld стал падать.

26.5.1.7. Создание тестового случая при повреждении таблицы

Следующая процедура относится к таблицам MyISAM. Для информации о шагах для поврежденных таблиц InnoDB см. раздел 1.7.

Если Вы сталкиваетесь с поврежденной таблицей MyISAM или mysqld всегда терпит неудачу после некоторых запросов обновления, Вы можете проверить, восстанавливаема ли проблема, делая следующее:

  1. Остановите демон MySQL: mysqladmin shutdown .

  2. Сделайте резервное копирование таблиц, чтобы принять меры против очень маловероятного случая, что ремонт сделает что-то плохое.
  3. Проверьте все таблицы с myisamchk -s database/*.MYI. Восстановите любые поврежденные таблицы с myisamchk -r database/table.MYI.
  4. Сделайте второе резервное копирование таблиц.
  5. Удалите (или переместите подальше) любые старые файлы системного журнала из каталога данных MySQL, если Вы нуждаетесь в большем количестве места.
  6. Запустите mysqld с включенным двоичным журналом. Если Вы хотите найти запрос, который разрушает mysqld , Вы должны запустить сервер также с общим журналом запросов. См. разделы 6.4.3 и 6.4.4.
  7. Когда Вы получили разрушенную таблицу, остановите сервер mysqld.
  8. Восстановите резервную копию.
  9. Перезапустите mysqld без включенного двоичного журнала.
  10. Повторно выполните запросы из mysqlbinlog binary-log-file | mysql. Двоичной журнал сохранен в каталоге базы данных MySQL с именем hostname-bin.NNNNNN.
  11. Если таблицы повреждены снова, или Вы можете заставить mysqld упасть с вышеупомянутой командой, Вы нашли восстанавливаемую ошибку. FTP таблицы и двоичный журнал в нашу базе данных ошибок, используя инструкции, данные в разделе 1.7. Если Вы клиент поддержки, Вы можете использовать центр поддержки клиентов MySQL ( http://www.mysql.com/support/), чтобы предупредить команду MySQL о проблеме и получить исправление как можно скорее.

26.5.2. Отладка клиента MySQL

Чтобы быть в состоянии отладить клиента MySQL с интегрированным пакетом отладки, Вы должны сконфигурировать MySQL с -DWITH_DEBUG=1 . См. раздел 2.8.4.

Прежде, чем выполнить клиента, Вы должны установить переменную окружения MYSQL_DEBUG:

shell> MYSQL_DEBUG=d:t:O,/tmp/client.trace
shell> export MYSQL_DEBUG
Это заставляет клиентов производить файл трассировки в /tmp/client.trace.

Если у Вас есть проблемы с Вашим собственным кодом клиента, Вы должны попытаться соединиться с сервером и выполнить Ваш запрос, используя клиента, который, как известно, работает. Сделайте это, работая с mysql в режиме отладки (предполагается, что Вы собрали MySQL с отладкой):

shell> mysql --debug=d:t:O,/tmp/client.trace
Это обеспечивает полезную информацию в случае, если Вы отправляете отчет об ошибках по почте. См. раздел 1.7.

Если Ваш клиент отказывает в некотором легально выглядящем коде, Вы должны проверить что Ваш файл mysql.h соответствует библиотеке MySQL. Очень частая ошибка состоит в том, чтобы использовать старый файл mysql.h от старой установки MySQL с новой библиотекой MySQL.

26.5.3. Пакет DBUG

Сервер MySQL и большинство клиентов MySQL собраны с пакетом DBUG, первоначально созданным Fred Fish. Когда Вы сконфигурировали MySQL для того, чтобы отладить, этот пакет позволяет получить файл трассировки того, что делает программа. См. раздел 26.5.1.2.

Этот раздел суммирует значения параметра, которые Вы можете определить в опциях отладки в командной строке для программ MySQL, которые были созданы с поддержкой отладки. Для получения дополнительной информации о программировании с пакетом DBUG, см. руководство DBUG в каталоге dbug исходных текстов MySQL. Лучше использовать недавний дистрибутив, чтобы получить наиболее обновленное руководство DBUG.

Пакет DBUG может использоваться, вызывая программу с опцией --debug[=debug_options] или -# [debug_options]. Если Вы определяете опцию --debug или -# без значения debug_options, большинство программ MySQL использует значение по умолчанию. Значение по умолчанию сервера d:t:i:o,/tmp/mysqld.trace в Unix и d:t:i:O,\mysqld.trace в Windows. Эффект этого значения по умолчанию:

  • d: Включает вывод для всего макроопределения отладки.

  • t: Трассирует вызовы и выходы функций.
  • i: Добавляет PID в строки вывода.
  • o,/tmp/mysqld.trace, O,\mysqld.trace: Устанавливает выходной файл отладки.

Большинство программ клиента использует значение по умолчанию debug_options d:t:o,/tmp/program_name.trace, независимо от платформы.

Вот некоторые строки управления отладкой в качестве примера, как они могли бы быть определены на командной строке оболочки:

--debug=d:t
--debug=d:f,main,subr1:F:L:t,20
--debug=d,input,output,files:n
--debug=d:t:i:O,\\mysqld.trace
Для mysqld также возможно изменить настройки DBUG во время выполнения, устанавливая системную переменную debug. Эта переменная имеет значения глобальное и сеанса:
mysql> SET GLOBAL debug = 'debug_options';
mysql> SET SESSION debug = 'debug_options';
Изменения во время выполнения требуют привилегии SUPER, даже для значения сеанса.

Значение debug_options последовательность отделенных двоеточиями полей:

field_1:field_2:...:field_N
Каждое поле в пределах значения состоит из принудительного символа флага, которому произвольно предшествует символ + или -, произвольно сопровождаемый разграниченным запятыми списком модификаторов:
[+|-]flag[,modifier,modifier,...,modifier]
Следующая таблица описывает разрешенные символы флага. Непризнанные символы флага будут тихо проигнорированы.

Флаг

Описание

d

Включите вывод из макроса DBUG_XXX для текущего состояния. Может сопровождаться списком ключевых слов, который включает вывод только для макроопределения DBUG с тем ключевым словом. Пустой список ключевых слов включает вывод для всего макроопределения.

В MySQL общий отладочный макрос включает слова enter, exit, error, warning, info и loop.

D

Задержка после каждого вывода строку. Параметр задержка в десятых долях секунды, согласно точности машины. Например, D,20 определяет задержку в две секунды.

f

Ограничить отладку, трассировку и профилирование списком названных функций. Пустой список включает все функции. Соответствующие флаги d или t должны все еще быть даны: этот флаг только ограничивает их действие.

F

Идентифицирует название исходного файла для каждой строки отладки или вывода трассировки.

i

Идентифицирует процесс по PID или ID потока для каждой строки отладки или вывода трассировки.

L

Идентифицирует номер строки исходного файла для каждой строки отладки или вывода трассировки.

n

Напечатает текущую функциональную глубину вложения для каждой строки отладки или вывода трассировки.

N

Номер каждой строки отладки.

o

Перенаправить выходной поток отладчика к указанному файлу. Вывод значения по умолчанию stderr.

O

Подобно o, но файл действительно сохраняется после каждой записи. При необходимости, файл закрыт и вновь открыт.

p

Ограничивает действия отладчика указанными процессами. Процесс должен быть идентифицирован с макросом DBUG_PROCESS и соответствовать одному в списке для действий отладчика.

P

Напечатает текущее название процесса для каждой строки отладки или вывода трассировки.

r

Продвигая новое состояние, не наследует функциональный уровень вложенности предыдущего состояния. Полезно, когда вывод должен начаться в левом поле.

S

Выполянть функцию _sanity(_file_, _line_) для каждой отлаживаемой функции, пока _sanity() не вернет что-то, что отличается от 0.

t

Включить строки трассировки вызова и завершения функций. Может сопровождаться списком (содержащим только один модификатор), задающим максимальный уровень трассировки, вне которого никакой вывод не происходит для отлаживающего или прослеживающего макроопределения. Значение по умолчанию опция времени компиляции.

Предваряющий символ + или - и сопровождающий список модификаторов используются для таких символов флага, как d или f, которые могут включить отладку для всех применимых модификаторов или только некоторые из них:

  • Без символа + или - значение флага установлено точно в список модификаторов как дано.

  • С символом + или - модификаторы в списке добавлены к (или вычтены из) текущего списка модификаторов.

Следующие примеры показывают, как это работает на флаге d. Пустой список d включает вывод для всего макроопределения отладки. Непустой список включает вывод только для ключевых слов в списке.

Эти запросы устанавливают значение d в список модификаторов как дано:

mysql> SET debug = 'd';
mysql> SELECT @@debug;
+---------+
| @@debug |
+---------+
| d       |
+---------+
mysql> SET debug = 'd,error,warning';
mysql> SELECT @@debug;
+-----------------+
| @@debug         |
+-----------------+
| d,error,warning |
+-----------------+
Лидирующие + или - добавляют к (или вычитают из) текущее значение d:
mysql> SET debug = '+d,loop';
mysql> SELECT @@debug;
+----------------------+
| @@debug              |
+----------------------+
| d,error,warning,loop |
+----------------------+
mysql> SET debug = '-d,error,loop';
mysql> SELECT @@debug;
+-----------+
| @@debug   |
+-----------+
| d,warning |
+-----------+
Добавление к все макросы разрешены результат не меняет:
mysql> SET debug = 'd';
mysql> SELECT @@debug;
+---------+
| @@debug |
+---------+
| d       |
+---------+
mysql> SET debug = '+d,loop';
mysql> SELECT @@debug;
+---------+
| @@debug |
+---------+
| d       |
+---------+
Выключение всех включенных макросов отключает флаг d полностью:
mysql> SET debug = 'd,error,loop';
mysql> SELECT @@debug;
+--------------+
| @@debug      |
+--------------+
| d,error,loop |
+--------------+
mysql> SET debug = '-d,error,loop';
mysql> SELECT @@debug;
+---------+
| @@debug |
+---------+
|         |
+---------+

Поиск

 

Найди своих коллег!

Вы можете направить письмо администратору этой странички, Алексею Паутову. mailto:alexey.v.pautov@mail.ru