Chinaunix首页 | 论坛 | 博客
  • 博客访问: 92517046
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类: Mysql/postgreSQL

2008-05-12 15:18:07

27.1. MySQL内部控件

    本章包含许多在你处理MySQL代码时需要了解的你事情。如果你想投入到MySQL的开发中,或想要接触到最新的中间版本的代码,或者就是想了解开发的进度,请参阅2.8.3节,“从开发源代码树安装”的说明。如果你对MySQL的内部插件感兴趣,你也可以订阅我们的内部插件邮件列表。这个列表的流量相对低一些。欲知如何订阅的详情,请参阅1.7.1.1节,“MySQL邮件列表”。在MySQL AB 的所有开发人员都在内部插件列表里, 此外,我们帮助那些正在处理MySQL代码的人。请随意使用这个邮件列表来问代码有关的问题,也可用它来发送你想奉献给MySQL项目的 补丁!

27.1.1. MySQL线程

    MySQL服务器创建如下线程:

  • TCP/IP 连接线程处理所有连接请求,并为每一个连接创建一个新的专用线程来处理认证和SQL查询处理。

  • Windows NT 平台上有一个名为管道处理程序(pipe handler)的线程,它和名为管道连接请求(pipe connect requests)的TCP/IP连接线程做同样的工作。

  • 信号线程处理所有的信号,这个线程通常也处理报警和调用process_alarm() 函数来强制使得空闲时间太长的连接超时。

  • mysqld是与DUSE_ALARM_THREAD线程一起编译的,这个专用线程是处理 创建的警报的。这个线程用在一些sigwait()函数有问题的系统上,或者用在你想在应用程序中使用thr_alarm()代码而不带专用信号处理线程之时。

  • 若想使用flush_time=val选项,会创建一个专用线程以给定的时间间隔刷新所有表格。

  • 每个连接都有它自己的线程。

  • 每个被使用INSERT DELAYED 的不同表格都会有自己的线程。

  • 若使用了master-host, 则会创建一个从属的复制线程从主线程读取并实施更新。

mysqladmin processlist 仅显示连接,INSERT DELAYED, 及复制线程

27.1.2. MySQL测试套件

     包含在Unix源码和二进制分发版中的测试系统可以让用户和开发人员对MySQL代码施行回归测试。这些测试可以在Unix上进行,目前它们还不能在原生的Windows环境下进行。

      当前的测试案例套件不能在MySQL中测试所有东西,但是它能发现SQL处理代码,OS/library文件中大多数明显的缺陷,并且在测试复件方面也是非常彻底的。我们的终极目标是对100%的代码进行测试。我们欢迎大家给我们的测试套件添加内容。你可能会特别想贡献出那些检查你系统里功能性危机的测试,因为这将确保未来所有发行版的MySQL会与你的应用程序一起更好地运行。

27.1.2.1. 运行MySQL测试套件

    测试系统包括一个测试语言解释器(mysqltest),一个运行所有测试的外壳脚本(mysql-test-run),用专用语言编写的测试案例,以及它们的预期结果。在系统上编译好之后,在源代码的root下键入make test 或mysql-test/mysql-test-run。如果安装了一个二进制分发版, cd 到安装root (如 /usr/local/mysql), 然后键入 scripts/mysql-test-run。所有测试应该都通过,假使有没通过的,若是一个MySQL里的缺陷,你可以试着找找是因为什么,并且报告这个问题。请参阅27.1.2.3节,“在MySQL测试套件里报告缺陷”

如果你想要运行测试套件的机器上已经运行了一个 mysqld ,只要它不占用9306 和 9307端口,就不用停掉它。如果占用了其中的一个,以可以编辑mysql-test-run把主端口和(或)从端口号改为其它可用的。.

可使用下面指令运行单个测试案例 mysql-test/mysql-test-run test_name.

若一个测试未通过,你可以用--force选项来检查运行着的mysql-test-run看是否是别的测试未通过

27.1.2.2. 扩展MySQL测试套件

你可以用mysqltest 语言编写你自己的测试案例。不幸地是,我们还没有写完相关方面完整地文档。但是,你可以查看我们现有的测试案例,并将它们作为范例。下面几点将有助于你入手:

  • 测试位于 mysql-test/t/*.test

  • 测试案例包括终止声明,测试案例类似于mysql命令行客户端的输入。 默认的声明是一个被发送到MySQL服务器的查询,除非这个声明被识别为内部命令(如 sleep)。

  • 所有产生结果的查询,例如SELECT, SHOW, EXPLAIN等,必须在 @/path/to/result/file之前。那个文件必须包含期望的结果。生成结果文件的一个简单办法是在mysql-test目录运行mysqltest -r < t/test-case-name.test ,然后编辑生成的结果文件,如果需要,可将它们调整到想要的输出端。在那种情况下,要小心避免添加或删除任何不可见的字符,确保只改变文本和(或)删除行。如果插入一行,要确保插入的区域被一个硬标识隔开,且在行尾有一个硬标识。你可能会想要使用od -c来确保你的文本编辑器在编辑 步骤中没有搞乱任何东西。当你发现一个缺陷而不得不编辑mysqltest -r的输出时,我们真希望你不要编辑它。

  • 为和我们的设置一致,你应该把你的结果文件放在mysql-test/r 目录,并取名为test_name.result。如果测试产生不止一个结果,你应该使用诸如 test_name.a.result,test_name.b.result等这样的名字。

  • 如果声明返回一个错误,你可以在声明的前一行使用--error error-number来详细说明它。错误号可能是由“,”分开的可能错误号的列表。

  • 如果你正编写一个重复的测试案例,你应该在测试文件的第一行写:source include/master-slave.inc;。用connection master; 和 connection slave;来切换主案例和从案例。如果你需要对一个替换的连接做点什么,对于主连接,用connection master1;,对于从连接,用connection slave1;。

  • 如果需要在一个循环里做点什么,可以用些这样的内容:

    let $1=1000;
    while ($1)
    {
     # do your queries here
     dec $1;
    }
    
  • 在查询之间休眠,使用sleep命令。此命令支持几分之几秒,所以,例如你想要休眠1.3秒,你可以使用sleep 1.3; 命令

  • 对你的测试案例要运行带附加选项的从案例,以命令行方式把它们放在mysql-test/t/test_name-slave.opt。对于主案例,把它们放在mysql-test/t/test_name-master.opt。

  • 如果对测试套件有问题,和想要献出一个测试案例,发送邮件信息到MySQL 内部插件 邮件列表。请参阅1.7.1.1节,“MySQL邮件列表”。 虽然这个列表不接受附件,你可以把相关文件通过ftp上传到:

27.1.2.3. 在MySQL测试套件中报告缺陷

如果你的MySQL的版本没有通过测试套件,你可以采取如下措施:

  • 在尽可能多地找到出错之时的错误之前,不要发送缺陷报告。查找之时,请使用mysqlbug脚本比便我们能获取你的系统和MySQL版本信息,参阅1.7.1.3节 ,“如何报告缺陷或问题

  • 确保包含了mysql-test-run的输出,以及  mysql-test/r目录下所有.reject文件的内容。

  • 如果测试套件里的测试未通过,用如下命令检查一下看它自己运行时是否通过测试:

    cd mysql-test
    mysql-test-run --local test-name
    

    如果未能通过,你应该用 --with-debug 配置MySQL并使用--debug选项来运行mysql-test-run。如果这样也未能通过,请把追踪文件var/tmp/master.trace 上传到 以便我们能检查它。请记得也要包含你系统的完整描述,mysqld 二进制文件的版本,以及你是如何编译它的。

  • 也试着带--force选项运行一下mysql-test-run ,看是否还有别的测试未通过。

  • 如果你是自己编译的MySQL,查看我们的手册看看如何在你的平台上编译MySQL,最好用一个在http://dev.mysql.com/downloads/上我们已经为你编译好的二进制版本。我们所有标准的二进制版本都能通过测试套件的测试!

  • 如果错误是Result length mismatch 或 Result content mismatch ,这意味测试的输出于期望的输出不匹配,这可能是在MySQL或你的mysqld 版本里的缺陷在某些环境下产生稍有不同的结果。

    未通过的测试结果放在和结果文件同主名但扩展名为.reject的文件里。如果测试案例未通过,你应该对两个文件做diff操作。如果你不能发现它们是如何不同,用od -c 命令检查它们,也检查一下文件长度。

  • 如果测试完全未通过,你应该检查mysql-test/var/log目录下的日志文件以获得有关错误的一些提示。

  • 如果你是为调试而编译MySQL,试一下带--gdb和(或)--debug参数运行mysql-test-run 。请参阅E.1.2节,“创建跟踪文件”

    如果你没有为调试而编译MySQL,这应该是你可能去做的。只要带--with-debug参数运行configure。 请参阅2.8节,“使用源码分发版安装MySQL ”

27.2. 为MySQL添加新函数

有两个途径来为MySQL添加新函数:

  • 你可以通过自行医函数接口 (UDF)来添加函数。自定义函数被编译为目标文件,然后用CREATE FUNCTION 和DROP FUNCTION 声明动态地添入到服务器中及从服务器中移出。参阅27.2.2节,“CREATE FUNCTION/DROP FUNCTION 语法”

  • 你可以将函数添加为MySQL固有(内建)函数。固有函数被编译进mysqld服务器中,成为永久可用的。

每种途径都有其优点和缺点:

  • 如果你编写自定义函数,你除了安装服务器本身之外还要安装目标文件。如果将你的函数编译进服务器中,你就不需要这么做了。

  • 你可以给二进制版本的MySQL分发版添加UDF。固有函数需要你去修正源码分发版。.

  • 如果你升级你的MySQL分发版,你可以继续使用先前安装了的UDF, 除非你升级到一个UDF接口改变了的新版本。对固有函数而言,每次升级你都必须重复一次修正。

无论你使用哪种方法去添加新函数,它们都可以被SQL声明调用,就像 ABS() 或 SOUNDEX()这样的固有函数一样。

另一个添加函数的方法时创建存储函数。这些函数时用SQL声明编写的,而不是编译目标代码。编写存储函数的语法在第20章:存储程序和函数 中描述。

下面的小节描述UDF接口的特性,给出编写UDF的指令,并讨论MySQL为防止UDF被误用而采取的安全预防措施。

给出源代码的例子来说明如何编写UDF,看一看MySQL源码分发版中提供的sql/udf_example.cc 文件。

27.2.1. 自定义函数接口的特性

MySQL自定义函数接口有如下特性和功能:

  • 函数能分÷返回字符串,整数或实数。

  • 你可以定义一次作用于一行的简单函数,或作用于多行的组的集合函数。

  • 提供给函数的信息使得函数可以检查传递给它们的参量的数目和类型。

  • 你可以让MySQL在将某参量传递给函数之前强制其为某一类型。

  • 你可以表示函数返回NULL 或发生错误。

27.2.2. CREATE FUNCTION/DROP FUNCTION 语法

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

DROP FUNCTION function_name

一个自定义函数 (UDF)就是用一个象ABS() 或 CONCAT()这样的固有(内建)函数一样作用的新函数去扩展MySQL。

function_name 是 用在SQL声明中以备调用的函数名字。RETURNS 子句说明函数返回值的类型。 shared_library_name 是共享目标文件的基本名,共享目标文件含有实现函数的代码。该文件必须位于一个能被你系统的动态连接者搜索的目录里。

你必须有mysql 数据库的INSERT 权限才能创建一个函数,你必须有mysql 数据库的DELETE权限才能撤销一个函数。这是因为CREATE FUNCTION 往记录函数名字,类型和共享名的mysql.func系统表里添加了一行,而DROP FUNCTION则是从表中删掉这一行。如果你没有这个系统表,你应该运行mysql_fix_privilege_tables脚本来创建一个。请参阅2.10.2节,“升级授权表”

一个有效的函数是一个用CREATE FUNCTION加载且没有用DROP FUNCTION移除的函数。每次服务器启动的时候会重新加载所有有效函数,除非你使用--skip-grant-tables参数启动mysqld。在这种情况下, 将跳过UDF的初始化,UDF不可用。

要了解编写自定义函数的说明,请参阅27.2.3节,“添加新的自定义函数”。要使得UDF机制能够起作用,必须使用C或者C++编写函数,你的系统必须支持动态加载,而且你必须是动态编译的mysqld(非静态)。

一个AGGREGATE函数就像一个MySQL固有的集合(总和)函数一样起作用,比如,SUM或COUNT()函数。要使得AGGREGATE 起作用,你的mysql.func表必须包括一个type列。如果你的mysql.func表没有这一 列,你应该运行mysql_fix_privilege_tables脚本来创建此 列。

27.2.3. 添加新的自定义函数

要使得UDF机制能够起作用,必须使用C或者C++编写函数,你的系统必须支持动态加载。MySQL 源码分发版包括一个sql/udf_example.cc 文件,此文件定义了5个新函数。可以参考这个文件,看UDF是如何调用常规工作。

为了能使用UDF,你需要动态链接mysqld。不要配置MySQL使用--with-mysqld-ldflags=-all-static参数。如果你想使用一个需要从mysqld 访问符号的UDF(例如在使用default_charset_info的sql/udf_example.cc文件中的metaphone函数),你必须使用-rdynamic参数来链接程序(参阅man dlopen)。如果你计划使用UDF, 一个经验法则就是,用with-mysqld-ldflags=-rdynamic设定MySQL,除非你有很好的理由不去这么做。

如果你使用的是预编译分发版的MySQL, 请使用MySQL-Max,其中含有一个动态链接了的服务器,它可以支持动态加载。

对于每个你想要使用在SQL声明中的函数,你应该定义相应的C (或 C++)函数。在下面的讨论中,xxx用来表示范例函数的名字,为了区分使用SQL还是C/C++,xxx()(上标)表示SQL函数调用,xxx()(下标)表示C/C++函数调用。

你为xxx()编写来实现接口的C/C++ 函数如下:

  • xxx() (必有)

    主函数。 这是函数结果被计算的地方。SQL函数数据类型与C/C++函数返回类型的对应关系如下:

    SQL 类型 C/C++ 类型
    STRING char *
    INTEGER long long
    REAL double
  • xxx_init() (可选)

    对xxx()的初始化函数。它可以被用来:

    • 检查传递给xxx()的参量数目。

    • 检查参量是否为必需的类型,或者,除此之外,在主函数被调用的时候告诉MySQL将参量强制为想要的类型。

    • 分配主函数需要的内存。

    • 指定结果的最大长度。

    • 指定(对于REAL 函数)小数的最多位数。

    • 指定结果是否可以为 NULL。

  • xxx_deinit() (可选)

    对xxx()的去初始化函数。它释放初始化函数分配的内存。

当SQL声明调用XXX()时,MySQL调用初始化函数xxx_init(),让它执行必要的设置,比如,检查 参量或分配内存。如果xxx_init() 返回一个错误,SQL声明会退出并给出错误信息,而主函数和去初始化函数并没有被调用。 否则,主函数xxx() 对每一行都被调用一次。所有行都处理完之后,调用去初始化函数xxx_deinit() 执行必要的清除。

对于象SUM()一样工作的集合函数,你也必须提供如下的函数:

  • xxx_clear() (在5.1节中必须)

    对一个新组重置当前集合值为初试集合值,但不插入任何参量。

  • xxx_add() (必须)

    添加参量到当前集合值。

MySQL 按下列操作来处理集合UDF:

  1. 调用 xxx_init() 让集合函数分配它需要用来存储结果的内存。

  2. 按照GROUP BY表达式来排序表。

  3. 为每个新组中的第一行调用xxx_clear()函数。

  4. 为属于同组的每一个新行调用xxx_add()函数。

  5. 当组改变时或每组的最后一行被处理完之后,调用xxx()来获取集合结果。

  6. 重复,以上3-步直到所有行被处理完。

  7. 调用xxx_deinit() 函数去释放UDF分配的内存。.

所有函数必须时线程安全的,这不仅对主函数,对初始化和去初始化函数也一样,也包括集合函数要求的附加函数。这个要求的一个结果就是,你不能分配任何变化的全局或静态变量。如果你需要内存,你可以在xxx_init()函数分配内存,然后在xxx_deinit()函数释放掉。

27.2.3.1. UDF 对简单函数的调用顺序

下面介绍创建简单UDF时需要定义的不同函数。27.2.3节,“添加新的自定义函数”中介绍了MySQL调用这些函数的顺序。

如本节所示,应该说明主函数xxx()。注意返回值和参数会有所不同,这取决于你说明的SQL函数xxx()在CREATE FUNCTION声明中返回的是STRING,INTEGER类型还是REAL类型示:

对于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() 能返回NULL,xxx_init()应maybe_null 为 1 。如果任一参量被说明了 maybe_null值,其 默认值是1 。

  • unsigned int decimals

    小数位数。默认值是传到主函数的参量里小数的最大位数。(例如,如果函数传递 1.34, 1.345, 和1.3, 那么默认值为,因为1.345 有3位小数。

  • unsigned int max_length

    结果的最大长度。max_length 的默认值因函数的结果类型而异。对字符串函数,默认值是最长参量的长度。对整型函数,默认是21位。对实型函数,默认是13再加上initid->decimals指示的小数位数。(对数字函数,长度包含正负号或者小数点符)。

    如果想返回团值,你可以把max_length 设为从65KB到16MB。这个内存不会被分配,但是如果有临时数据需要存储,这个设置了的值被用来决定使用哪种 列的类型。

  • char *ptr

    函数可以用作本身目的的指针。比如,函数可以用initid->ptr 来在分配了的内存内部通讯。 xxx_init() 应该分配内存,并指派给这个指针:

    initid->ptr = allocated_memory;
    

    在 xxx() 和 xxx_deinit()中,借用 initid->ptr 来使用或分配内存。

阅读(690) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~