63 lines
		
	
	
		
			1.9 KiB
		
	
	
	
		
			Ruby
		
	
	
	
			
		
		
	
	
			63 lines
		
	
	
		
			1.9 KiB
		
	
	
	
		
			Ruby
		
	
	
	
| # frozen_string_literal: true
 | |
| 
 | |
| module Gitlab
 | |
|   module Database
 | |
|     # The purpose of this class is to implement a various query analyzers based on `pg_query`
 | |
|     # And process them all via `Gitlab::Database::QueryAnalyzers::*`
 | |
|     class QueryAnalyzer
 | |
|       include ::Singleton
 | |
| 
 | |
|       ANALYZERS = [].freeze
 | |
| 
 | |
|       Parsed = Struct.new(
 | |
|         :sql, :connection, :pg
 | |
|       )
 | |
| 
 | |
|       def hook!
 | |
|         @subscriber = ActiveSupport::Notifications.subscribe('sql.active_record') do |event|
 | |
|           process_sql(event.payload[:sql], event.payload[:connection])
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       private
 | |
| 
 | |
|       def process_sql(sql, connection)
 | |
|         analyzers = enabled_analyzers(connection)
 | |
|         return unless analyzers.any?
 | |
| 
 | |
|         parsed = parse(sql, connection)
 | |
|         return unless parsed
 | |
| 
 | |
|         analyzers.each do |analyzer|
 | |
|           analyzer.analyze(parsed)
 | |
|         rescue => e # rubocop:disable Style/RescueStandardError
 | |
|           # We catch all standard errors to prevent validation errors to introduce fatal errors in production
 | |
|           Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e)
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       def enabled_analyzers(connection)
 | |
|         ANALYZERS.select do |analyzer|
 | |
|           analyzer.enabled?(connection)
 | |
|         rescue StandardError => e # rubocop:disable Style/RescueStandardError
 | |
|           # We catch all standard errors to prevent validation errors to introduce fatal errors in production
 | |
|           Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e)
 | |
|         end
 | |
|       end
 | |
| 
 | |
|       def parse(sql, connection)
 | |
|         parsed = PgQuery.parse(sql)
 | |
|         return unless parsed
 | |
| 
 | |
|         normalized = PgQuery.normalize(sql)
 | |
|         Parsed.new(normalized, connection, parsed)
 | |
|       rescue PgQuery::ParseError => e
 | |
|         # Ignore PgQuery parse errors (due to depth limit or other reasons)
 | |
|         Gitlab::ErrorTracking.track_exception(e)
 | |
| 
 | |
|         nil
 | |
|       end
 | |
|     end
 | |
|   end
 | |
| end
 |