forked from rails-sqlserver/activerecord-sqlserver-adapter
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
EXPLAIN it to them! http://youtu.be/ckb3YYZZZ2Q
- Loading branch information
1 parent
046c181
commit 7d00131
Showing
13 changed files
with
387 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,5 +32,6 @@ group :development do | |
gem 'mocha', '0.9.8' | ||
gem 'shoulda', '2.10.3' | ||
gem 'bench_press' | ||
gem 'nokogiri' | ||
end | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
41 changes: 41 additions & 0 deletions
41
lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
module ActiveRecord | ||
module ConnectionAdapters | ||
module Sqlserver | ||
module CoreExt | ||
module Explain | ||
|
||
SQLSERVER_STATEMENT_PREFIX = "EXEC sp_executesql " | ||
SQLSERVER_PARAM_MATCHER = /@\d+ =/ | ||
|
||
def exec_explain(queries) | ||
unprepared_queries = queries.map { |sql, bind| [unprepare_sqlserver_statement(sql), bind] } | ||
super(unprepared_queries) | ||
end | ||
|
||
private | ||
|
||
# This is somewhat hacky, but it should reliably reformat our prepared sql statment | ||
# which uses sp_executesql to just the first argument, then unquote it. Likewise our | ||
# do_exec_query method should substitude the @n args withe the quoted values. | ||
def unprepare_sqlserver_statement(sql) | ||
if sql.starts_with?(SQLSERVER_STATEMENT_PREFIX) | ||
executesql = sql.from(SQLSERVER_STATEMENT_PREFIX.length) | ||
executesql_args = executesql.split(', ') | ||
executesql_args.reject! { |arg| arg =~ SQLSERVER_PARAM_MATCHER } | ||
executesql_args.pop if executesql_args.many? | ||
executesql = executesql_args.join(', ').strip.match(/N'(.*)'/)[1] | ||
Utils.unquote_string(executesql) | ||
else | ||
sql | ||
end | ||
end | ||
|
||
|
||
end | ||
end | ||
end | ||
end | ||
end | ||
|
||
ActiveRecord::Base.extend ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::Explain | ||
ActiveRecord::Relation.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::Explain |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
67 changes: 67 additions & 0 deletions
67
lib/active_record/connection_adapters/sqlserver/showplan.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
require 'active_record/connection_adapters/sqlserver/showplan/printer_table' | ||
require 'active_record/connection_adapters/sqlserver/showplan/printer_xml' | ||
|
||
module ActiveRecord | ||
module ConnectionAdapters | ||
module Sqlserver | ||
module Showplan | ||
|
||
OPTION_ALL = 'SHOWPLAN_ALL' | ||
OPTION_TEXT = 'SHOWPLAN_TEXT' | ||
OPTION_XML = 'SHOWPLAN_XML' | ||
OPTIONS = [OPTION_ALL, OPTION_TEXT, OPTION_XML] | ||
|
||
def explain(arel, binds = []) | ||
sql = to_sql(arel) | ||
result = with_showplan_on { do_exec_query(sql, 'EXPLAIN', binds) } | ||
printer = showplan_printer.new(result) | ||
printer.pp | ||
end | ||
|
||
|
||
protected | ||
|
||
def with_showplan_on | ||
set_showplan_option(true) | ||
yield | ||
ensure | ||
set_showplan_option(false) | ||
end | ||
|
||
def set_showplan_option(enable = true) | ||
sql = "SET #{option} #{enable ? 'ON' : 'OFF'}" | ||
raw_connection_do(sql) | ||
rescue Exception => e | ||
raise ActiveRecordError, "#{option} could not be turned #{enable ? 'ON' : 'OFF'}, perhaps you do not have SHOWPLAN permissions?" | ||
end | ||
|
||
def option | ||
(SQLServerAdapter.showplan_option || OPTION_ALL).tap do |opt| | ||
raise(ArgumentError, "Unknown SHOWPLAN option #{opt.inspect} found.") if OPTIONS.exclude?(opt) | ||
end | ||
end | ||
|
||
def showplan_all? | ||
option == OPTION_ALL | ||
end | ||
|
||
def showplan_text? | ||
option == OPTION_TEXT | ||
end | ||
|
||
def showplan_xml? | ||
option == OPTION_XML | ||
end | ||
|
||
def showplan_printer | ||
case option | ||
when OPTION_XML then PrinterXml | ||
when OPTION_ALL, OPTION_TEXT then PrinterTable | ||
else PrinterTable | ||
end | ||
end | ||
|
||
end | ||
end | ||
end | ||
end |
69 changes: 69 additions & 0 deletions
69
lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
module ActiveRecord | ||
module ConnectionAdapters | ||
module Sqlserver | ||
module Showplan | ||
class PrinterTable | ||
|
||
cattr_accessor :max_column_width, :cell_padding | ||
self.max_column_width = 50 | ||
self.cell_padding = 1 | ||
|
||
attr_reader :result | ||
|
||
def initialize(result) | ||
@result = result | ||
end | ||
|
||
def pp | ||
@widths = compute_column_widths | ||
@separator = build_separator | ||
pp = [] | ||
pp << @separator | ||
pp << build_cells(result.columns) | ||
pp << @separator | ||
result.rows.each do |row| | ||
pp << build_cells(row) | ||
end | ||
pp << @separator | ||
pp.join("\n") + "\n" | ||
end | ||
|
||
private | ||
|
||
def compute_column_widths | ||
[].tap do |computed_widths| | ||
result.columns.each_with_index do |column, i| | ||
cells_in_column = [column] + result.rows.map { |r| cast_item(r[i]) } | ||
computed_width = cells_in_column.map(&:length).max | ||
final_width = computed_width > max_column_width ? max_column_width : computed_width | ||
computed_widths << final_width | ||
end | ||
end | ||
end | ||
|
||
def build_separator | ||
'+' + @widths.map {|w| '-' * (w + (cell_padding*2))}.join('+') + '+' | ||
end | ||
|
||
def build_cells(items) | ||
cells = [] | ||
items.each_with_index do |item, i| | ||
cells << cast_item(item).ljust(@widths[i]) | ||
end | ||
"| #{cells.join(' | ')} |" | ||
end | ||
|
||
def cast_item(item) | ||
case item | ||
when NilClass then 'NULL' | ||
when Float then item.to_s.to(9) | ||
else item.to_s.truncate(max_column_width) | ||
end | ||
end | ||
|
||
end | ||
|
||
end | ||
end | ||
end | ||
end |
25 changes: 25 additions & 0 deletions
25
lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
module ActiveRecord | ||
module ConnectionAdapters | ||
module Sqlserver | ||
module Showplan | ||
class PrinterXml | ||
|
||
def initialize(result) | ||
@result = result | ||
end | ||
|
||
def pp | ||
xml = @result.rows.first.first | ||
if defined?(Nokogiri) | ||
Nokogiri::XML(xml).to_xml :indent => 2, :encoding => 'UTF-8' | ||
else | ||
xml | ||
end | ||
end | ||
|
||
end | ||
|
||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.