Module: Sequel::Postgres::AutoParameterizeDuplicateQueryDetection

Defined in:
lib/sequel/extensions/pg_auto_parameterize_duplicate_query_detection.rb

Overview

Enable detecting duplicate queries inside a block

Defined Under Namespace

Classes: DuplicateQueries

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.extended(db) ⇒ Object



62
63
64
65
66
67
# File 'lib/sequel/extensions/pg_auto_parameterize_duplicate_query_detection.rb', line 62

def self.extended(db)
  db.instance_exec do
    @duplicate_query_detection_contexts = {}
    @duplicate_query_detection_mutex = Mutex.new
  end
end

Instance Method Details

#detect_duplicate_queries(opts = OPTS, &block) ⇒ Object

Run the duplicate query detector during the block. Options:

:backtrace_filter

Regexp used to filter the displayed backtrace.

:handler

If present, called with hash of duplicate query information, instead of raising or warning.

:warn

Always warn instead of raising for duplicate queries.

Note that if you nest calls to this method, only the top level call will respect the passed options.



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/sequel/extensions/pg_auto_parameterize_duplicate_query_detection.rb', line 122

def detect_duplicate_queries(opts=OPTS, &block)
  current = Sequel.current
  if state = duplicate_query_recorder_state(current)
    return change_duplicate_query_recorder_state(state, true, &block)
  end

  @duplicate_query_detection_mutex.synchronize do
    @duplicate_query_detection_contexts[current] = [true, Hash.new(0)]
  end

  begin
    yield
  rescue Exception => e
    raise
  ensure
    _, queries = @duplicate_query_detection_mutex.synchronize do
      @duplicate_query_detection_contexts.delete(current)
    end
    queries.delete_if{|_,v| v < 2}

    unless queries.empty?
      if handler = opts[:handler]
        handler.call(queries)
      else
        backtrace_filter = opts[:backtrace_filter]
        backtrace_filter_note = backtrace_filter ? " (filtered)" : ""
        query_info = queries.map do |k,v|
          backtrace = k[1]
          backtrace = backtrace.grep(backtrace_filter) if backtrace_filter
          "times:#{v}\nsql:#{k[0]}\nbacktrace#{backtrace_filter_note}:\n#{backtrace.join("\n")}\n"
        end
        message = "duplicate queries detected:\n\n#{query_info.join("\n")}"

        if e || opts[:warn]
          warn(message)
        else
          raise DuplicateQueries.new(message, queries)
        end
      end
    end
  end
end

#execute(sql, opts = OPTS, &block) ⇒ Object

Record each query executed so duplicates can be detected, if queries are being recorded.



85
86
87
88
89
90
91
92
93
# File 'lib/sequel/extensions/pg_auto_parameterize_duplicate_query_detection.rb', line 85

def execute(sql, opts=OPTS, &block)
  record, queries = duplicate_query_recorder_state

  if record
    queries[[sql.is_a?(Symbol) ? sql : sql.to_s, caller].freeze] += 1
  end

  super
end

#ignore_duplicate_queries(&block) ⇒ Object

Ignore (do not record) queries inside given block. This can be useful in situations where you want to run your entire test suite with duplicate query detection, but you have duplicate queries in some parts of your application where it is not trivial to use a different approach. You can mark those specific sections with ignore_duplicate_queries, and still get duplicate query detection for the rest of the application.



102
103
104
105
106
107
108
109
110
# File 'lib/sequel/extensions/pg_auto_parameterize_duplicate_query_detection.rb', line 102

def ignore_duplicate_queries(&block)
  if state = duplicate_query_recorder_state
    change_duplicate_query_recorder_state(state, false, &block)
  else
    # If we are not inside a detect_duplicate_queries block, there is
    # no need to do anything, since we are not recording queries.
    yield
  end
end