Module: PgCanary::SchemaIntrospection

Defined in:
lib/pg_canary/schema_introspection.rb

Overview

Index and column metadata for the rules, read through ActiveRecord's schema cache — caching, invalidation and thread safety ride on Rails. The catalog queries ActiveRecord issues are named "SCHEMA", which the Subscriber already ignores, so they are never analyzed themselves.

Defined Under Namespace

Classes: IndexInfo

Class Method Summary collapse

Class Method Details

.column_type(connection, table, column) ⇒ Object



42
43
44
# File 'lib/pg_canary/schema_introspection.rb', line 42

def column_type(connection, table, column)
  column_types(connection, table)[column]
end

.column_types(connection, table) ⇒ Object

=> { column_name => sql_type } e.g. { "id" => "bigint", "tags" => "text" }



47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/pg_canary/schema_introspection.rb', line 47

def column_types(connection, table)
  cache = connection.schema_cache
  return {} unless cache.data_source_exists?(table)

  cache.columns(table).to_h do |column|
    type = column.sql_type
    type = "#{type}[]" if column.respond_to?(:array?) && column.array?
    [column.name, type]
  end
rescue StandardError => e
  PgCanary.internal_error(e)
  {}
end

.index_info(definition) ⇒ Object

ActiveRecord returns plain-column indexes with an Array of column names, and expression indexes with the expressions as one SQL String.



63
64
65
66
67
68
69
70
# File 'lib/pg_canary/schema_introspection.rb', line 63

def index_info(definition)
  expressions = definition.columns.is_a?(String) ? definition.columns : nil
  columns = expressions ? [] : Array(definition.columns)
  opclasses = definition.opclasses
  opclasses = columns.to_h { |c| [c, opclasses.to_s] } unless opclasses.is_a?(Hash)
  IndexInfo.new(name: definition.name, using: definition.using.to_s,
                columns: columns, opclasses: opclasses, expressions: expressions)
end

.indexes(connection, table) ⇒ Object

=> [IndexInfo] — includes the primary key, which ActiveRecord's #indexes omits.



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/pg_canary/schema_introspection.rb', line 26

def indexes(connection, table)
  cache = connection.schema_cache
  return [] unless cache.data_source_exists?(table)

  list = cache.indexes(table).map { |definition| index_info(definition) }
  primary_key = Array(cache.primary_keys(table))
  if primary_key.any?
    list << IndexInfo.new(name: "#{table}_pkey", using: "btree",
                          columns: primary_key, opclasses: {}, expressions: nil)
  end
  list
rescue StandardError => e
  PgCanary.internal_error(e)
  []
end