rack-dev_insight 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/rack/dev_insight/errors.rb +0 -5
- data/lib/rack/dev_insight/extractor.rb +23 -0
- data/lib/rack/dev_insight/{ext/normalizer.rb → normalizer.rb} +1 -1
- data/lib/rack/dev_insight/result/sql/crud_aggregations.rb +2 -2
- data/lib/rack/dev_insight/result/sql.rb +1 -1
- data/lib/rack/dev_insight/version.rb +1 -1
- data/lib/rack/dev_insight.rb +3 -11
- metadata +12 -20
- data/Cargo.lock +0 -415
- data/Cargo.toml +0 -7
- data/ext/rack_dev_insight/Cargo.toml +0 -20
- data/ext/rack_dev_insight/extconf.rb +0 -6
- data/ext/rack_dev_insight/src/errors.rs +0 -32
- data/ext/rack_dev_insight/src/extractor.rs +0 -354
- data/ext/rack_dev_insight/src/lib.rs +0 -31
- data/ext/rack_dev_insight/src/normalizer.rs +0 -97
- data/lib/rack/dev_insight/ext/extractor.rb +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cbb5f9291376068796d23a7453b4a4c892a4f87744e3dd60ffefa3cb6df33ab9
|
4
|
+
data.tar.gz: 162b2adc0edb597c1154b2592aef379411d97eeaec3b1d9bb8b42f50a3c5af22
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dffa38c4ee3037673ab6ef6e086c77f0d44122146144034a9387a2162454c49e2d41e50412deac611d1730d4772271bb099a127690770e3f230c10b0360e0140
|
7
|
+
data.tar.gz: dabfbb00c1b734caa6b91e38df5c478183376a3c3200012b0f317c69e6d2dd793156d3f39048efd9c9f9c21fa603d0a50aebd27ccb1804118e60665e032bfb81
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
class DevInsight
|
5
|
+
module Extractor
|
6
|
+
class << self
|
7
|
+
def extract_crud_tables(dialect_name, statement)
|
8
|
+
crud_tables = SqlInsight.extract_crud_tables(dialect_name, statement)
|
9
|
+
results = { 'CREATE' => [], 'READ' => [], 'UPDATE' => [], 'DELETE' => [] }
|
10
|
+
|
11
|
+
crud_tables.each do |crud_table|
|
12
|
+
results['CREATE'].concat(crud_table.create_tables.map(&:name).map(&:value))
|
13
|
+
results['READ'].concat(crud_table.read_tables.map(&:name).map(&:value))
|
14
|
+
results['UPDATE'].concat(crud_table.update_tables.map(&:name).map(&:value))
|
15
|
+
results['DELETE'].concat(crud_table.delete_tables.map(&:name).map(&:value))
|
16
|
+
end
|
17
|
+
|
18
|
+
results
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -13,11 +13,11 @@ module Rack
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def add(dialect_name, statement, duration, query_id)
|
16
|
-
crud_tables = Extractor
|
16
|
+
crud_tables = Extractor.extract_crud_tables(dialect_name, statement)
|
17
17
|
|
18
18
|
crud_tables.each do |type, tables|
|
19
19
|
tables.each do |table|
|
20
|
-
key = "#{type}_#{table
|
20
|
+
key = "#{type}_#{table}"
|
21
21
|
data = @cached_data[key] ||= CrudAggregation.new(@id += 1, type, table, 0, 0, [])
|
22
22
|
data.count += 1
|
23
23
|
data.duration += duration
|
@@ -15,7 +15,7 @@ module Rack
|
|
15
15
|
@queries.add(statement, binds, backtrace, duration)
|
16
16
|
@crud_aggregations.add(dialect, statement, duration, @queries.id)
|
17
17
|
@normalized_aggregations.add(dialect, statement, duration, @queries.id)
|
18
|
-
rescue
|
18
|
+
rescue SqlInsight::Error => e
|
19
19
|
@errored_queries.add(@queries.id, e.message, statement, backtrace, duration)
|
20
20
|
end
|
21
21
|
|
data/lib/rack/dev_insight.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'securerandom'
|
4
|
-
|
5
|
-
require_relative 'dev_insight/ext/normalizer'
|
4
|
+
require 'sql_insight'
|
6
5
|
require_relative 'dev_insight/recorder/api_recorder'
|
7
6
|
require_relative 'dev_insight/recorder/request_recorder'
|
8
7
|
require_relative 'dev_insight/recorder/sql_recorder'
|
@@ -19,17 +18,10 @@ require_relative 'dev_insight/utils/camelizer'
|
|
19
18
|
require_relative 'dev_insight/config'
|
20
19
|
require_relative 'dev_insight/context'
|
21
20
|
require_relative 'dev_insight/errors'
|
21
|
+
require_relative 'dev_insight/extractor'
|
22
|
+
require_relative 'dev_insight/normalizer'
|
22
23
|
require_relative 'dev_insight/sql_dialects'
|
23
24
|
require_relative 'dev_insight/version'
|
24
|
-
|
25
|
-
# https://github.com/rake-compiler/rake-compiler/blob/master/README.md
|
26
|
-
# Technique to lookup the fat binaries first, and then lookup the gems compiled by the end user.
|
27
|
-
begin
|
28
|
-
RUBY_VERSION =~ /(\d+\.\d+)/
|
29
|
-
require_relative "dev_insight/#{Regexp.last_match(1)}/rack_dev_insight"
|
30
|
-
rescue LoadError
|
31
|
-
require_relative 'dev_insight/rack_dev_insight'
|
32
|
-
end
|
33
25
|
# Railtie
|
34
26
|
require_relative 'dev_insight/railtie' if defined?(Rails)
|
35
27
|
# Patches
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-dev_insight
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Takahiro Ebato
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-03-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -25,44 +25,35 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: sql_insight
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.
|
33
|
+
version: 0.1.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 0.
|
41
|
-
description:
|
42
|
-
|
40
|
+
version: 0.1.0
|
41
|
+
description: An observability tool for Rack applications, analyzing SQL and HTTP with
|
42
|
+
a Chrome extension as a dashboard
|
43
43
|
email:
|
44
44
|
- takahiro.ebato@gmail.com
|
45
45
|
executables: []
|
46
|
-
extensions:
|
47
|
-
- ext/rack_dev_insight/extconf.rb
|
46
|
+
extensions: []
|
48
47
|
extra_rdoc_files: []
|
49
48
|
files:
|
50
|
-
- Cargo.lock
|
51
|
-
- Cargo.toml
|
52
|
-
- ext/rack_dev_insight/Cargo.toml
|
53
|
-
- ext/rack_dev_insight/extconf.rb
|
54
|
-
- ext/rack_dev_insight/src/errors.rs
|
55
|
-
- ext/rack_dev_insight/src/extractor.rs
|
56
|
-
- ext/rack_dev_insight/src/lib.rs
|
57
|
-
- ext/rack_dev_insight/src/normalizer.rs
|
58
49
|
- lib/rack/dev_insight.rb
|
59
50
|
- lib/rack/dev_insight/config.rb
|
60
51
|
- lib/rack/dev_insight/context.rb
|
61
52
|
- lib/rack/dev_insight/disable_net_http_patch.rb
|
62
53
|
- lib/rack/dev_insight/enable_sql_patch.rb
|
63
54
|
- lib/rack/dev_insight/errors.rb
|
64
|
-
- lib/rack/dev_insight/
|
65
|
-
- lib/rack/dev_insight/
|
55
|
+
- lib/rack/dev_insight/extractor.rb
|
56
|
+
- lib/rack/dev_insight/normalizer.rb
|
66
57
|
- lib/rack/dev_insight/patches/api/net_http.rb
|
67
58
|
- lib/rack/dev_insight/patches/sql/mysql2.rb
|
68
59
|
- lib/rack/dev_insight/patches/sql/pg.rb
|
@@ -109,5 +100,6 @@ requirements: []
|
|
109
100
|
rubygems_version: 3.5.3
|
110
101
|
signing_key:
|
111
102
|
specification_version: 4
|
112
|
-
summary:
|
103
|
+
summary: An observability tool for Rack apps, analyzing SQL and HTTP with a Chrome
|
104
|
+
extension as a dashboard
|
113
105
|
test_files: []
|
data/Cargo.lock
DELETED
@@ -1,415 +0,0 @@
|
|
1
|
-
# This file is automatically @generated by Cargo.
|
2
|
-
# It is not intended for manual editing.
|
3
|
-
version = 3
|
4
|
-
|
5
|
-
[[package]]
|
6
|
-
name = "aho-corasick"
|
7
|
-
version = "1.1.2"
|
8
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
9
|
-
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
10
|
-
dependencies = [
|
11
|
-
"memchr",
|
12
|
-
]
|
13
|
-
|
14
|
-
[[package]]
|
15
|
-
name = "bindgen"
|
16
|
-
version = "0.69.1"
|
17
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
18
|
-
checksum = "9ffcebc3849946a7170a05992aac39da343a90676ab392c51a4280981d6379c2"
|
19
|
-
dependencies = [
|
20
|
-
"bitflags",
|
21
|
-
"cexpr",
|
22
|
-
"clang-sys",
|
23
|
-
"lazy_static",
|
24
|
-
"lazycell",
|
25
|
-
"peeking_take_while",
|
26
|
-
"proc-macro2",
|
27
|
-
"quote",
|
28
|
-
"regex",
|
29
|
-
"rustc-hash",
|
30
|
-
"shlex",
|
31
|
-
"syn 2.0.45",
|
32
|
-
]
|
33
|
-
|
34
|
-
[[package]]
|
35
|
-
name = "bitflags"
|
36
|
-
version = "2.4.1"
|
37
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
38
|
-
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
|
39
|
-
|
40
|
-
[[package]]
|
41
|
-
name = "cexpr"
|
42
|
-
version = "0.6.0"
|
43
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
44
|
-
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
|
45
|
-
dependencies = [
|
46
|
-
"nom",
|
47
|
-
]
|
48
|
-
|
49
|
-
[[package]]
|
50
|
-
name = "cfg-if"
|
51
|
-
version = "1.0.0"
|
52
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
53
|
-
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
54
|
-
|
55
|
-
[[package]]
|
56
|
-
name = "clang-sys"
|
57
|
-
version = "1.7.0"
|
58
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
59
|
-
checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1"
|
60
|
-
dependencies = [
|
61
|
-
"glob",
|
62
|
-
"libc",
|
63
|
-
"libloading",
|
64
|
-
]
|
65
|
-
|
66
|
-
[[package]]
|
67
|
-
name = "glob"
|
68
|
-
version = "0.3.1"
|
69
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
70
|
-
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
71
|
-
|
72
|
-
[[package]]
|
73
|
-
name = "indoc"
|
74
|
-
version = "2.0.4"
|
75
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
76
|
-
checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8"
|
77
|
-
|
78
|
-
[[package]]
|
79
|
-
name = "lazy_static"
|
80
|
-
version = "1.4.0"
|
81
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
82
|
-
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
83
|
-
|
84
|
-
[[package]]
|
85
|
-
name = "lazycell"
|
86
|
-
version = "1.3.0"
|
87
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
88
|
-
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
89
|
-
|
90
|
-
[[package]]
|
91
|
-
name = "libc"
|
92
|
-
version = "0.2.151"
|
93
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
94
|
-
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
|
95
|
-
|
96
|
-
[[package]]
|
97
|
-
name = "libloading"
|
98
|
-
version = "0.8.1"
|
99
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
100
|
-
checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161"
|
101
|
-
dependencies = [
|
102
|
-
"cfg-if",
|
103
|
-
"windows-sys",
|
104
|
-
]
|
105
|
-
|
106
|
-
[[package]]
|
107
|
-
name = "log"
|
108
|
-
version = "0.4.20"
|
109
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
110
|
-
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
111
|
-
|
112
|
-
[[package]]
|
113
|
-
name = "magnus"
|
114
|
-
version = "0.6.2"
|
115
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
116
|
-
checksum = "4778544796676e8428e9c622460ebf284bea52d8b10db3aeb449d8b5e61b3a13"
|
117
|
-
dependencies = [
|
118
|
-
"magnus-macros",
|
119
|
-
"rb-sys",
|
120
|
-
"rb-sys-env",
|
121
|
-
"seq-macro",
|
122
|
-
]
|
123
|
-
|
124
|
-
[[package]]
|
125
|
-
name = "magnus-macros"
|
126
|
-
version = "0.6.0"
|
127
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
128
|
-
checksum = "5968c820e2960565f647819f5928a42d6e874551cab9d88d75e3e0660d7f71e3"
|
129
|
-
dependencies = [
|
130
|
-
"proc-macro2",
|
131
|
-
"quote",
|
132
|
-
"syn 2.0.45",
|
133
|
-
]
|
134
|
-
|
135
|
-
[[package]]
|
136
|
-
name = "memchr"
|
137
|
-
version = "2.7.1"
|
138
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
139
|
-
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
140
|
-
|
141
|
-
[[package]]
|
142
|
-
name = "minimal-lexical"
|
143
|
-
version = "0.2.1"
|
144
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
145
|
-
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
146
|
-
|
147
|
-
[[package]]
|
148
|
-
name = "nom"
|
149
|
-
version = "7.1.3"
|
150
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
151
|
-
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
152
|
-
dependencies = [
|
153
|
-
"memchr",
|
154
|
-
"minimal-lexical",
|
155
|
-
]
|
156
|
-
|
157
|
-
[[package]]
|
158
|
-
name = "peeking_take_while"
|
159
|
-
version = "0.1.2"
|
160
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
161
|
-
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
162
|
-
|
163
|
-
[[package]]
|
164
|
-
name = "proc-macro2"
|
165
|
-
version = "1.0.73"
|
166
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
167
|
-
checksum = "2dd5e8a1f1029c43224ad5898e50140c2aebb1705f19e67c918ebf5b9e797fe1"
|
168
|
-
dependencies = [
|
169
|
-
"unicode-ident",
|
170
|
-
]
|
171
|
-
|
172
|
-
[[package]]
|
173
|
-
name = "quote"
|
174
|
-
version = "1.0.34"
|
175
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
176
|
-
checksum = "22a37c9326af5ed140c86a46655b5278de879853be5573c01df185b6f49a580a"
|
177
|
-
dependencies = [
|
178
|
-
"proc-macro2",
|
179
|
-
]
|
180
|
-
|
181
|
-
[[package]]
|
182
|
-
name = "rack_dev_insight"
|
183
|
-
version = "0.1.0"
|
184
|
-
dependencies = [
|
185
|
-
"indoc",
|
186
|
-
"magnus",
|
187
|
-
"rb-sys-test-helpers",
|
188
|
-
"sqlparser",
|
189
|
-
"tap",
|
190
|
-
]
|
191
|
-
|
192
|
-
[[package]]
|
193
|
-
name = "rb-sys"
|
194
|
-
version = "0.9.85"
|
195
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
196
|
-
checksum = "05b780e6858b0b0eced1d55d0f097c024b77a37b41f83bd35341130f78e37c51"
|
197
|
-
dependencies = [
|
198
|
-
"rb-sys-build",
|
199
|
-
]
|
200
|
-
|
201
|
-
[[package]]
|
202
|
-
name = "rb-sys-build"
|
203
|
-
version = "0.9.85"
|
204
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
205
|
-
checksum = "44957a3bc513dad1b0f20bdd0ee3b82e729a59da44086a6b40d8bc71958a6db8"
|
206
|
-
dependencies = [
|
207
|
-
"bindgen",
|
208
|
-
"lazy_static",
|
209
|
-
"proc-macro2",
|
210
|
-
"quote",
|
211
|
-
"regex",
|
212
|
-
"shell-words",
|
213
|
-
"syn 2.0.45",
|
214
|
-
]
|
215
|
-
|
216
|
-
[[package]]
|
217
|
-
name = "rb-sys-env"
|
218
|
-
version = "0.1.2"
|
219
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
220
|
-
checksum = "a35802679f07360454b418a5d1735c89716bde01d35b1560fc953c1415a0b3bb"
|
221
|
-
|
222
|
-
[[package]]
|
223
|
-
name = "rb-sys-test-helpers"
|
224
|
-
version = "0.2.0"
|
225
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
226
|
-
checksum = "bbd592029353df7955a76ca0b1bf0d9ccc57feab96615f80d1fe756462864949"
|
227
|
-
dependencies = [
|
228
|
-
"rb-sys",
|
229
|
-
"rb-sys-env",
|
230
|
-
"rb-sys-test-helpers-macros",
|
231
|
-
]
|
232
|
-
|
233
|
-
[[package]]
|
234
|
-
name = "rb-sys-test-helpers-macros"
|
235
|
-
version = "0.2.0"
|
236
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
237
|
-
checksum = "c21f156459adb755d58f73dbd783dc1de8b403635e637f7d1daec1c7a920c1f5"
|
238
|
-
dependencies = [
|
239
|
-
"quote",
|
240
|
-
"syn 2.0.45",
|
241
|
-
]
|
242
|
-
|
243
|
-
[[package]]
|
244
|
-
name = "regex"
|
245
|
-
version = "1.10.2"
|
246
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
247
|
-
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
|
248
|
-
dependencies = [
|
249
|
-
"aho-corasick",
|
250
|
-
"memchr",
|
251
|
-
"regex-automata",
|
252
|
-
"regex-syntax",
|
253
|
-
]
|
254
|
-
|
255
|
-
[[package]]
|
256
|
-
name = "regex-automata"
|
257
|
-
version = "0.4.3"
|
258
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
259
|
-
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
|
260
|
-
dependencies = [
|
261
|
-
"aho-corasick",
|
262
|
-
"memchr",
|
263
|
-
"regex-syntax",
|
264
|
-
]
|
265
|
-
|
266
|
-
[[package]]
|
267
|
-
name = "regex-syntax"
|
268
|
-
version = "0.8.2"
|
269
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
270
|
-
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
271
|
-
|
272
|
-
[[package]]
|
273
|
-
name = "rustc-hash"
|
274
|
-
version = "1.1.0"
|
275
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
276
|
-
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
277
|
-
|
278
|
-
[[package]]
|
279
|
-
name = "seq-macro"
|
280
|
-
version = "0.3.5"
|
281
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
282
|
-
checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4"
|
283
|
-
|
284
|
-
[[package]]
|
285
|
-
name = "shell-words"
|
286
|
-
version = "1.1.0"
|
287
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
288
|
-
checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
|
289
|
-
|
290
|
-
[[package]]
|
291
|
-
name = "shlex"
|
292
|
-
version = "1.2.0"
|
293
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
294
|
-
checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380"
|
295
|
-
|
296
|
-
[[package]]
|
297
|
-
name = "sqlparser"
|
298
|
-
version = "0.41.0"
|
299
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
300
|
-
checksum = "5cc2c25a6c66789625ef164b4c7d2e548d627902280c13710d33da8222169964"
|
301
|
-
dependencies = [
|
302
|
-
"log",
|
303
|
-
"sqlparser_derive",
|
304
|
-
]
|
305
|
-
|
306
|
-
[[package]]
|
307
|
-
name = "sqlparser_derive"
|
308
|
-
version = "0.2.1"
|
309
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
310
|
-
checksum = "3e9c2e1dde0efa87003e7923d94a90f46e3274ad1649f51de96812be561f041f"
|
311
|
-
dependencies = [
|
312
|
-
"proc-macro2",
|
313
|
-
"quote",
|
314
|
-
"syn 1.0.109",
|
315
|
-
]
|
316
|
-
|
317
|
-
[[package]]
|
318
|
-
name = "syn"
|
319
|
-
version = "1.0.109"
|
320
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
321
|
-
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
322
|
-
dependencies = [
|
323
|
-
"proc-macro2",
|
324
|
-
"quote",
|
325
|
-
"unicode-ident",
|
326
|
-
]
|
327
|
-
|
328
|
-
[[package]]
|
329
|
-
name = "syn"
|
330
|
-
version = "2.0.45"
|
331
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
332
|
-
checksum = "0eae3c679c56dc214320b67a1bc04ef3dfbd6411f6443974b5e4893231298e66"
|
333
|
-
dependencies = [
|
334
|
-
"proc-macro2",
|
335
|
-
"quote",
|
336
|
-
"unicode-ident",
|
337
|
-
]
|
338
|
-
|
339
|
-
[[package]]
|
340
|
-
name = "tap"
|
341
|
-
version = "1.0.1"
|
342
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
343
|
-
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
344
|
-
|
345
|
-
[[package]]
|
346
|
-
name = "unicode-ident"
|
347
|
-
version = "1.0.12"
|
348
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
349
|
-
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
350
|
-
|
351
|
-
[[package]]
|
352
|
-
name = "windows-sys"
|
353
|
-
version = "0.48.0"
|
354
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
355
|
-
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
356
|
-
dependencies = [
|
357
|
-
"windows-targets",
|
358
|
-
]
|
359
|
-
|
360
|
-
[[package]]
|
361
|
-
name = "windows-targets"
|
362
|
-
version = "0.48.5"
|
363
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
364
|
-
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
365
|
-
dependencies = [
|
366
|
-
"windows_aarch64_gnullvm",
|
367
|
-
"windows_aarch64_msvc",
|
368
|
-
"windows_i686_gnu",
|
369
|
-
"windows_i686_msvc",
|
370
|
-
"windows_x86_64_gnu",
|
371
|
-
"windows_x86_64_gnullvm",
|
372
|
-
"windows_x86_64_msvc",
|
373
|
-
]
|
374
|
-
|
375
|
-
[[package]]
|
376
|
-
name = "windows_aarch64_gnullvm"
|
377
|
-
version = "0.48.5"
|
378
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
379
|
-
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
380
|
-
|
381
|
-
[[package]]
|
382
|
-
name = "windows_aarch64_msvc"
|
383
|
-
version = "0.48.5"
|
384
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
385
|
-
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
386
|
-
|
387
|
-
[[package]]
|
388
|
-
name = "windows_i686_gnu"
|
389
|
-
version = "0.48.5"
|
390
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
391
|
-
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
392
|
-
|
393
|
-
[[package]]
|
394
|
-
name = "windows_i686_msvc"
|
395
|
-
version = "0.48.5"
|
396
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
397
|
-
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
398
|
-
|
399
|
-
[[package]]
|
400
|
-
name = "windows_x86_64_gnu"
|
401
|
-
version = "0.48.5"
|
402
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
403
|
-
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
404
|
-
|
405
|
-
[[package]]
|
406
|
-
name = "windows_x86_64_gnullvm"
|
407
|
-
version = "0.48.5"
|
408
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
409
|
-
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
410
|
-
|
411
|
-
[[package]]
|
412
|
-
name = "windows_x86_64_msvc"
|
413
|
-
version = "0.48.5"
|
414
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
415
|
-
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
data/Cargo.toml
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
[package]
|
2
|
-
name = "rack_dev_insight"
|
3
|
-
version = "0.1.0"
|
4
|
-
edition = "2021"
|
5
|
-
authors = ["Takahiro Ebato <takahiro.ebato@gmail.com>"]
|
6
|
-
license = "MIT"
|
7
|
-
publish = false
|
8
|
-
|
9
|
-
[lib]
|
10
|
-
crate-type = ["cdylib"]
|
11
|
-
|
12
|
-
[dependencies]
|
13
|
-
magnus = { version = "0.6.2" } # add features = ["embed"] when cargo build
|
14
|
-
sqlparser = { version = "0.41.0", features = ["visitor"] }
|
15
|
-
tap = { version = "1.0.1" }
|
16
|
-
indoc = { version = "2" }
|
17
|
-
|
18
|
-
[dev-dependencies]
|
19
|
-
magnus = { version = "0.6.2", features = ["embed"] }
|
20
|
-
rb-sys-test-helpers = { version = "0.2" }
|
@@ -1,32 +0,0 @@
|
|
1
|
-
use magnus::{class, exception::ExceptionClass, prelude::*, value::Lazy, RClass, RModule};
|
2
|
-
|
3
|
-
// define module Rack
|
4
|
-
pub static RACK: Lazy<RModule> = Lazy::new(|ruby| ruby.define_module("Rack").unwrap());
|
5
|
-
|
6
|
-
// define class Rack::DevInsight
|
7
|
-
pub static DEV_INSIGHT: Lazy<RClass> = Lazy::new(|ruby| {
|
8
|
-
ruby.get_inner(&RACK)
|
9
|
-
.define_class("DevInsight", class::object())
|
10
|
-
.unwrap()
|
11
|
-
});
|
12
|
-
|
13
|
-
// define class Rack::DevInsight::Error < StandardError
|
14
|
-
pub static ERROR: Lazy<ExceptionClass> = Lazy::new(|ruby| {
|
15
|
-
ruby.get_inner(&DEV_INSIGHT)
|
16
|
-
.define_error("Error", ruby.exception_standard_error())
|
17
|
-
.unwrap()
|
18
|
-
});
|
19
|
-
|
20
|
-
// define class Rack::DevInsight::ExtError < Rack::DevInsight::Error
|
21
|
-
pub static EXT_ERROR: Lazy<ExceptionClass> = Lazy::new(|ruby| {
|
22
|
-
ruby.get_inner(&DEV_INSIGHT)
|
23
|
-
.define_error("ExtError", ruby.get_inner(&ERROR))
|
24
|
-
.unwrap()
|
25
|
-
});
|
26
|
-
|
27
|
-
// define class Rack::DevInsight::ParserError < Rack::DevInsight::ExtError
|
28
|
-
pub static PARSER_ERROR: Lazy<ExceptionClass> = Lazy::new(|ruby| {
|
29
|
-
ruby.get_inner(&DEV_INSIGHT)
|
30
|
-
.define_error("ParserError", ruby.get_inner(&EXT_ERROR))
|
31
|
-
.unwrap()
|
32
|
-
});
|
@@ -1,354 +0,0 @@
|
|
1
|
-
use std::collections::HashMap;
|
2
|
-
use std::ops::ControlFlow;
|
3
|
-
|
4
|
-
use crate::errors::PARSER_ERROR;
|
5
|
-
use magnus::{Error, Ruby};
|
6
|
-
use sqlparser::ast::TableFactor::Table;
|
7
|
-
use sqlparser::ast::{Statement, TableFactor, Visit, Visitor};
|
8
|
-
use sqlparser::dialect::dialect_from_str;
|
9
|
-
use sqlparser::parser::Parser;
|
10
|
-
use tap::Tap;
|
11
|
-
|
12
|
-
#[derive(Default, Debug, PartialEq)]
|
13
|
-
#[magnus::wrap(class = "Rack::DevInsight::Extractor::CrudTables")]
|
14
|
-
pub struct CrudTables {
|
15
|
-
create_tables: Vec<String>,
|
16
|
-
read_tables: Vec<String>,
|
17
|
-
update_tables: Vec<String>,
|
18
|
-
delete_tables: Vec<String>,
|
19
|
-
}
|
20
|
-
|
21
|
-
impl CrudTables {
|
22
|
-
pub fn create_tables(&self) -> Vec<String> {
|
23
|
-
self.create_tables.clone()
|
24
|
-
}
|
25
|
-
pub fn read_tables(&self) -> Vec<String> {
|
26
|
-
self.read_tables.clone()
|
27
|
-
}
|
28
|
-
pub fn update_tables(&self) -> Vec<String> {
|
29
|
-
self.update_tables.clone()
|
30
|
-
}
|
31
|
-
pub fn delete_tables(&self) -> Vec<String> {
|
32
|
-
self.delete_tables.clone()
|
33
|
-
}
|
34
|
-
}
|
35
|
-
|
36
|
-
#[derive(Default, Debug)]
|
37
|
-
pub struct CrudTableExtractor {
|
38
|
-
create_tables: Vec<String>,
|
39
|
-
read_tables: Vec<String>,
|
40
|
-
update_tables: Vec<String>,
|
41
|
-
delete_tables: Vec<String>,
|
42
|
-
aliases: HashMap<String, String>,
|
43
|
-
to_subtract_from_read: Vec<String>,
|
44
|
-
}
|
45
|
-
|
46
|
-
impl Visitor for CrudTableExtractor {
|
47
|
-
type Break = ();
|
48
|
-
|
49
|
-
fn pre_visit_table_factor(&mut self, table_factor: &TableFactor) -> ControlFlow<Self::Break> {
|
50
|
-
if let Table { name, alias, .. } = table_factor {
|
51
|
-
self.read_tables.push(name.0[0].value.clone());
|
52
|
-
if let Some(alias) = alias {
|
53
|
-
self.aliases
|
54
|
-
.insert(alias.name.value.clone(), name.0[0].value.clone());
|
55
|
-
}
|
56
|
-
}
|
57
|
-
ControlFlow::Continue(())
|
58
|
-
}
|
59
|
-
|
60
|
-
fn pre_visit_statement(&mut self, statement: &Statement) -> ControlFlow<Self::Break> {
|
61
|
-
match statement {
|
62
|
-
Statement::Insert { table_name, .. } => {
|
63
|
-
self.create_tables.push(table_name.0[0].value.clone());
|
64
|
-
self.to_subtract_from_read
|
65
|
-
.push(table_name.0[0].value.clone());
|
66
|
-
}
|
67
|
-
Statement::Update { table, .. } => {
|
68
|
-
if let Table { name, .. } = &table.relation {
|
69
|
-
self.update_tables.push(name.0[0].value.clone());
|
70
|
-
self.to_subtract_from_read.push(name.0[0].value.clone());
|
71
|
-
}
|
72
|
-
}
|
73
|
-
Statement::Delete {
|
74
|
-
tables,
|
75
|
-
from,
|
76
|
-
using,
|
77
|
-
..
|
78
|
-
} => {
|
79
|
-
if !tables.is_empty() {
|
80
|
-
for obj_name in tables {
|
81
|
-
self.delete_tables.push(obj_name.0[0].value.clone());
|
82
|
-
self.to_subtract_from_read.push(obj_name.0[0].value.clone());
|
83
|
-
}
|
84
|
-
} else {
|
85
|
-
for table_with_joins in from {
|
86
|
-
if let Table { name, .. } = &table_with_joins.relation {
|
87
|
-
self.delete_tables.push(name.0[0].value.clone());
|
88
|
-
self.to_subtract_from_read.push(name.0[0].value.clone());
|
89
|
-
// subtract again since using contains the same table
|
90
|
-
if using.is_some() {
|
91
|
-
self.to_subtract_from_read.push(name.0[0].value.clone());
|
92
|
-
}
|
93
|
-
}
|
94
|
-
}
|
95
|
-
}
|
96
|
-
}
|
97
|
-
_ => {}
|
98
|
-
}
|
99
|
-
ControlFlow::Continue(())
|
100
|
-
}
|
101
|
-
}
|
102
|
-
|
103
|
-
impl CrudTableExtractor {
|
104
|
-
pub fn extract(
|
105
|
-
ruby: &Ruby,
|
106
|
-
dialect_name: String,
|
107
|
-
subject: String,
|
108
|
-
) -> Result<CrudTables, Error> {
|
109
|
-
let statements = match dialect_from_str(dialect_name) {
|
110
|
-
Some(dialect) => match Parser::parse_sql(dialect.as_ref(), &subject) {
|
111
|
-
Ok(statements) => statements,
|
112
|
-
Err(error) => {
|
113
|
-
return Err(Error::new(ruby.get_inner(&PARSER_ERROR), error.to_string()));
|
114
|
-
}
|
115
|
-
},
|
116
|
-
None => {
|
117
|
-
return Err(Error::new(
|
118
|
-
magnus::exception::arg_error(),
|
119
|
-
"Dialect not found",
|
120
|
-
));
|
121
|
-
}
|
122
|
-
};
|
123
|
-
let mut visitor = CrudTableExtractor::default();
|
124
|
-
statements.visit(&mut visitor);
|
125
|
-
let create_tables = visitor
|
126
|
-
.convert_alias_to_original(visitor.create_tables.clone())
|
127
|
-
.tap_mut(|vec| vec.sort());
|
128
|
-
let read_tables = visitor
|
129
|
-
.convert_alias_to_original(visitor.read_tables.clone())
|
130
|
-
.tap_mut(|vec| vec.sort());
|
131
|
-
let update_tables = visitor
|
132
|
-
.convert_alias_to_original(visitor.update_tables.clone())
|
133
|
-
.tap_mut(|vec| vec.sort());
|
134
|
-
let delete_tables = visitor
|
135
|
-
.convert_alias_to_original(visitor.delete_tables.clone())
|
136
|
-
.tap_mut(|vec| vec.sort());
|
137
|
-
Ok(CrudTables {
|
138
|
-
read_tables: visitor.subtract(
|
139
|
-
read_tables,
|
140
|
-
visitor.convert_alias_to_original(visitor.to_subtract_from_read.clone()),
|
141
|
-
),
|
142
|
-
create_tables,
|
143
|
-
update_tables,
|
144
|
-
delete_tables,
|
145
|
-
})
|
146
|
-
}
|
147
|
-
|
148
|
-
fn convert_alias_to_original(&self, tables: Vec<String>) -> Vec<String> {
|
149
|
-
tables
|
150
|
-
.into_iter()
|
151
|
-
.map(|table| {
|
152
|
-
if let Some(original) = self.aliases.get(&table) {
|
153
|
-
original.clone()
|
154
|
-
} else {
|
155
|
-
table
|
156
|
-
}
|
157
|
-
})
|
158
|
-
.collect()
|
159
|
-
}
|
160
|
-
|
161
|
-
fn subtract(&self, read_tables: Vec<String>, mut to_subtracts: Vec<String>) -> Vec<String> {
|
162
|
-
read_tables
|
163
|
-
.into_iter()
|
164
|
-
.filter(|read| {
|
165
|
-
if let Some(pos) = to_subtracts.iter().position(|sub| sub == read) {
|
166
|
-
to_subtracts.remove(pos);
|
167
|
-
false
|
168
|
-
} else {
|
169
|
-
true
|
170
|
-
}
|
171
|
-
})
|
172
|
-
.collect()
|
173
|
-
}
|
174
|
-
}
|
175
|
-
|
176
|
-
// Thorough tests are written in Ruby. Basic cases are here for sanity check and to help debug.
|
177
|
-
#[cfg(test)]
|
178
|
-
mod tests {
|
179
|
-
use super::*;
|
180
|
-
use indoc::indoc;
|
181
|
-
use rb_sys_test_helpers::with_ruby_vm;
|
182
|
-
|
183
|
-
#[test]
|
184
|
-
fn test_select_statement() {
|
185
|
-
with_ruby_vm(|| {
|
186
|
-
let ruby = Ruby::get().unwrap();
|
187
|
-
let sql = "SELECT a FROM t1 WHERE b = 1 AND c in (2, 3) AND d LIKE '%foo'; SELECT b FROM t2 WHERE c = 4";
|
188
|
-
match CrudTableExtractor::extract(&ruby, String::from("mysql"), sql.into()) {
|
189
|
-
Ok(result) => assert_eq!(result, CrudTables {
|
190
|
-
create_tables: vec![],
|
191
|
-
read_tables: vec!["t1".to_string(), "t2".to_string()],
|
192
|
-
update_tables: vec![],
|
193
|
-
delete_tables: vec![],
|
194
|
-
}),
|
195
|
-
Err(error) => unreachable!("Should not have errored. Error: {}", error)
|
196
|
-
}
|
197
|
-
}).unwrap()
|
198
|
-
}
|
199
|
-
|
200
|
-
#[test]
|
201
|
-
fn test_complicated_select_statement() {
|
202
|
-
with_ruby_vm(|| {
|
203
|
-
let ruby = Ruby::get().unwrap();
|
204
|
-
let sql = indoc! {" \
|
205
|
-
SELECT a \
|
206
|
-
FROM t1 \
|
207
|
-
INNER JOIN t2 ON t1.b = t2.b \
|
208
|
-
WHERE c IN ( \
|
209
|
-
SELECT c \
|
210
|
-
FROM t3 \
|
211
|
-
WHERE d = 1 \
|
212
|
-
) \
|
213
|
-
"};
|
214
|
-
match CrudTableExtractor::extract(&ruby, String::from("mysql"), sql.into()) {
|
215
|
-
Ok(result) => assert_eq!(
|
216
|
-
result,
|
217
|
-
CrudTables {
|
218
|
-
create_tables: vec![],
|
219
|
-
read_tables: vec!["t1".to_string(), "t2".to_string(), "t3".to_string()],
|
220
|
-
update_tables: vec![],
|
221
|
-
delete_tables: vec![],
|
222
|
-
}
|
223
|
-
),
|
224
|
-
Err(error) => unreachable!("Should not have errored. Error: {}", error),
|
225
|
-
}
|
226
|
-
})
|
227
|
-
.unwrap()
|
228
|
-
}
|
229
|
-
|
230
|
-
#[test]
|
231
|
-
fn test_complicated_insert_statement() {
|
232
|
-
with_ruby_vm(|| {
|
233
|
-
let ruby = Ruby::get().unwrap();
|
234
|
-
let sql = indoc! {" \
|
235
|
-
INSERT INTO t1 (a, b) \
|
236
|
-
SELECT t2_alias.a, t2_alias.b \
|
237
|
-
FROM t2 t2_alias \
|
238
|
-
INNER JOIN t3 t3_alias ON t2_alias.c = t3_alias.c \
|
239
|
-
WHERE t3_alias.d IN ( \
|
240
|
-
SELECT d \
|
241
|
-
FROM t4 \
|
242
|
-
WHERE e = 1 \
|
243
|
-
) \
|
244
|
-
"};
|
245
|
-
match CrudTableExtractor::extract(&ruby, String::from("mysql"), sql.into()) {
|
246
|
-
Ok(result) => assert_eq!(
|
247
|
-
result,
|
248
|
-
CrudTables {
|
249
|
-
create_tables: vec!["t1".to_string()],
|
250
|
-
read_tables: vec!["t2".to_string(), "t3".to_string(), "t4".to_string()],
|
251
|
-
update_tables: vec![],
|
252
|
-
delete_tables: vec![],
|
253
|
-
}
|
254
|
-
),
|
255
|
-
Err(error) => unreachable!("Should not have errored. Error: {}", error),
|
256
|
-
}
|
257
|
-
})
|
258
|
-
.unwrap()
|
259
|
-
}
|
260
|
-
|
261
|
-
#[test]
|
262
|
-
fn test_complicated_update_statement() {
|
263
|
-
with_ruby_vm(|| {
|
264
|
-
let ruby = Ruby::get().unwrap();
|
265
|
-
let sql = indoc! {"\
|
266
|
-
UPDATE t1
|
267
|
-
JOIN t2 ON t1.a = t2.a
|
268
|
-
JOIN t3 ON t2.a = t3.a
|
269
|
-
LEFT JOIN t4 ON t1.d = t4.a
|
270
|
-
SET t1.b = t2.b, t1.c = t3.c
|
271
|
-
WHERE t4.b = 1
|
272
|
-
AND t2.e = 2
|
273
|
-
AND t3.f = 3
|
274
|
-
"};
|
275
|
-
match CrudTableExtractor::extract(&ruby, String::from("mysql"), sql.into()) {
|
276
|
-
Ok(result) => assert_eq!(
|
277
|
-
result,
|
278
|
-
CrudTables {
|
279
|
-
create_tables: vec![],
|
280
|
-
read_tables: vec!["t2".to_string(), "t3".to_string(), "t4".to_string()],
|
281
|
-
update_tables: vec!["t1".to_string()],
|
282
|
-
delete_tables: vec![],
|
283
|
-
}
|
284
|
-
),
|
285
|
-
Err(error) => unreachable!("Should not have errored. Error: {}", error),
|
286
|
-
}
|
287
|
-
})
|
288
|
-
.unwrap()
|
289
|
-
}
|
290
|
-
|
291
|
-
#[test]
|
292
|
-
fn test_complicated_delete_statement() {
|
293
|
-
with_ruby_vm(|| {
|
294
|
-
let ruby = Ruby::get().unwrap();
|
295
|
-
let sql = indoc! {" \
|
296
|
-
DELETE t1_alias, t2_alias \
|
297
|
-
FROM t1 AS t1_alias \
|
298
|
-
INNER JOIN t2 AS t2_alias ON t1_alias.a = t2_alias.a \
|
299
|
-
WHERE t1.b IN ( \
|
300
|
-
SELECT b \
|
301
|
-
FROM t3 \
|
302
|
-
WHERE c = 1 \
|
303
|
-
) \
|
304
|
-
"};
|
305
|
-
match CrudTableExtractor::extract(&ruby, String::from("mysql"), sql.into()) {
|
306
|
-
Ok(result) => assert_eq!(
|
307
|
-
result,
|
308
|
-
CrudTables {
|
309
|
-
create_tables: vec![],
|
310
|
-
read_tables: vec!["t3".to_string()],
|
311
|
-
update_tables: vec![],
|
312
|
-
delete_tables: vec!["t1".to_string(), "t2".to_string()],
|
313
|
-
}
|
314
|
-
),
|
315
|
-
Err(error) => unreachable!("Should not have errored. Error: {}", error),
|
316
|
-
}
|
317
|
-
})
|
318
|
-
.unwrap()
|
319
|
-
}
|
320
|
-
|
321
|
-
#[test]
|
322
|
-
fn test_multiple_statements() {
|
323
|
-
with_ruby_vm(|| {
|
324
|
-
let ruby = Ruby::get().unwrap();
|
325
|
-
let sql = "SELECT a FROM t1 WHERE b = 1; INSERT INTO t2 (a, b) VALUES (1, 2); UPDATE t3 SET a = 1 WHERE b = 2; DELETE FROM t4 WHERE a = 1";
|
326
|
-
match CrudTableExtractor::extract(&ruby, String::from("mysql"), sql.into()) {
|
327
|
-
Ok(result) => assert_eq!(
|
328
|
-
result,
|
329
|
-
CrudTables {
|
330
|
-
create_tables: vec!["t2".to_string()],
|
331
|
-
read_tables: vec!["t1".to_string()],
|
332
|
-
update_tables: vec!["t3".to_string()],
|
333
|
-
delete_tables: vec!["t4".to_string()],
|
334
|
-
}
|
335
|
-
),
|
336
|
-
Err(error) => unreachable!("Should not have errored. Error: {}", error),
|
337
|
-
}
|
338
|
-
})
|
339
|
-
.unwrap()
|
340
|
-
}
|
341
|
-
|
342
|
-
#[test]
|
343
|
-
fn test_invalid_sql() {
|
344
|
-
with_ruby_vm(|| {
|
345
|
-
let ruby = Ruby::get().unwrap();
|
346
|
-
let sql = "SELECT a FROM t1 WHERE b = 1 WHERE c in (2, 3";
|
347
|
-
match CrudTableExtractor::extract(&ruby, String::from("mysql"), sql.into()) {
|
348
|
-
Ok(_) => unreachable!("Should have errored"),
|
349
|
-
Err(error) => assert!(error.is_kind_of(ruby.get_inner(&PARSER_ERROR))),
|
350
|
-
}
|
351
|
-
})
|
352
|
-
.unwrap()
|
353
|
-
}
|
354
|
-
}
|
@@ -1,31 +0,0 @@
|
|
1
|
-
use extractor::CrudTableExtractor;
|
2
|
-
use extractor::CrudTables;
|
3
|
-
use magnus::{class, define_module, function, method, prelude::*, Error};
|
4
|
-
use normalizer::Normalizer;
|
5
|
-
|
6
|
-
mod errors;
|
7
|
-
mod extractor;
|
8
|
-
mod normalizer;
|
9
|
-
|
10
|
-
#[magnus::init]
|
11
|
-
fn init() -> Result<(), Error> {
|
12
|
-
let rack_module = define_module("Rack").unwrap();
|
13
|
-
let dev_insight_class = rack_module
|
14
|
-
.define_class("DevInsight", class::object())
|
15
|
-
.unwrap();
|
16
|
-
|
17
|
-
let normalizer_module = dev_insight_class.define_module("Normalizer").unwrap();
|
18
|
-
normalizer_module.define_singleton_method("_normalize", function!(Normalizer::normalize, 2))?;
|
19
|
-
|
20
|
-
let extractor_module = dev_insight_class.define_module("Extractor").unwrap();
|
21
|
-
let crud_tables_class = extractor_module
|
22
|
-
.define_class("CrudTables", class::object())
|
23
|
-
.unwrap();
|
24
|
-
crud_tables_class
|
25
|
-
.define_singleton_method("_extract", function!(CrudTableExtractor::extract, 2))?;
|
26
|
-
crud_tables_class.define_method("_create_tables", method!(CrudTables::create_tables, 0))?;
|
27
|
-
crud_tables_class.define_method("_read_tables", method!(CrudTables::read_tables, 0))?;
|
28
|
-
crud_tables_class.define_method("_update_tables", method!(CrudTables::update_tables, 0))?;
|
29
|
-
crud_tables_class.define_method("_delete_tables", method!(CrudTables::delete_tables, 0))?;
|
30
|
-
Ok(())
|
31
|
-
}
|
@@ -1,97 +0,0 @@
|
|
1
|
-
use std::ops::ControlFlow;
|
2
|
-
|
3
|
-
use crate::errors::PARSER_ERROR;
|
4
|
-
use magnus::{exception, Error, Ruby};
|
5
|
-
use sqlparser::ast::Value::Placeholder;
|
6
|
-
use sqlparser::ast::{Expr, VisitMut, VisitorMut};
|
7
|
-
use sqlparser::dialect::dialect_from_str;
|
8
|
-
use sqlparser::parser::Parser;
|
9
|
-
|
10
|
-
pub struct Normalizer;
|
11
|
-
|
12
|
-
impl VisitorMut for Normalizer {
|
13
|
-
type Break = ();
|
14
|
-
|
15
|
-
fn post_visit_expr(&mut self, expr: &mut Expr) -> ControlFlow<Self::Break> {
|
16
|
-
if let Expr::Value(value) = expr {
|
17
|
-
*value = Placeholder("?".into());
|
18
|
-
}
|
19
|
-
ControlFlow::Continue(())
|
20
|
-
}
|
21
|
-
}
|
22
|
-
|
23
|
-
impl Normalizer {
|
24
|
-
pub fn normalize(
|
25
|
-
ruby: &Ruby,
|
26
|
-
dialect_name: String,
|
27
|
-
subject: String,
|
28
|
-
) -> Result<Vec<String>, Error> {
|
29
|
-
let mut statements = match dialect_from_str(&dialect_name) {
|
30
|
-
Some(dialect) => match Parser::parse_sql(dialect.as_ref(), &subject) {
|
31
|
-
Ok(statements) => statements,
|
32
|
-
Err(error) => {
|
33
|
-
return Err(Error::new(ruby.get_inner(&PARSER_ERROR), error.to_string()))
|
34
|
-
}
|
35
|
-
},
|
36
|
-
None => {
|
37
|
-
return Err(Error::new(
|
38
|
-
exception::arg_error(),
|
39
|
-
format!("Dialect not found {}", dialect_name),
|
40
|
-
))
|
41
|
-
}
|
42
|
-
};
|
43
|
-
statements.visit(&mut Self);
|
44
|
-
Ok(statements
|
45
|
-
.into_iter()
|
46
|
-
.map(|statement| statement.to_string())
|
47
|
-
.collect::<Vec<String>>())
|
48
|
-
}
|
49
|
-
}
|
50
|
-
|
51
|
-
// Thorough tests are written in Ruby. Basic cases are here for sanity check and to help debug.
|
52
|
-
#[cfg(test)]
|
53
|
-
mod tests {
|
54
|
-
use super::*;
|
55
|
-
use rb_sys_test_helpers::with_ruby_vm;
|
56
|
-
|
57
|
-
#[test]
|
58
|
-
fn test_single_sql() {
|
59
|
-
with_ruby_vm(|| {
|
60
|
-
let ruby = Ruby::get().unwrap();
|
61
|
-
let sql = "SELECT a FROM t1 WHERE b = 1 AND c in (2, 3) AND d LIKE '%foo'";
|
62
|
-
match Normalizer::normalize(&ruby, String::from("mysql"), sql.into()) {
|
63
|
-
Ok(result) => assert_eq!(
|
64
|
-
result,
|
65
|
-
["SELECT a FROM t1 WHERE b = ? AND c IN (?, ?) AND d LIKE ?"]
|
66
|
-
),
|
67
|
-
Err(error) => unreachable!("Should not have errored. Error: {}", error),
|
68
|
-
}
|
69
|
-
})
|
70
|
-
.unwrap();
|
71
|
-
}
|
72
|
-
|
73
|
-
#[test]
|
74
|
-
fn test_multiple_sqls() {
|
75
|
-
with_ruby_vm(|| {
|
76
|
-
let ruby = Ruby::get().unwrap();
|
77
|
-
let sql = "SELECT a FROM t1 WHERE b = 1 AND c in (2, 3) AND d LIKE '%foo'; SELECT a FROM t1 WHERE b = 1 AND c in (2, 3) AND d LIKE '%foo'";
|
78
|
-
match Normalizer::normalize(&ruby, String::from("mysql"), sql.into()) {
|
79
|
-
Ok(result) => assert_eq!(result, ["SELECT a FROM t1 WHERE b = ? AND c IN (?, ?) AND d LIKE ?", "SELECT a FROM t1 WHERE b = ? AND c IN (?, ?) AND d LIKE ?"]),
|
80
|
-
Err(error) => unreachable!("Should not have errored. Error: {}", error)
|
81
|
-
}
|
82
|
-
}).unwrap();
|
83
|
-
}
|
84
|
-
|
85
|
-
#[test]
|
86
|
-
fn test_incorrect_sql() {
|
87
|
-
with_ruby_vm(|| {
|
88
|
-
let ruby = Ruby::get().unwrap();
|
89
|
-
let invalid_sql = "SELECT a FROM t1 WHERE b = 1 WHERE c in (2, 3)";
|
90
|
-
match Normalizer::normalize(&ruby, String::from("mysql"), invalid_sql.into()) {
|
91
|
-
Ok(_) => unreachable!("Should have errored"),
|
92
|
-
Err(error) => assert!(error.is_kind_of(ruby.get_inner(&PARSER_ERROR))),
|
93
|
-
}
|
94
|
-
})
|
95
|
-
.unwrap();
|
96
|
-
}
|
97
|
-
}
|
@@ -1,21 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Rack
|
4
|
-
class DevInsight
|
5
|
-
module Extractor
|
6
|
-
class CrudTables
|
7
|
-
class << self
|
8
|
-
def extract(dialect_name, statement)
|
9
|
-
crud_tables = _extract(dialect_name, statement)
|
10
|
-
{
|
11
|
-
'CREATE' => crud_tables._create_tables,
|
12
|
-
'READ' => crud_tables._read_tables,
|
13
|
-
'UPDATE' => crud_tables._update_tables,
|
14
|
-
'DELETE' => crud_tables._delete_tables,
|
15
|
-
}
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|