convergence 1.0.6 → 1.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/.github/workflows/ruby.yml +35 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +57 -0
- data/CONTRIBUTING.md +80 -0
- data/Gemfile.lock +25 -9
- data/README.md +160 -1
- data/Rakefile +45 -2
- data/convergence.gemspec +5 -1
- data/lib/convergence/cli.rb +10 -2
- data/lib/convergence/column.rb +3 -2
- data/lib/convergence/command/apply.rb +38 -9
- data/lib/convergence/command/dryrun.rb +5 -5
- data/lib/convergence/command/export.rb +19 -1
- data/lib/convergence/command.rb +6 -0
- data/lib/convergence/database_connector/postgres_connector.rb +25 -0
- data/lib/convergence/database_connector/sqlite_connector.rb +22 -0
- data/lib/convergence/database_connector.rb +6 -0
- data/lib/convergence/default_parameter/mysql_default_parameter.rb +1 -1
- data/lib/convergence/default_parameter/postgres_default_parameter.rb +57 -0
- data/lib/convergence/default_parameter/sqlite_default_parameter.rb +57 -0
- data/lib/convergence/default_parameter.rb +6 -0
- data/lib/convergence/diff.rb +46 -3
- data/lib/convergence/dsl.rb +18 -3
- data/lib/convergence/dumper/mysql_schema_dumper.rb +37 -9
- data/lib/convergence/dumper/postgres_schema_dumper.rb +193 -0
- data/lib/convergence/dumper/sqlite_schema_dumper.rb +132 -0
- data/lib/convergence/dumper.rb +113 -0
- data/lib/convergence/sql_generator/mysql_generator.rb +32 -3
- data/lib/convergence/sql_generator/postgres_generator.rb +270 -0
- data/lib/convergence/sql_generator/sqlite_generator.rb +192 -0
- data/lib/convergence/table.rb +3 -2
- data/lib/convergence/version.rb +1 -1
- data/spec/config/spec_database.yml +12 -0
- data/spec/convergence/config_spec.rb +75 -0
- data/spec/convergence/diff_spec.rb +291 -44
- data/spec/convergence/dsl_spec.rb +26 -0
- data/spec/convergence/dumper/mysql_schema_dumper_spec.rb +25 -2
- data/spec/convergence/dumper/postgres_schema_dumper_spec.rb +103 -0
- data/spec/convergence/dumper/sqlite_schema_dumper_spec.rb +85 -0
- data/spec/convergence/dumper_spec.rb +110 -16
- data/spec/convergence/foreign_key_spec.rb +33 -0
- data/spec/convergence/index_spec.rb +52 -0
- data/spec/convergence/pretty_diff_spec.rb +85 -0
- data/spec/convergence/table_spec.rb +46 -0
- data/spec/fixtures/add_table_with_enum_set.schema +5 -0
- data/spec/fixtures/change_table_comment_to_paper.schema +2 -0
- data/spec/fixtures/execute_raw_sql.schema +28 -0
- data/spec/fixtures/postgres/add_columns_to_paper.schema +29 -0
- data/spec/fixtures/postgres/add_table.schema +32 -0
- data/spec/fixtures/postgres/change_comment_columns_to_paper.schema +28 -0
- data/spec/fixtures/postgres/change_table_comment_to_paper.schema +28 -0
- data/spec/fixtures/postgres/drop_foreign_key.schema +25 -0
- data/spec/fixtures/postgres/drop_table.schema +19 -0
- data/spec/fixtures/postgres/remove_columns_to_paper.schema +27 -0
- data/spec/fixtures/postgres/rename_column_on_paper.schema +28 -0
- data/spec/fixtures/postgres/rename_table.schema +28 -0
- data/spec/fixtures/postgres_test_db.sql +41 -0
- data/spec/fixtures/rename_column_to_author.schema +26 -0
- data/spec/fixtures/rename_table.schema +26 -0
- data/spec/fixtures/sqlite/add_columns_to_paper.schema +29 -0
- data/spec/fixtures/sqlite/add_table.schema +32 -0
- data/spec/fixtures/sqlite/change_columns_to_paper.schema +28 -0
- data/spec/fixtures/sqlite/drop_foreign_key.schema +25 -0
- data/spec/fixtures/sqlite/drop_table.schema +19 -0
- data/spec/fixtures/sqlite/remove_columns_to_paper.schema +27 -0
- data/spec/fixtures/sqlite/rename_column_on_paper.schema +28 -0
- data/spec/fixtures/sqlite/rename_table.schema +27 -0
- data/spec/fixtures/sqlite_test_db.sql +31 -0
- data/spec/fixtures/test_db.sql +10 -0
- data/spec/integrations/command_diff.rb +35 -0
- data/spec/integrations/command_dryrun.rb +59 -2
- data/spec/integrations/command_export.rb +28 -0
- data/spec/postgres_integrations/command_dryrun.rb +101 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/sqlite_integrations/command_dryrun.rb +92 -0
- metadata +135 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 633f45bac912daa7950afa5535bfd87d13a7b33b25ff72489fb7fa125d0b6bdd
|
|
4
|
+
data.tar.gz: 392d293827a735cfeb515e878eb6f565d593259d0ff9f36e7aae8c0b4076b1ec
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f4255769af38418d8d336b8db4ef7e1ec2844ed3fbcf99cfd4e4bafa055e9765e5d30aaa92d2962ea8152c0b0688821e632ab71e2700158bcbe2f6a212f06270
|
|
7
|
+
data.tar.gz: 481f9bbebb0423275c0695445ab065ca6d54771f7dc25f94b005ac2918a1d7ad94730766a0d8a7c869fb76e49d7b6cc8e8da7cfea177d77c6fd2c049dbf0051b
|
data/.github/workflows/ruby.yml
CHANGED
|
@@ -4,21 +4,52 @@ on: [push, pull_request]
|
|
|
4
4
|
|
|
5
5
|
jobs:
|
|
6
6
|
test:
|
|
7
|
+
name: Test with MySQL ${{ matrix.mysql }} / PostgreSQL 16, Ruby ${{ matrix.ruby }}
|
|
7
8
|
runs-on: ubuntu-latest
|
|
8
9
|
strategy:
|
|
10
|
+
fail-fast: false
|
|
9
11
|
matrix:
|
|
10
|
-
|
|
12
|
+
mysql: ['5.7', '8.0']
|
|
13
|
+
ruby: ['3.1', '3.2', '3.3', '3.4']
|
|
14
|
+
services:
|
|
15
|
+
mysql:
|
|
16
|
+
image: mysql:${{ matrix.mysql }}
|
|
17
|
+
env:
|
|
18
|
+
MYSQL_ROOT_PASSWORD: root
|
|
19
|
+
ports:
|
|
20
|
+
- 3306:3306
|
|
21
|
+
options: >-
|
|
22
|
+
--health-cmd "mysqladmin ping"
|
|
23
|
+
--health-interval 10s
|
|
24
|
+
--health-timeout 5s
|
|
25
|
+
--health-retries 5
|
|
26
|
+
postgres:
|
|
27
|
+
image: postgres:16
|
|
28
|
+
env:
|
|
29
|
+
POSTGRES_PASSWORD: postgres
|
|
30
|
+
ports:
|
|
31
|
+
- 5432:5432
|
|
32
|
+
options: >-
|
|
33
|
+
--health-cmd pg_isready
|
|
34
|
+
--health-interval 10s
|
|
35
|
+
--health-timeout 5s
|
|
36
|
+
--health-retries 5
|
|
11
37
|
steps:
|
|
12
|
-
- uses: actions/checkout@
|
|
38
|
+
- uses: actions/checkout@v4
|
|
39
|
+
- name: Install PostgreSQL and SQLite client libraries
|
|
40
|
+
run: sudo apt-get update && sudo apt-get install -y libpq-dev libsqlite3-dev sqlite3
|
|
13
41
|
- name: Set up Ruby
|
|
14
42
|
uses: ruby/setup-ruby@v1
|
|
15
43
|
with:
|
|
16
44
|
ruby-version: ${{ matrix.ruby }}
|
|
45
|
+
bundler-cache: true
|
|
17
46
|
- name: Install dependencies
|
|
18
47
|
run: bundle install
|
|
19
|
-
- name: Start MySQL
|
|
20
|
-
run: sudo systemctl start mysql.service
|
|
21
48
|
- name: Prepare database
|
|
22
49
|
run: bundle exec rake db:convergence:prepare
|
|
50
|
+
- name: Prepare PostgreSQL database
|
|
51
|
+
run: bundle exec rake db:convergence:postgres:prepare
|
|
52
|
+
- name: Prepare SQLite database
|
|
53
|
+
run: bundle exec rake db:convergence:sqlite:prepare
|
|
23
54
|
- name: Run tests
|
|
24
55
|
run: bundle exec rake spec
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,60 @@
|
|
|
1
|
+
## Convergence 1.2.0 (August 26, 2026) ##
|
|
2
|
+
|
|
3
|
+
* Add `renamed_from:` support to detect table/column renames instead of a
|
|
4
|
+
destructive drop+add, for MySQL, PostgreSQL, and SQLite (PR: #101)
|
|
5
|
+
|
|
6
|
+
*nishio-dens*
|
|
7
|
+
|
|
8
|
+
* Bump rexml to fix CVE-2024-49761 (PR: #103)
|
|
9
|
+
|
|
10
|
+
*nishio-dens*
|
|
11
|
+
|
|
12
|
+
## Convergence 1.1.0 (August 25, 2026) ##
|
|
13
|
+
|
|
14
|
+
* Add PostgreSQL support (PR: #93)
|
|
15
|
+
|
|
16
|
+
*nishio-dens*
|
|
17
|
+
|
|
18
|
+
* Add SQLite support (PR: #95)
|
|
19
|
+
|
|
20
|
+
*nishio-dens*
|
|
21
|
+
|
|
22
|
+
* Support ENUM/SET column types (PR: #92)
|
|
23
|
+
|
|
24
|
+
*nishio-dens*
|
|
25
|
+
|
|
26
|
+
* Add `execute()` DSL method to run raw SQL (PR: #94)
|
|
27
|
+
|
|
28
|
+
*nishio-dens*
|
|
29
|
+
|
|
30
|
+
* Add `--dump-rails-migration` option to export DB schema as a Rails migration file (PR: #96)
|
|
31
|
+
|
|
32
|
+
*nishio-dens*
|
|
33
|
+
|
|
34
|
+
* Add `--safe-migration` option to skip DROP TABLE queries (PR: #91)
|
|
35
|
+
|
|
36
|
+
*nishio-dens*
|
|
37
|
+
|
|
38
|
+
* Add `--ignore-auto-increment` option (PR: #90)
|
|
39
|
+
|
|
40
|
+
*nishio-dens*
|
|
41
|
+
|
|
42
|
+
* Fix MySQL 8.0 test failure and modernize CI Ruby matrix (PR: #89)
|
|
43
|
+
|
|
44
|
+
*nishio-dens*
|
|
45
|
+
|
|
46
|
+
* Bump diffy to >= 3.4.2, fixing CVE-2022-33127 (PR: #100)
|
|
47
|
+
|
|
48
|
+
*nishio-dens*
|
|
49
|
+
|
|
50
|
+
* Add specs for previously untested classes/commands (PR: #97)
|
|
51
|
+
|
|
52
|
+
*nishio-dens*
|
|
53
|
+
|
|
54
|
+
* Add CONTRIBUTING.md (PR: #98)
|
|
55
|
+
|
|
56
|
+
*nishio-dens*
|
|
57
|
+
|
|
1
58
|
## Convergence 1.0.6 (April 28, 2022) ##
|
|
2
59
|
|
|
3
60
|
* Fixes YAML.safe_load arguments (PR: #84)
|
data/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Contributing to Convergence
|
|
2
|
+
|
|
3
|
+
Thanks for considering a contribution! This document covers everything you need to get a development
|
|
4
|
+
environment running, test your change, and open a pull request.
|
|
5
|
+
|
|
6
|
+
## Development setup
|
|
7
|
+
|
|
8
|
+
Convergence supports MySQL, PostgreSQL, and SQLite, so the test suite exercises all three. You don't need
|
|
9
|
+
every adapter running to work on a change that's scoped to one of them, but `rake spec` runs the whole suite
|
|
10
|
+
regardless of which adapter you touched.
|
|
11
|
+
|
|
12
|
+
1. Install Ruby (see `required_ruby_version` in `convergence.gemspec` for the minimum version) and Bundler.
|
|
13
|
+
2. Install dependencies:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
$ bundle install
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`sqlite3`, `mysql2`, and `pg` are native extensions; if a gem fails to build, make sure the corresponding
|
|
20
|
+
client library/headers are installed for your OS (e.g. `libmysqlclient-dev`, `libpq-dev`, `libsqlite3-dev`
|
|
21
|
+
on Debian/Ubuntu, or the Homebrew equivalents on macOS).
|
|
22
|
+
3. Start MySQL and PostgreSQL (locally, in Docker, however you prefer) and set connection details for all
|
|
23
|
+
three adapters in `spec/config/spec_database.yml` if the defaults don't match your setup.
|
|
24
|
+
4. Prepare the test databases:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
$ bundle exec rake db:convergence:prepare # MySQL
|
|
28
|
+
$ bundle exec rake db:convergence:postgres:prepare # PostgreSQL
|
|
29
|
+
$ bundle exec rake db:convergence:sqlite:prepare # SQLite (no server needed, just a file)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Running the tests
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
$ bundle exec rspec
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
or, equivalently:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
$ bundle exec rake spec
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
If you change the schema of the shared test fixtures (`spec/fixtures/test_db.sql`,
|
|
45
|
+
`spec/fixtures/postgres_test_db.sql`, `spec/fixtures/sqlite_test_db.sql`), rebuild the affected database with
|
|
46
|
+
the corresponding `overhaul` task (e.g. `rake db:convergence:overhaul`) before rerunning the suite.
|
|
47
|
+
|
|
48
|
+
## Code style
|
|
49
|
+
|
|
50
|
+
The project uses RuboCop (see `.rubocop.yml` for the enabled/disabled cops); there's no dedicated Rake task
|
|
51
|
+
wired up, so run it directly if you want to check your changes:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
$ bundle exec rubocop
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Adding support for a new adapter capability
|
|
58
|
+
|
|
59
|
+
Convergence's DSL uses MySQL terminology for column types so that schema files stay close to portable across
|
|
60
|
+
adapters. If you're adding a feature to an existing adapter (or a new adapter entirely), the adapter-specific
|
|
61
|
+
logic lives in four places per adapter:
|
|
62
|
+
|
|
63
|
+
- `lib/convergence/database_connector/*_connector.rb` — opens the DB connection.
|
|
64
|
+
- `lib/convergence/dumper/*_schema_dumper.rb` — reads the current schema from the database.
|
|
65
|
+
- `lib/convergence/sql_generator/*_generator.rb` — turns a diff into `CREATE`/`ALTER`/`DROP` SQL.
|
|
66
|
+
- `lib/convergence/default_parameter/*_default_parameter.rb` — fills in / strips adapter-specific defaults
|
|
67
|
+
(e.g. MySQL's default charset) so diffing doesn't produce spurious noise.
|
|
68
|
+
|
|
69
|
+
All four are dispatched by adapter name in `lib/convergence/database_connector.rb`, `lib/convergence/command.rb`,
|
|
70
|
+
`lib/convergence/command/apply.rb`, and `lib/convergence/default_parameter.rb` respectively — a new adapter
|
|
71
|
+
needs a `when` branch added to each. Add a parallel fixture database/schema files under `spec/fixtures/` and a
|
|
72
|
+
matching test suite (see the existing `spec/postgres_integrations/` or `spec/sqlite_integrations/` for the
|
|
73
|
+
pattern) so the new adapter is covered end-to-end.
|
|
74
|
+
|
|
75
|
+
## Opening a pull request
|
|
76
|
+
|
|
77
|
+
- Keep pull requests focused on a single change; it makes review (and `CHANGELOG.md`/release notes) easier.
|
|
78
|
+
- Add or update specs for any behavior change.
|
|
79
|
+
- Update `README.md` if you're adding a user-facing option or adapter capability.
|
|
80
|
+
- Reference the issue you're addressing (e.g. `Closes #123`) in the pull request description.
|
data/Gemfile.lock
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
convergence (1.0
|
|
4
|
+
convergence (1.2.0)
|
|
5
|
+
benchmark
|
|
5
6
|
diff-lcs
|
|
6
|
-
diffy
|
|
7
|
+
diffy (>= 3.4.2)
|
|
8
|
+
logger
|
|
7
9
|
mysql2
|
|
10
|
+
pg
|
|
11
|
+
sqlite3
|
|
8
12
|
thor
|
|
9
13
|
|
|
10
14
|
GEM
|
|
11
15
|
remote: https://rubygems.org/
|
|
12
16
|
specs:
|
|
13
17
|
ast (2.4.1)
|
|
18
|
+
benchmark (0.5.0)
|
|
19
|
+
bigdecimal (4.1.2)
|
|
14
20
|
coderay (1.1.3)
|
|
15
21
|
diff-lcs (1.4.4)
|
|
16
|
-
diffy (3.4.
|
|
22
|
+
diffy (3.4.4)
|
|
17
23
|
ffi (1.14.2)
|
|
18
24
|
formatador (0.2.5)
|
|
19
25
|
guard (2.16.2)
|
|
@@ -30,12 +36,16 @@ GEM
|
|
|
30
36
|
guard (~> 2.1)
|
|
31
37
|
guard-compat (~> 1.1)
|
|
32
38
|
rspec (>= 2.99.0, < 4.0)
|
|
39
|
+
io-console (0.9.2)
|
|
33
40
|
listen (3.3.3)
|
|
34
41
|
rb-fsevent (~> 0.10, >= 0.10.3)
|
|
35
42
|
rb-inotify (~> 0.9, >= 0.9.10)
|
|
43
|
+
logger (1.7.0)
|
|
36
44
|
lumberjack (1.2.8)
|
|
37
|
-
method_source (1.
|
|
38
|
-
|
|
45
|
+
method_source (1.1.0)
|
|
46
|
+
mini_portile2 (2.8.9)
|
|
47
|
+
mysql2 (0.5.7)
|
|
48
|
+
bigdecimal
|
|
39
49
|
nenv (0.3.0)
|
|
40
50
|
notiffany (0.1.3)
|
|
41
51
|
nenv (~> 0.1)
|
|
@@ -43,16 +53,20 @@ GEM
|
|
|
43
53
|
parallel (1.20.1)
|
|
44
54
|
parser (3.0.0.0)
|
|
45
55
|
ast (~> 2.4.1)
|
|
46
|
-
|
|
56
|
+
pg (1.6.3)
|
|
57
|
+
pry (0.16.0)
|
|
47
58
|
coderay (~> 1.1)
|
|
48
59
|
method_source (~> 1.0)
|
|
60
|
+
reline (>= 0.6.0)
|
|
49
61
|
rainbow (3.0.0)
|
|
50
|
-
rake (13.
|
|
62
|
+
rake (13.4.2)
|
|
51
63
|
rb-fsevent (0.10.4)
|
|
52
64
|
rb-inotify (0.10.1)
|
|
53
65
|
ffi (~> 1.0)
|
|
54
66
|
regexp_parser (2.0.3)
|
|
55
|
-
|
|
67
|
+
reline (0.7.0)
|
|
68
|
+
io-console (~> 0.5)
|
|
69
|
+
rexml (3.4.4)
|
|
56
70
|
rspec (3.10.0)
|
|
57
71
|
rspec-core (~> 3.10.0)
|
|
58
72
|
rspec-expectations (~> 3.10.0)
|
|
@@ -79,6 +93,8 @@ GEM
|
|
|
79
93
|
parser (>= 2.7.1.5)
|
|
80
94
|
ruby-progressbar (1.11.0)
|
|
81
95
|
shellany (0.0.1)
|
|
96
|
+
sqlite3 (2.8.1)
|
|
97
|
+
mini_portile2 (~> 2.8.0)
|
|
82
98
|
thor (1.0.1)
|
|
83
99
|
unicode-display_width (1.7.0)
|
|
84
100
|
|
|
@@ -95,4 +111,4 @@ DEPENDENCIES
|
|
|
95
111
|
rubocop
|
|
96
112
|
|
|
97
113
|
BUNDLED WITH
|
|
98
|
-
|
|
114
|
+
4.0.19
|
data/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Convergence
|
|
2
2
|
|
|
3
3
|
Convergence is a pure-Ruby database schema migration tool.
|
|
4
|
-
Currently, This tool
|
|
4
|
+
Currently, This tool supports MySQL, PostgreSQL, and SQLite.
|
|
5
5
|
|
|
6
6
|
It defines DB Schema using Convergence DSL(like Rails DSL).
|
|
7
7
|
For more information about Convergence DSL, See below ['Detail About Convergence DSL'](#detail-about-convergence-dsl)
|
|
@@ -133,8 +133,10 @@ Create Table: CREATE TABLE `test_tables` (
|
|
|
133
133
|
```
|
|
134
134
|
Commands:
|
|
135
135
|
convergence apply FILE -c, --config=CONFIG # execute sql to your database
|
|
136
|
+
# [--dry-run], [--rollback-dry-run], [--safe-migration], [--ignore-auto-increment]
|
|
136
137
|
convergence diff FILE1 FILE2 # print diff of DSLs
|
|
137
138
|
convergence export -c, --config=CONFIG # export db schema to dsl
|
|
139
|
+
# [--dump-rails-migration], [--filename=FILENAME]
|
|
138
140
|
convergence help [COMMAND] # Describe available commands or one specific command
|
|
139
141
|
convergence version # print the version
|
|
140
142
|
```
|
|
@@ -152,6 +154,51 @@ username: root
|
|
|
152
154
|
password:
|
|
153
155
|
```
|
|
154
156
|
|
|
157
|
+
To use PostgreSQL instead, set `adapter: postgresql` (`postgres`/`pg` also work):
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
$ cat database.yml
|
|
161
|
+
adapter: postgresql
|
|
162
|
+
database: convergence_test
|
|
163
|
+
host: 127.0.0.1
|
|
164
|
+
username: postgres
|
|
165
|
+
password:
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
#### PostgreSQL notes
|
|
169
|
+
|
|
170
|
+
The Convergence DSL uses MySQL terminology for column types (`tinyint`, `mediumint`, `datetime`, ...) so the
|
|
171
|
+
same schema files stay close to portable across adapters; the PostgreSQL adapter maps them to their closest
|
|
172
|
+
native PostgreSQL type (e.g. `mediumint`/`int` → `integer`, `datetime` → `timestamp`, `json` → `jsonb`,
|
|
173
|
+
`extra: 'auto_increment'` → `GENERATED BY DEFAULT AS IDENTITY`, requiring PostgreSQL 10+).
|
|
174
|
+
|
|
175
|
+
A few MySQL-specific concepts have no PostgreSQL equivalent and are ignored on that adapter: `engine`,
|
|
176
|
+
`row_format`, `default_charset`/`character_set`, and `collate`. `enum`/`set` column types are not supported yet
|
|
177
|
+
on PostgreSQL.
|
|
178
|
+
|
|
179
|
+
To use SQLite instead, set `adapter: sqlite3` (`sqlite` also works) and point `database` at a file path:
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
$ cat database.yml
|
|
183
|
+
adapter: sqlite3
|
|
184
|
+
database: /path/to/development.sqlite3
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
#### SQLite notes
|
|
188
|
+
|
|
189
|
+
SQLite's `ALTER TABLE` support is intentionally limited by SQLite itself: it can only add/drop columns and
|
|
190
|
+
create/drop indexes on an existing table. Changing a column's type/null/default, and adding or removing a
|
|
191
|
+
foreign key or primary key on an existing table, all require SQLite's own "rebuild the table" procedure
|
|
192
|
+
(create a new table, copy the data over, drop the old one, rename it), which this adapter does not implement
|
|
193
|
+
yet -- it raises `NotImplementedError` instead of generating SQL that SQLite would reject. Creating a brand new
|
|
194
|
+
table with any of the above (including foreign keys) works fine. Renaming a table/column via `renamed_from:`
|
|
195
|
+
(see [Rename tables and columns](#rename-tables-and-columns)) is an exception to this limitation -- SQLite has
|
|
196
|
+
natively supported `RENAME TABLE`/`RENAME COLUMN` since 3.25.0, so it doesn't need the rebuild procedure.
|
|
197
|
+
|
|
198
|
+
Like PostgreSQL, MySQL-only table options (`engine`, `row_format`, `default_charset`/`character_set`, `collate`,
|
|
199
|
+
table/column `comment`) have no SQLite equivalent and are ignored. `extra: 'auto_increment'` maps to SQLite's
|
|
200
|
+
`INTEGER PRIMARY KEY AUTOINCREMENT`.
|
|
201
|
+
|
|
155
202
|
#### Use SSL connection
|
|
156
203
|
|
|
157
204
|
If you would like to use SSL connection, you can specify SSL options in database.yml
|
|
@@ -216,6 +263,43 @@ create_table "paper_authors", collate: "utf8_general_ci", comment: "Paper Author
|
|
|
216
263
|
end
|
|
217
264
|
```
|
|
218
265
|
|
|
266
|
+
### Export as a Rails migration file
|
|
267
|
+
|
|
268
|
+
If you're migrating to (or working alongside) a Rails app, pass `--dump-rails-migration` to export the schema as an
|
|
269
|
+
ActiveRecord migration instead of a Convergence DSL file. `--filename` sets the base filename/class name (Rails'
|
|
270
|
+
usual `snake_case` filename / `CamelCase` class name convention applies).
|
|
271
|
+
|
|
272
|
+
```
|
|
273
|
+
$ convergence export -c database.yml --dump-rails-migration --filename create_initial_tables
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
```ruby
|
|
277
|
+
# Filename: 20240101000000_create_initial_tables.rb
|
|
278
|
+
class CreateInitialTables < ActiveRecord::Migration[7.0]
|
|
279
|
+
def change
|
|
280
|
+
create_table :authors do |t|
|
|
281
|
+
t.string :name, limit: 110
|
|
282
|
+
t.timestamps
|
|
283
|
+
|
|
284
|
+
t.index :created_at, name: "index_authors_on_created_at"
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
The output is written to stdout with a `# Filename: ...` comment on top (including a timestamp prefix in the usual
|
|
291
|
+
Rails migration filename format) — redirect it to that file yourself, e.g.
|
|
292
|
+
`convergence export ... > db/migrate/$(date +%Y%m%d%H%M%S)_create_initial_tables.rb`.
|
|
293
|
+
|
|
294
|
+
A few notes on the conversion:
|
|
295
|
+
* An `id` column that matches Rails' implicit auto-incrementing primary key (`int`/`bigint`, `primary_key: true`,
|
|
296
|
+
`extra: 'auto_increment'`) is omitted, since `create_table` adds it automatically.
|
|
297
|
+
* `created_at`/`updated_at` columns with matching `datetime`/`null` options are collapsed into `t.timestamps`.
|
|
298
|
+
* Column types use Rails' type names (e.g. `varchar` → `string`, `datetime`/`timestamp` → `datetime`); `enum`/`set`
|
|
299
|
+
have no ActiveRecord equivalent and fall back to `string`.
|
|
300
|
+
* MySQL-only concepts with no `create_table` DSL equivalent (`character_set`, `collate`, `extra`, `after`) are
|
|
301
|
+
dropped from column definitions.
|
|
302
|
+
|
|
219
303
|
### Dry run
|
|
220
304
|
|
|
221
305
|
```
|
|
@@ -235,6 +319,56 @@ $ convergence apply example.schema -c database.yml --rollback-dry-run
|
|
|
235
319
|
$ convergence apply example.schema -c database.yml
|
|
236
320
|
```
|
|
237
321
|
|
|
322
|
+
### Ignore AUTO_INCREMENT changes
|
|
323
|
+
|
|
324
|
+
If you dump your schema from a database with a high AUTO_INCREMENT value (e.g. a dev/staging box you've been testing on)
|
|
325
|
+
and apply it to another database, convergence will normally generate a query that bumps AUTO_INCREMENT to match, which
|
|
326
|
+
can jump/skip a large range of ids. Pass `--ignore-auto-increment` to skip generating AUTO_INCREMENT change queries entirely.
|
|
327
|
+
|
|
328
|
+
```
|
|
329
|
+
$ convergence apply example.schema -c database.yml --ignore-auto-increment
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
This also applies to `--dry-run` and `--rollback-dry-run`.
|
|
333
|
+
|
|
334
|
+
### Safe migration
|
|
335
|
+
|
|
336
|
+
If you want to prevent convergence from ever dropping a table (e.g. when the DSL file removed a table that still
|
|
337
|
+
has important data), pass `--safe-migration`. It skips generating `DROP TABLE` queries; every other change is still applied.
|
|
338
|
+
|
|
339
|
+
```
|
|
340
|
+
$ convergence apply example.schema -c database.yml --safe-migration
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
This also applies to `--dry-run` and `--rollback-dry-run`.
|
|
344
|
+
|
|
345
|
+
### Rename tables and columns
|
|
346
|
+
|
|
347
|
+
Without `renamed_from:`, convergence detects a renamed table/column as a drop followed by an add -- which loses
|
|
348
|
+
the data in that table/column. Add `renamed_from:` to the new name so convergence generates `RENAME TABLE`/`RENAME
|
|
349
|
+
COLUMN` instead:
|
|
350
|
+
|
|
351
|
+
```ruby
|
|
352
|
+
create_table "user_accounts", renamed_from: "users" do |t|
|
|
353
|
+
t.int "id", primary_key: true, extra: "auto_increment"
|
|
354
|
+
t.varchar "full_name", limit: 200, renamed_from: "name"
|
|
355
|
+
end
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
A few things to know:
|
|
359
|
+
|
|
360
|
+
* When a table or column has `renamed_from:`, **only the rename is applied on that pass** -- any other change to
|
|
361
|
+
the same table/column (a type change, a new `null`/`default`, ...) is ignored until you run `apply` again. Run
|
|
362
|
+
`apply` a second time (with `renamed_from:` still in place, or removed) to pick up the rest.
|
|
363
|
+
* `renamed_from:` is safe to leave in the schema file after the rename has been applied -- convergence detects
|
|
364
|
+
that the target name already exists and treats it as a no-op.
|
|
365
|
+
* Swapping two names (A ↔ B) or chaining renames (A → B → C) in a single `apply` is not supported. Use an
|
|
366
|
+
intermediate temporary name and apply in multiple steps instead.
|
|
367
|
+
* MySQL's `RENAME COLUMN` requires MySQL 8.0+; there's no fallback for 5.7.
|
|
368
|
+
* `convergence diff`'s pretty-printed output does not yet understand renames -- it will describe a rename as "no
|
|
369
|
+
change" for the renamed table/column. This is a known limitation, unrelated to the SQL that `apply`/`dry-run`
|
|
370
|
+
actually generate.
|
|
371
|
+
|
|
238
372
|
### Include Other Schema files
|
|
239
373
|
|
|
240
374
|
```
|
|
@@ -242,6 +376,20 @@ include 'first_schema.schema'
|
|
|
242
376
|
include 'other_file.schema'
|
|
243
377
|
```
|
|
244
378
|
|
|
379
|
+
### Execute raw SQL
|
|
380
|
+
|
|
381
|
+
For things the DSL has no dedicated syntax for (views, triggers, stored procedures, data backfills, grants, ...),
|
|
382
|
+
use `execute` to run an arbitrary SQL statement. Unlike `create_table`, statements passed to `execute` are **not**
|
|
383
|
+
diffed against the current schema: they run every time the schema file is applied, so make sure the SQL itself is
|
|
384
|
+
idempotent (e.g. `CREATE OR REPLACE VIEW ...`, `CREATE TABLE IF NOT EXISTS ...`).
|
|
385
|
+
|
|
386
|
+
```
|
|
387
|
+
execute "CREATE OR REPLACE VIEW active_users AS SELECT * FROM users WHERE active = 1"
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
`execute` statements always run after the generated schema changes, and are skipped by `--rollback-dry-run` (there's
|
|
391
|
+
no way to know how to reverse an arbitrary SQL statement).
|
|
392
|
+
|
|
245
393
|
|
|
246
394
|
## Detail About Convergence DSL
|
|
247
395
|
|
|
@@ -273,6 +421,8 @@ Convergence is currently support column types below.
|
|
|
273
421
|
- timestamp
|
|
274
422
|
- year
|
|
275
423
|
- json
|
|
424
|
+
- enum
|
|
425
|
+
- set
|
|
276
426
|
|
|
277
427
|
```
|
|
278
428
|
create_table "tests", comment: 'Column type example' do |t|
|
|
@@ -282,6 +432,8 @@ create_table "tests", comment: 'Column type example' do |t|
|
|
|
282
432
|
t.varchar 'test_string', null: true, default: 'hello', limit: 300
|
|
283
433
|
t.text 'text_col'
|
|
284
434
|
t.datetime 'created_at'
|
|
435
|
+
t.enum 'status', values: %w(active inactive pending), default: 'active'
|
|
436
|
+
t.set 'flags', values: %w(a b c)
|
|
285
437
|
end
|
|
286
438
|
```
|
|
287
439
|
|
|
@@ -344,9 +496,16 @@ end
|
|
|
344
496
|
|
|
345
497
|
```
|
|
346
498
|
$ bundle exec rake db:convergence:prepare
|
|
499
|
+
$ bundle exec rake db:convergence:postgres:prepare
|
|
500
|
+
$ bundle exec rake db:convergence:sqlite:prepare
|
|
347
501
|
$ bundle exec rspec
|
|
348
502
|
```
|
|
349
503
|
|
|
504
|
+
## Contributing
|
|
505
|
+
|
|
506
|
+
Bug reports and pull requests are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for how to set up a
|
|
507
|
+
development environment, run the test suite, and what to include in a pull request.
|
|
508
|
+
|
|
350
509
|
## Copyright
|
|
351
510
|
|
|
352
511
|
Copyright © 2014-2018 S.nishio. See LICENSE.txt for further details.
|
data/Rakefile
CHANGED
|
@@ -5,8 +5,10 @@ require 'yaml'
|
|
|
5
5
|
RSpec::Core::RakeTask.new('spec')
|
|
6
6
|
task default: :spec
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
mysql_settings = Hash[
|
|
8
|
+
spec_database_settings = YAML.load_file("#{File.dirname(__FILE__)}/spec/config/spec_database.yml")
|
|
9
|
+
mysql_settings = Hash[spec_database_settings['mysql'].map { |k, v| [k.to_sym, v] }]
|
|
10
|
+
postgres_settings = Hash[spec_database_settings['postgresql'].map { |k, v| [k.to_sym, v] }]
|
|
11
|
+
sqlite_db_path = File.expand_path("#{File.dirname(__FILE__)}/spec/fixtures/#{spec_database_settings['sqlite3']['database']}")
|
|
10
12
|
|
|
11
13
|
namespace :db do
|
|
12
14
|
namespace :convergence do
|
|
@@ -30,5 +32,46 @@ namespace :db do
|
|
|
30
32
|
desc 'Prepare the test databases'
|
|
31
33
|
task prepare: [:build_databases, :create_tables]
|
|
32
34
|
task overhaul: [:drop_databases, :build_databases, :create_tables]
|
|
35
|
+
|
|
36
|
+
namespace :postgres do
|
|
37
|
+
desc 'Build the PostgreSQL database for tests'
|
|
38
|
+
task :build_databases do
|
|
39
|
+
ENV['PGPASSWORD'] = postgres_settings[:password].to_s
|
|
40
|
+
system("psql -U #{postgres_settings[:username]} -h #{postgres_settings[:host]} -p #{postgres_settings[:port]} -d postgres -c 'create database #{postgres_settings[:database]};'")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
task :drop_databases do
|
|
44
|
+
ENV['PGPASSWORD'] = postgres_settings[:password].to_s
|
|
45
|
+
system("psql -U #{postgres_settings[:username]} -h #{postgres_settings[:host]} -p #{postgres_settings[:port]} -d postgres -c 'drop database if exists #{postgres_settings[:database]};'")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
desc 'Create tables on the PostgreSQL test database'
|
|
49
|
+
task :create_tables do
|
|
50
|
+
ENV['PGPASSWORD'] = postgres_settings[:password].to_s
|
|
51
|
+
query_path = "#{File.dirname(__FILE__)}/spec/fixtures/postgres_test_db.sql"
|
|
52
|
+
system("psql -U #{postgres_settings[:username]} -h #{postgres_settings[:host]} -p #{postgres_settings[:port]} -d #{postgres_settings[:database]} -f #{query_path}")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
desc 'Prepare the PostgreSQL test database'
|
|
56
|
+
task prepare: [:build_databases, :create_tables]
|
|
57
|
+
task overhaul: [:drop_databases, :build_databases, :create_tables]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
namespace :sqlite do
|
|
61
|
+
desc 'Remove the SQLite test database file'
|
|
62
|
+
task :drop_databases do
|
|
63
|
+
File.delete(sqlite_db_path) if File.exist?(sqlite_db_path)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
desc 'Create tables on the SQLite test database'
|
|
67
|
+
task :create_tables do
|
|
68
|
+
query_path = "#{File.dirname(__FILE__)}/spec/fixtures/sqlite_test_db.sql"
|
|
69
|
+
system("sqlite3 #{sqlite_db_path} < #{query_path}")
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
desc 'Prepare the SQLite test database'
|
|
73
|
+
task prepare: [:drop_databases, :create_tables]
|
|
74
|
+
task overhaul: [:drop_databases, :create_tables]
|
|
75
|
+
end
|
|
33
76
|
end
|
|
34
77
|
end
|
data/convergence.gemspec
CHANGED
|
@@ -19,9 +19,13 @@ Gem::Specification.new do |spec|
|
|
|
19
19
|
spec.require_paths = ['lib']
|
|
20
20
|
|
|
21
21
|
spec.add_dependency 'mysql2'
|
|
22
|
+
spec.add_dependency 'pg'
|
|
23
|
+
spec.add_dependency 'sqlite3'
|
|
22
24
|
spec.add_dependency 'diff-lcs'
|
|
23
|
-
spec.add_dependency 'diffy'
|
|
25
|
+
spec.add_dependency 'diffy', '>= 3.4.2'
|
|
24
26
|
spec.add_dependency 'thor'
|
|
27
|
+
spec.add_dependency 'logger'
|
|
28
|
+
spec.add_dependency 'benchmark'
|
|
25
29
|
|
|
26
30
|
spec.required_ruby_version = ">= 2.4.0"
|
|
27
31
|
|
data/lib/convergence/cli.rb
CHANGED
|
@@ -11,13 +11,19 @@ class Convergence::CLI < Thor
|
|
|
11
11
|
method_option :config, aliases: '-c', type: :string, required: true, desc: 'Database Yaml Setting'
|
|
12
12
|
method_option :dry_run, type: :boolean
|
|
13
13
|
method_option :rollback_dry_run, type: :boolean
|
|
14
|
+
method_option :ignore_auto_increment, type: :boolean, desc: 'Do not generate AUTO_INCREMENT change queries'
|
|
15
|
+
method_option :safe_migration, type: :boolean, desc: 'Do not generate DROP TABLE queries'
|
|
14
16
|
|
|
15
17
|
def self.exit_on_failure?
|
|
16
18
|
true
|
|
17
19
|
end
|
|
18
20
|
|
|
19
21
|
def apply(file)
|
|
20
|
-
opts = {
|
|
22
|
+
opts = {
|
|
23
|
+
input: file,
|
|
24
|
+
ignore_auto_increment: options[:ignore_auto_increment],
|
|
25
|
+
safe_migration: options[:safe_migration]
|
|
26
|
+
}
|
|
21
27
|
if options[:dry_run]
|
|
22
28
|
require 'convergence/command/dryrun'
|
|
23
29
|
Convergence::Command::Dryrun.new(opts, config: config).execute
|
|
@@ -39,9 +45,11 @@ class Convergence::CLI < Thor
|
|
|
39
45
|
|
|
40
46
|
desc 'export', 'export db schema to dsl'
|
|
41
47
|
method_option :config, aliases: '-c', type: :string, required: true, desc: 'Database Yaml Setting'
|
|
48
|
+
method_option :dump_rails_migration, type: :boolean, desc: 'Output an ActiveRecord migration file instead of a Convergence DSL file'
|
|
49
|
+
method_option :filename, type: :string, desc: 'Base filename (without timestamp/extension) used for --dump-rails-migration'
|
|
42
50
|
def export
|
|
43
51
|
require 'convergence/command/export'
|
|
44
|
-
opts = {}
|
|
52
|
+
opts = { dump_rails_migration: options[:dump_rails_migration], filename: options[:filename] }
|
|
45
53
|
Convergence::Command::Export.new(opts, config: config).execute
|
|
46
54
|
end
|
|
47
55
|
|
data/lib/convergence/column.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
class Convergence::Column
|
|
2
|
-
attr_accessor :type, :column_name, :options
|
|
2
|
+
attr_accessor :type, :column_name, :options, :renamed_from
|
|
3
3
|
|
|
4
4
|
COLUMN_TYPE = %i(
|
|
5
5
|
tinyint
|
|
@@ -38,6 +38,7 @@ class Convergence::Column
|
|
|
38
38
|
def initialize(type, column_name, options = {})
|
|
39
39
|
@type = type
|
|
40
40
|
@column_name = column_name
|
|
41
|
-
@
|
|
41
|
+
@renamed_from = options[:renamed_from]&.to_s
|
|
42
|
+
@options = options.reject { |k| k == :renamed_from }
|
|
42
43
|
end
|
|
43
44
|
end
|