class ConceptQL::Scope
Scope
coordinates the creation of any common table expressions that might be used when a Recall operator is present in the statement.
Any time an operator is given a label, it becomes a candidate for a Recall operator to reuse the output of that operator somewhere else in the statement.
Scope
keeps track of all labeled operators and provides an API for Recall operators to fetch the results/domains from labeled operators.
Constants
- ADDITIONAL_COLUMNS
- COLUMN_TYPES
- DEFAULT_COLUMNS
Attributes
known_operators[RW]
person_ids[RW]
Public Class Methods
new(opts = {})
click to toggle source
# File lib/conceptql/scope.rb, line 46 def initialize(opts = {}) @known_operators = {} @recall_dependencies = {} @recall_stack = [] @annotation = {} @opts = opts.dup @annotation[:errors] = @errors = {} @annotation[:warnings] = @warnings = {} @annotation[:counts] = @counts = {} @annotation[:operators] = @operators = [] @query_columns = DEFAULT_COLUMNS.keys end
Public Instance Methods
add_counts(key, domain, counts)
click to toggle source
# File lib/conceptql/scope.rb, line 67 def add_counts(key, domain, counts) c = @counts[key] ||= {} c[domain] = counts end
add_errors(key, errors)
click to toggle source
# File lib/conceptql/scope.rb, line 59 def add_errors(key, errors) @errors[key] = errors end
add_operator(operator)
click to toggle source
# File lib/conceptql/scope.rb, line 123 def add_operator(operator) known_operators[operator.label] = operator end
add_operators(operator)
click to toggle source
# File lib/conceptql/scope.rb, line 72 def add_operators(operator) @operators << operator.operator_name @operators.compact! @operators.uniq! end
add_required_columns(op)
click to toggle source
# File lib/conceptql/scope.rb, line 78 def add_required_columns(op) @query_columns |= op.required_columns if op.required_columns end
add_warnings(key, errors)
click to toggle source
# File lib/conceptql/scope.rb, line 63 def add_warnings(key, errors) @warnings[key] = errors end
ctes()
click to toggle source
# File lib/conceptql/scope.rb, line 199 def ctes @ctes ||= sort_ctes([], known_operators, recall_dependencies) end
domains(label, db)
click to toggle source
# File lib/conceptql/scope.rb, line 153 def domains(label, db) fetch_operator(label).domains(db) rescue [:invalid] end
duplicate_label?(label)
click to toggle source
# File lib/conceptql/scope.rb, line 127 def duplicate_label?(label) known_operators.keys.map(&:downcase).include?(label.downcase) end
fetch_operator(label)
click to toggle source
# File lib/conceptql/scope.rb, line 203 def fetch_operator(label) known_operators[label] end
from(db, label)
click to toggle source
# File lib/conceptql/scope.rb, line 135 def from(db, label) ds = db.from(label) if ENV['CONCEPTQL_CHECK_COLUMNS'] # Work around requests for columns by operators. These # would fail because the CTE would not be defined. You # don't want to define the CTE normally, but to allow the # columns to still work, send the columns request to the # underlying operator. op = fetch_operator(label) (class << ds; self; end).send(:define_method, :columns) do (@main_op ||= op.evaluate(db)).columns end end ds end
nest(op) { || ... }
click to toggle source
# File lib/conceptql/scope.rb, line 82 def nest(op) add_required_columns(op) return yield unless label = op.is_a?(Operators::Recall) ? op.source : op.label unless label.is_a?(String) op.instance_eval do @errors = [] add_error("invalid label") end return end recall_dependencies[label] ||= [] if nested_recall?(label) op.instance_eval do @errors = [] add_error("nested recall") end return end if duplicate_label?(label) && !op.is_a?(Operators::Recall) op.instance_eval do @errors = [] add_error("duplicate label") end end if last = recall_stack.last recall_dependencies[last] << label end begin recall_stack.push(label) yield ensure recall_stack.pop if recall_stack.last == label end end
nested_recall?(label)
click to toggle source
# File lib/conceptql/scope.rb, line 131 def nested_recall?(label) recall_stack.map(&:downcase).include?(label.downcase) end
sort_ctes(sorted, unsorted, deps)
click to toggle source
# File lib/conceptql/scope.rb, line 159 def sort_ctes(sorted, unsorted, deps) if unsorted.empty? return sorted end add, unsorted = unsorted.partition do |label, _| deps[label].length == 0 end sorted += add new_deps = {} deps.map do |label, dps| new_deps[label] = dps - sorted.map(&:first) end sort_ctes(sorted, unsorted, new_deps) end
valid?()
click to toggle source
# File lib/conceptql/scope.rb, line 178 def valid? recall_dependencies.each_value do |deps| unless (deps - known_operators.keys).empty? return false end end true end
with_ctes(query, db)
click to toggle source
# File lib/conceptql/scope.rb, line 187 def with_ctes(query, db) raise "recall operator use without matching label" unless valid? query = query.from_self ctes.each do |label, operator| query = query.with(label, operator.evaluate(db)) end query end