dbviewer 0.5.1 → 0.5.3
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/README.md +156 -1
- data/app/controllers/concerns/dbviewer/database_operations.rb +11 -19
- data/app/controllers/dbviewer/api/entity_relationship_diagrams_controller.rb +84 -0
- data/app/controllers/dbviewer/api/queries_controller.rb +1 -1
- data/app/controllers/dbviewer/entity_relationship_diagrams_controller.rb +5 -6
- data/app/controllers/dbviewer/logs_controller.rb +1 -1
- data/app/controllers/dbviewer/tables_controller.rb +2 -8
- data/app/helpers/dbviewer/application_helper.rb +1 -1
- data/app/views/dbviewer/entity_relationship_diagrams/index.html.erb +217 -100
- data/app/views/dbviewer/tables/show.html.erb +278 -404
- data/config/routes.rb +7 -0
- data/lib/dbviewer/database/cache_manager.rb +78 -0
- data/lib/dbviewer/database/dynamic_model_factory.rb +62 -0
- data/lib/dbviewer/database/manager.rb +204 -0
- data/lib/dbviewer/database/metadata_manager.rb +129 -0
- data/lib/dbviewer/datatable/query_operations.rb +330 -0
- data/lib/dbviewer/datatable/query_params.rb +41 -0
- data/lib/dbviewer/engine.rb +11 -8
- data/lib/dbviewer/query/analyzer.rb +250 -0
- data/lib/dbviewer/query/collection.rb +39 -0
- data/lib/dbviewer/query/executor.rb +93 -0
- data/lib/dbviewer/query/logger.rb +108 -0
- data/lib/dbviewer/query/parser.rb +56 -0
- data/lib/dbviewer/storage/file_storage.rb +0 -3
- data/lib/dbviewer/version.rb +1 -1
- data/lib/dbviewer.rb +24 -7
- metadata +14 -14
- data/lib/dbviewer/cache_manager.rb +0 -78
- data/lib/dbviewer/database_manager.rb +0 -249
- data/lib/dbviewer/dynamic_model_factory.rb +0 -60
- data/lib/dbviewer/error_handler.rb +0 -18
- data/lib/dbviewer/logger.rb +0 -76
- data/lib/dbviewer/query_analyzer.rb +0 -239
- data/lib/dbviewer/query_collection.rb +0 -37
- data/lib/dbviewer/query_executor.rb +0 -91
- data/lib/dbviewer/query_parser.rb +0 -72
- data/lib/dbviewer/table_metadata_manager.rb +0 -136
- data/lib/dbviewer/table_query_operations.rb +0 -621
- data/lib/dbviewer/table_query_params.rb +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9f8d59c15a88c9b63e2765f80b554c2b2a0a33f82fdb094e1ed6f86fdf8dde20
|
4
|
+
data.tar.gz: d2bbe1fd8acae50c791ab4c32a061c78abbee095001e1647e32b4af4e6918753
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fcdf464a23ec52c45d85645ea0dcc9f57fe59c69b8452a68de7f000606d31d3e29c8b81ca237a228debc6884b57e8fb7a40500c1f02af550b3c5515456009ae5
|
7
|
+
data.tar.gz: 768b6cb0f7d67c0ecda6301c6a94471ad16a89f37c6164c8e4f976222f1e1f95720b17477e43811f4d9574c79c71e1f6e582471d0acf408b815aeba17b58c961
|
data/README.md
CHANGED
@@ -7,7 +7,6 @@ It's designed for development, debugging, and database analysis, offering a clea
|
|
7
7
|
|
8
8
|
<img width="1470" alt="image" src="https://github.com/user-attachments/assets/0d2719ad-f5b4-4818-891d-5bff7be6c5c3" />
|
9
9
|
|
10
|
-
|
11
10
|
## ✨ Features
|
12
11
|
|
13
12
|
- **Dashboard**: View a comprehensive dashboard with database analytics, largest tables, most complex tables, and recent SQL queries
|
@@ -270,6 +269,162 @@ The simplest way to update is using Bundler:
|
|
270
269
|
rails server
|
271
270
|
```
|
272
271
|
|
272
|
+
## 🛠️ Development Setup
|
273
|
+
|
274
|
+
To set up the development environment for contributing to DBViewer:
|
275
|
+
|
276
|
+
### Quick Setup
|
277
|
+
|
278
|
+
Run the setup script to automatically configure your development environment:
|
279
|
+
|
280
|
+
```bash
|
281
|
+
bin/setup
|
282
|
+
```
|
283
|
+
|
284
|
+
This script will:
|
285
|
+
|
286
|
+
- Install bundler and gem dependencies
|
287
|
+
- Set up the test dummy Rails application
|
288
|
+
- Create and seed the development database
|
289
|
+
- Prepare the test environment
|
290
|
+
- Clean up old logs and temporary files
|
291
|
+
|
292
|
+
### Manual Setup
|
293
|
+
|
294
|
+
If you prefer to set up manually:
|
295
|
+
|
296
|
+
```bash
|
297
|
+
# Install dependencies
|
298
|
+
bundle install
|
299
|
+
|
300
|
+
# Set up the dummy app database
|
301
|
+
cd test/dummy
|
302
|
+
bin/rails db:prepare
|
303
|
+
bin/rails db:migrate
|
304
|
+
bin/rails db:seed
|
305
|
+
cd ../..
|
306
|
+
|
307
|
+
# Prepare test environment
|
308
|
+
cd test/dummy && bin/rails db:test:prepare && cd ../..
|
309
|
+
```
|
310
|
+
|
311
|
+
### Development Commands
|
312
|
+
|
313
|
+
```bash
|
314
|
+
# Start the development server
|
315
|
+
cd test/dummy && bin/rails server
|
316
|
+
|
317
|
+
# Run tests
|
318
|
+
bundle exec rspec
|
319
|
+
|
320
|
+
# Run code quality checks
|
321
|
+
bin/rubocop
|
322
|
+
|
323
|
+
# Open an interactive console
|
324
|
+
bin/console
|
325
|
+
|
326
|
+
# Build the gem
|
327
|
+
gem build dbviewer.gemspec
|
328
|
+
```
|
329
|
+
|
330
|
+
### Testing Your Changes
|
331
|
+
|
332
|
+
1. Start the dummy Rails application: `cd test/dummy && bin/rails server`
|
333
|
+
2. Visit `http://localhost:3000/dbviewer` to test your changes
|
334
|
+
3. The dummy app includes sample data across multiple tables to test various DBViewer features
|
335
|
+
|
336
|
+
### Architecture Diagram
|
337
|
+
```mermaid
|
338
|
+
graph TB
|
339
|
+
subgraph "DBViewer Engine"
|
340
|
+
Engine[Engine<br/>Rails::Engine]
|
341
|
+
Config[Configuration<br/>Settings & Defaults]
|
342
|
+
SqlValidator[SqlValidator<br/>Query Validation]
|
343
|
+
end
|
344
|
+
|
345
|
+
subgraph "Controllers Layer"
|
346
|
+
HomeController[HomeController<br/>Dashboard & Overview]
|
347
|
+
TablesController[TablesController<br/>Table Operations]
|
348
|
+
LogsController[LogsController<br/>Query Logs]
|
349
|
+
ERDController[ERDController<br/>Entity Relationships]
|
350
|
+
APIController[API Controllers<br/>JSON Endpoints]
|
351
|
+
end
|
352
|
+
|
353
|
+
subgraph "Database Namespace"
|
354
|
+
Manager[Manager<br/>Database Operations]
|
355
|
+
CacheManager[CacheManager<br/>Caching Layer]
|
356
|
+
MetadataManager[MetadataManager<br/>Schema Information]
|
357
|
+
DynamicModelFactory[DynamicModelFactory<br/>ActiveRecord Models]
|
358
|
+
end
|
359
|
+
|
360
|
+
subgraph "Query Namespace"
|
361
|
+
QueryExecutor[Executor<br/>SQL Execution]
|
362
|
+
QueryLogger[Logger<br/>Query Logging]
|
363
|
+
QueryAnalyzer[Analyzer<br/>Performance Analysis]
|
364
|
+
QueryParser[Parser<br/>SQL Parsing]
|
365
|
+
end
|
366
|
+
|
367
|
+
subgraph "Datatable Namespace"
|
368
|
+
QueryOperations[QueryOperations<br/>Table Queries]
|
369
|
+
QueryParams[QueryParams<br/>Parameter Handling]
|
370
|
+
end
|
371
|
+
|
372
|
+
subgraph "Storage Namespace"
|
373
|
+
StorageBase[Base<br/>Storage Interface]
|
374
|
+
InMemoryStorage[InMemoryStorage<br/>Memory Storage]
|
375
|
+
FileStorage[FileStorage<br/>File Storage]
|
376
|
+
end
|
377
|
+
|
378
|
+
%% Configuration Dependencies (Decoupled)
|
379
|
+
Config -.->|"Dependency Injection"| Manager
|
380
|
+
Manager -->|"cache_expiry"| CacheManager
|
381
|
+
Manager -->|"config object"| QueryExecutor
|
382
|
+
|
383
|
+
%% Main Dependencies
|
384
|
+
Engine --> HomeController
|
385
|
+
Engine --> TablesController
|
386
|
+
Engine --> LogsController
|
387
|
+
Engine --> ERDController
|
388
|
+
|
389
|
+
Manager --> CacheManager
|
390
|
+
Manager --> MetadataManager
|
391
|
+
Manager --> DynamicModelFactory
|
392
|
+
Manager --> QueryOperations
|
393
|
+
|
394
|
+
CacheManager --> DynamicModelFactory
|
395
|
+
CacheManager --> MetadataManager
|
396
|
+
|
397
|
+
QueryOperations --> DynamicModelFactory
|
398
|
+
QueryOperations --> QueryExecutor
|
399
|
+
QueryOperations --> MetadataManager
|
400
|
+
|
401
|
+
QueryLogger --> StorageBase
|
402
|
+
StorageBase --> InMemoryStorage
|
403
|
+
StorageBase --> FileStorage
|
404
|
+
|
405
|
+
TablesController --> Manager
|
406
|
+
HomeController --> Manager
|
407
|
+
LogsController --> QueryLogger
|
408
|
+
APIController --> Manager
|
409
|
+
APIController --> QueryLogger
|
410
|
+
|
411
|
+
%% Decoupled Configuration Flow
|
412
|
+
Engine -.->|"setup()"| QueryLogger
|
413
|
+
Config -.->|"logging settings"| QueryLogger
|
414
|
+
|
415
|
+
classDef decoupled fill:#e1f5fe,stroke:#01579b,stroke-width:2px
|
416
|
+
classDef controller fill:#f3e5f5,stroke:#4a148c,stroke-width:2px
|
417
|
+
classDef database fill:#e8f5e8,stroke:#1b5e20,stroke-width:2px
|
418
|
+
classDef query fill:#fff3e0,stroke:#e65100,stroke-width:2px
|
419
|
+
classDef storage fill:#fce4ec,stroke:#880e4f,stroke-width:2px
|
420
|
+
|
421
|
+
class CacheManager,QueryLogger decoupled
|
422
|
+
class HomeController,TablesController,LogsController,ERDController,APIController controller
|
423
|
+
class Manager,MetadataManager,DynamicModelFactory database
|
424
|
+
class QueryExecutor,QueryAnalyzer,QueryParser query
|
425
|
+
class StorageBase,InMemoryStorage,FileStorage storage
|
426
|
+
```
|
427
|
+
|
273
428
|
## 🤌🏻 Contributing
|
274
429
|
|
275
430
|
Bug reports and pull requests are welcome.
|
@@ -10,7 +10,7 @@ module Dbviewer
|
|
10
10
|
|
11
11
|
# Initialize the database manager
|
12
12
|
def database_manager
|
13
|
-
@database_manager ||= ::Dbviewer::
|
13
|
+
@database_manager ||= ::Dbviewer::Database::Manager.new
|
14
14
|
end
|
15
15
|
|
16
16
|
# Initialize the table query operations manager
|
@@ -46,23 +46,15 @@ module Dbviewer
|
|
46
46
|
|
47
47
|
# Get the name of the current database adapter
|
48
48
|
def get_adapter_name
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
"
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|
49
|
+
adapter_name = database_manager.connection.adapter_name.downcase
|
50
|
+
adapter_mappings = {
|
51
|
+
/mysql/i => "MySQL",
|
52
|
+
/postgres/i => "PostgreSQL",
|
53
|
+
/sqlite/i => "SQLite",
|
54
|
+
/oracle/i => "Oracle",
|
55
|
+
/sqlserver|mssql/i => "SQL Server"
|
56
|
+
}
|
57
|
+
adapter_mappings.find { |pattern, _| adapter_name =~ pattern }&.last || adapter_name.titleize
|
66
58
|
rescue => e
|
67
59
|
Rails.logger.error("Error retrieving adapter name: #{e.message}")
|
68
60
|
"Unknown"
|
@@ -328,7 +320,7 @@ module Dbviewer
|
|
328
320
|
|
329
321
|
# Export table data to CSV
|
330
322
|
def export_table_to_csv(table_name, query_params = nil, include_headers = true)
|
331
|
-
records = database_manager.
|
323
|
+
records = database_manager.table_query_operations.table_records(table_name, query_params)
|
332
324
|
|
333
325
|
csv_data = CSV.generate do |csv|
|
334
326
|
# Add headers if requested
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Dbviewer
|
2
|
+
module Api
|
3
|
+
class EntityRelationshipDiagramsController < BaseController
|
4
|
+
before_action :set_tables
|
5
|
+
|
6
|
+
def relationships
|
7
|
+
# Fetch all relationships asynchronously
|
8
|
+
begin
|
9
|
+
@table_relationships = fetch_table_relationships
|
10
|
+
render_success({
|
11
|
+
relationships: @table_relationships,
|
12
|
+
status: "success"
|
13
|
+
})
|
14
|
+
rescue => e
|
15
|
+
Rails.logger.error("[DBViewer] Error fetching relationships: #{e.message}")
|
16
|
+
render json: {
|
17
|
+
relationships: [],
|
18
|
+
status: "error",
|
19
|
+
error: e.message
|
20
|
+
}, status: :internal_server_error
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def table_relationships
|
25
|
+
# Fetch relationships for specific tables
|
26
|
+
table_names = params[:tables]&.split(",") || []
|
27
|
+
|
28
|
+
if table_names.blank?
|
29
|
+
render json: {
|
30
|
+
relationships: [],
|
31
|
+
status: "error",
|
32
|
+
error: "No tables specified"
|
33
|
+
}, status: :bad_request
|
34
|
+
return
|
35
|
+
end
|
36
|
+
|
37
|
+
begin
|
38
|
+
relationships = []
|
39
|
+
|
40
|
+
table_names.each do |table_name|
|
41
|
+
next unless @tables.any? { |t| t[:name] == table_name }
|
42
|
+
|
43
|
+
begin
|
44
|
+
metadata = fetch_table_metadata(table_name)
|
45
|
+
if metadata && metadata[:foreign_keys].present?
|
46
|
+
metadata[:foreign_keys].each do |fk|
|
47
|
+
relationships << {
|
48
|
+
from_table: table_name,
|
49
|
+
to_table: fk[:to_table],
|
50
|
+
from_column: fk[:column],
|
51
|
+
to_column: fk[:primary_key],
|
52
|
+
name: fk[:name]
|
53
|
+
}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
rescue => e
|
57
|
+
Rails.logger.error("[DBViewer] Error fetching relationships for #{table_name}: #{e.message}")
|
58
|
+
# Continue with other tables even if one fails
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
render_success({
|
63
|
+
relationships: relationships,
|
64
|
+
status: "success",
|
65
|
+
processed_tables: table_names
|
66
|
+
})
|
67
|
+
rescue => e
|
68
|
+
Rails.logger.error("[DBViewer] Error in table_relationships: #{e.message}")
|
69
|
+
render json: {
|
70
|
+
relationships: [],
|
71
|
+
status: "error",
|
72
|
+
error: e.message
|
73
|
+
}, status: :internal_server_error
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def set_tables
|
80
|
+
@tables = fetch_tables
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -13,7 +13,7 @@ module Dbviewer
|
|
13
13
|
def fetch_recent_queries
|
14
14
|
return [] unless query_logging_enabled?
|
15
15
|
|
16
|
-
Dbviewer::Logger.instance.recent_queries(limit: queries_limit)
|
16
|
+
Dbviewer::Query::Logger.instance.recent_queries(limit: queries_limit)
|
17
17
|
end
|
18
18
|
|
19
19
|
def query_logging_enabled?
|
@@ -1,19 +1,18 @@
|
|
1
1
|
module Dbviewer
|
2
2
|
class EntityRelationshipDiagramsController < ApplicationController
|
3
3
|
def index
|
4
|
-
if
|
5
|
-
|
6
|
-
else
|
7
|
-
@table_relationships = []
|
4
|
+
# Only show warning if no tables exist, but don't fetch relationships on initial load
|
5
|
+
if @tables.blank?
|
8
6
|
flash.now[:warning] = "No tables found in database to generate ERD."
|
9
7
|
end
|
10
8
|
|
11
9
|
respond_to do |format|
|
12
|
-
format.html
|
10
|
+
format.html # Just render the HTML without relationships
|
13
11
|
format.json do
|
12
|
+
# For JSON requests, return just tables initially
|
14
13
|
render json: {
|
15
14
|
tables: @tables,
|
16
|
-
relationships:
|
15
|
+
relationships: []
|
17
16
|
}
|
18
17
|
end
|
19
18
|
end
|
@@ -11,7 +11,7 @@ module Dbviewer
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def show
|
14
|
-
query_params = Dbviewer::
|
14
|
+
query_params = Dbviewer::Datatable::QueryParams.new(
|
15
15
|
page: @current_page,
|
16
16
|
per_page: @per_page,
|
17
17
|
order_by: @order_by,
|
@@ -29,12 +29,6 @@ module Dbviewer
|
|
29
29
|
@records = ActiveRecord::Result.new(column_names, [])
|
30
30
|
end
|
31
31
|
|
32
|
-
# Fetch timestamp visualization data if the table has a created_at column
|
33
|
-
if has_timestamp_column?(@table_name)
|
34
|
-
@time_grouping = params[:time_group] || "daily"
|
35
|
-
@timestamp_data = fetch_timestamp_data(@table_name, @time_grouping)
|
36
|
-
end
|
37
|
-
|
38
32
|
respond_to do |format|
|
39
33
|
format.html # Default HTML response
|
40
34
|
format.json do
|
@@ -76,7 +70,7 @@ module Dbviewer
|
|
76
70
|
end
|
77
71
|
|
78
72
|
include_headers = params[:include_headers] != "0"
|
79
|
-
query_params = Dbviewer::
|
73
|
+
query_params = Dbviewer::Datatable::QueryParams.new(
|
80
74
|
page: @current_page,
|
81
75
|
per_page: (params[:limit] || 10000).to_i,
|
82
76
|
order_by: @order_by,
|