1

我正在开发一种将 Oracle SQL 转换为 ANSI SQL 的工具。我有一个可以解析 Oracle SQL 和 ANSI SQL 的语法。

我想从 AST 的 where 子句部分中提取 Oracle 外连接表达式,并在 AST 的 from 子句部分的末尾插入新的连接子句以匹配选择或子查询。

具有重写规则的树解析器可以进行这种类型的树转换吗?

即采用从 Oracle SQL 生成的 AST

SELECT
  a.columna, b.columnb
FROM
  tablea a,
  tableb b
WHERE
  a.columna2 (+) = b.columnb2 (+)
  AND
  a.columna3 = 'foo'
  AND
  b.columnb3 = 'bar'

并将其转换为 AST 用于 ANSI SQL

SELECT
  a.columna, b.columnb
FROM
  tablea a FULL OUTER JOIN tableb b ON (a.columna2 = b.columnb2 )
WHERE
  a.columna3 = 'foo'
  and
  b.columnb3 = 'bar'

注意 1:从子句中删除表引用tablea并替换为引用相同表和表别名的子句。tablebFROMJOIN

注意 2:Oracle 连接条件FULL OUTER JOIN通过 sql_condition 比较两侧的 OuterJoinIndicator (+) 标识为 a。

注意 3:连接条件比较从WHERE子句中删除,并用于构造连接子句 ON 条件 [删除了 OuterJoinIndicator(s)]。

4

2 回答 2

1

是的,这是很有可能的,特别是因为您有一个可以识别 Oracle 和 ANSI SQL 的语法。我曾经写过一个从 AREV BASIC 到 Visual BASIC 的翻译器,并且做了很多类似的转换。

在我的项目中,我使用了 ANTLR 2 并编写了一个主树语法,它什么也没做,只是根据我语法中的所有规则完全遍历树。然后我使用 ANTLR 2 的子类化来覆盖特定的规则来进行转换。我喜欢这一点,因为它让我可以逐步构建翻译,并在一次处理中保留我的所有表达式处理,在另一次处理中保持控制结构等。

ANTLR 3 不提供语法子类化,因此您将无法使用该方法。您将需要完整的树语法来打印生成的树。就个人而言,我会先编写该树语法并使其正常工作。然后我会复制该语法并删除所有操作,但添加重写 AST 的选项。然后修改转换所需的规则。如果您进行许多转换,您可能希望使用多遍,每遍使用一个树语法。您可能有一两次进行分析以帮助推动以后的传球。在我的 BASIC 翻译项目中,我进行了控制流分析、数据流分析和分析通过时的死代码删除。

如果您需要帮助编写特定的转换,您需要分享您的树语法。有很多树语法习语可以让你一头雾水。如果您需要帮助,Terence 的 ANTLR 3 书将是一笔宝贵的购买。如果您还没有编写树语法,请在遇到困难时发布问题。选择正确的根节点很重要。如果您想了解如何构建树和树解析器,可以查看我的 C 语法。它是 ANTLR 2,但树构建概念是相同的。 http://www.antlr3.org/grammar/cgram/grammars/

您需要保留评论和格式吗?这增加了另一层复杂性,我建议为此创建另一个问题。

于 2013-05-07T15:01:59.220 回答
0

如果您有两种不同的语法,您可能会发现语法中的“微小差异”导致这些子句的 AST 相当不同,因此您真正的问题是将一个的树结构转换为另一个的树结构. 而且您必须对整个树分段执行此操作,因为这些差异遍布整个语法。YMMV。

ANTLR 的树解析器很可能让您识别任意片段;这些当然是在其他语法的 AST 中生成等价物的线索。但是你必须编写很多这样的片段,以及代码对应的例程来逐个节点地组装等效的树。作为大型语法(例如 Oracle SQL)的一般规则,这可能需要大量工作。你可以这样做。

另一种选择是程序转换系统。这些工具允许您编写表面语法模式(例如,Oracle SQL 和 ANSI SQL 中的短语)来编码和直接应用您的转换。恕我直言,以这种方式编写转换要容易得多。你最终会写出这样的东西:

 source domain Oracle.
 target domain ANSISQL.
 rule xlate_Oracle_SELECT(c: columns, t1: table, t2: table,
                          c1: column, c2: column,
                          more_conditions: conditions):SQL_phrase
     "SELECT \c FROM \t1, \t2 WHERE c1 (+) = c2 (+) and \more_conditions";
  =>
      "SELECT \c FROM  \t1 FULL OUTER JOIN \t2 on ( c1 = c2 ) WHERE \more_conditions";

(反斜杠 ID 是模式变量,可以匹配在该位置合法的声明语法类型的任意子树。)

这样做的原因是转换工具用第一个语法解析第一个模式,因此得到一个可以匹配第一个语法树的树,并且类似地使用第二个语法解析第二个模式,得到一个替换树第二语法的规则。转换引擎将树匹配第一个模式,并用树替换第二个模式。因此,这样的规则将一小组蓝色树节点从蓝色树转换为一小组所需树类型的绿色节点。颜色类比应该清楚地表明,如果您想要准确的翻译,则必须将所有蓝色节点转换为绿色节点。

您需要额外的规则来翻译各种子条款,以掩盖语法中的差异,例如:

rule translate column(t: IDENTIFIER, c: IDENTIFIER, ):table->table
    "\t.\c" -> " \toSQLidentifier\(\t\).\toSQLidentifier\(\c\)";

这将通过调用自定义函数toSQLidentifier来处理两种语言拼写标识符的差异,该函数执行字符串黑客攻击。

我认为 ANTLR 不支持这种转换规则。您可以通过大量代码来模拟它。

如果您对两种语言都有一个“联合”语法(这就是您所暗示的),您可能会避免其中的一些,但这通常会给您带来高度模棱两可的语法,这会带来很多麻烦。如果你成功了,那么你只需要在语言不同的地方应用翻译规则(例如,一切都是蓝色节点)。

你也可以破解它:从左到右扫描树;prettyprint 等效的部分(弄清楚这一点比看起来更难),它们不同的地方,prettyprint 替换。这是一种非常脆弱的方法。

于 2013-05-07T14:03:50.603 回答