rails_lens 0.0.0 → 0.2.2

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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +23 -0
  3. data/LICENSE.txt +2 -2
  4. data/README.md +463 -9
  5. data/exe/rails_lens +25 -0
  6. data/lib/rails_lens/analyzers/association_analyzer.rb +111 -0
  7. data/lib/rails_lens/analyzers/base.rb +35 -0
  8. data/lib/rails_lens/analyzers/best_practices_analyzer.rb +114 -0
  9. data/lib/rails_lens/analyzers/column_analyzer.rb +97 -0
  10. data/lib/rails_lens/analyzers/composite_keys.rb +62 -0
  11. data/lib/rails_lens/analyzers/database_constraints.rb +35 -0
  12. data/lib/rails_lens/analyzers/delegated_types.rb +129 -0
  13. data/lib/rails_lens/analyzers/enums.rb +34 -0
  14. data/lib/rails_lens/analyzers/error_handling.rb +66 -0
  15. data/lib/rails_lens/analyzers/foreign_key_analyzer.rb +47 -0
  16. data/lib/rails_lens/analyzers/generated_columns.rb +56 -0
  17. data/lib/rails_lens/analyzers/index_analyzer.rb +128 -0
  18. data/lib/rails_lens/analyzers/inheritance.rb +212 -0
  19. data/lib/rails_lens/analyzers/notes.rb +325 -0
  20. data/lib/rails_lens/analyzers/performance_analyzer.rb +110 -0
  21. data/lib/rails_lens/annotation_pipeline.rb +87 -0
  22. data/lib/rails_lens/cli.rb +176 -0
  23. data/lib/rails_lens/cli_error_handler.rb +86 -0
  24. data/lib/rails_lens/commands.rb +164 -0
  25. data/lib/rails_lens/connection.rb +133 -0
  26. data/lib/rails_lens/erd/column_type_formatter.rb +32 -0
  27. data/lib/rails_lens/erd/domain_color_mapper.rb +40 -0
  28. data/lib/rails_lens/erd/mysql_column_type_formatter.rb +19 -0
  29. data/lib/rails_lens/erd/postgresql_column_type_formatter.rb +19 -0
  30. data/lib/rails_lens/erd/visualizer.rb +329 -0
  31. data/lib/rails_lens/errors.rb +78 -0
  32. data/lib/rails_lens/extension_loader.rb +261 -0
  33. data/lib/rails_lens/extensions/base.rb +194 -0
  34. data/lib/rails_lens/extensions/closure_tree_ext.rb +157 -0
  35. data/lib/rails_lens/file_insertion_helper.rb +168 -0
  36. data/lib/rails_lens/mailer/annotator.rb +226 -0
  37. data/lib/rails_lens/mailer/extractor.rb +201 -0
  38. data/lib/rails_lens/model_detector.rb +252 -0
  39. data/lib/rails_lens/parsers/class_info.rb +46 -0
  40. data/lib/rails_lens/parsers/module_info.rb +33 -0
  41. data/lib/rails_lens/parsers/parser_result.rb +55 -0
  42. data/lib/rails_lens/parsers/prism_parser.rb +90 -0
  43. data/lib/rails_lens/parsers.rb +10 -0
  44. data/lib/rails_lens/providers/association_notes_provider.rb +11 -0
  45. data/lib/rails_lens/providers/base.rb +37 -0
  46. data/lib/rails_lens/providers/best_practices_notes_provider.rb +11 -0
  47. data/lib/rails_lens/providers/column_notes_provider.rb +11 -0
  48. data/lib/rails_lens/providers/composite_keys_provider.rb +11 -0
  49. data/lib/rails_lens/providers/database_constraints_provider.rb +11 -0
  50. data/lib/rails_lens/providers/delegated_types_provider.rb +11 -0
  51. data/lib/rails_lens/providers/enums_provider.rb +11 -0
  52. data/lib/rails_lens/providers/extension_notes_provider.rb +20 -0
  53. data/lib/rails_lens/providers/extensions_provider.rb +22 -0
  54. data/lib/rails_lens/providers/foreign_key_notes_provider.rb +11 -0
  55. data/lib/rails_lens/providers/generated_columns_provider.rb +11 -0
  56. data/lib/rails_lens/providers/index_notes_provider.rb +20 -0
  57. data/lib/rails_lens/providers/inheritance_provider.rb +23 -0
  58. data/lib/rails_lens/providers/notes_provider_base.rb +25 -0
  59. data/lib/rails_lens/providers/performance_notes_provider.rb +11 -0
  60. data/lib/rails_lens/providers/schema_provider.rb +61 -0
  61. data/lib/rails_lens/providers/section_provider_base.rb +28 -0
  62. data/lib/rails_lens/railtie.rb +17 -0
  63. data/lib/rails_lens/rake_bootstrapper.rb +18 -0
  64. data/lib/rails_lens/route/annotator.rb +268 -0
  65. data/lib/rails_lens/route/extractor.rb +133 -0
  66. data/lib/rails_lens/route/parser.rb +59 -0
  67. data/lib/rails_lens/schema/adapters/base.rb +345 -0
  68. data/lib/rails_lens/schema/adapters/database_info.rb +118 -0
  69. data/lib/rails_lens/schema/adapters/mysql.rb +279 -0
  70. data/lib/rails_lens/schema/adapters/postgresql.rb +197 -0
  71. data/lib/rails_lens/schema/adapters/sqlite3.rb +96 -0
  72. data/lib/rails_lens/schema/annotation.rb +144 -0
  73. data/lib/rails_lens/schema/annotation_manager.rb +202 -0
  74. data/lib/rails_lens/tasks/annotate.rake +35 -0
  75. data/lib/rails_lens/tasks/erd.rake +24 -0
  76. data/lib/rails_lens/tasks/mailers.rake +27 -0
  77. data/lib/rails_lens/tasks/routes.rake +27 -0
  78. data/lib/rails_lens/tasks/schema.rake +108 -0
  79. data/lib/rails_lens/version.rb +5 -0
  80. data/lib/rails_lens.rb +138 -5
  81. metadata +215 -11
@@ -0,0 +1,279 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsLens
4
+ module Schema
5
+ module Adapters
6
+ class Mysql < Base
7
+ def adapter_name
8
+ 'MySQL'
9
+ end
10
+
11
+ def generate_annotation(_model_class)
12
+ lines = []
13
+ lines << "table = \"#{table_name}\""
14
+ lines << "database_dialect = \"#{database_dialect}\""
15
+
16
+ # Add storage engine information
17
+ if (engine = table_storage_engine)
18
+ lines << "storage_engine = \"#{engine}\""
19
+ end
20
+
21
+ # Add character set and collation
22
+ if (charset = table_charset)
23
+ lines << "character_set = \"#{charset}\""
24
+ end
25
+
26
+ if (collation = table_collation)
27
+ lines << "collation = \"#{collation}\""
28
+ end
29
+
30
+ lines << ''
31
+
32
+ add_columns_toml(lines)
33
+ add_indexes_toml(lines) if show_indexes?
34
+ add_foreign_keys_toml(lines) if show_foreign_keys?
35
+ add_partitions_toml(lines) if has_partitions?
36
+
37
+ lines.join("\n")
38
+ end
39
+
40
+ protected
41
+
42
+ def format_column(column)
43
+ parts = []
44
+ parts << column.name.ljust(column_name_width)
45
+
46
+ # MySQL specific type formatting
47
+ type_string = format_column_type(column)
48
+ parts << ":#{type_string.ljust(12)}"
49
+
50
+ attributes = []
51
+ attributes << 'not null' unless column.null
52
+ attributes << 'primary key' if primary_key?(column)
53
+
54
+ # MySQL specific: show auto_increment
55
+ attributes << 'auto_increment' if primary_key?(column) && column.extra == 'auto_increment'
56
+
57
+ # Show character set and collation for string columns
58
+ if %i[string text].include?(column.type) && column.respond_to?(:charset)
59
+ attributes << "charset: #{column.charset}" if column.charset
60
+ attributes << "collation: #{column.collation}" if column.collation
61
+ end
62
+
63
+ attributes << "default: #{format_default(column.default)}" if column.default && show_defaults?
64
+
65
+ # Add column comment if available
66
+ if show_comments? && column.respond_to?(:comment) && column.comment
67
+ attributes << "comment: \"#{column.comment}\""
68
+ end
69
+
70
+ parts << attributes.join(', ') unless attributes.empty?
71
+
72
+ " #{parts.join(' ')}"
73
+ end
74
+
75
+ def format_column_type(column)
76
+ case column.type
77
+ when :string
78
+ column.limit ? "varchar(#{column.limit})" : 'varchar'
79
+ when :text
80
+ case column.limit
81
+ when 0..255 then 'tinytext'
82
+ when 256..65_535 then 'text'
83
+ when 65_536..16_777_215 then 'mediumtext'
84
+ else 'longtext'
85
+ end
86
+ when :binary
87
+ case column.limit
88
+ when 0..255 then 'tinyblob'
89
+ when 256..65_535 then 'blob'
90
+ when 65_536..16_777_215 then 'mediumblob'
91
+ else 'longblob'
92
+ end
93
+ when :integer
94
+ # MySQL integer types
95
+ case column.limit
96
+ when 1 then 'tinyint'
97
+ when 2 then 'smallint'
98
+ when 3 then 'mediumint'
99
+ when 8 then 'bigint'
100
+ else 'int'
101
+ end
102
+ else
103
+ column.sql_type || column.type.to_s
104
+ end
105
+ end
106
+
107
+ def table_storage_engine
108
+ result = connection.execute("SHOW TABLE STATUS LIKE '#{table_name}'").first
109
+ return nil unless result
110
+
111
+ # Handle both hash and array results from different MySQL adapters
112
+ if result.is_a?(Hash)
113
+ result['Engine']
114
+ elsif result.is_a?(Array)
115
+ result[1] # Engine is typically the second column
116
+ end
117
+ rescue ActiveRecord::StatementInvalid => e
118
+ Rails.logger.debug { "Failed to fetch storage engine for #{table_name}: #{e.message}" }
119
+ nil
120
+ rescue => e
121
+ Rails.logger.debug { "MySQL error fetching storage engine: #{e.message}" }
122
+ nil
123
+ end
124
+
125
+ def table_charset
126
+ result = connection.execute("SHOW TABLE STATUS LIKE '#{table_name}'").first
127
+ return nil unless result
128
+
129
+ # Handle both hash and array results from different MySQL adapters
130
+ collation = if result.is_a?(Hash)
131
+ result['Collation']
132
+ elsif result.is_a?(Array)
133
+ result[14] # Collation is typically the 15th column
134
+ end
135
+
136
+ collation&.split('_')&.first
137
+ rescue ActiveRecord::StatementInvalid => e
138
+ Rails.logger.debug { "Failed to fetch charset for #{table_name}: #{e.message}" }
139
+ nil
140
+ rescue => e
141
+ Rails.logger.debug { "MySQL error fetching charset: #{e.message}" }
142
+ nil
143
+ end
144
+
145
+ def table_collation
146
+ result = connection.execute("SHOW TABLE STATUS LIKE '#{table_name}'").first
147
+ return nil unless result
148
+
149
+ # Handle both hash and array results from different MySQL adapters
150
+ if result.is_a?(Hash)
151
+ result['Collation']
152
+ elsif result.is_a?(Array)
153
+ result[14] # Collation is typically the 15th column
154
+ end
155
+ rescue ActiveRecord::StatementInvalid => e
156
+ Rails.logger.debug { "Failed to fetch collation for #{table_name}: #{e.message}" }
157
+ nil
158
+ rescue => e
159
+ Rails.logger.debug { "MySQL error fetching collation: #{e.message}" }
160
+ nil
161
+ end
162
+
163
+ def format_index(index)
164
+ base = " #{index.name}"
165
+
166
+ columns = Array(index.columns).join(', ')
167
+ base += " (#{columns})"
168
+
169
+ attributes = []
170
+ attributes << 'UNIQUE' if index.unique
171
+ attributes << 'FULLTEXT' if index.type == 'FULLTEXT'
172
+ attributes << 'SPATIAL' if index.type == 'SPATIAL'
173
+
174
+ # Show index type (BTREE, HASH)
175
+ attributes << "USING #{index.using}" if index.respond_to?(:using) && index.using
176
+
177
+ base += " #{attributes.join(' ')}" unless attributes.empty?
178
+ base
179
+ end
180
+
181
+ def has_partitions?
182
+ return false unless connection.respond_to?(:execute)
183
+
184
+ result = connection.execute(<<-SQL.squish)
185
+ SELECT COUNT(*) as count
186
+ FROM information_schema.partitions
187
+ WHERE table_schema = DATABASE()
188
+ AND table_name = '#{table_name}'
189
+ AND partition_name IS NOT NULL
190
+ SQL
191
+
192
+ count = if result.first.is_a?(Hash)
193
+ result.first['count'] || result.first[:count]
194
+ elsif result.first.is_a?(Array)
195
+ result.first[0] # count is the first column
196
+ else
197
+ 0
198
+ end
199
+
200
+ count.to_i.positive?
201
+ rescue ActiveRecord::StatementInvalid => e
202
+ # Table doesn't exist or no permission to query information_schema
203
+ Rails.logger.debug { "Failed to check partitions for #{table_name}: #{e.message}" }
204
+ false
205
+ rescue => e
206
+ # MySQL specific errors (connection issues, etc)
207
+ Rails.logger.debug { "MySQL error checking partitions: #{e.message}" }
208
+ false
209
+ end
210
+
211
+ def add_partitions(lines)
212
+ return unless connection.respond_to?(:execute)
213
+
214
+ partitions = connection.execute(<<-SQL.squish)
215
+ SELECT partition_name, partition_expression, partition_description
216
+ FROM information_schema.partitions
217
+ WHERE table_schema = DATABASE()
218
+ AND table_name = '#{table_name}'
219
+ AND partition_name IS NOT NULL
220
+ ORDER BY partition_ordinal_position
221
+ SQL
222
+
223
+ return if partitions.none?
224
+
225
+ lines << '' unless lines.last && lines.last.empty?
226
+ lines << 'Partitions:'
227
+
228
+ partitions.each do |partition|
229
+ lines << " #{partition['partition_name']}: #{partition['partition_description']}"
230
+ end
231
+ rescue ActiveRecord::StatementInvalid => e
232
+ # Permission denied or table doesn't exist
233
+ Rails.logger.debug { "Failed to fetch partitions for #{table_name}: #{e.message}" }
234
+ rescue => e
235
+ # MySQL specific errors
236
+ Rails.logger.debug { "MySQL error fetching partitions: #{e.message}" }
237
+ end
238
+
239
+ def add_partitions_toml(lines)
240
+ return unless connection.respond_to?(:execute)
241
+
242
+ partitions = connection.execute(<<-SQL.squish)
243
+ SELECT partition_name, partition_expression, partition_description
244
+ FROM information_schema.partitions
245
+ WHERE table_schema = DATABASE()
246
+ AND table_name = '#{table_name}'
247
+ AND partition_name IS NOT NULL
248
+ ORDER BY partition_ordinal_position
249
+ SQL
250
+
251
+ return if partitions.none?
252
+
253
+ lines << ''
254
+ lines << 'partitions = ['
255
+
256
+ partitions.each_with_index do |partition, i|
257
+ line = ' { '
258
+ attrs = []
259
+ attrs << "name = \"#{partition['partition_name']}\""
260
+ attrs << "description = \"#{partition['partition_description']}\""
261
+ attrs << "expression = \"#{partition['partition_expression']}\"" if partition['partition_expression']
262
+ line += attrs.join(', ')
263
+ line += ' }'
264
+ line += ',' if i < partitions.count - 1
265
+ lines << line
266
+ end
267
+
268
+ lines << ']'
269
+ rescue ActiveRecord::StatementInvalid => e
270
+ # Permission denied or table doesn't exist
271
+ Rails.logger.debug { "Failed to fetch partitions for #{table_name}: #{e.message}" }
272
+ rescue => e
273
+ # MySQL specific errors
274
+ Rails.logger.debug { "MySQL error fetching partitions: #{e.message}" }
275
+ end
276
+ end
277
+ end
278
+ end
279
+ end
@@ -0,0 +1,197 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsLens
4
+ module Schema
5
+ module Adapters
6
+ class Postgresql < Base
7
+ def adapter_name
8
+ 'PostgreSQL'
9
+ end
10
+
11
+ def generate_annotation(_model_class)
12
+ lines = []
13
+ lines << "table = \"#{table_name}\""
14
+ lines << "database_dialect = \"#{database_dialect}\""
15
+
16
+ # Add schema information for PostgreSQL
17
+ lines << "schema = \"#{schema_name}\"" if schema_name && schema_name != 'public'
18
+ lines << ''
19
+
20
+ add_columns_toml(lines)
21
+ add_indexes_toml(lines) if show_indexes?
22
+ add_foreign_keys_toml(lines) if show_foreign_keys?
23
+ add_check_constraints_toml(lines) if show_check_constraints?
24
+ add_table_comment_toml(lines) if show_comments?
25
+
26
+ lines.join("\n")
27
+ end
28
+
29
+ protected
30
+
31
+ def schema_name
32
+ @schema_name ||= if table_name.include?('.')
33
+ table_name.split('.').first
34
+ elsif connection.respond_to?(:current_schema)
35
+ connection.current_schema
36
+ end
37
+ end
38
+
39
+ def format_column(column)
40
+ parts = []
41
+ parts << column.name.ljust(column_name_width)
42
+
43
+ # PostgreSQL specific type formatting
44
+ type_string = format_column_type(column)
45
+ parts << ":#{type_string.ljust(12)}"
46
+
47
+ attributes = []
48
+ attributes << 'not null' unless column.null
49
+ attributes << 'primary key' if primary_key?(column)
50
+
51
+ # Show sequence for serial columns
52
+ if column.default&.match?(/nextval/)
53
+ attributes << "default: nextval('#{extract_sequence_name(column.default)}')"
54
+ elsif column.default && show_defaults?
55
+ attributes << "default: #{format_default(column.default)}"
56
+ end
57
+
58
+ # Add column comment if available
59
+ if show_comments? && (comment = column_comment(column.name))
60
+ attributes << "comment: \"#{comment}\""
61
+ end
62
+
63
+ parts << attributes.join(', ') unless attributes.empty?
64
+
65
+ " #{parts.join(' ')}"
66
+ end
67
+
68
+ def format_column_type(column)
69
+ case column.type
70
+ when :string
71
+ column.limit ? "string(#{column.limit})" : 'string'
72
+ when :decimal
73
+ if column.precision && column.scale
74
+ "decimal(#{column.precision},#{column.scale})"
75
+ else
76
+ 'decimal'
77
+ end
78
+ when :integer
79
+ case column.limit
80
+ when 2 then 'smallint'
81
+ when 8 then 'bigint'
82
+ else 'integer'
83
+ end
84
+ else
85
+ column.sql_type || column.type.to_s
86
+ end
87
+ end
88
+
89
+ def extract_sequence_name(default_value)
90
+ default_value.match(/nextval\('([^']+)'/)&.captures&.first || 'sequence'
91
+ end
92
+
93
+ def format_foreign_key(fk)
94
+ base = " #{fk.name} (#{fk.column} => #{fk.to_table}.#{fk.primary_key})"
95
+
96
+ # Add cascade options
97
+ options = []
98
+ options << "ON DELETE #{fk.on_delete.upcase}" if fk.on_delete
99
+ options << "ON UPDATE #{fk.on_update.upcase}" if fk.on_update
100
+
101
+ base += " #{options.join(' ')}" unless options.empty?
102
+ base
103
+ end
104
+
105
+ def fetch_check_constraints
106
+ return [] unless connection.supports_check_constraints?
107
+
108
+ connection.check_constraints(table_name).map do |constraint|
109
+ {
110
+ name: constraint.name,
111
+ expression: constraint.expression
112
+ }
113
+ end
114
+ rescue ActiveRecord::StatementInvalid => e
115
+ # Table doesn't exist or other database error
116
+ Rails.logger.debug { "Failed to fetch check constraints for #{table_name}: #{e.message}" }
117
+ []
118
+ rescue PG::Error => e
119
+ # PostgreSQL specific errors
120
+ Rails.logger.debug { "PostgreSQL error fetching check constraints: #{e.message}" }
121
+ []
122
+ end
123
+
124
+ def column_comment(column_name)
125
+ return nil unless connection.respond_to?(:column_comment)
126
+
127
+ connection.column_comment(table_name, column_name)
128
+ rescue ActiveRecord::StatementInvalid => e
129
+ # Table or column doesn't exist
130
+ Rails.logger.debug { "Failed to fetch column comment for #{table_name}.#{column_name}: #{e.message}" }
131
+ nil
132
+ rescue PG::Error => e
133
+ # PostgreSQL specific errors
134
+ Rails.logger.debug { "PostgreSQL error fetching column comment: #{e.message}" }
135
+ nil
136
+ end
137
+
138
+ def table_comment
139
+ return nil unless connection.respond_to?(:table_comment)
140
+
141
+ connection.table_comment(table_name)
142
+ rescue ActiveRecord::StatementInvalid => e
143
+ # Table doesn't exist
144
+ Rails.logger.debug { "Failed to fetch table comment for #{table_name}: #{e.message}" }
145
+ nil
146
+ rescue PG::Error => e
147
+ # PostgreSQL specific errors
148
+ Rails.logger.debug { "PostgreSQL error fetching table comment: #{e.message}" }
149
+ nil
150
+ end
151
+
152
+ def add_table_comment(lines)
153
+ comment = table_comment
154
+ return unless comment
155
+
156
+ lines << '' unless lines.last && lines.last.empty?
157
+ lines << 'Table Comment:'
158
+ lines << " #{comment}"
159
+ end
160
+
161
+ def format_index(index)
162
+ base = " #{index.name}"
163
+
164
+ # Show column names
165
+ columns = Array(index.columns).join(', ')
166
+ base += " (#{columns})"
167
+
168
+ # Index type
169
+ attributes = []
170
+ attributes << 'UNIQUE' if index.unique
171
+ attributes << "USING #{index.using.upcase}" if index.respond_to?(:using) && index.using
172
+ attributes << "WHERE #{index.where}" if index.respond_to?(:where) && index.where
173
+
174
+ base += " #{attributes.join(' ')}" unless attributes.empty?
175
+ base
176
+ end
177
+
178
+ def add_columns(lines)
179
+ lines << 'Columns:'
180
+
181
+ # Group columns by type for better readability
182
+ columns.each do |column|
183
+ lines << format_column(column)
184
+ end
185
+ end
186
+
187
+ def add_table_comment_toml(lines)
188
+ comment = table_comment
189
+ return unless comment
190
+
191
+ lines << ''
192
+ lines << "table_comment = \"#{comment.gsub('"', '\"')}\""
193
+ end
194
+ end
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsLens
4
+ module Schema
5
+ module Adapters
6
+ class Sqlite3 < Base
7
+ def adapter_name
8
+ 'SQLite'
9
+ end
10
+
11
+ def generate_annotation(_model_class)
12
+ lines = []
13
+ lines << "table = \"#{table_name}\""
14
+ lines << "database_dialect = \"#{database_dialect}\""
15
+ lines << ''
16
+
17
+ add_columns_toml(lines)
18
+ add_indexes_toml(lines) if show_indexes?
19
+ add_foreign_keys_toml(lines) if show_foreign_keys?
20
+ add_sqlite_pragmas_toml(lines)
21
+
22
+ lines.join("\n")
23
+ end
24
+
25
+ protected
26
+
27
+ def format_column(column)
28
+ parts = []
29
+ parts << column.name.ljust(column_name_width)
30
+ parts << ":#{column.type.to_s.ljust(12)}"
31
+
32
+ attributes = []
33
+ attributes << 'not null' unless column.null
34
+ attributes << 'primary key' if primary_key?(column)
35
+
36
+ # SQLite3 specific: show auto-increment info
37
+ attributes << 'autoincrement' if primary_key?(column) && column.type == :integer
38
+
39
+ attributes << "default: #{format_default(column.default)}" if column.default && show_defaults?
40
+
41
+ parts << attributes.join(', ') unless attributes.empty?
42
+
43
+ " #{parts.join(' ')}"
44
+ end
45
+
46
+ def fetch_indexes
47
+ # SQLite3 returns different index info, filter out auto-generated ones
48
+ super.reject { |index| index.name =~ /^sqlite_autoindex/ }
49
+ end
50
+
51
+ def fetch_check_constraints
52
+ # SQLite3 stores check constraints in table info
53
+ # This would require raw SQL queries to extract
54
+ []
55
+ end
56
+
57
+ def add_sqlite_pragmas_structured(lines)
58
+ # Add SQLite-specific information if needed
59
+ return unless connection.respond_to?(:execute)
60
+
61
+ begin
62
+ # Example: Foreign keys status
63
+ fk_status = connection.execute('PRAGMA foreign_keys').first
64
+ lines << 'FOREIGN_KEYS_ENABLED: false' if fk_status && fk_status['foreign_keys'].zero?
65
+ rescue ActiveRecord::StatementInvalid => e
66
+ # SQLite doesn't recognize the pragma or access denied
67
+ Rails.logger.debug { "Failed to fetch SQLite foreign_keys pragma: #{e.message}" }
68
+ rescue SQLite3::Exception => e
69
+ # SQLite specific errors (database locked, etc)
70
+ Rails.logger.debug { "SQLite error fetching pragmas: #{e.message}" }
71
+ end
72
+ end
73
+
74
+ def add_sqlite_pragmas_toml(lines)
75
+ # Add SQLite-specific information if needed
76
+ return unless connection.respond_to?(:execute)
77
+
78
+ begin
79
+ # Example: Foreign keys status
80
+ fk_status = connection.execute('PRAGMA foreign_keys').first
81
+ if fk_status && fk_status['foreign_keys'].zero?
82
+ lines << ''
83
+ lines << 'foreign_keys_enabled = false'
84
+ end
85
+ rescue ActiveRecord::StatementInvalid => e
86
+ # SQLite doesn't recognize the pragma or access denied
87
+ Rails.logger.debug { "Failed to fetch SQLite foreign_keys pragma: #{e.message}" }
88
+ rescue SQLite3::Exception => e
89
+ # SQLite specific errors (database locked, etc)
90
+ Rails.logger.debug { "SQLite error fetching pragmas: #{e.message}" }
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end