mirror of https://github.com/mongodb/mongo
1211 lines
37 KiB
Python
1211 lines
37 KiB
Python
"""GDB Pretty-printers for types in mongo::optimizer."""
|
|
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import gdb
|
|
import gdb.printing
|
|
|
|
if not gdb:
|
|
sys.path.insert(0, str(Path(os.path.abspath(__file__)).parent.parent.parent))
|
|
from buildscripts.gdb.mongo import get_boost_optional, lookup_type
|
|
|
|
OPTIMIZER_NS = "mongo::optimizer"
|
|
|
|
# Tracks the indentation for Op* tree types.
|
|
operator_indent_level = 0
|
|
|
|
|
|
def strip_namespace(value):
|
|
return str(value).split("::")[-1]
|
|
|
|
|
|
def eval_print_fn(val, print_fn):
|
|
"""Evaluate a print function, and return the resulting string."""
|
|
|
|
# The generated output from explain contains the string "\n" (two characters)
|
|
# replace them with a single EOL character so that GDB prints multi-line
|
|
# explains nicely.
|
|
pp_result = print_fn(val)
|
|
pp_str = str(pp_result).replace("\"", "").replace("\\n", "\n")
|
|
return pp_str
|
|
|
|
|
|
class OptimizerTypePrinter(object):
|
|
"""Base class that pretty prints via a single argument C++ function."""
|
|
|
|
def __init__(self, val, print_fn_name):
|
|
"""Initialize base printer."""
|
|
self.val = val
|
|
print_fn_symbol = gdb.lookup_symbol(print_fn_name)[0]
|
|
if print_fn_symbol is None:
|
|
# Couldn't find the function, this could be due to a variety of reasons but mostly
|
|
# likely it is not included and/or optimized out of the executable being debugged
|
|
# (e.g. there are some explain helpers which are only called from unit tests so won't
|
|
# be included in a mongod binary).
|
|
print("Warning: Could not find pretty print function: " + print_fn_name)
|
|
self.print_fn = None
|
|
else:
|
|
self.print_fn = print_fn_symbol.value()
|
|
|
|
@staticmethod
|
|
def display_hint():
|
|
"""Display hint."""
|
|
return None
|
|
|
|
def to_string(self):
|
|
"""Return string for printing."""
|
|
if self.print_fn is None:
|
|
return f"<{self.val.type}>"
|
|
return eval_print_fn(self.val, self.print_fn)
|
|
|
|
|
|
class IntervalPrinter(OptimizerTypePrinter):
|
|
"""Pretty-printer for mongo::optimizer::IntervalRequirement."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize IntervalPrinter."""
|
|
super().__init__(val, "ExplainGenerator::explainInterval")
|
|
|
|
|
|
class CompoundIntervalPrinter(OptimizerTypePrinter):
|
|
"""Pretty-printer for mongo::optimizer::CompoundIntervalRequirement."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize CompoundIntervalPrinter."""
|
|
super().__init__(val, "ExplainGenerator::explainCompoundInterval")
|
|
|
|
|
|
class CandidateIndexEntryPrinter(OptimizerTypePrinter):
|
|
"""Pretty-printer for mongo::optimizer::CandidateIndexEntry."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize CandidateIndexEntryPrinter."""
|
|
super().__init__(val, "ExplainGenerator::explainCandidateIndex")
|
|
|
|
|
|
class IntervalExprPrinter(OptimizerTypePrinter):
|
|
"""Pretty-printer for mongo::optimizer::IntervalRequirement::Node."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize IntervalExprPrinter."""
|
|
super().__init__(val, "ExplainGenerator::explainIntervalExpr")
|
|
|
|
|
|
class PSRExprPrinter(OptimizerTypePrinter):
|
|
"""Pretty-printer for mongo::optimizer::PSRExpr::Node."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize PSRExprPrinter."""
|
|
super().__init__(val, "ExplainGenerator::explainPartialSchemaReqExpr")
|
|
|
|
|
|
class StrongStringAliasPrinter(object):
|
|
"""Pretty-printer for mongo::optimizer::StrongStringAlias."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize StrongStringAliasPrinter."""
|
|
self.val = val
|
|
|
|
@staticmethod
|
|
def display_hint():
|
|
"""Display hint."""
|
|
return None
|
|
|
|
def to_string(self):
|
|
return self.val["_value"]
|
|
|
|
|
|
class MemoPrinter(OptimizerTypePrinter):
|
|
"""Pretty-printer for mongo::optimizer::cascades::Memo."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize MemoPrinter."""
|
|
super().__init__(val, "ExplainGenerator::explainMemo")
|
|
|
|
|
|
class FixedArityNodePrinter(object):
|
|
"""Pretty-printer for OpFixedArity."""
|
|
|
|
def __init__(self, val, arity, name):
|
|
"""Initialize FixedArityNodePrinter."""
|
|
self.val = val
|
|
self.arity = arity
|
|
self.name = name
|
|
self.custom_children = []
|
|
|
|
@staticmethod
|
|
def display_hint():
|
|
"""Display hint."""
|
|
return None
|
|
|
|
def children(self):
|
|
"""children."""
|
|
global operator_indent_level
|
|
|
|
prior_indent = operator_indent_level
|
|
current_indent = operator_indent_level + self.arity + len(self.custom_children) - 1
|
|
for child in self.custom_children:
|
|
lhs = "\n"
|
|
for _ in range(current_indent):
|
|
lhs += "| "
|
|
|
|
operator_indent_level = current_indent
|
|
yield lhs, child
|
|
current_indent -= 1
|
|
|
|
for i in range(self.arity):
|
|
lhs = "\n"
|
|
for _ in range(current_indent):
|
|
lhs += "| "
|
|
|
|
# A little weird, but most ABTs prefer to print the last child first.
|
|
operator_indent_level = current_indent
|
|
yield lhs, self.val["_nodes"][self.arity - i - 1]
|
|
current_indent -= 1
|
|
operator_indent_level = prior_indent
|
|
|
|
# Adds a custom child node which is not directly contained in the "_nodes" member variable.
|
|
def add_child(self, child):
|
|
self.custom_children.append(child)
|
|
|
|
def to_string(self):
|
|
# Default for nodes which just print their type.
|
|
return self.name
|
|
|
|
|
|
class Vector(object):
|
|
def __init__(self, vec):
|
|
self.vec = vec
|
|
self.start = vec['_M_impl']['_M_start']
|
|
self.finish = vec['_M_impl']['_M_finish']
|
|
|
|
def __iter__(self):
|
|
item = self.start
|
|
while item != self.finish:
|
|
elt = item.dereference()
|
|
item = item + 1
|
|
yield elt
|
|
|
|
def count(self):
|
|
item_type = self.vec.type.template_argument(0)
|
|
return int((int(self.finish) - int(self.start)) / item_type.sizeof)
|
|
|
|
def get(self, index):
|
|
if index > self.count() - 1:
|
|
raise gdb.GdbError("Invalid Vector access at index {} with size {}".format(
|
|
index, self.count()))
|
|
item = self.start + index
|
|
return item.dereference()
|
|
|
|
|
|
class DynamicArityNodePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for OpDynamicArity."""
|
|
|
|
def __init__(self, val, minArity, name):
|
|
"""Initialize DynamicArityNodePrinter."""
|
|
super().__init__(val, minArity, name)
|
|
self.dynamic_nodes = Vector(self.val["_dyNodes"])
|
|
self.dynamic_count = self.dynamic_nodes.count()
|
|
|
|
def children(self):
|
|
"""children."""
|
|
global operator_indent_level
|
|
|
|
prior_indent = operator_indent_level
|
|
operator_indent_level += self.dynamic_count
|
|
for res in super().children():
|
|
yield res
|
|
|
|
current_indent = operator_indent_level - 1
|
|
for child in self.dynamic_nodes:
|
|
lhs = "\n"
|
|
for _ in range(current_indent):
|
|
lhs += "| "
|
|
|
|
operator_indent_level = current_indent
|
|
yield lhs, child
|
|
current_indent -= 1
|
|
operator_indent_level = prior_indent
|
|
|
|
|
|
class ExpressionBinderPrinter(DynamicArityNodePrinter):
|
|
"""Pretty-printer for ExpressionBinder."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize ExpressionBinderPrinter."""
|
|
super().__init__(val, 0, "Binder")
|
|
|
|
def to_string(self):
|
|
res = "Binder[{"
|
|
bindings = Vector(self.val["_names"])
|
|
for name in bindings:
|
|
res += str(name) + " "
|
|
res += "}]"
|
|
return res
|
|
|
|
|
|
class FunctionCallPrinter(DynamicArityNodePrinter):
|
|
"""Pretty-printer for FunctionCall."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize FunctionCallPrinter."""
|
|
super().__init__(val, 0, "FunctionCall")
|
|
|
|
def to_string(self):
|
|
return "FunctionCall[{}]".format(self.val["_name"])
|
|
|
|
|
|
class UnionNodePrinter(DynamicArityNodePrinter):
|
|
"""Pretty-printer for UnionNode."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize UnionNodePrinter."""
|
|
super().__init__(val, 2, "Union")
|
|
|
|
|
|
class ScanNodePrinter(object):
|
|
"""Pretty-printer for ScanNode."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize ScanNodePrinter."""
|
|
self.val = val
|
|
# Use the FixedArityNodePrinter to handle access to the ExpressionBinder child.
|
|
_, self.binder = next(FixedArityNodePrinter(self.val, 1, "Scan").children())
|
|
|
|
def get_bound_projection(self):
|
|
pp = PolyValuePrinter(self.binder)
|
|
dynamic_type = lookup_type(pp.get_dynamic_type()).strip_typedefs()
|
|
binder = pp.cast_control_block(dynamic_type)
|
|
bound_projections = Vector(binder["_names"])
|
|
if bound_projections.count() != 1:
|
|
return "<unknown>"
|
|
return str(bound_projections.get(0))
|
|
|
|
def to_string(self):
|
|
return "Scan[{}, {}]".format(self.val["_scanDefName"], self.get_bound_projection())
|
|
|
|
|
|
class FilterNodePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for FilterNode."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize FilterNodePrinter."""
|
|
super().__init__(val, 2, "Filter")
|
|
|
|
|
|
class EvaluationNodePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for EvaluationNode."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize EvaluationNodePrinter."""
|
|
super().__init__(val, 2, "Evaluation")
|
|
|
|
|
|
class ConstantPrinter(object):
|
|
"""Pretty-printer for Constant."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize ConstantPrinter."""
|
|
self.val = val
|
|
|
|
@staticmethod
|
|
def display_hint():
|
|
"""Display hint."""
|
|
return None
|
|
|
|
@staticmethod
|
|
def print_sbe_value(tag, value):
|
|
value_print_fn = "mongo::sbe::value::print"
|
|
(print_fn_symbol, _) = gdb.lookup_symbol(value_print_fn)
|
|
if print_fn_symbol is None:
|
|
raise gdb.GdbError("Could not find pretty print function: " + value_print_fn)
|
|
print_fn = print_fn_symbol.value()
|
|
return print_fn(tag, value)
|
|
|
|
def to_string(self):
|
|
return "Constant[{}]".format(
|
|
ConstantPrinter.print_sbe_value(self.val["_tag"], self.val["_val"]))
|
|
|
|
|
|
class VariablePrinter(object):
|
|
"""Pretty-printer for Variable."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize VariablePrinter."""
|
|
self.val = val
|
|
|
|
@staticmethod
|
|
def display_hint():
|
|
"""Display hint."""
|
|
return None
|
|
|
|
def to_string(self):
|
|
return "Variable[{}]".format(self.val["_name"])
|
|
|
|
|
|
class RootNodePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for RootNode."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize RootNodePrinter."""
|
|
# The second child (References) of the RootNode will be printed inline, but allow the base
|
|
# class to print the node child.
|
|
super().__init__(val, 1, "RootNode")
|
|
|
|
def to_string(self):
|
|
projections = Vector(self.val["_property"]["_projections"]["_vector"])
|
|
return "\nRoot[{}]".format(projections.get(0))
|
|
|
|
|
|
class GroupByNodePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for GroupByNode."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize GroupByNodePrinter."""
|
|
super().__init__(val, 5, "GroupBy")
|
|
|
|
|
|
class PathComparePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for PathCompare."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize PathComparePrinter."""
|
|
super().__init__(val, 1, "PathCompare")
|
|
|
|
def to_string(self):
|
|
return "PathCompare[{}]".format(strip_namespace(self.val["_cmp"]))
|
|
|
|
|
|
class PathTraversePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for PathTraverse."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize PathTraversePrinter."""
|
|
super().__init__(val, 1, "PathTraverse")
|
|
|
|
def to_string(self):
|
|
depth = self.val["_maxDepth"]
|
|
return "PathTraverse[{}]".format(str(depth) if depth != 0 else "inf")
|
|
|
|
|
|
class PathGetPrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for PathGet."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize PathGetPrinter."""
|
|
super().__init__(val, 1, "PathGet")
|
|
|
|
def to_string(self):
|
|
return "PathGet[{}]".format(self.val["_name"])
|
|
|
|
|
|
class EvalFilterPrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for EvalFilter."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize EvalFilterPrinter."""
|
|
super().__init__(val, 2, "EvalFilter")
|
|
|
|
|
|
class UnaryOpPrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for UnaryOp."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize UnaryOpPrinter."""
|
|
super().__init__(val, 1, "UnaryOp")
|
|
|
|
def to_string(self):
|
|
return "UnaryOp[{}]".format(strip_namespace(self.val["_op"]))
|
|
|
|
|
|
class BinaryOpPrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for BinaryOp."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize BinaryOpPrinter."""
|
|
super().__init__(val, 2, "BinaryOp")
|
|
|
|
def to_string(self):
|
|
return "BinaryOp[{}]".format(strip_namespace(self.val["_op"]))
|
|
|
|
|
|
class EvalPathPrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for EvalPath."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize EvalPathPrinter."""
|
|
super().__init__(val, 2, "EvalPath")
|
|
|
|
|
|
class PathComposeMPrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for PathComposeM."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize PathComposeMPrinter."""
|
|
super().__init__(val, 2, "PathComposeM")
|
|
|
|
|
|
class PathComposeAPrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for PathComposeA."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize PathComposeAPrinter."""
|
|
super().__init__(val, 2, "PathComposeA")
|
|
|
|
|
|
class BlackholePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for Blackhole."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize BlackholePrinter."""
|
|
super().__init__(val, 0, "Blackhole")
|
|
|
|
|
|
class IfPrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for If."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize IfPrinter."""
|
|
super().__init__(val, 3, "If")
|
|
|
|
|
|
class LetPrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for Let."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize LetPrinter."""
|
|
super().__init__(val, 2, "Let")
|
|
|
|
|
|
class PathFieldPrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for PathField."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize PathFieldPrinter."""
|
|
super().__init__(val, 1, "PathField")
|
|
|
|
def to_string(self):
|
|
return "PathField[{}]".format(self.val["_name"])
|
|
|
|
|
|
class PathConstantPrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for PathConstant."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize PathConstantPrinter."""
|
|
super().__init__(val, 1, "PathConstant")
|
|
|
|
|
|
class PathLambdaPrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for PathLambda."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize PathLambdaPrinter."""
|
|
super().__init__(val, 1, "PathLambda")
|
|
|
|
|
|
class PathIdentityPrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for PathIdentity."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize PathIdentityPrinter."""
|
|
super().__init__(val, 0, "PathIdentity")
|
|
|
|
|
|
class PathDropPrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for PathDrop."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize PathDropPrinter."""
|
|
super().__init__(val, 0, "PathDrop")
|
|
|
|
def to_string(self):
|
|
return "PathDrop[{}]".format(str(self.val["_names"]))
|
|
|
|
|
|
class PathKeepPrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for PathKeep."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize PathKeepPrinter."""
|
|
super().__init__(val, 0, "PathKeep")
|
|
|
|
def to_string(self):
|
|
return "PathKeep[{}]".format(str(self.val["_names"]))
|
|
|
|
|
|
class PathObjPrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for PathObj."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize PathObjPrinter."""
|
|
super().__init__(val, 0, "PathObj")
|
|
|
|
|
|
class PathArrPrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for PathArr."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize PathArrPrinter."""
|
|
super().__init__(val, 0, "PathArr")
|
|
|
|
|
|
class PathDefaultPrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for PathDefault."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize PathDefaultPrinter."""
|
|
super().__init__(val, 1, "PathDefault")
|
|
|
|
|
|
class LambdaAbstractionPrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for LambdaAbstraction."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize LambdaAbstractionPrinter."""
|
|
super().__init__(val, 1, "LambdaAbstraction")
|
|
|
|
def to_string(self):
|
|
return "LambdaAbstraction[{}]".format(self.val["_varName"])
|
|
|
|
|
|
class LambdaApplicationPrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for LambdaApplication."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize LambdaApplicationPrinter."""
|
|
super().__init__(val, 2, "LambdaApplication")
|
|
|
|
|
|
class SourcePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for Source."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize SourcePrinter."""
|
|
super().__init__(val, 0, "Source")
|
|
|
|
|
|
class FieldProjectionMapPrinter(object):
|
|
"""Pretty-printer for FieldProjectionMap."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize FieldProjectionMapPrinter."""
|
|
self.val = val
|
|
|
|
@staticmethod
|
|
def display_hint():
|
|
"""Display hint."""
|
|
return None
|
|
|
|
def to_string(self):
|
|
rid_proj = self.val["_ridProjection"]
|
|
root_proj = self.val["_rootProjection"]
|
|
res = "{"
|
|
if get_boost_optional(rid_proj) is not None:
|
|
res += "<rid>: " + str(rid_proj) + ", "
|
|
if get_boost_optional(root_proj) is not None:
|
|
res += "<root>: " + str(root_proj) + ", "
|
|
|
|
# Python reformats the string with embedded "=" characters, avoid that by replacing here.
|
|
res += str(self.val["_fieldProjections"]).replace("=", ":").replace("{", "(").replace(
|
|
"}", ")")
|
|
res += "}"
|
|
return res
|
|
|
|
|
|
class PhysicalScanNodePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for PhysicalScanNode."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize PhysicalScanNodePrinter."""
|
|
super().__init__(val, 1, "PhysicalScan")
|
|
|
|
def to_string(self):
|
|
return "PhysicalScan[{}, {}]".format(
|
|
str(self.val["_fieldProjectionMap"]), str(self.val["_scanDefName"]))
|
|
|
|
|
|
class ValueScanNodePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for ValueScanNode."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize ValueScanNodePrinter."""
|
|
super().__init__(val, 1, "ValueScan")
|
|
|
|
def to_string(self):
|
|
return "ValueScan[hasRID={},arraySize={}]".format(self.val["_hasRID"],
|
|
self.val["_arraySize"])
|
|
|
|
|
|
class CoScanNodePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for CoScanNode."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize CoScanNodePrinter."""
|
|
super().__init__(val, 0, "CoScan")
|
|
|
|
|
|
class IndexScanNodePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for IndexScanNode."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize IndexScanNodePrinter."""
|
|
super().__init__(val, 1, "IndexScan")
|
|
|
|
def to_string(self):
|
|
return "IndexScan[{{{}}}, scanDef={}, indexDef={}, interval={}]".format(
|
|
self.val["_fieldProjectionMap"], self.val["_scanDefName"], self.val["_indexDefName"],
|
|
self.val["_indexInterval"]).replace("\n", "")
|
|
|
|
|
|
class SeekNodePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for SeekNode."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize SeekNodePrinter."""
|
|
super().__init__(val, 2, "Seek")
|
|
|
|
def to_string(self):
|
|
return "Seek[rid_projection: {}, {}, scanDef: {}]".format(self.val["_ridProjectionName"],
|
|
self.val["_fieldProjectionMap"],
|
|
self.val["_scanDefName"])
|
|
|
|
|
|
class MemoLogicalDelegatorNodePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for MemoLogicalDelegatorNode."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize MemoLogicalDelegatorNodePrinter."""
|
|
super().__init__(val, 0, "MemoLogicalDelegator")
|
|
|
|
def to_string(self):
|
|
return "MemoLogicalDelegator[{}]".format(self.val["_groupId"])
|
|
|
|
|
|
class MemoPhysicalDelegatorNodePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for MemoPhysicalDelegatorNode."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize MemoPhysicalDelegatorNodePrinter."""
|
|
super().__init__(val, 0, "MemoPhysicalDelegator")
|
|
|
|
def to_string(self):
|
|
return "MemoPhysicalDelegator[group: {}, index: {}]".format(self.val["_nodeId"]["_groupId"],
|
|
self.val["_nodeId"]["_index"])
|
|
|
|
|
|
class ResidualRequirementPrinter(object):
|
|
"""Pretty-printer for ResidualRequirement."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize ResidualRequirementPrinter."""
|
|
self.val = val
|
|
|
|
def to_string(self):
|
|
key = self.val["_key"]
|
|
req = self.val["_req"]
|
|
res = "<"
|
|
if get_boost_optional(key["_projectionName"]) is not None:
|
|
res += "refProj: " + str(get_boost_optional(key["_projectionName"])) + ", "
|
|
|
|
res += "path: '" + str(key["_path"]).replace("| ", "").replace("\n", " -> ") + "'"
|
|
|
|
if get_boost_optional(req["_boundProjectionName"]) is not None:
|
|
res += "boundProj: " + str(get_boost_optional(req["_boundProjectionName"])) + ", "
|
|
|
|
res += ">"
|
|
return res
|
|
|
|
|
|
class ScanParamsPrinter(object):
|
|
"""Pretty-printer for ScanParams."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize ScanParamsPrinter."""
|
|
self.val = val
|
|
|
|
@staticmethod
|
|
def display_hint():
|
|
"""Display hint."""
|
|
return None
|
|
|
|
def to_string(self):
|
|
res = "scan_params: (fields: " + str(self.val["_fieldProjectionMap"]) + ", "
|
|
residual_reqs = get_boost_optional(self.val["_residualRequirements"])
|
|
if residual_reqs is not None:
|
|
res += "residual: " + str(residual_reqs)
|
|
res += ")"
|
|
return res
|
|
|
|
|
|
class SargableNodePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for SargableNode."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize SargableNodePrinter."""
|
|
# Although Sargable technically has 3 children, avoid printing the refs (child1) and bind block (child2).
|
|
super().__init__(val, 1, "Sargable")
|
|
|
|
# Add children for requirements, candidateIndex, and scan_params.
|
|
self.add_child(str(self.val["_reqMap"]).replace("\n", ""))
|
|
self.add_child(self.print_candidate_indexes())
|
|
|
|
scan_params = get_boost_optional(self.val["_scanParams"])
|
|
if scan_params is not None:
|
|
self.add_child(scan_params)
|
|
|
|
def print_candidate_indexes(self):
|
|
res = "candidateIndexes: ["
|
|
indexes = Vector(self.val["_candidateIndexes"])
|
|
for i in range(indexes.count()):
|
|
if i > 0:
|
|
res += ", "
|
|
res += "<id: " + str(i) + ", " + str(indexes.get(i)).replace("\n", "") + ">"
|
|
res += "]"
|
|
return res
|
|
|
|
def to_string(self):
|
|
return "Sargable [" + strip_namespace(str(self.val["_target"])) + "]"
|
|
|
|
|
|
class RIDIntersectNodePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for RIDIntersectNode."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize RIDIntersectNodePrinter."""
|
|
super().__init__(val, 2, "RIDIntersect")
|
|
|
|
def to_string(self):
|
|
return "RIDIntersect[" + str(self.val["_scanProjectionName"]) + "]"
|
|
|
|
|
|
class RIDUnionNodePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for RIDUnionNode."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize RIDUnionNodePrinter."""
|
|
super().__init__(val, 4, "RIDUnion")
|
|
|
|
def to_string(self):
|
|
return "RIDUnion[" + str(self.val["_scanProjectionName"]) + "]"
|
|
|
|
|
|
def print_correlated_projections(projections):
|
|
# Strip off the extra absl map prefix.
|
|
return str(projections).split("elems ")[-1]
|
|
|
|
|
|
class BinaryJoinNodePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for BinaryJoinNode."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize BinaryJoinNodePrinter."""
|
|
super().__init__(val, 3, "BinaryJoin")
|
|
|
|
def to_string(self):
|
|
correlated = print_correlated_projections(self.val["_correlatedProjectionNames"])
|
|
return "BinaryJoin[type=" + str(strip_namespace(
|
|
self.val["_joinType"])) + ", " + correlated + "]"
|
|
|
|
|
|
def print_eq_join_condition(leftKeys, rightKeys):
|
|
condition = "Condition["
|
|
for i in range(leftKeys.count()):
|
|
condition += str(leftKeys.get(i)) + "==" + str(rightKeys.get(i)) + ","
|
|
condition += "]"
|
|
return condition
|
|
|
|
|
|
class HashJoinNodePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for HashJoinNode."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize HashJoinNodePrinter."""
|
|
super().__init__(val, 3, "HashJoin")
|
|
|
|
# Manually add the child which prints the sets of keys.
|
|
leftKeys = Vector(self.val["_leftKeys"])
|
|
rightKeys = Vector(self.val["_rightKeys"])
|
|
self.add_child(print_eq_join_condition(leftKeys, rightKeys))
|
|
|
|
def to_string(self):
|
|
return "HashJoin[type=" + strip_namespace(self.val["_joinType"]) + "]"
|
|
|
|
|
|
class MergeJoinNodePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for MergeJoinNode."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize MergeJoinNodePrinter."""
|
|
super().__init__(val, 3, "MergeJoin")
|
|
|
|
# Manually add the collation ops.
|
|
collationOps = Vector(self.val["_collation"])
|
|
collationChild = "Collation[" + ", ".join(str(collation)
|
|
for collation in collationOps) + "]"
|
|
self.add_child(collationChild)
|
|
|
|
# Manually add the child which prints the sets of keys.
|
|
leftKeys = Vector(self.val["_leftKeys"])
|
|
rightKeys = Vector(self.val["_rightKeys"])
|
|
self.add_child(print_eq_join_condition(leftKeys, rightKeys))
|
|
|
|
def to_string(self):
|
|
return "MergeJoin"
|
|
|
|
|
|
def print_collation_req(req):
|
|
spec = Vector(req["_spec"])
|
|
return ", ".join(
|
|
str(entry["first"]) + ": " + strip_namespace(entry["second"]) for entry in spec)
|
|
|
|
|
|
class SortedMergeNodePrinter(DynamicArityNodePrinter):
|
|
"""Pretty-printer for SortedMergeNode."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize SortedMergeNodePrinter."""
|
|
super().__init__(val, 2, "MergeJoin")
|
|
|
|
self.add_child("collation[" + print_collation_req(self.val["_collationReq"]) + "]")
|
|
|
|
def to_string(self):
|
|
return "SortedMerge"
|
|
|
|
|
|
class NestedLoopJoinNodePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for NestedLoopJoinNode."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize NestedLoopJoinNodePrinter."""
|
|
super().__init__(val, 3, "NestedLoopJoin")
|
|
|
|
def to_string(self):
|
|
correlated = print_correlated_projections(self.val["_correlatedProjectionNames"])
|
|
return "NestedLoopJoin[type=" + strip_namespace(
|
|
self.val["_joinType"]) + ", " + correlated + "]"
|
|
|
|
|
|
class UnwindNodePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for UnwindNode."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize UnwindNodePrinter."""
|
|
super().__init__(val, 3, "Unwind")
|
|
|
|
|
|
class UniqueNodePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for UniqueNode."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize UniqueNodePrinter."""
|
|
super().__init__(val, 2, "Unique")
|
|
|
|
|
|
class SpoolProducerNodePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for SpoolProducerNode."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize SpoolProducerNodePrinter."""
|
|
super().__init__(val, 4, "SpoolProducer")
|
|
|
|
def to_string(self):
|
|
return "SpoolProducer[" + strip_namespace(self.val["_type"]) + ", id:" + str(
|
|
self.val["_spoolId"]) + "]"
|
|
|
|
|
|
class SpoolConsumerNodePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for SpoolConsumerNode."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize SpoolConsumerNodePrinter."""
|
|
super().__init__(val, 1, "SpoolConsumer")
|
|
|
|
def to_string(self):
|
|
return "SpoolConsumer[" + strip_namespace(self.val["_type"]) + ", id:" + str(
|
|
self.val["_spoolId"]) + "]"
|
|
|
|
|
|
class CollationNodePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for CollationNode."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize CollationNodePrinter."""
|
|
|
|
# Don't print the references, will print them inline below.
|
|
super().__init__(val, 1, "Collation")
|
|
|
|
def to_string(self):
|
|
return "Collation[" + print_collation_req(self.val["_property"]) + "]"
|
|
|
|
|
|
class LimitSkipNodePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for LimitSkipNode."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize LimitSkipNodePrinter."""
|
|
super().__init__(val, 1, "LimitSkip")
|
|
|
|
def to_string(self):
|
|
return "LimitSkip[limit: " + str(self.val["_property"]["_limit"]) + ", skip: " + str(
|
|
self.val["_property"]["_skip"]) + "]"
|
|
|
|
|
|
class ExchangeNodePrinter(FixedArityNodePrinter):
|
|
"""Pretty-printer for ExchangeNode."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize ExchangeNodePrinter."""
|
|
super().__init__(val, 2, "Exchange")
|
|
|
|
def to_string(self):
|
|
return "Exchange[type: " + str(
|
|
self.val["_distribution"]["_distributionAndProjections"]
|
|
["_type"]) + ", projections: " + str(
|
|
self.val["_distribution"]["_distributionAndProjections"]["_projectionNames"]) + "]"
|
|
|
|
|
|
class ReferencesPrinter(DynamicArityNodePrinter):
|
|
"""Pretty-printer for References."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize ReferencesPrinter."""
|
|
super().__init__(val, 0, "References")
|
|
|
|
|
|
class PolyValuePrinter(object):
|
|
"""Pretty-printer for PolyValue."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize PolyValuePrinter."""
|
|
self.val = val
|
|
self.control_block = self.val["_object"]
|
|
self.tag = self.control_block.dereference()["_tag"]
|
|
|
|
if self.val.type.code == gdb.TYPE_CODE_REF:
|
|
self.poly_type = self.val.type.target().strip_typedefs()
|
|
else:
|
|
self.poly_type = self.val.type.strip_typedefs()
|
|
self.type_set = str(self.poly_type).split("<", 1)[1]
|
|
|
|
if self.tag < 0:
|
|
raise gdb.GdbError("Invalid PolyValue tag: {}, must be at least 0".format(self.tag))
|
|
|
|
# Check if the tag is out of range for the set of types that we know about.
|
|
if self.tag > len(self.type_set.split(",")):
|
|
raise gdb.GdbError("Unknown PolyValue tag: {} (max: {}), did you add a new one?".format(
|
|
self.tag, str(self.type_set)))
|
|
|
|
@staticmethod
|
|
def display_hint():
|
|
"""Display hint."""
|
|
return None
|
|
|
|
def cast_control_block(self, target_type):
|
|
return self.control_block.dereference().address.cast(
|
|
target_type.pointer()).dereference()["_t"]
|
|
|
|
def get_dynamic_type(self):
|
|
# Build up the dynamic type for the particular variant of this PolyValue instance. This is
|
|
# basically a reinterpret cast of the '_object' member variable to the correct instance
|
|
# of ControlBlockVTable<T, Ts...>::ConcreteType where T is the template argument derived
|
|
# from the _tag member variable.
|
|
poly_type = self.val.type.template_argument(self.tag)
|
|
dynamic_type = f"{OPTIMIZER_NS}::algebra::ControlBlockVTable<{poly_type.name}, "
|
|
dynamic_type += self.type_set
|
|
dynamic_type += "::ConcreteType"
|
|
return dynamic_type
|
|
|
|
def to_string(self):
|
|
dynamic_type = self.get_dynamic_type()
|
|
try:
|
|
dynamic_type = lookup_type(dynamic_type).strip_typedefs()
|
|
except gdb.error:
|
|
return "Unknown PolyValue tag: {}, did you add a new one?".format(self.tag)
|
|
# GDB automatically formats types with children, remove the extra characters to get the
|
|
# output that we want.
|
|
return str(self.cast_control_block(dynamic_type)).replace(" = ", "").replace("{",
|
|
"").replace(
|
|
"}", "")
|
|
|
|
|
|
class AtomPrinter(object):
|
|
"""Pretty-printer for Atom."""
|
|
|
|
def __init__(self, val):
|
|
"""Initialize AtomPrinter."""
|
|
self.val = val
|
|
|
|
def to_string(self):
|
|
return self.val["_expr"]
|
|
|
|
|
|
class ConjunctionPrinter(object):
|
|
"""Pretty-printer for Conjunction."""
|
|
|
|
def __init__(self, val, separator=" ^ "):
|
|
"""Initialize ConjunctionPrinter."""
|
|
self.val = val
|
|
self.dynamic_nodes = Vector(self.val["_dyNodes"])
|
|
self.dynamic_count = self.dynamic_nodes.count()
|
|
self.separator = separator
|
|
|
|
def to_string(self):
|
|
if self.dynamic_count == 0:
|
|
return "<empty>"
|
|
|
|
res = ""
|
|
first = True
|
|
for child in self.dynamic_nodes:
|
|
if first:
|
|
first = False
|
|
else:
|
|
res += self.separator
|
|
|
|
res += str(child)
|
|
return res
|
|
|
|
|
|
class DisjunctionPrinter(ConjunctionPrinter):
|
|
"""Pretty-printer for Disjunction."""
|
|
|
|
def __init__(self, val):
|
|
super().__init__(val, " U ")
|
|
|
|
|
|
def bool_expr_type(T):
|
|
return (f"{OPTIMIZER_NS}::algebra::PolyValue<" + f"{OPTIMIZER_NS}::BoolExpr<{T}>::Atom, " +
|
|
f"{OPTIMIZER_NS}::BoolExpr<{T}>::Conjunction, " +
|
|
f"{OPTIMIZER_NS}::BoolExpr<{T}>::Disjunction>")
|
|
|
|
|
|
def register_optimizer_printers(pp):
|
|
"""Registers a number of pretty printers related to the CQF optimizer."""
|
|
|
|
# IntervalRequirement printer.
|
|
pp.add("Interval", f"{OPTIMIZER_NS}::IntervalRequirement", False, IntervalPrinter)
|
|
pp.add("CompoundInterval", f"{OPTIMIZER_NS}::CompoundIntervalRequirement", False,
|
|
CompoundIntervalPrinter)
|
|
|
|
# IntervalReqExpr::Node printer.
|
|
pp.add(
|
|
"IntervalExpr",
|
|
bool_expr_type(f"{OPTIMIZER_NS}::IntervalRequirement"),
|
|
False,
|
|
IntervalExprPrinter,
|
|
)
|
|
|
|
# PSRExpr printer.
|
|
pp.add(
|
|
"PSRExpr",
|
|
bool_expr_type(
|
|
f"std::pair<{OPTIMIZER_NS}::PartialSchemaKey, {OPTIMIZER_NS}::PartialSchemaRequirement> "
|
|
),
|
|
False,
|
|
PSRExprPrinter,
|
|
)
|
|
|
|
# Memo printer.
|
|
pp.add("Memo", f"{OPTIMIZER_NS}::cascades::Memo", False, MemoPrinter)
|
|
|
|
# ResidualRequirement printer.
|
|
pp.add("ResidualRequirement", f"{OPTIMIZER_NS}::ResidualRequirement", False,
|
|
ResidualRequirementPrinter)
|
|
|
|
# CandidateIndexEntry printer.
|
|
pp.add("CandidateIndexEntry", f"{OPTIMIZER_NS}::CandidateIndexEntry", False,
|
|
CandidateIndexEntryPrinter)
|
|
|
|
# BoolExpr<ResidualRequirement> is handled by the PolyValue printer, but still need to add
|
|
# printers for each of the possible bool expr types.
|
|
bool_expr_types = ["Atom", "Conjunction", "Disjunction"]
|
|
bool_exprs = ["ResidualRequirement"]
|
|
for bool_type in bool_expr_types:
|
|
for expr in bool_exprs:
|
|
pp.add(bool_type, f"{OPTIMIZER_NS}::BoolExpr<{OPTIMIZER_NS}::{expr}>::{bool_type}",
|
|
False, getattr(sys.modules[__name__], bool_type + "Printer"))
|
|
|
|
# Utility types within the optimizer.
|
|
pp.add("StrongStringAlias", f"{OPTIMIZER_NS}::StrongStringAlias", True,
|
|
StrongStringAliasPrinter)
|
|
pp.add("FieldProjectionMap", f"{OPTIMIZER_NS}::FieldProjectionMap", False,
|
|
FieldProjectionMapPrinter)
|
|
|
|
pp.add("ScanParams", f"{OPTIMIZER_NS}::ScanParams", False, ScanParamsPrinter)
|
|
|
|
# Add the sub-printers for each of the possible ABT types.
|
|
# This is the set of known ABT variants that GDB is aware of. When adding to this list, ensure
|
|
# that a corresponding printer class exists with the name MyNewNodePrinter where "MyNewNode"
|
|
# exactly matches the entry in this list. The printer class may choose to derive from one of
|
|
# the arity printers to automatically print the children nodes. By default, just the name of
|
|
# your node will be printed. If you want to display more information (e.g. scan def name),
|
|
# then override the to_string() method.
|
|
abt_type_set = [
|
|
"Blackhole",
|
|
"Constant",
|
|
"Variable",
|
|
"UnaryOp",
|
|
"BinaryOp",
|
|
"If",
|
|
"Let",
|
|
"LambdaAbstraction",
|
|
"LambdaApplication",
|
|
"FunctionCall",
|
|
"EvalPath",
|
|
"EvalFilter",
|
|
"Source",
|
|
"PathConstant",
|
|
"PathLambda",
|
|
"PathIdentity",
|
|
"PathDefault",
|
|
"PathCompare",
|
|
"PathDrop",
|
|
"PathKeep",
|
|
"PathObj",
|
|
"PathArr",
|
|
"PathTraverse",
|
|
"PathField",
|
|
"PathGet",
|
|
"PathComposeM",
|
|
"PathComposeA",
|
|
"ScanNode",
|
|
"PhysicalScanNode",
|
|
"ValueScanNode",
|
|
"CoScanNode",
|
|
"IndexScanNode",
|
|
"SeekNode",
|
|
"MemoLogicalDelegatorNode",
|
|
"MemoPhysicalDelegatorNode",
|
|
"FilterNode",
|
|
"EvaluationNode",
|
|
"SargableNode",
|
|
"RIDIntersectNode",
|
|
"RIDUnionNode",
|
|
"BinaryJoinNode",
|
|
"HashJoinNode",
|
|
"MergeJoinNode",
|
|
"SortedMergeNode",
|
|
"NestedLoopJoinNode",
|
|
"UnionNode",
|
|
"GroupByNode",
|
|
"UnwindNode",
|
|
"UniqueNode",
|
|
"SpoolProducerNode",
|
|
"SpoolConsumerNode",
|
|
"CollationNode",
|
|
"LimitSkipNode",
|
|
"ExchangeNode",
|
|
"RootNode",
|
|
"References",
|
|
"ExpressionBinder",
|
|
]
|
|
for abt_type in abt_type_set:
|
|
pp.add(abt_type, f"{OPTIMIZER_NS}::{abt_type}", False,
|
|
getattr(sys.modules[__name__], abt_type + "Printer"))
|
|
|
|
# Add the generic PolyValue printer which determines the exact type at runtime and attempts to
|
|
# invoke the printer for that type.
|
|
pp.add('PolyValue', OPTIMIZER_NS + "::algebra::PolyValue", True, PolyValuePrinter)
|