schema_ferry 0.1.2 → 0.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/README.md +17 -16
- data/lib/schema_ferry/converter/column_converter.rb +1 -5
- data/lib/schema_ferry/converter/schema_converter.rb +44 -32
- data/lib/schema_ferry/converter/type_mapper.rb +13 -5
- data/lib/schema_ferry/dsl/table_rule.rb +1 -18
- data/lib/schema_ferry/schema_model.rb +0 -2
- data/lib/schema_ferry/source/mysql_reader.rb +0 -1
- data/lib/schema_ferry/target/schemafile_renderer.rb +0 -2
- data/lib/schema_ferry/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: e954e39c0309126d404ec1a7b95cfcaf062cbdf18525e6d179e8747c96d95aaf
|
|
4
|
+
data.tar.gz: 29c7495b19fb18d7cb9ea1a17fb64b8778dc7deeefeeee50858b0584adc57fe8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 939a4335e6ad17b7eabbde3d4f3979e09009493cef5b474ec033cfbf853f4def56fb505d82aa9ab4366ab6ff6158196aed1f9a4ea762470cc5e3938a148f181e
|
|
7
|
+
data.tar.gz: d7e983858d24f45938fbda3a8c76d4a0de06d496c949a91320903b0c49b1ff440450570440e7726860e6c2158bbddec080d3a92c78918f62182f84321ad21b42
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.0] - 2026-07-06
|
|
11
|
+
|
|
12
|
+
### Removed
|
|
13
|
+
|
|
14
|
+
- `add_index` — declaring a PostgreSQL-only index existed solely as a replacement path for skipped FULLTEXT indexes, but that undermined the tool's own "faithful mirror of MySQL" design: it was the only way to declare something with no MySQL counterpart. It also had its own bug — an index with a `where:` clause was dropped and re-created on every single run, because PostgreSQL rewrites predicates (operators, casts, parenthesization) when it stores them, so a declared clause can never reliably match what's on the target. A `pg_trgm` GIN index (or similar) is still the way to replace a FULLTEXT index, but only by hand, once you're fully cut over to PostgreSQL.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- `FULLTEXT`/`SPATIAL` indexes and spatial columns (`POINT`, `GEOMETRY`, `POLYGON`, `LINESTRING`, …) now raise `ConversionError` instead of printing a warning and silently excluding themselves. A warning is easy to miss in an unattended cron run's output; raising fails the same way any other error does — a non-zero exit status a monitor can catch. Exclude them explicitly with `ignore_index` / `ignore_column`.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- `POINT` columns are now caught and raise `ConversionError`, like other unsupported spatial types (`GEOMETRY`/`POLYGON`/`LINESTRING`/etc.), instead of silently becoming a meaningless `integer NOT NULL` column. ActiveRecord's mysql2 adapter misreports `POINT` as plain `:integer` — the string "point" happens to match an unanchored `/int/i` pattern deep in ActiveRecord's generic type map — unlike the others, which already came through as an unrecognized type and were rejected.
|
|
23
|
+
|
|
24
|
+
## [0.1.3] - 2026-07-06
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- The 0.1.2 fix only covered the two reported cases, not the underlying pattern: any type/option combination that PostgreSQL can't actually reproduce back to ActiveRecord causes `apply!` to re-run `change_column` forever, because ridgepole compares the declaration against a state it can never match.
|
|
29
|
+
- `DOUBLE` columns (`:float`) are read with `limit: 53` (the type's internal bit width), but a PostgreSQL column never reports a limit back. `limit` is no longer emitted for `:float` columns.
|
|
30
|
+
- Plain `DECIMAL` columns with a default (not just ones bumped from `BIGINT UNSIGNED`) now render their default as a string, matching the form ActiveRecord's schema dumper uses for decimal defaults — the same fix from 0.1.2, generalized to `TypeMapper` so it applies to every decimal column instead of only the unsigned-bigint conversion path.
|
|
31
|
+
|
|
10
32
|
## [0.1.2] - 2026-07-06
|
|
11
33
|
|
|
12
34
|
### Fixed
|
data/README.md
CHANGED
|
@@ -6,7 +6,7 @@ You're migrating a production MySQL database to PostgreSQL. Moving the data take
|
|
|
6
6
|
- **Sensible defaults, fully customizable** — built-in type mappings handle most cases; override anything with a few DSL rules
|
|
7
7
|
- **Safe to iterate** — `dry_run` shows the exact changes that would be applied, before touching anything
|
|
8
8
|
|
|
9
|
-
schema_ferry is designed to run repeatedly (e.g. via cron). Data migration is out of scope — pair it with [pgloader](https://
|
|
9
|
+
schema_ferry is designed to run repeatedly (e.g. via cron). Data migration is out of scope — pair it with [pgloader](https://github.com/dimitri/pgloader) (one-shot bulk copy) or CDC replication (AWS DMS, [Debezium](https://github.com/debezium/debezium), …), which load rows into the tables schema_ferry keeps in sync.
|
|
10
10
|
|
|
11
11
|
## Requirements
|
|
12
12
|
|
|
@@ -43,7 +43,7 @@ pipeline.apply! # applies the schema to PostgreSQL
|
|
|
43
43
|
|
|
44
44
|
There is also `pipeline.schemafile`, which returns the generated schema as a string without connecting to the target.
|
|
45
45
|
|
|
46
|
-
`apply!` makes the target match the generated schema — including **dropping** columns and indexes that are not part of it. Before running against a target that holds data, read [
|
|
46
|
+
`apply!` makes the target match the generated schema — including **dropping** columns and indexes from the target that are not part of it. Before running against a target that holds data, read [Coverage](#coverage) below.
|
|
47
47
|
|
|
48
48
|
### CLI
|
|
49
49
|
|
|
@@ -86,12 +86,24 @@ end
|
|
|
86
86
|
|
|
87
87
|
The same rules work in a CLI definition file.
|
|
88
88
|
|
|
89
|
-
###
|
|
89
|
+
### Coverage
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
schema_ferry syncs what can be done automatically — exactly where possible, or as an approximation with a warning where it isn't — and leaves the rest to add by hand, later. Where there's no reasonable equivalent at all, it raises instead of guessing. Add the column or index by hand once you're fully cut over to PostgreSQL — not before, since `apply!` delegates to [ridgepole](https://github.com/ridgepole/ridgepole), which drops anything missing from the generated schema on a managed table, including something added by hand as a stand-in — but never drops a table that's absent from it entirely.
|
|
92
92
|
|
|
93
93
|
Review `dry_run` output before your first `apply!` and whenever you change the conversion rules — those are the moments that introduce drops. Unattended runs in between only mirror changes made to the MySQL schema; if even those need review, schedule `dry-run` instead and apply by hand.
|
|
94
94
|
|
|
95
|
+
Normalized automatically, with a warning to stderr:
|
|
96
|
+
|
|
97
|
+
- **Index prefix lengths** (`KEY (col(10))`) are dropped silently — PostgreSQL indexes the full column.
|
|
98
|
+
- **Identifiers over 63 bytes** (MySQL allows 64): index and foreign key names are shortened deterministically (`first 54 bytes + _ + 8-char digest`), so repeated runs stay stable. Overlong table names are only warned about — rename those yourself.
|
|
99
|
+
- **Zero-date defaults** (`'0000-00-00 00:00:00'`) are invalid in PostgreSQL and are dropped.
|
|
100
|
+
- **BIGINT UNSIGNED columns on a foreign key** (either side) become signed `bigint` instead of `numeric(20)` — a numeric column cannot reference a bigint primary key. Values above 2⁶³−1 will not fit, the same trade-off as for `BIGINT UNSIGNED` primary keys.
|
|
101
|
+
|
|
102
|
+
Raises instead:
|
|
103
|
+
|
|
104
|
+
- **FULLTEXT indexes** — PostgreSQL has no equivalent construct (a `pg_trgm` GIN index is a common approximation, but it's not the same search semantics, so schema_ferry doesn't create one for you). Because of the drop behavior above, you can't pre-create a replacement during the sync period — add one once you're fully cut over to PostgreSQL. `ignore_index` them.
|
|
105
|
+
- **Spatial columns** (`POINT`, `GEOMETRY`, `POLYGON`, `LINESTRING`, …) — PostgreSQL has no built-in equivalent without PostGIS, which schema_ferry does not manage. `ignore_column` them.
|
|
106
|
+
|
|
95
107
|
## DSL reference
|
|
96
108
|
|
|
97
109
|
### Top-level
|
|
@@ -113,7 +125,6 @@ Review `dry_run` output before your first `apply!` and whenever you change the c
|
|
|
113
125
|
| `map_column :col, type: :type, default: value` | …and give it an explicit default |
|
|
114
126
|
| `ignore_column :col` | Exclude a column |
|
|
115
127
|
| `ignore_index :index_name` | Exclude an index |
|
|
116
|
-
| `add_index :col, ...options` | Declare a PostgreSQL-only index (options: `name`, `unique`, `using`, `opclass`, `where`, `order`) |
|
|
117
128
|
|
|
118
129
|
Ignoring a column also drops indexes and foreign keys that reference it. Renaming tables or columns is out of scope — clean up names after the cutover with a regular migration.
|
|
119
130
|
|
|
@@ -128,7 +139,7 @@ Ignoring a column also drops indexes and foreign keys that reference it. Renamin
|
|
|
128
139
|
| `TINYINT(1)` | `boolean` | see the caveat above if a column holds more than 0/1 |
|
|
129
140
|
| `TINYINT`…`BIGINT` (signed) | `smallint` / `integer` / `bigint` | widths normalized to PostgreSQL's three integer sizes |
|
|
130
141
|
| `TINYINT`…`INT` `UNSIGNED` | one size larger | e.g. `INT UNSIGNED` → `bigint` |
|
|
131
|
-
| `BIGINT UNSIGNED` | `numeric(20)` | PostgreSQL has no unsigned 8-byte integer; emitted with a warning. Columns on a foreign key become signed `bigint` instead — see
|
|
142
|
+
| `BIGINT UNSIGNED` | `numeric(20)` | PostgreSQL has no unsigned 8-byte integer; emitted with a warning. Columns on a foreign key become signed `bigint` instead — see above |
|
|
132
143
|
| `FLOAT` / `DOUBLE` | `double precision` | |
|
|
133
144
|
| `DECIMAL(p,s)` | `numeric(p,s)` | |
|
|
134
145
|
| `DATETIME` / `TIMESTAMP` | `timestamp` | use `map_type :datetime, to: :timestamptz` for `timestamptz` |
|
|
@@ -139,16 +150,6 @@ Ignoring a column also drops indexes and foreign keys that reference it. Renamin
|
|
|
139
150
|
|
|
140
151
|
`map_type` / `map_column` take Rails-style abstract type symbols (`:string`, `:integer`, `:jsonb`, …), not raw SQL type names.
|
|
141
152
|
|
|
142
|
-
### Automatic adjustments (with warnings)
|
|
143
|
-
|
|
144
|
-
Some MySQL constructs have no PostgreSQL equivalent. schema_ferry handles them and prints a `[schema_ferry]` warning to stderr:
|
|
145
|
-
|
|
146
|
-
- **FULLTEXT / SPATIAL indexes** are skipped. Declare a replacement with `add_index` (e.g. `add_index :body, using: :gin, opclass: :gin_trgm_ops` — requires `CREATE EXTENSION pg_trgm` on the target, done once by hand) and silence the warning with `ignore_index`. Don't create replacement indexes by hand: anything not in the generated schema is dropped on the next run.
|
|
147
|
-
- **Index prefix lengths** (`KEY (col(10))`) are dropped silently — PostgreSQL indexes the full column.
|
|
148
|
-
- **Identifiers over 63 bytes** (MySQL allows 64): index and foreign key names are shortened deterministically (`first 54 bytes + _ + 8-char digest`), so repeated runs stay stable. Overlong table names are only warned about — rename those yourself.
|
|
149
|
-
- **Zero-date defaults** (`'0000-00-00 00:00:00'`) are invalid in PostgreSQL and are dropped.
|
|
150
|
-
- **BIGINT UNSIGNED columns on a foreign key** (either side) become signed `bigint` instead of `numeric(20)` — a numeric column cannot reference a bigint primary key. Values above 2⁶³−1 will not fit, the same trade-off as for `BIGINT UNSIGNED` primary keys.
|
|
151
|
-
|
|
152
153
|
## How it works
|
|
153
154
|
|
|
154
155
|
Each run executes a three-stage pipeline:
|
|
@@ -45,11 +45,7 @@ module SchemaFerry
|
|
|
45
45
|
else
|
|
46
46
|
emit_warning "column #{raw[:name].inspect}: BIGINT UNSIGNED has no PostgreSQL " \
|
|
47
47
|
"integer equivalent; mapped to decimal(20, 0)."
|
|
48
|
-
|
|
49
|
-
# `default: "0"`), not a numeric literal — match that representation
|
|
50
|
-
# or ridgepole re-applies the default on every run.
|
|
51
|
-
new_default = raw[:default]&.to_s
|
|
52
|
-
raw.merge(type: :decimal, limit: nil, precision: 20, scale: 0, default: new_default)
|
|
48
|
+
raw.merge(type: :decimal, limit: nil, precision: 20, scale: 0)
|
|
53
49
|
end
|
|
54
50
|
end
|
|
55
51
|
|
|
@@ -8,6 +8,21 @@ module SchemaFerry
|
|
|
8
8
|
# PostgreSQL has no FULLTEXT/SPATIAL equivalent that ridgepole can express.
|
|
9
9
|
UNSUPPORTED_INDEX_TYPES = %i[fulltext spatial].freeze
|
|
10
10
|
|
|
11
|
+
# MySQL spatial column types have no PostgreSQL equivalent (that would
|
|
12
|
+
# require PostGIS, which schema_ferry does not manage). Most of these
|
|
13
|
+
# already raise TypeMapper's ConversionError, since ActiveRecord's
|
|
14
|
+
# mysql2 adapter reports them as an unrecognized type (nil). POINT is
|
|
15
|
+
# the sole exception: AR matches sql_type against an unanchored /int/i
|
|
16
|
+
# regex (see register_class_with_limit in
|
|
17
|
+
# ActiveRecord::ConnectionAdapters::AbstractAdapter#initialize_type_map),
|
|
18
|
+
# which matches "point" as a substring — so it's misreported as plain
|
|
19
|
+
# :integer instead. Without this check it would sail through as a
|
|
20
|
+
# meaningless integer column instead of raising. It must be caught
|
|
21
|
+
# here, by sql_type, and raised explicitly — the same failure mode as
|
|
22
|
+
# any other unsupported type, not a warning that's easy to miss in a
|
|
23
|
+
# cron log.
|
|
24
|
+
MISDETECTED_SPATIAL_SQL_TYPE = /\Apoint\b/i
|
|
25
|
+
|
|
11
26
|
def initialize(config)
|
|
12
27
|
@column_converter = ColumnConverter.new(TypeMapper.new(config.global_type_overrides))
|
|
13
28
|
@table_rules = config.table_rules
|
|
@@ -82,47 +97,46 @@ module SchemaFerry
|
|
|
82
97
|
def convert_columns(raw_columns, table_name, rule, ignored, fk_columns)
|
|
83
98
|
raw_columns
|
|
84
99
|
.reject { |c| ignored.include?(c[:name]) }
|
|
85
|
-
.map
|
|
100
|
+
.map do |c|
|
|
101
|
+
check_misdetected_spatial_type!(c, table_name, rule)
|
|
102
|
+
@column_converter.call(c, table_name, rule, fk_columns)
|
|
103
|
+
end
|
|
86
104
|
end
|
|
87
105
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
106
|
+
# map_column already lets a rule override any column's type before it
|
|
107
|
+
# ever reaches this check (ColumnConverter#call skips TypeMapper
|
|
108
|
+
# entirely when an override is present), same as for any other type.
|
|
109
|
+
def check_misdetected_spatial_type!(raw, table_name, rule)
|
|
110
|
+
return unless raw[:sql_type].to_s.match?(MISDETECTED_SPATIAL_SQL_TYPE)
|
|
111
|
+
return if rule&.column_type_overrides&.key?(raw[:name])
|
|
112
|
+
|
|
113
|
+
raise ConversionError, "#{table_name}.#{raw[:name]}: MySQL #{raw[:sql_type]} columns have no " \
|
|
114
|
+
"PostgreSQL equivalent without PostGIS, which schema_ferry does not " \
|
|
115
|
+
"manage. Exclude it with ignore_column :#{raw[:name]}."
|
|
95
116
|
end
|
|
96
117
|
|
|
97
|
-
def
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
using: opts[:using],
|
|
106
|
-
opclass: opts[:opclass],
|
|
107
|
-
where: opts[:where],
|
|
108
|
-
orders: opts[:order],
|
|
109
|
-
lengths: nil
|
|
110
|
-
)
|
|
111
|
-
end
|
|
118
|
+
def convert_indexes(raw_indexes, table_name, rule, ignored)
|
|
119
|
+
raw_indexes
|
|
120
|
+
.reject { |idx| rule&.ignored_indexes&.include?(idx[:name]) }
|
|
121
|
+
.reject { |idx| idx[:columns].intersect?(ignored) }
|
|
122
|
+
.map do |idx|
|
|
123
|
+
check_unsupported_index_type!(idx, table_name)
|
|
124
|
+
build_index_schema(idx, table_name)
|
|
125
|
+
end
|
|
112
126
|
end
|
|
113
127
|
|
|
114
128
|
def convert_foreign_keys(raw)
|
|
115
129
|
surviving_foreign_keys(raw).map { |fk| build_fk_schema(fk) }
|
|
116
130
|
end
|
|
117
131
|
|
|
118
|
-
|
|
119
|
-
|
|
132
|
+
# A warning here would be easy to miss in an unattended cron run.
|
|
133
|
+
# Raising makes this failure show up the same way as any other error:
|
|
134
|
+
# a non-zero exit status a monitor can catch.
|
|
135
|
+
def check_unsupported_index_type!(idx, table_name)
|
|
136
|
+
return unless UNSUPPORTED_INDEX_TYPES.include?(idx[:type])
|
|
120
137
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
"(e.g. add_index :col, using: :gin, opclass: :gin_trgm_ops) and " \
|
|
124
|
-
"silence this warning with ignore_index :#{idx[:name]}."
|
|
125
|
-
true
|
|
138
|
+
raise ConversionError, "#{table_name}: #{idx[:type].to_s.upcase} index #{idx[:name].inspect} " \
|
|
139
|
+
"has no PostgreSQL equivalent. Exclude it with ignore_index :#{idx[:name]}."
|
|
126
140
|
end
|
|
127
141
|
|
|
128
142
|
def build_index_schema(raw, table_name)
|
|
@@ -131,8 +145,6 @@ module SchemaFerry
|
|
|
131
145
|
columns: raw[:columns],
|
|
132
146
|
unique: raw[:unique],
|
|
133
147
|
using: raw[:using],
|
|
134
|
-
opclass: nil, # MySQL has no operator classes
|
|
135
|
-
where: raw[:where],
|
|
136
148
|
lengths: raw[:lengths],
|
|
137
149
|
orders: raw[:orders]
|
|
138
150
|
)
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
module SchemaFerry
|
|
4
4
|
module Converter
|
|
5
5
|
class TypeMapper
|
|
6
|
-
# PG has no limit concept for text/binary/bigint; drop the
|
|
7
|
-
# limits
|
|
8
|
-
|
|
6
|
+
# PG has no limit concept for text/binary/bigint/float; drop the
|
|
7
|
+
# MySQL-derived limits (float's limit: 53 is DOUBLE's internal bit
|
|
8
|
+
# width, not something AR ever reads back from a PG column).
|
|
9
|
+
LIMIT_STRIPPED_TYPES = %i[text binary bigint float].freeze
|
|
9
10
|
|
|
10
11
|
# PG's default timestamp precision is 6. Spelling it out makes ridgepole
|
|
11
12
|
# see a diff against the PG export (which omits it) on every run.
|
|
@@ -43,8 +44,15 @@ module SchemaFerry
|
|
|
43
44
|
pg_type, adjusted = normalize_integer(adjusted) if pg_type == :integer
|
|
44
45
|
adjusted.delete(:limit) if LIMIT_STRIPPED_TYPES.include?(pg_type)
|
|
45
46
|
strip_default_precision(pg_type, adjusted)
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
if pg_type == :decimal
|
|
48
|
+
# PG numeric(20) equals numeric(20,0) and is exported without scale.
|
|
49
|
+
adjusted[:scale] = nil if adjusted[:scale]&.zero?
|
|
50
|
+
# AR's schema dumper renders decimal defaults as a string (e.g.
|
|
51
|
+
# `default: "0"`), not a numeric literal. ridgepole compares against
|
|
52
|
+
# that dumped form, so a BigDecimal/Integer default never matches
|
|
53
|
+
# and gets re-applied on every run.
|
|
54
|
+
adjusted[:default] = adjusted[:default]&.to_s
|
|
55
|
+
end
|
|
48
56
|
|
|
49
57
|
[pg_type, adjusted]
|
|
50
58
|
end
|
|
@@ -6,11 +6,8 @@ module SchemaFerry
|
|
|
6
6
|
UNSET = Object.new.freeze
|
|
7
7
|
private_constant :UNSET
|
|
8
8
|
|
|
9
|
-
EXTRA_INDEX_OPTIONS = %i[name unique using where opclass order].freeze
|
|
10
|
-
|
|
11
9
|
attr_reader :table_name, :column_type_overrides,
|
|
12
|
-
:column_default_overrides, :ignored_columns, :ignored_indexes
|
|
13
|
-
:extra_indexes
|
|
10
|
+
:column_default_overrides, :ignored_columns, :ignored_indexes
|
|
14
11
|
|
|
15
12
|
def initialize(table_name)
|
|
16
13
|
@table_name = table_name.to_s
|
|
@@ -18,7 +15,6 @@ module SchemaFerry
|
|
|
18
15
|
@column_default_overrides = {}
|
|
19
16
|
@ignored_columns = []
|
|
20
17
|
@ignored_indexes = []
|
|
21
|
-
@extra_indexes = []
|
|
22
18
|
end
|
|
23
19
|
|
|
24
20
|
def map_column(column_name, type:, default: UNSET)
|
|
@@ -33,19 +29,6 @@ module SchemaFerry
|
|
|
33
29
|
def ignore_index(index_name)
|
|
34
30
|
@ignored_indexes << index_name.to_s
|
|
35
31
|
end
|
|
36
|
-
|
|
37
|
-
# Declares a PostgreSQL-side index that does not exist in MySQL (e.g. a
|
|
38
|
-
# pg_trgm GIN index replacing a skipped FULLTEXT index). Declared indexes
|
|
39
|
-
# are part of the generated schema, so sync keeps them.
|
|
40
|
-
def add_index(*columns, **options)
|
|
41
|
-
unknown = options.keys - EXTRA_INDEX_OPTIONS
|
|
42
|
-
unless unknown.empty?
|
|
43
|
-
raise ConfigError, "add_index: unknown option(s) #{unknown.map(&:inspect).join(", ")} " \
|
|
44
|
-
"(allowed: #{EXTRA_INDEX_OPTIONS.map(&:inspect).join(", ")})"
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
@extra_indexes << { columns: columns.map(&:to_s), options: options }
|
|
48
|
-
end
|
|
49
32
|
end
|
|
50
33
|
end
|
|
51
34
|
end
|
|
@@ -32,8 +32,6 @@ module SchemaFerry
|
|
|
32
32
|
:columns, # Array<String>
|
|
33
33
|
:unique, # Boolean
|
|
34
34
|
:using, # Symbol | nil
|
|
35
|
-
:opclass, # Symbol | Hash | nil (PostgreSQL operator class, e.g. :gin_trgm_ops)
|
|
36
|
-
:where, # String | nil
|
|
37
35
|
:lengths, # Integer | Hash | nil
|
|
38
36
|
:orders, # Symbol | Hash | nil
|
|
39
37
|
keyword_init: true
|
|
@@ -113,8 +113,6 @@ module SchemaFerry
|
|
|
113
113
|
parts = ["name: #{idx.name.inspect}"]
|
|
114
114
|
parts << "unique: true" if idx.unique
|
|
115
115
|
parts << "using: #{idx.using.inspect}" if idx.using
|
|
116
|
-
parts << "opclass: #{idx.opclass.inspect}" if idx.opclass
|
|
117
|
-
parts << "where: #{idx.where.inspect}" if idx.where
|
|
118
116
|
parts << "order: #{idx.orders.inspect}" if idx.orders
|
|
119
117
|
" t.index #{cols}, #{parts.join(", ")}"
|
|
120
118
|
end
|
data/lib/schema_ferry/version.rb
CHANGED