find_unsafe_parallel_functions.py
text/x-python-script
Filename: find_unsafe_parallel_functions.py
Type: text/x-python-script
Part: 0
#!/usr/bin/env python
#
# Do any functions declared PARALLEL SAFE or PARALLEL RESTRICTED have a call
# graph that could apparently reach an unsafe function?
#
# Obtain a list of pg_proc functions that are declared 'r' or 's':
#
# psql postgres -t -c "select prosrc from pg_proc where proparallel != 'u' and prolang = 12" > pg_functions.data
#
# Obtain a list of edges in the function call graph (macOS):
#
# otool -tvV | \
# awk '/^_[^:]*:/ { caller = substr($1,2); }
# /\tcallq\t/ { callee = substr($3,2);
# printf("%s%s\n", caller, callee); }' > call_graph.data
from networkx import DiGraph
from networkx.algorithms.shortest_paths.generic import has_path
from networkx.algorithms.shortest_paths.generic import shortest_path
import re
unsafe_functions = ["AssignTransactionId"]
# prune certain subgraphs from the graph because they are false positives
ignore_functions = ["errfinish", "LockAcquire", "ExecEvalNextValueExpr", "ExecInitExpr", "SPI_execute_plan", "_SPI_execute_plan", "SPI_cursor_fetch"]
pg_functions = []
with open("pg_functions.data", "r") as data:
for line in data:
pg_functions.append(line.strip())
call_graph = DiGraph()
with open("call_graph.data", "r") as data:
for line in data:
caller, callee = line.strip().split(":")
if callee not in ignore_functions:
call_graph.add_node(caller)
call_graph.add_node(callee)
call_graph.add_edge(caller, callee)
for pg_function in pg_functions:
if call_graph.has_node(pg_function):
for unsafe_function in unsafe_functions:
if has_path(call_graph, pg_function, unsafe_function):
print "There is a path from %s to %s: %s" % (pg_function, unsafe_function, "->".join(shortest_path(call_graph, pg_function, unsafe_function)))