54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
|
# File 'lib/pinspec/inputs/sampler.rb', line 54
def script
<<~RUBY
# frozen_string_literal: true
#
# Generated by pinspec #{VERSION}. Do not edit: regenerate it.
#
# READ-ONLY. This script issues SELECT statements and nothing else. It runs
# inside the target application via `bundle exec rails runner` so that the
# app's own adapter and database.yml decide what it connects to.
#
# Ruby 2.6 syntax floor: this runs in the app's Ruby, not pinspec's.
require "json"
unless defined?(ActiveRecord::Base)
abort("pinspec sampler: ActiveRecord is not loaded; is this a Rails application?")
end
PINSPEC_REQUESTS = JSON.parse(<<'PINSPEC_REQUESTS_JSON')
#{JSON.pretty_generate(@requests.map { |request| stringify(request) })}
PINSPEC_REQUESTS_JSON
def pinspec_connection
ActiveRecord::Base.connection
end
def pinspec_count(table)
quoted = pinspec_connection.quote_table_name(table)
pinspec_connection.select_value("SELECT COUNT(*) FROM " + quoted).to_i
rescue StandardError => e
warn("pinspec sampler: cannot count " + table + ": " + e.class.to_s)
0
end
# Deterministic positions rather than random ones: the same database always
# yields the same corpus, so a plan stays content-addressable.
def pinspec_offsets(total, wanted)
return (0...total).to_a if total <= wanted
fractions = [0.0, 1.0, 0.25, 0.5, 0.75]
offsets = []
fractions.each do |fraction|
offset = (fraction * (total - 1)).round
offsets.push(offset) unless offsets.include?(offset)
break if offsets.length >= wanted
end
offsets
end
def pinspec_rows_at(table, pk, offsets)
quoted = pinspec_connection.quote_table_name(table)
order = pinspec_connection.quote_column_name(pk)
rows = []
offsets.each do |offset|
sql = "SELECT * FROM " + quoted + " ORDER BY " + order +
" LIMIT 1 OFFSET " + offset.to_i.to_s
row = pinspec_connection.select_all(sql).to_a.first
rows.push(row) if row
end
rows
end
# One row per distinct status value: the single highest-coverage win for the
# ubiquitous `case status` service object.
def pinspec_stratified(table, column)
quoted = pinspec_connection.quote_table_name(table)
col = pinspec_connection.quote_column_name(column)
values = pinspec_connection.select_values(
"SELECT DISTINCT " + col + " FROM " + quoted +
" LIMIT #{MAX_DISTINCT_STATUSES}"
)
rows = []
values.each do |value|
next if value.nil?
sql = "SELECT * FROM " + quoted + " WHERE " + col + " = " +
pinspec_connection.quote(value) + " LIMIT 1"
row = pinspec_connection.select_all(sql).to_a.first
rows.push(row) if row
end
rows
end
def pinspec_primary_key(table)
pinspec_connection.primary_key(table) || "id"
rescue StandardError
"id"
end
result = {
"pinspec_sampler_version" => 1,
"env" => (defined?(Rails) ? Rails.env.to_s : ENV["RAILS_ENV"].to_s),
"counts" => {},
"rows" => {},
"stratified" => {},
"errors" => []
}
PINSPEC_REQUESTS.each do |request|
table = request["table"]
begin
total = pinspec_count(table)
result["counts"][table] = total
next if total.zero?
pk = pinspec_primary_key(table)
wanted = (request["limit"] || 5).to_i
result["rows"][table] = pinspec_rows_at(table, pk, pinspec_offsets(total, wanted))
if request["status_column"]
result["stratified"][table] = pinspec_stratified(table, request["status_column"])
end
rescue StandardError => e
result["errors"].push(table + ": " + e.class.to_s + ": " + e.message.to_s)
end
end
puts JSON.generate(result)
RUBY
end
|