Class: Gitlab::Database::QueryAnalyzers::PreventSetOperatorMismatch::Node
- Inherits:
-
Object
- Object
- Gitlab::Database::QueryAnalyzers::PreventSetOperatorMismatch::Node
- Extended by:
- Utils::StrongMemoize
- Defined in:
- lib/gitlab/database/query_analyzers/prevent_set_operator_mismatch/node.rb
Overview
The Node class allows us to traverse PgQuery nodes with tree like semantics.
This class balances convenience and performance. The PgQuery nodes are Google::Protobuf::MessageExts which contain a dynamic set of attributes known as fields. Accessing these fields can cause performance problems due to the large volume of iterable fields.
When possible use #dig over the descendant methods.
The filter available to each method reduces the traversed attributes. The default filter only traverses nodes required to parse for set operator mismatches.
Constant Summary collapse
- DEFAULT_NODES =
The default nodes help speed up traversal. Traversal of other nodes can greatly affect performance.
i[ a_star alias args column_ref fields func_call join_expr larg range_subselect range_var rarg res_target subquery val ].freeze
- DEFAULT_FIELD_FILTER =
->(field) { field.is_a?(Integer) || DEFAULT_NODES.include?(field) }.freeze
Class Method Summary collapse
-
.descendants(node, filter: DEFAULT_FIELD_FILTER, &blk) ⇒ Object
Recurse through children.
-
.dig(node, *attrs) ⇒ Object
Like Hash#dig, traverse attributes in sequential order and return the final value.
-
.locate_descendant(node, field, filter: DEFAULT_FIELD_FILTER) ⇒ Object
Return the first node that matches the field.
-
.locate_descendants(node, field, filter: DEFAULT_FIELD_FILTER) ⇒ Object
Return all nodes that match the field.
Class Method Details
.descendants(node, filter: DEFAULT_FIELD_FILTER, &blk) ⇒ Object
Recurse through children. The block will yield the child node and the name of that node. Calling without a block will return an Enumerator.
43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/gitlab/database/query_analyzers/prevent_set_operator_mismatch/node.rb', line 43 def descendants(node, filter: DEFAULT_FIELD_FILTER, &blk) if blk children(node, filter: filter) do |child_node, child_field| yield(child_node, child_field) descendants(child_node, filter: filter, &blk) end nil else enum_for(:descendants, node, filter: filter, &blk) end end |
.dig(node, *attrs) ⇒ Object
Like Hash#dig, traverse attributes in sequential order and return the final value. Return nil if any of the fields are not available.
68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/gitlab/database/query_analyzers/prevent_set_operator_mismatch/node.rb', line 68 def dig(node, *attrs) obj = node attrs.each do |attr| if obj.respond_to?(attr) obj = obj.public_send(attr) # rubocop:disable GitlabSecurity/PublicSend else obj = nil break end end obj end |
.locate_descendant(node, field, filter: DEFAULT_FIELD_FILTER) ⇒ Object
Return the first node that matches the field.
57 58 59 |
# File 'lib/gitlab/database/query_analyzers/prevent_set_operator_mismatch/node.rb', line 57 def locate_descendant(node, field, filter: DEFAULT_FIELD_FILTER) descendants(node, filter: filter).find { |_, child_field| child_field == field }&.first end |
.locate_descendants(node, field, filter: DEFAULT_FIELD_FILTER) ⇒ Object
Return all nodes that match the field.
62 63 64 |
# File 'lib/gitlab/database/query_analyzers/prevent_set_operator_mismatch/node.rb', line 62 def locate_descendants(node, field, filter: DEFAULT_FIELD_FILTER) descendants(node, filter: filter).select { |_, child_field| child_field == field }.map(&:first) end |