Class: PgCanary::Rules::QueryContext

Inherits:
Object
  • Object
show all
Includes:
PgQuerySupport
Defined in:
lib/pg_canary/rules/query_context.rb

Overview

What a rule's #check receives: everything about one executed query — the parsed AST (as Scopes), bind parameter values, the configuration, and access to schema metadata via the connection the query ran on.

Defined Under Namespace

Classes: Scope

Constant Summary

Constants included from PgQuerySupport

PgQuerySupport::COMPARISON_OPS

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(sql:, connection:, parse_result:, config:, binds: [], type_casted_binds: nil) ⇒ QueryContext

Returns a new instance of QueryContext.



82
83
84
85
86
87
88
89
# File 'lib/pg_canary/rules/query_context.rb', line 82

def initialize(sql:, connection:, parse_result:, config:, binds: [], type_casted_binds: nil)
  @sql = sql
  @connection = connection
  @parse_result = parse_result
  @config = config
  @binds = binds || []
  @type_casted_binds = type_casted_binds
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



80
81
82
# File 'lib/pg_canary/rules/query_context.rb', line 80

def config
  @config
end

#connectionObject (readonly)

Returns the value of attribute connection.



80
81
82
# File 'lib/pg_canary/rules/query_context.rb', line 80

def connection
  @connection
end

#parse_resultObject (readonly)

Returns the value of attribute parse_result.



80
81
82
# File 'lib/pg_canary/rules/query_context.rb', line 80

def parse_result
  @parse_result
end

#sqlObject (readonly)

Returns the value of attribute sql.



80
81
82
# File 'lib/pg_canary/rules/query_context.rb', line 80

def sql
  @sql
end

Instance Method Details

#bind_value(number) ⇒ Object

Value for a ParamRef ($n, 1-based). nil when unknown.



118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/pg_canary/rules/query_context.rb', line 118

def bind_value(number)
  index = number - 1
  return nil if index.negative?

  casted = @type_casted_binds
  return casted[index] if casted.is_a?(Array) && index < casted.length

  bind = @binds[index]
  return nil if bind.nil?

  bind.respond_to?(:value_for_database) ? bind.value_for_database : bind
end

#column_type(table, column) ⇒ Object



137
138
139
# File 'lib/pg_canary/rules/query_context.rb', line 137

def column_type(table, column)
  SchemaIntrospection.column_type(connection, table, column)
end

#column_types(table) ⇒ Object

=> { column_name => sql_type }



142
143
144
# File 'lib/pg_canary/rules/query_context.rb', line 142

def column_types(table)
  SchemaIntrospection.column_types(connection, table)
end

#each_scopeObject



108
109
110
# File 'lib/pg_canary/rules/query_context.rb', line 108

def each_scope(&)
  scopes.each(&)
end

#fingerprintObject

Cannot fail: a QueryContext is only built for SQL that already parsed.



92
93
94
# File 'lib/pg_canary/rules/query_context.rb', line 92

def fingerprint
  @fingerprint ||= PgQuery.fingerprint(sql)
end

#indexes(table) ⇒ Object

--- schema metadata for the query's connection ---



133
134
135
# File 'lib/pg_canary/rules/query_context.rb', line 133

def indexes(table)
  SchemaIntrospection.indexes(connection, table)
end

#scopesObject

All SELECT scopes in the statement, including CTEs, subqueries in FROM/WHERE and UNION branches.



98
99
100
101
102
103
104
105
106
# File 'lib/pg_canary/rules/query_context.rb', line 98

def scopes
  @scopes ||= begin
    stmts = []
    parse_result.tree.stmts.each do |raw|
      walk_ast(raw.stmt) { |msg| stmts << msg if msg.is_a?(PgQuery::SelectStmt) }
    end
    stmts.map { |s| Scope.new(s) }
  end
end

#tablesObject

All real table names touched anywhere in the query.



113
114
115
# File 'lib/pg_canary/rules/query_context.rb', line 113

def tables
  @tables ||= scopes.flat_map(&:tables).uniq
end