Class: Blazer::DataSource

Inherits:
Object
  • Object
show all
Defined in:
lib/blazer/data_source.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(id, settings) ⇒ DataSource

Returns a new instance of DataSource.



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# File 'lib/blazer/data_source.rb', line 7

def initialize(id, settings)
  @id = id
  @settings = settings

  unless settings["url"] || Rails.env.development?
    raise Blazer::Error, "Empty url"
  end

  @connection_model =
    Class.new(Blazer::Connection) do
      def self.name
        "Blazer::Connection::#{object_id}"
      end
      establish_connection(settings["url"]) if settings["url"]
    end
end

Instance Attribute Details

#connection_modelObject (readonly)

Returns the value of attribute connection_model.



5
6
7
# File 'lib/blazer/data_source.rb', line 5

def connection_model
  @connection_model
end

#idObject (readonly)

Returns the value of attribute id.



5
6
7
# File 'lib/blazer/data_source.rb', line 5

def id
  @id
end

#settingsObject (readonly)

Returns the value of attribute settings.



5
6
7
# File 'lib/blazer/data_source.rb', line 5

def settings
  @settings
end

Instance Method Details

#cacheObject



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/blazer/data_source.rb', line 48

def cache
  @cache ||= begin
    if settings["cache"].is_a?(Hash)
      settings["cache"]
    elsif settings["cache"]
      {
        "mode" => "all",
        "expires_in" => settings["cache"]
      }
    else
      {
        "mode" => "off"
      }
    end
  end
end

#cache_expires_inObject



69
70
71
# File 'lib/blazer/data_source.rb', line 69

def cache_expires_in
  (cache["expires_in"] || 60).to_f
end

#cache_key(statement) ⇒ Object



172
173
174
# File 'lib/blazer/data_source.rb', line 172

def cache_key(statement)
  ["blazer", "v3", id, Digest::MD5.hexdigest(statement)].join("/")
end

#cache_modeObject



65
66
67
# File 'lib/blazer/data_source.rb', line 65

def cache_mode
  cache["mode"]
end

#cache_slow_thresholdObject



73
74
75
# File 'lib/blazer/data_source.rb', line 73

def cache_slow_threshold
  (cache["slow_threshold"] || 15).to_f
end

#clear_cache(statement) ⇒ Object



168
169
170
# File 'lib/blazer/data_source.rb', line 168

def clear_cache(statement)
  Blazer.cache.delete(cache_key(statement))
end

#cost(statement) ⇒ Object



85
86
87
88
89
# File 'lib/blazer/data_source.rb', line 85

def cost(statement)
  result = explain(statement)
  match = /cost=\d+\.\d+..(\d+\.\d+) /.match(result)
  match[1] if match
end

#explain(statement) ⇒ Object



91
92
93
94
95
96
97
# File 'lib/blazer/data_source.rb', line 91

def explain(statement)
  if postgresql? || redshift?
    connection_model.connection.select_all("EXPLAIN #{statement}").rows.first.first
  end
rescue
  nil
end

#linked_columnsObject



28
29
30
# File 'lib/blazer/data_source.rb', line 28

def linked_columns
  settings["linked_columns"] || {}
end

#local_time_suffixObject



77
78
79
# File 'lib/blazer/data_source.rb', line 77

def local_time_suffix
  @local_time_suffix ||= Array(settings["local_time_suffix"])
end

#mysql?Boolean

Returns:

  • (Boolean)


194
195
196
# File 'lib/blazer/data_source.rb', line 194

def mysql?
  ["MySQL", "Mysql2", "Mysql2Spatial"].include?(adapter_name)
end

#nameObject



24
25
26
# File 'lib/blazer/data_source.rb', line 24

def name
  settings["name"] || @id
end

#postgresql?Boolean

Returns:

  • (Boolean)


186
187
188
# File 'lib/blazer/data_source.rb', line 186

def postgresql?
  ["PostgreSQL", "PostGIS"].include?(adapter_name)
end

#reconnectObject



198
199
200
# File 'lib/blazer/data_source.rb', line 198

def reconnect
  connection_model.establish_connection(settings["url"])
end

#redshift?Boolean

Returns:

  • (Boolean)


190
191
192
# File 'lib/blazer/data_source.rb', line 190

def redshift?
  ["Redshift"].include?(adapter_name)
end

#run_main_statement(statement, options = {}) ⇒ Object



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
# File 'lib/blazer/data_source.rb', line 99

def run_main_statement(statement, options = {})
  query = options[:query]
  Blazer.transform_statement.call(self, statement) if Blazer.transform_statement

  # audit
  if Blazer.audit
    audit = Blazer::Audit.new(statement: statement)
    audit.query = query
    audit.data_source = id
    audit.user = options[:user]
    audit.save!
  end

  start_time = Time.now
  columns, rows, error, cached_at, just_cached = run_statement(statement, options.merge(with_just_cached: true))
  duration = Time.now - start_time

  if Blazer.audit
    audit.duration = duration if audit.respond_to?(:duration=)
    audit.error = error if audit.respond_to?(:error=)
    audit.timed_out = error == Blazer::TIMEOUT_MESSAGE if audit.respond_to?(:timed_out=)
    audit.cached = cached_at.present? if audit.respond_to?(:cached=)
    if !cached_at && duration >= 10
      audit.cost = cost(statement) if audit.respond_to?(:cost=)
    end
    audit.save! if audit.changed?
  end

  if query && error != Blazer::TIMEOUT_MESSAGE
    query.checks.each do |check|
      check.update_state(rows, error)
    end
  end

  [columns, rows, error, cached_at, just_cached]
end

#run_statement(statement, options = {}) ⇒ Object



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
# File 'lib/blazer/data_source.rb', line 136

def run_statement(statement, options = {})
  columns = nil
  rows = nil
  error = nil
  cached_at = nil
  just_cached = false
  cache_key = self.cache_key(statement) if cache
  if cache && !options[:refresh_cache]
    value = Blazer.cache.read(cache_key)
    columns, rows, cached_at = Marshal.load(value) if value
  end

  unless rows
    comment = "blazer"
    if options[:user].respond_to?(:id)
      comment << ",user_id:#{options[:user].id}"
    end
    if options[:user].respond_to?(Blazer.user_name)
      # only include letters, numbers, and spaces to prevent injection
      comment << ",user_name:#{options[:user].send(Blazer.user_name).to_s.gsub(/[^a-zA-Z0-9 ]/, "")}"
    end
    if options[:query].respond_to?(:id)
      comment << ",query_id:#{options[:query].id}"
    end
    columns, rows, error, just_cached = run_statement_helper(statement, comment)
  end

  output = [columns, rows, error, cached_at]
  output << just_cached if options[:with_just_cached]
  output
end

#schemasObject



176
177
178
179
# File 'lib/blazer/data_source.rb', line 176

def schemas
  default_schema = (postgresql? || redshift?) ? "public" : connection_model.connection_config[:database]
  settings["schemas"] || [connection_model.connection_config[:schema] || default_schema]
end

#smart_columnsObject



32
33
34
# File 'lib/blazer/data_source.rb', line 32

def smart_columns
  settings["smart_columns"] || {}
end

#smart_variablesObject



36
37
38
# File 'lib/blazer/data_source.rb', line 36

def smart_variables
  settings["smart_variables"] || {}
end

#tablesObject



181
182
183
184
# File 'lib/blazer/data_source.rb', line 181

def tables
  columns, rows, error, cached_at = run_statement(connection_model.send(:sanitize_sql_array, ["SELECT table_name FROM information_schema.tables WHERE table_schema IN (?) ORDER BY table_name", schemas]))
  rows.map(&:first)
end

#timeoutObject



44
45
46
# File 'lib/blazer/data_source.rb', line 44

def timeout
  settings["timeout"]
end

#use_transaction?Boolean

Returns:

  • (Boolean)


81
82
83
# File 'lib/blazer/data_source.rb', line 81

def use_transaction?
  settings.key?("use_transaction") ? settings["use_transaction"] : true
end

#variable_defaultsObject



40
41
42
# File 'lib/blazer/data_source.rb', line 40

def variable_defaults
  settings["variable_defaults"] || {}
end