module Sequel::Postgres::AutoParameterizeDuplicateQueryDetection
Enable detecting duplicate queries inside a block
Public Class Methods
Source
# File lib/sequel/extensions/pg_auto_parameterize_duplicate_query_detection.rb 62 def self.extended(db) 63 db.instance_exec do 64 @duplicate_query_detection_contexts = {} 65 @duplicate_query_detection_mutex = Mutex.new 66 end 67 end
Public Instance Methods
Source
# File lib/sequel/extensions/pg_auto_parameterize_duplicate_query_detection.rb 122 def detect_duplicate_queries(opts=OPTS, &block) 123 current = Sequel.current 124 if state = duplicate_query_recorder_state(current) 125 return change_duplicate_query_recorder_state(state, true, &block) 126 end 127 128 @duplicate_query_detection_mutex.synchronize do 129 @duplicate_query_detection_contexts[current] = [true, Hash.new(0)] 130 end 131 132 begin 133 yield 134 rescue Exception => e 135 raise 136 ensure 137 _, queries = @duplicate_query_detection_mutex.synchronize do 138 @duplicate_query_detection_contexts.delete(current) 139 end 140 queries.delete_if{|_,v| v < 2} 141 142 unless queries.empty? 143 if handler = opts[:handler] 144 handler.call(queries) 145 else 146 backtrace_filter = opts[:backtrace_filter] 147 backtrace_filter_note = backtrace_filter ? " (filtered)" : "" 148 query_info = queries.map do |k,v| 149 backtrace = k[1] 150 backtrace = backtrace.grep(backtrace_filter) if backtrace_filter 151 "times:#{v}\nsql:#{k[0]}\nbacktrace#{backtrace_filter_note}:\n#{backtrace.join("\n")}\n" 152 end 153 message = "duplicate queries detected:\n\n#{query_info.join("\n")}" 154 155 if e || opts[:warn] 156 warn(message) 157 else 158 raise DuplicateQueries.new(message, queries) 159 end 160 end 161 end 162 end 163 end
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.
Source
# File lib/sequel/extensions/pg_auto_parameterize_duplicate_query_detection.rb 85 def execute(sql, opts=OPTS, &block) 86 record, queries = duplicate_query_recorder_state 87 88 if record 89 queries[[sql.is_a?(Symbol) ? sql : sql.to_s, caller].freeze] += 1 90 end 91 92 super 93 end
Record each query executed so duplicates can be detected, if queries are being recorded.
Source
# File lib/sequel/extensions/pg_auto_parameterize_duplicate_query_detection.rb 102 def ignore_duplicate_queries(&block) 103 if state = duplicate_query_recorder_state 104 change_duplicate_query_recorder_state(state, false, &block) 105 else 106 # If we are not inside a detect_duplicate_queries block, there is 107 # no need to do anything, since we are not recording queries. 108 yield 109 end 110 end
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.
Private Instance Methods
Source
# File lib/sequel/extensions/pg_auto_parameterize_duplicate_query_detection.rb 174 def change_duplicate_query_recorder_state(state, setting) 175 prev = state[0] 176 state[0] = setting 177 178 begin 179 yield 180 ensure 181 state[0] = prev 182 end 183 end
Temporarily change whether to record queries for the block, resetting the previous setting after the block exits.
Source
# File lib/sequel/extensions/pg_auto_parameterize_duplicate_query_detection.rb 168 def duplicate_query_recorder_state(current=Sequel.current) 169 @duplicate_query_detection_mutex.synchronize{@duplicate_query_detection_contexts[current]} 170 end
Get the query record state for the given context.