sql-jarvis 2.0.1 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -1
  3. data/LICENSE.txt +1 -1
  4. data/README.md +153 -52
  5. data/app/assets/fonts/blazer/glyphicons-halflings-regular.eot +0 -0
  6. data/app/assets/fonts/blazer/glyphicons-halflings-regular.svg +0 -0
  7. data/app/assets/fonts/blazer/glyphicons-halflings-regular.ttf +0 -0
  8. data/app/assets/fonts/blazer/glyphicons-halflings-regular.woff +0 -0
  9. data/app/assets/fonts/blazer/glyphicons-halflings-regular.woff2 +0 -0
  10. data/app/assets/images/blazer/favicon.png +0 -0
  11. data/app/assets/javascripts/blazer/Chart.js +4195 -3884
  12. data/app/assets/javascripts/blazer/Sortable.js +1493 -1097
  13. data/app/assets/javascripts/blazer/ace/ace.js +21294 -4
  14. data/app/assets/javascripts/blazer/ace/ext-language_tools.js +1991 -3
  15. data/app/assets/javascripts/blazer/ace/mode-sql.js +110 -1
  16. data/app/assets/javascripts/blazer/ace/snippets/sql.js +40 -1
  17. data/app/assets/javascripts/blazer/ace/snippets/text.js +14 -1
  18. data/app/assets/javascripts/blazer/ace/theme-twilight.js +116 -1
  19. data/app/assets/javascripts/blazer/application.js +4 -3
  20. data/app/assets/javascripts/blazer/bootstrap.js +623 -612
  21. data/app/assets/javascripts/blazer/chartkick.js +1769 -1248
  22. data/app/assets/javascripts/blazer/daterangepicker.js +263 -115
  23. data/app/assets/javascripts/blazer/highlight.min.js +3 -0
  24. data/app/assets/javascripts/blazer/{jquery_ujs.js → jquery-ujs.js} +161 -75
  25. data/app/assets/javascripts/blazer/jquery.js +9506 -9450
  26. data/app/assets/javascripts/blazer/jquery.stickytableheaders.js +321 -259
  27. data/app/assets/javascripts/blazer/moment-timezone-with-data.js +1212 -0
  28. data/app/assets/javascripts/blazer/queries.js +1 -1
  29. data/app/assets/javascripts/blazer/routes.js +3 -0
  30. data/app/assets/javascripts/blazer/selectize.js +3828 -3604
  31. data/app/assets/javascripts/blazer/stupidtable.js +255 -88
  32. data/app/assets/javascripts/blazer/vue.js +8015 -4583
  33. data/app/assets/stylesheets/blazer/application.css +41 -5
  34. data/app/assets/stylesheets/blazer/bootstrap.css.erb +879 -325
  35. data/app/assets/stylesheets/blazer/daterangepicker.css +269 -0
  36. data/app/assets/stylesheets/blazer/selectize.default.css +26 -10
  37. data/app/controllers/blazer/base_controller.rb +7 -0
  38. data/app/controllers/blazer/checks_controller.rb +1 -1
  39. data/app/controllers/blazer/dashboards_controller.rb +0 -4
  40. data/app/controllers/blazer/queries_controller.rb +20 -12
  41. data/app/helpers/blazer/base_helper.rb +1 -1
  42. data/app/mailers/blazer/slack_notifier.rb +76 -0
  43. data/app/models/blazer/check.rb +9 -0
  44. data/app/views/blazer/_nav.html.erb +0 -1
  45. data/app/views/blazer/_variables.html.erb +41 -19
  46. data/app/views/blazer/checks/_form.html.erb +16 -8
  47. data/app/views/blazer/checks/edit.html.erb +2 -0
  48. data/app/views/blazer/checks/index.html.erb +33 -4
  49. data/app/views/blazer/checks/new.html.erb +2 -0
  50. data/app/views/blazer/dashboards/_form.html.erb +4 -4
  51. data/app/views/blazer/dashboards/edit.html.erb +2 -0
  52. data/app/views/blazer/dashboards/new.html.erb +2 -0
  53. data/app/views/blazer/dashboards/show.html.erb +7 -3
  54. data/app/views/blazer/queries/_form.html.erb +11 -6
  55. data/app/views/blazer/queries/docs.html.erb +131 -0
  56. data/app/views/blazer/queries/home.html.erb +12 -3
  57. data/app/views/blazer/queries/run.html.erb +36 -6
  58. data/app/views/blazer/queries/schema.html.erb +46 -8
  59. data/app/views/blazer/queries/show.html.erb +4 -4
  60. data/app/views/layouts/blazer/application.html.erb +3 -3
  61. data/config/routes.rb +5 -1
  62. data/lib/blazer.rb +32 -13
  63. data/lib/blazer/adapters/athena_adapter.rb +1 -1
  64. data/lib/blazer/adapters/elasticsearch_adapter.rb +14 -17
  65. data/lib/blazer/adapters/mongodb_adapter.rb +1 -1
  66. data/lib/blazer/adapters/sql_adapter.rb +7 -1
  67. data/lib/blazer/engine.rb +4 -0
  68. data/lib/blazer/result.rb +62 -29
  69. data/lib/blazer/run_statement_job.rb +6 -9
  70. data/lib/blazer/version.rb +1 -1
  71. data/lib/generators/blazer/templates/config.yml.tt +13 -2
  72. data/lib/generators/blazer/templates/install.rb.tt +1 -0
  73. data/lib/tasks/blazer.rake +1 -0
  74. metadata +33 -37
  75. data/.gitattributes +0 -1
  76. data/.github/ISSUE_TEMPLATE.md +0 -7
  77. data/.gitignore +0 -14
  78. data/Gemfile +0 -7
  79. data/Rakefile +0 -1
  80. data/app/assets/javascripts/blazer/highlight.pack.js +0 -1
  81. data/app/assets/javascripts/blazer/moment-timezone.js +0 -1007
  82. data/app/assets/stylesheets/blazer/daterangepicker-bs3.css +0 -375
  83. data/blazer.gemspec +0 -30
@@ -1,6 +1,6 @@
1
1
  <div id="queries">
2
- <div id="header" style="margin-bottom: 20px;">
3
- <div class="pull-right">
2
+ <div id="header">
3
+ <div class="pull-right" style="line-height: 34px;">
4
4
  <% if blazer_user %>
5
5
  <div class='btn-group' style='margin-right: 20px;'>
6
6
  <%= link_to "All", root_path, class: !params[:filter] ? 'active btn btn-default btn-xs' : 'btn btn-default btn-xs' %>
@@ -21,12 +21,14 @@
21
21
  <span class="sr-only">Toggle Dropdown</span>
22
22
  </button>
23
23
  <ul class="dropdown-menu">
24
+ <li><%= link_to "Checks", checks_path %></li>
25
+ <li role="separator" class="divider"></li>
24
26
  <li><%= link_to "New Dashboard", new_dashboard_path %></li>
25
27
  <li><%= link_to "New Check", new_check_path %></li>
26
28
  </ul>
27
29
  </div>
28
30
  </div>
29
- <input type="text" v-model="searchTerm" placeholder="Start typing a query or person" style="width: 300px; display: inline-block;" autofocus=true class="search form-control" />
31
+ <input type="text" v-model="searchTerm" placeholder="Start typing a query, dashboard, or person" style="width: 300px; display: inline-block;" v-focus class="search form-control" />
30
32
  </div>
31
33
 
32
34
  <table class="table">
@@ -148,6 +150,13 @@
148
150
  return Routes.query_path(item.to_param)
149
151
  }
150
152
  }
153
+ },
154
+ directives: {
155
+ focus: {
156
+ inserted: function (el) {
157
+ el.focus()
158
+ }
159
+ }
151
160
  }
152
161
  })
153
162
  </script>
@@ -24,7 +24,7 @@
24
24
  <% end %>
25
25
  </p>
26
26
  <% end %>
27
- <p class="text-muted">
27
+ <p class="text-muted" style="margin-bottom: 10px;">
28
28
  <%= pluralize(@rows.size, "row") %>
29
29
 
30
30
  <% @checks.select(&:state).each do |check| %>
@@ -33,19 +33,46 @@
33
33
  &middot; <%= check.message %>
34
34
  <% end %>
35
35
  <% end %>
36
+
37
+ <% if @query && @result.forecastable? && !params[:forecast] %>
38
+ &middot;
39
+ <%= link_to "Forecast", query_path(@query, {forecast: "t"}.merge(variable_params)) %>
40
+ <% end %>
36
41
  </p>
37
42
  <% end %>
43
+ <% if @forecast_error %>
44
+ <div class="alert alert-danger"><%= @forecast_error %></div>
45
+ <% end %>
38
46
  <% if @rows.any? %>
39
47
  <% values = @rows.first %>
40
48
  <% chart_id = SecureRandom.hex %>
41
49
  <% column_types = @result.column_types %>
42
50
  <% chart_type = @result.chart_type %>
43
- <% chart_options = {id: chart_id, min: nil} %>
51
+ <% chart_options = {id: chart_id} %>
52
+ <% if ["line", "line2"].include?(chart_type) %>
53
+ <% chart_options.merge!(min: nil) %>
54
+ <% end %>
55
+ <% if chart_type == "scatter" %>
56
+ <% chart_options.merge!(library: {tooltips: {intersect: false}}) %>
57
+ <% elsif ["bar", "bar2"].include?(chart_type) %>
58
+ <% chart_options.merge!(library: {tooltips: {intersect: false, axis: 'x'}}) %>
59
+ <% elsif chart_type != "pie" %>
60
+ <% if column_types.size == 2 || @forecast %>
61
+ <% chart_options.merge!(library: {tooltips: {intersect: false, axis: 'x'}}) %>
62
+ <% else %>
63
+ <%# chartjs axis: 'x' has poor behavior with multiple series %>
64
+ <% chart_options.merge!(library: {tooltips: {intersect: false}}) %>
65
+ <% end %>
66
+ <% end %>
44
67
  <% series_library = {} %>
45
68
  <% target_index = @columns.index { |k| k.downcase == "target" } %>
46
69
  <% if target_index %>
47
70
  <% series_library[target_index - 1] = {pointStyle: "line", hitRadius: 5, borderColor: "#109618", pointBackgroundColor: "#109618", backgroundColor: "#109618"} %>
48
71
  <% end %>
72
+ <% if @forecast %>
73
+ <% color = "#54a3ee" %>
74
+ <% series_library[1] = {borderDash: [8], borderColor: color, pointBackgroundColor: color, backgroundColor: color, pointHoverBackgroundColor: color} %>
75
+ <% end %>
49
76
  <% if blazer_maps? && @markers.any? %>
50
77
  <div id="map" style="height: <%= @only_chart ? 300 : 500 %>px;"></div>
51
78
  <script>
@@ -76,11 +103,14 @@
76
103
  map.fitBounds(featureLayer.getBounds());
77
104
  </script>
78
105
  <% elsif chart_type == "line" %>
79
- <%= line_chart @columns[1..-1].each_with_index.map{ |k, i| {name: blazer_series_name(k), data: @rows.map{ |r| [r[0], r[i + 1]] }, library: series_library[i]} }, chart_options %>
106
+ <% chart_data = @columns[1..-1].each_with_index.map{ |k, i| {name: blazer_series_name(k), data: @rows.map{ |r| [r[0], r[i + 1]] }, library: series_library[i]} } %>
107
+ <%= line_chart chart_data, chart_options %>
80
108
  <% elsif chart_type == "line2" %>
81
109
  <%= line_chart @rows.group_by { |r| v = r[1]; (@boom[@columns[1]] || {})[v.to_s] || v }.each_with_index.map { |(name, v), i| {name: blazer_series_name(name), data: v.map { |v2| [v2[0], v2[2]] }, library: series_library[i]} }, chart_options %>
110
+ <% elsif chart_type == "pie" %>
111
+ <%= pie_chart @rows.map { |r| [(@boom[@columns[0]] || {})[r[0].to_s] || r[0], r[1]] }, chart_options %>
82
112
  <% elsif chart_type == "bar" %>
83
- <%= column_chart (values.size - 1).times.map { |i| name = @columns[i + 1]; {name: blazer_series_name(name), data: @rows.first(20).map { |r| [(@boom[@columns[0]] || {})[r[0].to_s] || r[0], r[i + 1]] } } }, id: chart_id %>
113
+ <%= column_chart (values.size - 1).times.map { |i| name = @columns[i + 1]; {name: blazer_series_name(name), data: @rows.first(20).map { |r| [(@boom[@columns[0]] || {})[r[0].to_s] || r[0], r[i + 1]] } } }, chart_options %>
84
114
  <% elsif chart_type == "bar2" %>
85
115
  <% first_20 = @rows.group_by { |r| r[0] }.values.first(20).flatten(1) %>
86
116
  <% labels = first_20.map { |r| r[0] }.uniq %>
@@ -90,9 +120,9 @@
90
120
  <% first_20 << [l, s, 0] unless first_20.find { |r| r[0] == l && r[1] == s } %>
91
121
  <% end %>
92
122
  <% end %>
93
- <%= column_chart first_20.group_by { |r| v = r[1]; (@boom[@columns[1]] || {})[v.to_s] || v }.each_with_index.map { |(name, v), i| {name: blazer_series_name(name), data: v.sort_by { |r2| labels.index(r2[0]) }.map { |v2| v3 = v2[0]; [(@boom[@columns[0]] || {})[v3.to_s] || v3, v2[2]] }} }, id: chart_id %>
123
+ <%= column_chart first_20.group_by { |r| v = r[1]; (@boom[@columns[1]] || {})[v.to_s] || v }.each_with_index.map { |(name, v), i| {name: blazer_series_name(name), data: v.sort_by { |r2| labels.index(r2[0]) }.map { |v2| v3 = v2[0]; [(@boom[@columns[0]] || {})[v3.to_s] || v3, v2[2]] }} }, chart_options %>
94
124
  <% elsif chart_type == "scatter" %>
95
- <%= scatter_chart @rows, xtitle: @columns[0], ytitle: @columns[1], id: chart_id %>
125
+ <%= scatter_chart @rows, xtitle: @columns[0], ytitle: @columns[1], **chart_options %>
96
126
  <% elsif @only_chart %>
97
127
  <% if @rows.size == 1 && @rows.first.size == 1 %>
98
128
  <% v = @rows.first.first %>
@@ -1,13 +1,25 @@
1
- <% blazer_title "Database Schema" %>
1
+ <% blazer_title "Schema: #{@data_source.name}" %>
2
+
3
+ <h1>Schema: <%= @data_source.name %></h1>
4
+
5
+ <hr />
6
+
7
+ <div id="header">
8
+ <input id="search" type="text" placeholder="Start typing a table or column" style="width: 300px; display: inline-block;" class="search form-control" />
9
+ </div>
2
10
 
3
11
  <% @schema.each do |table| %>
4
- <h4>
5
- <%= table[:table] %>
6
- <% if table[:schema] != "public" %>
7
- <small><%= table[:schema] %></small>
8
- <% end %>
9
- </h4>
10
- <table class="table" style="max-width: 500px;">
12
+ <table class="table schema-table">
13
+ <thead>
14
+ <tr>
15
+ <th colspan="2">
16
+ <%= table[:table] %>
17
+ <% if table[:schema] != "public" %>
18
+ <span class="text-muted" style="font-weight: normal;"><%= table[:schema] %></span>
19
+ <% end %>
20
+ </th>
21
+ </tr>
22
+ </thead>
11
23
  <tbody>
12
24
  <% table[:columns].each do |column| %>
13
25
  <tr>
@@ -18,3 +30,29 @@
18
30
  </tbody>
19
31
  </table>
20
32
  <% end %>
33
+
34
+ <script>
35
+ $("#search").on("keyup", function() {
36
+ var value = $(this).val().toLowerCase()
37
+ $(".schema-table").filter(function() {
38
+ // if found in table name, show entire table
39
+ // if just found in rows, show row
40
+
41
+ var found = $(this).find("thead").text().toLowerCase().indexOf(value) > -1
42
+
43
+ if (found) {
44
+ $(this).find("tbody tr").toggle(true)
45
+ } else {
46
+ $(this).find("tbody tr").filter(function() {
47
+ var found2 = $(this).text().toLowerCase().indexOf(value) > -1
48
+ $(this).toggle(found2)
49
+ if (found2) {
50
+ found = true
51
+ }
52
+ })
53
+ }
54
+
55
+ $(this).toggle(found)
56
+ })
57
+ }).focus()
58
+ </script>
@@ -5,7 +5,7 @@
5
5
  <div class="row" style="padding-top: 13px;">
6
6
  <div class="col-sm-8">
7
7
  <%= render partial: "blazer/nav" %>
8
- <h3 style="margin: 0; line-height: 34px; display: inline;">
8
+ <h3 style="line-height: 34px; display: inline; margin-left: 5px;">
9
9
  <%= @query.name %>
10
10
  </h3>
11
11
  </div>
@@ -14,7 +14,7 @@
14
14
  <%= link_to "Fork", new_query_path(variable_params.merge(fork_query_id: @query.id, data_source: @query.data_source, name: @query.name)), class: "btn btn-info" %>
15
15
 
16
16
  <% if !@error && @success %>
17
- <%= button_to "⤓ .csv", run_queries_path(query_id: @query.id, format: "csv"), params: {statement: @statement}, class: "btn btn-primary" %>
17
+ <%= button_to "⤓ .csv", run_queries_path(query_id: @query.id, format: "csv", forecast: params[:forecast]), params: {statement: @statement}, class: "btn btn-primary" %>
18
18
  <%= button_to "⤓ .xlsx", run_queries_path(query_id: @query.id, format: "xlsx"), params: {statement: @statement}, class: "btn btn-primary" %>
19
19
  <% end %>
20
20
  </div>
@@ -57,13 +57,13 @@
57
57
  $("#results").addClass("query-error").html(message)
58
58
  }
59
59
 
60
- <%= blazer_js_var "data", variable_params.merge(statement: @statement, query_id: @query.id) %>
60
+ <%= blazer_js_var "data", variable_params.merge(statement: @statement, query_id: @query.id, data_source: @query.data_source) %>
61
61
 
62
62
  runQuery(data, showRun, showError)
63
63
  </script>
64
64
  <% end %>
65
65
 
66
- <% unless %w(mongodb elasticsearch).include?(Blazer.data_sources[@query.data_source].adapter) %>
66
+ <% unless %w(mongodb).include?(Blazer.data_sources[@query.data_source].adapter) %>
67
67
  <script>
68
68
  // do not highlight really long queries
69
69
  // this can lead to performance issues
@@ -4,15 +4,15 @@
4
4
  <title><%= blazer_title ? blazer_title : "Blazer" %></title>
5
5
 
6
6
  <meta charset="utf-8" />
7
-
7
+ <%= favicon_link_tag "blazer/favicon.png" %>
8
8
  <%= stylesheet_link_tag "blazer/application" %>
9
9
  <%= javascript_include_tag "blazer/application" %>
10
10
  <script>
11
11
  <%= blazer_js_var "rootPath", root_path %>
12
12
  </script>
13
13
  <% if blazer_maps? %>
14
- <%= stylesheet_link_tag "https://api.mapbox.com/mapbox.js/v2.4.0/mapbox.css" %>
15
- <%= javascript_include_tag "https://api.mapbox.com/mapbox.js/v2.4.0/mapbox.js" %>
14
+ <%= stylesheet_link_tag "https://api.mapbox.com/mapbox.js/v3.1.1/mapbox.css", integrity: "sha384-o8tecBIfqi9yU5yYK2Ne/9A9hlOGFV9MBvCpmemvJH1XxqOe6h8Bl4mLxMi6PgjG", crossorigin: "anonymous" %>
15
+ <%= javascript_include_tag "https://api.mapbox.com/mapbox.js/v3.1.1/mapbox.js", integrity: "sha384-j81LqvtvYigFzGSUgAoFijpvoq4yGoCJSOXI9DFaUEpenR029MBE3E/X5Gr+WdO0", crossorigin: "anonymous" %>
16
16
  <% end %>
17
17
  <%= csrf_meta_tags %>
18
18
  </head>
data/config/routes.rb CHANGED
@@ -6,12 +6,16 @@ Blazer::Engine.routes.draw do
6
6
  get :tables, on: :collection
7
7
  get :columns, on: :collection
8
8
  get :schema, on: :collection
9
+ get :docs, on: :collection
9
10
  end
11
+
10
12
  resources :checks, except: [:show] do
11
13
  get :run, on: :member
12
14
  end
13
- resources :dashboards do
15
+
16
+ resources :dashboards, except: [:index] do
14
17
  post :refresh, on: :member
15
18
  end
19
+
16
20
  root to: "queries#home"
17
21
  end
data/lib/blazer.rb CHANGED
@@ -1,14 +1,19 @@
1
+ # dependencies
1
2
  require "csv"
2
3
  require "yaml"
3
4
  require "chartkick"
4
5
  require "zip/zip"
5
6
  require "axlsx"
6
7
  require "safely/core"
8
+
9
+ # modules
7
10
  require "blazer/version"
8
11
  require "blazer/data_source"
9
12
  require "blazer/result"
10
13
  require "blazer/excel_parser"
11
14
  require "blazer/run_statement"
15
+
16
+ # adapters
12
17
  require "blazer/adapters/base_adapter"
13
18
  require "blazer/adapters/athena_adapter"
14
19
  require "blazer/adapters/bigquery_adapter"
@@ -20,6 +25,8 @@ require "blazer/adapters/mongodb_adapter"
20
25
  require "blazer/adapters/presto_adapter"
21
26
  require "blazer/adapters/sql_adapter"
22
27
  require "blazer/adapters/snowflake_adapter"
28
+
29
+ # engine
23
30
  require "blazer/engine"
24
31
 
25
32
  module Blazer
@@ -37,14 +44,18 @@ module Blazer
37
44
  attr_accessor :subdomain
38
45
  attr_accessor :cache
39
46
  attr_accessor :transform_statement
47
+ attr_accessor :transform_variable
40
48
  attr_accessor :check_schedules
41
49
  attr_accessor :mapbox_access_token
42
50
  attr_accessor :assignees
43
51
  attr_accessor :anomaly_checks
52
+ attr_accessor :forecasting
44
53
  attr_accessor :async
45
54
  attr_accessor :images
46
55
  attr_accessor :query_viewable
47
56
  attr_accessor :query_editable
57
+ attr_accessor :override_csp
58
+ attr_accessor :slack_webhook_url
48
59
  attr_accessor :query_creatable
49
60
  end
50
61
  self.audit = true
@@ -54,8 +65,10 @@ module Blazer
54
65
  self.assignees = []
55
66
  self.subdomain = nil
56
67
  self.anomaly_checks = false
68
+ self.forecasting = false
57
69
  self.async = false
58
70
  self.images = false
71
+ self.override_csp = false
59
72
 
60
73
  TIMEOUT_MESSAGE = "Query timed out :("
61
74
  TIMEOUT_ERRORS = [
@@ -103,20 +116,11 @@ module Blazer
103
116
 
104
117
  def self.data_sources
105
118
  @data_sources ||= begin
106
- ds = Hash[
107
- settings["data_sources"].map do |id, s|
108
- [id, Blazer::DataSource.new(id, s)]
109
- end
110
- ]
111
- ds.default = ds.values.first
119
+ ds = Hash.new { |hash, key| raise Blazer::Error, "Unknown data source: #{key}" }
120
+ settings["data_sources"].each do |id, s|
121
+ ds[id] = Blazer::DataSource.new(id, s)
122
+ end
112
123
  ds
113
-
114
- # TODO Blazer 2.0
115
- # ds2 = Hash.new { |hash, key| raise Blazer::Error, "Unknown data source: #{key}" }
116
- # ds.each do |k, v|
117
- # ds2[k] = v
118
- # end
119
- # ds2
120
124
  end
121
125
  end
122
126
 
@@ -186,10 +190,15 @@ module Blazer
186
190
 
187
191
  def self.send_failing_checks
188
192
  emails = {}
193
+ slack_channels = {}
194
+
189
195
  Blazer::Check.includes(:query).where(state: ["failing", "error", "timed out", "disabled"]).find_each do |check|
190
196
  check.split_emails.each do |email|
191
197
  (emails[email] ||= []) << check
192
198
  end
199
+ check.split_slack_channels.each do |channel|
200
+ (slack_channels[channel] ||= []) << check
201
+ end
193
202
  end
194
203
 
195
204
  emails.each do |email, checks|
@@ -197,6 +206,16 @@ module Blazer
197
206
  Blazer::CheckMailer.failing_checks(email, checks).deliver_now
198
207
  end
199
208
  end
209
+
210
+ slack_channels.each do |channel, checks|
211
+ Safely.safely do
212
+ Blazer::SlackNotifier.failing_checks(channel, checks)
213
+ end
214
+ end
215
+ end
216
+
217
+ def self.slack?
218
+ slack_webhook_url.present?
200
219
  end
201
220
 
202
221
  def self.adapters
@@ -13,7 +13,7 @@ module Blazer
13
13
  client.start_query_execution(
14
14
  query_string: statement,
15
15
  # use token so we fetch cached results after query is run
16
- client_request_token: Digest::MD5.hexdigest(statement),
16
+ client_request_token: Digest::MD5.hexdigest([statement,data_source.id].join("/")),
17
17
  query_execution_context: {
18
18
  database: database,
19
19
  },
@@ -7,22 +7,17 @@ module Blazer
7
7
  error = nil
8
8
 
9
9
  begin
10
- header, body = statement.gsub(/\/\/.+/, "").strip.split("\n", 2)
11
- body = JSON.parse(body)
12
- body["timeout"] ||= data_source.timeout if data_source.timeout
13
- response = client.msearch(body: [JSON.parse(header), body])["responses"].first
14
- if response["error"]
15
- error = response["error"]
16
- else
17
- hits = response["hits"]["hits"]
18
- source_keys = hits.flat_map { |r| r["_source"].keys }.uniq
19
- hit_keys = (hits.first.try(:keys) || []) - ["_source"]
20
- columns = source_keys + hit_keys
21
- rows =
22
- hits.map do |r|
23
- source = r["_source"]
24
- source_keys.map { |k| source[k] } + hit_keys.map { |k| r[k] }
10
+ response = client.xpack.sql.query(body: {query: "#{statement} /*#{comment}*/"})
11
+ columns = response["columns"].map { |v| v["name"] }
12
+ # Elasticsearch does not differentiate between dates and times
13
+ date_indexes = response["columns"].each_index.select { |i| response["columns"][i]["type"] == "date" }
14
+ if columns.any?
15
+ rows = response["rows"]
16
+ date_indexes.each do |i|
17
+ rows.each do |row|
18
+ row[i] = Time.parse(row[i])
25
19
  end
20
+ end
26
21
  end
27
22
  rescue => e
28
23
  error = e.message
@@ -32,11 +27,13 @@ module Blazer
32
27
  end
33
28
 
34
29
  def tables
35
- client.indices.get_aliases(name: "*").map { |k, v| [k, v["aliases"].keys] }.flatten.uniq.sort
30
+ indices = client.cat.indices(format: "json").map { |v| v["index"] }
31
+ aliases = client.cat.aliases(format: "json").map { |v| v["alias"] }
32
+ (indices + aliases).uniq.sort
36
33
  end
37
34
 
38
35
  def preview_statement
39
- %!// header\n{"index": "{table}"}\n\n// body\n{"query": {"match_all": {}}, "size": 10}!
36
+ "SELECT * FROM \"{table}\" LIMIT 10"
40
37
  end
41
38
 
42
39
  protected
@@ -7,7 +7,7 @@ module Blazer
7
7
  error = nil
8
8
 
9
9
  begin
10
- documents = db.command({:$eval => "#{statement.strip}.toArray()"}).documents.first["retval"]
10
+ documents = db.command({:$eval => "#{statement.strip}.toArray()", nolock: true}).documents.first["retval"]
11
11
  columns = documents.flat_map { |r| r.keys }.uniq
12
12
  rows = documents.map { |r| columns.map { |c| r[c] } }
13
13
  rescue => e
@@ -155,7 +155,13 @@ module Blazer
155
155
  if postgresql? || redshift?
156
156
  execute("SET #{use_transaction? ? "LOCAL " : ""}statement_timeout = #{timeout.to_i * 1000}")
157
157
  elsif mysql?
158
- execute("SET max_execution_time = #{timeout.to_i * 1000}")
158
+ # use send as this method is private in Rails 4.2
159
+ mariadb = connection_model.connection.send(:mariadb?) rescue false
160
+ if mariadb
161
+ execute("SET max_statement_time = #{timeout.to_i * 1000}")
162
+ else
163
+ execute("SET max_execution_time = #{timeout.to_i * 1000}")
164
+ end
159
165
  else
160
166
  raise Blazer::TimeoutNotSupported, "Timeout not supported for #{adapter_name} adapter"
161
167
  end