activerecord-explain-analyze-0.1.0/0000755000175000017500000000000013525015737016005 5ustar srudsrudactiverecord-explain-analyze-0.1.0/.circleci/0000755000175000017500000000000013525015737017640 5ustar srudsrudactiverecord-explain-analyze-0.1.0/.circleci/config.yml0000644000175000017500000000064313525015737021633 0ustar srudsrud# Ruby CircleCI 2.0 configuration file # Check https://circleci.com/docs/2.0/language-ruby/ for more details version: 2 jobs: build: docker: - image: circleci/ruby:2.4.2 working_directory: ~/repo steps: - checkout - run: bundle install --jobs=4 --retry=3 --path vendor/bundle - run: bundle exec bundle-audit update && bundle exec bundle-audit check - run: bundle exec rspec activerecord-explain-analyze-0.1.0/Rakefile0000644000175000017500000000016513525015737017454 0ustar srudsrudrequire "bundler/gem_tasks" require "rspec/core/rake_task" RSpec::Core::RakeTask.new(:spec) task :default => :spec activerecord-explain-analyze-0.1.0/bin/0000755000175000017500000000000013525015737016555 5ustar srudsrudactiverecord-explain-analyze-0.1.0/bin/setup0000755000175000017500000000020313525015737017636 0ustar srudsrud#!/usr/bin/env bash set -euo pipefail IFS=$'\n\t' set -vx bundle install # Do any other automated setup that you need to do here activerecord-explain-analyze-0.1.0/bin/console0000755000175000017500000000055313525015737020150 0ustar srudsrud#!/usr/bin/env ruby require "bundler/setup" require "activerecord/explain/analyze" # You can add fixtures and/or initialization code here to make experimenting # with your gem easier. You can also use a different console, if you like. # (If you use this, don't forget to add pry to your Gemfile!) # require "pry" # Pry.start require "irb" IRB.start(__FILE__) activerecord-explain-analyze-0.1.0/README.md0000644000175000017500000000517613525015737017275 0ustar srudsrud# activerecord-explain-analyze [![CircleCI](https://circleci.com/gh/6/activerecord-explain-analyze.svg?style=svg)](https://circleci.com/gh/6/activerecord-explain-analyze) Extends ActiveRecord#explain with support for EXPLAIN ANALYZE and output formats of JSON, XML, and YAML. It currently supports ActiveRecord 4 and 5, and PostgreSQL only. ### Examples: ```ruby Wallet.where(base_currency: "USD").explain(analyze: true) ``` Results in: ```sql EXPLAIN for: SELECT "wallets".* FROM "wallets" WHERE "wallets"."deleted_at" IS NULL AND "wallets"."base_currency" = $1 Bitmap Heap Scan on public.wallets (cost=4.16..9.50 rows=1 width=164) (actual time=0.008..0.012 rows=30 loops=1) Output: id, canonical_id, client_id, wallet_type, base_currency, created_at, updated_at, deleted_at Recheck Cond: (wallets.deleted_at IS NULL) Filter: ((wallets.base_currency)::text = 'USD'::text) Heap Blocks: exact=1 Buffers: shared hit=2 -> Bitmap Index Scan on index_wallets_on_deleted_at (cost=0.00..4.16 rows=2 width=0) (actual time=0.003..0.003 rows=32 loops=1) Index Cond: (wallets.deleted_at IS NULL) Buffers: shared hit=1 Planning time: 0.041 ms Execution time: 0.026 ms ``` ```ruby Wallet.where(base_currency: "USD").explain(analyze: true, format: :json) ``` Results in: ```json [ { "Plan": { "Node Type": "Bitmap Heap Scan", "Parallel Aware": false, "Relation Name": "wallets", "Schema": "public", "Alias": "wallets", "Startup Cost": 4.16, "Total Cost": 9.50, "Plan Rows": 1, "Plan Width": 164, "Actual Startup Time": 0.008, "Actual Total Time": 0.013, "Actual Rows": 30, ... ``` You can then paste this JSON output into [PEV](http://tatiyants.com/pev/) or similar tools to get a visualization of the EXPLAIN query output: screen shot 2017-10-27 at 4 24 38 pm ## Installation Add this line to your application's Gemfile and run `bundle` to install: ```ruby gem 'activerecord-explain-analyze' ``` ## Development After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). activerecord-explain-analyze-0.1.0/.gitignore0000644000175000017500000000022413525015737017773 0ustar srudsrud/.bundle/ /.yardoc /Gemfile.lock /_yardoc/ /coverage/ /doc/ /pkg/ /spec/reports/ /tmp/ # rspec failure tracking .rspec_status .ruby-version *.gem activerecord-explain-analyze-0.1.0/lib/0000755000175000017500000000000013525015737016553 5ustar srudsrudactiverecord-explain-analyze-0.1.0/lib/activerecord-explain-analyze.rb0000644000175000017500000000066013525015737024653 0ustar srudsrudrequire "active_record" require "active_record/connection_adapters/postgresql_adapter" require "activerecord-explain-analyze/version" require "activerecord-explain-analyze/relation" require "activerecord-explain-analyze/postgresql_adapter" ActiveRecord::Relation.send(:prepend, ActiveRecordExplainAnalyze::Relation) ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send(:include, ActiveRecordExplainAnalyze::PostgreSQLAdapter) activerecord-explain-analyze-0.1.0/lib/activerecord-explain-analyze/0000755000175000017500000000000013525015737024324 5ustar srudsrudactiverecord-explain-analyze-0.1.0/lib/activerecord-explain-analyze/postgresql_adapter.rb0000644000175000017500000000164313525015737030560 0ustar srudsrudmodule ActiveRecordExplainAnalyze module PostgreSQLAdapter def explain_with_options(arel, binds = [], analyze, format) options = [] options.concat(["ANALYZE", "COSTS", "VERBOSE", "BUFFERS"]) if analyze options << "FORMAT #{format}" unless format == "TEXT" options_sql = options.size > 0 ? "(#{options.join(', ')})" : "" sql = "EXPLAIN #{options_sql} #{to_sql(arel, binds)}" result = exec_query(sql, "EXPLAIN", binds) if format == "TEXT" && explain_pretty_printer explain_pretty_printer.new.pp(result) else result.rows.map(&:first).join("\n") end end private def explain_pretty_printer if defined?(ExplainPrettyPrinter) # Rails 4: ExplainPrettyPrinter elsif defined?(PostgreSQL::ExplainPrettyPrinter) # Rails 5: PostgreSQL::ExplainPrettyPrinter else nil end end end end activerecord-explain-analyze-0.1.0/lib/activerecord-explain-analyze/relation.rb0000644000175000017500000000175213525015737026473 0ustar srudsrudmodule ActiveRecordExplainAnalyze module Relation EXPLAIN_FORMATS = [ "JSON", "TEXT", "XML", "YAML", ].freeze def explain(analyze: false, format: :text) format = format.to_s.upcase unless EXPLAIN_FORMATS.include?(format) raise ArgumentError, "format must be one of: #{EXPLAIN_FORMATS.join(', ')}" end queries = collecting_queries_for_explain { exec_queries } if analyze || format != "TEXT" exec_explain_with_options(queries, analyze: analyze, format: format) else exec_explain(queries) end end def exec_explain_with_options(queries, analyze:, format:) str = queries.map do |sql, binds| msg = "EXPLAIN for: #{sql}\n".dup msg << connection.explain_with_options(sql, binds, analyze, format) end.join("\n") # Overriding inspect to be more human readable, especially in the console. def str.inspect self end str end end end activerecord-explain-analyze-0.1.0/lib/activerecord-explain-analyze/version.rb0000644000175000017500000000007213525015737026335 0ustar srudsrudmodule ActiveRecordExplainAnalyze VERSION = "0.1.0" end activerecord-explain-analyze-0.1.0/Gemfile0000644000175000017500000000026713525015737017305 0ustar srudsrudsource "https://rubygems.org" git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } # Specify your gem's dependencies in activerecord-explain-analyze.gemspec gemspec activerecord-explain-analyze-0.1.0/.rspec0000644000175000017500000000006513525015737017123 0ustar srudsrud--format documentation --color --require spec_helper activerecord-explain-analyze-0.1.0/activerecord-explain-analyze.gemspec0000644000175000017500000000263113525015737025125 0ustar srudsrud# coding: utf-8 lib = File.expand_path("../lib", __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require "activerecord-explain-analyze/version" Gem::Specification.new do |spec| spec.name = "activerecord-explain-analyze" spec.version = ActiveRecordExplainAnalyze::VERSION spec.authors = ["Peter Graham"] spec.email = ["peterghm@gmail.com"] spec.licenses = ["MIT"] spec.summary = %q{ActiveRecord#explain with support for EXPLAIN ANALYZE and a variety of output formats} spec.description = %q{Extends ActiveRecord#explain with support for EXPLAIN ANALYZE and output formats of JSON, XML, and YAML.} spec.homepage = "https://github.com/6/activerecord-explain-analyze" spec.files = `git ls-files -z`.split("\x0").reject do |f| f.match(%r{^(test|spec|features)/}) end spec.bindir = "exe" spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] spec.add_dependency "activerecord", ">= 4" spec.add_dependency "pg" spec.add_development_dependency "bundler", "~> 1.15" spec.add_development_dependency "rake", "~> 10.0" spec.add_development_dependency "rspec", "~> 3.0" spec.add_development_dependency "pry" spec.add_development_dependency "rspec-collection_matchers" spec.add_development_dependency "rspec-its" spec.add_development_dependency "bundler-audit" end