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
|
|
|
|