生成抽象语法树的例子
sqlparse 是一个用于解析和分析 SQL 语句的 Python 库。它提供了一系列功能,可用于解析和操作 SQL 语句的不同组成部分,如关键字、标识符、表达式、函数、注释等。
以下是 sqlparse 库的一些主要功能:
解析 SQL 语句
sqlparse 可以解析和分析 SQL 语句,并将其拆分为不同的标记(token)。这使得您可以访问和操作 SQL 语句的各个部分,如 SELECT 语句中的列、表名、WHERE 子句等。
以sql代码SELECT * FROM users WHERE id = '1' OR '1'='1' or (1=2) or (x = '(123)')为例,编写了如下示例:
import sqlparse
# 递归打印 SQL 解析树
def print_tokens(tokens, indent=0):
for token in tokens:
if hasattr(token, "tokens"):
print_tokens(token.tokens, indent + 4)
else:
print(" " * indent,token.ttype, token.value)
# 解析 SQL 查询语句
def demo1():
user_input = "1' OR '1'='1"
sql_query = "SELECT * FROM users WHERE id = '" + user_input + "' or (1=2) or (x = '(123)')"
print(sql_query)
parsed = sqlparse.parse(sql_query)[0]
# 打印 AST 对象
print_tokens(parsed.tokens)
if __name__ == '__main__':
demo1()
运行的结果为:
Token.Keyword.DML SELECT
Token.Text.Whitespace
Token.Wildcard *
Token.Text.Whitespace
Token.Keyword FROM
Token.Text.Whitespace
Token.Name users
Token.Text.Whitespace
Token.Keyword WHERE
Token.Text.Whitespace
Token.Name id
Token.Text.Whitespace
Token.Operator.Comparison =
Token.Text.Whitespace
Token.Literal.String.Single '1'
Token.Text.Whitespace
Token.Keyword OR
Token.Text.Whitespace
Token.Literal.String.Single '1'
Token.Operator.Comparison =
Token.Literal.String.Single '1'
Token.Text.Whitespace
Token.Keyword or
Token.Text.Whitespace
Token.Punctuation (
Token.Literal.Number.Integer 1
Token.Operator.Comparison =
Token.Literal.Number.Integer 2
Token.Punctuation )
Token.Text.Whitespace
Token.Keyword or
Token.Text.Whitespace
Token.Punctuation (
Token.Name x
Token.Text.Whitespace
Token.Operator.Comparison =
Token.Text.Whitespace
Token.Literal.String.Single '(123)'
Token.Punctuation )
可以发现,sqlparse将SQL拆分成了一个一个token,在语法和词法解析领域,Token(记号)是指源代码中的最小语义单元,它代表了编程语言中的一个词法元素。Token 是语法分析器(Parser)在解析源代码时所使用的基本单位。
在编程语言中,Token 可以表示关键字、标识符、操作符、常量、字符串、注释等各种语法成分。语法分析器通过词法分析(Lexical Analysis)将源代码分解为一系列的 Token,以便进行进一步的语法分析和语义处理。
词法分析器(也称为词法解析器或扫描器)负责将源代码字符串转换为一系列 Token。它通过扫描源代码字符流,识别出连续的字符组成的词法单元,并为每个词法单元分配一个相应的 Token 类型。每个 Token 类型通常具有一个唯一的标识符和相关的属性值。
例如,在下面的代码片段中:
词法分析器将识别出以下 Token:
·标识符 Token:x
· 操作符 Token:=
· 数字常量 Token:10
· 操作符 Token:+
· 标识符 Token:y
词法分析器将将这些 Token 传递给语法分析器,后者负责根据语法规则进行进一步的解析和分析。
通过使用 Token,语法分析器可以更容易地理解和处理源代码的结构。每个 Token 都包含了与之相关的类型信息和属性值,使得语法分析器能够根据 Token 类型进行相应的语法规则匹配和语义处理。
在拿到这棵词法解析树之后,不管sql注入如何变幻,我们都可以定位到最小token单元,从而动态监测sql注入的行为。
格式化 SQL 语句
sqlparse 可以根据预定义的样式规则对 SQL 语句进行格式化。这使得 SQL 语句的结构更加清晰可读,便于理解和调试。
当使用 sqlparse 库来格式化 SQL 语句时,它会执行以下操作:
1.重新缩进:sqlparse 会根据语句的嵌套结构重新缩进语句,使其更具可读性和结构清晰。它会根据语句中的关键字、括号、逗号等进行缩进操作,以反映语句的层次结构。
2.关键字大小写转换:sqlparse 可以根据指定的选项将关键字转换为特定的大小写形式。您可以选择将关键字转换为大写、小写或首字母大写。这可以使 SQL 语句中的关键字在格式化后保持一致性。
3.空格和换行符处理:sqlparse 会添加适当的空格和换行符,以提高语句的可读性。它会在逗号、括号、运算符等符号周围添加空格,使语句更易于理解。此外,它会根据语句的结构添加适当的换行符,以使语句在屏幕上更好地展示。
4.注释处理:sqlparse 会解析 SQL 语句中的注释,并根据注释的位置将其正确地应用于格式化的结果。这有助于确保注释在格式化后保持正确的位置和格式。
5.字符串处理:sqlparse 会解析和处理 SQL 语句中的字符串,以确保在格式化后字符串的内容保持不变。它会保留字符串中的引号和转义字符,以保持字符串的完整性。
下面是一个例子:
def demo2():
sql_statement = 'select * from users where deleted_mark = false '
formatted_sql = sqlparse.format(sql_statement, reindent=True, keyword_case='upper')
print(formatted_sql)
上述代码中,我们的sql语句为select * from users where deleted_mark = false,最终的结果为:
SELECT *
FROM users
WHERE deleted_mark = FALSE
分析 SQL 语句结构
sqlparse 可以分析 SQL 语句的结构,并提供了一组 API 用于查找和操作特定类型的 SQL 元素。例如,您可以使用 sqlparse 查找和访问所有的表名、列名、函数调用等。
过滤 SQL 片段
sqlparse 可以根据指定的条件过滤和选择 SQL 语句中的特定部分。例如,您可以使用 sqlparse 仅提取 SELECT 语句中的列名,或者仅提取 WHERE 子句中的条件表达式。
解析 SQL 片段
sqlparse 不仅可以解析完整的 SQL 语句,还可以解析和分析 SQL 片段。这使得您可以处理和操作 SQL 片段而不仅仅是完整的语句。
python
ast库解析python代码
def demo3():
import ast
code = """
def greet(name=123):
print(f"Hello, {name}!")
return 1
"""
tree = ast.parse(code)
class MyVisitor(ast.NodeVisitor):
def visit_FunctionDef(self, node):
print("Function definition:", node.name)
# 获取参数名和默认值
for arg in node.args.args:
print("Parameter name:", arg.arg)
# 获取返回值
for statement in node.body:
if isinstance(statement, ast.Return):
print("Return value:", ast.literal_eval(statement.value))
self.generic_visit(node)
visitor = MyVisitor()
visitor.visit(tree)
运行的结果为
Function definition: greet
Parameter name: name
Return value: 1