pg_query
这个 Ruby 扩展使用实际的 PostgreSQL 服务器源来解析 SQL 查询并返回内部 PostgreSQL 解析树。
此外,该扩展允许您规范化查询(用 $n 替换常量值)并将这些规范化的查询再次解析为解析树。
当您构建此扩展时,它会构建 PostgreSQL 服务器源的部分(参见libpg_query),然后将其静态链接到此扩展中。
这看起来很复杂,但这是解析所有有效 PostgreSQL 查询的唯一可靠方法。
您可以在此处找到更多示例和更长的理由:https ://pganalyze.com/blog/parse-postgresql-queries-in-ruby.html
安装
sql
gem install pg_query
由于编译 PostgreSQL 的部分内容,在较慢的系统
上安装可能需要一段时间。预计最多需要 5 分钟。
用法
解析查询
sql
PgQuery.parse("SELECT 1")
=> #<PgQuery::ParserResult:0x000000012ec4e9e0
@query="SELECT 1",
@tree=<PgQuery::ParseResult:
version: 160001,
stmts: [
<PgQuery::RawStmt:
stmt: <PgQuery::Node:
select_stmt: <PgQuery::SelectStmt:
distinct_clause: [],
target_list: [
<PgQuery::Node:
res_target: <PgQuery::ResTarget:
name: "",
indirection: [],
val: <PgQuery::Node:
a_const: <PgQuery::A_Const:
ival: <PgQuery::Integer: ival: 1>,
isnull: false,
location: 7
>
>,
location: 7
>
>
],
from_clause: [],
group_clause: [],
group_distinct: false,
window_clause: [],
values_lists: [],
sort_clause: [],
limit_option: :LIMIT_OPTION_DEFAULT,
locking_clause: [],
op: :SETOP_NONE,
all: false
>
>,
stmt_location: 0,
stmt_len: 0
>
]
>,
@warnings=[],
@tables=nil,
@aliases=nil,
@cte_names=nil,
@functions=nil
>
修改已解析的查询并将其再次转换为 SQL
这是一个简单的例子deparse,对于更复杂的修改,请使用walk!。
sql
parsed_query = PgQuery.parse("SELECT * FROM users")
# Modify the parse tree in some way
parsed_query.tree.stmts[0].stmt.select_stmt.from_clause[0].range_var.relname = 'other_users'
# Turn it into SQL again
parsed_query.deparse
=> "SELECT * FROM other_users"
解析规范化查询
sql
# Normalizing a query (like pg_stat_statements in Postgres 10+)
PgQuery.normalize("SELECT 1 FROM x WHERE y = 'foo'")
=> "SELECT $1 FROM x WHERE y = $2"
从查询中提取表
sql
PgQuery.parse("SELECT $1 FROM x JOIN y USING (id) WHERE z = $2").tables
=> ["x", "y"]
从查询中提取列
sql
PgQuery.parse("SELECT $1 FROM x WHERE x.y = $2 AND z = $3").filter_columns
=> [["x", "y"], [nil, "z"]]
指纹查询
sql
PgQuery.parse("SELECT 1").fingerprint
=> "50fde20626009aba"
PgQuery.parse("SELECT 2; --- comment").fingerprint
=> "50fde20626009aba"
# Faster fingerprint method that is implemented inside the native C library
PgQuery.fingerprint("SELECT $1")
=> "50fde20626009aba"
将查询扫描成标记
sql
PgQuery.scan('SELECT 1 --comment')
=> [<PgQuery::ScanResult: version: 160001, tokens: [
<PgQuery::ScanToken: start: 0, end: 6, token: :SELECT, keyword_kind: :RESERVED_KEYWORD>,
<PgQuery::ScanToken: start: 7, end: 8, token: :ICONST, keyword_kind: :NO_KEYWORD>,
<PgQuery::ScanToken: start: 9, end: 18, token: :SQL_COMMENT, keyword_kind: :NO_KEYWORD>]>,
[]]
遍历解析树
对于通用用途,PgQuery 提供了walk!一种以递归方式处理已解析查询的方法。
这可用于创建定制的漂亮打印机:
sql
parsed_query = PgQuery.parse "SELECT * FROM tbl"
parsed_query.walk! { |node, k, v, location| puts k }
更有用的是,这可用于重写查询。例如:
sql
parsed_query.walk! do |node, k, v, location|
next unless k.eql?(:range_var) || k.eql?(:relation)
next if v.relname.nil?
v.relname = "X_" + v.relname
end
parsed_query.deparse
这个例子中有一些注意事项和限制。
首先,部分树节点被冻结。您可以替换它们,但无法就地修改。
其次,表重写比此示例更微妙。虽然这将重写表名,但它不会正确处理所有 CTE,也不会重写具有明确表名的列。
支持的 Ruby 版本
目前已测试并正式支持的 Ruby 版本:
- CRuby 2.7
- CRuby 3.0
- CRuby 3.1
- CRuby 3.2
- CRuby 3.3
不支持: - JRuby:pg_query依赖于 C 扩展,不鼓励使用/不支持 JRuby
- TruffleRuby:GraalVM不支持 sigjmp,它由 Postgres 错误处理代码使用(pg_query使用
Postgres 解析器和错误处理代码的副本)
#PG证书#PG考试#PostgreSQL培训#PostgreSQL考试#PostgreSQL认证
原作者:卢卡斯·菲特尔