2

这是我的用例:输入是表示任意复杂度的Oracle PL/SQL 语句的字符串。我们可以假设它是单个语句(不是脚本)。现在,这个输入字符串的几个位必须被重写

例如,表名需要加上前缀,选择列表中不使用列别名的聚合函数应该被分配一个默认值:

SELECT SUM(ABS(x.value)), 
TO_CHAR(y.ID,'111,111'),
y.some_col
FROM
tableX x,
(SELECT DISTINCT ID
FROM tableZ z
WHERE ID > 10) y
WHERE
...

变成

SELECT SUM(ABS(x.value)) COL1, 
TO_CHAR(y.ID,'111,111') COL2,
y.some_col
FROM
pref.tableX x,
(SELECT DISTINCT ID, some_col
FROM pref.tableZ z
WHERE ID > 10) y
WHERE
...

(免责声明:只是为了说明问题,声明没有意义)

由于聚合函数可能是嵌套的,而 subSELECTs 是一个 b_tch,我不敢使用正则表达式。好吧,实际上我做到了并取得了 80% 的成功,但我确实需要剩下的 20%。

我认为正确的方法是使用语法和解析器。我摆弄了 c++ ANTLR2(尽管我对语法和在这些帮助下的解析知之甚少)。我看不到获取 SQL 位的简单方法:

list<string> *ssel = theAST.getSubSelectList(); // fantasy land

任何人都可以提供一些关于“解析专业人员”如何解决这个问题的指示吗?编辑:我正在使用Oracle 9i

4

2 回答 2

2

也许您可以使用它,它将 select 语句更改为 xml 块:

declare
    cl clob;
begin
    dbms_lob.createtemporary (
        cl,
        true
    );
    sys.utl_xml.parsequery (
        user,
        'select e.deptno from emp e where deptno = 10',
        cl
    );
    dbms_output.put_line (cl);
    dbms_lob.freetemporary (cl);
end;
/ 

<QUERY>
  <SELECT>
    <SELECT_LIST>
      <SELECT_LIST_ITEM>
        <COLUMN_REF>
          <SCHEMA>MICHAEL</SCHEMA>
          <TABLE>EMP</TABLE>
          <TABLE_ALIAS>E</TABLE_ALIAS>
          <COLUMN_ALIAS>DEPTNO</COLUMN_ALIAS>
          <COLUMN>DEPTNO</COLUMN>
        </COLUMN_REF>
        ....
        ....
        ....
</QUERY>

见这里: http://forums.oracle.com/forums/thread.jspa?messageID=3693276& #3693276

现在你“只”需要解析这个 xml 块。

编辑1:

遗憾的是,我并不完全了解 OP 的需求,但我希望这会有所帮助(这是询问例如 query 列的“名称”的另一种方式select count(*),max(dummy) from dual):

set serveroutput on

DECLARE
 c       NUMBER;
 d       NUMBER;
 col_cnt PLS_INTEGER;
 f       BOOLEAN;
 rec_tab dbms_sql.desc_tab;
 col_num NUMBER;

PROCEDURE print_rec(rec in dbms_sql.desc_rec) IS
BEGIN
  dbms_output.new_line;
  dbms_output.put_line('col_type = ' || rec.col_type);
  dbms_output.put_line('col_maxlen = ' || rec.col_max_len);
  dbms_output.put_line('col_name = ' || rec.col_name);
  dbms_output.put_line('col_name_len = ' || rec.col_name_len);
  dbms_output.put_line('col_schema_name= ' || rec.col_schema_name);
  dbms_output.put_line('col_schema_name_len= ' || rec.col_schema_name_len);
  dbms_output.put_line('col_precision = ' || rec.col_precision);
  dbms_output.put_line('col_scale = ' || rec.col_scale);
  dbms_output.put('col_null_ok = ');

  IF (rec.col_null_ok) THEN
    dbms_output.put_line('True');
  ELSE
    dbms_output.put_line('False');
  END IF;
END;

BEGIN
  c := dbms_sql.open_cursor; 
  dbms_sql.parse(c,'select count(*),max(dummy) from dual ',dbms_sql.NATIVE); 
  dbms_sql.describe_columns(c, col_cnt, rec_tab);

  for i in rec_tab.first..rec_tab.last loop
    print_rec(rec_tab(i));
  end loop;

  dbms_sql.close_cursor(c);
END;
/

(有关更多信息,请参见此处:http ://www.psoug.org/reference/dbms_sql.html )

OP 还希望能够在查询中更改表的模式名称。我认为实现这一点的最简单方法是查询表名user_tables并在 sql 语句中搜索这些表名并为其添加前缀或执行'alter session set current_schema = ....'.

于 2009-10-23T17:24:44.473 回答
1

如果 SQL 语句字符串的来源是其他编码人员,您可以简单地坚持需要更改的部分只需用特殊的转义约定进行标记,例如,写 $TABLE 而不是表名,或者在需要的地方写 $TABLEPREFIX。然后可以通过子字符串搜索和替换来找到需要修补的地方。

如果您确实有任意 SQL 字符串并且无法很好地标记它们,那么您需要以某种方式解析 SQL 字符串,正如您所观察到的那样。XML 解决方案当然是一种可能的方式。

另一种方法是使用程序转换系统。这样的工具可以解析语言实例的字符串,构建 AST,对 AST 进行分析和转换,然后吐出修改后的字符串。

DMS Software Reengineering Toolkit就是这样一个系统。它有 PLSQL 前端解析器。它可以使用模式导向的转换来完成您似乎需要的重写。对于涉及选择项目的示例:

domain PLSQL.
rule use_explicit_column(e: expression):select_item -> select_item
   "\e" -> "\e \column\(\e\)".

要阅读规则,您需要了解引号内的内容代表我们想要操作的某些计算机语言中的抽象树。“域PLSQL”短语所说的是,“使用PLSQL解析器”来处理引用的字符串内容,这是它知道的。(DMS 有很多语言解析器可供选择)。术语“表达式”和“select_item”是来自感兴趣的语言的语法结构,例如在这种情况下是 PLSQL。请参阅 PLSQL 参考手册中的铁路图。反斜杠表示转义/元信息而不是目标语言语法。

规则所说的是,通过将那些解析元素转换为仅由表达式\e组成的select_item,将其转换为由相同表达式\e和相应列(\column(\e) )组成的select_item大概基于特定表的选择项列表中的位置。您必须实现一个函数,该函数可以从选择项的位置确定相应的名称。在此示例中,我选择定义列函数以接受感兴趣的表达式作为参数;表达式实际上是作为匹配树传递的,因此列函数可以确定它在 通过遍历抽象语法树来选择项目列表。

此规则仅处理选择项。您将添加更多规则来处理您感兴趣的其他各种情况。

转换系统为您做的是:

  • 解析感兴趣的语言片段
  • 构建 AST
  • 让您对感兴趣的地方进行模式匹配(通过进行 AST 模式匹配)但使用目标语言的表面语法
  • 用其他模式替换匹配的模式
  • 计算任意替换(作为 AST)
  • 从修改后的 AST 重新生成源文本。

虽然编写规则并不总是微不足道的,但如果您的问题被陈述为已提出,这是必要的。

XML 建议的解决方案是构建此类 AST 的另一种方法。尽管您可以从 XSLT 中获得很多东西,但它没有很好的模式匹配属性。我不知道 XML 是否有完整的解析树;DMS 解析器确实通过设计提供了此功能,因为如果您想要进行任意分析和转换,则需要它。

于 2009-10-26T07:58:27.753 回答