The OpenNET Project / Index page

[ новости /+++ | форум | теги | ]

Каталог документации / Раздел "Базы данных, SQL" / Оглавление документа

3 Расширение MySQL

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

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

Каждый метод имеет свои проблемы:

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

3.1.1 Синтаксис CREATE FUNCTION/DROP FUNCTION

CREATE [AGGREGATE] FUNCTION function_name RETURNS {STRING|REAL|INTEGER}
       SONAME shared_library_name
DROP FUNCTION function_name

Определяемые пользователем функции (user-definable function, UDF) представляют собой способ расширить MySQL новой функцией, которая работает подобно местным (встроенным) функциям MySQL типа ABS() или CONCAT().

AGGREGATE новая опция для MySQL Version 3.23. Функция с AGGREGATE работает точно так же, как и встроенная функция GROUP, подобно SUM или COUNT().

CREATE FUNCTION сохраняет имя функции, тип и общедоступное библиотечное имя в таблице mysql.func системы. Вы должны иметь привилегии insert и delete для базы данных mysql, чтобы создавать и удалять функции.

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

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

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

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

Чтобы mysqld мог использовать UDF, Вы должны конфигурировать MySQL с опцией --with-mysqld-ldflags=-rdynamic. Причина этого в том, что на многих платформах (включая Linux) Вы можете загружать динамическую библиотеку (вызовом dlopen()) из статически скомпонованной программы, которая собрана с опцией --with-mysqld-ldflags=-all-static, но если Вы хотите использовать UDF, который должен обратиться к символам из mysqld (подобно примеру methaphone в sql/udf_example.cc, который использует default_charset_info), Вы должны компоновать программу с -rdynamic. Подробности на man dlopen.

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

Функции, которые Вы пишете на C/C++ для реализации интерфейса с XXX():

xxx() (обязательна)
Основная функция. Это то место, где функциональный результат вычислен. Соответствие между типом SQL и типом возврата Вашей функции на C/C++ показывается ниже:
SQL-типC/C++-тип
STRINGchar *
INTEGERlong long
REALdouble
xxx_init() (опциональна)
Функция инициализации для xxx(). Это может использоваться для:
xxx_deinit() (опционально)
Функция деинициализации для xxx(). Это должно освободить любую память, распределенную функцией инициализации.

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

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

3.1.2.1 Соглашения по вызову UDF

Основная функция должна быть объявлена как показано ниже. Обратите внимание, что тип возврата и параметры отличаются в зависимости от того, объявите ли Вы тип возврата функции 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);

Функции инициализации и деинициализации объявлены подобно этому:

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 десятичных цифры.
unsigned int max_length
Максимальная длина результата-строки. Значение по умолчанию отличается в зависимости от типа результата функции. Для строчных функций значение по умолчанию равно длине самого длинного параметра. Для целочисленных функций значение по умолчанию соответствует 21 цифре. Для реальных функций значение по умолчанию 13+количество десятичных чисел, обозначенных как initid->decimals. Для числовых функций длина включает любой знак или десятичные символы отметки.
char *ptr
Указатель, который функция может использовать для собственных целей. Например, функции могут использовать initid->ptr, чтобы передать распределенную память между функциями. В xxx_init() как обычно распределите память и назначьте ее этому указателю:
initid->ptr=allocated_memory;
В xxx() и xxx_deinit() обратитесь к initid->ptr, чтобы использовать или освободить память.

3.1.2.2 Обработка параметров

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

unsigned int arg_count
Число параметров. Проверьте это значение в функции инициализации, если Вы хотите, чтобы Ваша функция была вызвана со специфическим числом параметров. Например, таким кодом:
if (args->arg_count != 2)
{
   strcpy(message,"XXX() requires two arguments");
   return 1;
}
enum Item_result *arg_type
Типы для каждого параметра. Возможные значения типов: STRING_RESULT, INT_RESULT и REAL_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;
}
Вы можете использовать функцию инициализации, чтобы установить элементы arg_type к типам, которые Вы хотите получить. Это заставляет MySQL привести параметры к тем типам для каждого обращения к xxx(). Например, чтобы определить первые два элемента как строку и число, сделайте следующее в xxx_init():
args->arg_type[0] = STRING_RESULT;
args->arg_type[1] = INT_RESULT;
char **args
args->args сообщает информацию функции инициализации относительно общего характера параметров, с которыми Ваша функция была вызвана. Для постоянного параметра (константы) i args->args[i] указывает на значение параметра. Для непостоянного параметра args->args[i] равно 0. Постоянный параметр представляет собой выражение, которое использует только константы, типа 3, 4*7-2 или SIN(3.14). Непостоянный параметр представляет собой выражение, которое обращается к значениям, которые могут изменяться, типа имени столбца или функций, которые вызваны с непостоянными параметрами. Для каждого обращения основной функции args->args хранит фактические параметры, которые переданы для в настоящее время обрабатываемой строки. Функции могут обратиться к параметру i следующим образом:
unsigned long *lengths
Для функции инициализации, массив lengths указывает максимальную длину строки для каждого параметра. Для каждого обращения к основной функции lengths хранит фактические длины любых строковых параметров, которые переданы для строки, обрабатываемой в настоящее время. Для параметров типов INT_RESULT или REAL_RESULT lengths хранит максимальную длину параметра (как для функции инициализации).

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

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

Значение возврата основной функции xxx() зависит от типа. Для функций типов long long и double оно представляет собой собственно функциональное значение. Строковые функции должны возвратить указатель на результат и сохранить длину строки в параметрах length. Здесь result представляет собой буфер длиной в 255 байт. Установите их к содержанию и длине значения. Например:

memcpy(result, "result string", 13);
*length=13;

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

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

*is_null=1;

Чтобы указать возврат ошибки в основной функции, установите параметр ошибки (error) в значение 1:

*error=1;

Если xxx() устанавливает *error в 1 для любой строки, функциональное значение NULL для текущей строки и для любых последующих строк, обработанных инструкцией, в которой вызывалась XXX(). Причем, xxx() не будет даже запрашиваться для последующих строк. ПРИМЕЧАНИЕ: В MySQL до версии 3.22.10 Вы должны установить *error и *is_null:

*error=1;
*is_null=1;

3.1.2.4 Компиляция и установка определяемых пользователем функций

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

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

shell> gcc -shared -o udf_example.so myfunc.cc

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

shell> make udf_example.o

Вы должны выполнить команду компиляции, подобную одной из тех, что отображает make, за исключением того, что Вы должны удалить опцию -c близко к концу строки и добавить -o udf_example.so в самый конец строки. На некоторых системах удалять -c не надо, пробуйте.

Как только Вы скомпилируете общедоступный объект, содержащий UDF, Вы должны установить его и сообщить MySQL о расширении функциональности. Компиляция общедоступного объекта из udf_example.cc производит файл с именем udf_example.so (точное имя может изменяться от платформы к платформе). Скопируйте этот файл в некоторый каталог, где ищет файлы ld, например, в /usr/lib. На многих системах Вы можете устанавливать системную переменную LD_LIBRARY или LD_LIBRARY_PATH, чтобы указать каталог, где Вы имеете Ваши файлы функции UDF. Руководство на dlopen сообщает Вам, которую переменную Вы должны использовать на Вашей системе. Вы должны установить это в mysql.server или в safe_mysqld и перезапустить mysqld.

После того, как библиотека установлена, сообщите mysqld относительно новых функций этими командами:

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 lookup RETURNS STRING SONAME "udf_example.so";
mysql> CREATE FUNCTION reverse_lookup RETURNS STRING SONAME
                  "udf_example.so";

Функции могут быть удалены, используя DROP FUNCTION:

mysql> DROP FUNCTION metaphon;
mysql> DROP FUNCTION myfunc_double;
mysql> DROP FUNCTION myfunc_int;
mysql> DROP FUNCTION lookup;
mysql> DROP FUNCTION reverse_lookup;

Инструкции CREATE FUNCTION и DROP FUNCTION модифицируют системную таблицу func в базе данных mysql. Имя функции, тип и общедоступное библиотечное имя будут сохранено в таблице. Вы должны иметь привилегии insert и delete для базы данных mysql, чтобы создавать и удалять свои функции.

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

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

3.1.3 Добавление новых встроенных функций

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

Чтобы добавить новую встроенную функцию MySQL, нужно:

  1. Добавьте одну строку в файл lex.h, которая определяет имя функции в массиве sql_functions[].
  2. Если функциональный прототип прост (берет не более трех параметров), Вы должны в lex.h определить SYM(FUNC_ARG#) (здесь # является числом параметров) как второй параметр в массиве sql_functions[] и добавить функцию, которая создает функциональный объект, в item_create.cc. Смотрите "ABS" и create_funcs_abs() как пример. Если функциональный прототип усложнен (например, берет переменное число параметров), Вы должны добавить две строки к sql_yacc.yy. Каждая указывает символ препроцессора, который yacc должен определить (это должно быть добавлено в начале файла). Затем определите функциональные параметры и добавьте элемент с этими параметрами для правила синтаксического анализа simple_expr. Для примера, проверьте все местонахождения ATAN в sql_yacc.yy, чтобы увидеть, как это выполнено.
  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. Вы должны, вероятно, также определить следующую объектную функцию:
    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 в качестве типичного примера того, как все это сделать.

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

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

Для объектной функции ::str() имеются некоторые дополнительные хитрости, которые надо знать:

3.2 Добавление новых процедур в MySQL

В MySQL Вы можете определять процедуру на C++, которая может обращаться и изменять данные в запросе прежде, чем они отправятся к пользователю. Модификация может быть выполнена на уровне строки или GROUP BY.

Авторы пакета создали процедуру примера в MySQL Version 3.23, чтобы показать Вам, что там может быть выполнено.

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

3.2.1 Анализ процедур

analyse([max elements,[max memory]])

Эта процедура определена в sql/sql_analyse.cc. Она исследует результат, полученный из Вашего запроса, и возвращает анализ результатов:

SELECT ... FROM ... WHERE ... PROCEDURE ANALYSE([max elements,[max memory]])

3.2.2 Написание процедур

На сегодняшний день единственной документацией для этого является исходный код пакета.

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

3.3 Начинка MySQL

Эта глава описывает много вещей, которые Вы должны знать при работе на коде MySQL. Если Вы планируете способствовать MySQL разработке, иметь доступ к коду отлаживаемых версий или хотите только следить за разработкой, следуйте командам в разделе " 2.3.4 Установка из дерева исходников для разработки". Если Вы заинтересованы внутренней организацией MySQL, Вы должны также подписаться на специальный список рассылки internals@lists.mysql.com.

3.3.1 Потоки в MySQL

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

mysqladmin processlist показывает только подключения, потоки репликации и INSERT DELAYED.

3.3.2 Набор тестов MySQL

До недавнего времени основной набор теста был основан на составляющих собственность данных заказчика и по этой причине не был публично доступен. Единственный публично доступная часть процесса тестирования состояла из теста crash-me, эталонного теста Perl DBI/DBD, находящегося в каталоге sql-bench, и разнообразных тестов, размещенных в каталоге tests. Отсутствие стандартизированного публично доступного набора тестов сделало трудным для пользователей и разработчиков тестирование кода MySQL. Чтобы исправить эту ситуацию, авторы пакета создали совершенно новую систему тестов, которая теперь включена в исходные и двоичные дистрибутивы, начиная с Version 3.23.23.

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

3.3.2.1 Запуск набора тестов MySQL

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

Если Вы имеете копию mysqld на машине, где Вы хотите выполнить набор тестов, Вы не должны останавливать ее, если она не использует порты 9306 и 9307. Если один из этих портов применяется, Вы должны отредактировать mysql-test-run и изменить значения главного или подчиненного порта к тому, которое является доступным.

Вы можете запустить индивидуально каждый тест командой mysql-test/mysql-test-run test_name.

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

3.3.2.2 Расширение набора тестов MySQL

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

3.3.2.3 Как сообщать об ошибках в наборе тестов MySQL

Если Ваша версия MySQL не выполняет набор тестов, Вы должны сделать так:




Партнёры:
PostgresPro
Inferno Solutions
Hosting by Hoster.ru
Хостинг:

Закладки на сайте
Проследить за страницей
Created 1996-2024 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру