This is an automated email from the ASF dual-hosted git repository. domino pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/madlib.git
commit 39c242185253ebaf8cf9bdd4c67b897fdaff673f Author: Domino Valdano <dvald...@vmware.com> AuthorDate: Wed Dec 16 18:44:45 2020 -0800 Add debug.plpy.prepare to utilities/debug.py_in Saves the original sql string, so that EXPLAIN plan can be built by execute which only receives an opaque C object (not sql string). --- src/ports/postgres/modules/utilities/debug.py_in | 83 ++++++++++++++++++++---- 1 file changed, 72 insertions(+), 11 deletions(-) diff --git a/src/ports/postgres/modules/utilities/debug.py_in b/src/ports/postgres/modules/utilities/debug.py_in index 7051dd6..5e288be 100644 --- a/src/ports/postgres/modules/utilities/debug.py_in +++ b/src/ports/postgres/modules/utilities/debug.py_in @@ -58,12 +58,53 @@ def print_mst_keys(table, label, force=False): dist_key = r[dist_key_col] plpy_orig.info("|_MST_KEYS_{label}|{mst_key}|{seg_id}|{dist_key}|{table}".format(**locals())) +class prep_entry: + def __init__(self, sql, args, kwargs): + self.sql = sql + self.args = args + self.kwargs = kwargs + +def plpy_prepare(*args, **kwargs): + """ debug.plpy.prepare(sql, ..., force=False) + + If you want debug.plpy.execute() to be able + to display the query and/or plan for a + prepared query, you must call this function + (as debug.plpy.prepare() ) in place of + regular plpy.prepare(). Otherwise the execute + wrapper will not have access to the query string, + so you will only get timing info (no plan). + """ + force = False + if 'force' in kwargs: + force = kwargs['force'] + del kwargs['force'] + + plpy = plpy_orig # override global plpy, + # to avoid infinite recursion + + if not (plpy_execute_enabled or force): + return plpy.prepare(*args, **kwargs) + + if len(args) < 1: + raise TypeError('debug.plpy.execute() takes at least 1 parameter, 0 passed') + elif type(sql) != str: + raise TypeError('debug.plpy.prepare() takes a str as its 1st parameter') + + sql = args[0] + plpy.info(sql) + + plan = plpy_orig.prepare(*args, **kwargs) + prep = prep_entry(sql, args[1:], kwargs) + plpy_wrapper.prepared_queries[plan] = prep + return plan + plpy_execute_enabled = False def plpy_execute(*args, **kwargs): - """ debug.plpy.execute(sql, ..., force=False) + """ debug.plpy.execute(q, ..., force=False) - Replace plpy.execute(sql, ...) with - debug.plpy.execute(sql, ...) to debug + Replace plpy.execute(q, ...) with + debug.plpy.execute(q, ...) to debug a query. Shows the query itself, the EXPLAIN of it, and how long the query takes to execute. @@ -81,17 +122,32 @@ def plpy_execute(*args, **kwargs): return plpy.execute(*args, **kwargs) if len(args) > 0: - sql = args[0] + q = args[0] else: raise TypeError('debug.plpy.execute() takes at least 1 parameter, 0 passed') - if type(sql) == str: # can't print if a PLyPlan object - plpy.info(sql) + prep = None + if type(q) == str: + plpy.info(q) + sql = q + elif repr(type(q)) == "<type 'PLyPlan'>": + if q in plpy_wrapper.prepared_queries: + prep = plpy_wrapper.prepared_queries[q] + sql = prep.sql + else: + sql = q + else: + raise TypeError( + "First arg of debug.plpy.execute() must be str or <type 'PLyPlan'>, got {}".format(type(q)) + ) - # Print EXPLAIN of sql command - res = plpy.execute("EXPLAIN " + sql, *args[1:], **kwargs) - for r in res: - plpy.info(r['QUERY PLAN']) + # Print EXPLAIN of sql command + explain_query = "EXPLAIN" + sql + if prep: + explain_query = plpy.prepare(explain_query, *prep.args, **prep.kwargs) + res = plpy.execute(explain_query, *args[1:], **kwargs) + for r in res: + plpy.info(r['QUERY PLAN']) # Run actual sql command, with timing start = time.time() @@ -139,7 +195,12 @@ def plpy_debug(*args, **kwargs): else: plpy_orig.debug(*args, **kwargs) -class plpy: +class plpy_wrapper: + prepare = staticmethod(plpy_prepare) execute = staticmethod(plpy_execute) info = staticmethod(plpy_info) debug = staticmethod(plpy_debug) + + prepared_queries = dict() + +plpy = plpy_wrapper