database_recorder 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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