代码之家  ›  专栏  ›  技术社区  ›  mats

在C++中修改PL/SQL语句字符串

  •  2
  • mats  · 技术社区  · 17 年前

    这是我的用例:输入是一个表示 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
    ...
    

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

    因为聚合函数可能是嵌套的,子select是一个分支,所以我不敢使用正则表达式。嗯,事实上我做到了,取得了80%的成功,但我确实需要剩下的20%。

    我想,正确的方法是使用语法和解析器。 我用C++ ANTLR2调侃(虽然我不太懂语法,在这样的帮助下解析)。我看不出一个简单的方法来获取SQL位:

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

    有人能就“解析专家”如何解决这个问题提供一些建议吗? 编辑:我正在使用 Oracle 9i .

    2 回复  |  直到 17 年前
        1
  •  2
  •   tuinstoel    17 年前

    也许您可以使用它,它将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块。

    Eddi1:

    遗憾的是,我不完全理解OP的需求,但我希望这能有所帮助(这是询问例如查询的列的“名称”的另一种方式) 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 = ....' .

        2
  •  1
  •   Ira Baxter    17 年前

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

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

    另一种方法是使用 program transformation system . 这种工具可以为语言实例解析一个字符串,构建ASTS,对ASTS进行分析和转换,然后输出一个修改后的字符串。

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

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

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

    规则所说的是,转换那些被解析的元素 选择项 S 仅由一个 表达 \ E ,将其转换为 选择项 由相同的表达式组成 e 以及相应的列( \列(e) )可能基于特定表的选择项列表中的位置。你必须执行 柱 函数,可以从所选项目的位置确定相应的名称。在本例中,我选择定义column函数以接受感兴趣的表达式作为参数;该表达式实际上作为匹配的树传递,因此column函数可以确定它在 选择项 通过浏览抽象语法树列出。

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

    转换系统为您做的是:

    • 分析感兴趣的语言片段
    • 建造一个AST
    • 让您为感兴趣的地方进行模式匹配(通过执行AST模式匹配) 但是使用目标语言的表面语法
    • 用其他模式替换匹配的模式
    • 计算算术替换(如asts)
    • 从修改后的AST重新生成源文本。

    虽然写规则并不总是琐碎的,但如果你的问题是必要的 表示为构成。

    XML建议的解决方案是构建此类AST的另一种方法。它没有很好的模式匹配属性,尽管您可以从XSLT中获得很多。我不知道的是XML是否有完整详细的解析树;如果您想进行任意的分析和转换,DMS解析器会根据需要通过设计来提供这一点。