3. ỨNG DỤNG PHÂN TÍCH MỘT SỐ CÚ PHÁP ĐƠN GIẢN
3.4. Phân tích câu lệnh SELECT SQL
Bộ phân tích đơn giản nhằm trích xuất thông tin table và column từ câu lệnh SELECT.
Cấu tạo
SELECT
<danh sách columns> FROM
<danh sách tables> WHERE
<danh sách điều kiện – chứa các biểu thức so sánh và câu lệnh select>
Sau đây là đoạn mã định nghĩa cho đối tượng phân tích simpleSQL.
Do đoạn code khá dài nên để thuận tiện, ta sẽ giải thích trực tiếp trong đoạn mã. # import các đối tượng sử dụng
from pyparsing import Literal, CaselessLiteral, Word, Upcase, delimitedList, Optional, \ Combine, Group, alphas, nums, alphanums, ParseException, Forward, oneOf,
quotedString, \
ZeroOrMore, restOfLine, Keyword
# Định nghĩa các SQL tokens
# Đối tượng kiểu Forward sẽ cho phép sử dụng toán tử << để bổ sung thêm thành phần theo đúng thứ tự
selectStmt = Forward()
# Keyword cho phép so khớp chính xác với nghĩa keyword, có nghĩa nó phải theo sau bởi một kí tự không phải keyword.
selectToken = Keyword("select", caseless=True) fromToken = Keyword("from", caseless=True)
# alphas = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' # alphanums = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVW...
ident = Word( alphas, alphanums + "_$" ).setName("identifier")
# delimitedList nối các ident với nhau theo .
columnName = Upcase( delimitedList( ident, ".", combine=True ) )
# Group sẽ nhóm các kết quả về dạng 0,1 hoặc n phần tử trong danh sách
columnNameList = Group( delimitedList( columnName ) )
tableName = Upcase( delimitedList( ident, ".", combine=True ) ) tableNameList = Group( delimitedList( tableName ) )
# Mệnh đề điều kiện
whereExpression = Forward()
# Định nghĩa các phép kết trong biểu thức bool
and_ = Keyword("and", caseless=True) or_ = Keyword("or", caseless=True) in_ = Keyword("in", caseless=True) E = CaselessLiteral("E")
# Định nghĩa các toán tử
binop = oneOf("= != < > >= <= eq ne lt le gt ge", caseless=True)
# Định nghĩa âm dương
arithSign = Word("+-",exact=1)
# Định nghĩa văn phạm một giá trị thực ( có dấu chấm động)
realNum = Combine( Optional(arithSign) + ( Word( nums ) + "." + Optional( Word(nums) ) | ( "." + Word(nums) ) ) + Optional( E + Optional(arithSign) + Word(nums) ) )
# Định nghĩa văn phạm số nguyên
intNum = Combine( Optional(arithSign) + Word( nums ) + Optional( E + Optional("+") + Word(nums) ) )
# Giá trị của columns
columnRval = realNum | intNum | quotedString | columnName # need to add support for alg expressions
# Mệnh đề điều kiện chứa : # - column <op> value
# - column IN (sanh sách value) # - column IN (một câu lệnh select) # - (Một mệnh đề điều kiện con)
whereCondition = Group(
( columnName + binop + columnRval ) |
( columnName + in_ + "(" + delimitedList( columnRval ) + ")" ) | ( columnName + in_ + "(" + selectStmt + ")" ) |
( "(" + whereExpression + ")" ) )
# Một mệnh đề điều kiện là kết hợp AND hoặc OR các mệnh đề điều kiện con
whereExpression << whereCondition + ZeroOrMore( ( and_ | or_ ) + whereExpression )
# Định nghĩa toàn bộ văn phạm
selectStmt << ( selectToken + ( '*' | columnNameList ).setResultsName( "columns" ) + fromToken +
tableNameList.setResultsName( "tables" ) +
Optional( Group( CaselessLiteral("where") + whereExpression ), "" ).setResultsName("where") )
simpleSQL = selectStmt
# Bõ qua các chú thích trên cùng 1 dòng
oracleSqlComment = "--" + restOfLine simpleSQL.ignore( oracleSqlComment )
Thực hiện test và kiểm tra kết quả def test( str ):
print str,"->" try:
tokens = simpleSQL.parseString( str ) print "tokens = ", tokens
print "tokens.columns =", tokens.columns print "tokens.tables =", tokens.tables print "tokens.where =", tokens.where except ParseException, err:
print " "*err.loc + "^\n" + err.msg print err
test( "SELECT * from XYZZY, ABC" ) SELECT * from XYZZY, ABC ->
tokens = ['select', '*', 'from', ['XYZZY', 'ABC'], ''] tokens.columns = *
tokens.tables = ['XYZZY', 'ABC'] tokens.where = ['']
test( "select * from SYS.XYZZY" ) select * from SYS.XYZZY ->
tokens = ['select', '*', 'from', ['SYS.XYZZY'], ''] tokens.columns = *
tokens.tables = ['SYS.XYZZY'] tokens.where = ['']
test( "Select A from Sys.dual where a in ('RED','GREEN','BLUE') and b in (10,20,30)" )
Select A from Sys.dual where a in ('RED','GREEN','BLUE') and b in (10,20,30) ->
tokens = ['select', ['A'], 'from', ['SYS.DUAL'], ['where', ['A', 'in', '(', "'RED'", "'GREEN'", "'BLUE'", ')'], 'and', ['B', 'in', '(', '10', '20', '30', ')']]]
tokens.columns = ['A']
tokens.tables = ['SYS.DUAL']
tokens.where = [['where', ['A', 'in', '(', "'RED'", "'GREEN'", "'BLUE'", ')'], 'and', ['B', 'in', '(', '10', '20', '30', ')']]]
test( "Select A,b from table1,table2 where table1.id eq table2.id -- test out
comparison operators" )
Select A,b from table1,table2 where table1.id eq table2.id -- test out comparison operators -> tokens = ['select', ['A', 'B'], 'from', ['TABLE1', 'TABLE2'], ['where', ['TABLE1.ID', 'eq',
'TABLE2.ID']]]
tokens.columns = ['A', 'B']
tokens.tables = ['TABLE1', 'TABLE2']