dbviewer 0.5.2 → 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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +92 -0
  3. data/app/controllers/concerns/dbviewer/database_operations.rb +11 -19
  4. data/app/controllers/dbviewer/api/entity_relationship_diagrams_controller.rb +84 -0
  5. data/app/controllers/dbviewer/api/queries_controller.rb +1 -1
  6. data/app/controllers/dbviewer/entity_relationship_diagrams_controller.rb +5 -6
  7. data/app/controllers/dbviewer/logs_controller.rb +1 -1
  8. data/app/controllers/dbviewer/tables_controller.rb +2 -8
  9. data/app/helpers/dbviewer/application_helper.rb +1 -1
  10. data/app/views/dbviewer/entity_relationship_diagrams/index.html.erb +217 -100
  11. data/app/views/dbviewer/tables/show.html.erb +278 -404
  12. data/config/routes.rb +7 -0
  13. data/lib/dbviewer/database/cache_manager.rb +78 -0
  14. data/lib/dbviewer/database/dynamic_model_factory.rb +62 -0
  15. data/lib/dbviewer/database/manager.rb +204 -0
  16. data/lib/dbviewer/database/metadata_manager.rb +129 -0
  17. data/lib/dbviewer/datatable/query_operations.rb +330 -0
  18. data/lib/dbviewer/datatable/query_params.rb +41 -0
  19. data/lib/dbviewer/engine.rb +1 -1
  20. data/lib/dbviewer/query/analyzer.rb +250 -0
  21. data/lib/dbviewer/query/collection.rb +39 -0
  22. data/lib/dbviewer/query/executor.rb +93 -0
  23. data/lib/dbviewer/query/logger.rb +108 -0
  24. data/lib/dbviewer/query/parser.rb +56 -0
  25. data/lib/dbviewer/storage/file_storage.rb +0 -3
  26. data/lib/dbviewer/version.rb +1 -1
  27. data/lib/dbviewer.rb +24 -7
  28. metadata +14 -14
  29. data/lib/dbviewer/cache_manager.rb +0 -78
  30. data/lib/dbviewer/database_manager.rb +0 -249
  31. data/lib/dbviewer/dynamic_model_factory.rb +0 -60
  32. data/lib/dbviewer/error_handler.rb +0 -18
  33. data/lib/dbviewer/logger.rb +0 -77
  34. data/lib/dbviewer/query_analyzer.rb +0 -239
  35. data/lib/dbviewer/query_collection.rb +0 -37
  36. data/lib/dbviewer/query_executor.rb +0 -91
  37. data/lib/dbviewer/query_parser.rb +0 -53
  38. data/lib/dbviewer/table_metadata_manager.rb +0 -136
  39. data/lib/dbviewer/table_query_operations.rb +0 -621
  40. 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