Module: ActiveRecord::SqlAnalyzer::Monkeypatches::Query

Defined in:
lib/active_record/sql_analyzer/monkeypatches/query.rb

Defined Under Namespace

Classes: QueryAnalyzerCall

Instance Method Summary collapse

Instance Method Details

#_query_analyzer_private_drain_transaction_queue(last_query) ⇒ Object

Drain the transaction query queue. Log the current transaction out to any logger that samples it.



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/active_record/sql_analyzer/monkeypatches/query.rb', line 74

def _query_analyzer_private_drain_transaction_queue(last_query)
  return unless @_query_analyzer_private_record_transaction

  reencoded_calls = nil

  SqlAnalyzer.config[:analyzers].each do |analyzer|
    reencoded_calls ||= @_query_analyzer_private_transaction_queue << QueryAnalyzerCall.new(last_query, caller)

    has_matching_calls = reencoded_calls.any? { |call| call.sql =~ analyzer[:table_regex] }

    # Record "full" transactions
    # Record all INSERT, UPDATE, and DELETE
    # Record all queries that match the analyzer's table_regex
    if has_matching_calls
      matching_calls = reencoded_calls.select do |call|
        (call.sql =~ /^(BEGIN|COMMIT|ROLLBACK|UPDATE|INSERT|DELETE)/) || (call.sql =~ analyzer[:table_regex])
      end

      SqlAnalyzer.background_processor << _query_analyzer_private_query_stanza(matching_calls, analyzer)
    end
  end

  @_query_analyzer_private_transaction_queue.clear
  @_query_analyzer_private_record_transaction = nil
end

#_query_analyzer_private_query_stanza(calls, analyzer) ⇒ Object

Helper method to construct the event for a query or transaction. safe_sql [string]: SQL statement or combined SQL statement for transaction calls: A list of QueryAnalyzerCall objects to be turned into call hashes



103
104
105
106
107
108
109
110
111
# File 'lib/active_record/sql_analyzer/monkeypatches/query.rb', line 103

def _query_analyzer_private_query_stanza(calls, analyzer)
  {
    # Calls are of the format {sql: String, caller: String}
    calls: calls.map(&:to_h),
    logger: analyzer[:logger_instance],
    tag: Thread.current[:_ar_analyzer_tag],
    request_path: Thread.current[:_ar_analyzer_request_path],
  }
end

#begin_db_transactionObject



42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/active_record/sql_analyzer/monkeypatches/query.rb', line 42

def begin_db_transaction
  @_query_analyzer_private_in_transaction = true

  record_transaction = SqlAnalyzer.config[:analyzers].any? do |analyzer|
    SqlAnalyzer.config[:should_log_sample_proc].call(analyzer[:name])
  end
  if record_transaction
    @_query_analyzer_private_transaction_queue ||= []
    @_query_analyzer_private_record_transaction = true
  else
    @_query_analyzer_private_record_transaction = nil
  end
  super
end

#commit_db_transactionObject



57
58
59
60
61
62
# File 'lib/active_record/sql_analyzer/monkeypatches/query.rb', line 57

def commit_db_transaction
  _query_analyzer_private_drain_transaction_queue("COMMIT")
  super
ensure
  @_query_analyzer_private_in_transaction = false
end

#exec_rollback_db_transactionObject



64
65
66
67
68
69
# File 'lib/active_record/sql_analyzer/monkeypatches/query.rb', line 64

def exec_rollback_db_transaction
  _query_analyzer_private_drain_transaction_queue("ROLLBACK")
  super
ensure
  @_query_analyzer_private_in_transaction = false
end

#execute(sql, *args) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/active_record/sql_analyzer/monkeypatches/query.rb', line 9

def execute(sql, *args)
  return super unless SqlAnalyzer.config
  safe_sql = nil
  query_analyzer_call = nil

  # Record "full" transactions (see below for more information about "full")
  if @_query_analyzer_private_in_transaction
    if @_query_analyzer_private_record_transaction
      safe_sql ||= sql.encode(Encoding::UTF_8, invalid: :replace, undef: :replace)
      query_analyzer_call ||= QueryAnalyzerCall.new(safe_sql, caller)
      @_query_analyzer_private_transaction_queue << query_analyzer_call
    end
  end

  # Record interesting queries
  SqlAnalyzer.config[:analyzers].each do |analyzer|
    if SqlAnalyzer.config[:should_log_sample_proc].call(analyzer[:name])
      # This is here rather than above intentionally.
      # We assume we're not going to be analyzing 100% of queries and want to only re-encode
      # when it's actually relevant.
      safe_sql ||= sql.encode(Encoding::UTF_8, invalid: :replace, undef: :replace)
      query_analyzer_call ||= QueryAnalyzerCall.new(safe_sql, caller)

      if safe_sql =~ analyzer[:table_regex]
        SqlAnalyzer.background_processor <<
          _query_analyzer_private_query_stanza([query_analyzer_call], analyzer)
      end
    end
  end

  super
end