better_structure_sql 0.1.0

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 (68) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +41 -0
  3. data/LICENSE +21 -0
  4. data/README.md +557 -0
  5. data/app/controllers/better_structure_sql/application_controller.rb +61 -0
  6. data/app/controllers/better_structure_sql/schema_versions_controller.rb +243 -0
  7. data/app/helpers/better_structure_sql/schema_versions_helper.rb +46 -0
  8. data/app/views/better_structure_sql/schema_versions/index.html.erb +110 -0
  9. data/app/views/better_structure_sql/schema_versions/show.html.erb +186 -0
  10. data/app/views/layouts/better_structure_sql/application.html.erb +105 -0
  11. data/config/database.yml +3 -0
  12. data/config/routes.rb +12 -0
  13. data/lib/better_structure_sql/adapters/base_adapter.rb +234 -0
  14. data/lib/better_structure_sql/adapters/mysql_adapter.rb +476 -0
  15. data/lib/better_structure_sql/adapters/mysql_config.rb +32 -0
  16. data/lib/better_structure_sql/adapters/postgresql_adapter.rb +646 -0
  17. data/lib/better_structure_sql/adapters/postgresql_config.rb +25 -0
  18. data/lib/better_structure_sql/adapters/registry.rb +115 -0
  19. data/lib/better_structure_sql/adapters/sqlite_adapter.rb +644 -0
  20. data/lib/better_structure_sql/adapters/sqlite_config.rb +26 -0
  21. data/lib/better_structure_sql/configuration.rb +129 -0
  22. data/lib/better_structure_sql/database_version.rb +46 -0
  23. data/lib/better_structure_sql/dependency_resolver.rb +63 -0
  24. data/lib/better_structure_sql/dumper.rb +544 -0
  25. data/lib/better_structure_sql/engine.rb +28 -0
  26. data/lib/better_structure_sql/file_writer.rb +180 -0
  27. data/lib/better_structure_sql/formatter.rb +70 -0
  28. data/lib/better_structure_sql/generators/base.rb +33 -0
  29. data/lib/better_structure_sql/generators/domain_generator.rb +22 -0
  30. data/lib/better_structure_sql/generators/extension_generator.rb +23 -0
  31. data/lib/better_structure_sql/generators/foreign_key_generator.rb +43 -0
  32. data/lib/better_structure_sql/generators/function_generator.rb +33 -0
  33. data/lib/better_structure_sql/generators/index_generator.rb +50 -0
  34. data/lib/better_structure_sql/generators/materialized_view_generator.rb +31 -0
  35. data/lib/better_structure_sql/generators/pragma_generator.rb +23 -0
  36. data/lib/better_structure_sql/generators/sequence_generator.rb +27 -0
  37. data/lib/better_structure_sql/generators/table_generator.rb +126 -0
  38. data/lib/better_structure_sql/generators/trigger_generator.rb +54 -0
  39. data/lib/better_structure_sql/generators/type_generator.rb +47 -0
  40. data/lib/better_structure_sql/generators/view_generator.rb +27 -0
  41. data/lib/better_structure_sql/introspection/extensions.rb +29 -0
  42. data/lib/better_structure_sql/introspection/foreign_keys.rb +29 -0
  43. data/lib/better_structure_sql/introspection/functions.rb +29 -0
  44. data/lib/better_structure_sql/introspection/indexes.rb +29 -0
  45. data/lib/better_structure_sql/introspection/sequences.rb +29 -0
  46. data/lib/better_structure_sql/introspection/tables.rb +29 -0
  47. data/lib/better_structure_sql/introspection/triggers.rb +29 -0
  48. data/lib/better_structure_sql/introspection/types.rb +37 -0
  49. data/lib/better_structure_sql/introspection/views.rb +41 -0
  50. data/lib/better_structure_sql/introspection.rb +31 -0
  51. data/lib/better_structure_sql/manifest_generator.rb +65 -0
  52. data/lib/better_structure_sql/migration_patch.rb +196 -0
  53. data/lib/better_structure_sql/pg_version.rb +44 -0
  54. data/lib/better_structure_sql/railtie.rb +124 -0
  55. data/lib/better_structure_sql/schema_loader.rb +168 -0
  56. data/lib/better_structure_sql/schema_version.rb +86 -0
  57. data/lib/better_structure_sql/schema_versions.rb +213 -0
  58. data/lib/better_structure_sql/version.rb +5 -0
  59. data/lib/better_structure_sql/zip_generator.rb +81 -0
  60. data/lib/better_structure_sql.rb +81 -0
  61. data/lib/generators/better_structure_sql/install_generator.rb +44 -0
  62. data/lib/generators/better_structure_sql/migration_generator.rb +34 -0
  63. data/lib/generators/better_structure_sql/templates/README +49 -0
  64. data/lib/generators/better_structure_sql/templates/add_metadata_migration.rb.erb +25 -0
  65. data/lib/generators/better_structure_sql/templates/better_structure_sql.rb +46 -0
  66. data/lib/generators/better_structure_sql/templates/migration.rb.erb +26 -0
  67. data/lib/tasks/better_structure_sql.rake +190 -0
  68. metadata +299 -0
@@ -0,0 +1,476 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterStructureSql
4
+ module Adapters
5
+ # MySQL adapter implementing introspection via information_schema and MySQL system tables.
6
+ #
7
+ # Provides MySQL-specific SQL generation with proper dialect support.
8
+ # This adapter handles MySQL's unique features including stored procedures, triggers,
9
+ # ENUM/SET types, and AUTO_INCREMENT sequences.
10
+ class MysqlAdapter < BaseAdapter
11
+ # Introspection methods using information_schema
12
+
13
+ # Fetch database extensions (not supported in MySQL)
14
+ #
15
+ # @param _connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] Database connection (unused)
16
+ # @return [Array] Empty array as MySQL doesn't support extensions like PostgreSQL
17
+ def fetch_extensions(_connection)
18
+ # MySQL doesn't support extensions like PostgreSQL
19
+ []
20
+ end
21
+
22
+ # Fetch custom types (not supported in MySQL)
23
+ #
24
+ # @param _connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] Database connection (unused)
25
+ # @return [Array] Empty array as MySQL doesn't support standalone custom types
26
+ # @note MySQL has ENUM and SET types, but they are defined inline with columns, not as custom types
27
+ def fetch_custom_types(_connection)
28
+ # MySQL has limited support for custom types (ENUM and SET are inline)
29
+ # Return empty array since ENUMs/SETs are defined per-column
30
+ []
31
+ end
32
+
33
+ # Fetch all tables from the current database
34
+ #
35
+ # @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] Database connection
36
+ # @return [Array<Hash>] Array of table hashes with :name, :schema, :columns, :primary_key, :constraints
37
+ def fetch_tables(connection)
38
+ query = <<~SQL.squish
39
+ SELECT
40
+ TABLE_NAME,
41
+ TABLE_SCHEMA
42
+ FROM information_schema.TABLES
43
+ WHERE TABLE_SCHEMA = DATABASE()
44
+ AND TABLE_TYPE = 'BASE TABLE'
45
+ ORDER BY TABLE_NAME
46
+ SQL
47
+
48
+ connection.execute(query).map do |row|
49
+ table_name = row[0] # MySQL returns arrays not hashes by default
50
+ {
51
+ name: table_name,
52
+ schema: row[1],
53
+ columns: fetch_columns(connection, table_name),
54
+ primary_key: fetch_primary_key(connection, table_name),
55
+ constraints: fetch_constraints(connection, table_name)
56
+ }
57
+ end
58
+ end
59
+
60
+ # Fetch all indexes from the current database
61
+ #
62
+ # @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] Database connection
63
+ # @return [Array<Hash>] Array of index hashes with :table, :name, :columns, :unique, :type
64
+ def fetch_indexes(connection)
65
+ query = <<~SQL.squish
66
+ SELECT
67
+ TABLE_NAME,
68
+ INDEX_NAME,
69
+ COLUMN_NAME,
70
+ SEQ_IN_INDEX,
71
+ NON_UNIQUE,
72
+ INDEX_TYPE
73
+ FROM information_schema.STATISTICS
74
+ WHERE TABLE_SCHEMA = DATABASE()
75
+ AND INDEX_NAME != 'PRIMARY'
76
+ ORDER BY TABLE_NAME, INDEX_NAME, SEQ_IN_INDEX
77
+ SQL
78
+
79
+ # Group by table and index name to build multi-column indexes
80
+ indexes_by_key = {}
81
+
82
+ connection.execute(query).each do |row|
83
+ table_name = row[0]
84
+ index_name = row[1]
85
+ column_name = row[2]
86
+ # seq_in_index = row[3] # Used for ordering, handled by ORDER BY in query
87
+ non_unique = row[4]
88
+ index_type = row[5]
89
+
90
+ key = "#{table_name}.#{index_name}"
91
+ indexes_by_key[key] ||= {
92
+ table: table_name,
93
+ name: index_name,
94
+ columns: [],
95
+ unique: non_unique.to_i.zero?,
96
+ type: index_type
97
+ }
98
+ indexes_by_key[key][:columns] << column_name
99
+ end
100
+
101
+ indexes_by_key.values
102
+ end
103
+
104
+ # Fetch all foreign keys from the current database
105
+ #
106
+ # @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] Database connection
107
+ # @return [Array<Hash>] Array of foreign key hashes with :table, :name, :column, :foreign_table, :foreign_column, :on_update, :on_delete
108
+ def fetch_foreign_keys(connection)
109
+ query = <<~SQL.squish
110
+ SELECT
111
+ kcu.TABLE_NAME,
112
+ kcu.CONSTRAINT_NAME,
113
+ kcu.COLUMN_NAME,
114
+ kcu.REFERENCED_TABLE_NAME,
115
+ kcu.REFERENCED_COLUMN_NAME,
116
+ rc.UPDATE_RULE,
117
+ rc.DELETE_RULE
118
+ FROM information_schema.KEY_COLUMN_USAGE kcu
119
+ JOIN information_schema.REFERENTIAL_CONSTRAINTS rc
120
+ ON kcu.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
121
+ AND kcu.TABLE_SCHEMA = rc.CONSTRAINT_SCHEMA
122
+ WHERE kcu.TABLE_SCHEMA = DATABASE()
123
+ AND kcu.REFERENCED_TABLE_NAME IS NOT NULL
124
+ ORDER BY kcu.TABLE_NAME, kcu.CONSTRAINT_NAME
125
+ SQL
126
+
127
+ connection.execute(query).map do |row|
128
+ {
129
+ table: row[0],
130
+ name: row[1],
131
+ column: row[2],
132
+ foreign_table: row[3],
133
+ foreign_column: row[4],
134
+ on_update: row[5],
135
+ on_delete: row[6]
136
+ }
137
+ end
138
+ end
139
+
140
+ # Fetch all views from the current database
141
+ #
142
+ # @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] Database connection
143
+ # @return [Array<Hash>] Array of view hashes with :schema, :name, :definition, :check_option, :updatable
144
+ def fetch_views(connection)
145
+ query = <<~SQL.squish
146
+ SELECT
147
+ TABLE_NAME,
148
+ VIEW_DEFINITION,
149
+ CHECK_OPTION,
150
+ IS_UPDATABLE
151
+ FROM information_schema.VIEWS
152
+ WHERE TABLE_SCHEMA = DATABASE()
153
+ ORDER BY TABLE_NAME
154
+ SQL
155
+
156
+ connection.execute(query).map do |row|
157
+ {
158
+ schema: 'public', # MySQL doesn't use schemas like PostgreSQL
159
+ name: row[0],
160
+ definition: row[1],
161
+ check_option: row[2],
162
+ updatable: row[3] == 'YES'
163
+ }
164
+ end
165
+ end
166
+
167
+ # Fetch materialized views (not supported in MySQL)
168
+ #
169
+ # @param _connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] Database connection (unused)
170
+ # @return [Array] Empty array as MySQL doesn't support materialized views
171
+ def fetch_materialized_views(_connection)
172
+ # MySQL doesn't support materialized views
173
+ []
174
+ end
175
+
176
+ # Fetch all stored procedures and functions from the current database
177
+ #
178
+ # @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] Database connection
179
+ # @return [Array<Hash>] Array of routine hashes with :schema, :name, :definition
180
+ def fetch_functions(connection)
181
+ query = <<~SQL.squish
182
+ SELECT
183
+ ROUTINE_NAME,
184
+ ROUTINE_TYPE
185
+ FROM information_schema.ROUTINES
186
+ WHERE ROUTINE_SCHEMA = DATABASE()
187
+ AND ROUTINE_TYPE IN ('PROCEDURE', 'FUNCTION')
188
+ ORDER BY ROUTINE_NAME
189
+ SQL
190
+
191
+ connection.execute(query).map do |row|
192
+ routine_name = row[0]
193
+ routine_type = row[1]
194
+
195
+ # Get complete CREATE statement using SHOW CREATE
196
+ create_query = if routine_type == 'PROCEDURE'
197
+ "SHOW CREATE PROCEDURE `#{routine_name}`"
198
+ else
199
+ "SHOW CREATE FUNCTION `#{routine_name}`"
200
+ end
201
+
202
+ create_result = connection.execute(create_query).first
203
+ # SHOW CREATE returns: [procedure_name, sql_mode, create_statement, ...]
204
+ create_statement = create_result[2] if create_result
205
+
206
+ {
207
+ schema: 'public',
208
+ name: routine_name,
209
+ definition: create_statement
210
+ }
211
+ end
212
+ end
213
+
214
+ # Fetch sequences (not supported in MySQL)
215
+ #
216
+ # @param _connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] Database connection (unused)
217
+ # @return [Array] Empty array as MySQL doesn't support sequences (uses AUTO_INCREMENT instead)
218
+ def fetch_sequences(_connection)
219
+ # MySQL doesn't have sequences (uses AUTO_INCREMENT)
220
+ []
221
+ end
222
+
223
+ # Fetch all triggers from the current database
224
+ #
225
+ # @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] Database connection
226
+ # @return [Array<Hash>] Array of trigger hashes with :table, :name, :event, :timing, :definition
227
+ def fetch_triggers(connection)
228
+ query = <<~SQL.squish
229
+ SELECT
230
+ TRIGGER_NAME,
231
+ EVENT_MANIPULATION,
232
+ EVENT_OBJECT_TABLE,
233
+ ACTION_TIMING
234
+ FROM information_schema.TRIGGERS
235
+ WHERE TRIGGER_SCHEMA = DATABASE()
236
+ ORDER BY EVENT_OBJECT_TABLE, TRIGGER_NAME
237
+ SQL
238
+
239
+ connection.execute(query).map do |row|
240
+ trigger_name = row[0]
241
+
242
+ # Get complete CREATE statement using SHOW CREATE
243
+ create_result = connection.execute("SHOW CREATE TRIGGER `#{trigger_name}`").first
244
+ # SHOW CREATE TRIGGER returns: [trigger_name, sql_mode, create_statement, ...]
245
+ create_statement = create_result[2] if create_result
246
+
247
+ {
248
+ table: row[2],
249
+ name: trigger_name,
250
+ event: row[1], # INSERT, UPDATE, DELETE
251
+ timing: row[3], # BEFORE, AFTER
252
+ definition: create_statement
253
+ }
254
+ end
255
+ end
256
+
257
+ # Capability methods - MySQL feature support
258
+
259
+ # Indicates whether MySQL supports extensions
260
+ #
261
+ # @return [Boolean] Always false for MySQL
262
+ def supports_extensions?
263
+ false
264
+ end
265
+
266
+ # Indicates whether MySQL supports materialized views
267
+ #
268
+ # @return [Boolean] Always false for MySQL
269
+ def supports_materialized_views?
270
+ false
271
+ end
272
+
273
+ # Indicates whether MySQL supports custom types
274
+ #
275
+ # @return [Boolean] Always false (ENUM/SET are inline with columns, not custom types)
276
+ def supports_custom_types?
277
+ false # ENUM/SET are inline with columns, not custom types
278
+ end
279
+
280
+ # Indicates whether MySQL supports domains
281
+ #
282
+ # @return [Boolean] Always false for MySQL
283
+ def supports_domains?
284
+ false
285
+ end
286
+
287
+ # Indicates whether MySQL supports stored procedures and functions
288
+ #
289
+ # @return [Boolean] Always true for MySQL
290
+ def supports_functions?
291
+ true # Stored procedures and functions
292
+ end
293
+
294
+ # Indicates whether MySQL supports triggers
295
+ #
296
+ # @return [Boolean] Always true for MySQL
297
+ def supports_triggers?
298
+ true
299
+ end
300
+
301
+ # Indicates whether MySQL supports sequences
302
+ #
303
+ # @return [Boolean] Always false (uses AUTO_INCREMENT instead)
304
+ def supports_sequences?
305
+ false # Uses AUTO_INCREMENT instead
306
+ end
307
+
308
+ # Indicates whether MySQL supports check constraints
309
+ #
310
+ # @return [Boolean] True for MySQL 8.0.16+, false for earlier versions
311
+ def supports_check_constraints?
312
+ version_at_least?(database_version, '8.0.16')
313
+ end
314
+
315
+ # Version detection
316
+
317
+ # Get the current MySQL database version
318
+ #
319
+ # @return [String] Normalized version string (e.g., "8.0.35")
320
+ def database_version
321
+ @database_version ||= begin
322
+ version_string = connection.select_value('SELECT VERSION()')
323
+ parse_version(version_string)
324
+ end
325
+ end
326
+
327
+ # Parse MySQL version string into normalized format
328
+ #
329
+ # @param version_string [String] Raw version string from MySQL (e.g., "8.0.35" or "5.7.44-log")
330
+ # @return [String] Normalized version (e.g., "8.0.35") or "unknown" if parsing fails
331
+ def parse_version(version_string)
332
+ # Example: "8.0.35" or "5.7.44-log"
333
+ # Extract major.minor.patch version
334
+ match = version_string.match(/(\d+\.\d+\.\d+)/)
335
+ return 'unknown' unless match
336
+
337
+ match[1]
338
+ end
339
+
340
+ private
341
+
342
+ # Helper methods for introspection
343
+
344
+ # Fetch columns for a specific table
345
+ #
346
+ # @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] Database connection
347
+ # @param table_name [String] Name of the table
348
+ # @return [Array<Hash>] Array of column hashes with :name, :type, :nullable, :default, etc.
349
+ def fetch_columns(connection, table_name)
350
+ query = <<~SQL.squish
351
+ SELECT
352
+ COLUMN_NAME,
353
+ DATA_TYPE,
354
+ IS_NULLABLE,
355
+ COLUMN_DEFAULT,
356
+ CHARACTER_MAXIMUM_LENGTH,
357
+ NUMERIC_PRECISION,
358
+ NUMERIC_SCALE,
359
+ COLUMN_TYPE,
360
+ EXTRA
361
+ FROM information_schema.COLUMNS
362
+ WHERE TABLE_NAME = #{connection.quote(table_name)}
363
+ AND TABLE_SCHEMA = DATABASE()
364
+ ORDER BY ORDINAL_POSITION
365
+ SQL
366
+
367
+ connection.execute(query).map do |row|
368
+ {
369
+ name: row[0],
370
+ type: resolve_column_type(row),
371
+ nullable: row[2] == 'YES',
372
+ default: row[3],
373
+ length: row[4],
374
+ precision: row[5],
375
+ scale: row[6],
376
+ column_type: row[7], # Full type with ENUM values, etc.
377
+ extra: row[8] # AUTO_INCREMENT, etc.
378
+ }
379
+ end
380
+ end
381
+
382
+ # Fetch primary key columns for a specific table
383
+ #
384
+ # @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] Database connection
385
+ # @param table_name [String] Name of the table
386
+ # @return [Array<String>] Array of primary key column names
387
+ def fetch_primary_key(connection, table_name)
388
+ query = <<~SQL.squish
389
+ SELECT COLUMN_NAME
390
+ FROM information_schema.KEY_COLUMN_USAGE
391
+ WHERE TABLE_NAME = #{connection.quote(table_name)}
392
+ AND TABLE_SCHEMA = DATABASE()
393
+ AND CONSTRAINT_NAME = 'PRIMARY'
394
+ ORDER BY ORDINAL_POSITION
395
+ SQL
396
+
397
+ connection.execute(query).pluck(0)
398
+ end
399
+
400
+ # Fetch check constraints for a specific table (MySQL 8.0.16+)
401
+ #
402
+ # @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter] Database connection
403
+ # @param table_name [String] Name of the table
404
+ # @return [Array<Hash>] Array of constraint hashes with :name, :definition, :type
405
+ def fetch_constraints(connection, table_name)
406
+ # MySQL 8.0.16+ supports check constraints
407
+ return [] unless supports_check_constraints?
408
+
409
+ query = <<~SQL.squish
410
+ SELECT
411
+ CONSTRAINT_NAME,
412
+ CHECK_CLAUSE
413
+ FROM information_schema.CHECK_CONSTRAINTS
414
+ WHERE CONSTRAINT_SCHEMA = DATABASE()
415
+ AND TABLE_NAME = #{connection.quote(table_name)}
416
+ ORDER BY CONSTRAINT_NAME
417
+ SQL
418
+
419
+ connection.execute(query).map do |row|
420
+ {
421
+ name: row[0],
422
+ definition: row[1],
423
+ type: :check
424
+ }
425
+ end
426
+ rescue StandardError
427
+ # If CHECK_CONSTRAINTS table doesn't exist (MySQL < 8.0.16), return empty
428
+ []
429
+ end
430
+
431
+ # Resolve MySQL column type into normalized format
432
+ #
433
+ # @param row [Array] Column information row from information_schema.COLUMNS
434
+ # @return [String] Normalized column type with length/precision if applicable
435
+ def resolve_column_type(row)
436
+ data_type = row[1]
437
+ column_type = row[7] # Full type definition
438
+
439
+ case data_type
440
+ when 'varchar', 'char'
441
+ # Use CHARACTER_MAXIMUM_LENGTH
442
+ length = row[4]
443
+ length ? "#{data_type}(#{length})" : data_type
444
+ when 'decimal', 'numeric'
445
+ precision = row[5]
446
+ scale = row[6]
447
+ if precision && scale
448
+ "#{data_type}(#{precision},#{scale})"
449
+ else
450
+ data_type
451
+ end
452
+ when 'enum', 'set'
453
+ # Return full column_type which includes values: enum('admin','user','guest')
454
+ column_type
455
+ when 'int', 'integer'
456
+ 'int'
457
+ when 'bigint'
458
+ 'bigint'
459
+ when 'tinyint'
460
+ # Check if it's boolean (tinyint(1))
461
+ column_type.include?('tinyint(1)') ? 'boolean' : 'tinyint'
462
+ when 'datetime', 'timestamp'
463
+ data_type
464
+ when 'text', 'mediumtext', 'longtext'
465
+ data_type
466
+ when 'blob', 'mediumblob', 'longblob'
467
+ data_type
468
+ when 'json'
469
+ 'json'
470
+ else
471
+ data_type
472
+ end
473
+ end
474
+ end
475
+ end
476
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterStructureSql
4
+ module Adapters
5
+ # MySQL-specific configuration settings
6
+ #
7
+ # Provides configuration options specific to MySQL database adapter.
8
+ # These settings control which MySQL features are included in schema dumps
9
+ # and how they are generated.
10
+ class MysqlConfig
11
+ # @return [Boolean] Whether to include stored procedures in schema dump
12
+ # @return [Boolean] Whether to include triggers in schema dump
13
+ # @return [Boolean] Whether to include views in schema dump
14
+ # @return [Boolean] Whether to use SHOW CREATE statements instead of information_schema
15
+ # @return [String] Default character set for MySQL (default: 'utf8mb4')
16
+ # @return [String] Default collation for MySQL (default: 'utf8mb4_unicode_ci')
17
+ # @return [String] Minimum MySQL version required (default: '8.0')
18
+ attr_accessor :include_stored_procedures, :include_triggers, :include_views, :use_show_create, :charset, :collation, :min_version
19
+
20
+ # Initialize MySQL configuration with default values
21
+ def initialize
22
+ @include_stored_procedures = true
23
+ @include_triggers = true
24
+ @include_views = true
25
+ @use_show_create = false # Use information_schema by default
26
+ @charset = 'utf8mb4'
27
+ @collation = 'utf8mb4_unicode_ci'
28
+ @min_version = '8.0'
29
+ end
30
+ end
31
+ end
32
+ end