fast_schema_dumper 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/CHANGELOG.md +13 -0
- data/lib/fast_schema_dumper/fast_dumper.rb +75 -18
- data/lib/fast_schema_dumper/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e66bdcea2398d20e0a2957851af140da62488eb5c4993e65131ea186d5ad9df6
|
|
4
|
+
data.tar.gz: d70566eea36f7e7dde2ac0b7aaf339e6353e642d82883d4497eedf73a91470be
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 670c4795780e2ab759a9064030432b2e97d7e5ae5031988b8197b427bf03b011d4c9aa028dc7354233b9af6b213ba651339135874202f71c83152fcdb6a11678
|
|
7
|
+
data.tar.gz: e382f5cab4f77f165b200891d85ecf9642c0aabbe9601d5260e3eb373ba26bb9c6b850dcaa1536dcdfbdfb88c273d321938e1003f9f5a8dd11a49e2a81b51252
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.5.4] - 2026-08-04
|
|
4
|
+
|
|
5
|
+
- Support MySQL functional indexes (e.g. `INDEX ((LOWER(name)))`). Indexes
|
|
6
|
+
containing expression parts are now dumped as a single string column, matching
|
|
7
|
+
`ActiveRecord::SchemaDumper` output. Requires MySQL 8.0.13+; older versions
|
|
8
|
+
fall back to the previous behavior. [#55](https://github.com/smartbank-inc/fast_schema_dumper/pull/55)
|
|
9
|
+
- Index lines are now sorted by the formatted statement string, matching
|
|
10
|
+
`ActiveRecord::SchemaDumper` ordering exactly. [#55](https://github.com/smartbank-inc/fast_schema_dumper/pull/55)
|
|
11
|
+
|
|
12
|
+
## [0.5.3] - 2026-05-18
|
|
13
|
+
|
|
14
|
+
- Add limit handling for binary/varbinary columns in format_column. [#39](https://github.com/smartbank-inc/fast_schema_dumper/pull/39)
|
|
15
|
+
|
|
3
16
|
## [0.5.2] - 2026-04-15
|
|
4
17
|
|
|
5
18
|
- Fix generated column dump order to match ActiveRecord::SchemaDumper. [#32](https://github.com/smartbank-inc/fast_schema_dumper/pull/32)
|
|
@@ -49,6 +49,16 @@ module FastSchemaDumper
|
|
|
49
49
|
")
|
|
50
50
|
|
|
51
51
|
# Get all indexes
|
|
52
|
+
# INFORMATION_SCHEMA.STATISTICS.EXPRESSION exists only in MySQL 8.0.13+
|
|
53
|
+
result = conn.exec_query("
|
|
54
|
+
SELECT COUNT(*) as count
|
|
55
|
+
FROM INFORMATION_SCHEMA.COLUMNS
|
|
56
|
+
WHERE TABLE_SCHEMA = 'information_schema'
|
|
57
|
+
AND TABLE_NAME = 'STATISTICS'
|
|
58
|
+
AND COLUMN_NAME = 'EXPRESSION'
|
|
59
|
+
")
|
|
60
|
+
expression_column = (result.first['count'] > 0) ? "s.EXPRESSION," : ""
|
|
61
|
+
|
|
52
62
|
indexes_data = conn.exec_query("
|
|
53
63
|
SELECT
|
|
54
64
|
s.TABLE_NAME,
|
|
@@ -56,6 +66,8 @@ module FastSchemaDumper
|
|
|
56
66
|
s.NON_UNIQUE,
|
|
57
67
|
s.COLUMN_NAME,
|
|
58
68
|
s.SEQ_IN_INDEX,
|
|
69
|
+
s.SUB_PART,
|
|
70
|
+
#{expression_column}
|
|
59
71
|
s.INDEX_COMMENT,
|
|
60
72
|
s.COLLATION
|
|
61
73
|
FROM INFORMATION_SCHEMA.STATISTICS s
|
|
@@ -69,6 +81,7 @@ module FastSchemaDumper
|
|
|
69
81
|
hash[idx['TABLE_NAME']] ||= {}
|
|
70
82
|
hash[idx['TABLE_NAME']][idx['INDEX_NAME']] ||= {
|
|
71
83
|
columns: [],
|
|
84
|
+
parts: [],
|
|
72
85
|
unique: idx['NON_UNIQUE'] == 0,
|
|
73
86
|
# length
|
|
74
87
|
orders: {},
|
|
@@ -81,10 +94,22 @@ module FastSchemaDumper
|
|
|
81
94
|
comment: idx['INDEX_COMMENT']
|
|
82
95
|
# enabled
|
|
83
96
|
}
|
|
84
|
-
hash[idx['TABLE_NAME']][idx['INDEX_NAME']]
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
97
|
+
index = hash[idx['TABLE_NAME']][idx['INDEX_NAME']]
|
|
98
|
+
desc = idx['COLLATION'] == 'D'
|
|
99
|
+
|
|
100
|
+
# Functional index parts (MySQL 8.0.13+) have COLUMN_NAME = NULL and the
|
|
101
|
+
# expression in EXPRESSION
|
|
102
|
+
if idx['EXPRESSION']
|
|
103
|
+
index[:parts] << {expression: idx['EXPRESSION'], desc:}
|
|
104
|
+
else
|
|
105
|
+
# :columns/:orders are for expression-free indexes.
|
|
106
|
+
index[:columns] << idx['COLUMN_NAME']
|
|
107
|
+
# Track descending order columns (COLLATION = 'D')
|
|
108
|
+
index[:orders][idx['COLUMN_NAME']] = :desc if desc
|
|
109
|
+
|
|
110
|
+
# Also keep the part in :parts so mixed indexes like
|
|
111
|
+
# ((MONTH(starts_at)) DESC, name) can be rebuilt from :parts alone.
|
|
112
|
+
index[:parts] << {column: idx['COLUMN_NAME'], sub_part: idx['SUB_PART'], desc:}
|
|
88
113
|
end
|
|
89
114
|
end
|
|
90
115
|
|
|
@@ -314,21 +339,12 @@ module FastSchemaDumper
|
|
|
314
339
|
end
|
|
315
340
|
|
|
316
341
|
# Indexes
|
|
317
|
-
# Rails
|
|
318
|
-
#
|
|
319
|
-
|
|
320
|
-
#
|
|
321
|
-
# This ensures that missing columns sort after existing ones
|
|
322
|
-
max_cols = indexes.values.map { |data| data[:columns].size }.max || 1
|
|
323
|
-
cols = index_data[:columns].dup
|
|
324
|
-
# Pad with a string that sorts after any real column name
|
|
325
|
-
cols += ["\xFF" * 100] * (max_cols - cols.size)
|
|
326
|
-
cols
|
|
327
|
-
end
|
|
328
|
-
|
|
329
|
-
sorted_indexes.each do |index_name, index_data|
|
|
330
|
-
@output << " #{format_index(index_name, index_data)}"
|
|
342
|
+
# Rails sorts the formatted index statements as strings
|
|
343
|
+
# ref: activerecord: lib/active_record/schema_dumper.rb#indexes_in_create
|
|
344
|
+
index_lines = indexes.except('PRIMARY').map do |index_name, index_data|
|
|
345
|
+
" #{format_index(index_name, index_data)}"
|
|
331
346
|
end
|
|
347
|
+
@output.concat(index_lines.sort)
|
|
332
348
|
|
|
333
349
|
# Respect the CHECK constraint
|
|
334
350
|
#
|
|
@@ -386,6 +402,11 @@ module FastSchemaDumper
|
|
|
386
402
|
col_def += ", limit: 3"
|
|
387
403
|
end
|
|
388
404
|
|
|
405
|
+
# limit (binary, varbinary)
|
|
406
|
+
if ['binary', 'varbinary'].include?(column['DATA_TYPE']) && column['CHARACTER_MAXIMUM_LENGTH']
|
|
407
|
+
col_def += ", limit: #{column['CHARACTER_MAXIMUM_LENGTH']}"
|
|
408
|
+
end
|
|
409
|
+
|
|
389
410
|
# size (text)
|
|
390
411
|
if column['DATA_TYPE'] == 'mediumtext'
|
|
391
412
|
col_def += ", size: :medium"
|
|
@@ -520,6 +541,11 @@ module FastSchemaDumper
|
|
|
520
541
|
end
|
|
521
542
|
|
|
522
543
|
def format_index(index_name, index_data)
|
|
544
|
+
parts = index_data[:parts] || []
|
|
545
|
+
if parts.any? { |part| part[:expression] }
|
|
546
|
+
return format_expression_index(index_name, index_data)
|
|
547
|
+
end
|
|
548
|
+
|
|
523
549
|
idx_def = "t.index "
|
|
524
550
|
|
|
525
551
|
idx_def += if index_data[:columns].size == 1
|
|
@@ -558,6 +584,37 @@ module FastSchemaDumper
|
|
|
558
584
|
idx_def
|
|
559
585
|
end
|
|
560
586
|
|
|
587
|
+
# Rails dumps indexes containing expression parts with a single string
|
|
588
|
+
# instead of a column name array. DESC and prefix lengths are inlined
|
|
589
|
+
# into the string, so no order:/length: options are emitted.
|
|
590
|
+
# ref: activerecord: lib/active_record/connection_adapters/mysql/schema_statements.rb#indexes
|
|
591
|
+
def format_expression_index(index_name, index_data)
|
|
592
|
+
columns_string = index_data[:parts].map { |part| format_index_part(part) }.join(", ")
|
|
593
|
+
|
|
594
|
+
idx_def = "t.index #{columns_string.inspect}"
|
|
595
|
+
idx_def += ", name: \"#{index_name}\""
|
|
596
|
+
idx_def += ", unique: true" if index_data[:unique]
|
|
597
|
+
|
|
598
|
+
if index_data[:comment] && !index_data[:comment].empty?
|
|
599
|
+
idx_def += ", comment: \"#{escape_string(index_data[:comment])}\""
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
idx_def
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
def format_index_part(part)
|
|
606
|
+
str = if part[:expression]
|
|
607
|
+
expression = part[:expression].gsub("\\'", "'")
|
|
608
|
+
expression.start_with?("(") ? expression : "(#{expression})"
|
|
609
|
+
else
|
|
610
|
+
quoted = "`#{part[:column].gsub("`", "``")}`"
|
|
611
|
+
quoted += "(#{part[:sub_part]})" if part[:sub_part]
|
|
612
|
+
quoted
|
|
613
|
+
end
|
|
614
|
+
str += " DESC" if part[:desc]
|
|
615
|
+
str
|
|
616
|
+
end
|
|
617
|
+
|
|
561
618
|
def balanced_parentheses?(str)
|
|
562
619
|
depth = 0
|
|
563
620
|
str.each_char do |char|
|