|
内容目录一、基本方法:1.parse(sql)2.format(sql)3.split()4.parsestream()二、Token三、其他类型四、案例:提取所有查询的字段和表名sqlparse是一个Python库,是一个用于Python的非验证SQL解析器,用于解析SQL语句并提供一个简单的API来访问解析后的SQL结构。可以帮助解析复杂的SQL查询,提取信息,或者对SQL语句进行一些基本的分析和操作。一、基本方法:sqlparse的__init__方法中提供了四个基础方法1.parse(sql)用于将一个或多个SQL语句的字符串解析成Python对象,这些对象构成了一个抽象语法树(AST)源码defparse(sql,encoding=None):"""Parsesqlandreturnalistofstatements.:paramsql:AstringcontainingoneormoreSQLstatements.:paramencoding:Theencodingofthestatement(optional).:returns:Atupleof:class:`~sqlparse.sql.Statement`instances."""returntuple(parsestream(sql,encoding))12345678'运行运行按照符号分割sql后返回一个元组,可以递归获取所有的值importsqlparseSQL="""CREATETABLEfoo(idintegerprimarykeycomment'id_comm',titlevarchar(200)notnullcomment'id_comm',descriptiontextcomment'id_comm');"""parsed=sqlparse.parse(SQL)[0]print(parsed)123456789102.format(sql)格式化代码,返回格式化后的代码字符串源码:defformat(sql,encoding=None,**options):"""Format*sql*accordingto*options*.Availableoptionsaredocumentedin:ref:`formatting`.Inadditiontotheformattingoptionsthisfunctionacceptsthekeyword"encoding"whichdeterminestheencodingofthestatement.:returns:TheformattedSQLstatementasstring."""12345678910'运行运行参数说明:sql:需要格式化的SQL语句字符串。reindent=True:自动重新缩进SQL语句,使代码块对齐。keyword_case=‘upper’:将SQL关键字转换为大写。可选值有’lower’、‘upper’或‘capitalize’。其他可选参数还包括indent_width(用于设置缩进的空格数,默认为2)、wrap_after(设置换行的字符数限制)等,以进一步定制输出样式。importsqlparsesql="""select*fromtblwhereid>10;"""format=sqlparse.format(sql,reindent=True,keyword_case='upper')print(format)#SELECT*#FROMtbl#WHEREid>10;12345678910113.split()按照符号分割sql语句,返回一个sql列表源码:defsplit(sql,encoding=None):"""Split*sql*intosinglestatements.:paramsql:AstringcontainingoneormoreSQLstatements.:paramencoding:Theencodingofthestatement(optional).:returns:Alistofstrings."""1234567'运行运行importsqlparsesql="""select*fromtblwhereid>10;select*fromtblwhereid>20;"""split=sqlparse.split(sql)print(split)#['select*fromtblwhereid>10;','select*fromtblwhereid>20;']123456784.parsestream()类似parse方法,流式解析sql,它的设计初衷是为了处理从流式输入(如文件、网络连接或任何可迭代的对象)读取的SQL代码,而不是一次性加载整个SQL字符串到内存中。这样,在处理大型SQL文件或连续的数据流时,可以更有效地管理内存。源码:defparsestream(stream,encoding=None):"""Parsessqlstatementsfromfile-likeobject.:paramstream:Afile-likeobject.:paramencoding:Theencodingofthestreamcontents(optional).:returns:Ageneratorof:class:`~sqlparse.sql.Statement`instances."""1234567'运行运行withopen('../static/pre_sql.sql','r',encoding='utf-8')asfile:forstatementinsqlparse.parse(file):print(statement)123二、Token源码:classToken:"""Baseclassforallotherclassesinthismodule.Itrepresentsasingletokenandhastwoinstanceattributes:``value``istheunchangedvalueofthetokenand``ttype``isthetypeofthetoken."""def__init__(self,ttype,value):value=str(value)self.value=valueself.ttype=ttypeself.parent=Noneself.is_group=Falseself.is_keyword=ttypeinT.Keywordself.is_whitespace=self.ttypeinT.Whitespaceself.normalized=value.upper()ifself.is_keywordelsevalue1234567891011121314151617sqlparse.sql.Token:这是最基本的Token类,表示SQL语句中的一个原子部分,如一个单词或者符号。它包含以下属性:value:该Token的实际文本内容,比如一个关键字像SELECT或一个标识符如表名。token_type:表示Token类型的枚举值,比如Keyword、Identifier、Punctuation等。position或start_pos:表示Token在原始SQL文本中的起始位置信息,有助于追踪Token的来源。相关Token子类和概念sqlparse.sql.Identifier:专门表示SQL中的标识符,如表名、列名等。这类Token可能会有额外的属性来表示是否为quotedidentifier(被引号包围的标识符)。sqlparse.sql.Keyword:表示SQL关键字,如SELECT,FROM,WHERE等。sqlparse.sql.Punctuation:表示SQL中的标点符号,如逗号,、分号;等。sqlparse.sql.Comment:用于表示SQL中的注释内容,可以是行内注释(--…)或块注释(/*…*/)。sqlparse.sql.Comparison:包含比较操作符(如=,!=,IN,BETWEEN等)以及它们两边的操作数,用于构建更复杂的表达式分析。sqlparse.sql.Statement:表示整个SQL语句,通常是由多个Token和其他Statement对象组成的树状结构,便于递归遍历整个SQL语句的结构。这里就需要引入sql解析的过程sql->语法分析器(Lexer)->Token流->语法分析器(Parse)->抽象语法树(AST)->树结构(TreeParse)每个解析结果都会附带一个tokens的属性,它是一个生成器,用于迭代解析后的Token序列,包含了一些类型信息,其中的类型信息有:#SpecialtokentypesText=Token.TextWhitespace=Text.WhitespaceNewline=Whitespace.NewlineError=Token.Error#Textthatdoesn'tbelongtothislexer(e.g.HTMLinPHP)Other=Token.Other#CommontokentypesforsourcecodeKeyword=Token.KeywordName=Token.NameLiteral=Token.LiteralString=Literal.StringNumber=Literal.NumberPunctuation=Token.PunctuationOperator=Token.OperatorComparison=Operator.ComparisonWildcard=Token.WildcardComment=Token.CommentAssignment=Token.Assignment#Generictypesfornon-sourcecodeGeneric=Token.GenericCommand=Generic.Command#StringandsomeothersarenotdirectchildrenofToken.#aliasthem:Token.Token=TokenToken.String=StringToken.Number=Number#SQLspecifictokensDML=Keyword.DMLDDL=Keyword.DDLCTE=Keyword.CTE1234567891011121314151617181920212223242526272829303132333435Text:基础文本类型,通常用于表示SQL语句中的普通文本部分。Whitespace:空白字符,包括空格、制表符等,用于分隔SQL语句的不同部分。Newline:特指换行符,用于标识新的一行开始。Error:表示解析过程中遇到的无法识别或错误的文本。Other:表示不属于当前解析器(如SQL解析器)预期的文本,例如在嵌入式SQL中可能遇到的其他语言(如HTML在PHP中的情况)。Keyword:SQL关键字,如SELECT,FROM,WHERE等。DML:数据操作语言(DataManipulationLanguage)关键字,如INSERT,UPDATE,DELETE,SELECT。DDL:数据定义语言(DataDefinitionLanguage)关键字,如CREATE,ALTER,DROP。CTE:公共表达式(CommonTableExpression)关键字,如WITH。Name:数据库对象名称,如表名、列名等。Literal:字面量值,直接写在SQL中的数据值。String:字符串字面量,如'examplestring'。Number:数字字面量,如42,3.14。Punctuation:标点符号,如逗号、括号等,用于分隔或包围SQL的各个部分。Operator:操作符,如+,-,*,/,=等。Comparison:比较操作符,如=,!=,等。Wildcard:通配符,如%在某些SQL上下文中的使用。Comment:注释,SQL中的单行或多行注释。Assignment:赋值操作符,如:=在某些SQL方言中用于赋值。Generic:通用类型,适用于非特定源代码的分隔。Command:命令,可能特指一些SQL命令或交互式shell命令。123456789101112131415161718192021Whitespace:空白字符(如空格、制表符、换行符等)Keyword:SQL关键字(如SELECT、FROM、WHERE等)Name:标识符(如表名、列名等)String.Single:单引号字符串字面量String.Double:双引号字符串字面量(在某些SQL方言中用于标识符)String.Backtick:反引号字符串字面量(如MySQL中的表名和列名)Identifier:表示SQL中的标识符,包括但不限于表名、列名、数据库名等。Compound:复合Token,可能包含多个子Token,用于更复杂的结构,如Case语句、When条件等。Number.Integer:整数Number.Float:浮点数Number.Hex:十六进制数Operator:操作符(如=、、+、-等)Punctuation:标点符号(如逗号、分号、括号等)Comment.Single:单行注释Comment.Multiline:多行注释Wildcard:通配符(如*)Function:函数名(如COUNT()、MAX()等)DML、DDL、DCL等:表示数据操作语言、数据定义语言、数据控制语言等的高级分类123456789101112131415161718三、其他类型有些属于token的属性但有些不属于token,比如Where、IdentifierList、Identifier、Parenthesis、Comment等sql='select1asid,name,casewhenname=""then3else4endasscorefromtblwhereid>10limit100'stmts=sqlparse.parse(sql)[0].tokensforstmtinstmts:print(f"{type(stmt)}::{stmt.ttype}::",stmt)#::Token.Keyword.DML::select#::Token.Text.Whitespace::#::None::1asid,name,casewhenname=""then3else4endasscore#::Token.Text.Whitespace::#::Token.Keyword::from#::Token.Text.Whitespace::#::None::tbl#::Token.Text.Whitespace::#::None::whereid>10#::Token.Keyword::limit#::Token.Text.Whitespace::#::Token.Literal.Number.Integer::10012345678910111213141516171819当查询有多列或者有多表时,会将其封装为IdentifierList,单表时候会被封装为Identifier,过滤条件被封装为Where,括号会被封装为Parenthesis,注释会被封装为Comment四、案例:提取所有查询的字段和表名importsqlparseimportresql='insertintotableinser_tblpartition(dt=dt)select1asid,name,casewhen(name=""orname="")then3else4endasscorefromtblwhereid>10limit100'stmts=sqlparse.parse(sql)[0].tokenscols=[]tbls=[]froms=[]wheres=[]last_key=''forstmtinstmts:ifstmt.value=='insert'orstmt.value=='select'orstmt.value=='from':last_key=stmt.value#剔除空格和换行ifstmt.ttypeissqlparse.tokens.Text.Whitespace:continue#关键字elifstmt.ttypeissqlparse.tokens.Keyword.DML:dml=stmt.valuelast_key=dml#字段elifisinstance(stmt,sqlparse.sql.IdentifierList):#判断上一个是什么类型iflast_key=='select':foridentifierinstmt.get_identifiers():col_name=identifier.valueifre.search('as',col_name,re.I):col_name=re.search('as(.*)',col_name,re.I).group(1).strip()cols.append(col_name)eliflast_key=='from':foridentifierinstmt.get_identifiers():froms.append(identifier.value)else:foridentifierinstmt.get_identifiers():tbls.append(identifier.value)elifisinstance(stmt,sqlparse.sql.Identifier):iflast_key=='select':cols.append(stmt.value)eliflast_key=='from':froms.append(stmt.value)else:tbls.append(stmt.value)elifisinstance(stmt,sqlparse.sql.Where):wheres.append(stmt.value)#表名print("cols:",cols)print("tbls:",tbls)print("froms:",froms)print("wheres:",wheres)#cols:['id','name','score']#tbls:['inser_tbl']#froms:['tbl']#wheres:['whereid>10']1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
|
|