v0.24.0的版本增加了bltVersionCompare,mysqlAffectedRows模块函数。此外,还为bltStr模块函数增加了format可选参数(可以在将整数,浮点数转为字符串时,对其进行格式化操作)。
zenglServer源代码的相关地址:https://github.com/zenglong/zenglServer 当前版本对应的tag标签为:v0.24.0
v0.24.0的版本增加了bltVersionCompare,mysqlAffectedRows模块函数。此外,还为bltStr模块函数增加了format可选参数(可以在将整数,浮点数转为字符串时,对其进行格式化操作)。
当前版本增加了bltVersionCompare模块函数,可以用于对版本号进行比较,该模块函数定义在module_builtin.c文件中:
/** * bltVersionCompare模块函数,用于对版本号进行比较 * 第一个参数version1必须是字符串类型,表示需要进行比较的第一个版本号 * 第二个参数version2也必须是字符串类型,表示需要进行比较的第二个版本号 * 当返回值大于0时,表示version1大于version2 * 当返回值等于0时,表示version1等于version2 * 当返回值小于0时,表示version1小于version2 * * 示例: use builtin; fun compare(v1, v2) c = bltVersionCompare(v1, v2); if(c > 0) print v1 + ' > ' + v2; elif(c < 0) print v1 + ' < ' + v2; else print v1 + ' == ' + v2; endif endfun fun compare2(v1, v2) if(bltVersionCompare(v1, v2) >= 0) // bltVersionCompare模块函数返回值大于或等于0,则说明v1版本号大于或等于v2版本号 print v1 + ' >= ' + v2; else print v1 + ' < ' + v2; endif endfun compare('v0.1.0', 'v0.2.0'); compare('v1.2.3', 'v1.2'); compare('v2.2.3', 'v2.2.2'); compare('2.3.0', 'v2.3'); print ''; compare2('3.2.1', '3.2'); compare2('3.2.0', '3.2'); compare2('3.2.0', '3.2.1'); print ''; 执行结果如下: v0.1.0 < v0.2.0 v1.2.3 > v1.2 v2.2.3 > v2.2.2 2.3.0 == v2.3 3.2.1 >= 3.2 3.2.0 >= 3.2 3.2.0 < 3.2.1 模块函数版本历史: - v0.24.0版本新增此模块函数 */ ZL_EXP_VOID module_builtin_version_compare(ZL_EXP_VOID * VM_ARG, ZL_EXP_INT argcount) { ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}}; const char * func_name = "bltVersionCompare"; if(argcount < 2) zenglApi_Exit(VM_ARG,"usage: %s(version1, version2)", func_name); zenglApi_GetFunArg(VM_ARG,1,&arg); if(arg.type != ZL_EXP_FAT_STR) { zenglApi_Exit(VM_ARG,"the first argument [version1] of %s must be string", func_name); } char * version1 = (char *)arg.val.str; zenglApi_GetFunArg(VM_ARG,2,&arg); if(arg.type != ZL_EXP_FAT_STR) { zenglApi_Exit(VM_ARG,"the second argument [version2] of %s must be string", func_name); } char * version2 = (char *)arg.val.str; int retval = builtin_version_compare(version1, version2); zenglApi_SetRetVal(VM_ARG, ZL_EXP_FAT_INT, ZL_EXP_NULL, retval, 0); }
从上面C代码的注释中可以看到,将需要比较的版本号作为参数传递给该模块函数,当模块函数的返回值大于0时,表示第一个参数对应的版本号大于第二个参数对应的版本号,当返回值等于0时,则表示两个参数对应的版本号是相等的,当返回值小于0时,则表示第一个参数对应的版本号小于第二个参数对应的版本号。
当前版本还增加了mysqlAffectedRows模块函数,可以返回前一次mysql操作所影响的记录行数,该模块函数定义在module_mysql.c文件中:
/** * mysqlAffectedRows模块函数,返回前一次mysql操作所影响的记录行数 * 第一个参数connection必须是整数类型,表示mysql连接相关的指针 * 模块函数的返回值表示受影响的记录数 * * 示例: use builtin, mysql; config['db_host'] = 'localhost'; // mysql数据库ip config['db_port'] = 3306; // mysql数据库端口 config['db_user'] = 'root'; // mysql用户名 config['db_passwd'] = '123456'; // mysql密码 config['db_name'] = 'testdb'; // mysql数据库名 fun finish_with_error(con) err = mysqlError(con); mysqlClose(con); 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 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(mysqlQuery(con, "CREATE TABLE IF NOT EXISTS `test_table` ( id int NOT NULL AUTO_INCREMENT, name varchar(255) NOT NULL DEFAULT '', score int NOT NULL DEFAULT 0, PRIMARY KEY (id) ) ENGINE=MyISAM DEFAULT CHARSET utf8 COLLATE utf8_general_ci COMMENT='my test table'")) finish_with_error(con); endif for(i=0; i < 3;i++) if(mysqlQuery(con, "INSERT INTO `test_table` (`name`,`score`) VALUES('" + bltRandomStr("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 4) + "', '" + bltRand(0, 100) + "')")) finish_with_error(con); endif // 通过mysqlAffectedRows模块函数,打印 INSERT 插入语句所添加的记录数 print 'insert table `test_table`, affected rows: ' + mysqlAffectedRows(con); endfor if(mysqlQuery(con, "UPDATE `test_table` SET `score` = '50' WHERE score < 50")) finish_with_error(con); endif // 通过mysqlAffectedRows模块函数,打印 UPDATE 更新语句所更新的记录数 print "update table `test_table`, affected rows: " + mysqlAffectedRows(con); if(mysqlQuery(con, "DELETE FROM `test_table` WHERE score > 80")) finish_with_error(con); endif // 通过mysqlAffectedRows模块函数,打印 DELETE 删除语句所删除的记录数 print "delete from table `test_table`, affected rows: " + mysqlAffectedRows(con); data_array = mysql_query(con, "select * from `test_table` order by id desc limit 20"); // 通过mysqlAffectedRows模块函数,打印 select 查询语句所查找出来的记录数 print 'select rows num: ' + mysqlAffectedRows(con); for(i=0;bltIterArray(data_array,&i,&data);) print data['id'] + ': ' + data['name'] + ' (score: ' + data['score'] + ')'; endfor 执行结果类似如下所示: insert table `test_table`, affected rows: 1 insert table `test_table`, affected rows: 1 insert table `test_table`, affected rows: 1 update table `test_table`, affected rows: 1 delete from table `test_table`, affected rows: 0 select rows num: 3 3: JBCF (score: 60) 2: REQR (score: 69) 1: RRTO (score: 50) 模块函数版本历史: - v0.24.0版本新增此模块函数 */ ZL_EXP_VOID module_mysql_affected_rows(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount) { ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}}; const char * func_name = "mysqlAffectedRows"; if(argcount != 1) zenglApi_Exit(VM_ARG,"usage: %s(connection)", func_name); zenglApi_GetFunArg(VM_ARG,1,&arg); if(arg.type != ZL_EXP_FAT_INT) { zenglApi_Exit(VM_ARG,"the first argument [connection] of %s must be integer", func_name); } MYSQL *con = (MYSQL *)arg.val.integer; MAIN_DATA * my_data = zenglApi_GetExtraData(VM_ARG, "my_data"); if(!is_valid_mysql_connection(&(my_data->resource_list), con)) { zenglApi_Exit(VM_ARG,"%s runtime error: invalid connection", func_name); } int rows = (int)mysql_affected_rows(con); zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, rows, 0); }
从上面C代码的相关注释中可以看到,该模块函数需要接受mysql连接相关的指针(可以通过mysqlInit模块函数来获得该连接指针)作为参数,返回的结果表示上一次mysql操作所影响的记录行数,例如,如果是新增操作,则该模块函数的返回值可以表示新增了多少条数据库记录,如果是更新操作,则返回值可以表示更新了多少条数据库记录,如果是删除操作,则返回值表示删除了多少条记录,如果是查询操作,则表示查询出了多少条记录。
当前版本为bltStr模块函数增加了format可选参数,可以在将整数,浮点数转为字符串时,对其进行格式化操作,详情可以参考该模块函数的C代码的注释部分,该模块函数的C源码定义在module_builtin.c文件中:
/** * bltStr模块函数,返回第一个参数的字符串形式 * 第一个参数data|&data,表示需要转成字符串的源数据(可以是整数,浮点数之类的数据) * 第二个参数是可选参数 * 当第二个参数是整数类型时,表示isSetData即是否将转换的结果赋值给第一个参数(此时还需要将第一个参数的引用传递进来) * 当第二个参数是字符串类型时,表示format即对整数或浮点数进行格式化,当第二个参数是format时,可以提供第三个可选参数来表示isSetData(是否将字符串结果赋值给第一个参数) * ........................................................................ * * 当第二个参数是字符串类型时,表示format即对整数或浮点数进行格式化 * 例如: use builtin; def TRUE 1; def FALSE 0; value = 1789.800000001; print bltStr(value, '%.2f'); print bltStr(value, '%012.12f'); print bltStr(value, '%.12E'); print bltStr(&value, '%012.100f', TRUE); print 'value: ' + value + '\n'; for(i=65;i <= 71;i++) print i + bltStr(i, ' - 0x%X') + bltStr(i, ' - %c'); endfor 上面会将浮点数进行格式化,例如:'%.2f'表示保留两位小数,此时可以把第三个参数设置为非0的整数值,来表示将转换结果赋值给第一个参数,执行结果类似如下所示: 1789.80 1789.800000001000 1.789800000001E+03 1789.80000000099994394986424595117568969726562500000000000000000000000000000000 value: 1789.80000000099994394986424595117568969726562500000000000000000000000000000000 65 - 0x41 - A 66 - 0x42 - B 67 - 0x43 - C 68 - 0x44 - D 69 - 0x45 - E 70 - 0x46 - F 71 - 0x47 - G 模块函数版本历史: - v0.8.0版本新增此模块函数 - v0.24.0版本增加format可选参数,用于对整数或浮点数进行格式化(格式化的语法请参考snprintf的C库函数,因为该模块函数的底层是通过snprintf来进行格式化的) */ ZL_EXP_VOID module_builtin_str(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: bltStr(data|&data[, isSetData=0]) or bltStr(data|&data[, format[, isSetData=0]])"); zenglApi_GetFunArg(VM_ARG,1,&arg); int isSetData = ZL_EXP_FALSE; char * format = NULL; if(argcount >= 2) { ZENGL_EXPORT_MOD_FUN_ARG arg2 = {ZL_EXP_FAT_NONE,{0}}; zenglApi_GetFunArg(VM_ARG,2,&arg2); if(arg2.type == ZL_EXP_FAT_INT) isSetData = arg2.val.integer; else if(arg2.type == ZL_EXP_FAT_STR) format = arg2.val.str; else zenglApi_Exit(VM_ARG,"the second argument of bltStr must be integer type as isSetData or string type as format"); if(argcount >= 3 && format != NULL) { zenglApi_GetFunArg(VM_ARG,3,&arg2); if(arg2.type != ZL_EXP_FAT_INT) zenglApi_Exit(VM_ARG,"the third argument [isSetData] of bltStr must be integer"); isSetData = arg2.val.integer; } } char * retstr; char tmpstr[80]; switch(arg.type) { case ZL_EXP_FAT_STR: retstr = arg.val.str; break; case ZL_EXP_FAT_INT: case ZL_EXP_FAT_FLOAT: if(format == NULL) { if(arg.type == ZL_EXP_FAT_INT) format = "%ld"; else format = "%.16g"; } else if(strchr(format, '*') || strchr(format, 's')) { zenglApi_Exit(VM_ARG,"bltStr format error: can't use * or s in format!"); } int retcount = 0; if(arg.type == ZL_EXP_FAT_INT) retcount = snprintf(tmpstr, sizeof(tmpstr), format, arg.val.integer); else retcount = snprintf(tmpstr, sizeof(tmpstr), format, arg.val.floatnum); if(retcount < 0) { zenglApi_Exit(VM_ARG,"bltStr error: %s", strerror(errno)); } retstr = tmpstr; break; case ZL_EXP_FAT_MEMBLOCK: retstr = "[array or class obj type]"; break; default: retstr = ""; break; } if(isSetData) { if(arg.type != ZL_EXP_FAT_STR) { arg.type = ZL_EXP_FAT_STR; arg.val.str = retstr; zenglApi_SetFunArg(VM_ARG,1,&arg); } } zenglApi_SetRetVal(VM_ARG, ZL_EXP_FAT_STR, retstr, 0, 0); }
从上面的C代码的注释中可以看到,当bltStr模块函数的第二个参数是字符串时,则表示对第一个参数对应的整数或浮点数进行格式化操作,由第二个字符串参数来指定具体的格式,例如,当第二个参数是'%.2f'时,表示将浮点数转为字符串时保留两位小数,当第二个参数是'0x%X'时表示将整数转为十六进制格式等等,具体的格式化语法可以参考snprintf的C库函数,因为该模块函数的底层是通过snprintf来进行格式化的。
为了测试新增的bltVersionCompare模块函数,以及bltStr模块函数新增的format可选参数,当前版本在 my_webroot/v0_24_0/ 目录中增加了test.zl的测试脚本,该脚本的代码如下:
use builtin; def TRUE 1; def FALSE 0; value = 1789.800000001; print bltStr(value, '%.2f'); print bltStr(value, '%012.12f'); print bltStr(value, '%.12E'); print bltStr(&value, '%012.100f', TRUE); print 'value: ' + value + '\n'; for(i=65;i <= 71;i++) print i + bltStr(i, ' - 0x%X') + bltStr(i, ' - %c'); endfor print ''; fun compare(v1, v2) c = bltVersionCompare(v1, v2); if(c > 0) print v1 + ' > ' + v2; elif(c < 0) print v1 + ' < ' + v2; else print v1 + ' == ' + v2; endif endfun fun compare2(v1, v2) if(bltVersionCompare(v1, v2) >= 0) // bltVersionCompare模块函数返回值大于或等于0,则说明v1版本号大于或等于v2版本号 print v1 + ' >= ' + v2; else print v1 + ' < ' + v2; endif endfun compare('v0.1.0', 'v0.2.0'); compare('v1.2.3', 'v1.2'); compare('v2.2.3', 'v2.2.2'); compare('2.3.0', 'v2.3'); print ''; compare2('3.2.1', '3.2'); compare2('3.2.0', '3.2'); compare2('3.2.0', '3.2.1'); print '';
上面的test.zl测试脚本在命令行中的执行结果如下:
[root@localhost zenglServerTest]# ./zenglServer -r "/v0_24_0/test.zl" 1789.80 1789.800000001000 1.789800000001E+03 1789.80000000099994394986424595117568969726562500000000000000000000000000000000 value: 1789.80000000099994394986424595117568969726562500000000000000000000000000000000 65 - 0x41 - A 66 - 0x42 - B 67 - 0x43 - C 68 - 0x44 - D 69 - 0x45 - E 70 - 0x46 - F 71 - 0x47 - G v0.1.0 < v0.2.0 v1.2.3 > v1.2 v2.2.3 > v2.2.2 2.3.0 == v2.3 3.2.1 >= 3.2 3.2.0 >= 3.2 3.2.0 < 3.2.1 [root@localhost zenglServerTest]#
为了测试新增的mysqlAffectedRows模块函数,当前版本在 my_webroot/v0_24_0/ 目录中增加了test_affected_rows.zl测试脚本,该脚本的代码如下:
use builtin, mysql; inc 'config.zl'; fun finish_with_error(con) err = mysqlError(con); mysqlClose(con); 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 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(mysqlQuery(con, "CREATE TABLE IF NOT EXISTS `test_table` ( id int NOT NULL AUTO_INCREMENT, name varchar(255) NOT NULL DEFAULT '', score int NOT NULL DEFAULT 0, PRIMARY KEY (id) ) ENGINE=MyISAM DEFAULT CHARSET utf8 COLLATE utf8_general_ci COMMENT='my test table'")) finish_with_error(con); endif for(i=0; i < 3;i++) if(mysqlQuery(con, "INSERT INTO `test_table` (`name`,`score`) VALUES('" + bltRandomStr("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 4) + "', '" + bltRand(0, 100) + "')")) finish_with_error(con); endif // 通过mysqlAffectedRows模块函数,打印 INSERT 插入语句所添加的记录数 print 'insert table `test_table`, affected rows: ' + mysqlAffectedRows(con); endfor if(mysqlQuery(con, "UPDATE `test_table` SET `score` = '50' WHERE score < 50")) finish_with_error(con); endif // 通过mysqlAffectedRows模块函数,打印 UPDATE 更新语句所更新的记录数 print "update table `test_table`, affected rows: " + mysqlAffectedRows(con); if(mysqlQuery(con, "DELETE FROM `test_table` WHERE score > 80")) finish_with_error(con); endif // 通过mysqlAffectedRows模块函数,打印 DELETE 删除语句所删除的记录数 print "delete from table `test_table`, affected rows: " + mysqlAffectedRows(con); data_array = mysql_query(con, "select * from `test_table` order by id desc limit 20"); // 通过mysqlAffectedRows模块函数,打印 select 查询语句所查找出来的记录数 print 'select rows num: ' + mysqlAffectedRows(con); for(i=0;bltIterArray(data_array,&i,&data);) print data['id'] + ': ' + data['name'] + ' (score: ' + data['score'] + ')'; endfor
该脚本在开头,会去加载config.zl配置脚本,在config.zl配置脚本中包含了和数据库相关的配置信息:
config['db_host'] = 'localhost'; // mysql数据库ip config['db_port'] = 3306; // mysql数据库端口 config['db_user'] = 'root'; // mysql用户名 config['db_passwd'] = '123456'; // mysql密码 config['db_name'] = 'testdb'; // mysql数据库名
所以,要测试test_affected_rows.zl脚本的话,必须先启动mysql数据库服务,还需要先创建好testdb数据库。test_affected_rows.zl脚本在执行时会自动在testdb数据库中创建test_table表结构(如果该表结构不存在的话才会进行创建),并在该表中插入,更新和删除数据,并通过mysqlAffectedRows模块函数来获取这些操作所影响的数据库记录数。该脚本在命令行中的执行结果类似如下所示:
[root@localhost zenglServerTest]# ./zenglServer -r "/v0_24_0/test_affected_rows.zl" insert table `test_table`, affected rows: 1 insert table `test_table`, affected rows: 1 insert table `test_table`, affected rows: 1 update table `test_table`, affected rows: 1 delete from table `test_table`, affected rows: 1 select rows num: 20 75: FYTS (score: 50) 73: KDRY (score: 66) 72: GWGX (score: 50) 71: IAFE (score: 67) 70: CFHK (score: 50) 69: LEGY (score: 80) 67: KGNI (score: 50) 66: EKFW (score: 50) 65: JVVE (score: 50) 64: FNIL (score: 72) 63: QJDN (score: 50) 62: CFBI (score: 75) 61: BOMN (score: 55) 60: BKGJ (score: 79) 59: IPOE (score: 50) 58: EKBG (score: 67) 57: KAJP (score: 76) 56: UOEL (score: 50) 55: FQUW (score: 50) 54: QLLG (score: 50) [root@localhost zenglServerTest]#
上面测试结果中,向test_table表中插入了三条数据,更新了一条数据,并删除了一条数据,最后从该表中查询了20条数据出来(在本次测试之前,该脚本已经测试过很多次了,因此该表中之前就已经有很多条记录了)。
我喜欢纯粹的创造。
—— 盗梦空间