如何在Python 3.x中使用pyparsing从Oracle SQL脚本中删除注解?

kulphzqa  于 2023-06-05  发布在  Oracle
关注(0)|答案(1)|浏览(150)

我有一个Oracle SQL脚本,其中包含多个语句,这些语句由“;“。我正在尝试删除整个脚本中的所有注解。这包括:
1.单行注解--直到换行。
1.阻止注解/*... */
1.此外,我希望能够将整个脚本分割成语句。
我知道一个名为sqlparse的库可以格式化以删除代码,但我遇到了这个边缘情况:

edge_case = """myval := oneval || otherval ||--;
-- single comment line
lpad(nvl(myval, ' '), 10, ' ');
"""

formatted = sqlparse.format(edge_case, strip_comments=True)
sqlparse.split(formatted)

# returns ['myval := oneval || otherval ||--;', "lpad(nvl(myval, ' '), 10, ' ');"]
# it splits into 2 statements when it should only be 1!

我的想法是专注于每一个注解情况,然后尝试只通过由sql中所有有效字符组成的字符串标记来解析其余的非注解代码。

from pyparsing import *

# define basic elements
semicolon = Literal(";")
# all valid chars excluding semicolon
all_valid_chars = alphanums + "_-+*/.,:()[]{}<>=&|"

# define an sql token
sql_token = Word(all_valid_chars)

# need to make sure that "--" is a special case
single_line_comment_token = Literal("--")
single_line_comment = single_line_comment_token + SkipTo(line_end) + line_end
test1_single_line_comment = """
-- single comment line
--- also a comment
---- also a comment
-- comment -- continues
-- comment /* also continues */

# - not a comment
"""
single_line_comment.run_tests(test1_single_line_comment)

# also handle multi line comments
multi_line_comment_open = Literal("/*")
multi_line_comment_close = Literal("*/")
multi_line_comment = multi_line_comment_open + SkipTo(multi_line_comment_close) + multi_line_comment_close

test1_multi_line_comment = """
/* multi line comment */
/* outer comment /* inner comment */

/* /* /* still valid */
/* -- still valid */
"""
multi_line_comment.run_tests(test1_multi_line_comment)

test2_multi_line_comment = """
/* multi line comment /* with nested comment should fail! */ */
"""
multi_line_comment.run_tests(test2_multi_line_comment, failure_tests=True)

我现在被困在这里,不知道在哪里继续。

statement2 = OneOrMore(single_line_comment | multi_line_comment | sql_token) + semicolon

# run tests
print(statement2.parseString("myval := oneval || otherval ||--;"))

我的问题:
1.这些情况下的每一个测试工作,但我不知道我应该如何测试在一起,以涵盖重叠,如。-- /*这应该是一个单行注解等。
1.我不知道如何正确处理其余的非注解代码。例如,所有有效字符都应该包括-,但这会打乱我的单行代码吗?面临的技术问题较少:
1.老实说,我不知道从哪里开始,也不知道如何构建我的代码,因为这些情况可能会重叠。我甚至不确定pyparsing是否是我应该做的来解决这个问题,但是根据我看到的试图从sql中删除注解的问题,解析是唯一真实的可靠的解决方案。

  1. run_tests不能处理多行。这使得很难测试块注解,尽管我可以使用parse_string
h4cxqtbf

h4cxqtbf1#

对于剥离注解(或任何pyparsing表达式),您应该使用transform_string,而不是parse_stringtransform_string扫描输入字符串并应用解析操作和抑制。下面是一些从脚本中剥离python注解的代码:

import pyparsing as pp

comment = "#" + pp.rest_of_line
# could also use the provided `python_style_comment`
# comment = pp.python_style_comment

python_source = """
def hello(s):
    # say a nice hello!
    print(f"Hi, {s}!")
    # add a hashtag for social media
    print("#hello")
"""

# suppress comments and transform the string
print(comment.suppress().transform_string(python_source))

给出:

def hello(s):
    
    print(f"Hi, {s}!")
    
    print("

糟糕,这并没有检测到#hello在一个带引号的字符串中。
为了解决这个问题,我们还插入了一个带引号的字符串解析器,但是这些字符串不会被抑制:

# parse quoted strings first, in case they contain a comment
transform_expr = pp.quoted_string | comment.suppress()
print(transform_expr.transform_string(python_source))

现在给出:

def hello(s):
    
    print(f"Hi, {s}!")
    
    print("#hello")

对于SQL注解处理,您将执行大致相同的操作:

sql_single_line_comment = '--' + pp.rest_of_line

# use pyparsing's definition for C-style /* ... */ multiline comments
sql_multi_line_comment = pp.c_style_comment

comment_remover = (
    # parse quoted strings separately so that they don't get suppressed
    pp.quoted_string
    | (sql_single_line_comment | sql_multi_line_comment).suppress()
)

sql = "SELECT * FROM STUDENTS; -- watch out for Bobby Tables!"
print(comment_remover.transform_string(sql))

打印:

SELECT * FROM STUDENTS;

我很高兴看到你正在使用run_tests!如果要定义跨多行的测试,而不是使用多行字符串形式,请将测试作为字符串列表传递:

expr.run_tests([test_str1, test_str2, test_str3])

不幸的是,我没有调用transform_string的run_tests版本。
最好在第一次剥离注解之后,在第二次传递中将行拆分为单独的语句。您可以尝试以下操作:

semi = pp.Literal(";")
semi.add_parse_action(pp.replace_with(";\n\n")

然后使用与剥离注解相同的转换模式(但这一次使用的是解析操作而不是抑制)。或使用scan_string定位“;“终止符,然后将位于分号之间的SQL写出到它们各自的文件中(留给读者作为练习)。

相关问题