在Oracle 9中伪造存储概要(2)

发表于:2013-11-15来源:IT博客大学习作者:jametong点击数: 标签:oracle
你可以也会发现,Oracle 9中的signature(签名)字段的值与Oracle 8中的值是不同的. 这是由于Oracle这两个版本之间策略上的最主要的调整就是为了提高存储概要的重

  你可以也会发现,Oracle 9中的signature(签名)字段的值与Oracle 8中的值是不同的. 这是由于Oracle这两个版本之间策略上的最主要的调整就是为了提高存储概要的重复利用.在Oracle 8中,只有在你的SQL语句与存储的SQL语句完全匹配(包含空格符/大小写以及换行符)的时候才可以使用.到Oracle 9之后,这个限制放宽了,只要在去除掉重复的”空字符”并且将文本都转换成同样的大小写之后SQL语句能够匹配就可以使用存储概要了.例如,下面的两条 SQL语句将使用同一个存储概要.

  select * from t1 where id = 5;

  SELECT *

  FROM T1

  WHERE ID = 5;

  策略上的这个调整导致了第一次创建这个执行计划的SQL语句的签名的调整;如果你的数据库从Oracle 8升级到Oracle 9,就必须更新存储概要或者必须确认它们不再被使用.(事实上,别名为dbms_outln包outln_pkg包含一个特别的存储过程 update_signatures来处理这个问题.

  不过,关于Oracle 9中这些表的最意义重大的事情却是对查询语句中涉及到的文本与对象的极度详细描述.创建图-3中显示的例子,并在继续阅读之前详细查看ol$hints表中的内容.

  drop table t1;

  create table t1

  nologging

  as

  select

  rownum id,

  rownum n1,

  object_name,

  rpad('x',500) padding

  from

  all_objects

  where

  rownum <= 100

  ;

  alter table t1

  add constraint t1_pk primary key (id);

  create index t1_i1 on t1(n1);

  analyze table t1 compute statistics;

  create or replace outline demo_1 on

  select * from t1

  where id = 5

  and n1 = 10

  ;

  Figure 3 Sample code.

  这个例子立足于一个简单的小表,包含两组相近的列,其中一个列为逐渐(从而也创建了索引),另外包含一个简单的非唯一索引.我们为一个典型的查询创建一个存储概要来查看我们可以如何对待它.

  如果针对由这个例子创建的存储概要demo_1运行图-1中的示例查询,我们将发现这个查询将附带6个提示.

  STAGE NODE HINT

  3 1 NO_EXPAND

  3 1 ORDERED

  3 1 NO_FACT(T1)

  3 1 INDEX(T1 T1_PK)

  2 1 NOREWRITE

  1 1 NOREWRITE

  不出意外,其中的第四行显示我们将使用主键索引来访问这张表.如果我们实际上想要Oracle使用这个非唯一索引T1_I1访问表,我们该对存储概要做什么呢?理论上讲,我们可以调整这个存储概要以使得

  3 1 INDEX(T1 T1_PK)

  变成

  3 1 INDEX(T1 T1_I1)

  新特性

  我们可以做的第一件事是查看包dbms_outln_edit.这个包在Oracle 9中引入,正如它的名字提示的那样,它的目标是编辑存储概要,这看上去令人充满希望.

  然而,查看包的方法列表,检查文档手册,我们发现这个包只包含如下几个”编辑相关”的方法.

  CREATE_EDIT_TABLES

  DROP_EDIT_TABLES

  CHANGE_JOIN_POS

  前两个方法允许我们创建或删除outln用户拥有的表的本地拷贝.第三个方法允许我们交换一个存储概要计划中的表连接顺序. 哪怕仅仅是帮助我们修改一个简单的提示的方法也是没有的.目前,这个包看上去实际上一无是处-但是它们注定会越来越完善.

  当然B方案就是去侵入它了!如果我们登录到outln用户,并自己诊察ol$hints表(也就是支撑视图user_outline_hints的表)的内容,我们可以尝试下面的这个更新操作:

  update ol$hints

  set

  hint_text = 'INDEX(T1 T1_I1)'

  where

  ol_name = 'demo_1'

  and hint# = 4

  ;

  登录回到我们的测试Schema,清空共享池,并且打开存储概要:

  connect test_user/test

  alter system flush shared_pool;

  alter session set use_stored_outlines = true;

  实际上,我们将发现侵入的存储概要确实如你所愿了.但是这是一个让人不爽的解决方案,

  因为我们一直会给一个关于”更改数据字典表”的严厉的警告.

  旧方法(1)

  接着,我们的目标就是寻找一种迂回但又看似无害的方法来改变存储概要表的内容,并且不需要直接的侵入存储概要表.

  从前(在Oracle 9以前),我们有多种实现办法,它们都是基于这样一个事实,存储概要的效果仅仅取决于进来的SQL语句的文本,而完全不关心对对象类型或者对象的所有者.

  将表替换成添加过提示的视图是一种有效的方法.(我相信,这种方法最初是由Tom Kyte在它的《Expert One on One: Oracle》这本书中介绍的).

  连接到另外一个拥有表T1的访问权限的Schema,按照下面的定义创建一个添加过提示的视图,视图与表的名称保持一致.

  Create or replace view t1 as

  Select /*+ index(t1,t1_i1) */

  *

  from test_user.t1;

  一旦视图创建完成,就在这个schema下使用下面的这个命令”重编译”这个已存在的存储概要.

  alter outline demo_1 rebuild;

  注意:必须拥有权限alter any outline才可以执行这个命令.

  如果登录回到原来的schema,清空缓存(flush shared pool),并且启用存储概要,我们将会发现原来的查询语句现在如愿以偿的使用上了索引T1_I1.

  3 1 INDEX(T1 T1_I1)

  这样为什么可行?因为存储概要并不属于任何一个schema. 当我们在另外一个schema中重编译这个称为demo_1的存储概要的时候,名称T1应用到了一个本地的包含提示的视图上了,因此Oracle将这个提示包装进了真实的执行计划中,从而也进入了这个存储概要.通过查看视图user_outline_hints,将会发现关键的那一行已经变成了

原文转自:http://blogread.cn/it/article/1030