dbviewer 0.5.0 → 0.5.1
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 +4 -4
- data/app/controllers/concerns/dbviewer/database_operations.rb +27 -45
- data/app/controllers/dbviewer/api/database_controller.rb +2 -6
- data/app/controllers/dbviewer/api/queries_controller.rb +18 -8
- data/app/controllers/dbviewer/api/tables_controller.rb +48 -23
- data/app/controllers/dbviewer/application_controller.rb +1 -1
- data/app/controllers/dbviewer/home_controller.rb +1 -1
- data/app/controllers/dbviewer/tables_controller.rb +2 -2
- data/app/views/dbviewer/home/index.html.erb +1 -1
- data/lib/dbviewer/database_manager.rb +40 -0
- data/lib/dbviewer/engine.rb +15 -0
- data/lib/dbviewer/logger.rb +0 -1
- data/lib/dbviewer/query_parser.rb +0 -10
- data/lib/dbviewer/version.rb +1 -1
- metadata +1 -2
- data/lib/generators/dbviewer/structured_api_generator.rb +0 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d16f8924e91f06f61088b4948397aa23530cd6e4a89e00ab483cc047ea5fc492
|
4
|
+
data.tar.gz: 98091d292dfe7c42f0644dc1734fb298f532cfc6f99e0176ff1ae517c8db1cc8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a423fec219eaf8f40adde10a316cd4e45f33eb27d514ead5a0da735db3a475ac1ac430851cc347095106ca011dda72eae4ab68a01cba8ea8782ab132c99de8b6
|
7
|
+
data.tar.gz: 57594e053bf4bd1389da099fa91236af80e579bfc6f0ebd72da5081187c16a2343717c803632781bf39eb8d5281151cc8614def62c035b1b35dfd2f8946da631
|
@@ -5,7 +5,7 @@ module Dbviewer
|
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
7
7
|
included do
|
8
|
-
helper_method :current_table?, :get_database_name if respond_to?(:helper_method)
|
8
|
+
helper_method :current_table?, :get_database_name, :get_adapter_name if respond_to?(:helper_method)
|
9
9
|
end
|
10
10
|
|
11
11
|
# Initialize the database manager
|
@@ -44,9 +44,33 @@ module Dbviewer
|
|
44
44
|
"Database"
|
45
45
|
end
|
46
46
|
|
47
|
+
# Get the name of the current database adapter
|
48
|
+
def get_adapter_name
|
49
|
+
adapter = database_manager.connection.adapter_name
|
50
|
+
|
51
|
+
# Format the adapter name for better display
|
52
|
+
case adapter.downcase
|
53
|
+
when /mysql/
|
54
|
+
"MySQL"
|
55
|
+
when /postgres/
|
56
|
+
"PostgreSQL"
|
57
|
+
when /sqlite/
|
58
|
+
"SQLite"
|
59
|
+
when /oracle/
|
60
|
+
"Oracle"
|
61
|
+
when /sqlserver/, /mssql/
|
62
|
+
"SQL Server"
|
63
|
+
else
|
64
|
+
adapter.titleize # Fallback to titleized version
|
65
|
+
end
|
66
|
+
rescue => e
|
67
|
+
Rails.logger.error("Error retrieving adapter name: #{e.message}")
|
68
|
+
"Unknown"
|
69
|
+
end
|
70
|
+
|
47
71
|
# Fetch all tables with their stats
|
48
72
|
# By default, don't include record counts for better performance on sidebar
|
49
|
-
def
|
73
|
+
def fetch_tables(include_record_counts = false)
|
50
74
|
database_manager.tables.map do |table_name|
|
51
75
|
table_stats = {
|
52
76
|
name: table_name
|
@@ -62,7 +86,7 @@ module Dbviewer
|
|
62
86
|
# Gather database analytics information
|
63
87
|
def fetch_database_analytics
|
64
88
|
# For analytics, we do need record counts
|
65
|
-
tables =
|
89
|
+
tables = fetch_tables(include_record_counts: true)
|
66
90
|
|
67
91
|
# Calculate overall statistics
|
68
92
|
analytics = {
|
@@ -77,48 +101,6 @@ module Dbviewer
|
|
77
101
|
analytics
|
78
102
|
end
|
79
103
|
|
80
|
-
# Calculate approximate schema size
|
81
|
-
def calculate_schema_size
|
82
|
-
adapter = database_manager.connection.adapter_name.downcase
|
83
|
-
|
84
|
-
case adapter
|
85
|
-
when /mysql/
|
86
|
-
query = <<-SQL
|
87
|
-
SELECT
|
88
|
-
SUM(data_length + index_length) AS size
|
89
|
-
FROM
|
90
|
-
information_schema.TABLES
|
91
|
-
WHERE
|
92
|
-
table_schema = DATABASE()
|
93
|
-
SQL
|
94
|
-
result = database_manager.execute_query(query).first
|
95
|
-
result ? result["size"].to_i : nil
|
96
|
-
when /postgres/
|
97
|
-
query = <<-SQL
|
98
|
-
SELECT pg_database_size(current_database()) AS size
|
99
|
-
SQL
|
100
|
-
result = database_manager.execute_query(query).first
|
101
|
-
result ? result["size"].to_i : nil
|
102
|
-
when /sqlite/
|
103
|
-
# For SQLite, we need to use the special PRAGMA method without LIMIT
|
104
|
-
# Get page count
|
105
|
-
page_count_result = database_manager.execute_sqlite_pragma("page_count")
|
106
|
-
page_count = page_count_result.first.values.first.to_i
|
107
|
-
|
108
|
-
# Get page size
|
109
|
-
page_size_result = database_manager.execute_sqlite_pragma("page_size")
|
110
|
-
page_size = page_size_result.first.values.first.to_i
|
111
|
-
|
112
|
-
# Calculate total size
|
113
|
-
page_count * page_size
|
114
|
-
else
|
115
|
-
nil # Unsupported database type for size calculation
|
116
|
-
end
|
117
|
-
rescue => e
|
118
|
-
Rails.logger.error("Error calculating database size: #{e.message}")
|
119
|
-
nil
|
120
|
-
end
|
121
|
-
|
122
104
|
# Get column information for a specific table
|
123
105
|
def fetch_table_columns(table_name)
|
124
106
|
database_manager.table_columns(table_name)
|
@@ -2,12 +2,8 @@ module Dbviewer
|
|
2
2
|
module Api
|
3
3
|
class DatabaseController < BaseController
|
4
4
|
def size
|
5
|
-
|
6
|
-
|
7
|
-
render_success(schema_size: size)
|
8
|
-
rescue => e
|
9
|
-
render_error("Error calculating schema size: #{e.message}")
|
10
|
-
end
|
5
|
+
size = database_manager.fetch_schema_size
|
6
|
+
render_success(schema_size: size)
|
11
7
|
end
|
12
8
|
end
|
13
9
|
end
|
@@ -2,17 +2,27 @@ module Dbviewer
|
|
2
2
|
module Api
|
3
3
|
class QueriesController < BaseController
|
4
4
|
def recent
|
5
|
-
@recent_queries = if Dbviewer.configuration.enable_query_logging
|
6
|
-
Dbviewer::Logger.instance.recent_queries(limit: 10)
|
7
|
-
else
|
8
|
-
[]
|
9
|
-
end
|
10
|
-
|
11
5
|
render_success({
|
12
|
-
enabled:
|
13
|
-
queries:
|
6
|
+
enabled: query_logging_enabled?,
|
7
|
+
queries: fetch_recent_queries
|
14
8
|
})
|
15
9
|
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def fetch_recent_queries
|
14
|
+
return [] unless query_logging_enabled?
|
15
|
+
|
16
|
+
Dbviewer::Logger.instance.recent_queries(limit: queries_limit)
|
17
|
+
end
|
18
|
+
|
19
|
+
def query_logging_enabled?
|
20
|
+
Dbviewer.configuration.enable_query_logging
|
21
|
+
end
|
22
|
+
|
23
|
+
def queries_limit
|
24
|
+
10
|
25
|
+
end
|
16
26
|
end
|
17
27
|
end
|
18
28
|
end
|
@@ -2,38 +2,63 @@ module Dbviewer
|
|
2
2
|
module Api
|
3
3
|
class TablesController < BaseController
|
4
4
|
def index
|
5
|
-
|
6
|
-
render_success(total_tables:
|
5
|
+
tables_count = fetch_tables_count
|
6
|
+
render_success(total_tables: tables_count)
|
7
7
|
end
|
8
8
|
|
9
9
|
def records
|
10
|
-
|
10
|
+
tables_stats = fetch_tables_stats
|
11
|
+
render_success(tables_stats)
|
12
|
+
end
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
def relationships_count
|
15
|
+
total_relationships = calculate_total_relationships
|
16
|
+
render_success(total_relationships: total_relationships)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
18
20
|
|
19
|
-
|
21
|
+
def fetch_tables_count
|
22
|
+
fetch_tables(include_record_counts: false).size
|
20
23
|
end
|
21
24
|
|
22
|
-
def
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
tables
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
25
|
+
def fetch_tables_stats
|
26
|
+
tables = fetch_tables(include_record_counts: true)
|
27
|
+
|
28
|
+
{
|
29
|
+
total_records: calculate_total_records(tables),
|
30
|
+
largest_tables: find_largest_tables(tables),
|
31
|
+
empty_tables: find_empty_tables(tables),
|
32
|
+
avg_records_per_table: calculate_average_records(tables)
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def calculate_total_relationships
|
37
|
+
tables = fetch_tables(include_record_counts: false)
|
38
|
+
|
39
|
+
tables.sum do |table|
|
40
|
+
metadata = fetch_table_metadata(table[:name])
|
41
|
+
metadata&.dig(:foreign_keys)&.size || 0
|
35
42
|
end
|
36
43
|
end
|
44
|
+
|
45
|
+
def calculate_total_records(tables)
|
46
|
+
tables.sum { |table| table[:record_count] }
|
47
|
+
end
|
48
|
+
|
49
|
+
def find_largest_tables(tables, limit = 10)
|
50
|
+
tables.sort_by { |table| -table[:record_count] }.first(limit)
|
51
|
+
end
|
52
|
+
|
53
|
+
def find_empty_tables(tables)
|
54
|
+
tables.select { |table| table[:record_count] == 0 }
|
55
|
+
end
|
56
|
+
|
57
|
+
def calculate_average_records(tables)
|
58
|
+
return 0 if tables.empty?
|
59
|
+
|
60
|
+
(calculate_total_records(tables).to_f / tables.size).round(1)
|
61
|
+
end
|
37
62
|
end
|
38
63
|
end
|
39
64
|
end
|
@@ -7,7 +7,7 @@ module Dbviewer
|
|
7
7
|
before_action :set_global_filters, only: [ :show, :export_csv ]
|
8
8
|
|
9
9
|
def index
|
10
|
-
@tables =
|
10
|
+
@tables = fetch_tables(include_record_counts: true)
|
11
11
|
end
|
12
12
|
|
13
13
|
def show
|
@@ -60,7 +60,7 @@ module Dbviewer
|
|
60
60
|
def query
|
61
61
|
@read_only_mode = true # Flag to indicate we're in read-only mode
|
62
62
|
@columns = fetch_table_columns(@table_name)
|
63
|
-
@tables =
|
63
|
+
@tables = fetch_tables # Fetch tables for sidebar
|
64
64
|
|
65
65
|
prepare_query
|
66
66
|
execute_query
|
@@ -3,7 +3,7 @@
|
|
3
3
|
<div class="col">
|
4
4
|
<h1 class="h3 mb-2">Database Overview</h1>
|
5
5
|
<p class="database-connection-info">
|
6
|
-
Connected to database: <span class="badge rounded-pill database-name-badge"><%= get_database_name %></span>
|
6
|
+
Connected to <%= get_adapter_name %> database: <span class="badge rounded-pill database-name-badge"><%= get_database_name %></span>
|
7
7
|
</p>
|
8
8
|
</div>
|
9
9
|
</div>
|
@@ -180,8 +180,48 @@ module Dbviewer
|
|
180
180
|
@cache_manager.clear_all
|
181
181
|
end
|
182
182
|
|
183
|
+
# Calculate the total size of the database schema
|
184
|
+
# @return [Integer, nil] Database size in bytes or nil if unsupported
|
185
|
+
def fetch_schema_size
|
186
|
+
case adapter_name
|
187
|
+
when /mysql/
|
188
|
+
fetch_mysql_size
|
189
|
+
when /postgres/
|
190
|
+
fetch_postgres_size
|
191
|
+
when /sqlite/
|
192
|
+
fetch_sqlite_size
|
193
|
+
else
|
194
|
+
nil # Unsupported database type for size calculation
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
183
198
|
private
|
184
199
|
|
200
|
+
def fetch_mysql_size
|
201
|
+
query = "SELECT SUM(data_length + index_length) AS size FROM information_schema.TABLES WHERE table_schema = DATABASE()"
|
202
|
+
fetch_size_from_query(query)
|
203
|
+
end
|
204
|
+
|
205
|
+
def fetch_postgres_size
|
206
|
+
query = "SELECT pg_database_size(current_database()) AS size"
|
207
|
+
fetch_size_from_query(query)
|
208
|
+
end
|
209
|
+
|
210
|
+
def fetch_sqlite_size
|
211
|
+
page_count = fetch_sqlite_pragma_value("page_count")
|
212
|
+
page_size = fetch_sqlite_pragma_value("page_size")
|
213
|
+
page_count * page_size
|
214
|
+
end
|
215
|
+
|
216
|
+
def fetch_sqlite_pragma_value(pragma_name)
|
217
|
+
execute_sqlite_pragma(pragma_name).first.values.first.to_i
|
218
|
+
end
|
219
|
+
|
220
|
+
def fetch_size_from_query(query)
|
221
|
+
result = execute_query(query).first
|
222
|
+
result ? result["size"].to_i : nil
|
223
|
+
end
|
224
|
+
|
185
225
|
# Ensure we have a valid database connection
|
186
226
|
# @return [ActiveRecord::ConnectionAdapters::AbstractAdapter] The database connection
|
187
227
|
def ensure_connection
|
data/lib/dbviewer/engine.rb
CHANGED
@@ -20,8 +20,23 @@ module Dbviewer
|
|
20
20
|
initializer "dbviewer.notifications" do
|
21
21
|
ActiveSupport::Notifications.subscribe("sql.active_record") do |*args|
|
22
22
|
event = ActiveSupport::Notifications::Event.new(*args)
|
23
|
+
|
24
|
+
next if skip_internal_query?(event)
|
25
|
+
|
23
26
|
Logger.instance.add(event)
|
24
27
|
end
|
25
28
|
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def skip_internal_query?(event)
|
33
|
+
caller_locations = caller_locations(1)
|
34
|
+
return false unless caller_locations
|
35
|
+
|
36
|
+
# Look for dbviewer in the call stack
|
37
|
+
caller_locations.any? { |l| l.path.include?("dbviewer") }
|
38
|
+
rescue
|
39
|
+
false
|
40
|
+
end
|
26
41
|
end
|
27
42
|
end
|
data/lib/dbviewer/logger.rb
CHANGED
@@ -68,15 +68,5 @@ module Dbviewer
|
|
68
68
|
rescue
|
69
69
|
[]
|
70
70
|
end
|
71
|
-
|
72
|
-
# Determine if a query should be skipped based on content
|
73
|
-
def self.should_skip_query?(event)
|
74
|
-
event.payload[:name] == "SCHEMA" ||
|
75
|
-
event.payload[:sql].include?("SHOW TABLES") ||
|
76
|
-
event.payload[:sql].include?("sqlite_master") ||
|
77
|
-
event.payload[:sql].include?("information_schema") ||
|
78
|
-
event.payload[:sql].include?("pg_catalog") ||
|
79
|
-
from_dbviewer?(event)
|
80
|
-
end
|
81
71
|
end
|
82
72
|
end
|
data/lib/dbviewer/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dbviewer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wailan Tirajoh
|
@@ -100,7 +100,6 @@ files:
|
|
100
100
|
- lib/dbviewer/table_query_params.rb
|
101
101
|
- lib/dbviewer/version.rb
|
102
102
|
- lib/generators/dbviewer/initializer_generator.rb
|
103
|
-
- lib/generators/dbviewer/structured_api_generator.rb
|
104
103
|
- lib/generators/dbviewer/templates/initializer.rb
|
105
104
|
- lib/tasks/dbviewer_tasks.rake
|
106
105
|
homepage: https://github.com/wailantirajoh/dbviewer
|
@@ -1,11 +0,0 @@
|
|
1
|
-
module Dbviewer
|
2
|
-
module Generators
|
3
|
-
class StructuredApiGenerator < Rails::Generators::Base
|
4
|
-
source_root File.expand_path("../templates", __FILE__)
|
5
|
-
|
6
|
-
def create_initializer
|
7
|
-
template "structured_api_initializer.rb", "config/initializers/dbviewer_structured_api.rb"
|
8
|
-
end
|
9
|
-
end
|
10
|
-
end
|
11
|
-
end
|