Intrepid中的php-sybase凑合能用了

以前说过Ubuntu升级到hardy之后,php-sybase没了,现在在intrepid里又有了,不过有些变化。

主要的原因好像是支持方式从以前的ctlib变成了dblib,其实是和mssql的支持作在一起了,所以现在没有了sybase_ct.so,并且如果使用了adodb(我新下载的版本是5.07),要修改drivers/adodb-sybase.inc.php的148行,把

return sybase_unbuffered_query($sql,$this->_connectionID);

替换为

return sybase_query($sql,$this->_connectionID);

因为sybase_unbuffered_query只有在使用CT library时才能用

另外一个小变化就是timestamp读出来以后的值变了,由于sybase内置的timestamp其实是varbinary类型,所以原来使用ctlib的时候读出来就是这样的:000007d001917b36,但现在变成了类似中文字符串的形式,所以也需要转换一下:

if (16 != strlen($ts))
    $ts = bin2hex($ts);

Update @ 2009-03-01

还是日期的问题,现在从数据库日期字段里取出来的数据多了个毫秒部分,比如Mar 01 2009 00:24:00:000AM,致命的是这个字符串交给strtotime()函数居然不返回值,把毫秒部分:000去掉就没事了。

忙碌的5月

2008年的5月是难以忘怀的,有人戏称是上帝的手机不小心开了震动模式了,权当是我们勇敢面对明天的一点自我安慰吧,逝者已矣。这么大的事件,我没帮上什么忙,也没帮上什么倒忙,除了捐了自己微薄的一个月工资以外,几乎什么都没有作,有些惭愧,但我也没闲着,原先考虑的.NET还是PHP?问题基本被消灭掉了,灾要救,其余的工作也要继续,不是么?

这个项目我最终选择了用PHP来作,原因不再说了,正确性让时间去证明吧。工作内容大体分两部分,一个比较简单的子系统和另外一个大一点,逻辑关系和计算规则比较复杂,专业性也较强的子系统。之所以说是子系统,这次开发的内容仍然要和原来.NET的系统一起使用,换个说法就是只升级了一部分,而原来的系统就是.NET和Java混合着用的,现在.NET、Java、PHP全到齐了:-)。

因为只是子系统开发、升级,所以用户管理、验证部分仍然使用原系统中的,省去了这部分的工作量,但需要作一些焊接的工作。

数据库方面,原系统仍然使用Sybase,新系统换到mysql库,为此专门搞了一个单向同步数据的模块,放到cron中每隔5分钟执行一次,效果还可以接受,同步过来的数据也算是对原Sybase的一个备份。不过库结构的命名是全新的,并且除字典表以外,主键全面从identity转向uuid。自定义的uuid按时间排序,还分配了可自定义部分,目前感觉使用效果应该比identity要好,还是需要时间证明了。uuid的调试是麻烦些,需要一点小技巧和烂笔头,还有phpMyAdmin这个好工具。

时间上,大概是从4月初开始的,刚开始连我4个人,其中1个PHP还没学会,另外2个是今年的应届毕业生(项目过程中,5月底左右,才完成论文答辩,领了毕业证)。我除了项目还有其它工作,刚开始帮他们起了个头,中间一直到5月中下旬才全面投入项目。PHP还没学会的这个后来主攻文档和测试、项目协调。

一开始花了大概10天的时间构建系统框架、连入adodb,smarty等类库,以及非常重要的开发规范制定。然后开始作那个比较小的子系统,一来可以不断完善系统框架,二来也算是锻炼队伍。原计划4月底完成,拖到了5月10号左右。

然后开始较复杂的子系统,由于时间太紧迫,又请了两位外援,一位是几乎全能的老手,除了专业业务不熟悉,别的前、后台都没问题,另外一位是一年经验的PHP开发者,速度虽然略慢但代码质量还可接受。

从5月15号开始,几乎是封闭式开发了,每天除了吃饭睡觉和大概一个小时的休息(乒乓球)时间,每天都工作到24点以后。原计划5月底开始测试(并非完成),跳票到了6月10号,还算是在可接受范围之内吧,只是一些非重点非必要的辅助功能都被我们留在以后作了。

项目基本上就是这些情况吧,谈几点感受:

  • 石家庄这种鬼地方,一半用户是微软的盲目崇拜者,一半用户是Java的盲目信仰者,PHP及其它开源技术的土壤简直就是盐碱地。
  • 在较成熟的PHP应用环境下,或者开发团队中有PHP熟练者的情况下,即使是.NET和Java的高手也是可以快速熟悉起来产生生产力的。
  • 不要信仰工具,不管是用什么开发工具,没有我们后来请的两位外援,再有2月也拿不出东西来。
  • 工作经验的确很重要,不仅仅是工作质量的差别,毕业生和有工作经验的人相比,工作精神、压力承受程度、解决问题的思维方式都有很大差别的。好在我们的团队成员在工作态度上还都是一流的,这一点我得感谢他们。
  • 欠缺的知识:在开发工作量测量、开发时间测量上还没有太好的方式,代码质量也没有很好的检查方法。需求表达、结构设计基本上靠文字描述和口头讲解(当然也有时间的原因),没有趁手的case工具。数据库结构设计和维护从原来的PowerDesigner又回归到了原始的sql文件+维护版sql文件,感觉用起来虽然不太方便,效率也不低,多服务器的环境下尤其好用。
  • 花一些时间搭建高仿真的测试环境很重要,我们的测试环境已经运行了3年多,系统和数据都是和生产环境一样的,对开发起到了很好的作用。
  • 拍脑门定工期的方式真的是后患无穷,但也没有更有说服力和科学依据的更好方式,头疼,系统分析这块当年没学好,就是学好了这么多年的发展也用不得了。

小结:很辛苦,但有所得,也很快乐,按照葛优的话说就是即完成了任务,又锻炼了队伍,呵呵。

与所有默默开垦盐碱地的同志们同勉,并对给予我默默支持的家人和同事致以无限感激。

ADOdb的数据字典功能及其它

在新项目中继续使用ADOdb,这个是没什么悬念的了,尝试过php5自带的pdo,感觉一般,对某些数据库的支持更一般。但是数据库结构如何维护这块放不下心,从以往的开发中看,系统开发维护期超过半年之后,如果在数据库schema管理上不下点功夫,在生产服务器和多个测试服务器以及sql结构之间,难免会产生schema不一致的情况,管理情况再差的,连最后一稿schema是哪个、以及schema注释都能丢失。所以,无意中瞥到ADOdb还有一个ADOdb Data Dictionary Library for PHP以后,就想看看这个能否用在多环境数据库schema同步方面,连同自己了解的一些其它情况,记录在这里。

ADOdb Data Dictionary Library for PHP

可以用简单的字符串或者数组的方式记录数据表的结构,然后调用相关功能来生成修改数据库的sql语句,并执行之。应该是在创建数据表方面还是非常方便的,虽然数据类型简单了一些,执行和调用也略显麻烦(抱歉我的确是这么认为的),但在简单的应用中,尤其是数据表修改不是很多的情况下,应该会很好用。

ADOdb XML Schema (AXMLS)

这个其实也是包含在ADOdb中的,其介绍也是直接列在了ADOdb Data Dictionary Library for PHP同一页的下方。在仔细把这段并不多的内容读完之后,我是半喜半忧,喜的是AXMLS这个东西可真好啊,相当于能够把整个数据库的schema xml化了,然后对实体数据库进行更新;忧的方面一是,在最新的ADOdb5.04当中,依然没有完全实现AXMLS中的全部功能,我建了一个简单的xml,其中带有一句:

<opt platform="mysql">TYPE=INNODB</opt>

然后在一个空的数据库上执行,输出sql,发现CREATE TABLE的sql中并没有带上ENGINE=InnoDB,所以就停止了尝试;忧的方面二是这么好的东西,它的官方网站上最新的消息却是2004年6月17号的,一个软件不可能在4年中一点都没有改动,况且那时候还没有php5呢吧。在互联网其它方面来搜索这个AXMLS,发现只是在adodb中有,并没有在其它项目中得到运用,可惜了了。

一些商业软件的实现

很多商业软件的数据库schema管理一般都在case工具中,也有生成sql甚至直接对数据库应用schema变更的功能,比如我以前使用的PowerDesigner,不仅是最好的数据库实体模型设计工具,而且设计完成后,直接连上数据库进行更新。也可以反过来,从实体数据库中分析出数据库模型。

东西是好东西,可用了一段时间不再想用下去了,原因是多方面的,一是Sybase的东西bug总是很多,有时候莫名其妙的错误提示很头疼,二是数据库大了以后,每次连线diff的时候都很慢,最要命的是,它在更改column时喜欢用临时表,然后删除原表建新表,再从临时表中把数据读回来,一旦这个过程由于数据库兼容问题或者其它原因被中断,下次再同步的时候,很有可能数据库结构更新了,而数据却丢失了,因为它有自动删除临时表的选项,并且一般大家还都需要打开这个选项;最后一个原因,这玩意儿既没有Linux版本,我也没有正版的授权,所以能下岗就下岗了。

自己用简单的脚本来实现

其实在实际的应用开发中,数据库相关要修改的地方会很多,不仅有表、索引、视图等数据库本身的元素,还可能会有一些数据的调整和处理,比如将表中的A、B字段去掉,他们的值相加存储在一个新的C字段中,如果说前者应该会有成型的工具能够实现的话,像后面所说的这种数据库,或者更准确一点说是数据调整在任何工具中都不会涉及到的。

反过来想,无论是对数据库如何进行操作,无外乎就是DDL数据库定义语言和DML数据库管理语言,也就是通过大家常说的SQL语句来操作,那么把这些简化为一个SQL语句集合,就可以将开发过程中所有的数据库变动转化为这一系列SQL语句的管理和执行,设计这样一个工具不就得了么?

我现在就是这么作的,在svn中除了有保存数据库最终状态的schema sql文件之外,还专门有一个php文件,用数组记录每一次需要对数据库调整的SQL,然后有一个执行记录表,记录哪些SQL执行过而哪些没有。这个执行记录表在每个数据库实例中都会创建,这样,我只需要简单的调整数据库连接参数,然后就可以通过执行php代码,依次逐个的执行每个SQL了。

有一个缺点,就是SQL语法太依赖具体的数据库类型了,没办法,好在更换数据库的情形并不多,平时尽量写标准的语法吧。另外真要转换数据库的时候,还可以另起炉灶嘛。

其它的开源软件都是怎么实现的呢?

我阅读过的开源软件源码并不多,但软件还是用了一些,在有些软件升级的时候,就会自动将数据库也升级了,他们都是怎么实现的呢?难道说只是在1.0升级到2.0的升级程序upgrade.php中,固定的、一次性执行数据库修改么?

关于同类数据备份和迁移

以前一个朋友用oracle数据库的时候,特别喜欢用tora(他管它叫蛤蟆,因为软件的about页上有只蛤蟆),但我却认为,在同类数据库,尤其是版本、配置相同的数据库之间进行数据转移,方便一点的方法是利用数据库的导出、导入功能,这样不会有DML覆盖范围以外的内容拉下,简单一点的,自己写个小程序,连上两个数据库,这边读来那边写,也不会慢到哪里去,想提前删个索引什么的也好控制。tora一类的工具,应该是在灌初始字典库的时候用最合适。另外,不是还有bcp么?

参考

adodb5连接sybase的一个错误

这个错误比较蹊跷,所以拿出来说一说。环境:php 5.2.3, adodb 5.04, ubuntu 7.10 Gutsy.

前两天为了使用adodb和sqlite3,就把adodb升级成了只支持php5的adodb5(5.0.4),过两天发现另外一个使用sybase的程序不正常了,运行时不工作直接退出,没有任何错误信息,通过添加die('hi');的方式定位到错误发生在$db = &ADONewConnection('sybase');这一行。

adodb对sybase的支持一直不强,不过还没有到不工作的地步,但这个错误没有任何提示,实在是不好找,无奈之下用ZendStudio来跟踪一下,弄了一个最简单的小程序,当然,它是不工作没错误直接结束:

#! /usr/bin/php
<?php
require_once('adodb/adodb.inc.php');
$db = &ADONewConnection('sybase');
print_r($db);
$db->Connect('server3', 'sa', '', 'dbname');
$rs = $db->Execute('select 1');
print_r($rs);
?>

ZendStudio下Debug就报错了:

Compile Error: /home/fwolf/dev/include/adodb5/drivers/adodb-sybase.inc.php line 271 - Cannot make static method ADOConnection::UnixDate() non static in class ADODB_sybase

拿着错误信息上网一查,原来是在adodb-sybase.inc.php文件中,180行开始的地方定义了两个函数UnixDate()和UnixTimeStamp()(提示错误在271行,271行是类ADODB_sybase的定义结束位置,所以这个错误是在代码编译是产生的,而不是运行时),而这两个函数在adodb.inc.php中是作为static函数定义的(2481、2505行),php5不允许覆盖static函数(这个在oop中好像是叫重载,太长时间没摸书本,记不清了),所以产生编译错误,程序中止。在adodb.inc.php的4089行,包含adodb-sybase.inc.php的时候又加上了@符号:

@include_once($file);

所以错误信息被屏蔽,不显示了。ZendStudio由于Debug的原因,可能所有的错误都捕捉到了,忽略@。去掉@之后,直接运行就也可以报错了。

问题弄清楚了,如何改呢,我想大概有两种方式:

  1. 修改adodb.inc.php,不将UnixDate()函数定义为static。
  2. 修改adodb-sybase.inc.php,取消两个函数的重复定义。

相比之下,觉得第一种方法更好一些,第二种方法可能会引发其它的错误。所以改下来一共是去掉四个static:

Line 2481: ADOConnection::UnixDate()
Line 2505: ADOConnection::UnixTimeStamp()
Line 3204: ADORecordSet::UnixDate()
Line 3215: ADORecordSet::UnixTimeStamp()

简单测试了一下,基本工作正常。

参考

PS: ZendStudio使用单独的一套php.ini,放在/path/to/ZendStudio-5.5.0/bin/php5目录下,我总是忘记,每次include path不对劲的时候都要找半天。

PDO和sqlite的一点体会

用php写了一个小工具,顺便体验了下PDO和sqlite,一个是php5自带的数据库层,一个是简易的文件型数据库,没什么章法,简单记录在这里。

  • 配合pdo使用,只用安装php5-sqlite即可,php5-sqlite3这个extension可能是单独的sqlite支持,就是类似mysql,有专门的sqlite_connect函数。
  • 系统中也可以安装单独的sqlite3(不带3的是sqlite2),采用类似mysql的shell方式管理库文件。
  • 通过PDO好像无法查询数据库的结构等信息,只能操作DML。
  • 不知道pdo使用sybase(dblib)的时候是否能在sql中使用limit,以及使用了是否有效,要到hardy 8.04 php5-sybase才支持pdo,到时候再试验。
  • Bug: rowCount()无法工作于pdo_sqlite数据库,其他一些函数也有小问题,官方文档的user comment中有说到。
  • PDO的exec方法可以一次执行多条sql,但不返回结果,而query则只能执行一条,返回结果信息。
  • 使用PDO的prepare和PDOStatement的execute不仅有助于加速同一sql不同参数的调用速度,还有助于防止sql注入攻击。
  • prepare的:name不能是表名,大概只能是where中的变值,prepare中的字符型:name不需要带上引号,这一点很方便。

总体感觉,PDO还没有AdoDb方便,大概是用熟了的感觉,但功能上的确要少一些。sqlite数据库表现不错,但由于数据类型、结构、功能相对简单,还是主要用在小型、简单一点的应用里更合适。

Update @ 2008-03-23

来一个使用PDO调sqlite库的例子:

try {
    $db = new PDO("sqlite:/path/to/db/file", null, null, array(PDO::ATTR_PERSISTENT => true));
} catch (PDOException $ex) {
    die("[Error] " . $ex->getMessage() . "\n");
}

// Useless but carefully is better, set encoding.
// Sqlite always use utf-8 internaly, UTF-8 is also it's default value.
$db->query('PRAGMA encoding = "UTF-8"');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// Begin query
$stmt = $db->query("PRAGMA table_info({$tbl})");
// PHP bug: rowCount() not work with pdo_sqlite
if (0 == count($stmt->fetchAll())) {
    // Table not exists or have no columns
    ......

另外在使用ADODB的时候,它默认好像并不直接支持sqlite3,而是sqlite2(ADODB版本为V5.04 13 Feb 2008 (c) 2000-2008 John Lim (jlim#natsoft.com)),所以只能通过pdo来实现(即ADODB中使用的数据库类型为pdo),可pdo在这个版本的ADODB中支持也不是很全面,drivers目录下甚至没有adodb-pdo_sqlite.inc.php文件(网上搜了一下,这个文件似乎在某些地方有),所以,只能这样用了:

//ADODB设定
$ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;

try
{
    $conn = &ADONewConnection('pdo');
    @$conn->Connect('sqlite://path/to/db/file');
}
catch (Exception $e)
{
    adodb_backtrace($e->getTrace());
    exit();
}

// Check create table, adodb&sqlite3 not work very well, MetaTables() un-usable
//$rs = $conn->MetaTables();
$rs = $conn->Execute("SELECT name FROM sqlite_master WHERE type='table' AND name='my_table_name'");
if (1 > $rs->RecordCount())
{
    // Table does not exists
    ......

Connect前面的@如果去掉,会有Notice错误出现:

Notice: Undefined property:  ADODB_pdo_base::$_genIDSQL in /home/fwolf/dev/php_includes/adodb5/drivers/adodb-pdo.inc.php on line 106

Notice: Undefined property:  ADODB_pdo_base::$_dropSeqSQL in /home/fwolf/dev/php_includes/adodb5/drivers/adodb-pdo.inc.php on line 108

所以,只能说是ADODB对sqlite3的兼容性还不是很好。