从当前版本开始,可以在编译时,添加mysql模块,从而可以进行相关的mysql数据库操作,只要在make命令后面加入USE_MYSQL=yes即可,在加入mysql模块前,请确保你的系统中包含了mysql_config程式和mysql.h开发头文件(有的系统中存在mysql_config,但是没有mysql.h,也无法编译通过)...

    页面导航: 项目下载地址:

    zenglServer源代码的相关地址:https://github.com/zenglong/zenglServer  当前版本对应的tag标签为:v0.3.0

zenglServer v0.3.0:

    v0.3.0及之后的版本的相关文章,将发布到zenglServer栏目下,之前版本的文章,请参考zengl编程语言栏目。

    从当前版本开始,可以在编译时,添加mysql模块,从而可以进行相关的mysql数据库操作,只要在make命令后面加入USE_MYSQL=yes即可:

[email protected]:~/zenglServer$ make USE_MYSQL=yes
cd zengl/linux && make libzengl.a
make[1]: Entering directory '/home/zengl/zenglServer/zengl/linux'
gcc -D ZL_LANG_EN_WITH_CH -g3 -ggdb -O0 -std=c99 -fvisibility=hidden -fPIC -c zengl_main.c zengl_parser.c zengl_symbol.c zengl_locals.c zengl_assemble.c zengl_ld.c zenglrun_main.c zenglrun_func.c zenglrun_hash_array.c zenglApi.c zenglApi_BltModFuns.c zenglDebug.c
ar rc libzengl.a zengl_main.o zengl_parser.o zengl_symbol.o zengl_locals.o zengl_assemble.o zengl_ld.o zenglrun_main.o zenglrun_func.o zenglrun_hash_array.o zenglApi.o zenglApi_BltModFuns.o zenglDebug.o
make[1]: Leaving directory '/home/zengl/zenglServer/zengl/linux'
gcc -g3 -ggdb -O0 -std=c99 main.c http_parser.c module_request.c module_builtin.c dynamic_string.c multipart_parser.c resources.c  main.h http_parser.h common_header.h module_request.h module_builtin.h dynamic_string.h multipart_parser.h resources.h  module_mysql.c module_mysql.h  zengl/linux/zengl_exportfuns.h  -o zenglServer zengl/linux/libzengl.a -lpthread -DUSE_MYSQL `mysql_config --cflags --libs` 

mysql module is enabled!!!
[email protected]:~/zenglServer$ 


    在加入mysql模块前,请确保你的系统中包含了mysql_config程式和mysql.h开发头文件(有的系统中存在mysql_config,但是没有mysql.h开发头文件,也无法编译通过),如果没有的话,如果是ubuntu系统,可以通过sudo apt-get install libmysqlclient-dev来添加开发mysql客户端所需要的文件,如果是centos系统,则可以通过yum install mysql-devel来加入开发所需的文件。此外,暂时没有对mysql多线程安全问题进行处理,如果在每个工作进程中设置了多个工作线程的话,需要自行处理mysql客户端的多线程问题。

    为了测试mysql模块,在my_webroot目录里的v0_3_0目录中,添加了相关的测试脚本:config.zl,create_table.zl,insert_data.zl 以及 select_data.zl 。其中,config.zl是mysql数据库配置脚本:

config['db_host'] = 'localhost';
config['db_port'] = 3306;
config['db_user'] = 'root';
config['db_passwd'] = '123456';
config['db_name'] = 'testdb';


    需要先在该配置脚本中,设置mysql服务器的主机名(或IP地址),端口号,用户名,密码,数据库名。如果db_host是localhost的话,mysql客户端库会使用mysqld.sock套接字文件去连接数据库,此时会忽略端口号的设置(在作者的ubuntu系统中是这样的),如果db_host设置的是IP地址,则端口号需要填写正确。

    create_table.zl脚本,则用于在数据库中创建cars测试表:

use builtin;
use mysql;
inc 'config.zl';

fun finish_with_error(con)
	err = mysqlError(con);
	mysqlClose(con);
	print '<p style="color:red">' + err + '</p></body></html>';
	bltExit(err);
endfun

print '<!Doctype html>
<html>
<head><meta http-equiv="content-type" content="text/html;charset=utf-8" />
<title>创建数据库表结构
</head>
<body>';

print 'mysql客户端库的版本信息:' + mysqlGetClientInfo() + '<br/>';
con = mysqlInit();
if(!con)
	bltExit('mysqlInit failed');
endif

if(!mysqlRealConnect(con, config['db_host'], config['db_user'], config['db_passwd'], config['db_name'], config['db_port']))
	finish_with_error(con);
endif

server_version = mysqlGetServerVersion(con);
print 'mysql服务端的版本号信息:' + server_version[0] + '.' + server_version[1] + '.' + server_version[2] + '<br/>';

if(mysqlQuery(con, "DROP TABLE IF EXISTS cars"))
	finish_with_error(con);
endif

if(mysqlQuery(con, "CREATE TABLE cars(
					  id int NOT NULL AUTO_INCREMENT, 
					  name varchar(255) NOT NULL DEFAULT '', 
					  price int NOT NULL DEFAULT 0, 
					  description text,
					  PRIMARY KEY (id)
					) ENGINE=MyISAM DEFAULT CHARSET utf8 COLLATE utf8_general_ci COMMENT='cars test table'"))
	finish_with_error(con);
endif

print '<p style="color:green">创建表 cars 成功!' + '</p>';
mysqlClose(con);
print '<p>关闭mysql连接!' + '</p><a href="insert_data.zl">点击我,插入数据</a>';
print '</body></html>';


    该脚本会在开头将config.zl包含进来,并通过config['db_host']等配置信息去连接mysql数据库,最后通过CREATE TABLE语句创建cars测试表。如果数据库配置没有问题的话,执行结果如下:


图1:创建cars测试表

    创建完cars表后,就可以插入数据了,数据插入是通过insert_data.zl脚本来实现的:

use builtin;
use mysql;
use request;
inc 'config.zl';

fun finish_with_error(con)
	err = mysqlError(con);
	mysqlClose(con);
	print '<p style="color:red">' + err + '</p></body></html>';
	bltExit(err);
endfun

print '<!Doctype html>
<html>
<head><meta http-equiv="content-type" content="text/html;charset=utf-8" />
<title>插入数据到cars测试表</title>
</head>
<body>';

body_array = rqtGetBodyAsArray();
if(body_array['submit'])
	con = mysqlInit();
	if(!con)
		bltExit('mysqlInit failed');
	endif

	if(!mysqlRealConnect(con, config['db_host'], config['db_user'], config['db_passwd'], config['db_name'], config['db_port']))
		finish_with_error(con);
	endif

	if(mysqlSetCharacterSet(con, "utf8"))
		finish_with_error(con);
	endif
	print 'mysql设置字符集:' + mysqlCharacterSetName(con) + '<br/>';

	if(mysqlQuery(con, "INSERT INTO cars (`name`,`price`,`description`) VALUES('" +
					mysqlRealEscapeString(con,body_array['name']) + "', '" +
					mysqlRealEscapeString(con,body_array['price']) + "', '" + 
					mysqlRealEscapeString(con,body_array['description']) + "')")
		)
		finish_with_error(con);
	endif

	print '<p style="color:green">插入数据到cars表成功!'+ '</p>';
	mysqlClose(con);
	print '关闭mysql连接!'+ 
		'<br/><br/>
		<a href="insert_data.zl">点击我,继续插入数据</a><br/><br/>';
else
	print '<form action="insert_data.zl" method="post" enctype="multipart/form-data">
		<p>车名:<input name="name" value="" type="text"></p>
		<p>价格:<input name="price" value="" type="text"></p>
		<p>描述:<textarea name="description" rows="5" cols="40"></textarea></p>
		<input name="submit" value="提交" type="submit" style="width:180px;height:25px">
	</form><br/><br/><br/><br/>';
endif

print '<a href="select_data.zl">点击我来查看数据</a></body></html>';


    该脚本的执行结果如下:


图2:添加数据的表单

    点击提交按钮,执行成功后的结果如下:


图3:添加数据成功

    查看数据是通过select_data.zl脚本来完成的:

use builtin;
use mysql;
inc 'config.zl';

fun finish_with_error(con)
	err = mysqlError(con);
	mysqlClose(con);
	print '<p style="color:red">' + err + '</p></body></html>';
	bltExit(err);
endfun

fun mysql_query(con, sql)
	if(mysqlQuery(con, sql))
		finish_with_error(con);
	endif
	result = mysqlStoreResult(con);
	return_array = bltArray();
	while(mysqlFetchResultRow(result, &result_array))
		return_array[] = result_array;
	endwhile
	mysqlFreeResult(result);
	return return_array;
endfun

print '<!Doctype html>
<html>
<head><meta http-equiv="content-type" content="text/html;charset=utf-8" />
<title>浏览cars测试表数据</title>
</head>
<body>';

con = mysqlInit();
if(!con)
	bltExit('mysqlInit failed');
endif

if(!mysqlRealConnect(con, config['db_host'], config['db_user'], config['db_passwd'], config['db_name'], config['db_port']))
	finish_with_error(con);
endif

if(mysqlSetCharacterSet(con, "utf8"))
	finish_with_error(con);
endif
print 'mysql设置字符集:' + mysqlCharacterSetName(con)+ '<br/>';

cars_array = mysql_query(con, "select * from cars limit 10");
print 'cars测试表中的前10条数据:<br/>';
for(i=0;bltIterArray(cars_array,&i,&car);)
	print '<p>' + car['id'] + ': ' + car['name'] + ' (价格: ' + car['price'] + ')<br/>' + 
			'[' + car['description'] + ']</p>';
endfor

if(!i) print '<p>暂无数据!</p>'; endif

mysqlClose(con);
print '关闭mysql连接!'+ 
		'<br/><br/>
		<a href="insert_data.zl">点击我,插入数据</a>';

print '</body></html>';


    该脚本的执行结果如下:


图4:查看数据

    这些测试脚本中所使用的mysqlInit,mysqlRealConnect之类的脚本函数都定义在mysql模块中,该模块相关的C源代码位于module_mysql.c文件里:

/*
 * module_mysql.c
 *
 *  Created on: 2017-9-26
 *      Author: zengl
 */

#include "main.h"
#include "module_mysql.h"
#include <mysql.h>
#include <stdlib.h>
#include <string.h>

#define MODULE_MYSQL_RES_SIGNER 0x54555352 // RSUT签名,从低字节到高字节,ASCII码

/**
 * 自定义的结构体,除了官方的MYSQL_RES指针外,
 * 还将结果中包含的字段数存储在num_fields中,这样就不用在每次提取一行数据时,都重复执行一次查询字段数的操作,
 * signer成员用于存储有效的签名,在对mysql_res进行相关操作之前,会先校验signer是否是有效的签名
 */
typedef struct _MODULE_MYSQL_RES {
	MYSQL_RES * mysql_res;
	int num_fields;
	unsigned int signer;
} MODULE_MYSQL_RES;

/**
 * 在zengl脚本退出之前,会自动通过下面的回调函数,
 * 将所有未关闭的数据库连接都关闭掉
 */
static void module_mysql_free_connection_resource_callback(ZL_EXP_VOID * VM_ARG, void * ptr)
{
	MYSQL *con = (MYSQL *)ptr;
	mysql_close(con);
}

/**
 * 在zengl脚本退出之前,会自动通过下面的回调函数,
 * 将所有未释放掉的和结果集相关的数据库资源都释放掉
 */
static void module_mysql_free_result_resource_callback(ZL_EXP_VOID * VM_ARG, void * ptr)
{
	MODULE_MYSQL_RES * result = (MODULE_MYSQL_RES *)ptr;
	mysql_free_result(result->mysql_res);
	zenglApi_FreeMem(VM_ARG, result);
}

/**
 * mysqlGetClientInfo模块函数对应的C函数
 * 通过mysql_get_client_info的官方库函数,来返回mysql客户端库的版本信息
 */
ZL_EXP_VOID module_mysql_get_client_info(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_STR, (char *)mysql_get_client_info(), 0, 0);
}

/**
 * mysqlGetServerVersion模块函数对应的C函数
 * 通过mysql_get_server_version官方库函数,获取服务端的版本号信息,
 * 主版本,子版本,修正版本号会依次存储在返回数组的前三个成员中
 */
ZL_EXP_VOID module_mysql_get_server_version(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}};
	if(argcount != 1)
		zenglApi_Exit(VM_ARG,"usage: mysqlGetServerVersion(connection)");
	zenglApi_GetFunArg(VM_ARG,1,&arg);
	if(arg.type != ZL_EXP_FAT_INT) {
		zenglApi_Exit(VM_ARG,"the first argument [connection] of mysqlGetServerVersion must be integer");
	}
	MYSQL *con = (MYSQL *)arg.val.integer;
	unsigned long server_version = mysql_get_server_version(con);
	long major_version = (long)(server_version / 10000);
	long minor_version = (long)((server_version % 10000) / 100);
	long patch_version = (long)(server_version % 100);
	ZENGL_EXPORT_MEMBLOCK memblock = {0};
	if(zenglApi_CreateMemBlock(VM_ARG,&memblock,0) == -1) {
		zenglApi_Exit(VM_ARG,zenglApi_GetErrorString(VM_ARG));
	}
	arg.type = ZL_EXP_FAT_INT;
	arg.val.integer = major_version;
	zenglApi_SetMemBlock(VM_ARG,&memblock,1,&arg);
	arg.val.integer = minor_version;
	zenglApi_SetMemBlock(VM_ARG,&memblock,2,&arg);
	arg.val.integer = patch_version;
	zenglApi_SetMemBlock(VM_ARG,&memblock,3,&arg);
	zenglApi_SetRetValAsMemBlock(VM_ARG,&memblock);
}

/**
 * mysqlInit模块函数对应的C函数
 * 通过mysql_init的官方库函数,初始化一个MYSQL结构体的connection连接指针,后续的各种数据库操作,包括连接数据库,查询数据库等操作都需要传递该指针,
 * 并通过resource_list_set_member函数将指针存储到资源列表中,如果该指针在脚本中没有被手动关闭的话,
 * 最后,会通过module_mysql_free_connection_resource_callback回调函数将指针自动关闭掉
 */
ZL_EXP_VOID module_mysql_Init(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	MYSQL * con = mysql_init(NULL);
	zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, (ZL_EXP_LONG)con, 0);
	MAIN_DATA * my_data = zenglApi_GetExtraData(VM_ARG, "my_data");
	int ret_code = resource_list_set_member(&(my_data->resource_list), con, module_mysql_free_connection_resource_callback);
	if(ret_code != 0) {
		zenglApi_Exit(VM_ARG, "mysqlInit add resource to resource_list failed, resource_list_set_member error code:%d", ret_code);
	}
}

/**
 * mysqlRealConnect模块函数对应的C函数
 * 通过mysql_real_connect官方库函数,连接mysql数据库,
 * 需要将mysqlInit初始化的连接指针,以及主机名(或IP地址),用户名,密码,数据库名,端口号作为参数传递给它,最后两个参数是可选的
 */
ZL_EXP_VOID module_mysql_real_connect(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}};
	if(argcount < 4)
		zenglApi_Exit(VM_ARG,"usage: mysqlRealConnect(connection, host, username, password[, select_db][, port])");
	zenglApi_GetFunArg(VM_ARG,1,&arg);
	if(arg.type != ZL_EXP_FAT_INT) {
		zenglApi_Exit(VM_ARG,"the first argument [connection] of mysqlRealConnect must be integer");
	}
	MYSQL *con = (MYSQL *)arg.val.integer;
	zenglApi_GetFunArg(VM_ARG,2,&arg);
	if(arg.type != ZL_EXP_FAT_STR) {
		zenglApi_Exit(VM_ARG,"the second argument [host] of mysqlRealConnect must be string");
	}
	char * host = arg.val.str;
	zenglApi_GetFunArg(VM_ARG,3,&arg);
	if(arg.type != ZL_EXP_FAT_STR) {
		zenglApi_Exit(VM_ARG,"the third argument [username] of mysqlRealConnect must be string");
	}
	char * username = arg.val.str;
	zenglApi_GetFunArg(VM_ARG,4,&arg);
	if(arg.type != ZL_EXP_FAT_STR) {
		zenglApi_Exit(VM_ARG,"the fourth argument [password] of mysqlRealConnect must be string");
	}
	char * password = arg.val.str;
	char * select_db = NULL;
	if(argcount >= 5) {
		zenglApi_GetFunArg(VM_ARG,5,&arg);
		if(arg.type == ZL_EXP_FAT_INT) {
			select_db = (char *)arg.val.integer;
		}
		else if(arg.type == ZL_EXP_FAT_STR) {
			select_db = arg.val.str;
		}
	}
	unsigned int port = 0;
	if(argcount >= 6) {
		zenglApi_GetFunArg(VM_ARG,6,&arg);
		if(arg.type == ZL_EXP_FAT_INT) {
			port = (unsigned int)arg.val.integer;
		}
	}
	MYSQL * retval = mysql_real_connect(con, host, username, password, select_db, port, NULL, 0);
	zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, (ZL_EXP_LONG)retval, 0);
}

/**
 * mysqlSetCharacterSet模块函数对应的C函数
 * 通过mysql_set_character_set官方库函数,设置当前连接默认的字符集
 */
ZL_EXP_VOID module_mysql_set_character_set(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}};
	if(argcount != 2)
		zenglApi_Exit(VM_ARG,"usage: mysqlSetCharacterSet(connection, charset_name)");
	zenglApi_GetFunArg(VM_ARG,1,&arg);
	if(arg.type != ZL_EXP_FAT_INT) {
		zenglApi_Exit(VM_ARG,"the first argument [connection] of mysqlSetCharacterSet must be integer");
	}
	MYSQL *con = (MYSQL *)arg.val.integer;
	zenglApi_GetFunArg(VM_ARG,2,&arg);
	if(arg.type != ZL_EXP_FAT_STR) {
		zenglApi_Exit(VM_ARG,"the second argument [charset_name] of mysqlSetCharacterSet must be string");
	}
	char * charset_name = arg.val.str;
	int retval = mysql_set_character_set(con, (const char *)charset_name);
	zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, (ZL_EXP_LONG)retval, 0);
}

/**
 * mysqlCharacterSetName模块函数对应的C函数
 * 通过mysql_character_set_name官方库函数,返回当前连接的默认字符集名称
 */
ZL_EXP_VOID module_mysql_character_set_name(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}};
	if(argcount != 1)
		zenglApi_Exit(VM_ARG,"usage: mysqlCharacterSetName(connection)");
	zenglApi_GetFunArg(VM_ARG,1,&arg);
	if(arg.type != ZL_EXP_FAT_INT) {
		zenglApi_Exit(VM_ARG,"the first argument [connection] of mysqlCharacterSetName must be integer");
	}
	MYSQL *con = (MYSQL *)arg.val.integer;
	zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_STR, (char *)mysql_character_set_name(con), 0, 0);
}

/**
 * mysqlRealEscapeString模块函数对应的C函数
 * 通过mysql_real_escape_string官方库函数,将字符串进行安全转义,使其能够用于sql语句中
 */
ZL_EXP_VOID module_mysql_real_escape_string(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}};
	if(argcount != 2)
		zenglApi_Exit(VM_ARG,"usage: mysqlRealEscapeString(connection, source_string)");
	zenglApi_GetFunArg(VM_ARG,1,&arg);
	if(arg.type != ZL_EXP_FAT_INT) {
		zenglApi_Exit(VM_ARG,"the first argument [connection] of mysqlRealEscapeString must be integer");
	}
	MYSQL *con = (MYSQL *)arg.val.integer;
	zenglApi_GetFunArg(VM_ARG,2,&arg);
	if(arg.type != ZL_EXP_FAT_STR) {
		zenglApi_Exit(VM_ARG,"the second argument [source_string] of mysqlRealEscapeString must be string");
	}
	char * source_string = arg.val.str;
	int source_length = strlen(source_string);
	int to_length = source_length * 2 + 1;
	char * to_string = (char *)zenglApi_AllocMem(VM_ARG, to_length);
	unsigned long retval = mysql_real_escape_string(con, to_string, (const char *)source_string, (unsigned long)source_length);
	if(retval == ((unsigned long) - 1)) {
		zenglApi_Exit(VM_ARG, "mysqlRealEscapeString failed: %s", (char *)mysql_error(con));
	}
	zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_STR, to_string, 0, 0);
	zenglApi_FreeMem(VM_ARG, to_string);
}

/**
 * mysqlError模块函数对应的C函数
 * 通过mysql_error官方库函数,将最近一次调用mysql api接口时发生的错误信息返回
 */
ZL_EXP_VOID module_mysql_error(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}};
	if(argcount != 1)
		zenglApi_Exit(VM_ARG,"usage: mysqlError(connection)");
	zenglApi_GetFunArg(VM_ARG,1,&arg);
	if(arg.type != ZL_EXP_FAT_INT) {
		zenglApi_Exit(VM_ARG,"the first argument [connection] of mysqlError must be integer");
	}
	MYSQL *con = (MYSQL *)arg.val.integer;
	zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_STR, (char *)mysql_error(con), 0, 0);
}

/**
 * mysqlQuery模块函数对应的C函数
 * 通过mysql_query官方库函数,执行具体的sql语句
 */
ZL_EXP_VOID module_mysql_query(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}};
	if(argcount != 2)
		zenglApi_Exit(VM_ARG,"usage: mysqlQuery(connection, statement)");
	zenglApi_GetFunArg(VM_ARG,1,&arg);
	if(arg.type != ZL_EXP_FAT_INT) {
		zenglApi_Exit(VM_ARG,"the first argument [connection] of mysqlQuery must be integer");
	}
	MYSQL *con = (MYSQL *)arg.val.integer;
	zenglApi_GetFunArg(VM_ARG,2,&arg);
	if(arg.type != ZL_EXP_FAT_STR) {
		zenglApi_Exit(VM_ARG,"the second argument [statement] of mysqlQuery must be string");
	}
	char * statement = arg.val.str;
	int retval = mysql_query(con, (const char *)statement);
	zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, retval, 0);
}

/**
 * mysqlClose模块函数对应的C函数
 * 通过mysql_close的官方库函数,关闭数据库连接
 * 并通过resource_list_remove_member函数将连接指针从资源列表中移除,以防止脚本结束时,自动对该连接指针再次执行关闭操作
 */
ZL_EXP_VOID module_mysql_close(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}};
	if(argcount != 1)
		zenglApi_Exit(VM_ARG,"usage: mysqlClose(connection)");
	zenglApi_GetFunArg(VM_ARG,1,&arg);
	if(arg.type != ZL_EXP_FAT_INT) {
		zenglApi_Exit(VM_ARG,"the first argument [connection] of mysqlClose must be integer");
	}
	MYSQL *con = (MYSQL *)arg.val.integer;
	mysql_close(con);
	zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, 0, 0);
	MAIN_DATA * my_data = zenglApi_GetExtraData(VM_ARG, "my_data");
	int ret_code = resource_list_remove_member(&(my_data->resource_list), con);
	if(ret_code != 0) {
		zenglApi_Exit(VM_ARG, "mysqlClose remove resource from resource_list failed, resource_list_remove_member error code:%d", ret_code);
	}
}

/**
 * mysqlStoreResult模块函数对应的C函数
 * 通过mysql_store_result官方库函数,存储查询结果,同时通过mysql_num_fields库函数,保存结果集中的字段数
 * 并通过resource_list_set_member函数,将结果指针存储到资源列表中,如果该结果指针没有在zengl脚本中被手动释放掉的话,
 * 在脚本结束时,会自动通过module_mysql_free_result_resource_callback回调函数将结果指针给释放掉
 */
ZL_EXP_VOID module_mysql_store_result(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}};
	if(argcount != 1)
		zenglApi_Exit(VM_ARG,"usage: mysqlStoreResult(connection)");
	zenglApi_GetFunArg(VM_ARG,1,&arg);
	if(arg.type != ZL_EXP_FAT_INT) {
		zenglApi_Exit(VM_ARG,"the first argument [connection] of mysqlStoreResult must be integer");
	}
	MYSQL *con = (MYSQL *)arg.val.integer;
	MYSQL_RES * res = mysql_store_result(con);
	MODULE_MYSQL_RES * result = zenglApi_AllocMem(VM_ARG, sizeof(MODULE_MYSQL_RES));
	result->mysql_res = res;
	result->num_fields = (int)mysql_num_fields(res);
	result->signer = MODULE_MYSQL_RES_SIGNER;
	zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, (ZL_EXP_LONG)result, 0);
	MAIN_DATA * my_data = zenglApi_GetExtraData(VM_ARG, "my_data");
	int ret_code = resource_list_set_member(&(my_data->resource_list), result, module_mysql_free_result_resource_callback);
	if(ret_code != 0) {
		zenglApi_Exit(VM_ARG, "mysqlStoreResult add resource to resource_list failed, resource_list_set_member error code:%d", ret_code);
	}
}

/**
 * mysqlFreeResult模块函数对应的C函数
 * 通过mysql_free_result官方库函数,将结果指针中的mysql_res给释放掉,再通过zenglApi_FreeMem接口将结果指针释放掉
 * 最后通过resource_list_remove_member函数,将结果指针从资源列表中移除
 */
ZL_EXP_VOID module_mysql_free_result(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}};
	if(argcount != 1)
		zenglApi_Exit(VM_ARG,"usage: mysqlFreeResult(result)");
	zenglApi_GetFunArg(VM_ARG,1,&arg);
	if(arg.type != ZL_EXP_FAT_INT) {
		zenglApi_Exit(VM_ARG,"the first argument [result] of mysqlFreeResult must be integer");
	}
	MODULE_MYSQL_RES * result = (MODULE_MYSQL_RES *)arg.val.integer;
	if(result->signer != MODULE_MYSQL_RES_SIGNER) {
		zenglApi_Exit(VM_ARG,"the first argument [result] of mysqlFreeResult is invalid");
	}
	mysql_free_result(result->mysql_res);
	zenglApi_FreeMem(VM_ARG, result);
	zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, 0, 0);
	MAIN_DATA * my_data = zenglApi_GetExtraData(VM_ARG, "my_data");
	int ret_code = resource_list_remove_member(&(my_data->resource_list), result);
	if(ret_code != 0) {
		zenglApi_Exit(VM_ARG, "mysqlFreeResult remove resource from resource_list failed, resource_list_remove_member error code:%d", ret_code);
	}
}

/**
 * mysqlFetchResultRow模块函数对应的C函数
 * 通过mysql_fetch_row官方库函数,将结果集中当前行游标对应的一行数据读取出来,
 * 同时通过mysql_fetch_field库函数,将每个字段的信息读取出来,
 * 从而根据字段名和行数据,构成一个名值对组成的哈希数组,并将该数组作为结果返回
 * 在读取行数据时,会根据每个字段的类型,对数据进行类型转换,例如,某个字段是整数类型,那么该字段对应的数据就会被转为整数类型,再存储到哈希数组中
 */
ZL_EXP_VOID module_mysql_fetch_result_row(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}};
	if(argcount != 2)
		zenglApi_Exit(VM_ARG,"usage: mysqlFetchResultRow(result, &result_array)");
	zenglApi_GetFunArg(VM_ARG,1,&arg);
	if(arg.type != ZL_EXP_FAT_INT) {
		zenglApi_Exit(VM_ARG,"the first argument [result] of mysqlFetchResultRow must be integer");
	}
	MODULE_MYSQL_RES * result = (MODULE_MYSQL_RES *)arg.val.integer;
	if(result->signer != MODULE_MYSQL_RES_SIGNER) {
		zenglApi_Exit(VM_ARG,"the first argument [result] of mysqlFetchResultRow is invalid");
	}
	MYSQL_ROW row;
	MYSQL_FIELD * field;
	MYSQL_RES * mysql_res = result->mysql_res;
	if ((row = mysql_fetch_row(mysql_res)))
	{
		int num_fields = result->num_fields;
		if(num_fields > 0) {
			mysql_field_seek(mysql_res,0);
		}
		ZENGL_EXPORT_MEMBLOCK memblock = {0};
		if(zenglApi_CreateMemBlock(VM_ARG,&memblock,0) == -1) {
			zenglApi_Exit(VM_ARG,zenglApi_GetErrorString(VM_ARG));
		}
		for(int i = 0; i < num_fields; i++)
		{
			field = mysql_fetch_field(mysql_res);
			if(row[i])
			{
				switch(field->type)
				{
				case MYSQL_TYPE_TINY:
				case MYSQL_TYPE_SHORT:
				case MYSQL_TYPE_INT24:
				case MYSQL_TYPE_LONG:
				case MYSQL_TYPE_YEAR:
					arg.type = ZL_EXP_FAT_INT;
					arg.val.integer = atol(row[i]);
					zenglApi_SetMemBlockByHashKey(VM_ARG, &memblock, field->name, &arg);
					break;
				case MYSQL_TYPE_FLOAT:
				case MYSQL_TYPE_DOUBLE:
				case MYSQL_TYPE_DECIMAL:
					arg.type = ZL_EXP_FAT_FLOAT;
					arg.val.floatnum = atof(row[i]);
					zenglApi_SetMemBlockByHashKey(VM_ARG, &memblock, field->name, &arg);
					break;
				default:
					arg.type = ZL_EXP_FAT_STR;
					arg.val.str = row[i];
					zenglApi_SetMemBlockByHashKey(VM_ARG, &memblock, field->name, &arg);
					break;
				}
			}
			else
			{
				arg.type = ZL_EXP_FAT_NONE;
				zenglApi_SetMemBlockByHashKey(VM_ARG, &memblock, field->name, &arg);
			}
		}
		zenglApi_GetFunArgInfo(VM_ARG,2,&arg);
		switch(arg.type){
		case ZL_EXP_FAT_ADDR:
		case ZL_EXP_FAT_ADDR_LOC:
		case ZL_EXP_FAT_ADDR_MEMBLK:
			break;
		default:
			zenglApi_Exit(VM_ARG,"the second argument [&result_array] of mysqlFetchResultRow must be address type");
			break;
		}
		arg.type = ZL_EXP_FAT_MEMBLOCK;
		arg.val.memblock = memblock;
		zenglApi_SetFunArg(VM_ARG,2,&arg);
		zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, 1, 0);
	}
	else
		zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, 0, 0);
}

/**
 * mysql模块的初始化函数,里面设置了与该模块相关的各个模块函数及其相关的处理句柄(对应的C函数)
 */
ZL_EXP_VOID module_mysql_init(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT moduleID)
{
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"mysqlGetClientInfo",module_mysql_get_client_info);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"mysqlGetServerVersion",module_mysql_get_server_version);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"mysqlInit",module_mysql_Init);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"mysqlRealConnect",module_mysql_real_connect);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"mysqlError",module_mysql_error);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"mysqlQuery",module_mysql_query);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"mysqlClose",module_mysql_close);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"mysqlSetCharacterSet",module_mysql_set_character_set);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"mysqlCharacterSetName",module_mysql_character_set_name);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"mysqlRealEscapeString",module_mysql_real_escape_string);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"mysqlStoreResult",module_mysql_store_result);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"mysqlFreeResult",module_mysql_free_result);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"mysqlFetchResultRow",module_mysql_fetch_result_row);
}


    上面C源码中用到的mysql_get_server_version之类的官方库函数的使用方法,请参考mysql的官方手册。

    限于篇幅,其他的C源码就不显示了。读者可以根据注释来理解相关代码,还可以配合gdb调试来进行分析。

结束语:

    迷失比想象中容易

——  《星际迷航》
 
上下篇

下一篇: zenglServer v0.4.0 daemon守护进程, epoll事件驱动

上一篇: 暂无

相关文章

zenglServer v0.16.0 增加curlSetPostByHashArray,curlSetHeaderByArray模块函数,调整进程名称等

zenglServer v0.10.0 使用zengl脚本的编译缓存,跳过编译过程

zenglServer v0.17.0 设置精简日志模式,设置允许上传文件大小,日志分割等

zenglServer v0.13.0 目录入口文件以及模板路径调整

zenglServer v0.11.0 共享内存,crustache转义,magick模块,新增bltDate等内建模块函数

zenglServer v0.4.0 daemon守护进程, epoll事件驱动