|
1
|
|
|
# frozen_string_literal: true |
|
2
|
|
|
|
|
3
|
1 |
|
require 'formatador' |
|
4
|
1 |
|
require 'ostruct' |
|
5
|
1 |
|
require 'json' |
|
6
|
|
|
|
|
7
|
1 |
|
module NoSE |
|
8
|
1 |
|
module CLI |
|
9
|
|
|
# Add a command to run the advisor for a given workload |
|
10
|
1 |
|
class NoSECLI < Thor |
|
11
|
1 |
|
desc 'search NAME', 'run the workload NAME' |
|
12
|
|
|
|
|
13
|
1 |
|
long_desc <<-LONGDESC |
|
14
|
|
|
`nose search` is the primary command to run the NoSE advisor. It will |
|
15
|
|
|
load the given workload, enumerate indexes for each statement, and |
|
16
|
|
|
construct and solve an ILP to produce statement execution plans. |
|
17
|
|
|
LONGDESC |
|
18
|
|
|
|
|
19
|
1 |
|
shared_option :mix |
|
20
|
1 |
|
shared_option :format |
|
21
|
1 |
|
shared_option :output |
|
22
|
|
|
|
|
23
|
1 |
|
option :max_space, type: :numeric, default: Float::INFINITY, |
|
24
|
|
|
aliases: '-s', |
|
25
|
|
|
desc: 'maximum space allocated to indexes' |
|
26
|
1 |
|
option :enumerated, type: :boolean, default: false, aliases: '-e', |
|
27
|
|
|
desc: 'whether enumerated indexes should be output' |
|
28
|
1 |
|
option :read_only, type: :boolean, default: false, |
|
29
|
|
|
desc: 'whether to ignore update statements' |
|
30
|
1 |
|
option :objective, type: :string, default: 'cost', |
|
31
|
|
|
enum: %w(cost space indexes), |
|
32
|
|
|
desc: 'the objective function to use in the ILP' |
|
33
|
1 |
|
option :by_id_graph, type: :boolean, default: false, |
|
34
|
|
|
desc: 'whether to group generated indexes in' \ |
|
35
|
|
View Code Duplication |
'graphs by ID', |
|
|
|
|
|
|
36
|
|
|
aliases: '-i' |
|
37
|
|
|
|
|
38
|
1 |
|
def search(name) |
|
|
|
|
|
|
39
|
|
|
# Get the workload and cost model |
|
40
|
7 |
|
workload = Workload.load name |
|
41
|
|
|
workload.mix = options[:mix].to_sym \ |
|
42
|
7 |
|
unless options[:mix] == 'default' && workload.mix != :default |
|
43
|
7 |
|
workload.remove_updates if options[:read_only] |
|
44
|
7 |
|
cost_model = get_class_from_config options, 'cost', :cost_model |
|
45
|
|
|
|
|
46
|
|
|
# Execute the advisor |
|
47
|
7 |
|
objective = Search::Objective.const_get options[:objective].upcase |
|
48
|
7 |
|
result = search_result workload, cost_model, options[:max_space], |
|
49
|
|
|
objective, options[:by_id_graph] |
|
50
|
|
|
output_search_result result, options unless result.nil? |
|
51
|
|
|
end |
|
52
|
|
|
|
|
53
|
1 |
|
private |
|
54
|
|
|
|
|
55
|
|
|
# Output results from the search procedure |
|
56
|
|
|
# @return [void] |
|
57
|
1 |
|
def output_search_result(result, options) |
|
58
|
|
|
# Output the results in the specified format |
|
59
|
|
|
file = if options[:output].nil? |
|
60
|
|
|
$stdout |
|
61
|
|
|
else |
|
62
|
|
|
File.open(options[:output], 'w') |
|
63
|
|
|
end |
|
64
|
|
|
|
|
65
|
|
|
begin |
|
66
|
|
|
backend = get_backend options, result |
|
67
|
|
|
send(('output_' + options[:format]).to_sym, |
|
68
|
|
|
result, file, options[:enumerated], backend) |
|
69
|
|
|
rescue |
|
70
|
|
|
nil |
|
71
|
|
|
ensure |
|
72
|
|
|
file.close unless options[:output].nil? |
|
73
|
|
|
end |
|
74
|
|
|
end |
|
75
|
|
|
end |
|
76
|
|
|
end |
|
77
|
|
|
end |
|
78
|
|
|
|