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

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

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

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

  • Вы можете добавить функцию через механизм определяемых пользователем функций (user-definable function, UDF). Они добавляются динамически, используя команды CREATE FUNCTION и DROP FUNCTION. Подробности в разделе "3.1.1 Синтаксис CREATE FUNCTION/DROP FUNCTION".
  • Вы можете добавить функцию как внутреннюю в MySQL. Такие функции компилируются прямо внутрь сервера mysqld и становятся доступными на постоянной основе.

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

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

Независимо от метода, который Вы используете, чтобы добавить новые функции, они могут использоваться точно так же как местные функции типа 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().
  • Проверки, что параметры имеют требуемый тип или выдачи предписания, чтобы MySQL принудительно привел параметры к типам, которые Вы хотите иметь, когда основная функция вызвана.
  • Распределения любой память, требуемой для основной функции.
  • Определения максимальной длины результата.
  • Указания (для функций типа REAL) максимального количества десятичных чисел.
  • Указания того, может или нет результат быть NULL.
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 следующим образом:
  • Параметр типа 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:
    double real_val;
    real_val = *((double*) 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. Этот файл содержит следующие функции:

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

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

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() имеются некоторые дополнительные хитрости, которые надо знать:

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

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. Она исследует результат, полученный из Вашего запроса, и возвращает анализ результатов:

  • max elements (по умолчанию 256) задает максимальное число разных значений, которые analyse заметит в столбце. Это используется, чтобы проверить оптимальность применения типа ENUM.
  • max memory (по умолчанию 8192) задает максимум памяти, которую analyse должен распределить на столбец при попытке найти все отличные значения.
SELECT ... FROM ... WHERE ... PROCEDURE ANALYSE([max elements,[max memory]])

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

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

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

  • sql/sql_analyse.cc
  • sql/procedure.h
  • sql/procedure.cc
  • sql/sql_select.cc

3.3 Начинка MySQL

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

3.3.1 Потоки в MySQL

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

  • Поток TCP/IP-подключений обрабатывает все подключения, запрашивает и создает новый специализированный поток, чтобы обработать авторизацию и запросы SQL для каждого подключения.
  • В Windows NT имеется драйвер именованного канала, который делает ту же самую работу, что и поток TCP/IP, но с запросами на именованном канале.
  • Поток сигнала обрабатывает все сигналы. Он также обычно обрабатывает тревоги и вызывает process_alarm(), чтобы завершить подключения, которые были неактивны слишком долго.
  • Если mysqld компилируется с -DUSE_ALARM_THREAD, специализированный поток, который обрабатывает тревоги, будет создан. Это используется только на некоторых системах, где имеются проблемы с sigwait(), или если есть недостатки в применении кода thr_alarm() в прикладной программе без специализированного потока обработки сигнала.
  • Если использована опция --flush_time=#, будет создан еще один специализированный поток, который сбрасывает таблицы на диск.
  • Каждое соединение обрабатывается своим потоком.
  • Каждая таблица, на которой использована инструкция INSERT DELAYED, получает собственный поток.
  • Если Вы используете --master-host, будет запущен поток репликации, чтобы читать и применять модификации с главного сервера.

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, чтобы писать Ваши собственные случаи теста. К сожалению, авторы пакета еще не написали полную документацию для него. Вы можете, однако, рассматривать текущие случаи теста и использовать их как пример. Следующие пункты должны помочь Вам:

  • Тесты находятся в каталоге mysql-test/t/*.test
  • Случай теста состоит из завершенной точкой с запятой (;) инструкции и подобен вводу клиента командной строки mysql. Инструкция по умолчанию: запрос, который будет послан серверу MySQL, если он не распознан как внутренняя команда (например, sleep).
  • Все запросы, которые производят результаты, например, SELECT, SHOW, EXPLAIN и прочие, нужно предварить указанием @/path/to/result/file. Файл должен содержать ожидаемые результаты. Простой способ генерировать файл результата состоит в том, чтобы выполнить mysqltest -r < t/test-case-name.test из каталога mysql-test, а затем отредактировать сгенерированные файлы результата, если необходимо скорректировать их к ожидаемому выводу. В этом случае будьте очень осторожны относительно добавления или удаления любых невидимых символов. Если Вы должны вставить строку, удостоверьтесь, что поля отделяются позициями табуляции, и имеется табуляция в конце. Вы можете использовать od -c, чтобы удостовериться, что Ваш текстовый редактор не добавляет что-нибудь неожиданное в течение процесса редактирования.
  • Чтобы все соответствовало установке, Вы должны поместить Ваши файлы результата в каталог mysql-test/r и назвать их как test_name.result. Если тест производит больше, чем один результат, Вы должны использовать test_name.a.result, test_name.b.result и так далее.
  • Если инструкция возвращает ошибку, Вы должны на строке перед ней указать --error error-number. Здесь error-number может быть списком возможных кодов ошибок, отделяемых запятыми (,).
  • Если Вы записываете случай теста репликации, Вы должны в первой строке файла теста помещать source include/master-slave.inc;. Чтобы переключаться между главной и подчиненной системами, используйте connection master; и connection slave;. Если Вы должны делать что-то на альтернативном подключении, Вы можете сделать подключение connection master1; для главной и connection slave1; для подчиненной системы.
  • Если Вы должны делать что-то в цикле, Вы можете использовать:
    let $1=1000;
    while ($1)
    {
      # Выполняем здесь запрос.
      dec $1;
    }
    
  • Чтобы бездействовать между запросами, используйте команду sleep. Она поддерживает доли секунды, так что Вы можете указать sleep 1.5;, например, чтобы бездействовать 1.5 секунды.
  • Чтобы выполнять подчиненного с дополнительными параметрами для Вашего случая теста, поместите их в формате командной строки в mysql-test/t/test_name-slave.opt. Для главной системы поместите их в файл mysql-test/t/test_name-master.opt.
  • Если Вы имеете вопрос относительно набора теста или случай теста, который может пригодиться всем, напишите об этом на internals@lists.mysql.com. Поскольку список не принимает вложения, Вы должны закачать по ftp все релевантные файлы на ftp://support.mysql.com/pub/mysql/Incoming.

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

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

  • Не торопитесь посылать отчет об ошибке! Сначала разберитесь толком, что там у Вас происходит и почему. Если отчет все-таки придется послать, пожалуйста, используйте для его генерации скрипт mysqlbug, чтобы разработчики могли получить информацию относительно Вашей системы и версии MySQL.
  • Удостоверьтесь, что включили вывод mysql-test-run и содержание всех .reject файлов в каталоге mysql-test/r.
  • Если тест валится в наборе, проверьте, что с ним будет происходить при непосредственном запуске командой:
    cd mysql-test
    mysql-test-run --local test-name
    
    Если это терпит неудачу, то сконфигурируйте MySQL с опцией --with-debug и выполните mysql-test-run с опцией --debug. Если это также терпит неудачу, закачайте файл трассировки var/tmp/master.trace на ftp://support.mysql.com/pub/mysql/secret, чтобы авторы могли исследовать это. Пожалуйста, не забудьте также включить полное описание Вашей системы, версию mysqld и параметры компиляции.
  • Попробуйте также выполнить mysql-test-run с опцией --force, чтобы увидеть, имеется ли любой другой тест, который тоже терпит неудачу.
  • Если Вы компилировали MySQL самостоятельно, изучите руководство на предмет того, как компилировать MySQL на Вашей платформе или, что предпочтительно, используйте один из готовых двоичных дистрибутивов, который уже откомпилирован и может быть скачан с http://www.mysql.com/downloads. Все стандартные двоичные файлы должны проходить тестирование.
  • Если Вы получаете ошибку, подобно Result length mismatch или Result content mismatch, это означает, что вывод теста не соответствовал точно ожидаемому выводу. Это может быть ошибкой в MySQL, или дело в том, что Ваша версия mysqld производит малость иные результаты при некоторых обстоятельствах. Неудачные результаты теста будут помещены в файл с тем же самым основным именем, что и файл результата, но с расширением .reject. Если Ваш случай теста терпит неудачу, Вы должны сравнить два файла. Если Вы не можете увидеть, чем они отличаются, исследуйте их с помощью od -c и проверьте их длины.
  • Если тест терпит неудачу полностью, Вы должны проверить журналы в каталоге mysql-test/var/log для выяснения того, что не так.
  • Если Вы компилировали MySQL с отладкой, можно попробовать отлаживать тест запуском mysql-test-run с опциями --gdb и/или --debug. Подробности в разделе "6.1.2 Создание файлов трассировки ". Если Вы не компилировали MySQL для отладки, вероятно, стоит сделать это. Только определите параметр --with-debug для вызова configure! Подробности в разделе "2.3 Установка исходников MySQL".

Поиск

 

Найди своих коллег!