dbviewer 0.5.2 → 0.5.4
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 +92 -0
- 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 +232 -141
- 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 +1 -1
- 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 -77
- 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 -53
- 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
@@ -1,91 +0,0 @@
|
|
1
|
-
module Dbviewer
|
2
|
-
# QueryExecutor handles executing SQL queries and formatting results
|
3
|
-
class QueryExecutor
|
4
|
-
# Initialize with a connection and configuration
|
5
|
-
# @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] Database connection
|
6
|
-
# @param config [Dbviewer::Configuration] Configuration object
|
7
|
-
def initialize(connection, config = nil)
|
8
|
-
@connection = connection
|
9
|
-
@config = config || Dbviewer.configuration
|
10
|
-
end
|
11
|
-
|
12
|
-
# Execute a raw SQL query after validating for safety
|
13
|
-
# @param sql [String] SQL query to execute
|
14
|
-
# @return [ActiveRecord::Result] Result set with columns and rows
|
15
|
-
# @raise [StandardError] If the query is invalid or unsafe
|
16
|
-
def execute_query(sql)
|
17
|
-
# Validate and normalize the SQL
|
18
|
-
normalized_sql = ::Dbviewer::SqlValidator.validate!(sql.to_s)
|
19
|
-
|
20
|
-
# Get max records from configuration
|
21
|
-
max_records = @config.max_records || 10000
|
22
|
-
|
23
|
-
# Add a safety limit if not already present
|
24
|
-
unless normalized_sql =~ /\bLIMIT\s+\d+\s*$/i
|
25
|
-
normalized_sql = "#{normalized_sql} LIMIT #{max_records}"
|
26
|
-
end
|
27
|
-
|
28
|
-
# Log and execute the query
|
29
|
-
Rails.logger.debug("[DBViewer] Executing SQL query: #{normalized_sql}")
|
30
|
-
start_time = Time.now
|
31
|
-
result = @connection.exec_query(normalized_sql)
|
32
|
-
duration = Time.now - start_time
|
33
|
-
|
34
|
-
Rails.logger.debug("[DBViewer] Query completed in #{duration.round(2)}s, returned #{result.rows.size} rows")
|
35
|
-
result
|
36
|
-
rescue => e
|
37
|
-
Rails.logger.error("[DBViewer] SQL query error: #{e.message} for query: #{sql}")
|
38
|
-
raise e
|
39
|
-
end
|
40
|
-
|
41
|
-
# Execute a SQLite PRAGMA command without adding a LIMIT clause
|
42
|
-
# @param pragma [String] PRAGMA command to execute (without the "PRAGMA" keyword)
|
43
|
-
# @return [ActiveRecord::Result] Result set with the PRAGMA value
|
44
|
-
# @raise [StandardError] If the query is invalid or cannot be executed
|
45
|
-
def execute_sqlite_pragma(pragma)
|
46
|
-
sql = "PRAGMA #{pragma}"
|
47
|
-
Rails.logger.debug("[DBViewer] Executing SQLite pragma: #{sql}")
|
48
|
-
result = @connection.exec_query(sql)
|
49
|
-
Rails.logger.debug("[DBViewer] Pragma completed, returned #{result.rows.size} rows")
|
50
|
-
result
|
51
|
-
rescue => e
|
52
|
-
Rails.logger.error("[DBViewer] SQLite pragma error: #{e.message} for pragma: #{pragma}")
|
53
|
-
raise e
|
54
|
-
end
|
55
|
-
|
56
|
-
# Convert ActiveRecord::Relation to a standard result format
|
57
|
-
# @param records [ActiveRecord::Relation] Records to convert
|
58
|
-
# @param column_names [Array<String>] Column names
|
59
|
-
# @return [ActiveRecord::Result] Result set with columns and rows
|
60
|
-
def to_result_set(records, column_names)
|
61
|
-
rows = records.map do |record|
|
62
|
-
column_names.map do |col|
|
63
|
-
# Handle serialized attributes
|
64
|
-
value = record[col]
|
65
|
-
serialize_if_needed(value)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
ActiveRecord::Result.new(column_names, rows)
|
70
|
-
rescue => e
|
71
|
-
Rails.logger.error("[DBViewer] Error converting to result set: #{e.message}")
|
72
|
-
ActiveRecord::Result.new([], [])
|
73
|
-
end
|
74
|
-
|
75
|
-
private
|
76
|
-
|
77
|
-
# Serialize complex objects for display
|
78
|
-
# @param value [Object] Value to serialize
|
79
|
-
# @return [String, Object] Serialized value or original value
|
80
|
-
def serialize_if_needed(value)
|
81
|
-
case value
|
82
|
-
when Hash, Array
|
83
|
-
value.to_json rescue value.to_s
|
84
|
-
when Time, Date, DateTime
|
85
|
-
value.to_s
|
86
|
-
else
|
87
|
-
value
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
@@ -1,53 +0,0 @@
|
|
1
|
-
module Dbviewer
|
2
|
-
# QueryParser handles parsing SQL queries and extracting useful information
|
3
|
-
class QueryParser
|
4
|
-
# Extract table names from an SQL query string
|
5
|
-
def self.extract_tables(sql)
|
6
|
-
return [] if sql.nil?
|
7
|
-
|
8
|
-
# Convert to lowercase for case-insensitive matching
|
9
|
-
sql = sql.downcase
|
10
|
-
|
11
|
-
# Extract table names after FROM or JOIN
|
12
|
-
sql.scan(/(?:from|join)\s+[`"']?(\w+)[`"']?/).flatten.uniq
|
13
|
-
end
|
14
|
-
|
15
|
-
# Normalize a SQL query to find similar patterns
|
16
|
-
# Replaces specific values with placeholders
|
17
|
-
def self.normalize(sql)
|
18
|
-
return "" if sql.nil?
|
19
|
-
|
20
|
-
sql.gsub(/\b\d+\b/, "N")
|
21
|
-
.gsub(/'[^']*'/, "'X'")
|
22
|
-
.gsub(/"[^"]*"/, '"X"')
|
23
|
-
end
|
24
|
-
|
25
|
-
# Format bind parameters for storage
|
26
|
-
def self.format_binds(binds)
|
27
|
-
return [] unless binds.respond_to?(:map)
|
28
|
-
|
29
|
-
binds.map do |bind|
|
30
|
-
if bind.respond_to?(:value)
|
31
|
-
bind.value
|
32
|
-
elsif bind.is_a?(Array) && bind.size == 2
|
33
|
-
bind.last
|
34
|
-
else
|
35
|
-
bind.to_s
|
36
|
-
end
|
37
|
-
end
|
38
|
-
rescue
|
39
|
-
[]
|
40
|
-
end
|
41
|
-
|
42
|
-
# Determine if a query should be skipped based on content
|
43
|
-
# Rails and ActiveRecord often run internal queries that are not useful for logging
|
44
|
-
def self.should_skip_query?(event)
|
45
|
-
event.payload[:name] == "SCHEMA" ||
|
46
|
-
event.payload[:sql].include?("SHOW TABLES") ||
|
47
|
-
event.payload[:sql].include?("sqlite_master") ||
|
48
|
-
event.payload[:sql].include?("information_schema") ||
|
49
|
-
event.payload[:sql].include?("schema_migrations") ||
|
50
|
-
event.payload[:sql].include?("pg_catalog")
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
@@ -1,136 +0,0 @@
|
|
1
|
-
module Dbviewer
|
2
|
-
# TableMetadataManager handles retrieving and managing table structure information
|
3
|
-
class TableMetadataManager
|
4
|
-
# Initialize with a connection and cache manager
|
5
|
-
# @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] Database connection
|
6
|
-
# @param cache_manager [Dbviewer::CacheManager] Cache manager instance
|
7
|
-
def initialize(connection, cache_manager)
|
8
|
-
@connection = connection
|
9
|
-
@cache_manager = cache_manager
|
10
|
-
end
|
11
|
-
|
12
|
-
# Get all tables in the database
|
13
|
-
# @return [Array<String>] List of table names
|
14
|
-
def tables
|
15
|
-
@connection.tables.sort
|
16
|
-
end
|
17
|
-
|
18
|
-
# Get column information for a table
|
19
|
-
# @param table_name [String] Name of the table
|
20
|
-
# @return [Array<Hash>] List of column details
|
21
|
-
def table_columns(table_name)
|
22
|
-
cached_columns = @cache_manager.get_columns(table_name)
|
23
|
-
return cached_columns if cached_columns
|
24
|
-
|
25
|
-
columns = @connection.columns(table_name).map do |column|
|
26
|
-
{
|
27
|
-
name: column.name,
|
28
|
-
type: column.type,
|
29
|
-
null: column.null,
|
30
|
-
default: column.default,
|
31
|
-
primary: column.name == primary_key(table_name)
|
32
|
-
}
|
33
|
-
end
|
34
|
-
|
35
|
-
# Cache the result
|
36
|
-
@cache_manager.store_columns(table_name, columns)
|
37
|
-
columns
|
38
|
-
end
|
39
|
-
|
40
|
-
# Get detailed metadata about a table
|
41
|
-
# @param table_name [String] Name of the table
|
42
|
-
# @return [Hash] Table metadata
|
43
|
-
def table_metadata(table_name)
|
44
|
-
cached_metadata = @cache_manager.get_metadata(table_name)
|
45
|
-
return cached_metadata if cached_metadata
|
46
|
-
|
47
|
-
metadata = {
|
48
|
-
primary_key: primary_key(table_name),
|
49
|
-
indexes: fetch_indexes(table_name),
|
50
|
-
foreign_keys: fetch_foreign_keys(table_name),
|
51
|
-
reverse_foreign_keys: fetch_reverse_foreign_keys(table_name)
|
52
|
-
}
|
53
|
-
|
54
|
-
@cache_manager.store_metadata(table_name, metadata)
|
55
|
-
metadata
|
56
|
-
end
|
57
|
-
|
58
|
-
# Get the primary key of a table
|
59
|
-
# @param table_name [String] Name of the table
|
60
|
-
# @return [String, nil] Primary key column name or nil if not found
|
61
|
-
def primary_key(table_name)
|
62
|
-
@connection.primary_key(table_name)
|
63
|
-
rescue => e
|
64
|
-
Rails.logger.error("[DBViewer] Error retrieving primary key for table #{table_name}: #{e.message}")
|
65
|
-
nil
|
66
|
-
end
|
67
|
-
|
68
|
-
# Check if a column exists in a table
|
69
|
-
# @param table_name [String] Name of the table
|
70
|
-
# @param column_name [String] Name of the column
|
71
|
-
# @return [Boolean] true if column exists, false otherwise
|
72
|
-
def column_exists?(table_name, column_name)
|
73
|
-
columns = table_columns(table_name)
|
74
|
-
columns.any? { |col| col[:name].to_s == column_name.to_s }
|
75
|
-
end
|
76
|
-
|
77
|
-
# Get table indexes
|
78
|
-
# @param table_name [String] Name of the table
|
79
|
-
# @return [Array<Hash>] List of indexes with details
|
80
|
-
def fetch_indexes(table_name)
|
81
|
-
@connection.indexes(table_name).map do |index|
|
82
|
-
{
|
83
|
-
name: index.name,
|
84
|
-
columns: index.columns,
|
85
|
-
unique: index.unique
|
86
|
-
}
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
# Get foreign keys
|
91
|
-
# @param table_name [String] Name of the table
|
92
|
-
# @return [Array<Hash>] List of foreign keys with details
|
93
|
-
def fetch_foreign_keys(table_name)
|
94
|
-
@connection.foreign_keys(table_name).map do |fk|
|
95
|
-
{
|
96
|
-
name: fk.name,
|
97
|
-
from_table: fk.from_table,
|
98
|
-
to_table: fk.to_table,
|
99
|
-
column: fk.column,
|
100
|
-
primary_key: fk.primary_key
|
101
|
-
}
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
# Get reverse foreign keys (tables that reference this table)
|
106
|
-
# @param table_name [String] Name of the table
|
107
|
-
# @return [Array<Hash>] List of reverse foreign keys with details
|
108
|
-
def fetch_reverse_foreign_keys(table_name)
|
109
|
-
reverse_fks = []
|
110
|
-
|
111
|
-
# Get all tables and check their foreign keys
|
112
|
-
tables.each do |other_table|
|
113
|
-
next if other_table == table_name
|
114
|
-
|
115
|
-
begin
|
116
|
-
@connection.foreign_keys(other_table).each do |fk|
|
117
|
-
if fk.to_table == table_name
|
118
|
-
reverse_fks << {
|
119
|
-
name: fk.name,
|
120
|
-
from_table: fk.from_table,
|
121
|
-
to_table: fk.to_table,
|
122
|
-
column: fk.column,
|
123
|
-
primary_key: fk.primary_key,
|
124
|
-
relationship_type: "has_many"
|
125
|
-
}
|
126
|
-
end
|
127
|
-
end
|
128
|
-
rescue => e
|
129
|
-
Rails.logger.error("[DBViewer] Error retrieving foreign keys for table #{other_table}: #{e.message}")
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
reverse_fks
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|