database_recorder 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bd8b63b3fa1b57dd6c2c5cdf501af590f75a5fcb52c322bc4eee702c55026db6
4
- data.tar.gz: f2753254f7c7d79ef0fd7dd65e3439197419ae01d96e4625eb3a9f4632835bd9
3
+ metadata.gz: 5c43f3f7036d73a7133d4a79984dc7035ec2ccfcaccbf23b3e2f36053debc0f5
4
+ data.tar.gz: 0d8bc0cdf33d2626811f95bfac45b6dfce0383455a39b58940350048c9d92a70
5
5
  SHA512:
6
- metadata.gz: 343167b893fd491b57405ad465e1b835fcf077d3cc806a4c3a4e6ca7c1253e0967acca90760c30e395de30aa8529421305910f9714879f118892259acf03dbe7
7
- data.tar.gz: a2f8defdbd1e9f251afd6c999077870689c20f9c7868cc5d01a2947ab63c3ee1eec2c5401b14eef1378e450dbeb2fb170f93d011e54f8c8e677c150394f0b024
6
+ metadata.gz: 611e9419ea6c8f1e536c2b0ba5ad19b46327842e15032168c14db5ac8d109cca14a15bf000329538b894851bb51e6ceaf22fc5970356f31f2f3abd182cced339
7
+ data.tar.gz: 25eba80806a035ca9da8063a1d745e218b3a0851b008fb0b31b3036680c82758210fa596d3386d31daed0bbee72041242150bcadd37337a828918ee511c0fdb1
@@ -4,13 +4,13 @@ module DatabaseRecorder
4
4
  module Mysql2
5
5
  module ClientExt
6
6
  def query(sql, options = {})
7
- Recorder.record(self, sql: sql) do
7
+ Recorder.store_query(self, sql: sql, source: :query) do
8
8
  super
9
9
  end
10
10
  end
11
11
 
12
12
  def prepare(*args)
13
- Recorder.record(self, sql: args[0]) do
13
+ Recorder.prepare_statement(self, sql: args[0], source: :prepare) do
14
14
  super
15
15
  end
16
16
  end
@@ -12,29 +12,16 @@ module DatabaseRecorder
12
12
  sql.match?(/information_schema.statistics/)
13
13
  end
14
14
 
15
- def record(adapter, sql:)
16
- return yield if ignore_query?(sql)
17
-
18
- Core.log_query(sql)
19
- if Config.replay_recordings && !Recording.cache.nil?
20
- Recording.push(sql: sql)
21
- data = Recording.cached_query_for(sql)
22
- return yield unless data # cache miss
15
+ def format_result(result)
16
+ { 'count' => result.count, 'fields' => result.fields, 'values' => result.to_a } if result.is_a?(::Mysql2::Result)
17
+ # else
18
+ # last_insert_id = adapter.query('SELECT LAST_INSERT_ID() AS _dbr_last_insert_id').to_a
19
+ # { 'count' => last_insert_id.count, 'fields' => ['id'], 'values' => last_insert_id }
20
+ end
23
21
 
24
- RecordedResult.new.prepare(data['result'].slice('count', 'fields', 'values')) if data['result']
25
- else
26
- yield.tap do |result|
27
- result_data =
28
- if result.is_a? ::Mysql2::Result
29
- { 'count' => result.count, 'fields' => result.fields, 'values' => result.to_a }
30
- # else
31
- # last_insert_id = adapter.query('SELECT LAST_INSERT_ID() AS _dbr_last_insert_id').to_a
32
- # { 'count' => last_insert_id.count, 'fields' => ['id'], 'values' => last_insert_id }
33
- end
34
-
35
- Recording.push(sql: sql, result: result_data)
36
- end
37
- end
22
+ def prepare_statement(adapter, sql: nil, name: nil, binds: nil, source: nil)
23
+ @last_prepared = Recording.push_prepared(name: name, sql: sql, binds: binds, source: source)
24
+ yield if !Config.replay_recordings || Recording.cache.nil?
38
25
  end
39
26
 
40
27
  def setup
@@ -47,9 +34,38 @@ module DatabaseRecorder
47
34
  end
48
35
  end
49
36
 
50
- def update_record(adapter, *args)
51
- Recording.update_last(*args)
52
- yield
37
+ def store_prepared_statement(adapter, source:, binds:)
38
+ # sql = @last_prepared&.send(:[], 'sql')
39
+ sql = @last_prepared['sql']
40
+ Core.log_query(sql, source)
41
+ if Config.replay_recordings && !Recording.cache.nil?
42
+ data = Recording.cache.find { |query| query['sql'] == sql }
43
+ return yield unless data # cache miss
44
+
45
+ Recording.push(sql: data['sql'], binds: data['binds'], source: source)
46
+ RecordedResult.new(data['result'].slice('count', 'fields', 'values'))
47
+ else
48
+ yield.tap do |result|
49
+ Recording.update_prepared(sql: sql, binds: binds, result: format_result(result), source: source)
50
+ end
51
+ end
52
+ end
53
+
54
+ def store_query(adapter, sql:, source:)
55
+ return yield if ignore_query?(sql)
56
+
57
+ Core.log_query(sql, source)
58
+ if Config.replay_recordings && !Recording.cache.nil?
59
+ Recording.push(sql: sql, source: source)
60
+ data = Recording.cached_query_for(sql)
61
+ return yield unless data # cache miss
62
+
63
+ RecordedResult.new.prepare(data['result'].slice('count', 'fields', 'values')) if data['result']
64
+ else
65
+ yield.tap do |result|
66
+ Recording.push(sql: sql, result: format_result(result), source: source)
67
+ end
68
+ end
53
69
  end
54
70
  end
55
71
  end
@@ -4,7 +4,7 @@ module DatabaseRecorder
4
4
  module Mysql2
5
5
  module StatementExt
6
6
  def execute(*args, **kwargs)
7
- Recorder.update_record(self, *args) do
7
+ Recorder.store_prepared_statement(self, source: :execute, binds: args) do
8
8
  super
9
9
  end
10
10
  end
@@ -4,47 +4,43 @@ module DatabaseRecorder
4
4
  module PG
5
5
  module ConnectionExt
6
6
  def async_exec(sql)
7
- Recorder.record(sql: sql, source: :async_exec) do
7
+ Recorder.store_query(sql: sql, source: :async_exec) do
8
8
  super
9
9
  end
10
10
  end
11
11
 
12
12
  def sync_exec(sql)
13
- Recorder.record(sql: sql, source: :sync_exec) do
13
+ Recorder.store_query(sql: sql, source: :sync_exec) do
14
14
  super
15
15
  end
16
16
  end
17
17
 
18
18
  def exec(*args)
19
- Recorder.record(sql: args[0], source: :exec) do
19
+ Recorder.store_query(sql: args[0], source: :exec) do
20
20
  super
21
21
  end
22
22
  end
23
23
 
24
- def query(*args)
25
- Recorder.record(sql: args[0], source: :query) do
24
+ def exec_params(*args)
25
+ Recorder.store_query(sql: args[0], binds: args[1], source: :exec_params) do
26
26
  super
27
27
  end
28
28
  end
29
29
 
30
- def exec_params(*args)
31
- Recorder.record(sql: args[0], binds: args[1], source: :exec_params) do
30
+ def exec_prepared(*args)
31
+ Recorder.store_prepared_statement(name: args[0], binds: args[1], source: :exec_prepared) do
32
32
  super
33
33
  end
34
34
  end
35
35
 
36
- # def async_exec_params(*args)
37
- # puts ">>> #{args[0]}"
38
- # super
39
- # end
40
-
41
- # def sync_exec_params(*args)
42
- # puts ">>> #{args[0]}"
43
- # super
44
- # end
36
+ def prepare(*args)
37
+ Recorder.prepare_statement(name: args[0], sql: args[1], source: :prepare) do
38
+ super
39
+ end
40
+ end
45
41
 
46
- def exec_prepared(*args)
47
- Recorder.record(sql: args[0], binds: args[1], source: :exec_prepared) do
42
+ def query(*args)
43
+ Recorder.store_query(sql: args[0], source: :query) do
48
44
  super
49
45
  end
50
46
  end
@@ -11,29 +11,56 @@ module DatabaseRecorder
11
11
  sql.match?(/ pg_attribute |SHOW max_identifier_length|SHOW search_path/)
12
12
  end
13
13
 
14
- def record(sql:, binds: nil, source: nil)
14
+ def format_result(result)
15
+ { 'count' => result.count, 'fields' => result.fields, 'values' => result.values } if result
16
+ end
17
+
18
+ def prepare_statement(sql: nil, name: nil, binds: nil, source: nil)
19
+ Recording.push_prepared(name: name, sql: sql, binds: binds, source: source)
20
+ yield if !Config.replay_recordings || Recording.cache.nil?
21
+ end
22
+
23
+ def setup
24
+ ::PG::Connection.class_eval do
25
+ prepend ConnectionExt
26
+ end
27
+ end
28
+
29
+ def store_prepared_statement(name: nil, sql: nil, binds: nil, source: nil)
30
+ if Config.replay_recordings && !Recording.cache.nil?
31
+ data = Recording.cache.find { |query| query['name'] == name }
32
+ return yield unless data # cache miss
33
+
34
+ Core.log_query(data['sql'], source)
35
+ Recording.push(sql: data['sql'], binds: data['binds'], source: source)
36
+ RecordedResult.new(data['result'].slice('count', 'fields', 'values'))
37
+ else
38
+ Core.log_query(sql, source)
39
+ yield.tap do |query_result|
40
+ result = format_result(query_result)
41
+ query = Recording.update_prepared(name: name, sql: sql, binds: binds, result: result, source: source)
42
+ Core.log_query(query['sql'], source)
43
+ end
44
+ end
45
+ end
46
+
47
+ def store_query(name: nil, sql: nil, binds: nil, source: nil)
15
48
  return yield if ignore_query?(sql)
16
49
 
17
50
  Core.log_query(sql, source)
51
+ @prepared_statement = nil
18
52
  if Config.replay_recordings && !Recording.cache.nil?
19
- Recording.push(sql: sql, binds: binds)
53
+ Recording.push(sql: sql, binds: binds, source: source)
20
54
  data = Recording.cached_query_for(sql)
21
55
  return yield unless data # cache miss
22
56
 
23
57
  RecordedResult.new(data['result'].slice('count', 'fields', 'values'))
24
58
  else
25
59
  yield.tap do |result|
26
- result_data = result ? { 'count' => result.count, 'fields' => result.fields, 'values' => result.values } : nil
27
- Recording.push(sql: sql, binds: binds, result: result_data)
60
+ Recording.push(name: name, sql: sql, binds: binds, result: format_result(result), source: source)
28
61
  end
29
62
  end
30
63
  end
31
-
32
- def setup
33
- ::PG::Connection.class_eval do
34
- prepend ConnectionExt
35
- end
36
- end
37
64
  end
38
65
  end
39
66
  end
@@ -5,7 +5,7 @@ require 'forwardable'
5
5
  module DatabaseRecorder
6
6
  class Recording
7
7
  attr_accessor :cache, :entities
8
- attr_reader :from_cache, :options, :queries, :started
8
+ attr_reader :from_cache, :options, :prepared_queries, :queries, :started
9
9
 
10
10
  def initialize(options: {})
11
11
  (@@instances ||= {})[Process.pid] = self
@@ -14,6 +14,7 @@ module DatabaseRecorder
14
14
  @options = options
15
15
  @queries = []
16
16
  @search_index = 0
17
+ @@prepared_queries ||= {}
17
18
  end
18
19
 
19
20
  def cached_query_for(sql)
@@ -38,11 +39,16 @@ module DatabaseRecorder
38
39
  @entities.shift
39
40
  end
40
41
 
41
- def push(sql:, binds: nil, result: nil, name: nil)
42
+ def push(sql:, name: nil, binds: nil, result: nil, source: nil)
42
43
  query = { 'name' => name, 'sql' => sql, 'binds' => binds, 'result' => result }.compact
43
44
  @queries.push(query)
44
45
  end
45
46
 
47
+ def push_prepared(name: nil, sql: nil, binds: nil, result: nil, source: nil)
48
+ query = { 'name' => name, 'sql' => sql, 'binds' => binds, 'result' => result }.compact
49
+ @@prepared_queries[name || sql] = query
50
+ end
51
+
46
52
  def start
47
53
  @started = true
48
54
  storage = Config.storage&.new(self, name: options[:name])
@@ -55,15 +61,20 @@ module DatabaseRecorder
55
61
  result
56
62
  end
57
63
 
58
- def update_last(*args)
59
- @queries.last['binds'] = args
64
+ def update_prepared(name: nil, sql: nil, binds: nil, result: nil, source: nil)
65
+ query = @@prepared_queries[name || sql]
66
+ query['sql'] = sql if sql
67
+ query['binds'] = binds if binds
68
+ query['result'] = result if result
69
+ @queries.push(query)
70
+ query
60
71
  end
61
72
 
62
73
  class << self
63
74
  extend Forwardable
64
75
 
65
- def_delegators :current_instance, :cache, :cached_query_for, :from_cache, :new_entity, :pull_entity, :push,
66
- :queries, :update_last
76
+ def_delegators :current_instance, :cache, :cached_query_for, :from_cache, :new_entity, :prepared_queries,
77
+ :pull_entity, :push, :push_prepared, :queries, :update_prepared
67
78
 
68
79
  def current_instance
69
80
  (@@instances ||= {})[Process.pid]
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DatabaseRecorder
4
- VERSION = '0.1.1'
4
+ VERSION = '0.2.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: database_recorder
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mattia Roccoberton
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-04-11 00:00:00.000000000 Z
11
+ date: 2022-04-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: coderay