Release Notes v.200.30 Third Alpha
8 July 2005
目 录
概述
新特性
API更改
语言改进:
语言改进概述
数据定义语言(DDL)
数据描述语言(DML)
存储过程语言(PSQL)
保留字
索引改进
优化
字符改进
安全
配置
实用工具
UDF
BUG修复记录 [/b]
Firebird 2.0概述
注意!
Alpha 3的ODS(数据库的磁盘存储结构)已经与Alpha 1及Alpha 2完全不兼容。这意味着在Alpha 1及Alpha 2下创建的任何数据库,将不能在Alpha 3下使用(是的,你被警告!)
注意,这也意味着将Alpha 3安装到已安装的Alpha 2的目录,已有安全数据库将不能被读取。(例如,在WIN32的卸载时,保留安全数据库)
如果您想继续使用Alpha 2的数据库,在安装Alpha 3之前,备份您的数据库。
如果您在覆盖安装之前,不明智地忽略了此文档,您可以用“回溯”的方式回到您可以备份的状态。也就是说,卸载Alpha 3,查找Alpha 2,重新安装它,备份数据,最后再安装Alpha 3。
再次, 我们提醒您,Firebird 2的这个早期发布版本是测试版。测试它直到它终止, 但别将它投入生产用途,并且不要尝试应用它在你关心的任何数据库!
所有新变动和新特性是基于进一步的更改及/或撤消随后Alpha和Beta版本,以达成主要的最终发行版。别指望这个Alpha版建立的数据库或升级的ODS会兼容以后的测试版/发行版。
Bug报告及支持
此alpha的目标是发现Bug和“gotchas”。在Firebird项目的网页上,阅读指导文章《如何有效地报告Bug》,请提出您的观点。
在测试此软件时,请遵循如下指导方针:
1、写详细的臭虫报告, 确切提供您的Firebird服务器模式、版本号及套件。并且提供操作系统平台的细节。你的报告,包括可再现的测试数据,并且更新它到我们的“测试跟踪范围”页面。对于唯一稳定发行版,不要更新报告到主要的“Bug跟踪“。
2、如果您想开始一个关于Bug或实现的讨论线程,这么做:订阅、提交给测试人员列表或直接给开发人员列表。
3、如果您是一个Firebird新手,并且需要“新手”建议,我们推荐您不要从这里开始您的经验。下载稳定的1.5发行套件自学,并且使用Firebird 1.5快速上手指南、Firebird支持列表帮助你入门。
4、在Alpha版里,不要使用普通的Bug跟踪或Firebird支持列表报告Bug,或者新技术运作的扩展细节。
5、考虑加入您的区域(语言)正式的测试小组,细节和联系方式在Firebird开发者角落的问题解答部分。
祝您测试愉快!
Firebird项目组
新特性及改进
衍生表(Derived tables) A. Brinkman
如同 SQL200X定义的,在DSQL里实现并支持衍生表(FROM条件的子查询)。一个衍生表是一个来源于动态SELECT语句的数据集。如果需要,衍生表能被用来建立复合查询,并且能被各种联接语句包含使用,就如同普通表或视图一样。
语法形式
SELECT
<select list>
FROM
<table reference list>
<table reference list> ::= <table reference> [{<comma> <table reference>}...]
<table reference> ::=
<table primary>
| <joined table>
<table primary> ::=
<table> [[AS] <correlation name>]
| <derived table>
<derived table> ::=
<query expression> [[AS] <correlation name>]
[<left paren> <derived column list> <right paren>]
<derived column list> ::= <column name> [{<comma> <column name>}...]
范例
a.衍生表例子
SELECT
*
FROM
(SELECT
RDB$RELATION_NAME, RDB$RELATION_ID
FROM
RDB$RELATIONS) AS R (RELATION_NAME, RELATION_ID)
b.基于衍生表之上的聚集并且包含一个聚集
SELECT
DT.FIELDS,
Count(*)
FROM
(SELECT
R.RDB$RELATION_NAME,
Count(*)
FROM
RDB$RELATIONS R
JOIN RDB$RELATION_FIELDS RF ON (RF.RDB$RELATION_NAME = R.RDB$RELATION_NAME)
GROUP BY
R.RDB$RELATION_NAME) AS DT (RELATION_NAME, FIELDS)
GROUP BY
DT.FIELDS
c.联合与排序例子
SELECT
DT.*
FROM
(SELECT
R.RDB$RELATION_NAME,
R.RDB$RELATION_ID
FROM
RDB$RELATIONS R
UNION ALL
SELECT
R.RDB$OWNER_NAME,
R.RDB$RELATION_ID
FROM
RDB$RELATIONS R
ORDER BY
2) AS DT
WHERE
DT.RDB$RELATION_ID <= 4
要点:
● 衍生表的每列必须有名称。未命名的表达式(比如常数)必须起一个别名,或者使用列的列表。
● 列的列表数目应该与查询表达式里的列表数目相同。
● 优化引擎能极有效率地处理一个衍生表。然而,如果衍生表涉及一个内联接并且包含一个子查询,那么,不能做联接排序。
Win32原生协议被再实现 D. Yemanov
现在,XNET在Windows下默认使用原生协议,并且也支持连接到经典服务器模式。
垃圾收集已经重做 V. Horsun
新的GC线程实现及组合协作+后台激活。更多信息。
在原始设备上存储数据库 E. Kunze,
N.Samofatov
现在,你能在原始设备上存放数据库,并使用数据库别名来引用设备。更多信息。
经典模式(Classic)现在支持所有API服务 N. Samofatov
向经典模式移植API服务已完成。现在所有API服务函数在Linux与Windows的经典模式服务器上都可使用。更多信息。
约束检查逻辑现已重做 V. Horsun
更精确地检查PK/UK/FK(主键/唯一键/外键)约束。更多信息。
等待事务的加锁超时 A. Karyakin,
D. Yemanov
为等待事务增加锁定超时(参见新的TPB值isc_tpb_lock_timeout)。更多信息。
重新实现LIKE/CONTAINING/STARTING操作符 N. Samofatov
1、操作符现在已能正确操作大二进制(BLOB)数据
2、现在模式匹配使用一种single-pass Knuth-Morris-Pratt算法
3、当LIKE里使用NULL或ESCAPE字符时,引擎不再出故障
更多信息。
更新视图逻辑已重做 D. Yemanov
解决了视图隐含更新的问题,但仍然有更新触发器。更多信息。
新的数据库停工方式 N. Samofatov
单用户和完全停工方式已经实现。更多信息。
ODS更改 Various contributors
● Firebird 2用ODS(数据存储文件结构)的第11个版本来创建数据库。
● 异常消息的最大宽度从72字节提高到1021个字节。(V. Horsun)
● 添加RDB$DESCRIPTION到RDB$GENERATORS里,所以现在你能在创建生成子时包含描述文本。(C. Valderrama)
● 添加RDB$DESCRIPTION及RDB$SYSTEM_FLAG到RDB$ROLES,允许分别描述文本和标记用户定义规则。(C. Valderrama)
● 引进ODS类型的概念,能够区分Interbase与Firebird数据库。Firebird创建调试版本数据库和私有分支。(N. Samofatov)
● 更多信息
UDF(用户自定义函数)改进 C. Valderrama
● 能通过空指针(NULL)发送SQL NULL信息。(更多信息)
● 外部函数库ib_udf升级,允许字符串函数ASCII_CHAR、LOWER、LPAD、LTRIM、RPAD、RTIM、SUBSTR和SUBSTRLEN的返回值为NULL,并能正确解析。有了这些函数的声明,ib_udf_upgrade.sql这个脚本能被应用到pre-v.2数据库,升级它们使之能在升级库上工作。此脚本仅在如下情况使用:当你在Firebird 2.0里使用新版ib_idf函数库,并且操作请求改进的预期空值。
更多信息...
更改WNET协议 D. Yemanov
WNET(aka NetBEUI)协议不再扮演客户端。更多信息。
比较有效地检查串联溢出 O. Loa,
D. Yemanov
编译时检查串联溢出,被替换则由运行时检查。
更改同步逻辑 N. Samofatov
1、超级服务器模式下,在线程共享管理器的锁管理器里,锁资源争夺已经显著减少。
2、一种罕见的紊乱情况被检测并修复,它可以导致超级服务器模式在请求期间悬挂处理直到收到下一个请求。
3、锁管理器的内存堆已经改为拥有更多信息,并且OWN_hung可以正确检测到。
4、为不同的引擎实例分离锁管理器同步对象已经实现。
杂项
● 64位支持,包括ODS检测。(N. Samofatov)
● 引进40位(内部64位)记录数,消除30G表格容量限制。(N. Samofatov)
● BUG检查日志消息现在包含文件名与在线数字。(A. Brinkman)
● 便利的线程安全和信号安全调试日志已经实现。(N. Samofatov)
● 打印输出各种内置结构(DSQL结点树、BLR、DYN等)的例程已升级。(N.Samofatov)
● 为了修复一些递归过程的问题,PSQL和请求克隆逻辑里的不变式跟踪已被重做,例子SF bug#627057。(N. Samofatov)
● Posix的超级服务器版本现在能优雅地处理SIGTERM和SIGINT,关闭所有连接。(A. Peshkov)
● 更多信息。
Firebird 2的API更改
扩展的isc_dsql_info() API 呼叫 D. Yemanov
如果必要,函数呼叫isc_dsql_info()已被扩展为使连接别名被检索到。
API 识别客户端版本 N. Samofatov
在ibase.h里,对于Firebird 2.0,C/C++客户端版本FB_API_VER被定义为20。更多消息。
一般语言增强
错误报告 C.Valderrama
DSQL词法分析器现在将回报告一个完成语句的行号和列号。
数据定义语言(DDL)
CREATE SEQUENCE D.Yemanov
为了与SQL-99语法一致,SEQUENCE作为GENERATOR的同义词被提出。在SQL规范的描述中,SEQUENCE是一个语法术语,而GENERATOR是一个遗留下列的InterBase的语法术语。建议在你的应用程序中使用标准的SEQUENCE语法。
序列生成器是一个生产连续的精确的数字值的机制,一次一个数值。一个序列生产器是一个命名的计划对象。在dialect 3 语法下它是一个 BIGINT 类型,在dialect 1 语法下它是一个 INTEGER 类型。
语法格式:
CREATE { SEQUENCE | GENERATOR } <名字>
DROP { SEQUENCE | GENERATOR } <名字>
SET GENERATOR <名字> TO <起始值>
ALTER SEQUENCE <名字> RESTART WITH <起始值>
GEN_ID (<名字>, <增长值>)
NEXT value FOR <名字>
例子
1.
CREATE SEQUENCE S_EMPLOYEE;
2.
ALTER SEQUENCE S_EMPLOYEE RESTART WITH 0;
请参阅 NEXT value FOR 的说明。
REVOKE ADMIN OPTION FROM D. Yemanov
系统管理员(SYSDBA),数据库建立者 (creator) 或 对象的所有者 (owner) 可以将在这个对象上的权限授予给其他用户。任何时候,这些权限都是可以被继承的。依次通过使用 WITH GRANT OPTION ,授权者赋予权限给被授权者,使被授权者成为同样权限的授权者。这个功能可以由原始授权者通过使用 REVOKE GRANT OPTION FROM <用户> 来清除。
任何时候,都有第二个方法来处理角色。你可以建立一个角色,给这个角色赋予一组权利,然后将这个角色授予一个或多个用户,使用这个方法来代替指定同样的权限给多个用户(维护用户很快就成为一个恶梦)。对于这个角色的权限的任何改变都作用于这些用户。
通过使用 WITH ADMIN OPTION,授权者(典型的是建立者)赋予被授权者权利,在这种机制下被授权者成为同样的授权者角色。在FB 2.0版本以前,如果原始的授权者不直接对系统表进行操作,这种继承授权的能力是不能去掉的。现在,这种对角色授权的继承能力可以通过原始授权者对用户使用 REVOKE ADMIN OPTION FROM 就可以清除掉。
视图更新逻辑的改变 D. Yemanov
只对基表应用 NOT NULL 约束,忽略视图列继承来自域的定义。
RECREATE EXCEPTION and CREATE OR ALTER EXCEPTION D. Yemanov
实现了 DDL 语句 RECREATE EXCEPTION 和 CREATE 或者 ALTER EXCEPTION (这个需要 SF #1167973),允许建立、重新建立或者修改一个异常,这依赖它是否存在:
如果定义的异常不存在,RECREATE EXCEPTION 和 CREATE EXCEPTION 的作用完全一样。假如异常存在,如果没有与它相依赖的事情,异常的定义将被完全替换。
假如异常不存在的话,CREATE 或 ALTER EXCEPTION 将建立异常,如果存在的话将会不影响任何依赖地修改异常的定义。
Known "friendly names" (mnemonics) can now be used for declaring
BLOB filter sub_types A. Peshkov
Previously, the only allowed syntax for declaring a blob filter was:
declare filter <name> input_type <number> output_type <number>
entry_point <function_in_library> module_name <library_name>;
The alternative new syntax is:
declare filter <name> input_type <mnemonic> output_type <mnemonic>
entry_point <function_in_library> module_name <library_name>;
where <mnemonic> refers to a subtype known to the engine.
Initially they are binary, text and others mostly for internal usage, but an adventurous user could
write a new mnemonic in rdb$types and use it, since it is parsed only at declaration time. The
engine keeps the numerical value. Remember, only negative subtype values are meant to be
defined by users.
To get the predefined types, do
select RDB$TYPE, RDB$TYPE_NAME, RDB$SYSTEM_FLAG
from rdb$types
where rdb$field_name = 'RDB$FIELD_SUB_TYPE';
RDB$TYPE RDB$TYPE_NAME RDB$SYSTEM_FLAG
========= ============================ =================
0 BINARY 1
1 TEXT 1
2 BLR 1
3 ACL 1
4 RANGES 1
5 SUMMARY 1
6 FORMAT 1
7 TRANSACTION_DESCRIPTION 1
8 EXTERNAL_FILE_DESCRIPTION 1
Examples
Original declaration:
declare filter pesh input_type 0 output_type 3
entry_point 'f' module_name 'p';
Alternative declaration:
declare filter pesh input_type binary output_type acl
entry_point 'f' module_name 'p';
Declaring a name for a user defined blob subtype (remember to commit after the insertion):
SQL> insert into rdb$types
CON> values('RDB$FIELD_SUB_TYPE', -100, 'XDR', 'test type', 0);
SQL> commit;
SQL> declare filter pesh2 input_type xdr output_type text
CON> entry_point 'p2' module_name 'p';
SQL> show filter pesh2;
BLOB Filter: PESH2
Input subtype: -100 Output subtype: 1
Filter library is p
Entry point is p2
建立外键约束不再需要独占访问 V. Horsun
现在,可以在不需要对整个数据库进行独占锁定的情况下建立外键约束。
ALTER EXTERNAL FUNCTION C. Valderrama
ALTER EXTERNAL FUNCTION has been implemented, to change the entry_point or the
module_name when the UDF declaration cannot be dropped due to existing dependencies.
COMMENT 语句 C. Valderrama
COMMENT 语句,用来设置元数据描述,现在已经被实现了。
语法格式:
COMMENT ON DATABASE IS {'txt'|NULL};
COMMENT ON <basic_type> name IS {'txt'|NULL};
COMMENT ON COLUMN tblviewname.fieldname IS {'txt'|NULL};
COMMENT ON PARAMETER procname.parname IS {'txt'|NULL};
一个空字符串 '' 将被视为 NULL 当内码。(DYN IN THIS CASE)works this way with blobs。
其中:<basic_type>:可以是:DOMAIN,TABLE,VIEW,PROCEDURE,TRIGGER, EXTERNAL FUNCTION,FILTER,EXCEPTION,GENERATOR,SEQUENCE,INDEX,ROLE, CHARACTER SET,COLLATION,SECURITY CLASS(没有实现,因为这个类型是隐藏的)。
数据类型
BLOB SUB_TYPE BINARY C. Valderrama
作为 SUB_TYPE 0 的同义词被引入。
数据操纵语言(DML)
EXECUTE BLOCK 语句 V. Horsun
SQL语言的扩展语句 EXECUTE BLOCK 使得在 SELECT 语句规范中可以使用“动态 PSQL”。它有这样的效果,如果是存储过程中的话,将允许一个PSQL自包含代码动态SQL语句中执行。
语法格式:
EXECUTE BLOCK [ (param datatype = ?, param datatype = ?, ...) ]
[ RETURNS (param datatype, param datatype, ...) }
AS
[DECLARE VARIABLE var datatype; ...]
BEGIN
...
END
对于客户端,通过使用参数 isc_info_sql_stmt_type 调用 call isc_dsql_sql_info 返回结果。
●如果程序块有输出参数使用:isc_info_sql_stmt_select,这在语法上类似一个 SELECT 查询:客户端打开一个游标,可以从中获取数据,使用后必须关闭。
●如果程序块没有输出参数使用isc_info_sql_stmt_exec_procedure ,这在语法上类似执行 EXECUTE 查询:客户端没有游标并且一直执行直到程序块结束或者被 SUSPEND 终止。
客户端预处理只进行SQL 语句开始或者使用 ‘?’代替‘:’作为参数标识符预处理,因为,在程序块内,使用冒号前缀将被引用为局部变量或参数。
例子:
用户 SQL 为
EXECUTE BLOCK (X INTEGER = :X)
RETURNS (Y VARCHAR)
AS
DECLARE V INTEGER;
BEGIN
INSERT INTO T(...) valueS (... :X ...);
SELECT ... FROM T INTO :Y;
SUSPEND;
END
经过预处理的 SQL 为
EXECUTE BLOCK (X INTEGER = ?)
RETURNS (Y VARCHAR)
AS
DECLARE V INTEGER;
BEGIN
INSERT INTO T(...) valueS (... :X ...);
SELECT ... FROM T INTO :Y;
SUSPEND;
END
ROWS 语法 D. Yemanov
ROWS 语法用来限制从一个查询短语中返回的行数。对于一个高级的查询语句,它将要求为主程序返回指定数量的行。更多理解可以参考 FIRST/SKIP 子句, ROWS 语法与最近的 SQL 标准保持一致,并带来了额外的好处。它可以在联合,任意类型的子查询中使用,也可以在 UPDATE 或者 DELETE 语句中使用。
它在 DSQL 和 PSQL 中同时有效。
语法格式
SELECT ...
[ORDER BY <expr_list>]
ROWS <expr1> [TO <expr2>]
例子:
1.
SELECT * FROM T1
UNION ALL
SELECT * FROM T2
ORDER BY COL
ROWS 10 TO 100
2.
SELECT COL1, COL2,
( SELECT COL3 FROM T3 ORDER BY COL4 DESC ROWS 1 )
FROM T4
3.
DELETE FROM T5
ORDER BY COL5
ROWS 1
注释点
1、当 <expr2> 省略时,ROWS <expr1> 在语法上等于 FIRST <expr1>。当 < expr1> 和 <expr2> 同时使用时,ROWS <expr1> TO <expr2> 意味着和这个语句相同: FIRST (<expr2>-<expr1>+1) SKIP (<expr1> - 1)
2、没有在语法等同与不使用 FIRST 子句而单独使用 SKIP 子句的功效。
UNION DISTINCT 语法能够使用 D. Yemanov
为了与 SQL-99 规范保持一致,UNION DISTINCT 现在允许作为一个简单的 UNION 的同义词使用。将来提供更多信息
新的 DISTINCT 相等比较判断(NULL=NULL)作为真 O. Loa,D. Yemanov
语法格式
<value> IS [NOT] DISTINCT FROM <value>
例子:
1.
SELECT * FROM T1
JOIN T2
ON T1.NAME IS NOT DISTINCT FROM T2.NAME;
2.
SELECT * FROM T
WHERE T.MARK IS DISTINCT FROM 'test';
注释点:
1、因为 DISTINCT 判断时认为两个 NULL 值不是不同的,它从不计算 UNKNOWN 的实际值。就像 IS [NOT] NULL 判断一样,它只能是真或者假。
2、NOT DISTINCT 判断可以被索引优化,假如其中一个有效的话。
NULL 现在可以在语法作为一个值来使用 D. Yemanov
你现在可以指定 A=NULL,B>NULL,等等。(所有这些计算结果都等于 FALSE)。更多信息将来提供。
视图语法规范的扩展 D. Yemanov
FIRST/SKIP 与 ROWS 语法和 PLAN 与 GROUP BY 子句现在可以使用到视图规范中了。更多的信息将来提供。
实现了 CROSS JOIN D. Yemanov
现在支持了 CROSS JOIN 了。逻辑上,语法格式为:
A CROSS JOIN B
等于下面中的一个:
A INNER JOIN B ON 1=1
或者,简单的:
FROM A,B
更多的信息将来提供。
子查询和 INSERT 语句现在可以使用联合数据集 D. Yemanov
在子查询和 INSERT INTO SELECT ……中的 SELECT 规范中现在可以使用一个 UNION 数据集了。更多的信息将来提供。
联合中的类型强制逻辑改善 A. Brinkman
联合中的数据子集之间的自动类型强制逻辑现在更加智能化了。决定 兼容数据类型的聚合处理后的结果的值数据类型的确定,现在使用更灵活的规则,像分支表达式以及联合查询中表达式中的相同位置的列等。
语法规则
1、DTS设置的数据类型将包容我们必须自行决定的结果数据类型。(译者注:DTS--数据类型转换处理系统)
2、所有的数据类型在DTS中都是可以比较的。
3、分下列情况:
A、假如DTS中任意一个数据类型是字符串,那么:
i、假如 DTS 中间有任意一个数据类型是可变字符串,那么结果数据类型是可变字符串类型,并且字符串的最大长度等于 DTS 中最长的字符串长度。
ii、其他情况下,结果数据类型是定长字符串,并且字符串的长度等于 DTS 中最长的字符串的长度。
iii、字符集和collation使用 DTS 中遇到的第一个字符串数据类型的字符集和collation。
B、如果 DTS 中的所有数据类型都是精确数字,那么结果数据类型也是精确数字,并且范围等于 DTS 中数据类型最大的范围并结合 DTS 中最大的精度。
注释:只是在运行时检查精度越限,作为开发者应该进行估计避免因聚合解析作用而导致的精度越界。
C、假如 DTS 中的数据类型任意一个是近似数,那么 DTS 中的每一个数据类型都应是数字类型,否则就会报错。
D、如果 DTS 中的数据类型一些是 datetime 数据类型,那么 DTS 中的每个数据都应是 datetime 数据类型并具有相同的 datetime 类型。
E、如果 DTS 中任意一个数据类型是 BLOB,那么 DTS 中的每一个数据类型都应是 BLOB 并且拥有同样的子类型。
UPDATE 和 DELETE 语句语法扩展 O. Loa
ROWS 规范和 PLAN 与 ORDER BY 子句现在可以使用在 UPDATE 和 DELETE 语句中。更多的信息将来提供。
————————————————————————————————————————
环境变量 ROW_COUNT 返回查询计数 D. Yemanov
ROW_COUNT 现在可以返回 SELECT 语句返回的行的数量。更多的信息将来提供。
通过系统函数获取环境变量 N. Samofatov
环境变量的值现在可以使用系统函数 RBD$GET_CONTEXT 和 RDB$SET_CONTEXT 取得。这些新的内建函数提供了通过 SQL 访问一些关于当前连接和当前事务的信息的途径。它们同样提供了一个返回用户环境变量和与它相关连的事务和连接的机制。
语法格式
RDB$SET_CONTEXT( <namespace>, <variable>, <value>)
RDB$GET_CONTEXT( <namespace>, <variable> )
这些函数的真正格式是一个存在于数据库内部的外部函数,它在调用时从一个动态载入库载入。下面的定义时数据库引擎在数据库建立时自动建立的:
定义
DECLARE EXTERNAL FUNCTION RDB$GET_CONTEXT
VARCHAR(80),
VARCHAR(80)
RETURNS VARCHAR(255) FREE_IT;
DECLARE EXTERNAL FUNCTION RDB$SET_CONTEXT
VARCHAR(80),
VARCHAR(80),
VARCHAR(255)
RETURNS INTEGER BY value;
使用 RDB$SET_CONTEXT 和 RDB$GET_CONTEXT 设置和返回环境变量的当前值。一组具有类似属性的环境变量使用名字空间标识符。这个名字空间决定了使用规则,比如哪个变量可以被读取或写入,或依赖谁。
名字空间和变量名字是大小写敏感的。
●RDB$GET_CONTEXT 返回变量的当前值。如果在名字空间不存在这个变量,函数返回 NULL。
●RDB$SET_CONTEXT 设置指定变量的值,如果它是可写的。假如这个变量在调用前就存在这个函数返回值为 1 否则返回 0。
●要从环境中删除一个变量,把它的值设为 NULL。
预定义命名空间
一个固定个数的预定义名字空间有效:
●USER_SESSION 是一个提供访问指定会话的用户定义变量的名字空间。你可以为这个变量定义和设置值,并可以在这个环境中使用任意的名。字
●USER_TRANSACTION 是一个为单独事务提供类似可能值的名字空间。
●SYYTEM 名字空间提供下列只读变量:
变量名 值
NETWORK_PROTOCOL 客户端连接使用的网络协议。当前使用值有:"TCPv4", "WNET","XNET" 和 NULL。
CLIENT_ADDRESS 远程客户端的网络协议地址,描述为一个字符串。TCPv4 协议下 IP 地址的值格式为
"xxx.xxx.xxx.xxx";XNET 协议下为本地进程 ID;其他协议为 NULL。
DB_NAME 当前数据库的规范名字。它可以是别名(如果连接访问文件名被禁止
DatabaseAccess=NONE),也可以是完整的数据库文件的名字。
ISOLATION_LEVEL 当前事务的隔离级别。返回值将是"READCOMMITTED", "CONSISTENCY", "SNAPSHOT"中的一个。
TRANSACTION_ID 当前事务的数字ID。返回值与使用 CURRENT_TRANSACTION 假变量的返回值相同。
SESSION_ID 当前会话的数字ID。返回值与使用 CURRENT_CONNECTION 假变量的值相同。
CURRENT_USER 当前的用户。返回值与使用 CURRENT_USER 假变量返回值,或者预定义变量 USER 的值相同。
CURRENT_ROLE 当前连接的角色。返回值与 CURRENT_ROLE 假变量相同。
注释
为了避免依托Firebird服务器进行 DoS 攻击,存放在每一个事务或会话环境中的变量数量被限制在1000之内。
使用例子
create procedure set_context(User_ID varchar(40), Trn_ID integer) as
begin
RDB$SET_CONTEXT('USER_TRANSACTION', 'Trn_ID', Trn_ID);
RDB$SET_CONTEXT('USER_TRANSACTION', 'User_ID', User_ID);
end;
create table journal (
jrn_id integer not null primary key,
jrn_lastuser varchar(40),
jrn_lastaddr varchar(255),
jrn_lasttransaction integer
);
CREATE TRIGGER UI_JOURNAL FOR JOURNAL AFTER INSERT OR UPDATE
as
begin
new.jrn_lastuser = rdb$get_context('USER_TRANSACTION', 'User_ID');
new.jrn_lastaddr = rdb$get_context('SYSTEM', 'CLIENT_ADDRESS');
new.jrn_lasttransaction = rdb$get_context('USER_TRANSACTION', 'Trn_ID');
end;
execute procedure set_context('skidder', 1);
insert into journal(jrn_id) values(0);
commit;
当 rdb$set_context 返回 1 或零时,它可以使用在一个简单的 SELECT 语句里工作。更多的信息将来提供。
CURRENT_TIMESTAMP 和 'NOW' 现在返回毫秒 D. Yemanov
环境变量 CURRENT_TIMESTAMP 和当前日期时间 ‘NOW’ 现在将以毫秒返回次秒(sub-second)时间部分。更多的信息将来提供。
增加了内建函数 IIF() O. Loa
函数
IIF (<search_condition>, <value1>, <value2>)
作为CASE的快捷方式被实现
CASE
WHEN <search_condition> THEN <value1>
ELSE <value2>
END
如果给定的查找条件结果为真返回第一个子表达式的值,否则返回第二个子表达式的值。
例子
SELECT IIF(VAL > 0, VAL, -VAL) FROM OPERATION
内建函数 SUBSTRING() 增强 O. Loa,D. Yemanov
内建函数 SUBSTRING() 现在可以使用任意的表达式作为它的参数。更多的信息将来提供。
GROUP BY 任意表达式 A. Brinkman
GROUP BY 条件现在可以是是任何有效的表达式。
例子
...
GROUP BY
SUBSTRING(CAST((A * B) / 2 AS VARCHAR(15)) FROM 1 FOR 2)
空值排序改变 N. Samofatov
在一个排序的数据集放置空值现在修改为与 SQL 标准一直,也就是说空值的排序位置被固定。如果是 ASC[ENDING] 顺序则将它放在底部,如果是 DESC[ENDING] 在将它放在顶部;反之亦然。这只使用在数据库以新的 ODS 系统建立时有效,它需要使用索引改变顺序来工作。更多的信息将来提供。
Improvements in user-specified query plans D. Yemanov
1. Plan fragments are propagated to nested levels of joins, enabling manual optimization of complex outer joins
2. A user-supplied plan will be checked for correctness in outer joins
3. Short-circuit optimization for user-supplied plans has been added
4. A user-specified access path can be supplied for any SELECT-based statement or clause
Syntax rules
The following schema describing the syntax rules should be helpful when composing plans.
PLAN ( { <stream_retrieval> | <sorted_streams> | <joined_streams> } )
<stream_retrieval> ::= { <natural_scan> | <indexed_retrieval> | <navigational_scan> }
<natural_scan> ::= <stream_alias> NATURAL
<indexed_retrieval> ::= <stream_alias> INDEX ( <index_name> [, <index_name> ...] )
<navigational_scan> ::= <stream_alias> ORDER <index_name> [ INDEX ( <index_name> [,
<index_name> ...] ) ]
<sorted_streams> ::= SORT ( <stream_retrieval> )
<joined_streams> ::= JOIN ( <stream_retrieval>, <stream_retrieval> [,
<stream_retrieval> ...] )
| [SORT] MERGE ( <sorted_streams>, <sorted_streams> )
Details
Natural scan means that all rows are fetched in their natural storage order. Thus, all pages must be read before search criteria
are validated.
Indexed retrieval uses an index range scan to find row ids that match the given search criteria. The found matches are combined
in a sparse bitmap which is sorted by page numbers, so every data page will be read only once. After that the table pages are
read and required rows are fetched from them.
Navigational scan uses an index to return rows in the given order, if such an operation is appropriate.
The index b-tree is walked from the leftmost node to the rightmost one.
If any search criterion is used on a column specified in an ORDER BY clause, the navigation is limited to some subtree
path, depending on a predicate.
If any search criterion is used on other columns which are indexed, then a range index scan is performed in advance and
every fetched key has its row id validated against the resulting bitmap. Then a data page is read and the required row is
fetched.
Note that a navigational scan incurs random page I/O, as reads are not optimized.
A sort operation performs an external sort of the given stream retrieval.
A join can be performed either via the nested loops algorithm (JOIN plan) or via the sort merge algorithm (MERGE plan).
An inner nested loop join may contain as many streams as are required to be joined. All of them are equivalent.
An outer nested loops join always operates with two streams, so you'll see nested JOIN clauses in the case of 3 or more
outer streams joined.
A sort merge operates with two input streams which are sorted beforehand, then merged in a single run.
Examples
SELECT RDB$RELATION_NAME
FROM RDB$RELATIONS
WHERE RDB$RELATION_NAME LIKE 'RDB$%'
PLAN (RDB$RELATIONS NATURAL)
ORDER BY RDB$RELATION_NAME
SELECT R.RDB$RELATION_NAME, RF.RDB$FIELD_NAME
FROM RDB$RELATIONS R
JOIN RDB$RELATION_FIELDS RF
ON R.RDB$RELATION_NAME = RF.RDB$RELATION_NAME
PLAN MERGE (SORT (R NATURAL), SORT (RF NATURAL))
Notes
A PLAN clause may be used in all select expressions, including subqueries, derived tables and view definitions.
It can be also used in UPDATE and DELETE statements, because they're implicitly based on select expressions.
a.
If a PLAN clause contains some invalid retrieval description, then either an error will be returned or this bad
clause will be silently ignored, depending on severity of the issue.
b.
ORDER <navigational_index> INDEX ( <filter_indices> ) kind of plan is reported by the engine and can be used
in the user-supplied plans starting with FB 2.0.
DSQL parsing of table aliases is stricter A. Brinkman
Alias handling and ambiguous field detecting have been improved. In summary:
When a table alias is provided for a table, either that alias, or no alias, must be used. It is no longer valid to supply only the
table name.
a.
Ambiguity checking now checks first for ambiguity at the current level of scope, making it valid in some conditions for
columns to be used without qualifiers at a higher scope level.
b.
Examples
1、When an alias is present it must be used; or no alias at all is allowed.
This query was allowed in FB1.5 and earlier versions:
SELECT
RDB$RELATIONS.RDB$RELATION_NAME
FROM
RDB$RELATIONS R
but will now correctly report an error that the field "RDB$RELATIONS.RDB$RELATION_NAME" could not be
found.
Use this (preferred):
SELECT
R.RDB$RELATION_NAME
FROM
RDB$RELATIONS R
or this statement:
SELECT
RDB$RELATION_NAME
FROM
RDB$RELATIONS R
a.The statement below will now correctly use the FieldID from the subquery and from the updating table:
UPDATE
TableA
SET
FieldA = (SELECT SUM(A.FieldB) FROM TableA A
WHERE A.FieldID = TableA.FieldID)
Note :: In Firebird it is possible to provide an alias in an update statement, but many other database vendors do not
support it. These SQL statements will improve the interchangeability of Firebird's SQL with other SQL database products.
b.This example did not run correctly in Firebird 1.5 and earlier:
SELECT
RDB$RELATIONS.RDB$RELATION_NAME,
R2.RDB$RELATION_NAME
FROM
RDB$RELATIONS
JOIN RDB$RELATIONS R2 ON
(R2.RDB$RELATION_NAME = RDB$RELATIONS.RDB$RELATION_NAME)
If RDB$RELATIONS contained 90 records, it would return 90 * 90 = 8100 records, but in Firebird 2 it will correctly
return 90 records.
2.a.This failed in Firebird 1.5, but is possible in Firebird 2:
SELECT
(SELECT RDB$RELATION_NAME FROM RDB$DATABASE)
FROM
RDB$RELATIONS
b.Ambiguity checking in subqueries: the query below would run in Firebird 1.5 without reporting an ambiguity, but will
report it in Firebird 2:
SELECT
(SELECT
FIRST 1 RDB$RELATION_NAME
FROM
RDB$RELATIONS R1
JOIN RDB$RELATIONS R2 ON
(R2.RDB$RELATION_NAME = R1.RDB$RELATION_NAME))
FROM
RDB$DATABASE
GROUP BY 和 ORDER BY 子句的改善 A. Brinkman
现在可以在这些子句中使用别名了。
例子:
1.SELECT RDB$RELATION_ID AS ID
FROM RDB$RELATIONS
ORDER BY ID
2.SELECT RDB$RELATION_NAME AS ID, COUNT(*)
FROM RDB$RELATION_FIELDS
GROUP BY ID
NEXT value FOR 表达式 D. Yemanov
增加了顺应 SQL-99的表达式--NEXT value FOR,它作为 GEN_ID(<generator-name>,1) 的同义词,相应的引入了 SQL 标准的 CREATE SEQUENCE 语法作为等同于 CREATE GENERATOR 的语法。
例子
1.SELECT GEN_ID(S_EMPLOYEE, 1) FROM RDB$DATABASE;
2.INSERT INTO EMPLOYEE (ID, NAME)
valueS (NEXT value FOR S_EMPLOYEE, 'John Smith');
注释点
1.通常,Currently, increment ("step"> values not equal to 1 (one) can be used only by calling the GEN_ID function.
Future versions are expected to provide full support for SQL-99 sequence generators, which allows the required
increment values to be specified at the DDL level. Unless there is a vital need to use a step value that is not 1, use
of a NEXT value FOR value expression instead of the GEN_ID function is recommended.
2.GEN_ID(<name>, 0) allows you to retrieve the current sequence value, but it should be never used in
insert/update statements, as it produces a high risk of uniqueness violations in a concurrent environment.
RETURNING clause for INSERT statements D. Yemanov
The RETURNING clause syntax has been implemented for the INSERT statement, enabling the return of a result set from the
INSERT statement. The set contains the column values actually stored. Most common usage would be for retrieving the value of the primary key generated inside a BEFORE-trigger.
Syntax Pattern
INSERT INTO ... valueS (...) [RETURNING <column_list> [INTO <variable_list>]]
Available in DSQL and PSQL.
Example(s)
1.
INSERT INTO T1 (F1, F2)
valueS (:F1, :F2)
RETURNING F1, F2 INTO :V1, :V2;
2.
INSERT INTO T2 (F1, F2)
valueS (1, 2)
RETURNING ID INTO
Notes
1. The INTO part (i.e. the variable list) is allowed in PSQL only (to assign local variables) and rejected in DSQL.
2. In DSQL, values are being returned within the same protocol roundtrip as the INSERT itself is executed.
3.If the RETURNING clause is present, then the statement is described as isc_info_sql_stmt_exec_procedure by the API (instead of isc_info_sql_stmt_insert), so the existing connectivity drivers should support this feature automagically.
4. Any explicit record change (update or delete) performed by AFTER-triggers is ignored by the RETURNING clause.
5. Cursor based inserts (INSERT INTO ... SELECT ... RETURNING ...) are not supported.
6. Currently this clause can return only table column values, arbitrary expressions are not allowed. This is expected to be
introduced in the future versions.
UNIONs are allowed in ANY/ALL/IN subqueries D. Yemanov
The subquery element of an ANY, ALL or IN search may now be a UNION query.
存储过程语言(PSQL)
PSQL中的显示游标 D. Yemanov
现在,在PSQL中能够定义并使用多重游标. 显示游标与在存储过程和触发器中一样,能够在DSQL的EXECUTE BLOCK结构中使用.
语法格式:
DECLARE [VARIABLE] <游标名> CURSOR FOR ( < SELECT语句> );
OPEN <游标名>;
FETCH <游标名> INTO [, ...];
CLOSE <游标名>;
例子
1.
DECLARE RNAME CHAR(31);
DECLARE C CURSOR FOR
( SELECT RDB$RELATION_NAME FROM RDB$RELATIONS );
BEGIN
OPEN C;
WHILE (1 = 1) DO
BEGIN
FETCH C INTO :RNAME;
IF (ROW_COUNT = 0) THEN
LEAVE;
SUSPEND;
END
CLOSE C;
END
2.
DECLARE RNAME CHAR(31);
DECLARE FNAME CHAR(31);
DECLARE C CURSOR FOR
( SELECT RDB$FIELD_NAME FROM RDB$RELATION_FIELDS
WHERE RDB$RELATION_NAME = :RNAME ORDER BY RDB$FIELD_POSITION );
BEGIN
FOR
SELECT RDB$RELATION_NAME
FROM RDB$RELATIONS
INTO :RNAME
DO
BEGIN
OPEN C;
FETCH C INTO :FNAME;
CLOSE C;
SUSPEND;
END
END
。 游标定义只允许出现在PSQL块/过程/触发器的定义部分,作为任何规定的本地变量来定义.
。 游标名在上下文中必须是唯一的.它们不能和其他通过AS CURSOR子句, FOR SELECT已经定义的游标名冲突.然而,一个游标能在同一个上下文中通过任何其它类型的变量所共享它的名称,因为对各自的有效的运转是不一样的.
。 允许使用WHERE CURRENT OF子句来对指定位置进行更新与删除.
。 禁止试图取得或关闭一个FOR SELECT游标.
。 试图去打开一个已经打开的游标,或者取得或关闭一个已经关闭的游标,都将失败.
。 所有没有明确关闭的游标都将在退出当前PSQL块/过程/触发器时自动关闭.
。 ROW_COUNT系统变量能够被使用在FETCH语句之后,检查是否有记录返回.
存储过程变量的缺省值 V. Horsun
现在能够定义存储过程变量的缺省值.语法和定义一个字段或域的缺省值一样,除非你使用’=’来代替’DEFAULT’关键字.
有缺省值的变量必须位于变量列表的最后, 也就是说,不能将没有默认值的变量在有默认值的变量之后定义.调用者必须在有缺省值的变量之前对没有缺省值的变量赋值.例如,象这样是不合法的:为arg1,arg2赋值,没有设置arg3,设置arg4…
在运行中改变缺省值.如果你定义一个有缺省值的过程(如P1),从另一个过程(如P2)中调用它,缺省的变量,那么P1的缺省值将被引擎在执行P1开始的时候直接所代替.意思就是,如果你对P1改变了缺省值,,它不需要去重新编译P2了.
然而,它仍然需要断开所有客户连接,因为在Borland Interbase 6 beta "Data Definition Guide" (DataDef.pdf),在” Altering and dropping procedures in use(在使用中改变和删除过程)”章节中.
例子:
CONNECT ... ;
CREATE PROCEDURE P1 (X INTEGER = 123)
RETURNS (Y INTEGER)
AS
BEGIN
Y = X;
SUSPEND;
END;
COMMIT;
SELECT * FROM P1;
Y
============
123
EXECUTE PROCEDURE P1;
Y
============
123
CREATE PROCEDURE P2
RETURNS (Y INTEGER)
AS
BEGIN
FOR SELECT Y FROM P1 INTO :Y
DO SUSPEND;
END;
COMMIT;
SELECT * FROM P2;
Y
============
123
ALTER PROCEDURE P1 (X INTEGER = CURRENT_TRANSACTION)
RETURNS (Y INTEGER)
AS
BEGIN
Y = X;
SUSPEND;
END;
COMMIT;
SELECT * FROM P1;
Y
============
5875
SELECT * FROM P2;
Y
============
123
COMMIT;
CONNECT ... ;
SELECT * FROM P2;
Y
============
5880
注,变量缺省值的原始资料和BLR被保存在RDB$FIELDS中.
LEAVE <label> 语法的支持 D. Yemanov
新的LEAVE语法现在允许象Java风格一样,在PSQL循环中跳出标记处并且终止. 它的用途是去停止当前执行块并退出指定标记,恢复到执行循环以后的语句..
语法格式:
<标记名>: <循环语句>
...
LEAVE [<标记名>]
<循环语句> 处应是其中之一: WHILE, FOR SELECT, FOR EXECUTE STATEMENT
例子:
1.
FOR
SELECT COALESCE(RDB$SYSTEM_FLAG, 0), RDB$RELATION_NAME
FROM RDB$RELATIONS
ORDER BY 1
INTO :RTYPE, :RNAME
DO
BEGIN
IF (RTYPE = 0) THEN
SUSPEND;
ELSE
LEAVE; -- 退出当前循环
END
2.
CNT = 100;
L1:
WHILE (CNT >= 0) DO
BEGIN
IF (CNT < 50) THEN
LEAVE L1; --退出WHILE循环
CNT = CNT - l;
END
3.
STMT1 = 'SELECT RDB$RELATION_NAME FROM RDB$RELATIONS';
L1:
FOR EXECUTE STATEMENT :STMT1 INTO :RNAME
DO
BEGIN
STMT2 = 'SELECT RDB$FIELD_NAME FROM RDB$RELATION_FIELDS
WHERE RDB$RELATION_NAME = ';
L2:
FOR EXECUTE STATEMENT :STMT2 || :RNAME INTO :FNAME
DO
BEGIN
IF (RNAME = 'RDB$DATABASE') THEN
LEAVE L1; --退出到外部循环
ELSE
IF (RNAME = 'RDB$RELATIONS') THEN
LEAVE L2; -- 退出内部循环
ELSE
SUSPEND;
END
END
注意LEAVE后面没有标记,意味着中断当前(最内部)循环.
OLD 环境变量现在只读 D. Yemanov
OLD环境变量集现在在触发器模块中是只读的.试图去访问一些OLD值将会被拒绝.以后会有更多的这方面的信息.
PSQL 栈跟踪 V. Horsun
当PSQL 执行(存储过程或触发器)中出现异常时,现在API客户端分离出一个简单的栈跟踪错误状态矢量.栈跟踪从出现异常错误的点开始,通过一个字符串(最大 2048字节)表述出并且由所有的存储过程和触发器组成返回给最外层的调用者.如果实际的跟踪信息长于2KB的话,它将被截断.
附加消息条目将被添加到状态矢量,如下:
isc_stack_trace, isc_arg_string, <字串长度>, <字串>
isc_stack_trace is a new error code with value of 335544842L.
例子
建立元数据
CREATE TABLE ERR (
ID INT NOT NULL PRIMARY KEY,
NAME VARCHAR(16));
CREATE EXCEPTION EX '!';
CREATE OR ALTER PROCEDURE ERR_1 AS
BEGIN
EXCEPTION EX 'ID = 3';
END;
CREATE OR ALTER TRIGGER ERR_BI FOR ERR
BEFORE INSERT AS
BEGIN
IF (NEW.ID = 2)
THEN EXCEPTION EX 'ID = 2';
IF (NEW.ID = 3)
THEN EXECUTE PROCEDURE ERR_1;
IF (NEW.ID = 4)
THEN NEW.ID = 1 / 0;
END;
CREATE OR ALTER PROCEDURE ERR_2 AS
BEGIN
INSERT INTO ERR valueS (3, '333');
END;
1. 来自触发器的用户异常:
SQL> INSERT INTO ERR valueS (2, '2');
Statement failed, SQLCODE = -836
exception 3
-ID = 2
-At trigger 'ERR_BI'
2.来自过程调用触发器的用户异常:
SQL> INSERT INTO ERR valueS (3, '3');
Statement failed, SQLCODE = -836
exception 3
-ID = 3
-At procedure 'ERR_1'
At trigger 'ERR_BI'
3. 在触发器运行中出现异常(被0除):
SQL> INSERT INTO ERR valueS (4, '4');
Statement failed, SQLCODE = -802
arithmetic exception, numeric overflow, or string truncation
-At trigger 'ERR_BI'
4. 来自过程中的用户异常:
SQL> EXECUTE PROCEDURE ERR_1;
Statement failed, SQLCODE = -836
exception 3
-ID = 3
-At procedure 'ERR_1'
5. 来自过程中更深层调用栈的用户异常:
SQL> EXECUTE PROCEDURE ERR_2;
Statement failed, SQLCODE = -836
exception 3
-ID = 3
-At procedure 'ERR_1'
At trigger 'ERR_BI'
At procedure 'ERR_2'
调用一个UDF里的void函数 N. Samofatov
在PSQL中支持UDF, 如: RDB$SET_CONTEXT,尽管是它是空的函数,但是它能被调用.
(在面向对象的Pascal语言里,称作“过程”). 以后会有更多的这方面的信息.
新的保留字与变化
从Firebird 1.5以来,下面的关键字已经被增加或改变.那些标有星号 (*)是没有出现在标准的SQL中的.
新增的保留关键字
BIT_LENGTH
BOTH
CHAR_LENGTH
CHARACTER_LENGTH
CLOSE
CROSS
FETCH
LEADING
LOWER
OCTET_LENGTH
OPEN
ROWS
TRAILING
TRIM
CLOSE
OPEN
从非保留关键字改变成保留关键字
USING
新增的非保留关键字
BACKUP *
BLOCK *
COLLATION
COMMENT *
DIFFERENCE *
IIF *
NEXT
SCALAR_ARRAY *
SEQUENCE
RESTART
RETURNING *
从保留关键字改变成非保留关键字
ACTION
RESTRICT
WEEKDAY *
CASCADE
ROLE
YEARDAY *
FREE_IT *
TYPE
取消的保留关键字
BASENAME *
GROUP_COMMIT_WAIT *
NUM_LOG_BUFS *
CACHE *
LOGFILE *
RAW_PARTITIONS *
CHECK_POINT_LEN *
LOG_BUF_SIZE *
索引增强
取消了原252字节的索引长度限制 A.Brinkman
完全重写的索引代码非常快,支持更多记录(40位)。原来合计键值长度252字节的限制已取消,现在索引长度取决于页面大小(索引长度为1/4页面大小)。
表达式索引 O.Loa, D.Yemanov, A.Karyakin
现在动态DDL可使用任何表达式建立索引,允许使用索引过的存取路径用于表达式搜索。
语法结构:
CREATE [UNIQUE] [ASC[ENDING] | DESC[ENDING]] INDEX <索引名>
ON <表名>
COMPUTED BY ( <表达式> )
例子:
1.
CREATE INDEX IDX1 ON T1
COMPUTED BY ( UPPER(COL1 COLLATE PXW_CYRL) );
COMMIT;
/**/
SELECT * FROM T1
WHERE UPPER(COL1 COLLATE PXW_CYRL) = '姓名'
-- PLAN (T1 INDEX (IDX1))
2.
CREATE INDEX IDX2 ON T2
COMPUTED BY ( EXTRACT(YEAR FROM COL2) || EXTRACT(MONTH FROM COL2) );
COMMIT;
/**/
SELECT * FROM T2
ORDER BY EXTRACT(YEAR FROM COL2) || EXTRACT(MONTH FROM COL2)
-- PLAN (T2 ORDER IDX2)
注意:
1. 为了使数据库引擎能使用索引查找,用于查找的表达式必须和用于索引声明的表达式完全匹配。如果表达式不匹配,系统将不会使用给出的索引进行数据提取和排序操作。
2. 表达式索引具有和普通索引相同的特性和限制,除非定义了,否则不能被组合 (多段) 使用.
空键值处理 V.Horsun, A.Brinkman
* 空键值在唯一性检查时被忽略.(V.Horsun)
* 当系统需要忽略时、扫描索引时空值将被忽略. (A. Brinkman)
更多信息见后
更有效的索引压缩 A. Brinkman
相关信息见后
每个索引段可选择性维护 D.Yemanow A. Brinkman
每个索引段的选择性信息现在能够优化,开始变得更尽可能聪明的决定访问路径. 更多信息见后
Firebird ODS11及以上的索引结构
使用新的索引结构的原因是:
- 更好的支持删除一个索引键时大量的数据复制(导致慢的垃圾回收)
- 支持超过32bits的记录数(40bits)
- 增加了索引键值的长度(1/4页面大小)
ODS10及以下的索引结构:
head node node node node node node
node node node node node node node ... end marker
header =
typedef struct btr {
struct pag btr_header;
SLONG btr_sibling; // right sibling page
SLONG btr_left_sibling; // left sibling page
SLONG btr_prefix_total; // sum of all prefixes on page
USHORT btr_relation; // relation id for consistency
USHORT btr_length; // length of data in bucket
UCHAR btr_id; // index id for consistency
UCHAR btr_level; // index level (0 = leaf)
struct btn btr_nodes[1];
};
node =
struct btn {
UCHAR btn_prefix; // size of compressed prefix
UCHAR btn_length; // length of data in node
UCHAR btn_number[4]; // page or record number
UCHAR btn_data[1];
};
end marker(结束标记) = END_BUCKET 或 END_LEVEL
结束标记对叶节点来说是记录数,对非叶节点来说是页数
如果节点是END_BUCKET标记,则它应当包含和同级下一页的首节点一样的数据
如果节点是END_LEVEL标记,则其前缀(prefix)和长度(length)均为零,这样的话它就不包含数据,
所以每一级(除了末页)的每一个首节点均包含一个退化为零长度的节点
ODS11的索引结构:
head jump info jump nodes ... node [*] node node
node node node node node node node … end marker
jump info =
struct IndexJumpInfo {
USHORT firstNodeOffset; // offset to first node in page [*]
USHORT jumpAreaSize; // size area before a new jumpnode is made
UCHAR jumpers; // nr of jump-nodes in page, with a maximum of 255
};
jump node =
struct IndexJumpNode {
UCHAR* nodePointer; // pointer to where this node can be read from the page
USHORT prefix; // length of prefix against previous jump node
USHORT length; // length of data in jump node (together with prefix this is prefix for pointing node)
USHORT offset; // offset to node in page
UCHAR* data; // Data can be read from here
};
新索引结构的新标志:
新标志增加到header->pag_flags
标志btr_large_keys(32)存储压缩的长度、前缀及记录号, 这也意谓着长度和前缀能够大到1/4页面大小(页大小为4096时是1024),并且在将来ODS结构再改变时很容易扩展,而且记录数能够很容易的扩展,比如扩到40bits。这些数被存储为每7位加1位(最高位)标记(可变长度编码),每一个字节(Byte)都需要移动7位后存储。如,25存为1字节0x19,130 = 2个字节 0x82 0x01,65535 = 3个字节 0xFF 0xFF 0x03
重复节点:
每一个非叶节点增加了一个新的标志用来存储记录数,以提高重复节点的索引读取速度。这个标志是btr_all_recordnumber(16位),由于这个增加的信息,插入和删除多个重复(比如外键是null)的键值时变得更快(比如垃圾回收!)。除了重复的节点(length=0)并不存储自己的长度信息外,第一个字节还有3位用来决定该节点是否重复存储。除了ZERO_LENGTH(4),还有END_LEVEL(1)、END_BUCKET(2)、ZERO_PREFIX_ZERO_LENGTH(3)和ONE_LENGTH(5)这几个标记,6和7保留将来使用。
跳转节点:
跳转节点是对页内节点所在位置的参考。它包含了指定节点的偏移信息和参考节点的前缀数据,但是跳转节点自己也是前缀压缩的。
理想的生成一个新的跳转节点的时机是在每一个jumpAreaSize(跳转区域大小)后的第一个节点建立之后,但这仅在激活(解除)一个索引或以同样的顺序插入节点并存储到索引这种情况有效。
如果节点插入到两个跳转节点之间则只有偏移被更新,但也仅在偏移没有超出指定的范围(+/-10%)时。
当节点被删除时仅是偏移被更新或跳转节点被删除。这意谓着在首、尾跳转节点之间能够只有一小缺口,所以我们在生成新的跳转节点时不会浪费时间。
前缀和长度也按可变长度编码存放.
例子数据:
(x) = x 字节大小
header (34)
52 (2) 256 (2) 2 (1) 30 (2) 0 (1)
2 (1) 260 (2) FI (2) 1 (1) 1 (1)
514 (2) U (1) 0 (1) 1 (1) 0 (1)
A (1) ...
2 (1) 6 (1) 21386 (3) REBIRD (6) ...
2 (1) 2 (1) 1294 (2) EL (2) ...
指向固定头后 = 0x22
批向跳转信息后 = 0x29
指向第一个跳转节点 = 0x29 + 6(跳转节为点1) + 5(跳转节点2) = 0x34
跳转节点1参考的是数据是”FIREBIRD”的这个节点,因为这个节点有一个长为2的前缀,其前两个字符”FI”存储在跳转节点中。
我们的下一个跳转节点指数据为”FUEL”的节点,其前缀也是2,这样跳转节点2应该包含”FU”,但由于前一个节点已经包含 “F”,所以前缀被压缩为只存储”U”。
null状态:
需要存储的数据由btr.cpp中的程序compress()决定。
对升序(ascending)索引来说并不存储数据(键长度是零),将自动把它们作为索引的第一入口,并正确排序(对单字段索引节点长度及前缀均是零)。
降序(descending)索引将存储一个值为0xFF(255)的单字节数据,为了区分有值(零字符串是0xFF)与null(空)状态我们插入了一个字节0xFE(254)在数据之前,这只在数值以0xFF或0xFE开始时才这样做,所以我们保持正确的顺序。
例如:
升序索引节点,1段
前缀 长度 存储数据 数值/状态
0 0 NULL
0 0 NULL
0 1 x65(A) A
1 1 x65(A) AA
... … … …
降序索引节点,1段
前缀 长度 存储数据 数值/状态
... … … …
0 2 xFE xFE (þ) x4A (J) 0xFE 0x4A
1 1 xFF (ÿ) 0xFF
0 1 xFF NULL
1 0 xFF NULL
END_LEVEL
升序索引节点,3段
前缀 长度 存储数据 数值/状态
0 0 NULL, NULL, NULL
0 10 x01(1) x70(F) x73(I) x82(R) x69(E)
x01(1) x66(B) x73(I) x82(R) x68(D) NULL, NULL,
FIREBIRD
0 10 x02(2) x70(F) x73(I) x82(R) x69(E)
x02(2) x66(B) x73(I) x82(R) x68(D) NULL, FIREBIRD,
NULL
0 10 x03(3) x70(F) x73(I) x82(R) x69(E)
x03(3) x66(B) x73(I) x82(R) x68(D) FIREBIRD, NULL,
NULL
3 9 x00(0) x00(0) x02(2) x65(A) x00(0)
x00(0) x00(0) x01(1) x66(B) FI, A, B
… … … …
降序索引节点,3段
前缀 长度 存储数据 数值/状态
0 12 xFC xB9 xB6 xFF xFF xFD xBE
xFF xFF xFF xFE xBD FI, A, B
3 17 xAD xBA xFC xBD xB6 xAD xBB
xFD xFF xFF xFF xFF xFE xFF
xFF xFF xFF FIREBIRD, NULL,
NULL
1 19 xFF xFF xFF xFF xFD xB9 xB6
xAD xBA xFD xBD xB6 xAD xBB
xFE xFF xFF xFF xFF NULL, FIREBIRD,
NULL
6 14 xFF xFF xFF xFF xFE xB9 xB6
xAD xBA xFE xBD xB6 xAD xBB NULL,NULL,
FIREBIRD
11 4 xFF xFF xFF xFF NULL, NULL, NULL
END_LEVEL
© ABVisie 2005, Arno Brinkman
Optimizations
Optimizer improvements A. Brinkman,
D. Yemanov
Distribute HAVING clause conjunctions to the WHERE clause when
possible
l
l Distribute UNION conjunctions to the inner streams when possible
l Improved cross join and merge/sort handling
l Better optimization of mixed AND/OR predicates
l Let's choose a reasonable join order for intermixed inner and outer joins
MERGE PLAN may now be generated for joins using equality comparsion
on expressions
l
l Better logic regarding unique indices handling
l Improved logic for OR expressions
Available in ODS 11.0 only:
l Usage of segment-level selectivities
l Better support for IS NULL
l Better support for STARTING WITH
l Matching both OR and AND nodes to indices
l Better cost estimations and hence better join orders
l Allowed indexed order (navigational walk) for outer joins
Improved PLAN clause D. Yemanov
A PLAN clause optionally allows you to provide your own instructions to the
engine and have it ignore the plan supplied by the optimizer. Firebird 2
enhancements allow you to specify more possible paths for the engine. For
example:
PLAN (A ORDER IDX1 INDEX (IDX2, IDX3))
For more details, please refer to the topic in the DML section, Improvements in
user-specified query plans.
Firebird v.200.30 Optimizations, Third Alpha
http://rusty/fb2/optimization.html (1 of 2) [7/07/2005 8:10:39 AM]
Buffer cache improvements O. Loa,
D. Yemanov
Better choice of streams order in joins and better index usage in general (D.
Yemanov)
l
l Much faster algorithms to process the dirty pages tree
Increased maximum page cache size to 128K pages (2GB for 16K page
size).
l
More information to come.
Faster evaluation of IN and OR O. Loa
Constant IN predicate or multiple OR booleans are now evaluated faster. More
information to come.
Improved optimization for unique
retrieval A. Brinkman
The optimizer will now use a more realistic cost value for unique retrieval. More
information to come.
More optimization of NOT conditions D. Yemanov
NOT conditions are simplified and optimized via an index when possible.
Example(s):
(NOT NOT A = 0) -> (A = 0)
(NOT A > 0) -> (A <= 0)
Firebird v.200.30 Optimizations, Third Alpha
http://rusty/fb2/optimization.html (2 of 2) [7/07/2005 8:10:39 AM]
字符集的改进
新的非ACSII字符集接口
作者 Adriano dos Santos Fernandes
新的国际字符集接口的描述是这个Firebird 2最终Alpha版一个特征。以前是由N. Samofatov,新接口特征的改进是由我来完成的。
构架
任何字符字段或者变量的声明中,FireBird允许字符集和整理。在数据库创建的时候也能指定默认的字符集,因此每次声明CHAR/VARCHAR变量的时候不必明确的用包含CHARACTER SET 的字句来说明。
数据库连接的时候你也能指定客户端读字符串时所用的字符集。如果客户端(或者连接端)的字符集没有特别声明,NONE字符集就是被假定的了。
有两个特殊的字符集NONE和OCTETS(八进制)在声明里能够被使用。然而,OCTETS不能够作为连接字符集使用。这两个字符集很相似,不同在于空字符在NONE中的ASCII码为0x20,在OCTETS 为0x00。NONE和OCTETS的“特殊”意思在于它们没有遵守别的字符集的转换规则。
别的字符集的转换过程为:字符1->UNICODE->字符2;
NONE/OCTETS集的字节仅仅是拷贝:NONE/OCTETS->字符2和字符1->NONE/OCTETS
改进
好的格式检查
某些字符集(特别是多字符集)并不是任何字符串都能接收的。现在,当从NONE/OCTETS连接时和客户端发出字符串(声明字符串和参数)时,新的引擎能确认字符串为合适的格式。
大写转换
1.5版的FB中,默认的字符集(二进制)处理顺序中,如果没有特别的处理顺序被指定,那么只有ASCII字符能被转换为大写。
例如:
isql -q -ch dos850
SQL> create database 'test.fdb';
SQL> create table t (c char(1) character set dos850);
SQL> insert into t values ('a');
SQL> insert into t values ('e');
SQL> insert into t values ('á');
SQL> insert into t values ('é');
SQL>
SQL> select c, upper(c) from t;
C UPPER
====== ======
a A
e E
á á
é é
在FB2.0中为:
C UPPER
====== ======
a A
e E
á Á
é É
最大字符串长度
FB1.5.X的引擎里没有确认多字节字符串(MBCS)的逻辑长度。因此,一个UNICODE_FS
字段是许多普通声明字段字符的长度的三倍,即一个UNICODE_FSS字符最大长度的三倍。
这是为遗留下来的字符集提供的兼容性。但是,新的字符集(例如:UTF8)就没有继承这种限制了。
NONE作为配置字符集
当NONE作为配置(连接)字符集使用的时候,XSQLVAR的sql子类型(sqlsubtye)存储了被读字段的字符集的编号,而以前该编号一直为零。
BLOBS 和处理
数据管理处理子句COLLATE支持BLOBS。
例如:
select blob_column from table
where blob_column collate unicode = 'foo';
新的字符集和处理
UTF8字符集
UNICODE_FSS字符集有一个数字问题:它是UTF8的一个老的版本,这个版本允许接受些难看的字符串,而且对字符串的长度没有强迫纠正。
FB1.5.X版的UTF8只是UNICODE_FSS的一个别名。而现在,UTF8是一个新的字符集了,没有UNICODE_FSS所留下的问题。
UNICODE 处理(对UTF8来说)
UCS_BASIC对UTF8来说,在运行时没有区别(根据UNICODE的编码顺序排序)。UNICODE处理排序算法采用的是UCA(Unicode 处理算法)。
排序的列子:
isql -q -ch dos850
SQL> create database 'test.fdb';
SQL> create table t (c char(1) character set utf8);
SQL> insert into t values ('a');
SQL> insert into t values ('A');
SQL> insert into t values ('á');
SQL> insert into t values ('b');
SQL> insert into t values ('B');
SQL> select * from t order by c collate ucs_basic;
C
======
A
B
a
b
á
SQL> select * from t order by c collate unicode;
C
======
a
A
á
b
B
巴西语的处理
驱动集
新的字符集和处理是通过动态库来实现的,它们安装在intl的子目录下,通过一个描述文件来管理。例如,可以看fbintl.conf。
并不是所有实现的字符集和处理都需要在描述文件中被列出来。只有列出来的才能使用,重复项不会被加载。
在被安装在服务器上后,字符集和处理应该在数据库的系统表中注册(rdb$character_sets 和rdb$collations)。Firebird 2的安装目录下的misc/ intl.sql文件描述了注册和反注册的一系列的存储过程。
BLOB的改进
- 关于BLOBS的查找中,现在能够被处理了;
- 一个BLOB的完整的内容也能够比较了;
- 从一种BLOB的字符串转换成另一种BLOB的字符串的时候,字符集也能作相应的转换了。
新的字符串函数
LOWER将输入参数转换成所有的小写字符。例如需要的时候,甚至使用一个非ASCII字符集。
TRIM 能从字符串的左边和/或右边去掉字符(默认是空字符)。
语法格式
TRIM <left paren> [ [ <trim specification> ] [ <trim character> ] FROM ] <value
expression> <right paren>
<trim specification> ::= LEADING | TRAILING | BOTH
<trim character> ::= <value expression>
规则:
1. 如果<trim specification>没有显式声明,BOTH就是被假设的了;
2. 如果<trim character>没有显示声明,’’就是被假设的了;
3. 如果<trim specification>和/或者<trim character>被显式声明,FROM也应该被显式声明;
4. 如果<trim specification>和<trim character>都没有被显式声明,FROM也不应该被显式声明。
例如:
A) select
rdb$relation_name,
trim(leading 'RDB$' from rdb$relation_name)
from rdb$relations
where rdb$relation_name starting with 'RDB$';
B) select
trim(rdb$relation_name) || ' is a system table'
from rdb$relations
where rdb$system_flag = 1;
BIT_LENGTH 返回字符串的比特(bits)长度
CHAR_LENGTH/CHARACTER_LENGTH 返回字符串的字符(characters)长度
OCTET_LENGTH 返回的是字符串的字节(bytes)长度
这三种语句都有相同的语法格式,如下:
<length function> ::=
{ BIT_LENGTH | CHAR_LENGTH | CHARACTER_LENGTH | OCTET_LENGTH } ( <value expression><)
例如:
select
rdb$relation_name,
char_length(rdb$relation_name),
char_length(trim(rdb$relation_name))
from rdb$relations;
INTL插件集
字符集和处理通过描述文件来安装。当冲突存在的时候,服务器会记录在日志上。将会有更多的信息和测试想法。
已修复的字符集BUG
下面列出来的字符集和处理已经被修复了:
SF#1073212 用一个COLLATE字句在一个大列上排序会导致服务器关闭
SF#939844 在一个UNICODE的数据库上使用超过263个字符的查询会抛出一个GDS异常
SF#977785 使用多字节字符集(UTF-8,East_Asian集)将返回错误的字符长度
SF#536243 在UNICODE_FSS字符串上使用UPPER()函数的时候,现在能有正确的返回值了
SF#642726 UPPER因为ISO8859_1而不能将aacute正确转换为Aacute;
SF#544630 当使用UNICODE作为连接字符串时所报告的问题;(将会有更多信息)
未注册的bug 一个依靠当前索引的查询使用的字符串的第一个字符就是一个压缩对字符的最后一个字符时,执行后会有不同的返回值
未注册的bug 一个字符集里的SUBSTRING在BLOB字段中不能正确工作
未注册的bug 在多字节的BLOBS中,模式匹配能够以二进制的方式执行了
未注册的bug 如果数据库有些列使用不同的字符集,而用多字节字符集连接时数据库导致的不安全
请测试者注意
还有些包含UNICODE处理(基本的)和large blobs字段的操作(=,LIKE,CONTAINING)需要被测试。
新的字符集/处理
俄语字符集和字典处理
乌克兰语字符集和字典处理
拉丁语字典处理
立陶宛语字典处理
爱沙尼亚字典处理
UTF8
UTF8
Unicode 4.0支持UTF8字符集和处理 UCS_BASIC 和UNICODE.
安全
改变概述
更好的密码加密算法
密码的加密/解密现在采用的是一种更安全的密码哈希算法。
用户能修改他们自己的密码
现在,用户能修改他们自己的密码
GSEC现在能使用服务API(Services API)
没有服务器的许可,安全数据库将拒绝工作
如果不是通过服务器管理工具(Services Manager)来连接security2.fdb,服务器将会拒绝
能够对暴力攻击进行保护
试图通过对账号和密码使用暴力攻击技术来得到服务器的权限,现在能被检测出来和锁定了。
攻击性API已经被关闭
几个能够被用于攻击的API已经被关闭了。将会有更多的信息。
Firebird 2.0里详细的安全变化
有几个改进已经被应用到安全性的提高上。被一般承认的特别要注意的方面包括:
。 在安全数据库里缺少对抗暴力破解的密码加密算法
。 能够让有正确账号的远程用户打开安全数据库,并从里面读出哈希值(特别会引起某些人对这个值合并感兴趣)
。 让用户改变他们自己的密码
。 远端对服务器直接密码暴力破解时缺少保护
Firebird 1.5的认证
DES算法在Firebird 1.5里面被使用了两次来对密码产生了哈希值:在和安全数据库(security database)里面存储的哈希值比较之前,第一次使用是在客户端,接着是在服务器。但是,当SYSDBA改变了一个密码后,这个顺序完全被打破了。客户端执行哈希计算两次并且直接将结果存储在安全数据库里。因此,哈希管理部分完全只依赖于客户端(或者,事实上,客户端自定义)。
Firebird 2:服务器哈希计算
另一种方法也被采用来使用更强壮的哈希值。存储在服务器上的哈希值应该只能在服务器侧计算得到。这样一个对策一直都在Firebird里面------在服务API里面。这会导致在任何活动的客户端对客户管理使用服务API的决定。现在,gsec 和 isc_user_add(修改,删除)API函数全部使用服务来访问安全数据库。(嵌入式访问POSIX上典型的服务器是一个列外――如下所示)
这样任何改变的方法都会使密码被哈希计算了――它总是在服务器端执行。Gsec的在安全数据库上计算哈希值的问题现在也不存在了:它仅仅是请求服务来做它的工作!
只要服务器侧的构架支持,这些新的gsec 也能够在旧的Firebird版本上使用。
哈希算法――SHA-1
在Firebird 2.0里面选用的是SHA-1。数据被存储在安全数据库的USERS表的PASSWORD字段里,有两部分组成――一个随机数,作为计算这个特殊哈希值和它自己哈希值的种子。它用SHA1计算(种子||用户名||密码)((salt||username||password))。
这种方法会导致下面这些情况:
1. 对A有效的哈希值对B无效
2. 当一个用户改变他的密码时――恰好和他从前的字符串相同――存储在USERS.PASSWORD字段里面数据是新值
虽然这些情况并不会提高对试图暴力破解密码的抵抗,但是他使对偷数据库密码的“可视”化分析变的更难了些。
Firebird 2的GESC
当用gsec进行安全预览即维护用户说明和认证密码的时候,存在一个问题。虽然不知道正确密码的人是不能改变安全数据库的数据的,但是旧的gsec存在相对容易的方法更改。它会将坏的旧版DES哈希值写入USERS.PASSWORD字段中,而且如果在Firebird.conf的配置文件中,LegacyHash参数的IN被设置为0,默认情况下如此,那么,一旦安全数据库更新,就有可能登录上服务器了。因此,采取了些特别的措施来使这种远程连接安全数据库的情况完全不可能发生。如果有旧版的程序因想直接取得权限而导致连接失败,就不要吃惊了:它本来就是这样设计的。用户信息现在只能通过服务器API和由isc_user_*API函数集实现的相应内在权限的服务来获得。
。 安全数据库的结构改变了。通常,能让任何用户改变他们的密码,现在包含Ivan Prenosil的一个补丁,因此有些小的不同。
。 Firebird 1.5里面USERS表被PUBLIC声明为可读的,没有密码确认过程会导致引擎失败。Ivan’s的补丁解决方案使用了视图,采用了一个条件”WHERE USER=””。这样做是因为引擎里面为SQL中的变量USER留下了空值,并不是’authenticator’值,虽然这被看成引擎的代码。
当这个缺陷被修复,就可能加上条件”USER=’authenticator’”了。因为用户名一直被转换为大写,所以这个短语能正确被执行。
。 为了能够避免通过SQL语句实现用户认证,找到了一个更好的方法。结果就
不是SYSDBA的用户只能看见他自己登录,无论是使用任何用户管理管理工具进行攻击(gsec,或者其它使用服务API图形化接口)。SYSDBA仍然对所有用户有完全管辖权限。
。 调用GSEC的语法改变了
因为GSEC现在使用服务API,所以这个开关:
-database <安全数据名称>
被移除了。代替的新开关为:
-server <管理的服务器>
这个开关最重要的目的是,使用时更加便利了:为了管理远端服务器上的安全数据库,没有必要实施补丁和使用安全数据库的名字了。
例子:
TCP/IP:
gsec -user sysdba -password masterkey -server firebird.company.com:
(注意冒号后缀作为服务器名称的开关)
Win32的命名管道:
gsec -user sysdba -password masterkey -server \\nt_srv\
(注意没有命名管道的后缀作为服务器名称的开关)
。 传统的Firebird密码的最大长度为8个字节,黑客用暴力攻击的话,就能够有机会进入的。2.0版对此采取了一些保护。当较长次数试图通过错误的密码取得权限时,认证进程就会及时锁住一段时间,最小化黑客找到正确密码的机会。
POSIX上典型的服务器
因为技术上和历史上的原因,POSIX上使用典型服务器而采用嵌入式客户端时就非常易受攻击而使安全性暴露。用户通过嵌入式权限访问数据库时必须获得最新的访问安全数据的权限。这就是为什么采用更好的密码哈希值的绝对要求了。一个有用户级权限的恶意用户能够很容易的偷偷复制一份安全数据库,把它带回家里,然后用旧的DES哈希值对它进行暴力破解!最后,他能改变存储在服务器上关键数据库的数据了。Firebird2 则不容易受这种危险。
但是在嵌入式的POSIX服务上则多了一个安全性的问题:它的服务 API是通过命令行gsec来实现的,和普通用户一样。因此,一个嵌入式的维护工具必须对安全数据库有完全的权限。
限制对安全数据库的直接访问的权限是为了保护它不被旧版的客户端软件访问。幸运的是,因为它作为旧的客户端和新的服务器端一起的产物在产品中一同出现是不太可能的,所以它同时也最小化了暴露在嵌入式典型POSIX上的危险性。
然而,Firebird安全水平在一些重要方面仍然不能让人满意,因此在打开Internet网上的3050端口前先仔细读读这小节。
Firebird上一个重要的安全性问题仍然没有解决:瘦加密的密码很“清楚”地在网上传输。如果不对旧的客户端改写,就不太可能解决这个问题。采用另一种方法,一个有密码且采用了新的安全算法的用户就不能使用旧的客户端附加到服务器上了。在下一版中,这个问题已经被考虑通过更新一些API的某些方面来改进,Firebird 2.0中还没有改变密码传输的方式。
1.5版和2.0版中,和Firebird服务器交换数据的问题能够通过任意的IP隧道软件(如ZeBeDee)来毫不费力的解决掉。
更新新的安全数据库
如果你试图将pre-Firebird 2版的安全数据库—security.fdb 或者 一个更名的isc4.gdb—放在Firebird的新的安装目录中并且想连接到服务器上时,你就会得到”不能附加到密码数据库”的提示。这个不是缺陷:本来就这样设计的。旧版Firebird安全数据库不能够直接在2.0版或者更高版本上使用。有新结构的安全数据库名为securtiy2.fdb。
为了能够使用旧的安全数据库,必须对Firebird服务器安装目录下../misc/upgrade子目录下的security_database.sql脚本进行更新。(当前版位于Sourcefore的CVS树的firebird2目录下的../src/misc/upgrade/v2)
更新步骤如下:
1. 把你的旧版安全数据库放在任何你知道的地方,除了Firebird的新安装目录。任何时候都要记得备份。
2. 启动Firebird 2,使用的是本地security2.fdb。
3. 以SYSDBA身份连接旧版安全数据库并运行脚本。
4. 停止Firebird服务。
5. 拷贝更新后的数据库到Firebird 2的安装目录。
6. 打开 firebird.conf,设置LegacyHash为1(记住删除”#”注释符号)。注意下面的警告!
7. 从启Firebird。
警告:只要你更改LegacyHash=1,Firebird的安全性就不能完全地运行。要使它正常运行,下面几步是必要的:
1. 改变数据SYSDBA的密码
2. 用户都改变他们的密码(2.0用户能自己改变他们的密码)
3. 把LegacyHash改为默认值0,或者注释掉
4. 停止和重启Firebird,让更改的配置生效
New Configuration Parameters and
Changes
ExternalFileAccess A. Peshkov
Modified in Firebird 2, to allow the first path cited in ExternalFilesAccess to be
used as the default when a new external file is created.
LegacyHash A. Peshkov
This parameter enables you to temporarily configure Firebird 2's new security to
run with your old passwords in an upgraded security database (security.fdb).
Refer to the Security section for instructions on upgrading your existing Firebird
1.5 security.fdb (or a renamed isc4.gdb) to the new security database layout.
GCPolicy V. Horsun
Garbage collection policy. It is now possible to choose the policy for garbage
collection on SuperServer. The possible settings are cooperative, background and
combined, as explained in firebird.conf. Classic supports only cooperative. More
detail to come.
UsePriorityScheduler A. Peshkov
Setting this parameter to zero disables switching of thread priorities completely. It
affects only the Win32 SuperServer.
DeadThreadsCollection is no longer used A. Peshkov
Dead threads are now efficiently released "on the fly", making configuration
unnecessary. Firebird 2.0 silently ignores this parameter.
TCPNoNagle default has changed K. Kuznetzov
The default value for TcpNoNagle is now TCP_NODELAY.
Utilities
On-line incremental backup
(NBak/NBackup) N. Samofatov
Fast, on-line, page-level incremental backup facilities have been implemented. The backup
engine comprises two parts:
l NBAK, the engine support module
l NBACKUP, the tool that does the actual backups
NBAK
The functional responsibilities of NBAK are:
to redirect writes to difference files when asked (ALTER DATABASE BEGIN
BACKUP statement)
1.
to produce a GUID for the database snapshot and write it into the database header
before the ALTER DATABASE BEGIN BACKUP statement returns
2.
to merge differences into the database when asked (ALTER DATABASE END
BACKUP statement)
3.
to mark pages written by the engine with the current SCN [page scan] counter value
for the database
4.
5. to increment SCN on each change of backup state
The backup state cycle is:
nbak_state_normal -> nbak_state_stalled -> nbak_state_merge ->
nbak_state_normal
n In normal state writes go directly to the main database files.
n In stalled state writes go to the difference file only and the main files are read only.
In merge state new pages are not allocated from difference files. Writes go to the
main database files. Reads of mapped pages compare both page versions and return
the version which is fresher, because we don't know if it is merged or not.
n
This merge state logic has one quirky part. Both Microsoft and Linux define the contents
of file growth as "undefined" i.e., garbage, and both zero-initialize them.
This is why we don't read mapped pages beyond the original end of the main database file
and keep them current in difference file until the end of a merge. This is almost half of
nbak fetch and write logic, tested by using modified PIO on existing files containing
garbage.
NBACKUP
The functional responsibilities of NBACKUP are
1. to provide a convenient way to issue ALTER DATABASE BEGIN/END BACKUP
2. to fix up the database after filesystem copy (physically change nbak_state_diff to
nbak_state_normal in the database header)
to create and restore incremental backups.
Incremental backups are multi-level. That means if you do a Level 2 backup every
day and a Level 3 backup every hour, each Level 3 backup contains all pages
changed from the beginning of the day till the hour when the Level 3 backup is
made.
3.
Creating incremental backups has the following algorithm:
Issue ALTER DATABASE BEGIN BACKUP to redirect writes to the difference
file
1.
2. Look up the SCN and GUID of the most recent backup at the previous level
Stream database pages having SCN larger than was found at step 2 to the backup
file.
3.
Write the GUID of the previous-level backup to the header, to enable the
consistency of the backup chain to be checked during restore.
4.
5. Issue ALTER DATABASE END BACKUP
Add a record of this backup operation to RDB$BACKUP_HISTORY. Record
current level, SCN, snapshot GUID and some miscellaneous stuff for user
consumption.
6.
Restore
Restore is simple: we reconstruct the physical database image for the chain of backup files,
checking that the backup_guid of each file matches prev_guid of the next one, then fix it
up (change its state in header to nbak_state_normal).
Usage
nbackup <options>
Valid options
-L <database> Lock database for filesystem copy
-U <database> Unlock previously locked database
-F <database> Fixup database after filesystem copy
-B <level> <database> [<filename>] Create incremental backup
-R <database> [<file0> [<file1>...]] Restore incremental backup
Notes
l <database> may specify a database alias
l incremental backups of multi-file databases are not supported yet
l "stdout" may be used as a value of <filename> for the -B option
ISQL improvements Various developers
-b[ail]
Command line switch -b to instruct isql to bail out on error when used in
non-interactive mode, returning an error code to the operating system. (D. Ivanov,
C. Valderrama)
When using scripts as input in the command line, it may be totally unappropriate to
let isql continue executing a batch of commands after an error has happened.
Therefore, the "-b[ail]" option will cause script execution to stop at the first error it
detects. No further statements in the input script will be executed and isql will return
an error code to the operating system.
Most cases have been covered, but if you find some error that's not recognized
by isql, you should inform the project, as this is a feature in progress.
m
Currently there is no differentiation by error code---any non-zero return code
should be interpreted as failure. Depending on other options (like -o, -m and
-m2) , isql will show the error message on screen or will send it to a file.
m
Some features
Even if isql is executing nested scripts, it will cease all execution and will
return to the operating system when it detects an error. Nested scripts happen
when a script A is used as isql input but in turn A contains an INPUT
command to load script B an so on. Isql doesn't check for direct or indirect
recursion, thus if the programmer makes a mistake and script A loads itself or
loads script B that in turn loads script A again, isql will run until it exhaust
memory or an error is returned from the database, at whose point -bail if
activated will stop all activity.
m
The line number of the failure is not yet known. It has been a private test
feature for some years but needs more work to be included in the official isql.
m
DML errors will be caught when being prepared or executed, depending on
the type of error.
m
DDL errors will be caught when being prepared or executed by default, since
isql uses AUTODDL ON by default. However, if AUTO DLL is OFF, the
server only complains when the script does an explicit COMMIT and this
may involve several SQL statements.
m
m The feature can be enabled/disabled interactively or from a script by means of
the SET BAIL [ON | OFF] command. As it's the case with other SET
commands, simply using SET BAIL will toggle the state between activated
and deactivated. Using SET will display the state of the switch among many
others.
Even if BAIL is activated, it doesn't mean it will change isql behavior. An
additional requirement should be met: the session should be non-interactive.
A non-interactive session happens when the user calls isql in batch mode,
giving it a script as input.
Example
isql -b -i my_fb.sql -o results.log -m -m2
However, if the user loads isql interactively and later executes a script with
the input command, this is considered an interactive session even though isql
knows it is executing a script.
Example
isql
Use CONNECT or CREATE DATABASE to specify a database
SQL> set bail;
SQL> input my_fb.sql;
SQL> ^Z
Whatever contents the script has, it will be executed completely, errors and
all, even if the BAIL option is enabled.
m
New -m2 command-line switch to output Stats and Plans
Command-line option -M2 to send the statistics and plans to the same output file as
the other output (via the -o[utput] switch). (C. Valderrama)
When the user specifies that the output should be sent to a file, two possibilities
have existed for years: either
at the command line, the switch -o followed by a file name is used
or
m
the command OUTput followed by a file name is used, either in a batch
session or in the interactive isql shell. (In either case, simply passing the
command OUTput is enough to have the output returned to the console).
However, although error messages are shown in the console, they are not
output to the file.
m
The -m command line switch was added, to meld (mix) the error messages with the
normal output to wherever the output was being redirected.
This left still another case: statistics about operations (SET STATs command) and
SQL plans as the server returns them. SET PLAN and SET PLANONLY commands
have been treated as diagnostic messages and, as such, were always sent to the
console.
What the -m2 command line switch does is to ensure that stats and plans
information go to the same file the output has been redirected to.
Note: neither -m nor -m2 has an interactive counterpart through a SET command.
They are for use only as command-line isql options.
l ODS version is now returned in the SHOW DATABASE command (C. Valderrama)
New command SET HEADING ON/OFF toggle
Some people consider it useful to be able to do a SELECT inside isql and have the
output sent to a file, for additional processing later, especially if the number of
columns makes isql display impracticable. However, isql by default prints column
headers and in this scenario, they are a nuisance. Therefore, printing the column
headers -- previously a fixed feature -- can now be enabled/disabled interactively or
from a script by means of the
SET HEADing [ON | OFF]
command in the isql shell. As is the case with other SET commands, simply using
SET HEAD will toggle the state between activated and deactivated.
Note: this switch cannot be deactivated with a command line parameter.
Using SET will display the state of SET HEAD, along with other switches that can
be toggled on/off in the isql shell.
l
Ability to show the line number where an error happened in a script
In previous versions, the only reasonable way to know where a script had caused an
error was using the switched -e for echoing commands, -o to send the output to a file
and -m to merge the error output to the same file. This way, you could observe the
commands isql executed and the errors if they exist. The script continued executing
to the end. The server only gives a line number related to the single command
(statement) that it's executing, for some DSQL failures. For other errors, you only
know the statement caused problems.
With the addition of -b for bail as described in (1), the user is given the power to tell
isql to stop executing scripts when an error happens, but you still need to echo the
commands to the output file to discover which statement caused the failure.
Now, the ability to signal a script-related line number of a failure enables the user to
go to the script directly and find the offending statement. When the server provides
line and column information, you will be told the exact line in the script that caused
the problem. When the server only indicates a failure, you will be told the starting
line of the statement that caused the failure, related to the whole script.
This feature works even if there are nested scripts, namely, if script SA includes
script SB and SB causes a failure, the line number is related to SB. When SB is read
completely, isql continues executing SA and then isql continues counting lines
related to SA, since each file gets a separate line counter. A script SA includes SB
when SA uses the INPUT command to load SB.
Lines are counted according to what the underlying IO layer considers separate
lines. For ports using EDITLINE, a line is what readline() provides in a single call.
The line length limit of 32767 bytes remains unchanged.
SHOW SYSTEM command shows predefined UDFs
The SHOW <object_type> command is meant to show user objects of that type. The
SHOW SYSTEM commmand is meant to show system objects but, until now, it
only showed system tables. Now it lists the predefined, system UDFs incorporated
into FB 2. It may be enhanced to list system views if we create some of them in the
future.
l
SQLDA_DISPLAY command
SQLDA_DISPLAY command shows the input SQLDA parameters of INSERTs,
UPDATEs and DELETEs. Implemented by A. dos Santos Fernandes.
l
ISQL Bugs fixed
SF #910430 - ISQL and database dialect (C. Valderrama, B. Rodriguez Somoza)
When ISQL disconnected from a database, either by dropping it or by trying to
connect to a non-existent database, it remembered the SQL dialect of the previous
connection, which could lead to some inappropriate warning messages.
l
SF #223126 - Misplaced collation when extracting metadadata with ISQL (B.
Rodriguez Somoza)
l
l SF #223513 - Ambiguity between tables and views (B. Rodriguez Somoza)
l SF #518349 - ISQL show mangles relationship (B. Rodriguez Somoza)
l Stopped possible crashes with long terminators (C. Valderrama)
l Made SET SQLDA_DISPLAY work in release versions (C. Valderrama)
Avoided several SQL> prompts when using the INPUT command interactively (C.
Valderrama)
l
l Stopped some memory leaks
(Better descriptions expected.)
GBAK Enhancements
New Restore Switches V. Horsun
The new GBAK switch -RECREATE_DATABASE [OVERWRITE] replaces the old
-R[EPLACE_DATABASE] switch and makes it harder for the unsuspecting to overwrite a
database accidentally.
gbak -R[ECREATE_DATABASE] and gbak -C[REATE_DATABASE] are now
equivalent
l
gbak -R[ECREATE_DATABASE] O[VERWRITE] is equivalent to the old gbak
-R[EPLACE_DATABASE]
l
That is to say, now you won't be able to have gbak restore over an existing database unless
you include the O[VERWRITE] flag.
GSEC
GSEC return code C. Valderrama
GSEC now returns an error code when used as a non-interactive utility. Zero indicates
success; any other code indicates failure.
External Functions (UDFs)
Ability to signal SQL NULL via a NULL
pointer C. Valderrama C.
Previous to Firebird 2, UDF authors only could guess that their UDFs might
return a null, but they had no way to ascertain it. This led to several problems
with UDFs. It would often be assumed that a null string would be passed as an
empty string, a null numeric would be equivalent to zero and a null date would
mean the base date used by the engine. For a numeric value, the author could not
always assume null if the UDF was compiled for an environment where it was
known that null was not normally recognized.
Several UDFs, including the ib_udf library distributed with Firebird, assumed
that an empty string was more likely to signal a null parameter than a string of
length zero. The trick may work with CHAR type, since the minimum declared
CHAR length is one and would contain a blank character normally: hence, binary
zero in the first position would have the effect of signalling NULL. However, but
it is not applicable to VARCHAR or CSTRING, where a length of zero is valid.
The other solution was to rely on raw descriptors, but this imposes a lot more
things to check than they would want to tackle. The biggest problem is that the
engine won't obey the declared type for a parameter; it will simply send whatever
data it has for that parameter, so the UDF is left to decide whether to reject the
result or to try to convert the parameter to the expected data type. Since UDFs
have no formal mechanism to signal errors, the returned value would have to be
used as an indicator.
The basic problem was to keep the simplicity of the typical declarations (no
descriptors) while at the same time being able to signal null.
The engine normally passed UDF parameters by reference. In practical terms,
that means passing a pointer to the data to tell the UDF that we have SQL NULL.
However, we could not impose the risk of crashing an unknown number of
different, existing public and private UDFs that do nt expect NULL. The syntax
had to be enhanced to enable NULL handling to be requested explicitly.
The solution, therefore, is to restrict a request for SQL NULL signaling to UDFs
that are known to be capable of dealing with the new scenario. To avoid adding
more keywords, the NULL keyword is appended to the UDF parameter type and
no other change is required.
Example
declare external function sample
int null
returns int by value...;
If you are already using functions from ib_udf and want to take advantage of null
signaling (and null recognition) in some functions, you should connect to your
desired database, run the script ../misc/upgrade/ib_udf_upgrade.sql that is in the
Firebird directory, and commit afterwards. It is recommended to do this when no
other users are connected to the database.
The code in the listed functions in that script has been modified to recognize null
only when NULL is signaled by the engine. Therefore, starting with FB v2,
rtrim, ltrim and several other string functions no longer assume that an empty
string means a NULL string.
The functions won't crash if you don't upgrade: they will simply be unable to
detect NULL.
If you have never used ib_udf in your database and want to do so, you should
connect to the database, run the script ../udf/ib_udf2.sql, preferably when no
other users are connected, and commit afterwards.
l Note the "2" at the end of the name.
l The original script for FB v1.5 is still available in the same directory.
UDF library diagnostic messages improved A. Peshkov
Diagnostics regarding a missing/unusable UDF module have previously made it
hard to tell whether a module was missing or access to it was being denied due to
the UDFAccess setting in firebird.conf. Now we have separate, understandable
messages for each case.

