一个可定制的用于scim2.0过滤器的解析器/transpiler
scim2-filter-parser的Python项目详细描述
说明
scim 2.0定义的查询如下:
'emails[type eq "work" and value co "@example.com"] or ims[type eq "xmpp" and value co "@foo.com"]'
这些可能很难使用,并转换成sql来运行数据库。
这就是scim 2.0过滤器解析器(sfp)可以提供帮助的地方。
SFP分为四个模块,每个模块处理 将scim调用转换为sql查询。
第一步是标记查询或词法分析,其中过滤器查询 被分解成许多代币组成。
sfp-lexer 'emails[type eq "work" and value co "@example.com"] or ims[type eq "xmpp" and value co "@foo.com"]' Token(type='ATTRNAME', value='emails', lineno=1, index=0) Token(type='LBRACKET', value='[', lineno=1, index=6) Token(type='ATTRNAME', value='type', lineno=1, index=7) Token(type='EQ', value='eq', lineno=1, index=12) Token(type='COMP_VALUE', value='work', lineno=1, index=15) Token(type='AND', value='and', lineno=1, index=22) Token(type='ATTRNAME', value='value', lineno=1, index=26) Token(type='CO', value='co', lineno=1, index=32) Token(type='COMP_VALUE', value='@example.com', lineno=1, index=35) Token(type='RBRACKET', value=']', lineno=1, index=49) Token(type='OR', value='or', lineno=1, index=51) Token(type='ATTRNAME', value='ims', lineno=1, index=54) Token(type='LBRACKET', value='[', lineno=1, index=57) Token(type='ATTRNAME', value='type', lineno=1, index=58) Token(type='EQ', value='eq', lineno=1, index=63) Token(type='COMP_VALUE', value='xmpp', lineno=1, index=66) Token(type='AND', value='and', lineno=1, index=73) Token(type='ATTRNAME', value='value', lineno=1, index=77) Token(type='CO', value='co', lineno=1, index=83) Token(type='COMP_VALUE', value='@foo.com', lineno=1, index=86) Token(type='RBRACKET', value=']', lineno=1, index=96)
第二步是将这一系列标记转换为抽象语法树。
sfp-parser 'emails[type eq "work" and value co "@example.com"] or ims[type eq "xmpp" and value co "@foo.com"]' Filter(expr=LogExpr, negated=False, namespace=None) LogExpr(op='or', expr1=Filter, expr2=Filter) Filter(expr=Filter, negated=False, namespace=None) Filter(expr=Filter, negated=False, namespace=AttrPath) Filter(expr=LogExpr, negated=False, namespace=None) LogExpr(op='and', expr1=Filter, expr2=Filter) Filter(expr=AttrExpr, negated=False, namespace=None) AttrExpr(value='eq', attr_path=AttrPath, comp_value=CompValue) AttrPath(attr_name='type', sub_attr=None, uri=None) CompValue(value='work') Filter(expr=AttrExpr, negated=False, namespace=None) AttrExpr(value='co', attr_path=AttrPath, comp_value=CompValue) AttrPath(attr_name='value', sub_attr=None, uri=None) CompValue(value='@example.com') AttrPath(attr_name='emails', sub_attr=None, uri=None) Filter(expr=Filter, negated=False, namespace=None) Filter(expr=Filter, negated=False, namespace=AttrPath) Filter(expr=LogExpr, negated=False, namespace=None) LogExpr(op='and', expr1=Filter, expr2=Filter) Filter(expr=AttrExpr, negated=False, namespace=None) AttrExpr(value='eq', attr_path=AttrPath, comp_value=CompValue) AttrPath(attr_name='type', sub_attr=None, uri=None) CompValue(value='xmpp') Filter(expr=AttrExpr, negated=False, namespace=None) AttrExpr(value='co', attr_path=AttrPath, comp_value=CompValue) AttrPath(attr_name='value', sub_attr=None, uri=None) CompValue(value='@foo.com') AttrPath(attr_name='ims', sub_attr=None, uri=None)
第三步是将ast转换成我们选择的语言。 上面的查询被转换成下面的sql。
sfp-transpiler 'emails[type eq "work" and value co "@example.com"] or ims[type eq "xmpp" and value co "@foo.com"]' ((emails.type = {0}) AND (emails.value LIKE {1})) OR ((ims.type = {2}) AND (ims.value LIKE {3})) {0: 'work', 1: '%@example.com%', 2: 'xmpp', 3: '%@foo.com%'}
第四步是获取sql where子句的一个片段并完成 SQL查询的其余部分。
sfp-query 'emails[type eq "work" and value co "@example.com"] or ims[type eq "xmpp" and value co "@foo.com"]' >>> DO NOT USE THIS OUTPUT DIRECTLY >>> SQL INJECTION ATTACK RISK >>> SQL PREVIEW: SELECT DISTINCT users.* FROM users LEFT JOIN emails ON emails.user_id = users.id LEFT JOIN schemas ON schemas.user_id = users.id WHERE ((emails.type = work) AND (emails.value LIKE %@example.com%)) OR ((ims.type = xmpp) AND (ims.value LIKE %@foo.com%));
请注意,sfp不使用预注入的参数构建sql查询。 这将造成SQL注入攻击漏洞。而是一个Query 对象已创建,可以强制显示自己,如上图所示 通过printing查询对象。
使用
尽管提供了命令行垫片,但该库旨在使用 以编程方式。库的用户应该实例化 scim2_filter_parser.query.Query类,具有属性映射和可选的 使查询中的所有必需字段都可访问所需的任何联接。
例如,如果用户信息存储在users表和电子邮件中 信息存储在不同的表emails中,然后是属性映射 连接可以这样定义:
attr_map = { ('userName', None, None): 'users.username', ('name', 'familyName', None): 'users.family_name', ('meta', 'lastModified', None): 'users.update_ts', ('emails', None, None): 'emails.address', ('emails', 'value', None): 'emails.address', } joins = ( 'LEFT JOIN emails ON emails.user_id = users.id', ) q = Query(filter, 'users', attr_map, joins) q.sql # Will be equal to 'SELECT * FROM users ... q.params # Will be equal to the paramters specific to the filter query.
属性映射(attr_map)是scim attribute,subattribute, 以及表字段的架构uri。您需要将此自定义为 特定数据库架构。
Query.sql方法返回可以用作第一个 使用您喜爱的数据库引擎调用cursor.execute()时的参数。 如果您使用的数据库需要替换字符,而不是%s, 然后您可以将Query类子类化,并重写placeholder类 级别变量。有关此子类的示例,请参见查询模块和单元测试 和sqlite一起。
Query.params方法返回一个可以用作 调用cursor.execute()时的第二个参数。
spped
SFP相当快。查看speed_test.py脚本了解长和短脚本的详细信息 已测试筛选查询。SFP在不到54微秒的时间内将一个简短的筛选查询转译为SQL。 对于更长的查询,sfp只花了273微秒。
scim2筛选器分析器git:(master)python-m timeit-s“import speed_test”“speed_test.short()” 10000圈,最好是3:53.8 usec/圈 scim2筛选器分析器git:(master)python-m timeit-s“import speed_test”“speed_test.long()” 1000个循环,最好3:273 usec/循环
-
该项目仍处于生命的初级阶段,应相应地加以利用。